[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Remove muxer overhead from Bandwidth field in DASH manifest
From: Karthick Jeyapal Fixes bug id #7386 Muxer overhead calculations was intented for HLS playlist as Apple's mediastreamvalidator tests were failing. But applying the same fix for DASH manifest proved counterproductive, as Bandwidth can be used for segment name templates. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index c36ab12..2564b9e 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -611,7 +611,7 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind if (os->bit_rate > 0) snprintf(bandwidth_str, sizeof(bandwidth_str), " bandwidth=\"%d\"", - os->bit_rate + os->muxer_overhead); + os->bit_rate); if (as->media_type == AVMEDIA_TYPE_VIDEO) { AVStream *st = s->streams[i]; -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] avformat/dashenc: Add CODECS tag to HLS master playlist
From: Karthick Jeyapal --- libavformat/dashenc.c | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index ae57fd5..c36ab12 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -864,6 +864,7 @@ static int write_manifest(AVFormatContext *s, int final) if (c->hls_playlist && !c->master_playlist_created) { char filename_hls[1024]; const char *audio_group = "A1"; +const char audio_codec_str[128] = "\0"; int is_default = 1; int max_audio_bitrate = 0; @@ -895,21 +896,31 @@ static int write_manifest(AVFormatContext *s, int final) playlist_file, i, is_default); max_audio_bitrate = FFMAX(st->codecpar->bit_rate + os->muxer_overhead, max_audio_bitrate); +if (!av_strnstr(audio_codec_str, os->codec_str, sizeof(audio_codec_str))) { +if (strlen(audio_codec_str)) +av_strlcat(audio_codec_str, ",", sizeof(audio_codec_str)); +av_strlcat(audio_codec_str, os->codec_str, sizeof(audio_codec_str)); +} is_default = 0; } for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; +char codec_str[128]; AVStream *st = s->streams[i]; OutputStream *os = &c->streams[i]; char *agroup = NULL; int stream_bitrate = st->codecpar->bit_rate + os->muxer_overhead; +av_strlcpy(codec_str, os->codec_str, sizeof(codec_str)); if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && max_audio_bitrate) { agroup = (char *)audio_group; stream_bitrate += max_audio_bitrate; +av_strlcat(codec_str, ",", sizeof(codec_str)); +av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); } get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_stream_info(st, out, stream_bitrate, playlist_file, agroup, NULL, NULL); +ff_hls_write_stream_info(st, out, stream_bitrate, playlist_file, agroup, + codec_str, NULL); } avio_close(out); if (use_rename) -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Reduce Muxing overhead for chunked CMAF format
From: Karthick Jeyapal SIDX atom being inserted for every MOOF atom increases the muxing overhead. This behaviour can be disabled for chunked CMAF format by enabling Global SIDX option of mov muxer. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 87e31e2..9a33321 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1065,7 +1065,7 @@ static int dash_init(AVFormatContext *s) if (c->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) -av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov", 0); +av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0); else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); } else { -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Format VP9 level as decimal instead of hexadecimal
From: Karthick Jeyapal Commit ID 63c69d51c7532fb6c2460076329b50ec51a0f290 fixed the bug in vpcc, get_vp9_level() function, causing this change. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 9a33321..f429ebc 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -211,7 +211,7 @@ static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, VPCC vpcc; int ret = ff_isom_get_vpcc_features(s, par, frame_rate, &vpcc); if (ret == 0) { -av_strlcatf(str, size, "vp09.%02x.%02x.%02x", +av_strlcatf(str, size, "vp09.%02x.%02d.%02x", vpcc.profile, vpcc.level, vpcc.bitdepth); } else { // Default to just vp9 in case of error while finding out profile or level -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Format VP9 bitdepth as decimal instead of Hexadecimal
From: Karthick Jeyapal For example bitdepth should be printed as 10 instead of 0A. Thanks to Hendrik Leppkes for pointing this out --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index f429ebc..1a201c3 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -211,7 +211,7 @@ static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, VPCC vpcc; int ret = ff_isom_get_vpcc_features(s, par, frame_rate, &vpcc); if (ret == 0) { -av_strlcatf(str, size, "vp09.%02x.%02d.%02x", +av_strlcatf(str, size, "vp09.%02x.%02d.%02d", vpcc.profile, vpcc.level, vpcc.bitdepth); } else { // Default to just vp9 in case of error while finding out profile or level -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] avformat/dashenc: Format VP9 level as decimal instead of hexadecimal
From: Karthick Jeyapal Commit ID 63c69d51c7532fb6c2460076329b50ec51a0f290 fixed the bug in vpcc, get_vp9_level() function, causing this change. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 9a33321..f429ebc 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -211,7 +211,7 @@ static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, VPCC vpcc; int ret = ff_isom_get_vpcc_features(s, par, frame_rate, &vpcc); if (ret == 0) { -av_strlcatf(str, size, "vp09.%02x.%02x.%02x", +av_strlcatf(str, size, "vp09.%02x.%02d.%02x", vpcc.profile, vpcc.level, vpcc.bitdepth); } else { // Default to just vp9 in case of error while finding out profile or level -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avdevice/decklink: Add option to align Capture start time
From: Karthick Jeyapal This option is useful for maintaining input synchronization across N different hardware devices deployed for 'N-way' redundancy. The system time of different hardware devices should be synchronized with protocols such as NTP or PTP, before using this option. --- doc/indevs.texi | 10 ++ libavdevice/decklink_common_c.h | 1 + libavdevice/decklink_dec.cpp| 20 libavdevice/decklink_dec_c.c| 1 + 4 files changed, 32 insertions(+) diff --git a/doc/indevs.texi b/doc/indevs.texi index ed2784b..dfd530a 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -371,6 +371,16 @@ If set to @option{true}, timestamps are forwarded as they are without removing the initial offset. Defaults to @option{false}. +@item timestamp_align +Capture start time alignment in seconds. If set to nonzero, input frames are +dropped till the system timestamp aligns with configured value. +Alignment difference of upto one frame duration is tolerated. +This is useful for maintaining input synchronization across N different +hardware devices deployed for 'N-way' redundancy. The system time of different +hardware devices should be synchronized with protocols such as NTP or PTP, +before using this option. +Defaults to @samp{0}. + @end table @subsection Examples diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index 32a5d70..c4a8985 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -56,6 +56,7 @@ struct decklink_cctx { int raw_format; int64_t queue_size; int copyts; +int timestamp_align; }; #endif /* AVDEVICE_DECKLINK_COMMON_C_H */ diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 7fabef2..24f5ca9 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -703,6 +703,26 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( return S_OK; } +// Drop the frames till system's timestamp aligns with the configured value. +if (0 == ctx->frameCount && cctx->timestamp_align) { +int64_t current_time_us = av_gettime(); +int64_t align_factor_us = (cctx->timestamp_align * 100); +int remainder = current_time_us % align_factor_us; +if (videoFrame) { +videoFrame->GetStreamTime(&frameTime, &frameDuration, 100); +} else if (audioFrame) { +long sample_count = audioFrame->GetSampleFrameCount(); +frameDuration = (long)(sample_count * 100) / bmdAudioSampleRate48kHz; +} else { +frameDuration = 0; +} +// threshold of one frameDuration +if(remainder > frameDuration) { +++ctx->dropped; +return S_OK; +} +} + ctx->frameCount++; if (ctx->audio_pts_source == PTS_SRC_WALLCLOCK || ctx->video_pts_source == PTS_SRC_WALLCLOCK) wallclock = av_gettime_relative(); diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index 6ab3819..bef9c14 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -84,6 +84,7 @@ static const AVOption options[] = { { "queue_size","input queue buffer size", OFFSET(queue_size), AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024 * 1024)}, 0, INT64_MAX, DEC }, { "audio_depth", "audio bitdepth (16 or 32)", OFFSET(audio_depth), AV_OPT_TYPE_INT, { .i64 = 16}, 16, 32, DEC }, { "decklink_copyts", "copy timestamps, do not remove the initial offset", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, +{ "timestamp_align", "Capture start time alignment (in seconds)", OFFSET(timestamp_align), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC }, { NULL }, }; -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avdevice/decklink: Add option to align Capture start time
From: Karthick Jeyapal This option is useful for maintaining input synchronization across N different hardware devices deployed for 'N-way' redundancy. The system time of different hardware devices should be synchronized with protocols such as NTP or PTP, before using this option. --- doc/indevs.texi | 13 + libavdevice/decklink_common_c.h | 1 + libavdevice/decklink_dec.cpp| 11 +++ libavdevice/decklink_dec_c.c| 1 + 4 files changed, 26 insertions(+) diff --git a/doc/indevs.texi b/doc/indevs.texi index ed2784b..694bac9 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -371,6 +371,19 @@ If set to @option{true}, timestamps are forwarded as they are without removing the initial offset. Defaults to @option{false}. +@item timestamp_align +Capture start time alignment in seconds. If set to nonzero, input frames are +dropped till the system timestamp aligns with configured value. +Alignment difference of upto one frame duration is tolerated. +This is useful for maintaining input synchronization across N different +hardware devices deployed for 'N-way' redundancy. The system time of different +hardware devices should be synchronized with protocols such as NTP or PTP, +before using this option. +Note that this method not foolproof. In some border cases input synchronization +may not happen due to thread scheduling jitters in the OS. Either sync could go +wrong by 1 frame or in a rarer case by even @option{timestamp_align} seconds. +Defaults to @samp{0}. + @end table @subsection Examples diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index 32a5d70..8e3bbeb 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -56,6 +56,7 @@ struct decklink_cctx { int raw_format; int64_t queue_size; int copyts; +int64_t timestamp_align; }; #endif /* AVDEVICE_DECKLINK_COMMON_C_H */ diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 7fabef2..1f4d68b 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -703,6 +703,17 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( return S_OK; } +// Drop the frames till system's timestamp aligns with the configured value. +if (0 == ctx->frameCount && cctx->timestamp_align) { +AVRational remainder = av_make_q(av_gettime() % cctx->timestamp_align, 100); +AVRational frame_duration = av_make_q(ctx->video_st->r_frame_rate.den, + ctx->video_st->r_frame_rate.num); +if (av_cmp_q(remainder, frame_duration) > 0) { +++ctx->dropped; +return S_OK; +} +} + ctx->frameCount++; if (ctx->audio_pts_source == PTS_SRC_WALLCLOCK || ctx->video_pts_source == PTS_SRC_WALLCLOCK) wallclock = av_gettime_relative(); diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index 6ab3819..2e6fcb6 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -84,6 +84,7 @@ static const AVOption options[] = { { "queue_size","input queue buffer size", OFFSET(queue_size), AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024 * 1024)}, 0, INT64_MAX, DEC }, { "audio_depth", "audio bitdepth (16 or 32)", OFFSET(audio_depth), AV_OPT_TYPE_INT, { .i64 = 16}, 16, 32, DEC }, { "decklink_copyts", "copy timestamps, do not remove the initial offset", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, +{ "timestamp_align", "Capture start time alignment (in seconds)", OFFSET(timestamp_align), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, DEC }, { NULL }, }; -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Support HTTP Persistent for master.mu8 as well
--- libavformat/dashenc.c | 11 ++- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 3f5f290e25..15b84a0f3b 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -878,14 +878,14 @@ static int write_manifest(AVFormatContext *s, int final) snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", filename_hls); set_http_options(&opts, c); -ret = avio_open2(&out, temp_filename, AVIO_FLAG_WRITE, NULL, &opts); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename, &opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } av_dict_free(&opts); -ff_hls_write_playlist_version(out, 7); +ff_hls_write_playlist_version(c->m3u8_out, 7); for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; @@ -894,7 +894,7 @@ static int write_manifest(AVFormatContext *s, int final) if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_audio_rendition(out, (char *)audio_group, +ff_hls_write_audio_rendition(c->m3u8_out, (char *)audio_group, playlist_file, i, is_default); max_audio_bitrate = FFMAX(st->codecpar->bit_rate + os->muxer_overhead, max_audio_bitrate); @@ -923,10 +923,11 @@ static int write_manifest(AVFormatContext *s, int final) av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); } get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_stream_info(st, out, stream_bitrate, playlist_file, agroup, +ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, + playlist_file, agroup, codec_str, NULL); } -avio_close(out); +dashenc_io_close(s, &c->m3u8_out, temp_filename); if (use_rename) if ((ret = avpriv_io_move(temp_filename, filename_hls)) < 0) return ret; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/dashenc: Support HTTP Persistent for master.m3u8 as well
--- libavformat/dashenc.c | 11 ++- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 3f5f290e25..15b84a0f3b 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -878,14 +878,14 @@ static int write_manifest(AVFormatContext *s, int final) snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", filename_hls); set_http_options(&opts, c); -ret = avio_open2(&out, temp_filename, AVIO_FLAG_WRITE, NULL, &opts); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename, &opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } av_dict_free(&opts); -ff_hls_write_playlist_version(out, 7); +ff_hls_write_playlist_version(c->m3u8_out, 7); for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; @@ -894,7 +894,7 @@ static int write_manifest(AVFormatContext *s, int final) if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_audio_rendition(out, (char *)audio_group, +ff_hls_write_audio_rendition(c->m3u8_out, (char *)audio_group, playlist_file, i, is_default); max_audio_bitrate = FFMAX(st->codecpar->bit_rate + os->muxer_overhead, max_audio_bitrate); @@ -923,10 +923,11 @@ static int write_manifest(AVFormatContext *s, int final) av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); } get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_stream_info(st, out, stream_bitrate, playlist_file, agroup, +ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, + playlist_file, agroup, codec_str, NULL); } -avio_close(out); +dashenc_io_close(s, &c->m3u8_out, temp_filename); if (use_rename) if ((ret = avpriv_io_move(temp_filename, filename_hls)) < 0) return ret; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Support HTTP Persistent for master.mu8 as well
--- libavformat/dashenc.c | 11 ++- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 3f5f290e25..15b84a0f3b 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -878,14 +878,14 @@ static int write_manifest(AVFormatContext *s, int final) snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", filename_hls); set_http_options(&opts, c); -ret = avio_open2(&out, temp_filename, AVIO_FLAG_WRITE, NULL, &opts); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename, &opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } av_dict_free(&opts); -ff_hls_write_playlist_version(out, 7); +ff_hls_write_playlist_version(c->m3u8_out, 7); for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; @@ -894,7 +894,7 @@ static int write_manifest(AVFormatContext *s, int final) if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_audio_rendition(out, (char *)audio_group, +ff_hls_write_audio_rendition(c->m3u8_out, (char *)audio_group, playlist_file, i, is_default); max_audio_bitrate = FFMAX(st->codecpar->bit_rate + os->muxer_overhead, max_audio_bitrate); @@ -923,10 +923,11 @@ static int write_manifest(AVFormatContext *s, int final) av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); } get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_stream_info(st, out, stream_bitrate, playlist_file, agroup, +ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, + playlist_file, agroup, codec_str, NULL); } -avio_close(out); +dashenc_io_close(s, &c->m3u8_out, temp_filename); if (use_rename) if ((ret = avpriv_io_move(temp_filename, filename_hls)) < 0) return ret; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: URL close unconditionally after DELETE segments
Fixes bug with HTTP DELETE when HTTP Persistent is ON. Right now, HTTP Persistent connections is supported only for POSTs and PUTs. HTTP DELETE will still open a new connection every time. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 15b84a0f3b..b0a59af3ee 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1253,7 +1253,7 @@ static void dashenc_delete_file(AVFormatContext *s, char *filename) { } av_dict_free(&http_opts); -dashenc_io_close(s, &out, filename); +ff_format_io_close(s, &out); } else if (unlink(filename) < 0) { av_log(s, AV_LOG_ERROR, "failed to delete %s: %s\n", filename, strerror(errno)); } -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Support HTTP persistent for init segments as well
--- libavformat/dashenc.c | 7 +-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index b0a59af3ee..4e2ea2ebf2 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -355,8 +355,11 @@ static int flush_init_segment(AVFormatContext *s, OutputStream *os) return ret; os->pos = os->init_range_length = range_length; -if (!c->single_file) -ff_format_io_close(s, &os->out); +if (!c->single_file) { +char filename[1024]; +snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile); +dashenc_io_close(s, &os->out, filename); +} return 0; } -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Disable writing CODECS tag for HEVC streams
For HEVC streams, only the FourCC tag is written without profile, level etc., This is breaking playout support in native Safari. Native Safari playout expects the full info in CODECS tag or None at all. --- libavformat/dashenc.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 4e2ea2ebf2..f8b3d106d5 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -915,6 +915,7 @@ static int write_manifest(AVFormatContext *s, int final) AVStream *st = s->streams[i]; OutputStream *os = &c->streams[i]; char *agroup = NULL; +char *codec_str_ptr = NULL; int stream_bitrate = st->codecpar->bit_rate + os->muxer_overhead; if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) continue; @@ -925,10 +926,13 @@ static int write_manifest(AVFormatContext *s, int final) av_strlcat(codec_str, ",", sizeof(codec_str)); av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); } +if (st->codecpar->codec_id != AV_CODEC_ID_HEVC) { +codec_str_ptr = codec_str; +} get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); ff_hls_write_stream_info(st, c->m3u8_out, stream_bitrate, playlist_file, agroup, - codec_str, NULL); + codec_str_ptr, NULL); } dashenc_io_close(s, &c->m3u8_out, temp_filename); if (use_rename) -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/http : Added check for valid URL context before calling shutdown
--- libavformat/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/http.c b/libavformat/http.c index 3a35bc7eac..240304f6e6 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -1650,7 +1650,7 @@ static int http_close(URLContext *h) av_freep(&s->inflate_buffer); #endif /* CONFIG_ZLIB */ -if (!s->end_chunked_post) +if (s->hd && !s->end_chunked_post) /* Close the write direction by sending the end of chunked encoding. */ ret = http_shutdown(h, h->flags); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc : Handled error from ff_http_do_new_request() cleanly
--- libavformat/dashenc.c | 10 -- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index d151921175..2c1cce0c92 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -168,6 +168,8 @@ static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, URLContext *http_url_context = ffio_geturlcontext(*pb); av_assert0(http_url_context); err = ff_http_do_new_request(http_url_context, filename); +if (err < 0) +ff_format_io_close(s, pb); #endif } return err; @@ -177,6 +179,9 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam DASHContext *c = s->priv_data; int http_base_proto = filename ? ff_is_http_proto(filename) : 0; +if (!*pb) +return; + if (!http_base_proto || !c->http_persistent) { ff_format_io_close(s, pb); #if CONFIG_HTTP_PROTOCOL @@ -318,7 +323,8 @@ static int flush_dynbuf(OutputStream *os, int *range_length) // write out to file *range_length = avio_close_dyn_buf(os->ctx->pb, &buffer); os->ctx->pb = NULL; -avio_write(os->out, buffer + os->written_len, *range_length - os->written_len); +if (os->out) +avio_write(os->out, buffer + os->written_len, *range_length - os->written_len); os->written_len = 0; av_free(buffer); @@ -1496,9 +1502,9 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) use_rename ? "%s.tmp" : "%s", os->full_path); set_http_options(&opts, c); ret = dashenc_io_open(s, &os->out, os->temp_path, &opts); +av_dict_free(&opts); if (ret < 0) return ret; -av_dict_free(&opts); } //write out the data immediately in streaming mode -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc: Handled the error from dashenc_io_open()
--- libavformat/dashenc.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 6ce70e0076..2f403257c0 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -527,8 +527,12 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); set_http_options(&http_opts, c); -dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); av_dict_free(&http_opts); +if (ret < 0) { +av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename_hls); +return; +} for (i = start_index; i < os->nb_segments; i++) { Segment *seg = os->segments[i]; double duration = (double) seg->duration / timescale; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Added an option to ignore io errors
When dashenc has to run for long duration(say 24x7 live stream), one can enable this option to ignore the io failure of few segment's upload due to an intermittent network issues. When the network connection recovers dashenc will continue with the upload of the current segments, leading to the recovery of the stream. --- doc/muxers.texi | 3 +++ libavformat/dashenc.c | 26 -- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index a02ac01b55..f1cc6f5fee 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -300,6 +300,9 @@ If this flag is set, the dash segment files will be in in ISOBMFF format. @item webm If this flag is set, the dash segment files will be in in WebM format. +@item -ignore_io_errors @var{ignore_io_errors} +Ignore IO errors during open and write. Useful for long-duration runs with network output. + @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 2f403257c0..92b09417df 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -138,6 +138,7 @@ typedef struct DASHContext { int index_correction; char *format_options_str; SegmentType segment_type_option; /* segment type as specified in options */ +int ignore_io_errors; } DASHContext; static struct codec_string { @@ -846,7 +847,10 @@ static int write_manifest(AVFormatContext *s, int final) av_dict_free(&opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); -return ret; +if (c->ignore_io_errors) +return 0; +else +return ret; } out = c->mpd_out; avio_printf(out, "\n"); @@ -937,7 +941,10 @@ static int write_manifest(AVFormatContext *s, int final) av_dict_free(&opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); -return ret; +if (c->ignore_io_errors) +return 0; +else +return ret; } ff_hls_write_playlist_version(c->m3u8_out, 7); @@ -1565,8 +1572,12 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) set_http_options(&opts, c); ret = dashenc_io_open(s, &os->out, os->temp_path, &opts); av_dict_free(&opts); -if (ret < 0) -return ret; +if (ret < 0) { +if (c->ignore_io_errors) +return 0; +else +return ret; +} } //write out the data immediately in streaming mode @@ -1577,9 +1588,11 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) write_styp(os->ctx->pb); avio_flush(os->ctx->pb); len = avio_get_dyn_buf (os->ctx->pb, &buf); -avio_write(os->out, buf + os->written_len, len - os->written_len); +if (os->out) { +avio_write(os->out, buf + os->written_len, len - os->written_len); +avio_flush(os->out); +} os->written_len = len; -avio_flush(os->out); } return ret; @@ -1670,6 +1683,7 @@ static const AVOption options[] = { { "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_AUTO }, 0, UINT_MAX, E, "segment_type"}, { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX, E, "segment_type"}, { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, "segment_type"}, +{ "ignore_io_errors", "Ignore IO errors during open and write. Useful for long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { NULL }, }; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/movenc : Don't write sidx for empty urls
When movenc is used by other segmenting muxers such as dashenc, url field is always empty. In such cases it is better to not write sidx, instead of throwing errors. --- libavformat/movenc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6dab5193b0..150a505a4a 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -6706,6 +6706,9 @@ static int mov_write_trailer(AVFormatContext *s) mov->tracks[i].data_offset = 0; if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) { int64_t end; +// If url is an empty string("") don't write sidx atom. +if (s->url[0] == '\0') +return res; av_log(s, AV_LOG_INFO, "Starting second pass: inserting sidx atoms\n"); res = shift_data(s); if (res < 0) -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/movenc : Don't write sidx for empty urls
When movenc is used by other segmenting muxers such as dashenc, url field is always empty. In such cases it is better to not write sidx, instead of throwing errors. --- libavformat/movenc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6dab5193b0..d47ecc65ca 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -6706,6 +6706,9 @@ static int mov_write_trailer(AVFormatContext *s) mov->tracks[i].data_offset = 0; if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) { int64_t end; +// If url is an empty string("") don't write sidx atom. +if (s->url[0] == '\0') +return 0; av_log(s, AV_LOG_INFO, "Starting second pass: inserting sidx atoms\n"); res = shift_data(s); if (res < 0) -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] avformat/dashenc: Handled the error from dashenc_io_open()
--- libavformat/dashenc.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 6ce70e0076..2f403257c0 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -527,8 +527,12 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); set_http_options(&http_opts, c); -dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); av_dict_free(&http_opts); +if (ret < 0) { +av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename_hls); +return; +} for (i = start_index; i < os->nb_segments; i++) { Segment *seg = os->segments[i]; double duration = (double) seg->duration / timescale; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Added an option to ignore io errors
When dashenc has to run for long duration(say 24x7 live stream), one can enable this option to ignore the io failure of few segment's upload due to an intermittent network issues. When the network connection recovers dashenc will continue with the upload of the current segments, leading to the recovery of the stream. --- doc/muxers.texi | 3 +++ libavformat/dashenc.c | 17 +++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index a02ac01b55..f1cc6f5fee 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -300,6 +300,9 @@ If this flag is set, the dash segment files will be in in ISOBMFF format. @item webm If this flag is set, the dash segment files will be in in WebM format. +@item -ignore_io_errors @var{ignore_io_errors} +Ignore IO errors during open and write. Useful for long-duration runs with network output. + @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 2f403257c0..04218af6a6 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -138,6 +138,7 @@ typedef struct DASHContext { int index_correction; char *format_options_str; SegmentType segment_type_option; /* segment type as specified in options */ +int ignore_io_errors; } DASHContext; static struct codec_string { @@ -846,7 +847,7 @@ static int write_manifest(AVFormatContext *s, int final) av_dict_free(&opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); -return ret; +return c->ignore_io_errors ? 0 : ret; } out = c->mpd_out; avio_printf(out, "\n"); @@ -937,7 +938,7 @@ static int write_manifest(AVFormatContext *s, int final) av_dict_free(&opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); -return ret; +return c->ignore_io_errors ? 0 : ret; } ff_hls_write_playlist_version(c->m3u8_out, 7); @@ -1565,8 +1566,9 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) set_http_options(&opts, c); ret = dashenc_io_open(s, &os->out, os->temp_path, &opts); av_dict_free(&opts); -if (ret < 0) -return ret; +if (ret < 0) { +return c->ignore_io_errors ? 0 : ret; +} } //write out the data immediately in streaming mode @@ -1577,9 +1579,11 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) write_styp(os->ctx->pb); avio_flush(os->ctx->pb); len = avio_get_dyn_buf (os->ctx->pb, &buf); -avio_write(os->out, buf + os->written_len, len - os->written_len); +if (os->out) { +avio_write(os->out, buf + os->written_len, len - os->written_len); +avio_flush(os->out); +} os->written_len = len; -avio_flush(os->out); } return ret; @@ -1670,6 +1674,7 @@ static const AVOption options[] = { { "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_AUTO }, 0, UINT_MAX, E, "segment_type"}, { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX, E, "segment_type"}, { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, "segment_type"}, +{ "ignore_io_errors", "Ignore IO errors during open and write. Useful for long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { NULL }, }; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Added proper logging when io_open fails for write
--- libavformat/dashenc.c | 19 +-- 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 04218af6a6..09207dcf44 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -209,6 +209,15 @@ static const char *get_format_str(SegmentType segment_type) { return NULL; } +static int handle_io_open_error(AVFormatContext *s, int err, char *url) { +DASHContext *c = s->priv_data; +char errbuf[AV_ERROR_MAX_STRING_SIZE]; +av_strerror(err, errbuf, sizeof(errbuf)); +av_log(s, c->ignore_io_errors ? AV_LOG_WARNING : AV_LOG_ERROR, + "Unable to open %s for writing: %s\n", url, errbuf); +return c->ignore_io_errors ? 0 : err; +} + static inline SegmentType select_segment_type(SegmentType segment_type, enum AVCodecID codec_id) { if (segment_type == SEGMENT_TYPE_AUTO) { @@ -531,7 +540,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont ret = dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); av_dict_free(&http_opts); if (ret < 0) { -av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename_hls); +handle_io_open_error(s, ret, temp_filename_hls); return; } for (i = start_index; i < os->nb_segments; i++) { @@ -846,8 +855,7 @@ static int write_manifest(AVFormatContext *s, int final) ret = dashenc_io_open(s, &c->mpd_out, temp_filename, &opts); av_dict_free(&opts); if (ret < 0) { -av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); -return c->ignore_io_errors ? 0 : ret; +return handle_io_open_error(s, ret, temp_filename); } out = c->mpd_out; avio_printf(out, "\n"); @@ -937,8 +945,7 @@ static int write_manifest(AVFormatContext *s, int final) ret = dashenc_io_open(s, &c->m3u8_out, temp_filename, &opts); av_dict_free(&opts); if (ret < 0) { -av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); -return c->ignore_io_errors ? 0 : ret; +return handle_io_open_error(s, ret, temp_filename); } ff_hls_write_playlist_version(c->m3u8_out, 7); @@ -1567,7 +1574,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) ret = dashenc_io_open(s, &os->out, os->temp_path, &opts); av_dict_free(&opts); if (ret < 0) { -return c->ignore_io_errors ? 0 : ret; +return handle_io_open_error(s, ret, os->temp_path); } } -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Used the movenc option no_sidx instead of global_sidx
Anyways the intended behaviour was to disable SIDX atom. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 4d9b564a94..84bc58d6c1 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1162,7 +1162,7 @@ static int dash_init(AVFormatContext *s) if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) -av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0); +av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+no_sidx", 0); else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); } else { -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/movenc: Added an option to disable SIDX atom
--- doc/muxers.texi | 2 ++ libavformat/movenc.c | 7 +-- libavformat/movenc.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index f1cc6f5fee..6ca27b04a3 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1325,6 +1325,8 @@ more efficient), but with this option set, the muxer writes one moof/mdat pair for each track, making it easier to separate tracks. This option is implicitly set when writing ismv (Smooth Streaming) files. +@item -movflags no_sidx +Don't write sidx atom. @item -movflags faststart Run a second pass moving the index (moov atom) to the beginning of the file. This operation can take a while, and will not work in various situations such diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6dab5193b0..83278e8bfd 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -75,6 +75,7 @@ static const AVOption options[] = { { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, +{ "no_sidx", "Don't write sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_NO_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_colr", "Write colr atom (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, @@ -4603,7 +4604,8 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, mov_write_moof_tag_internal(avio_buf, mov, tracks, 0); moof_size = ffio_close_null_buf(avio_buf); -if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) +if (mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_NO_SIDX))) mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); if (mov->write_prft > MOV_PRFT_NONE && mov->write_prft < MOV_PRFT_NB) @@ -5422,7 +5424,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) * the next fragment. This means the cts of the first sample must * be the same in all fragments, unless end_pts was updated by * the packet causing the fragment to be written. */ -if ((mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) || +if ((mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_NO_SIDX))) || mov->mode == MODE_ISM) pkt->pts = pkt->dts + trk->end_pts - trk->cluster[trk->entry].dts; } else { diff --git a/libavformat/movenc.h b/libavformat/movenc.h index fe605d1ad2..ee6749bce2 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -257,6 +257,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_SKIP_TRAILER (1 << 18) #define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS (1 << 19) #define FF_MOV_FLAG_FRAG_EVERY_FRAME (1 << 20) +#define FF_MOV_FLAG_NO_SIDX (1 << 21) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] avformat/movenc: Added an option to disable SIDX atom
--- doc/muxers.texi | 4 libavformat/movenc.c | 12 ++-- libavformat/movenc.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index f1cc6f5fee..ca10741900 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1325,6 +1325,10 @@ more efficient), but with this option set, the muxer writes one moof/mdat pair for each track, making it easier to separate tracks. This option is implicitly set when writing ismv (Smooth Streaming) files. +@item -movflags skip_sidx +Skip writing of sidx atom. When bitrate overhead due to sidx atom is high, +this option could be used for cases where sidx atom is not mandatory. +When global_sidx flag is enabled, this option will be ignored. @item -movflags faststart Run a second pass moving the index (moov atom) to the beginning of the file. This operation can take a while, and will not work in various situations such diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6dab5193b0..3781a32895 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -75,6 +75,7 @@ static const AVOption options[] = { { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, +{ "skip_sidx", "Skip writing of sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_colr", "Write colr atom (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, @@ -4603,7 +4604,8 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, mov_write_moof_tag_internal(avio_buf, mov, tracks, 0); moof_size = ffio_close_null_buf(avio_buf); -if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) +if (mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_SKIP_SIDX))) mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); if (mov->write_prft > MOV_PRFT_NONE && mov->write_prft < MOV_PRFT_NB) @@ -5422,7 +5424,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) * the next fragment. This means the cts of the first sample must * be the same in all fragments, unless end_pts was updated by * the packet causing the fragment to be written. */ -if ((mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) || +if ((mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_SKIP_SIDX))) || mov->mode == MODE_ISM) pkt->pts = pkt->dts + trk->end_pts - trk->cluster[trk->entry].dts; } else { @@ -6067,6 +6070,11 @@ static int mov_init(AVFormatContext *s) s->flags &= ~AVFMT_FLAG_AUTO_BSF; } +if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX && s->flags & FF_MOV_FLAG_SKIP_SIDX) { +av_log(s, AV_LOG_WARNING, "Global SIDX enabled; Ignoring skip_sidx option\n"); +s->flags &= ~FF_MOV_FLAG_SKIP_SIDX; +} + if (mov->flags & FF_MOV_FLAG_FASTSTART) { mov->reserved_moov_size = -1; } diff --git a/libavformat/movenc.h b/libavformat/movenc.h index fe605d1ad2..68d6f23a5a 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -257,6 +257,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_SKIP_TRAILER (1 << 18) #define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS (1 << 19) #define FF_MOV_FLAG_FRAG_EVERY_FRAME (1 << 20) +#define FF_MOV_FLAG_SKIP_SIDX (1 << 21) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Used the movenc option skip_sidx instead of global_sidx
Anyways the intended behaviour was to disable SIDX atom. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 4d9b564a94..585b34cb97 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1162,7 +1162,7 @@ static int dash_init(AVFormatContext *s) if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) -av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0); +av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx", 0); else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); } else { -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3] avformat/movenc: Added an option to disable SIDX atom
--- doc/muxers.texi | 4 libavformat/movenc.c | 12 ++-- libavformat/movenc.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index f1cc6f5fee..ca10741900 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1325,6 +1325,10 @@ more efficient), but with this option set, the muxer writes one moof/mdat pair for each track, making it easier to separate tracks. This option is implicitly set when writing ismv (Smooth Streaming) files. +@item -movflags skip_sidx +Skip writing of sidx atom. When bitrate overhead due to sidx atom is high, +this option could be used for cases where sidx atom is not mandatory. +When global_sidx flag is enabled, this option will be ignored. @item -movflags faststart Run a second pass moving the index (moov atom) to the beginning of the file. This operation can take a while, and will not work in various situations such diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6dab5193b0..2f7755bf69 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -75,6 +75,7 @@ static const AVOption options[] = { { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, +{ "skip_sidx", "Skip writing of sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_colr", "Write colr atom (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, @@ -4603,7 +4604,8 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, mov_write_moof_tag_internal(avio_buf, mov, tracks, 0); moof_size = ffio_close_null_buf(avio_buf); -if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) +if (mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_SKIP_SIDX))) mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); if (mov->write_prft > MOV_PRFT_NONE && mov->write_prft < MOV_PRFT_NB) @@ -5422,7 +5424,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) * the next fragment. This means the cts of the first sample must * be the same in all fragments, unless end_pts was updated by * the packet causing the fragment to be written. */ -if ((mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) || +if ((mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_SKIP_SIDX))) || mov->mode == MODE_ISM) pkt->pts = pkt->dts + trk->end_pts - trk->cluster[trk->entry].dts; } else { @@ -6067,6 +6070,11 @@ static int mov_init(AVFormatContext *s) s->flags &= ~AVFMT_FLAG_AUTO_BSF; } +if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX && s->flags & FF_MOV_FLAG_SKIP_SIDX) { +av_log(s, AV_LOG_WARNING, "Global SIDX enabled; Ignoring skip_sidx option\n"); +mov->flags &= ~FF_MOV_FLAG_SKIP_SIDX; +} + if (mov->flags & FF_MOV_FLAG_FASTSTART) { mov->reserved_moov_size = -1; } diff --git a/libavformat/movenc.h b/libavformat/movenc.h index fe605d1ad2..68d6f23a5a 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -257,6 +257,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_SKIP_TRAILER (1 << 18) #define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS (1 << 19) #define FF_MOV_FLAG_FRAG_EVERY_FRAME (1 << 20) +#define FF_MOV_FLAG_SKIP_SIDX (1 << 21) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v4] avformat/movenc: Added an option to disable SIDX atom
--- doc/muxers.texi | 4 libavformat/movenc.c | 12 ++-- libavformat/movenc.h | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index f1cc6f5fee..ca10741900 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1325,6 +1325,10 @@ more efficient), but with this option set, the muxer writes one moof/mdat pair for each track, making it easier to separate tracks. This option is implicitly set when writing ismv (Smooth Streaming) files. +@item -movflags skip_sidx +Skip writing of sidx atom. When bitrate overhead due to sidx atom is high, +this option could be used for cases where sidx atom is not mandatory. +When global_sidx flag is enabled, this option will be ignored. @item -movflags faststart Run a second pass moving the index (moov atom) to the beginning of the file. This operation can take a while, and will not work in various situations such diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 6dab5193b0..28cf8b719c 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -75,6 +75,7 @@ static const AVOption options[] = { { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, +{ "skip_sidx", "Skip writing of sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_colr", "Write colr atom (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, @@ -4603,7 +4604,8 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks, mov_write_moof_tag_internal(avio_buf, mov, tracks, 0); moof_size = ffio_close_null_buf(avio_buf); -if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) +if (mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_SKIP_SIDX))) mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size); if (mov->write_prft > MOV_PRFT_NONE && mov->write_prft < MOV_PRFT_NB) @@ -5422,7 +5424,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) * the next fragment. This means the cts of the first sample must * be the same in all fragments, unless end_pts was updated by * the packet causing the fragment to be written. */ -if ((mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX)) || +if ((mov->flags & FF_MOV_FLAG_DASH && +!(mov->flags & (FF_MOV_FLAG_GLOBAL_SIDX | FF_MOV_FLAG_SKIP_SIDX))) || mov->mode == MODE_ISM) pkt->pts = pkt->dts + trk->end_pts - trk->cluster[trk->entry].dts; } else { @@ -6067,6 +6070,11 @@ static int mov_init(AVFormatContext *s) s->flags &= ~AVFMT_FLAG_AUTO_BSF; } +if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX && mov->flags & FF_MOV_FLAG_SKIP_SIDX) { +av_log(s, AV_LOG_WARNING, "Global SIDX enabled; Ignoring skip_sidx option\n"); +mov->flags &= ~FF_MOV_FLAG_SKIP_SIDX; +} + if (mov->flags & FF_MOV_FLAG_FASTSTART) { mov->reserved_moov_size = -1; } diff --git a/libavformat/movenc.h b/libavformat/movenc.h index fe605d1ad2..68d6f23a5a 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -257,6 +257,7 @@ typedef struct MOVMuxContext { #define FF_MOV_FLAG_SKIP_TRAILER (1 << 18) #define FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS (1 << 19) #define FF_MOV_FLAG_FRAG_EVERY_FRAME (1 << 20) +#define FF_MOV_FLAG_SKIP_SIDX (1 << 21) int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/hlsenc : Added an option to ignore IO errors
Useful for long duration runs with network output --- doc/muxers.texi | 3 +++ libavformat/hlsenc.c | 41 +++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index ca10741900..8eefcf1e82 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1018,6 +1018,9 @@ Use persistent HTTP connections. Applicable only for HTTP output. @item timeout Set timeout for socket I/O operations. Applicable only for HTTP output. +@item -ignore_io_errors +Ignore IO errors during open, write and delete. Useful for long-duration runs with network output. + @end table @anchor{ico} diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 42adcfbab1..bdd2a113bd 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -227,6 +227,7 @@ typedef struct HLSContext { AVIOContext *m3u8_out; AVIOContext *sub_m3u8_out; int64_t timeout; +int ignore_io_errors; } HLSContext; static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, @@ -496,8 +497,11 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, proto = avio_find_protocol_name(s->url); if (hls->method || (proto && !av_strcasecmp(proto, "http"))) { av_dict_set(&options, "method", "DELETE", 0); -if ((ret = vs->avf->io_open(vs->avf, &out, path, AVIO_FLAG_WRITE, &options)) < 0) +if ((ret = vs->avf->io_open(vs->avf, &out, path, AVIO_FLAG_WRITE, &options)) < 0) { +if (hls->ignore_io_errors) +ret = 0; goto fail; +} ff_format_io_close(vs->avf, &out); } else if (unlink(path) < 0) { av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n", @@ -525,6 +529,8 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, if (hls->method || (proto && !av_strcasecmp(proto, "http"))) { av_dict_set(&options, "method", "DELETE", 0); if ((ret = vs->vtt_avf->io_open(vs->vtt_avf, &out, sub_path, AVIO_FLAG_WRITE, &options)) < 0) { +if (hls->ignore_io_errors) +ret = 0; av_free(sub_path); goto fail; } @@ -1380,8 +1386,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) set_http_options(s, &options, hls); snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", vs->m3u8_name); -if ((ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options)) < 0) +if ((ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options)) < 0) { +if (hls->ignore_io_errors) +ret = 0; goto fail; +} for (en = vs->segments; en; en = en->next) { if (target_duration <= en->duration) @@ -1428,8 +1437,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ff_hls_write_end_list(hls->m3u8_out); if( vs->vtt_m3u8_name ) { -if ((ret = hlsenc_io_open(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name, &options)) < 0) +if ((ret = hlsenc_io_open(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name, &options)) < 0) { +if (hls->ignore_io_errors) +ret = 0; goto fail; +} ff_hls_write_playlist_header(hls->sub_m3u8_out, hls->version, hls->allowcache, target_duration, sequence, PLAYLIST_TYPE_NONE); for (en = vs->segments; en; en = en->next) { @@ -1452,7 +1464,6 @@ fail: hlsenc_io_close(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name); if (use_temp_file) ff_rename(temp_filename, vs->m3u8_name, s); - if (ret >= 0 && hls->master_pl_name) if (create_master_playlist(s, vs) < 0) av_log(s, AV_LOG_WARNING, "Master playlist creation failed\n"); @@ -1611,13 +1622,19 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) if (err < 0) return err; } else if (c->segment_type != SEGMENT_TYPE_FMP4) { -if ((err = hlsenc_io_open(s, &oc->pb, oc->url, &options)) < 0) +if ((err = hlsenc_io_open(s, &oc->pb, oc->url, &options)) < 0) { +if (c->ignore_io_errors) +err = 0; goto fail; +} } if (vs->vtt_basename) { set_http_options(s, &options, c); -if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->url, &options)) < 0) +if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->url, &options)) < 0) { +if (c->ignore_io_errors) +err = 0; goto fail; +} } av_dict_free(&options); @@ -2257,9 +2274,9 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) set_http_options(s, &options, hls); ret = hlsenc_io_open(s, &vs->out, vs->avf->url, &options);
[FFmpeg-devel] [PATCH 1/2] avformat/hlsenc: Handled error from ff_http_do_new_request() function
This patch fixes the segmentation fault issues due to unhandled errors from ff_http_do_new_request function. --- libavformat/hlsenc.c | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 31ef0237ae..42adcfbab1 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -241,6 +241,9 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, URLContext *http_url_context = ffio_geturlcontext(*pb); av_assert0(http_url_context); err = ff_http_do_new_request(http_url_context, filename); +if (err < 0) +ff_format_io_close(s, pb); + #endif } return err; @@ -249,6 +252,8 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { HLSContext *hls = s->priv_data; int http_base_proto = filename ? ff_is_http_proto(filename) : 0; +if (!*pb) +return; if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); #if CONFIG_HTTP_PROTOCOL @@ -2329,7 +2334,8 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } vs->packets_written++; -ret = ff_write_chained(oc, stream_index, pkt, s, 0); +if (oc->pb) +ret = ff_write_chained(oc, stream_index, pkt, s, 0); return ret; } -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc : Refactored HLS media playlist related code
Made it as a separate function, so that it could be reused (in future) --- libavformat/dashenc.c | 135 +++--- 1 file changed, 75 insertions(+), 60 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 585b34cb97..f797b7bd1c 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -407,6 +407,78 @@ static void get_hls_playlist_name(char *playlist_name, int string_size, snprintf(playlist_name, string_size, "media_%d.m3u8", id); } +static void get_start_index_number(OutputStream *os, DASHContext *c, + int *start_index, int *start_number) { +*start_index = 0; +*start_number = 1; +if (c->window_size) { +*start_index = FFMAX(os->nb_segments - c->window_size, 0); +*start_number = FFMAX(os->segment_index - c->window_size, 1); +} +} + +static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, + int representation_id, int final) { +DASHContext *c = s->priv_data; +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVDictionary *http_opts = NULL; +int target_duration = 0; +int ret = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); +int i, start_index, start_number; + +get_start_index_number(os, c, &start_index, &start_number); +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); +av_dict_free(&http_opts); +if (ret < 0) { +handle_io_open_error(s, ret, temp_filename_hls); +return; +} +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +double duration = (double) seg->duration / timescale; +if (target_duration <= duration) +target_duration = lrint(duration); +} + +ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file, + os->init_range_length, os->init_start_pos); + +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +ret = ff_hls_write_file_entry(c->m3u8_out, 0, c->single_file, +(double) seg->duration / timescale, 0, +seg->range_length, seg->start_pos, NULL, +c->single_file ? os->initfile : seg->file, +NULL); +if (ret < 0) { +av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); +} +} + +if (final) +ff_hls_write_end_list(c->m3u8_out); + +dashenc_io_close(s, &c->m3u8_out, temp_filename_hls); + +if (use_rename) +if (avpriv_io_move(temp_filename_hls, filename_hls) < 0) { +av_log(os->ctx, AV_LOG_WARNING, "renaming file %s to %s failed\n\n", temp_filename_hls, filename_hls); +} +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -463,11 +535,8 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont int representation_id, int final) { DASHContext *c = s->priv_data; -int i, start_index = 0, start_number = 1; -if (c->window_size) { -start_index = FFMAX(os->nb_segments - c->window_size, 0); -start_number = FFMAX(os->segment_index - c->window_size, 1); -} +int i, start_index, start_number; +get_start_index_number(os, c, &start_index, &start_number); if (c->use_template) { int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE; @@ -527,61 +596,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont } if (c->hls_playlist && start_index < os->nb_segments && os->segment_type == SEGMENT_TYPE_MP4) { -int timescale = os->ctx->streams[0]->time_base.den; -char temp_filename_hls[1024]; -char filename_hls[1024]; -AVDictionary *http_opts = NULL; -int target_duration = 0; -int ret = 0; -const char *proto = avio_find_protocol_name(c->dirname); -int use_rename = proto && !strcmp(proto, "file"); - -get_hls_playlist_name(filename_hls, sizeof(filename_hls), - c->dirname, representation_id); - -snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Added support for Low-latency HLS(LHLS)
Apple doesn't have an official spec for LHLS. Meanwhile hls.js player folks are trying to standardize a open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md This option will also try to comply with the above open spec, till Apple's spec officially supports it. Applicable only when @var{streaming} and @var{hls_playlist} options are enabled. --- doc/muxers.texi | 7 +++ libavformat/dashenc.c | 31 +++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 809f88662e..f09a89db82 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -305,6 +305,13 @@ If this flag is set, the dash segment files will be in in WebM format. @item -ignore_io_errors @var{ignore_io_errors} Ignore IO errors during open and write. Useful for long-duration runs with network output. +@item -lhls @var{lhls} +Enable Low-latency HLS(LHLS). Adds #EXT-X-PREFETCH tag with current segment's URI. +Apple doesn't have an official spec for LHLS. Meanwhile hls.js player folks are +trying to standardize a open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md +This option will also try to comply with the above open spec, till Apple's spec officially supports it. +Applicable only when @var{streaming} and @var{hls_playlist} options are enabled. + @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index f797b7bd1c..8685642437 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -139,6 +139,7 @@ typedef struct DASHContext { char *format_options_str; SegmentType segment_type_option; /* segment type as specified in options */ int ignore_io_errors; +int lhls; } DASHContext; static struct codec_string { @@ -418,7 +419,8 @@ static void get_start_index_number(OutputStream *os, DASHContext *c, } static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, - int representation_id, int final) { + int representation_id, int final, + char *prefetch_url) { DASHContext *c = s->priv_data; int timescale = os->ctx->streams[0]->time_base.den; char temp_filename_hls[1024]; @@ -431,6 +433,11 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, int i, start_index, start_number; get_start_index_number(os, c, &start_index, &start_number); + +if (!c->hls_playlist || start_index >= os->nb_segments || +os->segment_type != SEGMENT_TYPE_MP4) +return; + get_hls_playlist_name(filename_hls, sizeof(filename_hls), c->dirname, representation_id); @@ -468,6 +475,9 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, } } +if (prefetch_url) +avio_printf(c->m3u8_out, "#EXT-X-PREFETCH:%s\n", prefetch_url); + if (final) ff_hls_write_end_list(c->m3u8_out); @@ -594,9 +604,8 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont } avio_printf(out, "\t\t\t\t\n"); } -if (c->hls_playlist && start_index < os->nb_segments && os->segment_type == SEGMENT_TYPE_MP4) -{ -write_hls_media_playlist(os, s, representation_id, final); +if (!c->lhls || final) { +write_hls_media_playlist(os, s, representation_id, final, NULL); } } @@ -1054,6 +1063,15 @@ static int dash_init(AVFormatContext *s) c->seg_duration = c->min_seg_duration; } #endif +if (c->lhls && !c->streaming) { +av_log(s, AV_LOG_WARNING, "LHLS option will be ignored as streaming is not enabled\n"); +c->lhls = 0; +} + +if (c->lhls && !c->hls_playlist) { +av_log(s, AV_LOG_WARNING, "LHLS option will be ignored as hls_playlist is not enabled\n"); +c->lhls = 0; +} av_strlcpy(c->dirname, s->url, sizeof(c->dirname)); ptr = strrchr(c->dirname, '/'); @@ -1635,6 +1653,10 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) { return handle_io_open_error(s, ret, os->temp_path); } +if (c->lhls) { +char *prefetch_url = use_rename ? NULL : os->filename; +write_hls_media_playlist(os, s, pkt->stream_index, 0, prefetch_url); +} } //write out the data immediately in streaming mode @@ -1760,6 +1782,7 @@ static const AVOption options[] = { { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX, E, "segment_type"}, { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, "segment_type"}, { "ignore_io_errors", "Ignore IO errors during open and write. Useful
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Added support for Low-latency HLS(Experimental)
Apple doesn't have an official spec for LHLS. Meanwhile hls.js player folks are trying to standardize a open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md This option will also try to comply with the above open spec, till Apple's spec officially supports it. Applicable only when @var{streaming} and @var{hls_playlist} options are enabled. --- doc/muxers.texi | 8 libavformat/dashenc.c | 37 + 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 809f88662e..4ed46a2220 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -305,6 +305,14 @@ If this flag is set, the dash segment files will be in in WebM format. @item -ignore_io_errors @var{ignore_io_errors} Ignore IO errors during open and write. Useful for long-duration runs with network output. +@item -lhls @var{lhls} +Enable Low-latency HLS(LHLS). Adds #EXT-X-PREFETCH tag with current segment's URI. +Apple doesn't have an official spec for LHLS. Meanwhile hls.js player folks are +trying to standardize a open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md +This option will also try to comply with the above open spec, till Apple's spec officially supports it. +Applicable only when @var{streaming} and @var{hls_playlist} options are enabled. +This is an experimental feature. + @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index f797b7bd1c..cfd0f601d4 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -139,6 +139,7 @@ typedef struct DASHContext { char *format_options_str; SegmentType segment_type_option; /* segment type as specified in options */ int ignore_io_errors; +int lhls; } DASHContext; static struct codec_string { @@ -418,7 +419,8 @@ static void get_start_index_number(OutputStream *os, DASHContext *c, } static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, - int representation_id, int final) { + int representation_id, int final, + char *prefetch_url) { DASHContext *c = s->priv_data; int timescale = os->ctx->streams[0]->time_base.den; char temp_filename_hls[1024]; @@ -431,6 +433,11 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, int i, start_index, start_number; get_start_index_number(os, c, &start_index, &start_number); + +if (!c->hls_playlist || start_index >= os->nb_segments || +os->segment_type != SEGMENT_TYPE_MP4) +return; + get_hls_playlist_name(filename_hls, sizeof(filename_hls), c->dirname, representation_id); @@ -468,6 +475,9 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, } } +if (prefetch_url) +avio_printf(c->m3u8_out, "#EXT-X-PREFETCH:%s\n", prefetch_url); + if (final) ff_hls_write_end_list(c->m3u8_out); @@ -594,9 +604,8 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont } avio_printf(out, "\t\t\t\t\n"); } -if (c->hls_playlist && start_index < os->nb_segments && os->segment_type == SEGMENT_TYPE_MP4) -{ -write_hls_media_playlist(os, s, representation_id, final); +if (!c->lhls || final) { +write_hls_media_playlist(os, s, representation_id, final, NULL); } } @@ -1054,6 +1063,21 @@ static int dash_init(AVFormatContext *s) c->seg_duration = c->min_seg_duration; } #endif +if (c->lhls && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { +av_log(s, AV_LOG_ERROR, + "LHLS is experimental, Please set -strict experimental in order to enable it.\n"); +return AVERROR_EXPERIMENTAL; +} + +if (c->lhls && !c->streaming) { +av_log(s, AV_LOG_WARNING, "LHLS option will be ignored as streaming is not enabled\n"); +c->lhls = 0; +} + +if (c->lhls && !c->hls_playlist) { +av_log(s, AV_LOG_WARNING, "LHLS option will be ignored as hls_playlist is not enabled\n"); +c->lhls = 0; +} av_strlcpy(c->dirname, s->url, sizeof(c->dirname)); ptr = strrchr(c->dirname, '/'); @@ -1635,6 +1659,10 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) if (ret < 0) { return handle_io_open_error(s, ret, os->temp_path); } +if (c->lhls) { +char *prefetch_url = use_rename ? NULL : os->filename; +write_hls_media_playlist(os, s, pkt->stream_index, 0, prefetch_url); +} } //write out the data immediately in streaming mode @@ -1760,6 +1788,7 @@ static const AVOption options[] = { { "mp4", "make segment file in ISOBMFF format",
[FFmpeg-devel] [PATCH v2 1/2] avformat/dashenc : Refactored HLS media playlist related code
Made it as a separate function, so that it could be reused (in future) --- libavformat/dashenc.c | 135 +++--- 1 file changed, 75 insertions(+), 60 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 585b34cb97..f797b7bd1c 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -407,6 +407,78 @@ static void get_hls_playlist_name(char *playlist_name, int string_size, snprintf(playlist_name, string_size, "media_%d.m3u8", id); } +static void get_start_index_number(OutputStream *os, DASHContext *c, + int *start_index, int *start_number) { +*start_index = 0; +*start_number = 1; +if (c->window_size) { +*start_index = FFMAX(os->nb_segments - c->window_size, 0); +*start_number = FFMAX(os->segment_index - c->window_size, 1); +} +} + +static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, + int representation_id, int final) { +DASHContext *c = s->priv_data; +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVDictionary *http_opts = NULL; +int target_duration = 0; +int ret = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); +int i, start_index, start_number; + +get_start_index_number(os, c, &start_index, &start_number); +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +ret = dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); +av_dict_free(&http_opts); +if (ret < 0) { +handle_io_open_error(s, ret, temp_filename_hls); +return; +} +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +double duration = (double) seg->duration / timescale; +if (target_duration <= duration) +target_duration = lrint(duration); +} + +ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file, + os->init_range_length, os->init_start_pos); + +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +ret = ff_hls_write_file_entry(c->m3u8_out, 0, c->single_file, +(double) seg->duration / timescale, 0, +seg->range_length, seg->start_pos, NULL, +c->single_file ? os->initfile : seg->file, +NULL); +if (ret < 0) { +av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n"); +} +} + +if (final) +ff_hls_write_end_list(c->m3u8_out); + +dashenc_io_close(s, &c->m3u8_out, temp_filename_hls); + +if (use_rename) +if (avpriv_io_move(temp_filename_hls, filename_hls) < 0) { +av_log(os->ctx, AV_LOG_WARNING, "renaming file %s to %s failed\n\n", temp_filename_hls, filename_hls); +} +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -463,11 +535,8 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont int representation_id, int final) { DASHContext *c = s->priv_data; -int i, start_index = 0, start_number = 1; -if (c->window_size) { -start_index = FFMAX(os->nb_segments - c->window_size, 0); -start_number = FFMAX(os->segment_index - c->window_size, 1); -} +int i, start_index, start_number; +get_start_index_number(os, c, &start_index, &start_number); if (c->use_template) { int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE; @@ -527,61 +596,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont } if (c->hls_playlist && start_index < os->nb_segments && os->segment_type == SEGMENT_TYPE_MP4) { -int timescale = os->ctx->streams[0]->time_base.den; -char temp_filename_hls[1024]; -char filename_hls[1024]; -AVDictionary *http_opts = NULL; -int target_duration = 0; -int ret = 0; -const char *proto = avio_find_protocol_name(c->dirname); -int use_rename = proto && !strcmp(proto, "file"); - -get_hls_playlist_name(filename_hls, sizeof(filename_hls), - c->dirname, representation_id); - -snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "
[FFmpeg-devel] [PATCH] avformat/file: Fix file delete for Windows
From: Karthick Jeyapal Fixes bug id : 7638 --- libavformat/file.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libavformat/file.c b/libavformat/file.c index 1d321c4..e613b91 100644 --- a/libavformat/file.c +++ b/libavformat/file.c @@ -173,7 +173,11 @@ static int file_delete(URLContext *h) av_strstart(filename, "file:", &filename); ret = rmdir(filename); -if (ret < 0 && errno == ENOTDIR) +if (ret < 0 && (errno == ENOTDIR +# ifdef _WIN32 +|| errno == EINVAL +# endif +)) ret = unlink(filename); if (ret < 0) return AVERROR(errno); -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Format xs:datetime in millisecond precision
For low latency streaming even milliseconds matter! --- libavformat/dashenc.c | 13 ++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index cfd0f601d4..912c1cf11d 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -32,6 +32,7 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/rational.h" +#include "libavutil/time.h" #include "libavutil/time_internal.h" #include "avc.h" @@ -668,12 +669,18 @@ static void write_time(AVIOContext *out, int64_t time) static void format_date_now(char *buf, int size) { -time_t t = time(NULL); struct tm *ptm, tmbuf; -ptm = gmtime_r(&t, &tmbuf); +int64_t time_us = av_gettime(); +int64_t time_ms = time_us / 1000; +const time_t time_s = time_ms / 1000; +int millisec = time_ms - (time_s * 1000); +ptm = gmtime_r(&time_s, &tmbuf); if (ptm) { -if (!strftime(buf, size, "%Y-%m-%dT%H:%M:%SZ", ptm)) +int len; +if (!strftime(buf, size, "%Y-%m-%dT%H:%M:%S", ptm)) buf[0] = '\0'; +len = strlen(buf); +snprintf(buf + len, size - len, ".%03dZ", millisec); } } -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/dashenc: Format xs:datetime in millisecond precision
For low latency streaming even milliseconds matter! --- libavformat/dashenc.c | 15 --- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index cfd0f601d4..9c90cf17e5 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -32,6 +32,7 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/rational.h" +#include "libavutil/time.h" #include "libavutil/time_internal.h" #include "avc.h" @@ -668,12 +669,20 @@ static void write_time(AVIOContext *out, int64_t time) static void format_date_now(char *buf, int size) { -time_t t = time(NULL); struct tm *ptm, tmbuf; -ptm = gmtime_r(&t, &tmbuf); +int64_t time_us = av_gettime(); +int64_t time_ms = time_us / 1000; +const time_t time_s = time_ms / 1000; +int millisec = time_ms - (time_s * 1000); +ptm = gmtime_r(&time_s, &tmbuf); if (ptm) { -if (!strftime(buf, size, "%Y-%m-%dT%H:%M:%SZ", ptm)) +int len; +if (!strftime(buf, size, "%Y-%m-%dT%H:%M:%S", ptm)) { buf[0] = '\0'; +return; +} +len = strlen(buf); +snprintf(buf + len, size - len, ".%03dZ", millisec); } } -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Added documentation for $ext$ identifier in filenames
--- doc/muxers.texi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/muxers.texi b/doc/muxers.texi index 4ed46a2220..d2d985f1ac 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -216,6 +216,8 @@ It creates a MPD manifest file and segment files for each stream. The segment filename might contain pre-defined identifiers used with SegmentTemplate as defined in section 5.3.9.4.4 of the standard. Available identifiers are "$RepresentationID$", "$Number$", "$Bandwidth$" and "$Time$". +In addition to the standard identifiers, an ffmpeg-specific "$ext$" identifier is also supported. +When specified ffmpeg will replace $ext$ in the file name with muxing format's extensions such as mp4, webm etc., @example ffmpeg -re -i -map 0 -map 0 -c:a libfdk_aac -c:v libx264 -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Skip writing trailer for MP4 output when in streaming mode
In streaming mode mp4 trailer is not required for playout. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 9c90cf17e5..6299e179c2 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1210,7 +1210,7 @@ static int dash_init(AVFormatContext *s) if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) -av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx", 0); +av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); } else { -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/tee : Pass standards compliance value to slave muxers as well
--- libavformat/tee.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libavformat/tee.c b/libavformat/tee.c index ef3b113a47..89a4ceb280 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -236,6 +236,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) avf2->io_close = avf->io_close; avf2->interrupt_callback = avf->interrupt_callback; avf2->flags = avf->flags; +avf2->strict_std_compliance = avf->strict_std_compliance; tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map)); if (!tee_slave->stream_map) { -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Added comments
Added comments regarding usage of certain movflags in streaming mode. --- libavformat/dashenc.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index a0b44a0ec3..f8782756b4 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1216,6 +1216,17 @@ static int dash_init(AVFormatContext *s) if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) +// Explanation for why certain movflags are used for streaming: +// frag_every_frame :- Every frame should be moof fragment, so +// the data from current frame can be streamed without +// waiting for the completion of the entire segment. +// skip_sidx :- The SIDX atom for each moof will result in a +// significant bitrate overhead. Hence disabling it here. +// skip_trailer :- Writing mp4 trailer means that a list of all +// fragment's information is stored, which results continuous +// growth in memory usage as more fragments are muxed. +// Disabling trailer results in deterministic memory usage. +// Anyways trailer is unnecessary of fmp4 segment. av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc: Added option to repeatedly publish master playlist
The master playlist can be published at a specified interval with this option --- doc/muxers.texi | 3 +++ libavformat/dashenc.c | 9 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 36010cf2d1..372fab2f92 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -315,6 +315,9 @@ This option will also try to comply with the above open spec, till Apple's spec Applicable only when @var{streaming} and @var{hls_playlist} options are enabled. This is an experimental feature. +@item -master_m3u8_publish_rate @var{master_m3u8_publish_rate} +Publish master playlist repeatedly every after specified number of segment intervals. + @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 37a7547b12..a0b44a0ec3 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -141,6 +141,7 @@ typedef struct DASHContext { SegmentType segment_type_option; /* segment type as specified in options */ int ignore_io_errors; int lhls; +int master_publish_rate; } DASHContext; static struct codec_string { @@ -965,13 +966,18 @@ static int write_manifest(AVFormatContext *s, int final) return ret; } -if (c->hls_playlist && !c->master_playlist_created) { +if (c->hls_playlist) { char filename_hls[1024]; const char *audio_group = "A1"; char audio_codec_str[128] = "\0"; int is_default = 1; int max_audio_bitrate = 0; +// Publish master playlist only the configured rate +if (c->master_playlist_created && (!c->master_publish_rate || + c->streams[0].segment_index % c->master_publish_rate)) +return 0; + if (*c->dirname) snprintf(filename_hls, sizeof(filename_hls), "%smaster.m3u8", c->dirname); else @@ -1798,6 +1804,7 @@ static const AVOption options[] = { { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, "segment_type"}, { "ignore_io_errors", "Ignore IO errors during open and write. Useful for long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "lhls", "Enable Low-latency HLS(Experimental). Adds #EXT-X-PREFETCH tag with current segment's URI", OFFSET(lhls), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, +{ "master_m3u8_publish_rate", "Publish master playlist every after this many segment intervals", OFFSET(master_publish_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT_MAX, E}, { NULL }, }; -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Added comments
Added comments regarding usage of certain movflags in streaming mode. --- libavformat/dashenc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index a0b44a0ec3..c5e882f4ae 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1216,6 +1216,9 @@ static int dash_init(AVFormatContext *s) if (os->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming) +// frag_every_frame : Allows lower latency streaming +// skip_sidx : Reduce bitrate overhead +// skip_trailer : Avoids growing memory usage with time av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); else av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); -- 2.17.1 (Apple Git-112) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/4] libformat/tcp: Moved all options to a separate structure
--- libavformat/tcp.c | 46 +- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/libavformat/tcp.c b/libavformat/tcp.c index 07b4ed9..06368ff 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -32,18 +32,22 @@ #include #endif -typedef struct TCPContext { -const AVClass *class; -int fd; +typedef struct TCPOptions { int listen; -int open_timeout; int rw_timeout; int listen_timeout; int recv_buffer_size; int send_buffer_size; +} TCPOptions; + +typedef struct TCPContext { +const AVClass *class; +int fd; +int open_timeout; +TCPOptions options; } TCPContext; -#define OFFSET(x) offsetof(TCPContext, x) +#define OFFSET(x) (offsetof(TCPContext, options) + offsetof(TCPOptions, x)) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { @@ -87,26 +91,26 @@ static int tcp_open(URLContext *h, const char *uri, int flags) if (p) { if (av_find_info_tag(buf, sizeof(buf), "listen", p)) { char *endptr = NULL; -s->listen = strtol(buf, &endptr, 10); +s->options.listen = strtol(buf, &endptr, 10); /* assume if no digits were found it is a request to enable it */ if (buf == endptr) -s->listen = 1; +s->options.listen = 1; } if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) { -s->rw_timeout = strtol(buf, NULL, 10); +s->options.rw_timeout = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) { -s->listen_timeout = strtol(buf, NULL, 10); +s->options.listen_timeout = strtol(buf, NULL, 10); } } -if (s->rw_timeout >= 0) { +if (s->options.rw_timeout >= 0) { s->open_timeout = -h->rw_timeout = s->rw_timeout; +h->rw_timeout = s->options.rw_timeout; } hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf(portstr, sizeof(portstr), "%d", port); -if (s->listen) +if (s->options.listen) hints.ai_flags |= AI_PASSIVE; if (!hostname[0]) ret = getaddrinfo(NULL, portstr, &hints, &ai); @@ -142,21 +146,21 @@ static int tcp_open(URLContext *h, const char *uri, int flags) /* Set the socket's send or receive buffer sizes, if specified. If unspecified or setting fails, system default is used. */ -if (s->recv_buffer_size > 0) { -setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->recv_buffer_size, sizeof (s->recv_buffer_size)); +if (s->options.recv_buffer_size > 0) { +setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &s->options.recv_buffer_size, sizeof (s->options.recv_buffer_size)); } -if (s->send_buffer_size > 0) { -setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->send_buffer_size, sizeof (s->send_buffer_size)); +if (s->options.send_buffer_size > 0) { +setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &s->options.send_buffer_size, sizeof (s->options.send_buffer_size)); } -if (s->listen == 2) { +if (s->options.listen == 2) { // multi-client if ((ret = ff_listen(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) < 0) goto fail1; -} else if (s->listen == 1) { +} else if (s->options.listen == 1) { // single client if ((ret = ff_listen_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen, - s->listen_timeout, h)) < 0) + s->options.listen_timeout, h)) < 0) goto fail1; // Socket descriptor already closed here. Safe to overwrite to client one. fd = ret; @@ -198,11 +202,11 @@ static int tcp_accept(URLContext *s, URLContext **c) TCPContext *sc = s->priv_data; TCPContext *cc; int ret; -av_assert0(sc->listen); +av_assert0(sc->options.listen); if ((ret = ffurl_alloc(c, s->filename, s->flags, &s->interrupt_callback)) < 0) return ret; cc = (*c)->priv_data; -ret = ff_accept(sc->fd, sc->listen_timeout, s); +ret = ff_accept(sc->fd, sc->options.listen_timeout, s); if (ret < 0) return ret; cc->fd = ret; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/4] libavformat/tcp: Added an option to reuse sockets
--- doc/protocols.texi | 4 ++ libavformat/tcp.c | 150 libavformat/tcp.h | 27 ++ libavformat/utils.c | 2 + 4 files changed, 183 insertions(+) create mode 100644 libavformat/tcp.h diff --git a/doc/protocols.texi b/doc/protocols.texi index a7968ff..62d317d 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -1242,6 +1242,10 @@ Set receive buffer size, expressed bytes. @item send_buffer_size=@var{bytes} Set send buffer size, expressed bytes. + +@item reuse_sockets=@var{1|0} +Reuse sockets instead of opening a new socket each time. +Default value is 0. @end table The following example shows how to setup a listening TCP connection diff --git a/libavformat/tcp.c b/libavformat/tcp.c index 06368ff..8bca628 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -1,6 +1,7 @@ /* * TCP protocol * Copyright (c) 2002 Fabrice Bellard + * Copyright (c) 2017 Akamai Technologies, Inc * * This file is part of FFmpeg. * @@ -19,6 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" +#include "tcp.h" +#include "libavcodec/internal.h" #include "libavutil/avassert.h" #include "libavutil/parseutils.h" #include "libavutil/opt.h" @@ -38,6 +41,7 @@ typedef struct TCPOptions { int listen_timeout; int recv_buffer_size; int send_buffer_size; +int reuse_sockets; } TCPOptions; typedef struct TCPContext { @@ -47,6 +51,16 @@ typedef struct TCPContext { TCPOptions options; } TCPContext; +typedef struct TCPSocket { +char *hostname; +int port; +int in_use; +int64_t last_close_time; +int fd; +TCPOptions options; +struct TCPSocket *next; +} TCPSocket; + #define OFFSET(x) (offsetof(TCPContext, options) + offsetof(TCPOptions, x)) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM @@ -56,6 +70,7 @@ static const AVOption options[] = { { "listen_timeout", "Connection awaiting timeout (in milliseconds)", OFFSET(listen_timeout), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, +{ "reuse_sockets", "Reuse sockets instead of opening a new socket each time", OFFSET(reuse_sockets), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E }, { NULL } }; @@ -66,6 +81,116 @@ static const AVClass tcp_class = { .version= LIBAVUTIL_VERSION_INT, }; +static TCPSocket *first_socket = NULL; + +static TCPSocket* tcp_socket_create (TCPContext *s, char *name, int port) +{ +TCPSocket *p = NULL; + +p = (TCPSocket *) calloc (1, sizeof(TCPSocket)); +if (p == NULL) { +return NULL; +} + +p->hostname = strdup(name); +p->port = port; +p->in_use = 1; +p->fd = s->fd; +p->options = s->options; +p->next = NULL; + +return p; +} + +static int tcp_socket_free (TCPSocket *socket) +{ +if (socket) { +closesocket(socket->fd); +free(socket->hostname); +free(socket); +} +return 0; +} + +static void tcp_list_add(TCPContext *s, char *name, int port) +{ +TCPSocket *socket = tcp_socket_create(s, name, port); + +if (!socket) +return; +avpriv_lock_avformat(); +socket->next = first_socket; +first_socket = socket; +avpriv_unlock_avformat(); + +} + +static void tcp_list_remove_next(TCPSocket *socket) +{ +TCPSocket *temp; +if (socket) { +temp = socket->next; +socket->next = socket->next->next; +} else { +temp = first_socket; +first_socket = first_socket->next; +} +tcp_socket_free(temp); + +} + +static int tcp_socket_find (TCPContext *s, char *name, int port) +{ +int lfd = -1; +TCPSocket *p = first_socket; +TCPSocket *prev = NULL; +int64_t current_time = av_gettime(); +avpriv_lock_avformat(); +while(p) { +const int idle_timeout = 60 * 100; +// Remove idle connections +if (!p->in_use && (current_time - p->last_close_time) > idle_timeout) { +tcp_list_remove_next(prev); +} else { +// Reuse the connection if a correct match if found +if (!p->in_use && lfd == -1 && +!strcmp(p->hostname, name) && (p->port == port) && +!memcmp(&p->options, &s->options, sizeof(s->options))) { +p->in_use = 1; +lfd = p->fd; +} +prev = p; +} +p = p->next; + +} +avpriv_unlock_avformat(); +return lfd; +} + +static void tcp_socket_release (int search_fd) +{ +TCPSocket *p = first_sock
[FFmpeg-devel] [PATCH 3/4] libavformat/http: Reuse TCP sockets when 'multiple_requests' is set
--- libavformat/http.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavformat/http.c b/libavformat/http.c index bd9148f..b7bdcdc 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -227,6 +227,9 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options) ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL); if (!s->hd) { +if (s->multiple_requests && !strcmp(lower_proto, "tcp")) { +av_dict_set_int(options, "reuse_sockets", 1, 0); +} err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE, &h->interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist, h); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 4/4] libavformat/hlsenc: Persistent HTTP connections supported as an option
--- doc/muxers.texi | 3 +++ libavformat/hlsenc.c | 5 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 91bbe67..1e1468c 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -794,6 +794,9 @@ files. @item http_user_agent Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item http_persistent +Use persistent HTTP connections. Applicable only for HTTP output. + @end table @anchor{ico} diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 5ea9d21..79db77a 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -175,6 +175,7 @@ typedef struct HLSContext { double initial_prog_date_time; char current_segment_final_filename_fmt[1024]; // when renaming segments char *user_agent; +int http_persistent; } HLSContext; static int get_int_from_double(double val) @@ -228,7 +229,8 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } if (c->user_agent) av_dict_set(options, "user_agent", c->user_agent, 0); - +if (c->http_persistent) +av_dict_set_int(options, "multiple_requests", c->http_persistent, 0); } static int replace_int_data_in_filename(char *buf, int buf_size, const char *filename, char placeholder, int64_t number) @@ -1829,6 +1831,7 @@ static const AVOption options[] = { {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, {"datetime", "current datetime as MMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, {"http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,E}, +{"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, { NULL }, }; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/3] libavformat/avio: Utility function to return URLContext
--- libavformat/avio_internal.h | 8 libavformat/aviobuf.c | 8 2 files changed, 16 insertions(+) diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index c01835d..04c1ad5 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -133,6 +133,14 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); int ffio_fdopen(AVIOContext **s, URLContext *h); /** + * Return the URLContext associated with the AVIOContext + * + * @param s IO context + * @return pointer to URLContext or NULL. + */ +URLContext *ffio_geturlcontext(AVIOContext *s); + +/** * Open a write-only fake memory stream. The written data is not stored * anywhere - this is only used for measuring the amount of data * written. diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 3b4c843..1353c80 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -980,6 +980,14 @@ fail: return AVERROR(ENOMEM); } +URLContext* ffio_geturlcontext(AVIOContext *s) { +AVIOInternal *internal = s->opaque; +if (internal) +return internal->h; +else +return NULL; +} + int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size) { uint8_t *buffer; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/3] libavformat/http: Handled multiple_requests option during write
--- libavformat/http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/http.c b/libavformat/http.c index bd9148f..0854c01 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -1527,7 +1527,7 @@ static int http_write(URLContext *h, const uint8_t *buf, int size) /* silently ignore zero-size data since chunk encoding that would * signal EOF */ -if (size > 0) { +if (size > 0 || s->multiple_requests) { /* upload data using chunked encoding */ snprintf(temp, sizeof(temp), "%x\r\n", size); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 3/3] libavformat/hlsenc: Persistent HTTP connections supported as an option
--- doc/muxers.texi | 3 +++ libavformat/hlsenc.c | 50 +++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 91bbe67..1e1468c 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -794,6 +794,9 @@ files. @item http_user_agent Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item http_persistent +Use persistent HTTP connections. Applicable only for HTTP output. + @end table @anchor{ico} diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 5ea9d21..4a581b4 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -44,6 +44,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "http.h" #include "internal.h" #include "os_support.h" @@ -175,6 +176,7 @@ typedef struct HLSContext { double initial_prog_date_time; char current_segment_final_filename_fmt[1024]; // when renaming segments char *user_agent; +int http_persistent; } HLSContext; static int get_int_from_double(double val) @@ -215,10 +217,40 @@ static int mkdir_p(const char *path) { return ret; } +static int is_http_proto(char *filename) { +const char *proto = avio_find_protocol_name(filename); +return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +} + +static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, + AVDictionary **options) { +HLSContext *hls = s->priv_data; +int http_base_proto = is_http_proto(filename); +int err; +if (*pb == NULL || !http_base_proto || !hls->http_persistent) { +err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); +} else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +err = ff_http_do_new_request(http_url_context, filename); +} +return err; +} + +static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { +HLSContext *hls = s->priv_data; +int http_base_proto = is_http_proto(filename); + +if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { +ff_format_io_close(s, pb); +} else if (*pb) { +URLContext *http_url_context = ffio_geturlcontext(*pb); +http_url_context->prot->url_write(http_url_context, NULL, 0); +} +} + static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c) { -const char *proto = avio_find_protocol_name(s->filename); -int http_base_proto = proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +int http_base_proto = is_http_proto(s->filename); if (c->method) { av_dict_set(options, "method", c->method, 0); @@ -228,6 +260,8 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } if (c->user_agent) av_dict_set(options, "user_agent", c->user_agent, 0); +if (c->http_persistent) +av_dict_set_int(options, "multiple_requests", 1, 0); } @@ -1263,17 +1297,17 @@ static int hls_start(AVFormatContext *s) err = AVERROR(ENOMEM); goto fail; } -err = s->io_open(s, &oc->pb, filename, AVIO_FLAG_WRITE, &options); +err = hlsenc_io_open(s, &oc->pb, filename, &options); av_free(filename); av_dict_free(&options); if (err < 0) return err; } else -if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((err = hlsenc_io_open(s, &oc->pb, oc->filename, &options)) < 0) goto fail; if (c->vtt_basename) { set_http_options(s, &options, c); -if ((err = s->io_open(s, &vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->filename, &options)) < 0) goto fail; } av_dict_free(&options); @@ -1661,9 +1695,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hls->size = new_start_pos - hls->start_pos; if (!byterange_mode) { -ff_format_io_close(s, &oc->pb); +hlsenc_io_close(s, &oc->pb, +hls->fmp4_init_mode ? hls->base_output_dirname : oc->filename); if (hls->vtt_avf) { -ff_format_io_close(s, &hls->vtt_avf->pb); +hlsenc_io_close(s, &hls->vtt_avf->pb, hls->vtt_avf->filename); } } if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) { @@ -1829,6 +1864,7 @@ static const AVOption options[] = { {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, {"datetime", "current datetime as MMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, "start_sequ
[FFmpeg-devel] [PATCH 1/1] avformat/dashenc: Added configuration to override HTTP User-Agent
--- doc/muxers.texi | 2 ++ libavformat/dashenc.c | 22 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 91bbe67..412fede 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -247,6 +247,8 @@ DASH-templated name to used for the initialization segment. Default is "init-str DASH-templated name to used for the media segments. Default is "chunk-stream$RepresentationID$-$Number%05d$.m4s" @item -utc_timing_url @var{utc_url} URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; +@item -http_user_agent @var{user_agent} +Override User-Agent field in HTTP header. Applicable only for HTTP output. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 7813f44..a68f7fb 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -100,6 +100,7 @@ typedef struct DASHContext { const char *init_seg_name; const char *media_seg_name; const char *utc_timing_url; +const char *user_agent; } DASHContext; static struct codec_string { @@ -210,6 +211,12 @@ static int flush_dynbuf(OutputStream *os, int *range_length) return avio_open_dyn_buf(&os->ctx->pb); } +static void set_http_options(AVDictionary **options, DASHContext *c) +{ +if (c->user_agent) +av_dict_set(options, "user_agent", c->user_agent, 0); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -575,16 +582,19 @@ static int write_manifest(AVFormatContext *s, int final) int use_rename = proto && !strcmp(proto, "file"); static unsigned int warned_non_file = 0; AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0); +AVDictionary *opts = NULL; if (!use_rename && !warned_non_file++) av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n"); snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename); -ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL); +set_http_options(&opts, c); +ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &opts); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } +av_dict_free(&opts); avio_printf(out, "\n"); avio_printf(out, "http://www.w3.org/2001/XMLSchema-instance\"\n"; "\txmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" @@ -768,9 +778,11 @@ static int dash_init(AVFormatContext *s) ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), c->init_seg_name, i, 0, os->bit_rate, 0); } snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile); -ret = s->io_open(s, &os->out, filename, AVIO_FLAG_WRITE, NULL); +set_http_options(&opts, c); +ret = s->io_open(s, &os->out, filename, AVIO_FLAG_WRITE, &opts); if (ret < 0) return ret; +av_dict_free(&opts); os->init_start_pos = 0; if (!strcmp(os->format_name, "mp4")) { @@ -974,12 +986,15 @@ static int dash_flush(AVFormatContext *s, int final, int stream) } if (!c->single_file) { +AVDictionary *opts = NULL; ff_dash_fill_tmpl_params(filename, sizeof(filename), c->media_seg_name, i, os->segment_index, os->bit_rate, os->start_pts); snprintf(full_path, sizeof(full_path), "%s%s", c->dirname, filename); snprintf(temp_path, sizeof(temp_path), use_rename ? "%s.tmp" : "%s", full_path); -ret = s->io_open(s, &os->out, temp_path, AVIO_FLAG_WRITE, NULL); +set_http_options(&opts, c); +ret = s->io_open(s, &os->out, temp_path, AVIO_FLAG_WRITE, &opts); if (ret < 0) break; +av_dict_free(&opts); if (!strcmp(os->format_name, "mp4")) write_styp(os->ctx->pb); } else { @@ -1188,6 +1203,7 @@ static const AVOption options[] = { { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.m4s"}, 0, 0, E }, { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.m4s"}, 0, 0, E }, { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E }, +{ "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), A
[FFmpeg-devel] [PATCH v2 1/3] libavformat/avio: Utility function to return URLContext
--- libavformat/avio_internal.h | 8 libavformat/aviobuf.c | 13 + 2 files changed, 21 insertions(+) diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index c01835d..04c1ad5 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -133,6 +133,14 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); int ffio_fdopen(AVIOContext **s, URLContext *h); /** + * Return the URLContext associated with the AVIOContext + * + * @param s IO context + * @return pointer to URLContext or NULL. + */ +URLContext *ffio_geturlcontext(AVIOContext *s); + +/** * Open a write-only fake memory stream. The written data is not stored * anywhere - this is only used for measuring the amount of data * written. diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 3b4c843..86eb657 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -980,6 +980,19 @@ fail: return AVERROR(ENOMEM); } +URLContext* ffio_geturlcontext(AVIOContext *s) +{ +AVIOInternal *internal; +if (!s) +return NULL; + +internal = s->opaque; +if (internal && s->read_packet == io_read_packet) +return internal->h; +else +return NULL; +} + int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size) { uint8_t *buffer; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/3] libavformat/http: Handled multiple_requests option during write
--- libavformat/http.c | 5 + 1 file changed, 5 insertions(+) diff --git a/libavformat/http.c b/libavformat/http.c index 056d5f6..45c0e72 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -171,6 +171,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, const char *hoststr, const char *auth, const char *proxyauth, int *new_location); static int http_read_header(URLContext *h, int *new_location); +static int http_shutdown(URLContext *h, int flags); void ff_http_init_auth_state(URLContext *dest, const URLContext *src) { @@ -306,6 +307,10 @@ int ff_http_do_new_request(URLContext *h, const char *uri) AVDictionary *options = NULL; int ret; +ret = http_shutdown(h, h->flags); +if (ret < 0) +return ret; + s->chunkend = 0; s->off = 0; s->icy_data_read = 0; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/3] libavformat/hlsenc: Persistent HTTP connections supported as an option
--- doc/muxers.texi | 3 +++ libavformat/hlsenc.c | 48 +--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..c1d753b 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -850,6 +850,9 @@ ffmpeg -re -i in.ts -f hls -master_pl_name master.m3u8 \ This example creates HLS master playlist with name master.m3u8 and keep publishing it repeatedly every after 30 segments i.e. every after 60s. +@item http_persistent +Use persistent HTTP connections. Applicable only for HTTP output. + @end table @anchor{ico} diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..32c99a0 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "http.h" #include "internal.h" #include "os_support.h" @@ -204,6 +205,7 @@ typedef struct HLSContext { char *var_stream_map; /* user specified variant stream map string */ char *master_pl_name; unsigned int master_publish_rate; +int http_persistent; } HLSContext; static int get_int_from_double(double val) @@ -244,10 +246,38 @@ static int mkdir_p(const char *path) { return ret; } +static int is_http_proto(char *filename) { +const char *proto = avio_find_protocol_name(filename); +return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +} + +static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, + AVDictionary **options) { +HLSContext *hls = s->priv_data; +int http_base_proto = is_http_proto(filename); +int err; +if (*pb == NULL || !http_base_proto || !hls->http_persistent) { +err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); +} else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); +err = ff_http_do_new_request(http_url_context, filename); +} +return err; +} + +static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { +HLSContext *hls = s->priv_data; +int http_base_proto = is_http_proto(filename); + +if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { +ff_format_io_close(s, pb); +} +} + static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c) { -const char *proto = avio_find_protocol_name(s->filename); -int http_base_proto = proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +int http_base_proto = is_http_proto(s->filename); if (c->method) { av_dict_set(options, "method", c->method, 0); @@ -257,6 +287,8 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } if (c->user_agent) av_dict_set(options, "user_agent", c->user_agent, 0); +if (c->http_persistent) +av_dict_set_int(options, "multiple_requests", 1, 0); } @@ -1430,17 +1462,17 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) err = AVERROR(ENOMEM); goto fail; } -err = s->io_open(s, &oc->pb, filename, AVIO_FLAG_WRITE, &options); +err = hlsenc_io_open(s, &oc->pb, filename, &options); av_free(filename); av_dict_free(&options); if (err < 0) return err; } else -if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((err = hlsenc_io_open(s, &oc->pb, oc->filename, &options)) < 0) goto fail; if (vs->vtt_basename) { set_http_options(s, &options, c); -if ((err = s->io_open(s, &vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->filename, &options)) < 0) goto fail; } av_dict_free(&options); @@ -2148,11 +2180,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) avio_open_dyn_buf(&oc->pb); vs->packets_written = 0; ff_format_io_close(s, &vs->out); +hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } else { -ff_format_io_close(s, &oc->pb); +hlsenc_io_close(s, &oc->pb, oc->filename); } if (vs->vtt_avf) { -ff_format_io_close(s, &vs->vtt_avf->pb); +hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->filename); } } if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) { @@ -2337,6 +2370,7 @@ static const AVOption options[] = { {"var_stream_map", "Variant stream map string", OFFSET(var_stream_map), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,E}, {"master_pl_name", "Create HLS master playlist with this name", OFFSET(master_pl_name), AV_OPT_TYPE_STRING, {.str =
[FFmpeg-devel] Common Media Application Format
[PATCH 1/2] avformat/hlsenc: Modularized playlist creation to allow [PATCH 2/2] avformat/dashenc: Option to generate hls playlist as well One of the biggest applications of MPEG Common Media Application Format, is to have common media file/segments, that could be used by both HLS and DASH players. To achieve this possibility in ffmpeg we need to have a muxer plugin that generates both DASH manifest and HLS playlist files. Two simple options that we have are 1. Modify the dashenc plugin to generate the HLS playlist files 2. Modify the hlsenc plugin to generate DASH manifest files Both options are equally good. I have chosen option 1, just for the sake easiness to implement the same. Regards, Karthick ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/hlsenc: Modularized playlist creation to allow reuse
--- libavformat/hlsenc.c | 130 +++--- libavformat/hlsenc.h | 158 +++ 2 files changed, 177 insertions(+), 111 deletions(-) create mode 100644 libavformat/hlsenc.h diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..4e017eb 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "hlsenc.h" #include "internal.h" #include "os_support.h" @@ -73,35 +74,11 @@ typedef struct HLSSegment { struct HLSSegment *next; } HLSSegment; -typedef enum HLSFlags { -// Generate a single media file and use byte ranges in the playlist. -HLS_SINGLE_FILE = (1 << 0), -HLS_DELETE_SEGMENTS = (1 << 1), -HLS_ROUND_DURATIONS = (1 << 2), -HLS_DISCONT_START = (1 << 3), -HLS_OMIT_ENDLIST = (1 << 4), -HLS_SPLIT_BY_TIME = (1 << 5), -HLS_APPEND_LIST = (1 << 6), -HLS_PROGRAM_DATE_TIME = (1 << 7), -HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime e.g.: %%03d -HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime e.g.: %%09t -HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s -HLS_TEMP_FILE = (1 << 11), -HLS_PERIODIC_REKEY = (1 << 12), -} HLSFlags; - typedef enum { SEGMENT_TYPE_MPEGTS, SEGMENT_TYPE_FMP4, } SegmentType; -typedef enum { -PLAYLIST_TYPE_NONE, -PLAYLIST_TYPE_EVENT, -PLAYLIST_TYPE_VOD, -PLAYLIST_TYPE_NB, -} PlaylistType; - typedef struct VariantStream { unsigned number; int64_t sequence; @@ -1022,19 +999,6 @@ static void hls_free_segments(HLSSegment *p) } } -static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version, - int target_duration, int64_t sequence) -{ -avio_printf(out, "#EXTM3U\n"); -avio_printf(out, "#EXT-X-VERSION:%d\n", version); -if (hls->allowcache == 0 || hls->allowcache == 1) { -avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES"); -} -avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); -avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -} - static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc) { size_t len = strlen(oc->filename); @@ -1101,8 +1065,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -avio_printf(master_pb, "#EXTM3U\n"); -avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version); +hls_write_playlist_version(master_pb, hls->version); /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { @@ -1143,18 +1106,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += aud_st->codecpar->bit_rate; bandwidth += bandwidth / 10; -if (!bandwidth) { -av_log(NULL, AV_LOG_WARNING, -"Bandwidth info not available, set audio and video bitrates\n"); -av_freep(&m3U8_rel_name); -continue; -} - -avio_printf(master_pb, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); -if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0) -avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, -vid_st->codecpar->height); -avio_printf(master_pb, "\n%s\n\n", m3U8_rel_name); +hls_write_stream_info(vid_st, master_pb, bandwidth, m3U8_rel_name); av_freep(&m3U8_rel_name); } @@ -1209,12 +1161,8 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; -write_m3u8_head_block(hls, out, hls->version, target_duration, sequence); -if (hls->pl_type == PLAYLIST_TYPE_EVENT) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); -} else if (hls->pl_type == PLAYLIST_TYPE_VOD) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); -} +hls_write_playlist_header(out, hls->version, hls->allowcache, + target_duration, sequence, hls->pl_type); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ avio_printf(out, "#EXT-X-DISCONTINUITY\n"); @@ -1231,74 +1179,34 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) iv_string = en->iv_string; } -if (en->discont) { -avio_printf(out, "#EXT-X-DISCONTINUITY\n"); -} - if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) { -avio_printf
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/dashenc.c | 101 -- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..1cf2481 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 201668a..1fa9d82 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsenc.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,13 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, const char *base_url, int id) { +if (base_url) +sprintf(playlist_name, "%smedia_%d.m3u8", base_url, id); +else +sprintf(playlist_name, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +272,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +333,53 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +if (target_duration < seg->duration) +target_duration = seg->duration; +} +target_duration = lrint((double) target_duration / timescale); + +hls_write_playlist_header (out_hls, 6, -1, +target_duration, start_number, PLAYLIST_TYPE_NONE); + +hls_write_init_file(out_hls, os->initfile, c->single_file, +os->init_range_length, os->init_start_pos); + +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +hls_write_file_entry(out_hls, 0, c->single_file, 0, + (double) seg->duration / timescale, + seg->range_length, seg->start_pos, NULL, + c->single_file ? os->initfile : seg->file, NULL); +} + +if (final) +hls_write_end_list(out_hls); + +avio_close(out_hls); +if (use_rename) +avpriv_io_move(temp_filename_hls, filename_hls); +} + } static char *xmlescape(const char *str) { @@ -391,7 +449,8 @@ static void format_date_now(char *buf, int size) } } -static int write_adaptati
[FFmpeg-devel] [PATCH 1/2] avformat/hlsenc: Minor fix in setting http options for master playlist
--- libavformat/hlsenc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..525605b 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1089,8 +1089,7 @@ static int create_master_playlist(AVFormatContext *s, return 0; } -if (hls->user_agent) - av_dict_set(&options, "user-agent", hls->user_agent, 0); +set_http_options(s, &options, hls); ret = s->io_open(s, &master_pb, hls->master_m3u8_url, AVIO_FLAG_WRITE,\ &options); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/hlsenc: Refactor an inconsistent variable name
--- libavformat/hlsenc.c | 18 +- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 525605b..611cc99 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1074,7 +1074,7 @@ static int create_master_playlist(AVFormatContext *s, AVDictionary *options = NULL; unsigned int i, j; int m3u8_name_size, ret, bandwidth; -char *m3U8_rel_name; +char *m3u8_rel_name; input_vs->m3u8_created = 1; if (!hls->master_m3u8_created) { @@ -1108,14 +1108,14 @@ static int create_master_playlist(AVFormatContext *s, vs = &(hls->var_streams[i]); m3u8_name_size = strlen(vs->m3u8_name) + 1; -m3U8_rel_name = av_malloc(m3u8_name_size); -if (!m3U8_rel_name) { +m3u8_rel_name = av_malloc(m3u8_name_size); +if (!m3u8_rel_name) { ret = AVERROR(ENOMEM); goto fail; } -av_strlcpy(m3U8_rel_name, vs->m3u8_name, m3u8_name_size); +av_strlcpy(m3u8_rel_name, vs->m3u8_name, m3u8_name_size); ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name, - m3U8_rel_name, m3u8_name_size); + m3u8_rel_name, m3u8_name_size); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Unable to find relative URL\n"); goto fail; @@ -1145,7 +1145,7 @@ static int create_master_playlist(AVFormatContext *s, if (!bandwidth) { av_log(NULL, AV_LOG_WARNING, "Bandwidth info not available, set audio and video bitrates\n"); -av_freep(&m3U8_rel_name); +av_freep(&m3u8_rel_name); continue; } @@ -1153,14 +1153,14 @@ static int create_master_playlist(AVFormatContext *s, if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0) avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, vid_st->codecpar->height); -avio_printf(master_pb, "\n%s\n\n", m3U8_rel_name); +avio_printf(master_pb, "\n%s\n\n", m3u8_rel_name); -av_freep(&m3U8_rel_name); +av_freep(&m3u8_rel_name); } fail: if(ret >=0) hls->master_m3u8_created = 1; -av_freep(&m3U8_rel_name); +av_freep(&m3u8_rel_name); ff_format_io_close(s, &master_pb); return ret; } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/1] avformat/dashenc: Associate mpd extension with dash muxer
--- libavformat/dashenc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 201668a..0fee3cd 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1219,6 +1219,7 @@ static const AVClass dash_class = { AVOutputFormat ff_dash_muxer = { .name = "dash", .long_name = NULL_IF_CONFIG_SMALL("DASH Muxer"), +.extensions = "mpd", .priv_data_size = sizeof(DASHContext), .audio_codec= AV_CODEC_ID_AAC, .video_codec= AV_CODEC_ID_H264, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] avformat/hlsenc: Modularized playlist creation to allow reuse
--- libavformat/hlsenc.c | 237 +++ libavformat/hlsenc.h | 67 +++ 2 files changed, 193 insertions(+), 111 deletions(-) create mode 100644 libavformat/hlsenc.h diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..d5b1b98 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "hlsenc.h" #include "internal.h" #include "os_support.h" @@ -73,35 +74,11 @@ typedef struct HLSSegment { struct HLSSegment *next; } HLSSegment; -typedef enum HLSFlags { -// Generate a single media file and use byte ranges in the playlist. -HLS_SINGLE_FILE = (1 << 0), -HLS_DELETE_SEGMENTS = (1 << 1), -HLS_ROUND_DURATIONS = (1 << 2), -HLS_DISCONT_START = (1 << 3), -HLS_OMIT_ENDLIST = (1 << 4), -HLS_SPLIT_BY_TIME = (1 << 5), -HLS_APPEND_LIST = (1 << 6), -HLS_PROGRAM_DATE_TIME = (1 << 7), -HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime e.g.: %%03d -HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime e.g.: %%09t -HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s -HLS_TEMP_FILE = (1 << 11), -HLS_PERIODIC_REKEY = (1 << 12), -} HLSFlags; - typedef enum { SEGMENT_TYPE_MPEGTS, SEGMENT_TYPE_FMP4, } SegmentType; -typedef enum { -PLAYLIST_TYPE_NONE, -PLAYLIST_TYPE_EVENT, -PLAYLIST_TYPE_VOD, -PLAYLIST_TYPE_NB, -} PlaylistType; - typedef struct VariantStream { unsigned number; int64_t sequence; @@ -206,6 +183,113 @@ typedef struct HLSContext { unsigned int master_publish_rate; } HLSContext; +void ff_hls_write_playlist_version(AVIOContext *out, int version) { +if (!out) +return; +avio_printf(out, "#EXTM3U\n"); +avio_printf(out, "#EXT-X-VERSION:%d\n", version); +} + +void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, + int bandwidth, char *filename) { +if (!out || !filename) +return; + +if (!bandwidth) { +av_log(NULL, AV_LOG_WARNING, +"Bandwidth info not available, set audio and video bitrates\n"); +return; +} + +avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); +if (st && st->codecpar->width > 0 && st->codecpar->height > 0) +avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width, +st->codecpar->height); +avio_printf(out, "\n%s\n\n", filename); +} + +void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, + int target_duration, int64_t sequence, + uint32_t playlist_type) { +if (!out) +return; +ff_hls_write_playlist_version(out, version); +if (allowcache == 0 || allowcache == 1) { +avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", allowcache == 0 ? "NO" : "YES"); +} +avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); +avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); +av_log(NULL, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); + +if (playlist_type == PLAYLIST_TYPE_EVENT) { +avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); +} else if (playlist_type == PLAYLIST_TYPE_VOD) { +avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); +} +} + +void ff_hls_write_init_file(AVIOContext *out, char *filename, +int byterange_mode, int64_t size, int64_t pos) { +avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", filename); +if (byterange_mode) { +avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", size, pos); +} +avio_printf(out, "\n"); +} + +void ff_hls_write_file_entry(AVIOContext *out, int insert_discont, +int byterange_mode, uint32_t flags, double duration, +int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set +char *baseurl, //Ignored if NULL +char *filename, double *prog_date_time) { +if (!out || !filename) +return; + +if (insert_discont) { +avio_printf(out, "#EXT-X-DISCONTINUITY\n"); +} +if (flags & HLS_ROUND_DURATIONS) +avio_printf(out, "#EXTINF:%ld,\n", lrint(duration)); +else +avio_printf(out, "#EXTINF:%f,\n", duration); +if (byterange_mode) +avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", size, pos); + +if ((flags & HLS_PROGRAM_DATE_TIME) && prog_date_time) { +time_t tt, wrongsecs; +int milli; +struct tm *tm, tmpbuf; +char buf0[128], buf1[128]; +tt = (int64_t)prog_date_time; +milli = av_clip(lrint(1000*(*prog
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/dashenc.c | 102 -- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..1cf2481 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 201668a..4c3962a 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsenc.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,13 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, const char *base_url, int id) { +if (base_url) +sprintf(playlist_name, "%smedia_%d.m3u8", base_url, id); +else +sprintf(playlist_name, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +272,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +333,54 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +if (target_duration < seg->duration) +target_duration = seg->duration; +} +target_duration = lrint((double) target_duration / timescale); + +ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +ff_hls_write_init_file(out_hls, os->initfile, c->single_file, + os->init_range_length, os->init_start_pos); + +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +ff_hls_write_file_entry(out_hls, 0, c->single_file, 0, +(double) seg->duration / timescale, +seg->range_length, seg->start_pos, NULL, +c->single_file ? os->initfile : seg->file, +NULL); +} + +if (final) +ff_hls_write_end_list(out_hls); + +avio_close(out_hls); +if (use_rename) +avpriv_io_move(temp_filename_hls, filename_hls); +} + } static char *xmlescape(const char *str) { @@ -391,7 +450,8 @@ static
[FFmpeg-devel] [PATCH v3 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/dashenc.c | 103 -- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..1cf2481 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 201668a..3fd1ef3 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsenc.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,14 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, int string_size, + const char *base_url, int id) { +if (base_url) +snprintf(playlist_name, string_size, "%smedia_%d.m3u8", base_url, id); +else +snprintf(playlist_name, string_size, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +273,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +334,55 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +if (target_duration < seg->duration) +target_duration = seg->duration; +} +target_duration = lrint((double) target_duration / timescale); + +ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +ff_hls_write_init_file(out_hls, os->initfile, c->single_file, + os->init_range_length, os->init_start_pos); + +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +ff_hls_write_file_entry(out_hls, 0, c->single_file, 0, +(double) seg->duration / timescale, +seg->range_length, seg->start_pos, NULL, +c->single_file ? os->initfile : seg->file, +NULL); +} + +if (final) +ff_hls_write_end_list(out_hls); + +avio_close(out_hls); +if (use_rename) +a
[FFmpeg-devel] [PATCH] avformat/hlsenc: Added option to add EXT-X-INDEPENDENT-SEGMENTS tag
--- doc/muxers.texi | 4 libavformat/hlsenc.c | 14 ++ 2 files changed, 18 insertions(+) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..9d9ca31 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -737,6 +737,10 @@ The file specified by @code{hls_key_info_file} will be checked periodically and detect updates to the encryption info. Be sure to replace this file atomically, including the file containing the AES encryption key. +@item independent_segments +Add the @code{#EXT-X-INDEPENDENT-SEGMENTS} to playlists that has video segments +and when all the segments of that playlist are guaranteed to start with a Key frame. + @item split_by_time Allow segments to start on frames other than keyframes. This improves behavior on some players when the time between keyframes is inconsistent, diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..5fc355a 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -88,6 +88,7 @@ typedef enum HLSFlags { HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s HLS_TEMP_FILE = (1 << 11), HLS_PERIODIC_REKEY = (1 << 12), +HLS_INDEPENDENT_SEGMENTS = (1 << 13), } HLSFlags; typedef enum { @@ -1191,6 +1192,10 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) sequence = 0; } +if (hls->flags & HLS_INDEPENDENT_SEGMENTS) { +hls->version = 6; +} + if (hls->segment_type == SEGMENT_TYPE_FMP4) { hls->version = 7; } @@ -1220,6 +1225,9 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) avio_printf(out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } +if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { +avio_printf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); +} for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { @@ -1732,6 +1740,11 @@ static int hls_write_header(AVFormatContext *s) vs->start_pts = AV_NOPTS_VALUE; vs->current_segment_final_filename_fmt[0] = '\0'; +if (hls->flags & HLS_SPLIT_BY_TIME) { +// Independent segments cannot be guaranteed when splitting by time +hls->flags &= ~HLS_INDEPENDENT_SEGMENTS; +} + if (hls->flags & HLS_PROGRAM_DATE_TIME) { time_t now0; time(&now0); @@ -2323,6 +2336,7 @@ static const AVOption options[] = { {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, "flags"}, {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"}, {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"}, +{"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"}, {"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" }, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/hlsenc: Added option to add EXT-X-INDEPENDENT-SEGMENTS tag
--- doc/muxers.texi | 4 libavformat/hlsenc.c | 17 + 2 files changed, 21 insertions(+) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..9d9ca31 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -737,6 +737,10 @@ The file specified by @code{hls_key_info_file} will be checked periodically and detect updates to the encryption info. Be sure to replace this file atomically, including the file containing the AES encryption key. +@item independent_segments +Add the @code{#EXT-X-INDEPENDENT-SEGMENTS} to playlists that has video segments +and when all the segments of that playlist are guaranteed to start with a Key frame. + @item split_by_time Allow segments to start on frames other than keyframes. This improves behavior on some players when the time between keyframes is inconsistent, diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..bad5e14 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -88,6 +88,7 @@ typedef enum HLSFlags { HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s HLS_TEMP_FILE = (1 << 11), HLS_PERIODIC_REKEY = (1 << 12), +HLS_INDEPENDENT_SEGMENTS = (1 << 13), } HLSFlags; typedef enum { @@ -1191,6 +1192,10 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) sequence = 0; } +if (hls->flags & HLS_INDEPENDENT_SEGMENTS) { +hls->version = 6; +} + if (hls->segment_type == SEGMENT_TYPE_FMP4) { hls->version = 7; } @@ -1220,6 +1225,9 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) avio_printf(out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } +if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { +avio_printf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); +} for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { @@ -1732,6 +1740,14 @@ static int hls_write_header(AVFormatContext *s) vs->start_pts = AV_NOPTS_VALUE; vs->current_segment_final_filename_fmt[0] = '\0'; +if (hls->flags & HLS_SPLIT_BY_TIME) { +// Independent segments cannot be guaranteed when splitting by time +hls->flags &= ~HLS_INDEPENDENT_SEGMENTS; +av_log(s, AV_LOG_WARNING, + "'split_by_time' and 'independent_segments' cannot be enabled together. " + "Disabling 'independent_segments' flag\n"); +} + if (hls->flags & HLS_PROGRAM_DATE_TIME) { time_t now0; time(&now0); @@ -2323,6 +2339,7 @@ static const AVOption options[] = { {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, "flags"}, {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"}, {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"}, +{"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"}, {"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" }, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3] avformat/hlsenc: Added option to add EXT-X-INDEPENDENT-SEGMENTS tag
--- doc/muxers.texi | 4 libavformat/hlsenc.c | 17 + 2 files changed, 21 insertions(+) diff --git a/doc/muxers.texi b/doc/muxers.texi index 0bb8ad2..9d9ca31 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -737,6 +737,10 @@ The file specified by @code{hls_key_info_file} will be checked periodically and detect updates to the encryption info. Be sure to replace this file atomically, including the file containing the AES encryption key. +@item independent_segments +Add the @code{#EXT-X-INDEPENDENT-SEGMENTS} to playlists that has video segments +and when all the segments of that playlist are guaranteed to start with a Key frame. + @item split_by_time Allow segments to start on frames other than keyframes. This improves behavior on some players when the time between keyframes is inconsistent, diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 3c47ced..3010714 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -88,6 +88,7 @@ typedef enum HLSFlags { HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s HLS_TEMP_FILE = (1 << 11), HLS_PERIODIC_REKEY = (1 << 12), +HLS_INDEPENDENT_SEGMENTS = (1 << 13), } HLSFlags; typedef enum { @@ -1191,6 +1192,10 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) sequence = 0; } +if (hls->flags & HLS_INDEPENDENT_SEGMENTS) { +hls->version = 6; +} + if (hls->segment_type == SEGMENT_TYPE_FMP4) { hls->version = 7; } @@ -1220,6 +1225,9 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) avio_printf(out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } +if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { +avio_printf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); +} for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { @@ -1732,6 +1740,14 @@ static int hls_write_header(AVFormatContext *s) vs->start_pts = AV_NOPTS_VALUE; vs->current_segment_final_filename_fmt[0] = '\0'; +if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { +// Independent segments cannot be guaranteed when splitting by time +hls->flags &= ~HLS_INDEPENDENT_SEGMENTS; +av_log(s, AV_LOG_WARNING, + "'split_by_time' and 'independent_segments' cannot be enabled together. " + "Disabling 'independent_segments' flag\n"); +} + if (hls->flags & HLS_PROGRAM_DATE_TIME) { time_t now0; time(&now0); @@ -2323,6 +2339,7 @@ static const AVOption options[] = { {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, "flags"}, {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"}, {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"}, +{"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"}, {"use_localtime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"use_localtime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" }, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 1/2] avformat/hlsenc: Modularized playlist creation to allow reuse
--- libavformat/hlsenc.c | 238 +++ libavformat/hlsenc.h | 68 +++ 2 files changed, 194 insertions(+), 112 deletions(-) create mode 100644 libavformat/hlsenc.h diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..5c4f459 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "hlsenc.h" #include "internal.h" #include "os_support.h" @@ -73,36 +74,11 @@ typedef struct HLSSegment { struct HLSSegment *next; } HLSSegment; -typedef enum HLSFlags { -// Generate a single media file and use byte ranges in the playlist. -HLS_SINGLE_FILE = (1 << 0), -HLS_DELETE_SEGMENTS = (1 << 1), -HLS_ROUND_DURATIONS = (1 << 2), -HLS_DISCONT_START = (1 << 3), -HLS_OMIT_ENDLIST = (1 << 4), -HLS_SPLIT_BY_TIME = (1 << 5), -HLS_APPEND_LIST = (1 << 6), -HLS_PROGRAM_DATE_TIME = (1 << 7), -HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime e.g.: %%03d -HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime e.g.: %%09t -HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime e.g.: %%014s -HLS_TEMP_FILE = (1 << 11), -HLS_PERIODIC_REKEY = (1 << 12), -HLS_INDEPENDENT_SEGMENTS = (1 << 13), -} HLSFlags; - typedef enum { SEGMENT_TYPE_MPEGTS, SEGMENT_TYPE_FMP4, } SegmentType; -typedef enum { -PLAYLIST_TYPE_NONE, -PLAYLIST_TYPE_EVENT, -PLAYLIST_TYPE_VOD, -PLAYLIST_TYPE_NB, -} PlaylistType; - typedef struct VariantStream { unsigned number; int64_t sequence; @@ -207,6 +183,113 @@ typedef struct HLSContext { unsigned int master_publish_rate; } HLSContext; +void ff_hls_write_playlist_version(AVIOContext *out, int version) { +if (!out) +return; +avio_printf(out, "#EXTM3U\n"); +avio_printf(out, "#EXT-X-VERSION:%d\n", version); +} + +void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, + int bandwidth, char *filename) { +if (!out || !filename) +return; + +if (!bandwidth) { +av_log(NULL, AV_LOG_WARNING, +"Bandwidth info not available, set audio and video bitrates\n"); +return; +} + +avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); +if (st && st->codecpar->width > 0 && st->codecpar->height > 0) +avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width, +st->codecpar->height); +avio_printf(out, "\n%s\n\n", filename); +} + +void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, + int target_duration, int64_t sequence, + uint32_t playlist_type) { +if (!out) +return; +ff_hls_write_playlist_version(out, version); +if (allowcache == 0 || allowcache == 1) { +avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", allowcache == 0 ? "NO" : "YES"); +} +avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); +avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); +av_log(NULL, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); + +if (playlist_type == PLAYLIST_TYPE_EVENT) { +avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); +} else if (playlist_type == PLAYLIST_TYPE_VOD) { +avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); +} +} + +void ff_hls_write_init_file(AVIOContext *out, char *filename, +int byterange_mode, int64_t size, int64_t pos) { +avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", filename); +if (byterange_mode) { +avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", size, pos); +} +avio_printf(out, "\n"); +} + +void ff_hls_write_file_entry(AVIOContext *out, int insert_discont, +int byterange_mode, uint32_t flags, double duration, +int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set +char *baseurl, //Ignored if NULL +char *filename, double *prog_date_time) { +if (!out || !filename) +return; + +if (insert_discont) { +avio_printf(out, "#EXT-X-DISCONTINUITY\n"); +} +if (flags & HLS_ROUND_DURATIONS) +avio_printf(out, "#EXTINF:%ld,\n", lrint(duration)); +else +avio_printf(out, "#EXTINF:%f,\n", duration); +if (byterange_mode) +avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", size, pos); + +if ((flags & HLS_PROGRAM_DATE_TIME) && prog_date_time) { +time_t tt, wrongsecs; +int milli; +struct tm *tm, tmpbuf; +char buf0[128], buf1[128]; +tt = (int64_t)prog_date_time;
[FFmpeg-devel] [PATCH v4 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/Makefile | 2 +- libavformat/dashenc.c | 103 -- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 9d9ca31..16b033f 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/Makefile b/libavformat/Makefile index fd8b9f9..4bffdf2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -135,7 +135,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER)+= concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER)+= rawenc.o -OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o +OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o hlsplaylist.o OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER)+= daudenc.o diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 201668a..952a52f 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,14 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, int string_size, + const char *base_url, int id) { +if (base_url) +snprintf(playlist_name, string_size, "%smedia_%d.m3u8", base_url, id); +else +snprintf(playlist_name, string_size, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +273,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +334,55 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +if (target_duration < seg->duration) +target_duration = seg->duration; +} +target_duration = lrint((double) target_duration / timescale); + +ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +ff_hls_wri
[FFmpeg-devel] [PATCH v4 1/2] avformat/hlsenc: Modularized playlist creation to allow reuse
--- libavformat/Makefile | 2 +- libavformat/hlsenc.c | 115 +++ libavformat/hlsplaylist.c | 136 ++ libavformat/hlsplaylist.h | 51 + 4 files changed, 209 insertions(+), 95 deletions(-) create mode 100644 libavformat/hlsplaylist.c create mode 100644 libavformat/hlsplaylist.h diff --git a/libavformat/Makefile b/libavformat/Makefile index b1e7b19..fd8b9f9 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -215,7 +215,7 @@ OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HEVC_MUXER)+= rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o -OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o +OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o OBJS-$(CONFIG_ICO_MUXER) += icoenc.o diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..7c759a8 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "os_support.h" @@ -96,13 +97,6 @@ typedef enum { SEGMENT_TYPE_FMP4, } SegmentType; -typedef enum { -PLAYLIST_TYPE_NONE, -PLAYLIST_TYPE_EVENT, -PLAYLIST_TYPE_VOD, -PLAYLIST_TYPE_NB, -} PlaylistType; - typedef struct VariantStream { unsigned number; int64_t sequence; @@ -1023,19 +1017,6 @@ static void hls_free_segments(HLSSegment *p) } } -static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version, - int target_duration, int64_t sequence) -{ -avio_printf(out, "#EXTM3U\n"); -avio_printf(out, "#EXT-X-VERSION:%d\n", version); -if (hls->allowcache == 0 || hls->allowcache == 1) { -avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES"); -} -avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); -avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -} - static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc) { size_t len = strlen(oc->filename); @@ -1101,8 +1082,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -avio_printf(master_pb, "#EXTM3U\n"); -avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version); +ff_hls_write_playlist_version(master_pb, hls->version); /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { @@ -1143,18 +1123,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += aud_st->codecpar->bit_rate; bandwidth += bandwidth / 10; -if (!bandwidth) { -av_log(NULL, AV_LOG_WARNING, -"Bandwidth info not available, set audio and video bitrates\n"); -av_freep(&m3u8_rel_name); -continue; -} - -avio_printf(master_pb, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); -if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0) -avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, -vid_st->codecpar->height); -avio_printf(master_pb, "\n%s\n\n", m3u8_rel_name); +ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name); av_freep(&m3u8_rel_name); } @@ -1183,6 +1152,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) char *iv_string = NULL; AVDictionary *options = NULL; double prog_date_time = vs->initial_prog_date_time; +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; @@ -1213,12 +1183,8 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; -write_m3u8_head_block(hls, out, hls->version, target_duration, sequence); -if (hls->pl_type == PLAYLIST_TYPE_EVENT) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); -} else if (hls->pl_type == PLAYLIST_TYPE_VOD) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); -} +ff_hls_write_playlist_header(out, hls->version, hls->allowcache, + target_duration, sequence, hls->pl_type); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ avio_printf(out, "#EXT-X-DISCONTINUITY\n"); @@ -1238,74 +1204,35 @@ static int hls_window(AVFormatContext *s, int last, Variant
[FFmpeg-devel] [PATCH v2 1/3] avformat/hlsenc:addition of #EXT-X-MEDIA tag and AUDIO attribute
From: Vishwanath Dixit --- doc/muxers.texi | 12 ++ libavformat/hlsenc.c | 63 ++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 9d9ca31..7e6d594 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -831,6 +831,18 @@ be a video only stream with video bitrate 1000k, the second variant stream will be an audio only stream with bitrate 64k and the third variant stream will be a video only stream with bitrate 256k. Here, three media playlist with file names out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created. +@example +ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k \ + -map 0:a -map 0:a -map 0:v -map 0:v -f hls \ + -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \ + -master_pl_name master.m3u8 \ + http://example.com/live/out.m3u8 +@end example +This example creates two audio only and two video only variant streams. In +addition to the #EXT-X-STREAM-INF tag for each variant stream in the master +playlist, #EXT-X-MEDIA tag is also added for the two audio only variant streams +and they are mapped to the two video only variant streams with audio group names +'aud_low' and 'aud_high'. By default, a single hls variant containing all the encoded streams is created. diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..58d7c58 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -147,6 +147,7 @@ typedef struct VariantStream { AVStream **streams; unsigned int nb_streams; int m3u8_created; /* status of media play-list creation */ +char *agroup; /* audio group name */ char *baseurl; } VariantStream; @@ -1069,7 +1070,7 @@ static int create_master_playlist(AVFormatContext *s, VariantStream * const input_vs) { HLSContext *hls = s->priv_data; -VariantStream *vs; +VariantStream *vs, *temp_vs; AVStream *vid_st, *aud_st; AVIOContext *master_pb = 0; AVDictionary *options = NULL; @@ -1104,6 +1105,34 @@ static int create_master_playlist(AVFormatContext *s, avio_printf(master_pb, "#EXTM3U\n"); avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version); +/* For audio only variant streams add #EXT-X-MEDIA tag with attributes*/ +for (i = 0; i < hls->nb_varstreams; i++) { +vs = &(hls->var_streams[i]); + +if (vs->has_video || vs->has_subtitle || !vs->agroup) +continue; + +m3u8_name_size = strlen(vs->m3u8_name) + 1; +m3u8_rel_name = av_malloc(m3u8_name_size); +if (!m3u8_rel_name) { +ret = AVERROR(ENOMEM); +goto fail; +} +av_strlcpy(m3u8_rel_name, vs->m3u8_name, m3u8_name_size); +ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name, + m3u8_rel_name, m3u8_name_size); +if (ret < 0) { +av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n"); +goto fail; +} + +avio_printf(master_pb, "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"group_%s\"", +vs->agroup); +avio_printf(master_pb, ",NAME=\"audio_0\",DEFAULT=YES,URI=\"%s\"\n", +m3u8_rel_name); +av_freep(&m3u8_rel_name); +} + /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { vs = &(hls->var_streams[i]); @@ -1136,6 +1165,25 @@ static int create_master_playlist(AVFormatContext *s, continue; } +/** + * Traverse through the list of audio only rendition streams and find + * the rendition which has highest bitrate in the same audio group + */ +if (vs->agroup) { +for (j = 0; j < hls->nb_varstreams; j++) { +temp_vs = &(hls->var_streams[j]); +if (!temp_vs->has_video && !temp_vs->has_subtitle && +temp_vs->agroup && +!strcmp(temp_vs->agroup, vs->agroup)) { +if (!aud_st) +aud_st = temp_vs->streams[0]; +if (temp_vs->streams[0]->codecpar->bit_rate > +aud_st->codecpar->bit_rate) +aud_st = temp_vs->streams[0]; +} +} +} + bandwidth = 0; if (vid_st) bandwidth += vid_st->codecpar->bit_rate; @@ -1154,6 +1202,10 @@ static int create_master_playlist(AVFormatContext *s, if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0) avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, vid_st->codecpar->height); + +if (vs->agroup && aud_st) +avio_printf(master_pb, ",AUDIO=\"group_%s\"", vs->agroup); + avio_printf(master_pb, "\n%s\n\n", m3u8
[FFmpeg-devel] [PATCH v2 2/3] avcodec/libx264:setting profile and level in avcodec context
From: Vishwanath Dixit --- libavcodec/libx264.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index 9c67c91..6b93aa8 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -454,6 +454,9 @@ static av_cold int X264_init(AVCodecContext *avctx) X264Context *x4 = avctx->priv_data; AVCPBProperties *cpb_props; int sw,sh; +x264_nal_t *nal; +uint8_t *p; +int nnal, s, i; if (avctx->global_quality > 0) av_log(avctx, AV_LOG_WARNING, "-qscale is ignored, -crf is recommended.\n"); @@ -799,12 +802,11 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!x4->enc) return AVERROR_EXTERNAL; -if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { -x264_nal_t *nal; -uint8_t *p; -int nnal, s, i; +s = x264_encoder_headers(x4->enc, &nal, &nnal); +avctx->profile = nal->p_payload[5]; +avctx->level = nal->p_payload[7]; -s = x264_encoder_headers(x4->enc, &nal, &nnal); +if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { avctx->extradata = p = av_mallocz(s + AV_INPUT_BUFFER_PADDING_SIZE); if (!p) return AVERROR(ENOMEM); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/3] avformat/hlsenc:addition of CODECS attribute in the master playlist
From: Vishwanath Dixit --- libavformat/hlsenc.c | 65 +++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 58d7c58..8a634de 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1066,6 +1066,62 @@ static int get_relative_url(const char *master_url, const char *media_url, return 0; } +static char *get_codec_str(AVStream *vid_st, AVStream *aud_st) { +size_t codec_str_size = 64; +char *codec_str = av_malloc(codec_str_size); +int video_str_len = 0; + +if (!codec_str) +return NULL; + +if (!vid_st && !aud_st) { +goto fail; +} + +if (vid_st) { +if (vid_st->codecpar->profile != FF_PROFILE_UNKNOWN && +vid_st->codecpar->level != FF_LEVEL_UNKNOWN && +vid_st->codecpar->codec_id == AV_CODEC_ID_H264) { +/* TODO : Set constraint_set flags correctly. Hardcoded to 0 here. */ +snprintf(codec_str, codec_str_size, "avc1.%02x00%02x", + vid_st->codecpar->profile, vid_st->codecpar->level); +} else { +goto fail; +} +video_str_len = strlen(codec_str); +} + +if (aud_st) { +char *audio_str = codec_str; +if (video_str_len) { +codec_str[video_str_len] = ','; +video_str_len += 1; +audio_str += video_str_len; +codec_str_size -= video_str_len; +} +if (aud_st->codecpar->codec_id == AV_CODEC_ID_MP2) { +snprintf(audio_str, codec_str_size, "mp4a.40.33"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_MP3) { +snprintf(audio_str, codec_str_size, "mp4a.40.34"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_AAC) { +/* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */ +snprintf(audio_str, codec_str_size, "mp4a.40.2"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_AC3) { +snprintf(audio_str, codec_str_size, "mp4a.A5"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_EAC3) { +snprintf(audio_str, codec_str_size, "mp4a.A6"); +} else { +goto fail; +} +} + +return codec_str; + +fail: +av_free(codec_str); +return NULL; +} + static int create_master_playlist(AVFormatContext *s, VariantStream * const input_vs) { @@ -1076,7 +1132,7 @@ static int create_master_playlist(AVFormatContext *s, AVDictionary *options = NULL; unsigned int i, j; int m3u8_name_size, ret, bandwidth; -char *m3u8_rel_name; +char *m3u8_rel_name, *codec_str; input_vs->m3u8_created = 1; if (!hls->master_m3u8_created) { @@ -1203,6 +1259,13 @@ static int create_master_playlist(AVFormatContext *s, avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, vid_st->codecpar->height); +codec_str = get_codec_str(vid_st, aud_st); + +if (codec_str) { +avio_printf(master_pb, ",CODECS=\"%s\"", codec_str); +av_free(codec_str); +} + if (vs->agroup && aud_st) avio_printf(master_pb, ",AUDIO=\"group_%s\"", vs->agroup); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/hlsenc: Added context to av_log calls
--- libavformat/hlsenc.c | 9 - 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..379a4ec 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1057,7 +1057,6 @@ static int get_relative_url(const char *master_url, const char *media_url, if (p) { base_len = FFABS(p - master_url); if (av_strncasecmp(master_url, media_url, base_len)) { -av_log(NULL, AV_LOG_WARNING, "Unable to find relative url\n"); return AVERROR(EINVAL); } } @@ -1096,7 +1095,7 @@ static int create_master_playlist(AVFormatContext *s, &options); av_dict_free(&options); if (ret < 0) { -av_log(NULL, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", +av_log(s, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", hls->master_m3u8_url); goto fail; } @@ -1118,7 +1117,7 @@ static int create_master_playlist(AVFormatContext *s, ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name, m3u8_rel_name, m3u8_name_size); if (ret < 0) { -av_log(NULL, AV_LOG_ERROR, "Unable to find relative URL\n"); +av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n"); goto fail; } @@ -1132,7 +1131,7 @@ static int create_master_playlist(AVFormatContext *s, } if (!vid_st && !aud_st) { -av_log(NULL, AV_LOG_WARNING, "Media stream not found\n"); +av_log(s, AV_LOG_WARNING, "Media stream not found\n"); continue; } @@ -1144,7 +1143,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += bandwidth / 10; if (!bandwidth) { -av_log(NULL, AV_LOG_WARNING, +av_log(s, AV_LOG_WARNING, "Bandwidth info not available, set audio and video bitrates\n"); av_freep(&m3u8_rel_name); continue; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2] avformat/hlsenc: Added context to av_log calls
Also removed a redundant av_log call --- libavformat/hlsenc.c | 9 - 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..379a4ec 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1057,7 +1057,6 @@ static int get_relative_url(const char *master_url, const char *media_url, if (p) { base_len = FFABS(p - master_url); if (av_strncasecmp(master_url, media_url, base_len)) { -av_log(NULL, AV_LOG_WARNING, "Unable to find relative url\n"); return AVERROR(EINVAL); } } @@ -1096,7 +1095,7 @@ static int create_master_playlist(AVFormatContext *s, &options); av_dict_free(&options); if (ret < 0) { -av_log(NULL, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", +av_log(s, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", hls->master_m3u8_url); goto fail; } @@ -1118,7 +1117,7 @@ static int create_master_playlist(AVFormatContext *s, ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name, m3u8_rel_name, m3u8_name_size); if (ret < 0) { -av_log(NULL, AV_LOG_ERROR, "Unable to find relative URL\n"); +av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n"); goto fail; } @@ -1132,7 +1131,7 @@ static int create_master_playlist(AVFormatContext *s, } if (!vid_st && !aud_st) { -av_log(NULL, AV_LOG_WARNING, "Media stream not found\n"); +av_log(s, AV_LOG_WARNING, "Media stream not found\n"); continue; } @@ -1144,7 +1143,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += bandwidth / 10; if (!bandwidth) { -av_log(NULL, AV_LOG_WARNING, +av_log(s, AV_LOG_WARNING, "Bandwidth info not available, set audio and video bitrates\n"); av_freep(&m3u8_rel_name); continue; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 2/3] avcodec/libx264:setting profile and level in avcodec context
From: Vishwanath Dixit --- libavcodec/libx264.c | 17 - 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index 9c67c91..2f7f53c 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/eval.h" #include "libavutil/internal.h" #include "libavutil/opt.h" @@ -454,6 +455,9 @@ static av_cold int X264_init(AVCodecContext *avctx) X264Context *x4 = avctx->priv_data; AVCPBProperties *cpb_props; int sw,sh; +x264_nal_t *nal; +uint8_t *p; +int nnal, s, i; if (avctx->global_quality > 0) av_log(avctx, AV_LOG_WARNING, "-qscale is ignored, -crf is recommended.\n"); @@ -799,12 +803,15 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!x4->enc) return AVERROR_EXTERNAL; -if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { -x264_nal_t *nal; -uint8_t *p; -int nnal, s, i; +s = x264_encoder_headers(x4->enc, &nal, &nnal); +// Assert for NAL start code and SPS unit type +av_assert0((nal->p_payload[0] | nal->p_payload[1] | nal->p_payload[2]) == 0 && nal->p_payload[3] == 1); +av_assert0((nal->p_payload[4] & 0x1F) == 7); +// bits 0-7 LSB for profile. bits 8-11 for constrained set flags. +avctx->profile = ((uint32_t)nal->p_payload[5]) | (((uint32_t)nal->p_payload[6] >> 4) << 8); +avctx->level = nal->p_payload[7]; -s = x264_encoder_headers(x4->enc, &nal, &nnal); +if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { avctx->extradata = p = av_mallocz(s + AV_INPUT_BUFFER_PADDING_SIZE); if (!p) return AVERROR(ENOMEM); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 3/3] avformat/hlsenc:addition of CODECS attribute in the master playlist
From: Vishwanath Dixit --- libavformat/hlsenc.c | 66 +++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 58d7c58..92c90a1 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1066,6 +1066,63 @@ static int get_relative_url(const char *master_url, const char *media_url, return 0; } +static char *get_codec_str(AVStream *vid_st, AVStream *aud_st) { +size_t codec_str_size = 64; +char *codec_str = av_malloc(codec_str_size); +int video_str_len = 0; + +if (!codec_str) +return NULL; + +if (!vid_st && !aud_st) { +goto fail; +} + +if (vid_st) { +if (vid_st->codecpar->profile != FF_PROFILE_UNKNOWN && +vid_st->codecpar->level != FF_LEVEL_UNKNOWN && +vid_st->codecpar->codec_id == AV_CODEC_ID_H264) { +snprintf(codec_str, codec_str_size, "avc1.%02x%02x%02x", + vid_st->codecpar->profile & 0xFF, + ((vid_st->codecpar->profile >> 8) << 4) & 0xFF, + vid_st->codecpar->level); +} else { +goto fail; +} +video_str_len = strlen(codec_str); +} + +if (aud_st) { +char *audio_str = codec_str; +if (video_str_len) { +codec_str[video_str_len] = ','; +video_str_len += 1; +audio_str += video_str_len; +codec_str_size -= video_str_len; +} +if (aud_st->codecpar->codec_id == AV_CODEC_ID_MP2) { +snprintf(audio_str, codec_str_size, "mp4a.40.33"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_MP3) { +snprintf(audio_str, codec_str_size, "mp4a.40.34"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_AAC) { +/* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */ +snprintf(audio_str, codec_str_size, "mp4a.40.2"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_AC3) { +snprintf(audio_str, codec_str_size, "mp4a.A5"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_EAC3) { +snprintf(audio_str, codec_str_size, "mp4a.A6"); +} else { +goto fail; +} +} + +return codec_str; + +fail: +av_free(codec_str); +return NULL; +} + static int create_master_playlist(AVFormatContext *s, VariantStream * const input_vs) { @@ -1076,7 +1133,7 @@ static int create_master_playlist(AVFormatContext *s, AVDictionary *options = NULL; unsigned int i, j; int m3u8_name_size, ret, bandwidth; -char *m3u8_rel_name; +char *m3u8_rel_name, *codec_str; input_vs->m3u8_created = 1; if (!hls->master_m3u8_created) { @@ -1203,6 +1260,13 @@ static int create_master_playlist(AVFormatContext *s, avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, vid_st->codecpar->height); +codec_str = get_codec_str(vid_st, aud_st); + +if (codec_str) { +avio_printf(master_pb, ",CODECS=\"%s\"", codec_str); +av_free(codec_str); +} + if (vs->agroup && aud_st) avio_printf(master_pb, ",AUDIO=\"group_%s\"", vs->agroup); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v4 2/3] avcodec/libx264:setting profile and level in avcodec context
From: Vishwanath Dixit --- libavcodec/libx264.c | 18 +- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index 9c67c91..545826c 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -19,11 +19,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/eval.h" #include "libavutil/internal.h" #include "libavutil/opt.h" #include "libavutil/mem.h" #include "libavutil/pixdesc.h" +#include "libavutil/reverse.h" #include "libavutil/stereo3d.h" #include "libavutil/intreadwrite.h" #include "avcodec.h" @@ -454,6 +456,9 @@ static av_cold int X264_init(AVCodecContext *avctx) X264Context *x4 = avctx->priv_data; AVCPBProperties *cpb_props; int sw,sh; +x264_nal_t *nal; +uint8_t *p; +int nnal, s, i; if (avctx->global_quality > 0) av_log(avctx, AV_LOG_WARNING, "-qscale is ignored, -crf is recommended.\n"); @@ -799,12 +804,15 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!x4->enc) return AVERROR_EXTERNAL; -if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { -x264_nal_t *nal; -uint8_t *p; -int nnal, s, i; +s = x264_encoder_headers(x4->enc, &nal, &nnal); +// Assert for NAL start code and SPS unit type +av_assert0((nal->p_payload[0] | nal->p_payload[1] | nal->p_payload[2]) == 0 && nal->p_payload[3] == 1); +av_assert0((nal->p_payload[4] & 0x1F) == 7); +// bits 0-7 LSB for profile. bits 8-11 for constrained set flags. +avctx->profile = ((uint32_t)nal->p_payload[5]) | ((uint32_t)ff_reverse[nal->p_payload[6]] << 8); +avctx->level = nal->p_payload[7]; -s = x264_encoder_headers(x4->enc, &nal, &nnal); +if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { avctx->extradata = p = av_mallocz(s + AV_INPUT_BUFFER_PADDING_SIZE); if (!p) return AVERROR(ENOMEM); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v4 3/3] avformat/hlsenc:addition of CODECS attribute in the master playlist
From: Vishwanath Dixit --- libavformat/hlsenc.c | 67 +++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 58d7c58..d48963c 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -39,6 +39,7 @@ #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/random_seed.h" +#include "libavutil/reverse.h" #include "libavutil/opt.h" #include "libavutil/log.h" #include "libavutil/time_internal.h" @@ -1066,6 +1067,63 @@ static int get_relative_url(const char *master_url, const char *media_url, return 0; } +static char *get_codec_str(AVStream *vid_st, AVStream *aud_st) { +size_t codec_str_size = 64; +char *codec_str = av_malloc(codec_str_size); +int video_str_len = 0; + +if (!codec_str) +return NULL; + +if (!vid_st && !aud_st) { +goto fail; +} + +if (vid_st) { +if (vid_st->codecpar->profile != FF_PROFILE_UNKNOWN && +vid_st->codecpar->level != FF_LEVEL_UNKNOWN && +vid_st->codecpar->codec_id == AV_CODEC_ID_H264) { +snprintf(codec_str, codec_str_size, "avc1.%02x%02x%02x", + vid_st->codecpar->profile & 0xFF, + ff_reverse[(vid_st->codecpar->profile >> 8) & 0xFF], + vid_st->codecpar->level); +} else { +goto fail; +} +video_str_len = strlen(codec_str); +} + +if (aud_st) { +char *audio_str = codec_str; +if (video_str_len) { +codec_str[video_str_len] = ','; +video_str_len += 1; +audio_str += video_str_len; +codec_str_size -= video_str_len; +} +if (aud_st->codecpar->codec_id == AV_CODEC_ID_MP2) { +snprintf(audio_str, codec_str_size, "mp4a.40.33"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_MP3) { +snprintf(audio_str, codec_str_size, "mp4a.40.34"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_AAC) { +/* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */ +snprintf(audio_str, codec_str_size, "mp4a.40.2"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_AC3) { +snprintf(audio_str, codec_str_size, "mp4a.A5"); +} else if (aud_st->codecpar->codec_id == AV_CODEC_ID_EAC3) { +snprintf(audio_str, codec_str_size, "mp4a.A6"); +} else { +goto fail; +} +} + +return codec_str; + +fail: +av_free(codec_str); +return NULL; +} + static int create_master_playlist(AVFormatContext *s, VariantStream * const input_vs) { @@ -1076,7 +1134,7 @@ static int create_master_playlist(AVFormatContext *s, AVDictionary *options = NULL; unsigned int i, j; int m3u8_name_size, ret, bandwidth; -char *m3u8_rel_name; +char *m3u8_rel_name, *codec_str; input_vs->m3u8_created = 1; if (!hls->master_m3u8_created) { @@ -1203,6 +1261,13 @@ static int create_master_playlist(AVFormatContext *s, avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, vid_st->codecpar->height); +codec_str = get_codec_str(vid_st, aud_st); + +if (codec_str) { +avio_printf(master_pb, ",CODECS=\"%s\"", codec_str); +av_free(codec_str); +} + if (vs->agroup && aud_st) avio_printf(master_pb, ",AUDIO=\"group_%s\"", vs->agroup); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v5 1/2] avformat/hlsenc: Modularized playlist creation to allow reuse
--- libavformat/Makefile | 2 +- libavformat/hlsenc.c | 115 +++--- libavformat/hlsplaylist.c | 138 ++ libavformat/hlsplaylist.h | 51 + 4 files changed, 211 insertions(+), 95 deletions(-) create mode 100644 libavformat/hlsplaylist.c create mode 100644 libavformat/hlsplaylist.h diff --git a/libavformat/Makefile b/libavformat/Makefile index b1e7b19..fd8b9f9 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -215,7 +215,7 @@ OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HEVC_MUXER)+= rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o -OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o +OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o OBJS-$(CONFIG_ICO_MUXER) += icoenc.o diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..7c759a8 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "os_support.h" @@ -96,13 +97,6 @@ typedef enum { SEGMENT_TYPE_FMP4, } SegmentType; -typedef enum { -PLAYLIST_TYPE_NONE, -PLAYLIST_TYPE_EVENT, -PLAYLIST_TYPE_VOD, -PLAYLIST_TYPE_NB, -} PlaylistType; - typedef struct VariantStream { unsigned number; int64_t sequence; @@ -1023,19 +1017,6 @@ static void hls_free_segments(HLSSegment *p) } } -static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version, - int target_duration, int64_t sequence) -{ -avio_printf(out, "#EXTM3U\n"); -avio_printf(out, "#EXT-X-VERSION:%d\n", version); -if (hls->allowcache == 0 || hls->allowcache == 1) { -avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES"); -} -avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); -avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -} - static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc) { size_t len = strlen(oc->filename); @@ -1101,8 +1082,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -avio_printf(master_pb, "#EXTM3U\n"); -avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version); +ff_hls_write_playlist_version(master_pb, hls->version); /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { @@ -1143,18 +1123,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += aud_st->codecpar->bit_rate; bandwidth += bandwidth / 10; -if (!bandwidth) { -av_log(NULL, AV_LOG_WARNING, -"Bandwidth info not available, set audio and video bitrates\n"); -av_freep(&m3u8_rel_name); -continue; -} - -avio_printf(master_pb, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); -if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0) -avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, -vid_st->codecpar->height); -avio_printf(master_pb, "\n%s\n\n", m3u8_rel_name); +ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name); av_freep(&m3u8_rel_name); } @@ -1183,6 +1152,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) char *iv_string = NULL; AVDictionary *options = NULL; double prog_date_time = vs->initial_prog_date_time; +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; @@ -1213,12 +1183,8 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; -write_m3u8_head_block(hls, out, hls->version, target_duration, sequence); -if (hls->pl_type == PLAYLIST_TYPE_EVENT) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); -} else if (hls->pl_type == PLAYLIST_TYPE_VOD) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); -} +ff_hls_write_playlist_header(out, hls->version, hls->allowcache, + target_duration, sequence, hls->pl_type); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ avio_printf(out, "#EXT-X-DISCONTINUITY\n"); @@ -1238,74 +1204,35 @@ static int hls_window(AVFormatContext *s, int last, VariantS
[FFmpeg-devel] [PATCH] avformat/hlsenc: Fixed initial setting for end_pts
This patch fixes Bug #6868 Sometimes end_pts is getting initialized to audio stream's first pts, while the duration is calculated based on video stream's pts. In this patch the end_pts is initialized with the correct stream's first pts. --- libavformat/hlsenc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30ccf73..6997a5c 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1737,6 +1737,7 @@ static int hls_write_header(AVFormatContext *s) vs->sequence = hls->start_sequence; hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE; vs->start_pts = AV_NOPTS_VALUE; +vs->end_pts = AV_NOPTS_VALUE; vs->current_segment_final_filename_fmt[0] = '\0'; if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { @@ -2111,7 +2112,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) if (vs->start_pts == AV_NOPTS_VALUE) { vs->start_pts = pkt->pts; -vs->end_pts = pkt->pts; } if (vs->has_video) { @@ -2123,6 +2123,8 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) is_ref_pkt = can_split = 0; if (is_ref_pkt) { +if (vs->end_pts == AV_NOPTS_VALUE) +vs->end_pts = pkt->pts; if (vs->new_start) { vs->new_start = 0; vs->duration = (double)(pkt->pts - vs->end_pts) -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 1/3] libavformat/avio: Utility function to return URLContext
--- libavformat/avio_internal.h | 8 libavformat/aviobuf.c | 13 + 2 files changed, 21 insertions(+) diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index c01835d..04c1ad5 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -133,6 +133,14 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); int ffio_fdopen(AVIOContext **s, URLContext *h); /** + * Return the URLContext associated with the AVIOContext + * + * @param s IO context + * @return pointer to URLContext or NULL. + */ +URLContext *ffio_geturlcontext(AVIOContext *s); + +/** * Open a write-only fake memory stream. The written data is not stored * anywhere - this is only used for measuring the amount of data * written. diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 3b4c843..86eb657 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -980,6 +980,19 @@ fail: return AVERROR(ENOMEM); } +URLContext* ffio_geturlcontext(AVIOContext *s) +{ +AVIOInternal *internal; +if (!s) +return NULL; + +internal = s->opaque; +if (internal && s->read_packet == io_read_packet) +return internal->h; +else +return NULL; +} + int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size) { uint8_t *buffer; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 2/3] libavformat/http: Handled multiple_requests option during write
--- libavformat/http.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/libavformat/http.c b/libavformat/http.c index 056d5f6..cf86adc 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -171,6 +171,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, const char *hoststr, const char *auth, const char *proxyauth, int *new_location); static int http_read_header(URLContext *h, int *new_location); +static int http_shutdown(URLContext *h, int flags); void ff_http_init_auth_state(URLContext *dest, const URLContext *src) { @@ -306,6 +307,11 @@ int ff_http_do_new_request(URLContext *h, const char *uri) AVDictionary *options = NULL; int ret; +ret = http_shutdown(h, h->flags); +if (ret < 0) +return ret; + +s->end_chunked_post = 0; s->chunkend = 0; s->off = 0; s->icy_data_read = 0; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3 3/3] libavformat/hlsenc: Persistent HTTP connections supported as an option
--- doc/muxers.texi | 3 +++ libavformat/hlsenc.c | 48 +--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 9d9ca31..8ec48c2 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -854,6 +854,9 @@ ffmpeg -re -i in.ts -f hls -master_pl_name master.m3u8 \ This example creates HLS master playlist with name master.m3u8 and keep publishing it repeatedly every after 30 segments i.e. every after 60s. +@item http_persistent +Use persistent HTTP connections. Applicable only for HTTP output. + @end table @anchor{ico} diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 6997a5c..d5c732f 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -45,6 +45,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "http.h" #include "internal.h" #include "os_support.h" @@ -205,6 +206,7 @@ typedef struct HLSContext { char *var_stream_map; /* user specified variant stream map string */ char *master_pl_name; unsigned int master_publish_rate; +int http_persistent; } HLSContext; static int get_int_from_double(double val) @@ -245,10 +247,38 @@ static int mkdir_p(const char *path) { return ret; } +static int is_http_proto(char *filename) { +const char *proto = avio_find_protocol_name(filename); +return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +} + +static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, + AVDictionary **options) { +HLSContext *hls = s->priv_data; +int http_base_proto = is_http_proto(filename); +int err; +if (!*pb || !http_base_proto || !hls->http_persistent) { +err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); +} else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); +err = ff_http_do_new_request(http_url_context, filename); +} +return err; +} + +static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { +HLSContext *hls = s->priv_data; +int http_base_proto = is_http_proto(filename); + +if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { +ff_format_io_close(s, pb); +} +} + static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c) { -const char *proto = avio_find_protocol_name(s->filename); -int http_base_proto = proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +int http_base_proto = is_http_proto(s->filename); if (c->method) { av_dict_set(options, "method", c->method, 0); @@ -258,6 +288,8 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } if (c->user_agent) av_dict_set(options, "user_agent", c->user_agent, 0); +if (c->http_persistent) +av_dict_set_int(options, "multiple_requests", 1, 0); } @@ -1437,17 +1469,17 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) err = AVERROR(ENOMEM); goto fail; } -err = s->io_open(s, &oc->pb, filename, AVIO_FLAG_WRITE, &options); +err = hlsenc_io_open(s, &oc->pb, filename, &options); av_free(filename); av_dict_free(&options); if (err < 0) return err; } else -if ((err = s->io_open(s, &oc->pb, oc->filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((err = hlsenc_io_open(s, &oc->pb, oc->filename, &options)) < 0) goto fail; if (vs->vtt_basename) { set_http_options(s, &options, c); -if ((err = s->io_open(s, &vtt_oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->filename, &options)) < 0) goto fail; } av_dict_free(&options); @@ -2165,11 +2197,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) avio_open_dyn_buf(&oc->pb); vs->packets_written = 0; ff_format_io_close(s, &vs->out); +hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } else { -ff_format_io_close(s, &oc->pb); +hlsenc_io_close(s, &oc->pb, oc->filename); } if (vs->vtt_avf) { -ff_format_io_close(s, &vs->vtt_avf->pb); +hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->filename); } } if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) { @@ -2355,6 +2388,7 @@ static const AVOption options[] = { {"var_stream_map", "Variant stream map string", OFFSET(var_stream_map), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0,E}, {"master_pl_name", "Create HLS master playlist with this name", OFFSET(master_pl_name), AV_OPT_TYPE_STRING, {.str = NULL},
[FFmpeg-devel] [PATCH v6 1/2] avformat/hlsenc: Modularized playlist creation to allow reuse
--- libavformat/Makefile | 2 +- libavformat/hlsenc.c | 115 +++--- libavformat/hlsplaylist.c | 138 ++ libavformat/hlsplaylist.h | 51 + 4 files changed, 211 insertions(+), 95 deletions(-) create mode 100644 libavformat/hlsplaylist.c create mode 100644 libavformat/hlsplaylist.h diff --git a/libavformat/Makefile b/libavformat/Makefile index b1e7b19..fd8b9f9 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -215,7 +215,7 @@ OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HEVC_MUXER)+= rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o -OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o +OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o OBJS-$(CONFIG_ICO_MUXER) += icoenc.o diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index d5c732f..f63b08d 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -46,6 +46,7 @@ #include "avformat.h" #include "avio_internal.h" #include "http.h" +#include "hlsplaylist.h" #include "internal.h" #include "os_support.h" @@ -97,13 +98,6 @@ typedef enum { SEGMENT_TYPE_FMP4, } SegmentType; -typedef enum { -PLAYLIST_TYPE_NONE, -PLAYLIST_TYPE_EVENT, -PLAYLIST_TYPE_VOD, -PLAYLIST_TYPE_NB, -} PlaylistType; - typedef struct VariantStream { unsigned number; int64_t sequence; @@ -1055,19 +1049,6 @@ static void hls_free_segments(HLSSegment *p) } } -static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version, - int target_duration, int64_t sequence) -{ -avio_printf(out, "#EXTM3U\n"); -avio_printf(out, "#EXT-X-VERSION:%d\n", version); -if (hls->allowcache == 0 || hls->allowcache == 1) { -avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES"); -} -avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); -avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); -} - static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc) { size_t len = strlen(oc->filename); @@ -1133,8 +1114,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -avio_printf(master_pb, "#EXTM3U\n"); -avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version); +ff_hls_write_playlist_version(master_pb, hls->version); /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { @@ -1175,18 +1155,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += aud_st->codecpar->bit_rate; bandwidth += bandwidth / 10; -if (!bandwidth) { -av_log(NULL, AV_LOG_WARNING, -"Bandwidth info not available, set audio and video bitrates\n"); -av_freep(&m3u8_rel_name); -continue; -} - -avio_printf(master_pb, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth); -if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0) -avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width, -vid_st->codecpar->height); -avio_printf(master_pb, "\n%s\n\n", m3u8_rel_name); +ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name); av_freep(&m3u8_rel_name); } @@ -1215,6 +1184,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) char *iv_string = NULL; AVDictionary *options = NULL; double prog_date_time = vs->initial_prog_date_time; +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; @@ -1245,12 +1215,8 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; -write_m3u8_head_block(hls, out, hls->version, target_duration, sequence); -if (hls->pl_type == PLAYLIST_TYPE_EVENT) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n"); -} else if (hls->pl_type == PLAYLIST_TYPE_VOD) { -avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n"); -} +ff_hls_write_playlist_header(out, hls->version, hls->allowcache, + target_duration, sequence, hls->pl_type); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ avio_printf(out, "#EXT-X-DISCONTINUITY\n"); @@ -1270,74 +1236,35 @@ static int hls_window(AVFormatContext *s, in
[FFmpeg-devel] [PATCH v6 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/Makefile | 2 +- libavformat/dashenc.c | 103 -- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 8ec48c2..3d0c7bf 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/Makefile b/libavformat/Makefile index fd8b9f9..4bffdf2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -135,7 +135,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER)+= concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER)+= rawenc.o -OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o +OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o hlsplaylist.o OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER)+= daudenc.o diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 0fee3cd..ee9dc85 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,14 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, int string_size, + const char *base_url, int id) { +if (base_url) +snprintf(playlist_name, string_size, "%smedia_%d.m3u8", base_url, id); +else +snprintf(playlist_name, string_size, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +273,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +334,55 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +if (target_duration < seg->duration) +target_duration = seg->duration; +} +target_duration = lrint((double) target_duration / timescale); + +ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +ff_hls_wri
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/Makefile | 2 +- libavformat/dashenc.c | 103 -- 3 files changed, 103 insertions(+), 5 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 8ec48c2..3d0c7bf 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/Makefile b/libavformat/Makefile index fd8b9f9..4bffdf2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -135,7 +135,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER)+= concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER)+= rawenc.o -OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o +OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o hlsplaylist.o OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER)+= daudenc.o diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 0fee3cd..efc2012 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,14 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, int string_size, + const char *base_url, int id) { +if (base_url) +snprintf(playlist_name, string_size, "%smedia_%d.m3u8", base_url, id); +else +snprintf(playlist_name, string_size, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +273,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +334,55 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +double duration = (double) seg->duration / timescale; +if (target_duration <= duration) +target_duration = hls_get_int_from_double(duration); +} + +ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +
[FFmpeg-devel] [PATCH 1/2] avformat/hlsenc: Refactored 'get_int_from_double' function to allow reuse
From: Karthick Jeyapal --- libavformat/hlsenc.c | 7 +-- libavformat/hlsplaylist.h | 5 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index f63b08d..cdfbf45 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -203,11 +203,6 @@ typedef struct HLSContext { int http_persistent; } HLSContext; -static int get_int_from_double(double val) -{ -return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val; -} - static int mkdir_p(const char *path) { int ret = 0; char *temp = av_strdup(path); @@ -1211,7 +1206,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) for (en = vs->segments; en; en = en->next) { if (target_duration <= en->duration) -target_duration = get_int_from_double(en->duration); +target_duration = hls_get_int_from_double(en->duration); } vs->discontinuity_set = 0; diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index fd36c7e..68ef8d4 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -32,6 +32,11 @@ typedef enum { PLAYLIST_TYPE_NB, } PlaylistType; +static inline int hls_get_int_from_double(double val) +{ +return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val; +} + void ff_hls_write_playlist_version(AVIOContext *out, int version); void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, char *filename); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/2] avformat/hlsenc: Refactored 'get_int_from_double' function to allow reuse
From: Karthick Jeyapal --- libavformat/hlsenc.c | 7 +-- libavformat/hlsplaylist.h | 5 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index f63b08d..cdfbf45 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -203,11 +203,6 @@ typedef struct HLSContext { int http_persistent; } HLSContext; -static int get_int_from_double(double val) -{ -return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val; -} - static int mkdir_p(const char *path) { int ret = 0; char *temp = av_strdup(path); @@ -1211,7 +1206,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) for (en = vs->segments; en; en = en->next) { if (target_duration <= en->duration) -target_duration = get_int_from_double(en->duration); +target_duration = hls_get_int_from_double(en->duration); } vs->discontinuity_set = 0; diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index fd36c7e..68ef8d4 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -32,6 +32,11 @@ typedef enum { PLAYLIST_TYPE_NB, } PlaylistType; +static inline int hls_get_int_from_double(double val) +{ +return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val; +} + void ff_hls_write_playlist_version(AVIOContext *out, int version); void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, char *filename); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/2] avformat/dashenc: Option to generate hls playlist as well
This is to take full advantage of Common Media Application Format. Now server can generate one content and serve both HLS and DASH players. --- doc/muxers.texi | 3 ++ libavformat/Makefile | 2 +- libavformat/dashenc.c | 110 +++--- 3 files changed, 108 insertions(+), 7 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 8ec48c2..3d0c7bf 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,9 @@ DASH-templated name to used for the media segments. Default is "chunk-stream$Rep URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso"; @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. +@item -hls_playlist @var{hls_playlist} +Generate HLS playlist files as well. The master playlist is generated with the filename master.m3u8. +One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. @item -adaptation_sets @var{adaptation_sets} Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. diff --git a/libavformat/Makefile b/libavformat/Makefile index fd8b9f9..4bffdf2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -135,7 +135,7 @@ OBJS-$(CONFIG_CONCAT_DEMUXER)+= concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o OBJS-$(CONFIG_DATA_MUXER)+= rawenc.o -OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o +OBJS-$(CONFIG_DASH_MUXER)+= dash.o dashenc.o hlsplaylist.o OBJS-$(CONFIG_DASH_DEMUXER) += dash.o dashdec.o OBJS-$(CONFIG_DAUD_DEMUXER) += dauddec.o OBJS-$(CONFIG_DAUD_MUXER)+= daudenc.o diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 0fee3cd..1783675 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -36,6 +36,7 @@ #include "avc.h" #include "avformat.h" #include "avio_internal.h" +#include "hlsplaylist.h" #include "internal.h" #include "isom.h" #include "os_support.h" @@ -101,6 +102,8 @@ typedef struct DASHContext { const char *media_seg_name; const char *utc_timing_url; const char *user_agent; +int hls_playlist; +int master_playlist_created; } DASHContext; static struct codec_string { @@ -217,6 +220,14 @@ static void set_http_options(AVDictionary **options, DASHContext *c) av_dict_set(options, "user_agent", c->user_agent, 0); } +static void get_hls_playlist_name(char *playlist_name, int string_size, + const char *base_url, int id) { +if (base_url) +snprintf(playlist_name, string_size, "%smedia_%d.m3u8", base_url, id); +else +snprintf(playlist_name, string_size, "media_%d.m3u8", id); +} + static int flush_init_segment(AVFormatContext *s, OutputStream *os) { DASHContext *c = s->priv_data; @@ -262,7 +273,8 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c) +static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +int representation_id, int final) { int i, start_index = 0, start_number = 1; if (c->window_size) { @@ -322,6 +334,55 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext } avio_printf(out, "\t\t\t\t\n"); } +if (c->hls_playlist && start_index < os->nb_segments) +{ +int timescale = os->ctx->streams[0]->time_base.den; +char temp_filename_hls[1024]; +char filename_hls[1024]; +AVIOContext *out_hls = NULL; +AVDictionary *http_opts = NULL; +int target_duration = 0; +const char *proto = avio_find_protocol_name(c->dirname); +int use_rename = proto && !strcmp(proto, "file"); + +get_hls_playlist_name(filename_hls, sizeof(filename_hls), + c->dirname, representation_id); + +snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); + +set_http_options(&http_opts, c); +avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +av_dict_free(&http_opts); +for (i = start_index; i < os->nb_segments; i++) { +Segment *seg = os->segments[i]; +double duration = (double) seg->duration / timescale; +if (target_duration <= duration) +target_duration = hls_get_int_from_double(duration); +} + +ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, + start_number, PLAYLIST_TYPE_NONE); + +
[FFmpeg-devel] [PATCH 2/3] avformat/hlsenc: Handle NULL input in IO open and close utility functions
From: Karthick Jeyapal --- libavformat/hlsenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 9048cb2..ff982c5 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -244,7 +244,7 @@ static int is_http_proto(char *filename) { static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, AVDictionary **options) { HLSContext *hls = s->priv_data; -int http_base_proto = is_http_proto(filename); +int http_base_proto = filename ? is_http_proto(filename) : 0; int err; if (!*pb || !http_base_proto || !hls->http_persistent) { err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); @@ -258,7 +258,7 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { HLSContext *hls = s->priv_data; -int http_base_proto = is_http_proto(filename); +int http_base_proto = filename ? is_http_proto(filename) : 0; if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/3] avformat/hlsenc: Call avio_flush during persistent http connections
From: Karthick Jeyapal Since close is not called, during http persistent connection, flush needs to be called so that output is written on time. --- libavformat/hlsenc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index cdfbf45..9048cb2 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -262,6 +262,8 @@ static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); +} else { +avio_flush(*pb); } } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 3/3] avformat/hlsenc: Extend persistent http connections to playlists
From: Karthick Jeyapal Before this patch persistent http connections would work only for media segments. The playlists were still opening a new connection everytime. This patch extends persistent http connections to playlists as well. --- libavformat/hlsenc.c | 46 ++ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index ff982c5..350836d 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -201,6 +201,8 @@ typedef struct HLSContext { char *master_pl_name; unsigned int master_publish_rate; int http_persistent; +AVIOContext *m3u8_out; +AVIOContext *sub_m3u8_out; } HLSContext; static int mkdir_p(const char *path) { @@ -1081,7 +1083,6 @@ static int create_master_playlist(AVFormatContext *s, HLSContext *hls = s->priv_data; VariantStream *vs; AVStream *vid_st, *aud_st; -AVIOContext *master_pb = 0; AVDictionary *options = NULL; unsigned int i, j; int m3u8_name_size, ret, bandwidth; @@ -1102,8 +1103,7 @@ static int create_master_playlist(AVFormatContext *s, set_http_options(s, &options, hls); -ret = s->io_open(s, &master_pb, hls->master_m3u8_url, AVIO_FLAG_WRITE,\ - &options); +ret = hlsenc_io_open(s, &hls->m3u8_out, hls->master_m3u8_url, &options); av_dict_free(&options); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", @@ -,7 +,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -ff_hls_write_playlist_version(master_pb, hls->version); +ff_hls_write_playlist_version(hls->m3u8_out, hls->version); /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { @@ -1152,7 +1152,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += aud_st->codecpar->bit_rate; bandwidth += bandwidth / 10; -ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name); +ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name); av_freep(&m3u8_rel_name); } @@ -1160,7 +1160,7 @@ fail: if(ret >=0) hls->master_m3u8_created = 1; av_freep(&m3u8_rel_name); -ff_format_io_close(s, &master_pb); +hlsenc_io_close(s, &hls->m3u8_out, hls->master_m3u8_url); return ret; } @@ -1170,8 +1170,6 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) HLSSegment *en; int target_duration = 0; int ret = 0; -AVIOContext *out = NULL; -AVIOContext *sub_out = NULL; char temp_filename[1024]; int64_t sequence = FFMAX(hls->start_sequence, vs->sequence - vs->nb_entries); const char *proto = avio_find_protocol_name(s->filename); @@ -1203,7 +1201,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) set_http_options(s, &options, hls); snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", vs->m3u8_name); -if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options)) < 0) goto fail; for (en = vs->segments; en; en = en->next) { @@ -1212,33 +1210,33 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; -ff_hls_write_playlist_header(out, hls->version, hls->allowcache, +ff_hls_write_playlist_header(hls->m3u8_out, hls->version, hls->allowcache, target_duration, sequence, hls->pl_type); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ -avio_printf(out, "#EXT-X-DISCONTINUITY\n"); +avio_printf(hls->m3u8_out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { -avio_printf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); +avio_printf(hls->m3u8_out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); } for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { -avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); +avio_printf(hls->m3u8_out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); if (*en->iv_string) -avio_printf(out, ",IV=0x%s", en->iv_string); -avio_printf(out, "\n"); +avio_printf(hls->m3u8_out, ",IV=0x%s", en->iv_string); +avio_printf(hls->m3u8_out, "\n"); key_uri = en->key_uri; iv_string = en->iv_string; } if ((hls->segment_type == SEGME
[FFmpeg-devel] [PATCH v2 1/3] avformat/hlsenc: Call avio_flush during persistent http connections
From: Karthick Jeyapal Since close is not called, during http persistent connection, flush needs to be called so that output is written on time. --- libavformat/hlsenc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index fdf614b..30d0285 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -266,6 +266,8 @@ static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); +} else { +avio_flush(*pb); } } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/3] avformat/hlsenc: Handle NULL input in IO open and close utility functions
From: Karthick Jeyapal --- libavformat/hlsenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 30d0285..af9d949 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -246,7 +246,7 @@ static int is_http_proto(char *filename) { static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, AVDictionary **options) { HLSContext *hls = s->priv_data; -int http_base_proto = is_http_proto(filename); +int http_base_proto = filename ? is_http_proto(filename) : 0; int err = AVERROR_MUXER_NOT_FOUND; if (!*pb || !http_base_proto || !hls->http_persistent) { err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); @@ -262,7 +262,7 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { HLSContext *hls = s->priv_data; -int http_base_proto = is_http_proto(filename); +int http_base_proto = filename ? is_http_proto(filename) : 0; if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/3] avformat/hlsenc: Extend persistent http connections to playlists
From: Karthick Jeyapal Before this patch persistent http connections would work only for media segments. The playlists were still opening a new connection everytime. This patch extends persistent http connections to playlists as well. --- libavformat/hlsenc.c | 56 +--- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index af9d949..e3442c3 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -203,6 +203,8 @@ typedef struct HLSContext { char *master_pl_name; unsigned int master_publish_rate; int http_persistent; +AVIOContext *m3u8_out; +AVIOContext *sub_m3u8_out; } HLSContext; static int mkdir_p(const char *path) { @@ -1085,7 +1087,6 @@ static int create_master_playlist(AVFormatContext *s, HLSContext *hls = s->priv_data; VariantStream *vs; AVStream *vid_st, *aud_st; -AVIOContext *master_pb = 0; AVDictionary *options = NULL; unsigned int i, j; int m3u8_name_size, ret, bandwidth; @@ -1106,8 +1107,7 @@ static int create_master_playlist(AVFormatContext *s, set_http_options(s, &options, hls); -ret = s->io_open(s, &master_pb, hls->master_m3u8_url, AVIO_FLAG_WRITE,\ - &options); +ret = hlsenc_io_open(s, &hls->m3u8_out, hls->master_m3u8_url, &options); av_dict_free(&options); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Failed to open master play list file '%s'\n", @@ -1115,7 +1115,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -ff_hls_write_playlist_version(master_pb, hls->version); +ff_hls_write_playlist_version(hls->m3u8_out, hls->version); /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ for (i = 0; i < hls->nb_varstreams; i++) { @@ -1156,7 +1156,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += aud_st->codecpar->bit_rate; bandwidth += bandwidth / 10; -ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name); +ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name); av_freep(&m3u8_rel_name); } @@ -1164,7 +1164,7 @@ fail: if(ret >=0) hls->master_m3u8_created = 1; av_freep(&m3u8_rel_name); -ff_format_io_close(s, &master_pb); +hlsenc_io_close(s, &hls->m3u8_out, hls->master_m3u8_url); return ret; } @@ -1174,8 +1174,6 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) HLSSegment *en; int target_duration = 0; int ret = 0; -AVIOContext *out = NULL; -AVIOContext *sub_out = NULL; char temp_filename[1024]; int64_t sequence = FFMAX(hls->start_sequence, vs->sequence - vs->nb_entries); const char *proto = avio_find_protocol_name(s->filename); @@ -1207,7 +1205,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) set_http_options(s, &options, hls); snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", vs->m3u8_name); -if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &options)) < 0) +if ((ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options)) < 0) goto fail; for (en = vs->segments; en; en = en->next) { @@ -1216,67 +1214,67 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) } vs->discontinuity_set = 0; -ff_hls_write_playlist_header(out, hls->version, hls->allowcache, +ff_hls_write_playlist_header(hls->m3u8_out, hls->version, hls->allowcache, target_duration, sequence, hls->pl_type); if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){ -avio_printf(out, "#EXT-X-DISCONTINUITY\n"); +avio_printf(hls->m3u8_out, "#EXT-X-DISCONTINUITY\n"); vs->discontinuity_set = 1; } if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) { -avio_printf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); +avio_printf(hls->m3u8_out, "#EXT-X-INDEPENDENT-SEGMENTS\n"); } for (en = vs->segments; en; en = en->next) { if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) || av_strcasecmp(en->iv_string, iv_string))) { -avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); +avio_printf(hls->m3u8_out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri); if (*en->iv_string) -avio_printf(out, ",IV=0x%s", en->iv_string); -avio_printf(out, "\n"); +avio_printf(hls->m3u8_out, ",IV=0x%s", en->iv_string); +avio_printf(hls->m3u8_out, "\n"); key_uri = en->key_uri; iv_string = en->iv_string; } if ((hls->segment_type ==
[FFmpeg-devel] [PATCH] avformat/hlsenc: Minor fix for persistent http connection of init fmp4
From: Karthick Jeyapal --- libavformat/hlsenc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index fdf614b..0e2f412 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1787,7 +1787,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) vs->init_range_length = range_length; avio_open_dyn_buf(&oc->pb); vs->packets_written = 0; -ff_format_io_close(s, &vs->out); hlsenc_io_close(s, &vs->out, vs->base_output_dirname); } else { hlsenc_io_close(s, &oc->pb, oc->filename); -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/hlsenc, utils: Moved is_http_proto from hlsenc to utils for re-use
From: Karthick Jeyapal --- libavformat/hlsenc.c | 12 +++- libavformat/internal.h | 8 libavformat/utils.c| 5 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index e3442c3..03d54c7 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -240,15 +240,10 @@ static int mkdir_p(const char *path) { return ret; } -static int is_http_proto(char *filename) { -const char *proto = avio_find_protocol_name(filename); -return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; -} - static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, AVDictionary **options) { HLSContext *hls = s->priv_data; -int http_base_proto = filename ? is_http_proto(filename) : 0; +int http_base_proto = filename ? ff_is_http_proto(filename) : 0; int err = AVERROR_MUXER_NOT_FOUND; if (!*pb || !http_base_proto || !hls->http_persistent) { err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); @@ -264,8 +259,7 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { HLSContext *hls = s->priv_data; -int http_base_proto = filename ? is_http_proto(filename) : 0; - +int http_base_proto = filename ? ff_is_http_proto(filename) : 0; if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); } else { @@ -275,7 +269,7 @@ static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c) { -int http_base_proto = is_http_proto(s->filename); +int http_base_proto = ff_is_http_proto(s->filename); if (c->method) { av_dict_set(options, "method", c->method, 0); diff --git a/libavformat/internal.h b/libavformat/internal.h index 36a5721..8f168c9 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -619,6 +619,14 @@ int ff_format_output_open(AVFormatContext *s, const char *url, AVDictionary **op void ff_format_io_close(AVFormatContext *s, AVIOContext **pb); /** + * Utility function to check if the file uses http or https protocol + * + * @param s AVFormatContext + * @param filename URL or file name to open for writing + */ +int ff_is_http_proto(char *filename); + +/** * Parse creation_time in AVFormatContext metadata if exists and warn if the * parsing fails. * diff --git a/libavformat/utils.c b/libavformat/utils.c index 84e4920..f18a7c8 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -5459,6 +5459,11 @@ void ff_format_io_close(AVFormatContext *s, AVIOContext **pb) *pb = NULL; } +int ff_is_http_proto(char *filename) { +const char *proto = avio_find_protocol_name(filename); +return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +} + int ff_parse_creation_time_metadata(AVFormatContext *s, int64_t *timestamp, int return_seconds) { AVDictionaryEntry *entry; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Persistent HTTP connections supported as an option
From: Karthick Jeyapal --- libavformat/dashenc.c | 67 +-- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 5687530..e7d1a0d 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -37,6 +37,9 @@ #include "avformat.h" #include "avio_internal.h" #include "hlsplaylist.h" +#if CONFIG_HTTP_PROTOCOL +#include "http.h" +#endif #include "internal.h" #include "isom.h" #include "os_support.h" @@ -103,7 +106,10 @@ typedef struct DASHContext { const char *utc_timing_url; const char *user_agent; int hls_playlist; +int http_persistent; int master_playlist_created; +AVIOContext *mpd_out; +AVIOContext *m3u8_out; } DASHContext; static struct codec_string { @@ -117,6 +123,36 @@ static struct codec_string { { 0, NULL } }; +static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, + AVDictionary **options) { +DASHContext *c = s->priv_data; +int http_base_proto = filename ? ff_is_http_proto(filename) : 0; +int err = AVERROR_MUXER_NOT_FOUND; +if (!*pb || !http_base_proto || !c->http_persistent) { +err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); +#if CONFIG_HTTP_PROTOCOL +} else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); +err = ff_http_do_new_request(http_url_context, filename); +#endif +} +return err; +} + +static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { +DASHContext *c = s->priv_data; +int http_base_proto = filename ? ff_is_http_proto(filename) : 0; + +if (!http_base_proto || !c->http_persistent) { +ff_format_io_close(s, pb); +#if CONFIG_HTTP_PROTOCOL +} else { +avio_flush(*pb); +#endif +} +} + static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, char *str, int size) { @@ -218,6 +254,8 @@ static void set_http_options(AVDictionary **options, DASHContext *c) { if (c->user_agent) av_dict_set(options, "user_agent", c->user_agent, 0); +if (c->http_persistent) +av_dict_set_int(options, "multiple_requests", 1, 0); } static void get_hls_playlist_name(char *playlist_name, int string_size, @@ -273,9 +311,10 @@ static void dash_free(AVFormatContext *s) av_freep(&c->streams); } -static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext *c, +static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatContext *s, int representation_id, int final) { +DASHContext *c = s->priv_data; int i, start_index = 0, start_number = 1; if (c->window_size) { start_index = FFMAX(os->nb_segments - c->window_size, 0); @@ -339,7 +378,6 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext int timescale = os->ctx->streams[0]->time_base.den; char temp_filename_hls[1024]; char filename_hls[1024]; -AVIOContext *out_hls = NULL; AVDictionary *http_opts = NULL; int target_duration = 0; int ret = 0; @@ -352,7 +390,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext snprintf(temp_filename_hls, sizeof(temp_filename_hls), use_rename ? "%s.tmp" : "%s", filename_hls); set_http_options(&http_opts, c); -avio_open2(&out_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, &http_opts); +dashenc_io_open(s, &c->m3u8_out, temp_filename_hls, &http_opts); av_dict_free(&http_opts); for (i = start_index; i < os->nb_segments; i++) { Segment *seg = os->segments[i]; @@ -361,15 +399,15 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext target_duration = hls_get_int_from_double(duration); } -ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, +ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration, start_number, PLAYLIST_TYPE_NONE); -ff_hls_write_init_file(out_hls, os->initfile, c->single_file, +ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file, os->init_range_length, os->init_start_pos); for (i = start_index; i < os->nb_segments; i++) { Segment *seg = os->segments[i]; -ret = ff_hls_write_file_entry(out_hls, 0, c->single_file, +ret = ff_hls_write_file_entry(c->m3u8_out, 0, c->single_file, (double) seg->duration / timescale, 0, seg->range_length, seg->start_pos, NULL, c->single_file ? os->initfile : seg->file, @@ -380,9 +418,10 @@ static void output_segme
[FFmpeg-devel] [PATCH v2 1/2] avformat/hlsenc, utils: Moved is_http_proto from hlsenc to utils for re-use
From: Karthick Jeyapal --- libavformat/hlsenc.c | 12 +++- libavformat/internal.h | 8 libavformat/utils.c| 5 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index e3442c3..03d54c7 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -240,15 +240,10 @@ static int mkdir_p(const char *path) { return ret; } -static int is_http_proto(char *filename) { -const char *proto = avio_find_protocol_name(filename); -return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; -} - static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, AVDictionary **options) { HLSContext *hls = s->priv_data; -int http_base_proto = filename ? is_http_proto(filename) : 0; +int http_base_proto = filename ? ff_is_http_proto(filename) : 0; int err = AVERROR_MUXER_NOT_FOUND; if (!*pb || !http_base_proto || !hls->http_persistent) { err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options); @@ -264,8 +259,7 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) { HLSContext *hls = s->priv_data; -int http_base_proto = filename ? is_http_proto(filename) : 0; - +int http_base_proto = filename ? ff_is_http_proto(filename) : 0; if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) { ff_format_io_close(s, pb); } else { @@ -275,7 +269,7 @@ static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c) { -int http_base_proto = is_http_proto(s->filename); +int http_base_proto = ff_is_http_proto(s->filename); if (c->method) { av_dict_set(options, "method", c->method, 0); diff --git a/libavformat/internal.h b/libavformat/internal.h index 36a5721..8f168c9 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -619,6 +619,14 @@ int ff_format_output_open(AVFormatContext *s, const char *url, AVDictionary **op void ff_format_io_close(AVFormatContext *s, AVIOContext **pb); /** + * Utility function to check if the file uses http or https protocol + * + * @param s AVFormatContext + * @param filename URL or file name to open for writing + */ +int ff_is_http_proto(char *filename); + +/** * Parse creation_time in AVFormatContext metadata if exists and warn if the * parsing fails. * diff --git a/libavformat/utils.c b/libavformat/utils.c index 84e4920..f18a7c8 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -5459,6 +5459,11 @@ void ff_format_io_close(AVFormatContext *s, AVIOContext **pb) *pb = NULL; } +int ff_is_http_proto(char *filename) { +const char *proto = avio_find_protocol_name(filename); +return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0; +} + int ff_parse_creation_time_metadata(AVFormatContext *s, int64_t *timestamp, int return_seconds) { AVDictionaryEntry *entry; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel