1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00
This commit is contained in:
Jacob Su 2025-02-20 13:50:19 +08:00 committed by GitHub
commit 9f83d62ab8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 3187 additions and 115 deletions

View file

@ -1795,6 +1795,13 @@ vhost hls.srs.com {
# default: off # default: off
enabled on; enabled on;
# whether to use fmp4 as container
# The default value is off, then HLS use ts as container format,
# if on, HLS use fmp4 as container format.
# Overwrite by env SRS_VHOST_HLS_HLS_USE_FMP4 for all vhosts.
# default: off
hls_use_fmp4 on;
# the hls fragment in seconds, the duration of a piece of ts. # the hls fragment in seconds, the duration of a piece of ts.
# Overwrite by env SRS_VHOST_HLS_HLS_FRAGMENT for all vhosts. # Overwrite by env SRS_VHOST_HLS_HLS_FRAGMENT for all vhosts.
# default: 10 # default: 10
@ -1860,6 +1867,26 @@ vhost hls.srs.com {
# Overwrite by env SRS_VHOST_HLS_HLS_TS_FILE for all vhosts. # Overwrite by env SRS_VHOST_HLS_HLS_TS_FILE for all vhosts.
# default: [app]/[stream]-[seq].ts # default: [app]/[stream]-[seq].ts
hls_ts_file [app]/[stream]-[seq].ts; hls_ts_file [app]/[stream]-[seq].ts;
# the hls fmp4 file name.
# we supports some variables to generate the filename.
# [vhost], the vhost of stream.
# [app], the app of stream.
# [stream], the stream name of stream.
# [2006], replace this const to current year.
# [01], replace this const to current month.
# [02], replace this const to current date.
# [15], replace this const to current hour.
# [04], replace this const to current minute.
# [05], replace this const to current second.p
# [999], replace this const to current millisecond.
# [timestamp],replace this const to current UNIX timestamp in ms.
# [seq], the sequence number of fmp4.
# [duration], replace this const to current ts duration.
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/dvr#custom-path
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-hls#hls-config
# Overwrite by env SRS_VHOST_HLS_HLS_FMP4_FILE for all vhosts.
# default: [app]/[stream]-[seq].m4s
hls_fmp4_file [app]/[stream]-[seq].m4s;
# the hls entry prefix, which is base url of ts url. # the hls entry prefix, which is base url of ts url.
# for example, the prefix is: # for example, the prefix is:
# http://your-server/ # http://your-server/

26
trunk/conf/hls.mp4.conf Normal file
View file

@ -0,0 +1,26 @@
# the config for srs to delivery hls
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-hls
# @see full.conf for detail config.
listen 1935;
max_connections 1000;
daemon off;
srs_log_tank console;
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
http_api {
enabled on;
listen 1985;
}
vhost __defaultVhost__ {
hls {
enabled on;
hls_use_fmp4 on;
hls_path ./objs/nginx/html;
hls_fragment 2;
hls_window 10;
}
}

View file

@ -2683,7 +2683,7 @@ srs_error_t SrsConfig::check_normal_config()
&& m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec"
&& m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify"
&& m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file" && m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file"
&& m != "hls_key_file_path" && m != "hls_key_url" && m != "hls_dts_directly" && m != "hls_ctx" && m != "hls_ts_ctx") { && m != "hls_key_file_path" && m != "hls_key_url" && m != "hls_dts_directly" && m != "hls_ctx" && m != "hls_ts_ctx" && m != "hls_use_fmp4" && m != "hls_fmp4_file") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str()); return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str());
} }
@ -6936,6 +6936,31 @@ bool SrsConfig::get_hls_enabled(SrsConfDirective* vhost)
return SRS_CONF_PREFER_FALSE(conf->arg0()); return SRS_CONF_PREFER_FALSE(conf->arg0());
} }
bool SrsConfig::get_hls_use_fmp4(std::string vhost)
{
SRS_OVERWRITE_BY_ENV_BOOL("srs.vhost.hls.hls_use_fmp4"); // SRS_VHOST_HLS_HLS_USE_FMP4
static bool DEFAULT = false;
SrsConfDirective* conf = get_vhost(vhost);
if (!conf) {
return DEFAULT;
}
conf = conf->get("hls");
if (!conf) {
return DEFAULT;
}
conf = conf->get("hls_use_fmp4");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return SRS_CONF_PREFER_FALSE(conf->arg0());
}
string SrsConfig::get_hls_entry_prefix(string vhost) string SrsConfig::get_hls_entry_prefix(string vhost)
{ {
SRS_OVERWRITE_BY_ENV_STRING("srs.vhost.hls.hls_entry_prefix"); // SRS_VHOST_HLS_HLS_ENTRY_PREFIX SRS_OVERWRITE_BY_ENV_STRING("srs.vhost.hls.hls_entry_prefix"); // SRS_VHOST_HLS_HLS_ENTRY_PREFIX
@ -7012,6 +7037,25 @@ string SrsConfig::get_hls_ts_file(string vhost)
return conf->arg0(); return conf->arg0();
} }
string SrsConfig::get_hls_fmp4_file(std::string vhost)
{
SRS_OVERWRITE_BY_ENV_STRING("srs.vhost.hls.hls_fmp4_file"); // SRS_VHOST_HLS_HLS_FMP4_FILE
static string DEFAULT = "[app]/[stream]-[seq].m4s";
SrsConfDirective* conf = get_hls(vhost);
if (!conf) {
return DEFAULT;
}
conf = conf->get("hls_fmp4_file");
if (!conf || conf->arg0().empty()) {
return DEFAULT;
}
return conf->arg0();
}
bool SrsConfig::get_hls_ts_floor(string vhost) bool SrsConfig::get_hls_ts_floor(string vhost)
{ {
SRS_OVERWRITE_BY_ENV_BOOL("srs.vhost.hls.hls_ts_floor"); // SRS_VHOST_HLS_HLS_TS_FLOOR SRS_OVERWRITE_BY_ENV_BOOL("srs.vhost.hls.hls_ts_floor"); // SRS_VHOST_HLS_HLS_TS_FLOOR

View file

@ -933,6 +933,8 @@ public:
// Whether HLS is enabled. // Whether HLS is enabled.
virtual bool get_hls_enabled(std::string vhost); virtual bool get_hls_enabled(std::string vhost);
virtual bool get_hls_enabled(SrsConfDirective* vhost); virtual bool get_hls_enabled(SrsConfDirective* vhost);
// Whether HLS use fmp4 container format
virtual bool get_hls_use_fmp4(std::string vhost);
// Get the HLS m3u8 list ts segment entry prefix info. // Get the HLS m3u8 list ts segment entry prefix info.
virtual std::string get_hls_entry_prefix(std::string vhost); virtual std::string get_hls_entry_prefix(std::string vhost);
// Get the HLS ts/m3u8 file store path. // Get the HLS ts/m3u8 file store path.
@ -941,6 +943,8 @@ public:
virtual std::string get_hls_m3u8_file(std::string vhost); virtual std::string get_hls_m3u8_file(std::string vhost);
// Get the HLS ts file path template. // Get the HLS ts file path template.
virtual std::string get_hls_ts_file(std::string vhost); virtual std::string get_hls_ts_file(std::string vhost);
// Get the HLS fmp4 file path template.
virtual std::string get_hls_fmp4_file(std::string vhost);
// Whether enable the floor(timestamp/hls_fragment) for variable timestamp. // Whether enable the floor(timestamp/hls_fragment) for variable timestamp.
virtual bool get_hls_ts_floor(std::string vhost); virtual bool get_hls_ts_floor(std::string vhost);
// Get the hls fragment time, in srs_utime_t. // Get the hls fragment time, in srs_utime_t.
@ -985,6 +989,7 @@ public:
// Whether enable hls_ctx // Whether enable hls_ctx
virtual bool get_hls_ctx_enabled(std::string vhost); virtual bool get_hls_ctx_enabled(std::string vhost);
// Whether enable session for ts file. // Whether enable session for ts file.
// The ts file including .ts file for MPEG-ts segment, .m4s file and init.mp4 file for fmp4 segment.
virtual bool get_hls_ts_ctx_enabled(std::string vhost); virtual bool get_hls_ts_ctx_enabled(std::string vhost);
// hds section // hds section
private: private:

File diff suppressed because it is too large Load diff

View file

@ -32,11 +32,14 @@ class SrsTsAacJitter;
class SrsTsMessageCache; class SrsTsMessageCache;
class SrsHlsSegment; class SrsHlsSegment;
class SrsTsContext; class SrsTsContext;
class SrsMp4M2tsInitEncoder;
class SrsFmp4SegmentEncoder;
// The wrapper of m3u8 segment from specification: // The wrapper of m3u8 segment from specification:
// //
// 3.3.2. EXTINF // 3.3.2. EXTINF
// The EXTINF tag specifies the duration of a media segment. // The EXTINF tag specifies the duration of a media segment.
// TODO: refactor this to support fmp4 segment.
class SrsHlsSegment : public SrsFragment class SrsHlsSegment : public SrsFragment
{ {
public: public:
@ -56,11 +59,58 @@ public:
SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w); SrsHlsSegment(SrsTsContext* c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter* w);
virtual ~SrsHlsSegment(); virtual ~SrsHlsSegment();
public: public:
void config_cipher(unsigned char* key,unsigned char* iv); void config_cipher(unsigned char* key, unsigned char* iv);
// replace the placeholder // replace the placeholder
virtual srs_error_t rename(); virtual srs_error_t rename();
}; };
class SrsInitMp4Segment : public SrsFragment
{
private:
SrsFileWriter* fw_;
SrsMp4M2tsInitEncoder* init_;
unsigned char kid_[16];
unsigned char const_iv_[16];
uint8_t const_iv_size_;
public:
SrsInitMp4Segment();
virtual ~SrsInitMp4Segment();
public:
virtual srs_error_t config_cipher(unsigned char* kid, unsigned char* const_iv, uint8_t const_iv_size);
// Write the init mp4 file, with the v_tid(video track id) and a_tid (audio track id).
virtual srs_error_t write(SrsFormat* format, int v_tid, int a_tid);
virtual srs_error_t write_video_only(SrsFormat* format, int v_tid);
virtual srs_error_t write_audio_only(SrsFormat* format, int a_tid);
private:
virtual srs_error_t init_encoder();
};
// TODO: merge this code with SrsFragmentedMp4 in dash
class SrsHlsM4sSegment : public SrsFragment
{
private:
SrsFileWriter* fw_;
SrsFmp4SegmentEncoder* enc_;
public:
// sequence number in m3u8.
int sequence_no;
// Will be saved in m3u8 file.
unsigned char iv[16];
public:
SrsHlsM4sSegment(SrsFileWriter* fw);
virtual ~SrsHlsM4sSegment();
virtual srs_error_t initialize(int64_t time, uint32_t v_tid, uint32_t a_tid, int sequence_number, std::string m4s_path);
virtual void config_cipher(unsigned char* key, unsigned char* iv);
virtual srs_error_t write(SrsSharedPtrMessage* shared_msg, SrsFormat* format);
virtual srs_error_t reap(uint64_t& dts);
};
// The hls async call: on_hls // The hls async call: on_hls
class SrsDvrAsyncCallOnHls : public ISrsAsyncCallTask class SrsDvrAsyncCallOnHls : public ISrsAsyncCallTask
{ {
@ -217,6 +267,155 @@ private:
virtual srs_error_t _refresh_m3u8(std::string m3u8_file); virtual srs_error_t _refresh_m3u8(std::string m3u8_file);
}; };
// Mux the HLS stream(m3u8 and m4s files).
// Generally, the m3u8 muxer only provides methods to open/close segments,
// to flush video/audio, without any mechenisms.
//
// That is, user must use HlsCache, which will control the methods of muxer,
// and provides HLS mechenisms.
class SrsHlsFmp4Muxer
{
private:
SrsRequest* req_;
private:
std::string hls_entry_prefix_;
std::string hls_path_;
std::string hls_m4s_file_;
bool hls_cleanup_;
bool hls_wait_keyframe_;
std::string m3u8_dir_;
double hls_aof_ratio_;
// TODO: FIXME: Use TBN 1000.
srs_utime_t hls_fragment_;
srs_utime_t hls_window_;
SrsAsyncCallWorker* async_;
private:
// Whether use floor algorithm for timestamp.
bool hls_ts_floor_;
// The deviation in piece to adjust the fragment to be more
// bigger or smaller.
int deviation_ts_;
// The previous reap floor timestamp,
// used to detect the dup or jmp or ts.
int64_t accept_floor_ts_;
int64_t previous_floor_ts_;
bool init_mp4_ready_;
private:
// Whether encrypted or not
bool hls_keys_;
int hls_fragments_per_key_;
// The key file name
std::string hls_key_file_;
// The key file path
std::string hls_key_file_path_;
// The key file url
std::string hls_key_url_;
// The key and iv.
unsigned char key_[16];
unsigned char kid_[16];
unsigned char iv_[16];
// The underlayer file writer.
SrsFileWriter* writer_;
private:
int sequence_no_;
srs_utime_t max_td_;
std::string m3u8_;
std::string m3u8_url_;
int video_track_id_;
int audio_track_id_;
uint64_t video_dts_;
private:
// The available cached segments in m3u8.
SrsFragmentWindow* segments_;
// The current writing segment.
SrsHlsM4sSegment* current_;
private:
// Latest audio codec, parsed from stream.
SrsAudioCodecId latest_acodec_;
// Latest audio codec, parsed from stream.
SrsVideoCodecId latest_vcodec_;
public:
SrsHlsFmp4Muxer();
virtual ~SrsHlsFmp4Muxer();
public:
virtual void dispose();
public:
virtual int sequence_no();
virtual std::string ts_url();
virtual srs_utime_t duration();
virtual int deviation();
public:
SrsAudioCodecId latest_acodec();
void set_latest_acodec(SrsAudioCodecId v);
SrsVideoCodecId latest_vcodec();
void set_latest_vcodec(SrsVideoCodecId v);
public:
// Initialize the hls muxer.
virtual srs_error_t initialize(int v_tid, int a_tid);
// When publish or unpublish stream.
virtual srs_error_t on_publish(SrsRequest* req);
virtual srs_error_t write_init_mp4(SrsFormat* format, bool has_video, bool has_audio);
virtual srs_error_t write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
virtual srs_error_t write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
virtual srs_error_t on_unpublish();
// When publish, update the config for muxer.
virtual srs_error_t update_config(SrsRequest* r);
// Open a new segment(a new ts file)
virtual srs_error_t segment_open(srs_utime_t basetime);
virtual srs_error_t on_sequence_header();
// Whether segment overflow,
// that is whether the current segment duration>=(the segment in config)
virtual bool is_segment_overflow();
// Whether wait keyframe to reap the ts.
virtual bool wait_keyframe();
// Whether segment absolutely overflow, for pure audio to reap segment,
// that is whether the current segment duration>=2*(the segment in config)
virtual bool is_segment_absolutely_overflow();
public:
// Whether current hls muxer is pure audio mode.
// virtual bool pure_audio();
// virtual srs_error_t flush_audio(SrsTsMessageCache* cache);
// virtual srs_error_t flush_video(SrsTsMessageCache* cache);
// When flushing video or audio, we update the duration. But, we should also update the
// duration before closing the segment. Keep in mind that it's fine to update the duration
// several times using the same dts timestamp.
void update_duration(uint64_t dts);
// Close segment(ts).
virtual srs_error_t segment_close();
private:
virtual srs_error_t do_segment_close();
virtual srs_error_t write_hls_key();
virtual srs_error_t refresh_m3u8();
virtual srs_error_t _refresh_m3u8(std::string m3u8_file);
};
// The base class for HLS controller
class ISrsHlsController
{
public:
ISrsHlsController();
virtual ~ISrsHlsController();
public:
virtual srs_error_t initialize() = 0;
virtual void dispose() = 0;
// When publish or unpublish stream.
virtual srs_error_t on_publish(SrsRequest* req) = 0;
virtual srs_error_t on_unpublish() = 0;
virtual srs_error_t write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format) = 0;
virtual srs_error_t write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format) = 0;
virtual srs_error_t on_sequence_header(SrsSharedPtrMessage* msg, SrsFormat* format) = 0;
virtual int sequence_no() = 0;
virtual std::string ts_url() = 0;
virtual srs_utime_t duration() = 0;
virtual int deviation() = 0;
};
// The hls stream cache, // The hls stream cache,
// use to cache hls stream and flush to hls muxer. // use to cache hls stream and flush to hls muxer.
// //
@ -232,14 +431,23 @@ private:
// when timestamp convert to flv tbn, it will loose precise, // when timestamp convert to flv tbn, it will loose precise,
// so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter, // so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter,
// we use a aac jitter to correct the audio pts. // we use a aac jitter to correct the audio pts.
class SrsHlsController class SrsHlsController : public ISrsHlsController
{ {
private: private:
// The HLS muxer to reap ts and m3u8. // The HLS muxer to reap ts and m3u8.
// The TS is cached to SrsTsMessageCache then flush to ts segment. // The TS is cached to SrsTsMessageCache then flush to ts segment.
SrsHlsMuxer* muxer; SrsHlsMuxer* muxer;
// The TS cache // The TS cache
// TODO: support both fmp4 and ts format
SrsTsMessageCache* tsmc; SrsTsMessageCache* tsmc;
// If the diff=dts-previous_audio_dts is about 23,
// that's the AAC samples is 1024, and we use the samples to calc the dts.
int64_t previous_audio_dts;
// The total aac samples.
uint64_t aac_samples;
// Whether directly turn FLV timestamp to TS DTS.
bool hls_dts_directly;
public: public:
SrsHlsController(); SrsHlsController();
virtual ~SrsHlsController(); virtual ~SrsHlsController();
@ -258,11 +466,11 @@ public:
// must write a #EXT-X-DISCONTINUITY to m3u8. // must write a #EXT-X-DISCONTINUITY to m3u8.
// @see: hls-m3u8-draft-pantos-http-live-streaming-12.txt // @see: hls-m3u8-draft-pantos-http-live-streaming-12.txt
// @see: 3.4.11. EXT-X-DISCONTINUITY // @see: 3.4.11. EXT-X-DISCONTINUITY
virtual srs_error_t on_sequence_header(); virtual srs_error_t on_sequence_header(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
// write audio to cache, if need to flush, flush to muxer. // write audio to cache, if need to flush, flush to muxer.
virtual srs_error_t write_audio(SrsAudioFrame* frame, int64_t pts); virtual srs_error_t write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
// write video to muxer. // write video to muxer.
virtual srs_error_t write_video(SrsVideoFrame* frame, int64_t dts); virtual srs_error_t write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
private: private:
// Reopen the muxer for a new hls segment, // Reopen the muxer for a new hls segment,
// close current segment, open a new segment, // close current segment, open a new segment,
@ -271,12 +479,51 @@ private:
virtual srs_error_t reap_segment(); virtual srs_error_t reap_segment();
}; };
// Transmux RTMP stream to HLS(m3u8 and ts). class SrsHlsMp4Controller : public ISrsHlsController
{
private:
bool has_video_sh_;
bool has_audio_sh_;
int video_track_id_;
int audio_track_id_;
// Current audio dts.
uint64_t audio_dts_;
// Current video dts.
uint64_t video_dts_;
SrsRequest* req_;
SrsHlsFmp4Muxer* muxer_;
public:
SrsHlsMp4Controller();
virtual ~SrsHlsMp4Controller();
public:
virtual srs_error_t initialize();
virtual void dispose();
// When publish or unpublish stream.
virtual srs_error_t on_publish(SrsRequest* req);
virtual srs_error_t on_unpublish();
virtual srs_error_t write_audio(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
virtual srs_error_t write_video(SrsSharedPtrMessage* shared_video, SrsFormat* format);
virtual srs_error_t on_sequence_header(SrsSharedPtrMessage* shared_audio, SrsFormat* format);
virtual int sequence_no();
virtual std::string ts_url();
virtual srs_utime_t duration();
virtual int deviation();
};
// Transmux RTMP stream to HLS(m3u8 and ts,fmp4).
// TODO: FIXME: add utest for hls. // TODO: FIXME: add utest for hls.
class SrsHls class SrsHls
{ {
private: private:
SrsHlsController* controller; ISrsHlsController* controller;
private: private:
SrsRequest* req; SrsRequest* req;
// Whether the HLS is enabled. // Whether the HLS is enabled.
@ -290,14 +537,7 @@ private:
bool reloading_; bool reloading_;
// To detect heartbeat and dispose it if configured. // To detect heartbeat and dispose it if configured.
srs_utime_t last_update_time; srs_utime_t last_update_time;
private:
// If the diff=dts-previous_audio_dts is about 23,
// that's the AAC samples is 1024, and we use the samples to calc the dts.
int64_t previous_audio_dts;
// The total aac samples.
uint64_t aac_samples;
// Whether directly turn FLV timestamp to TS DTS.
bool hls_dts_directly;
private: private:
SrsOriginHub* hub; SrsOriginHub* hub;
SrsRtmpJitter* jitter; SrsRtmpJitter* jitter;

View file

@ -74,6 +74,7 @@ protected:
virtual srs_error_t serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int64_t start, int64_t end); virtual srs_error_t serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int64_t start, int64_t end);
// Support HLS streaming with pseudo session id. // Support HLS streaming with pseudo session id.
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
// the ts file including: .ts .m4s init.mp4
virtual srs_error_t serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
}; };

View file

@ -1298,6 +1298,7 @@ class SrsVideoFrame : public SrsFrame
{ {
public: public:
// video specified // video specified
// TODO: H.264 and H.265 reused AvcFrameType and AvcFrameTrait?
SrsVideoAvcFrameType frame_type; SrsVideoAvcFrameType frame_type;
SrsVideoAvcFrameTrait avc_packet_type; SrsVideoAvcFrameTrait avc_packet_type;
// whether sample_units contains IDR frame. // whether sample_units contains IDR frame.
@ -1361,6 +1362,7 @@ public:
public: public:
virtual bool is_aac_sequence_header(); virtual bool is_aac_sequence_header();
virtual bool is_mp3_sequence_header(); virtual bool is_mp3_sequence_header();
// TODO: is avc|hevc|av1 sequence header
virtual bool is_avc_sequence_header(); virtual bool is_avc_sequence_header();
private: private:
// Demux the video packet in H.264 codec. // Demux the video packet in H.264 codec.

File diff suppressed because it is too large Load diff

View file

@ -113,6 +113,27 @@ enum SrsMp4BoxType
SrsMp4BoxTypeSIDX = 0x73696478, // 'sidx' SrsMp4BoxTypeSIDX = 0x73696478, // 'sidx'
SrsMp4BoxTypeHEV1 = 0x68657631, // 'hev1' SrsMp4BoxTypeHEV1 = 0x68657631, // 'hev1'
SrsMp4BoxTypeHVCC = 0x68766343, // 'hvcC' SrsMp4BoxTypeHVCC = 0x68766343, // 'hvcC'
SrsMp4BoxTypeSENC = 0x73656e63, // 'senc'
SrsMp4BoxTypeSAIZ = 0x7361697a, // 'saiz'
SrsMp4BoxTypeSAIO = 0x7361696f, // 'saio'
SrsMp4BoxTypeENCV = 0x656e6376, // 'encv'
SrsMp4BoxTypeENCA = 0x656e6361, // 'enca'
SrsMp4BoxTypeSINF = 0x73696e66, // 'sinf'
SrsMp4BoxTypeSCHI = 0x73636869, // 'schi'
SrsMp4BoxTypeTENC = 0x74656e63, // 'tenc'
SrsMp4BoxTypeFRMA = 0x66726d61, // 'frma'
SrsMp4BoxTypeSCHM = 0x7363686d, // 'schm'
};
// Common encryption scheme types
// @see ISO-IEC-23001-7.pdf, 4.2
enum SrsMp4CENSchemeType
{
SrsMp4CENSchemeCENC = 0x63656e63, // 'cenc'
SrsMp4CENSchemeCBC1 = 0x63626331, // 'cbc1'
SrsMp4CENSchemeCENS = 0x63656e73, // 'cens'
SrsMp4CENSchemeCBCS = 0x63626373, // 'cbcs'
SrsMp4CENSchemeSVE1 = 0x73766531, // 'sve1'
}; };
// 8.4.3.3 Semantics // 8.4.3.3 Semantics
@ -317,9 +338,9 @@ public:
// Get the header of moof. // Get the header of moof.
virtual SrsMp4MovieFragmentHeaderBox* mfhd(); virtual SrsMp4MovieFragmentHeaderBox* mfhd();
virtual void set_mfhd(SrsMp4MovieFragmentHeaderBox* v); virtual void set_mfhd(SrsMp4MovieFragmentHeaderBox* v);
// Get the traf.
virtual SrsMp4TrackFragmentBox* traf(); // Let moof support more than one traf
virtual void set_traf(SrsMp4TrackFragmentBox* v); virtual void add_traf(SrsMp4TrackFragmentBox* v);
}; };
// 8.8.5 Movie Fragment Header Box (mfhd) // 8.8.5 Movie Fragment Header Box (mfhd)
@ -499,7 +520,7 @@ class SrsMp4TrackFragmentRunBox : public SrsMp4FullBox
public: public:
// The number of samples being added in this run; also the number of rows in the following // The number of samples being added in this run; also the number of rows in the following
// table (the rows can be empty) // table (the rows can be empty)
//uint32_t sample_count; // uint32_t sample_count;
// The following are optional fields // The following are optional fields
public: public:
// added to the implicit or explicit data_offset established in the track fragment header. // added to the implicit or explicit data_offset established in the track fragment header.
@ -710,8 +731,7 @@ public:
virtual ~SrsMp4MovieExtendsBox(); virtual ~SrsMp4MovieExtendsBox();
public: public:
// Get the track extends box. // Get the track extends box.
virtual SrsMp4TrackExtendsBox* trex(); virtual void add_trex(SrsMp4TrackExtendsBox* v);
virtual void set_trex(SrsMp4TrackExtendsBox* v);
}; };
// 8.8.3 Track Extends Box(trex) // 8.8.3 Track Extends Box(trex)
@ -1869,6 +1889,348 @@ public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc); virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
}; };
// Sample auxiliary information sizes box (saiz)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.7.8, page 62
// @see https://github.com/gpac/mp4box.js/blob/master/src/parsing/saiz.js
// Syntax
// aligned(8) class SampleAuxiliaryInformationSizesBox extends FullBox('saiz', version=0, flags)
// {
// if (flags & 1) {
// unsigned int(32) aux_info_type;
// unsigned int(32) aux_info_type_parameter;
// }
// unsigned int(8) default_sample_info_size;
// unsigned int(32) sample_count;
// if (default_sample_info_size == 0) {
// unsigned int(8) sample_info_size[sample_count];
// }
// }
class SrsMp4SampleAuxiliaryInfoSizeBox: public SrsMp4FullBox
{
public:
uint32_t aux_info_type;
uint32_t aux_info_type_parameter;
uint8_t default_sample_info_size;
uint32_t sample_count;
std::vector<uint8_t> sample_info_sizes;
public:
SrsMp4SampleAuxiliaryInfoSizeBox();
virtual ~SrsMp4SampleAuxiliaryInfoSizeBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Sample auxiliary information offsets box (saio)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.7.9, page 63
// @see https://github.com/gpac/mp4box.js/blob/master/src/parsing/saio.js
// Syntax
// aligned(8) class SampleAuxiliaryInformationOffsetsBox extends FullBox('saio', version, flags)
// {
// if (flags & 1) {
// unsigned int(32) aux_info_type;
// unsigned int(32) aux_info_type_parameter;
// }
// unsigned int(32) entry_count;
// if (version == 0) {
// unsigned int(32) offset[entry_count];
// } else {
// unsigned int(64) offset[entry_count];
// }
// }
class SrsMp4SampleAuxiliaryInfoOffsetBox: public SrsMp4FullBox
{
public:
uint32_t aux_info_type;
uint32_t aux_info_type_parameter;
// uint32_t entry_count;
std::vector<uint64_t> offsets;
public:
SrsMp4SampleAuxiliaryInfoOffsetBox();
virtual ~SrsMp4SampleAuxiliaryInfoOffsetBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
enum SrsMp4CencSampleEncryptionFlags
{
SrsMp4CencSampleEncryptionTrackDefault = 0x01,
SrsMp4CencSampleEncryptionUseSubSample = 0x02,
};
struct SrsMp4SubSampleEncryptionInfo : public ISrsCodec
{
uint16_t bytes_of_clear_data;
uint32_t bytes_of_protected_data;
SrsMp4SubSampleEncryptionInfo();
virtual ~SrsMp4SubSampleEncryptionInfo();
virtual uint64_t nb_bytes();
virtual srs_error_t encode(SrsBuffer* buf);
virtual srs_error_t decode(SrsBuffer* buf);
virtual std::stringstream& dumps(std::stringstream& ss, SrsMp4DumpContext dc);
};
class SrsMp4SampleEncryptionEntry : public ISrsCodec
{
public:
// if flags && 0x02
std::vector<SrsMp4SubSampleEncryptionInfo> subsample_infos;
public:
SrsMp4SampleEncryptionEntry(SrsMp4FullBox* senc, uint8_t per_sample_iv_size);
virtual ~SrsMp4SampleEncryptionEntry();
virtual srs_error_t set_iv(uint8_t* iv, uint8_t iv_size);
virtual uint64_t nb_bytes();
virtual srs_error_t encode(SrsBuffer* buf);
virtual srs_error_t decode(SrsBuffer* buf);
virtual std::stringstream& dumps(std::stringstream& ss, SrsMp4DumpContext dc);
private:
SrsMp4FullBox* senc_;
uint8_t per_sample_iv_size_;
uint8_t* iv_;
};
// Sample encryption box (senc)
// @see ISO-IEC-23001-7.pdf 7.2.1
// @see https://cdn.standards.iteh.ai/samples/84637/c960c91d60ae4da7a2f9380bd7e08642/ISO-IEC-FDIS-23001-7.pdf
// CENC SAI: sample auxiliary information associated with a sample and containing cryptographic information
// such as initialization vector or subsample information
// @see ISO-IEC-23001-7.pdf 7.2.2
// Syntax
// aligned(8) class SampleEncryptionBox extend FullBox(`senc`, version=0, flags)
// {
// unsigned int(32) sample_count;
// {
// unsigned int(Per_Sample_IV_Size*8) InitializationVector;
// if (flags & 0x000002)
// {
// unsigned int(16) subsample_count;
// {
// unsigned int(16) BytesOfClearData;
// unsigned int(32) BytesOfProtectedData;
// } [ subsample_count ]
// }
// } [ sample_count ]
// }
class SrsMp4SampleEncryptionBox: public SrsMp4FullBox
{
public:
std::vector<SrsMp4SampleEncryptionEntry*> entries;
private:
uint8_t per_sample_iv_size_;
public:
// @see ISO-IEC-23001-7.pdf 9.1
// Per_Sample_IV_Size has supported values: 0, 8, 16.
SrsMp4SampleEncryptionBox(uint8_t per_sample_iv_size);
virtual ~SrsMp4SampleEncryptionBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Original Format Box (frma)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.2, page 81
// aligned(8) class OriginalFormatBox(codingname) extends Box ('frma') {
// unsigned int(32) data_format = codingname;
// }
class SrsMp4OriginalFormatBox : public SrsMp4Box
{
private:
uint32_t data_format_;
public:
SrsMp4OriginalFormatBox(uint32_t original_format);
virtual ~SrsMp4OriginalFormatBox();
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Scheme Type Box (schm)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.5, page 81
// aligned(8) class SchemeTypeBox extends FullBox('schm', 0, flags) {
// unsigned int(32) scheme_type; // 4CC identifying the scheme
// unsigned int(32) scheme_version; // scheme version
// if (flags & 0x000001) {
// unsigned int(8) scheme_uri[]; // browser uri
// }
// }
// @see @see ISO-IEC-23001-7.pdf 4.1
// the scheme_version field SHALL be set to 0x00010000 (Major version 1, Minor version 0).
#define SCHM_SCHEME_URI_MAX_SIZE 128
class SrsMp4SchemeTypeBox : public SrsMp4FullBox
{
public:
uint32_t scheme_type;
uint32_t scheme_version;
char scheme_uri[SCHM_SCHEME_URI_MAX_SIZE];
uint32_t scheme_uri_size;
public:
SrsMp4SchemeTypeBox();
virtual ~SrsMp4SchemeTypeBox();
public:
virtual void set_scheme_uri(char* uri, uint32_t uri_size);
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// Scheme Information Box (schi)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.6, page 82
// aligned(8) class SchemeInformationBox extends Box('schi') {
// Box scheme_specific_data[];
// }
class SrsMp4SchemeInfoBox : public SrsMp4Box
{
public:
SrsMp4SchemeInfoBox();
virtual ~SrsMp4SchemeInfoBox();
};
// Protection Scheme Information Box (sinf)
// @see ISO_IEC_14496-12-base-format-2012.pdf, 8.12.1, page 80
// aligned(8) class ProtectionSchemeInfoBox(fmt) extends Box('sinf') {
// OriginalFormatBox(fmt) original_format; // frma
// SchemeTypeBox scheme_type_box; // optional
// SchemeInformationBox info; // optional
// }
class SrsMp4ProtectionSchemeInfoBox : public SrsMp4Box
{
public:
SrsMp4ProtectionSchemeInfoBox();
virtual ~SrsMp4ProtectionSchemeInfoBox();
public:
// Get the Original Format Box (frma)
virtual SrsMp4OriginalFormatBox* frma();
virtual void set_frma(SrsMp4OriginalFormatBox* v);
// Get the Scheme Type Box (schm)
virtual SrsMp4SchemeTypeBox* schm();
virtual void set_schm(SrsMp4SchemeTypeBox* v);
// Get the Scheme Information Box (schi)
virtual SrsMp4SchemeInfoBox* schi();
virtual void set_schi(SrsMp4SchemeInfoBox* v);
};
// Track Encryption box (tenc)
// @see ISO-IEC-23001-7.pdf 8.2
// aligned(8) class TrackEncryptionBox extends FullBox('tenc', version, flags=0) {
// unsigned int(8) reserved = 0;
// if (version == 0) {
// unsigned int(8) reserved = 0;
// } else { // version is 1 or greater
// unsigned int(4) default_crypt_byte_block;
// unsigned int(4) default_skip_byte_block;
// }
// unsigned int(8) default_isProtected;
// unsigned int(8) default_Per_Sample_IV_Size;
// unsigned int(8)[16] default_KID;
// if (default_isProtected == 1 && default_Per_Sample_IV_Size == 0) {
// unsigned int(8) default_constant_IV_size;
// unsigned int(8)[default_constant_IV_size] default_constant_IV;
// }
// }
// @see https://developer.apple.com/documentation/http-live-streaming/about-the-common-media-application-format-with-http-live-streaming-hls
// For fragmented MPEG-4 Segments, an EXT-X-KEY tag with a METHOD=SAMPLE-AES attribute indicates that
// the Segment is encrypted using the `cbcs` scheme in ISO/IEC 23001-7.
// HLS supports unencrypted and encrypted with 'cbcs'.
// @see ISO-IEC-23001-7.pdf 10.4.1 Definition
// 'cbcs' AES-CBC subsample pattern encryption scheme.
// The 'scheme_type' field of the scheme Type Box('schm') SHALL be set to 'cbcs'.
// the version of the Track Encryption Box('tenc') SHALL be 1.
// Encrypted video tracks using NAL Structured Video conforming to ISO/IEC 14496-15 SHALL be
// protected using Subsample encryption specified in 9.5, and SHALL use pattern encryption as specified
// in 9.6. As a result, the fields crypt_byte_block and skip_byte_block SHALL NOT be 0.
// Constant IVs SHALL be used; 'default_Per_Sample_IV_Size' and 'Per_Sample_IV_Size', SHALL be 0.
// Tracks other than video are protected using whole-block full-sample encryption as specified in 9.7 and
// hence skip_byte_block SHALL be 0.
// Pattern Block length, i.e. crypt_byte_block + skip_byte_block SHOULD equal 10.
// For all video NAL units, including in 'avc1', the slice header SHALL be unencrypted.
// The first complete byte of video slice data(following the video slice header) SHALL begin a single
// Subsample protected byte range indicated by the start of BytesOfProtectedData, which extends to
// the end of the video NAL.
// NOTE 1 For AVC VCL NAL units, the encryption pattern starts at an offset rounded to the next byte after
// the slice header, i.e. on the first full byte of slice data. For HEVC, the encryption pattern starts after
// the byte_alignment() field that terminates the slice_segment_header(), i.e. on the first byte of slice data.
//
// @see ISO-IEC-23001-7.pdf 10.4.2 'cbcs' AES-CBC mode pattern encryption scheme application(informative)
// An encrypt:skip pattern of 1:9(i.e. 10% partial encryption) is recommended. Even though the syntax
// allows many different encryption patterns, a pattern of ten Blocks is recommended. This means that the
// skipped Blocks will be (10-N). The number of encrypted cipher blocks N can span multiple contiguous
// 16-byte Blocks(e.g. three encrypted Blocks followed by seven unencrypted Blocks would result in 30%
// partial encryption of the video data).
// For example, to achieve 10 % encryption, the first Block of the pattern is encrypted and the following
// nine Blocks are left unencrypted. The pattern is repeated every 160 bytes of the protected range, until
// the end of the range. If the protected range of the slice body is not a multiple of the pattern length
// (e.g. 160 bytes), then the pattern sequence applies to the included whole 16-byte Blocks and a partial
// 16-byte Block that may remain where the pattern is terminated by the byte length of the range
// BytesOfProtectedData, is left unencrypted.
//
// @see ISO-IEC-23001-7.pdf 9.7 Whole-block full sample encryption
// In whole-block full sample encryption, the entire sample is protected. Every sample is encrypted
// starting at offset 0(there is no unprotected preamble) up to the last 16-byte boundary, leaving any
// trailing 0-15 bytes in the clear. The IV is reset at every sample.
class SrsMp4TrackEncryptionBox : public SrsMp4FullBox
{
public:
uint8_t reserved;
uint8_t reserved_2;
uint8_t default_crypt_byte_block;
uint8_t default_skip_byte_block;
uint8_t default_is_protected;
uint8_t default_per_sample_IV_size;
uint8_t default_KID[16];
uint8_t default_constant_IV_size;
uint8_t default_constant_IV[16];
public:
SrsMp4TrackEncryptionBox();
virtual ~SrsMp4TrackEncryptionBox();
public:
virtual void set_default_constant_IV(uint8_t* iv, uint8_t iv_size);
protected:
virtual int nb_header();
virtual srs_error_t encode_header(SrsBuffer* buf);
virtual srs_error_t decode_header(SrsBuffer* buf);
public:
virtual std::stringstream& dumps_detail(std::stringstream& ss, SrsMp4DumpContext dc);
};
// TODO: add SchemeTypeBox(schm), set scheme_type=cbcs
// Generally, a MP4 sample contains a frame, for example, a video frame or audio frame. // Generally, a MP4 sample contains a frame, for example, a video frame or audio frame.
class SrsMp4Sample class SrsMp4Sample
{ {
@ -1931,7 +2293,7 @@ public:
virtual srs_error_t write(SrsMp4MovieBox* moov); virtual srs_error_t write(SrsMp4MovieBox* moov);
// Write the samples info to moof. // Write the samples info to moof.
// @param The dts is the dts of last segment. // @param The dts is the dts of last segment.
virtual srs_error_t write(SrsMp4MovieFragmentBox* moof, uint64_t dts); virtual srs_error_t write(SrsMp4TrackFragmentBox* traf, uint64_t dts);
private: private:
virtual srs_error_t write_track(SrsFrameType track, virtual srs_error_t write_track(SrsFrameType track,
SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts, SrsMp4DecodingTime2SampleBox* stts, SrsMp4SyncSampleBox* stss, SrsMp4CompositionTime2SampleBox* ctts,
@ -2111,22 +2473,67 @@ private:
}; };
// A fMP4 encoder, to write the init.mp4 with sequence header. // A fMP4 encoder, to write the init.mp4 with sequence header.
// TODO: What the M2ts short for?
class SrsMp4M2tsInitEncoder class SrsMp4M2tsInitEncoder
{ {
private: private:
ISrsWriter* writer; ISrsWriter* writer;
private:
uint8_t crypt_byte_block_;
uint8_t skip_byte_block_;
unsigned char kid_[16];
unsigned char iv_[16];
uint8_t iv_size_;
bool is_protected_;
public: public:
SrsMp4M2tsInitEncoder(); SrsMp4M2tsInitEncoder();
virtual ~SrsMp4M2tsInitEncoder(); virtual ~SrsMp4M2tsInitEncoder();
public: public:
// Initialize the encoder with a writer w. // Initialize the encoder with a writer w.
virtual srs_error_t initialize(ISrsWriter* w); virtual srs_error_t initialize(ISrsWriter* w);
// set encryption
// TODO: review kid(map to a key) and iv, which are shared between audio/video tracks.
virtual void config_encryption(uint8_t crypt_byte_block, uint8_t skip_byte_block, unsigned char* kid, unsigned char* iv, uint8_t iv_size);
// Write the sequence header. // Write the sequence header.
// TODO: merge this method to its sibling.
virtual srs_error_t write(SrsFormat* format, bool video, int tid); virtual srs_error_t write(SrsFormat* format, bool video, int tid);
/**
* The mp4 box format for init.mp4.
*
* |ftyp|
* |moov|
* | |mvhd|
* | |trak|
* | |trak|
* | |....|
* | |mvex|
* | | |trex|
* | | |trex|
* | | |....|
*
* Write the sequence header with both video and audio track.
*/
virtual srs_error_t write(SrsFormat* format, int v_tid, int a_tid);
private:
/**
* box->type = 'encv' or 'enca'
* |encv|
* | |sinf|
* | | |frma|
* | | |schm|
* | | |schi|
* | | | |tenc|
*/
virtual srs_error_t config_sample_description_encryption(SrsMp4SampleEntry* box);
}; };
// A fMP4 encoder, to cache segments then flush to disk, because the fMP4 should write // A fMP4 encoder, to cache segments then flush to disk, because the fMP4 should write
// trun box before mdat. // trun box before mdat.
// TODO: fmp4 support package more than one tracks.
class SrsMp4M2tsSegmentEncoder class SrsMp4M2tsSegmentEncoder
{ {
private: private:
@ -2160,6 +2567,52 @@ public:
virtual srs_error_t flush(uint64_t& dts); virtual srs_error_t flush(uint64_t& dts);
}; };
// A fMP4 encoder, to cache segments then flush to disk, because the fMP4 should write
// trun box before mdat.
// TODO: fmp4 support package more than one tracks.
class SrsFmp4SegmentEncoder
{
private:
ISrsWriter* writer_;
uint32_t sequence_number_;
// TODO: audio, video may have different basetime.
srs_utime_t decode_basetime_;
uint32_t audio_track_id_;
uint32_t video_track_id_;
private:
uint32_t nb_audios_;
uint32_t nb_videos_;
uint32_t styp_bytes_;
uint64_t mdat_audio_bytes_;
uint64_t mdat_video_bytes_;
SrsMp4SampleManager* audio_samples_;
SrsMp4SampleManager* video_samples_;
unsigned char* key_;
unsigned char iv_[16];
bool do_sample_encryption_;
public:
SrsFmp4SegmentEncoder();
virtual ~SrsFmp4SegmentEncoder();
public:
// Initialize the encoder with a writer w.
virtual srs_error_t initialize(ISrsWriter* w, uint32_t sequence, srs_utime_t basetime, uint32_t v_tid, uint32_t a_tid);
// config cipher
virtual srs_error_t config_cipher(unsigned char* key, unsigned char* iv);
// Cache a sample.
// @param ht, The sample handler type, audio/soun or video/vide.
// @param ft, The frame type. For video, it's SrsVideoAvcFrameType.
// @param dts The output dts in milliseconds.
// @param pts The output pts in milliseconds.
// @param sample The output payload, user must free it.
// @param nb_sample The output size of payload.
// @remark All samples are RAW AAC/AVC data, because sequence header is writen to init.mp4.
virtual srs_error_t write_sample(SrsMp4HandlerType ht, uint16_t ft,
uint32_t dts, uint32_t pts, uint8_t* sample, uint32_t nb_sample);
// Flush the encoder, to write the moof and mdat.
virtual srs_error_t flush(uint64_t& dts);
};
// LCOV_EXCL_START // LCOV_EXCL_START
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
// MP4 dumps functions. // MP4 dumps functions.

View file

@ -405,12 +405,14 @@ srs_error_t SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMes
// use vod stream for .flv/.fhv // use vod stream for .flv/.fhv
if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) { if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) {
return serve_flv_file(w, r, fullpath); return serve_flv_file(w, r, fullpath);
} else if (srs_string_ends_with(fullpath, ".mp4")) {
return serve_mp4_file(w, r, fullpath);
} else if (srs_string_ends_with(upath, ".m3u8")) { } else if (srs_string_ends_with(upath, ".m3u8")) {
return serve_m3u8_file(w, r, fullpath); return serve_m3u8_file(w, r, fullpath);
} else if (srs_string_ends_with(upath, ".ts")) { } else if (srs_string_ends_with(upath, ".ts") ||
srs_string_ends_with(upath, ".m4s") ||
srs_path_basename(upath) == "init.mp4") {
return serve_ts_file(w, r, fullpath); return serve_ts_file(w, r, fullpath);
} else if (srs_string_ends_with(fullpath, ".mp4")) {
return serve_mp4_file(w, r, fullpath);
} }
// serve common static file. // serve common static file.

View file

@ -352,6 +352,7 @@ private:
virtual srs_error_t serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
virtual srs_error_t serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
virtual srs_error_t serve_m3u8_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_m3u8_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
// the ts file including: .ts .m4s init.mp4
virtual srs_error_t serve_ts_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_ts_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
protected: protected:
// When access flv file with x.flv?start=xxx // When access flv file with x.flv?start=xxx
@ -371,6 +372,7 @@ protected:
// Remark 2: // Remark 2:
// If use two same "hls_ctx" in different requests, SRS cannot detect so that they will be treated as one. // If use two same "hls_ctx" in different requests, SRS cannot detect so that they will be treated as one.
virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_m3u8_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
// the ts file including: .ts .m4s init.mp4
virtual srs_error_t serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath); virtual srs_error_t serve_ts_ctx(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
protected: protected:
// Copy the fs to response writer in size bytes. // Copy the fs to response writer in size bytes.

View file

@ -3732,12 +3732,14 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5)
if (true) { if (true) {
MockSrsConfig conf; MockSrsConfig conf;
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{hls{hls_keys on;hls_fragments_per_key 5;hls_key_file xxx;hls_key_file_path xxx2;hls_key_url xxx3;}}")); HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "vhost ossrs.net{hls{hls_keys on;hls_fragments_per_key 5;hls_key_file xxx;hls_key_file_path xxx2;hls_key_url xxx3;hls_use_fmp4 on;hls_fmp4_file xx.m4s;}}"));
EXPECT_TRUE(conf.get_hls_keys("ossrs.net")); EXPECT_TRUE(conf.get_hls_keys("ossrs.net"));
EXPECT_EQ(5, conf.get_hls_fragments_per_key("ossrs.net")); EXPECT_EQ(5, conf.get_hls_fragments_per_key("ossrs.net"));
EXPECT_STREQ("xxx", conf.get_hls_key_file("ossrs.net").c_str()); EXPECT_STREQ("xxx", conf.get_hls_key_file("ossrs.net").c_str());
EXPECT_STREQ("xxx2", conf.get_hls_key_file_path("ossrs.net").c_str()); EXPECT_STREQ("xxx2", conf.get_hls_key_file_path("ossrs.net").c_str());
EXPECT_STREQ("xxx3", conf.get_hls_key_url("ossrs.net").c_str()); EXPECT_STREQ("xxx3", conf.get_hls_key_url("ossrs.net").c_str());
EXPECT_TRUE(conf.get_hls_use_fmp4("ossrs.net"));
EXPECT_STREQ("xx.m4s", conf.get_hls_fmp4_file("ossrs.net").c_str());
} }
if (true) { if (true) {
@ -5046,6 +5048,18 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHls)
SrsSetEnvConfig(hls_dts_directly, "SRS_VHOST_HLS_HLS_DTS_DIRECTLY", "off"); SrsSetEnvConfig(hls_dts_directly, "SRS_VHOST_HLS_HLS_DTS_DIRECTLY", "off");
EXPECT_FALSE(conf.get_vhost_hls_dts_directly("__defaultVhost__")); EXPECT_FALSE(conf.get_vhost_hls_dts_directly("__defaultVhost__"));
SrsSetEnvConfig(hls_use_fmp4_on, "SRS_VHOST_HLS_HLS_USE_FMP4", "on");
EXPECT_TRUE(conf.get_hls_use_fmp4("__defaultVhost__"));
SrsSetEnvConfig(hls_use_fmp4_off, "SRS_VHOST_HLS_HLS_USE_FMP4", "off");
EXPECT_FALSE(conf.get_hls_use_fmp4("__defaultVhost__"));
SrsSetEnvConfig(hls_use_fmp4_unexpected, "SRS_VHOST_HLS_HLS_USE_FMP4", "xx");
EXPECT_FALSE(conf.get_hls_use_fmp4("__defaultVhost__"));
SrsSetEnvConfig(hls_fmp4_file, "SRS_VHOST_HLS_HLS_FMP4_FILE", "xxx.m4s");
EXPECT_STREQ("xxx.m4s", conf.get_hls_fmp4_file("__defaultVhost__").c_str());
} }
} }

View file

@ -898,11 +898,10 @@ VOID TEST(KernelMp4Test, TREXBox)
} }
SrsMp4MovieExtendsBox box; SrsMp4MovieExtendsBox box;
EXPECT_TRUE(NULL == box.trex());
SrsMp4TrackExtendsBox* trex = new SrsMp4TrackExtendsBox(); SrsMp4TrackExtendsBox* trex = new SrsMp4TrackExtendsBox();
box.set_trex(trex); box.add_trex(trex);
EXPECT_TRUE(trex == box.trex()); EXPECT_TRUE(trex == box.get(SrsMp4BoxTypeTREX));
} }
VOID TEST(KernelMp4Test, TKHDBox) VOID TEST(KernelMp4Test, TKHDBox)