mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Merge 62a544a3fd
into 93cba246bc
This commit is contained in:
commit
9f83d62ab8
14 changed files with 3187 additions and 115 deletions
|
@ -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
26
trunk/conf/hls.mp4.conf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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.
|
||||||
|
|
|
@ -405,13 +405,15 @@ 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.
|
||||||
return serve_file(w, r, fullpath);
|
return serve_file(w, r, fullpath);
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue