diff --git a/scripts/installCommonDeps.sh b/scripts/installCommonDeps.sh index 555d99527..0db664d2e 100644 --- a/scripts/installCommonDeps.sh +++ b/scripts/installCommonDeps.sh @@ -94,6 +94,12 @@ install_ffmpeg(){ fi rm -fr ${DIR} tar xf ${SRC} + + # Apply patch + pushd ${DIR} + patch -p0 < $PATHNAME/patches/ffmpeg-init-prog-datetime.patch + popd + pushd ${DIR} [[ "${DISABLE_NONFREE}" == "true" ]] && \ PKG_CONFIG_PATH=${PREFIX_DIR}/lib/pkgconfig CFLAGS=-fPIC ./configure --prefix=${PREFIX_DIR} --enable-shared --disable-static --disable-libvpx --disable-vaapi --enable-libfreetype --enable-libsrt || \ diff --git a/scripts/patches/ffmpeg-init-prog-datetime.patch b/scripts/patches/ffmpeg-init-prog-datetime.patch new file mode 100644 index 000000000..bd5d1557a --- /dev/null +++ b/scripts/patches/ffmpeg-init-prog-datetime.patch @@ -0,0 +1,35 @@ +diff -Naur ffmpeg-4.1.3/libavformat/hlsenc.c ffmpeg-patched/libavformat/hlsenc.c +--- libavformat/hlsenc.c 2019-04-01 19:16:31.000000000 +0800 ++++ libavformat/hlsenc.c 2022-03-29 15:00:13.432493656 +0800 +@@ -227,6 +227,7 @@ + AVIOContext *m3u8_out; + AVIOContext *sub_m3u8_out; + int64_t timeout; ++ float init_prog_date_time; // Set by a private option. + } HLSContext; + + static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, +@@ -1347,6 +1348,7 @@ + double *prog_date_time_p = (hls->flags & HLS_PROGRAM_DATE_TIME) ? &prog_date_time : NULL; + int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); + ++ + hls->version = 3; + if (byterange_mode) { + hls->version = 4; +@@ -2566,6 +2568,7 @@ + time_t now0; + time(&now0); + vs->initial_prog_date_time = now0; ++ vs->initial_prog_date_time = hls->init_prog_date_time; + } + if (hls->format_options_str) { + ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); +@@ -2804,6 +2807,7 @@ + static const AVOption options[] = { + {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, + {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, ++ {"hls_init_prog_date_time", "set initial prog_date_time", OFFSET(init_prog_date_time), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0, FLT_MAX, E}, + {"hls_init_time", "set segment length in seconds at init list", OFFSET(init_time), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0, FLT_MAX, E}, + {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, + {"hls_delete_threshold", "set number of unreferenced segments to keep before deleting", OFFSET(hls_delete_threshold), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E}, diff --git a/source/core/owt_base/LiveStreamOut.cpp b/source/core/owt_base/LiveStreamOut.cpp index 0d4c44e1a..109a2b29f 100644 --- a/source/core/owt_base/LiveStreamOut.cpp +++ b/source/core/owt_base/LiveStreamOut.cpp @@ -16,6 +16,7 @@ DEFINE_LOGGER(LiveStreamOut, "owt.LiveStreamOut"); LiveStreamOut::LiveStreamOut(const std::string& url, bool hasAudio, bool hasVideo, EventRegistry* handle, int streamingTimeout, StreamingOptions& options) : AVStreamOut(url, hasAudio, hasVideo, handle, streamingTimeout) , m_options(options) + , m_startTimeStamp(0) { switch(m_options.format) { case STREAMING_FORMAT_RTSP: @@ -55,6 +56,15 @@ LiveStreamOut::~LiveStreamOut() close(); } +void LiveStreamOut::onFrame(const Frame& frame) +{ + if (m_startTimeStamp == 0) { + m_startTimeStamp = frame.timeStamp; + ELOG_WARN("Setting start timestamp %u", m_startTimeStamp); + } + AVStreamOut::onFrame(frame); +} + bool LiveStreamOut::isAudioFormatSupported(FrameFormat format) { switch (format) { @@ -123,10 +133,16 @@ bool LiveStreamOut::getHeaderOpt(std::string& url, AVDictionary **options) av_dict_set(options, "hls_segment_filename", segment_uri.c_str(), 0); av_dict_set(options, "hls_flags", "delete_segments", 0); + av_dict_set(options, "hls_flags", "program_date_time", 1); av_dict_set_int(options, "hls_time", m_options.hls_time, 0); av_dict_set_int(options, "hls_list_size", m_options.hls_list_size, 0); + if (m_startTimeStamp > 0) { + av_dict_set_int(options, "hls_init_prog_date_time", + m_startTimeStamp, 0); + m_startTimeStamp = 0; + } if (url.find("http://") == 0 || url.find("https://") == 0) { av_dict_set(options, "method", m_options.hls_method, 0); diff --git a/source/core/owt_base/LiveStreamOut.h b/source/core/owt_base/LiveStreamOut.h index 02a533e2b..dec9f52d5 100644 --- a/source/core/owt_base/LiveStreamOut.h +++ b/source/core/owt_base/LiveStreamOut.h @@ -46,6 +46,7 @@ class LiveStreamOut : public AVStreamOut { LiveStreamOut(const std::string& url, bool hasAudio, bool hasVideo, EventRegistry* handle, int streamingTimeout, StreamingOptions& options); ~LiveStreamOut(); + void onFrame(const Frame&) override; protected: bool isAudioFormatSupported(FrameFormat format) override; bool isVideoFormatSupported(FrameFormat format) override; @@ -57,6 +58,7 @@ class LiveStreamOut : public AVStreamOut { private: StreamingOptions m_options; + uint32_t m_startTimeStamp; }; }