[FFmpeg-devel] [PATCH] avformat/dashenc: Fix a bug with writing "final" manifest
This bug was introduced in the commit 951561b64ee6c11f01daedd9dcf73276cc1e765b --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 5f1333e436..b88d4b3496 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1631,7 +1631,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) } } if (ret >= 0) { -if (c->has_video) { +if (c->has_video && !final) { c->nr_of_streams_flushed++; if (c->nr_of_streams_flushed != c->nr_of_streams_to_flush) return ret; -- 2.20.1 (Apple Git-117) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".
[FFmpeg-devel] [PATCH] avformat/dashenc: Disable streaming for webm output
Currently streaming for webm output doesn't work. Disabling explicitly will make sure that the manifest will get generated correctly. --- libavformat/dashenc.c | 5 + 1 file changed, 5 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index f8d71166d4..d8dcbc1230 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1200,6 +1200,11 @@ static int dash_init(AVFormatContext *s) "Override -init_seg_name and/or -media_seg_name and/or " "-single_file_name to end with the extension .webm\n"); } +if (c->streaming) { +// Streaming not supported as matroskaenc buffers internally before writing the output +av_log(s, AV_LOG_WARNING, "One or more streams in WebM output format. Streaming option will be ignored\n"); +c->streaming = 0; +} } ctx->oformat = av_guess_format(os->format_name, NULL, NULL); -- 2.20.1 (Apple Git-117) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".
[FFmpeg-devel] [PATCH] avformat/dashenc : Fix streaming mode support for webm output
--- libavformat/dashenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index f8d71166d4..9dd520787f 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1750,10 +1750,10 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) } //write out the data immediately in streaming mode -if (c->streaming && os->segment_type == SEGMENT_TYPE_MP4) { +if (c->streaming) { int len = 0; uint8_t *buf = NULL; -if (!os->written_len) +if (!os->written_len && os->segment_type == SEGMENT_TYPE_MP4) write_styp(os->ctx->pb); avio_flush(os->ctx->pb); len = avio_get_dyn_buf (os->ctx->pb, ); -- 2.20.1 (Apple Git-117) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Add support for Global SIDX
--- doc/muxers.texi | 3 ++ libavformat/dashenc.c | 119 -- 2 files changed, 84 insertions(+), 38 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index aac7d94edf..83ae017d6c 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -293,6 +293,9 @@ Set container format (mp4/webm) options using a @code{:} separated list of key=value parameters. Values containing @code{:} special characters must be escaped. +@item -global_sidx @var{global_sidx} +Write global SIDX atom. Applicable only for single file, mp4 output, non-streaming mode. + @item -dash_segment_type @var{dash_segment_type} Possible values: @item auto diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 1b74bce060..f8d71166d4 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -140,6 +140,7 @@ typedef struct DASHContext { int64_t timeout; int index_correction; char *format_options_str; +int global_sidx; SegmentType segment_type_option; /* segment type as specified in options */ int ignore_io_errors; int lhls; @@ -368,7 +369,7 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, } } -static int flush_dynbuf(OutputStream *os, int *range_length) +static int flush_dynbuf(DASHContext *c, OutputStream *os, int *range_length) { uint8_t *buffer; @@ -380,16 +381,21 @@ static int flush_dynbuf(OutputStream *os, int *range_length) av_write_frame(os->ctx, NULL); avio_flush(os->ctx->pb); -// write out to file -*range_length = avio_close_dyn_buf(os->ctx->pb, ); -os->ctx->pb = NULL; -if (os->out) -avio_write(os->out, buffer + os->written_len, *range_length - os->written_len); -os->written_len = 0; -av_free(buffer); - -// re-open buffer -return avio_open_dyn_buf(>ctx->pb); +if (!c->single_file) { +// write out to file +*range_length = avio_close_dyn_buf(os->ctx->pb, ); +os->ctx->pb = NULL; +if (os->out) +avio_write(os->out, buffer + os->written_len, *range_length - os->written_len); +os->written_len = 0; +av_free(buffer); + +// re-open buffer +return avio_open_dyn_buf(>ctx->pb); +} else { +*range_length = avio_tell(os->ctx->pb) - os->pos; +return 0; +} } static void set_http_options(AVDictionary **options, DASHContext *c) @@ -508,7 +514,7 @@ static int flush_init_segment(AVFormatContext *s, OutputStream *os) DASHContext *c = s->priv_data; int ret, range_length; -ret = flush_dynbuf(os, _length); +ret = flush_dynbuf(c, os, _length); if (ret < 0) return ret; @@ -537,8 +543,12 @@ static void dash_free(AVFormatContext *s) return; for (i = 0; i < s->nb_streams; i++) { OutputStream *os = >streams[i]; -if (os->ctx && os->ctx->pb) -ffio_free_dyn_buf(>ctx->pb); +if (os->ctx && os->ctx->pb) { +if (!c->single_file) +ffio_free_dyn_buf(>ctx->pb); +else +avio_close(os->ctx->pb); +} ff_format_io_close(s, >out); if (os->ctx) avformat_free_context(os->ctx); @@ -1106,6 +1116,16 @@ static int dash_init(AVFormatContext *s) c->lhls = 0; } +if (c->global_sidx && !c->single_file) { +av_log(s, AV_LOG_WARNING, "Global SIDX option will be ignored as single_file is not enabled\n"); +c->global_sidx = 0; +} + +if (c->global_sidx && c->streaming) { +av_log(s, AV_LOG_WARNING, "Global SIDX option will be ignored as streaming is enabled\n"); +c->global_sidx = 0; +} + av_strlcpy(c->dirname, s->url, sizeof(c->dirname)); ptr = strrchr(c->dirname, '/'); if (ptr) { @@ -1201,9 +1221,6 @@ static int dash_init(AVFormatContext *s) ctx->avoid_negative_ts = s->avoid_negative_ts; ctx->flags = s->flags; -if ((ret = avio_open_dyn_buf(>pb)) < 0) -return ret; - if (c->single_file) { if (os->single_file_name) ff_dash_fill_tmpl_params(os->initfile, sizeof(os->initfile), os->single_file_name, i, 0, os->bit_rate, 0); @@ -1214,7 +1231,14 @@ static int dash_init(AVFormatContext *s) } snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile); set_http_options(, c); -ret = s->io_open(s, >out, filename, AVIO_FLAG_WRITE, ); +if (!c->single_file) { +if ((ret = avio_open_dyn_buf(>pb)) < 0) +return ret; +ret = s->io_open(s, >out, filename, AVIO_FLAG_WRITE, ); +} else { +ctx->url = av_strdup(filename); +ret = avio_open2(>pb, filename, AVIO_FLAG_WRITE, NULL, ); +} av_dict_free(); if (ret < 0) return ret; @@ -1232,8 +1256,12 @@ static int dash_init(AVFormatContext *s) // skip_sidx :
[FFmpeg-devel] [PATCH 1/2] avformat/movenc: Fix skip_trailer when global_sidx is enabled
--- libavformat/movenc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 8969d5b170..f46cbc5ea5 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -6745,9 +6745,8 @@ static int mov_write_trailer(AVFormatContext *s) avio_seek(pb, mov->reserved_header_pos, SEEK_SET); mov_write_sidx_tags(pb, mov, -1, 0); avio_seek(pb, end, SEEK_SET); -avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER); -mov_write_mfra_tag(pb, mov); -} else if (!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER)) { +} +if (!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER)) { avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER); mov_write_mfra_tag(pb, mov); } -- 2.17.2 (Apple Git-113) ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".
[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(, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); else av_dict_set(, "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 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(, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); else av_dict_set(, "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] 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] 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(, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx", 0); +av_dict_set(, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx+skip_trailer", 0); else av_dict_set(, "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/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 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(, ); +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(_s, ); 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: 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(, ); +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(_s, ); 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] 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:", ); 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 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, _index, _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(_opts, c); +ret = dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); +av_dict_free(_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, >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, _index, _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" : "%s", filename_hls); - -
[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, _index, _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", 0,
[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, _index, _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(_opts, c); +ret = dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); +av_dict_free(_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, >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, _index, _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" : "%s", filename_hls); - -
[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, _index, _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 for
[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(, "method", "DELETE", 0); -if ((ret = vs->avf->io_open(vs->avf, , path, AVIO_FLAG_WRITE, )) < 0) +if ((ret = vs->avf->io_open(vs->avf, , path, AVIO_FLAG_WRITE, )) < 0) { +if (hls->ignore_io_errors) +ret = 0; goto fail; +} ff_format_io_close(vs->avf, ); } 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(, "method", "DELETE", 0); if ((ret = vs->vtt_avf->io_open(vs->vtt_avf, , sub_path, AVIO_FLAG_WRITE, )) < 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, , hls); snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", vs->m3u8_name); -if ((ret = hlsenc_io_open(s, >m3u8_out, temp_filename, )) < 0) +if ((ret = hlsenc_io_open(s, >m3u8_out, temp_filename, )) < 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, >sub_m3u8_out, vs->vtt_m3u8_name, )) < 0) +if ((ret = hlsenc_io_open(s, >sub_m3u8_out, vs->vtt_m3u8_name, )) < 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, >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, >pb, oc->url, )) < 0) +if ((err = hlsenc_io_open(s, >pb, oc->url, )) < 0) { +if (c->ignore_io_errors) +err = 0; goto fail; +} } if (vs->vtt_basename) { set_http_options(s, , c); -if ((err = hlsenc_io_open(s, _oc->pb, vtt_oc->url, )) < 0) +if ((err = hlsenc_io_open(s, _oc->pb, vtt_oc->url, )) < 0) { +if (c->ignore_io_errors) +err = 0; goto fail; +} } av_dict_free(); @@ -2257,9 +2274,9 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) set_http_options(s, , hls); ret = hlsenc_io_open(s, >out, vs->avf->url, ); if (ret < 0) { -av_log(s, AV_LOG_ERROR, "Failed to open file '%s'\n", - vs->avf->url); -return ret; +
[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 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 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 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(, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0); +av_dict_set(, "movflags", "frag_every_frame+dash+delay_moov+skip_sidx", 0); else av_dict_set(, "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 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(, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0); +av_dict_set(, "movflags", "frag_every_frame+dash+delay_moov+no_sidx", 0); else av_dict_set(, "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/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, >m3u8_out, temp_filename_hls, _opts); av_dict_free(_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, >mpd_out, temp_filename, ); av_dict_free(); 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, >m3u8_out, temp_filename, ); av_dict_free(); 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, >out, os->temp_path, ); av_dict_free(); 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 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(); 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(); 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(, c); ret = dashenc_io_open(s, >out, os->temp_path, ); av_dict_free(); -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, ); -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 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(_opts, c); -dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); +ret = dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); av_dict_free(_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] 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] 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 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(); 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(); 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(, c); ret = dashenc_io_open(s, >out, os->temp_path, ); av_dict_free(); -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, ); -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 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(_opts, c); -dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); +ret = dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); av_dict_free(_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 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, ); 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(, c); ret = dashenc_io_open(s, >out, os->temp_path, ); +av_dict_free(); if (ret < 0) return ret; -av_dict_free(); } //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 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(>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] 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 = >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, >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] 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, >out); +if (!c->single_file) { +char filename[1024]; +snprintf(filename, sizeof(filename), "%s%s", c->dirname, os->initfile); +dashenc_io_close(s, >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: 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(_opts); -dashenc_io_close(s, , filename); +ff_format_io_close(s, ); } 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 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(, c); -ret = avio_open2(, temp_filename, AVIO_FLAG_WRITE, NULL, ); +ret = dashenc_io_open(s, >m3u8_out, temp_filename, ); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } av_dict_free(); -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, >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(, c); -ret = avio_open2(, temp_filename, AVIO_FLAG_WRITE, NULL, ); +ret = dashenc_io_open(s, >m3u8_out, temp_filename, ); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } av_dict_free(); -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, >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(, c); -ret = avio_open2(, temp_filename, AVIO_FLAG_WRITE, NULL, ); +ret = dashenc_io_open(s, >m3u8_out, temp_filename, ); if (ret < 0) { av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename); return ret; } av_dict_free(); -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, >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] 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] 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(, , 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 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, ); 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, ); 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] 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, ); 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] 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(, "movflags", "frag_every_frame+dash+delay_moov", 0); +av_dict_set(, "movflags", "frag_every_frame+dash+delay_moov+global_sidx", 0); else av_dict_set(, "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 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 = >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 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] avformat/dashenc: Add CODECS tag to HLS master playlist
From: Karthick Jeyapal --- libavformat/dashenc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index ae57fd5..229a56e 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -909,7 +909,8 @@ static int write_manifest(AVFormatContext *s, int final) stream_bitrate += max_audio_bitrate; } 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, + os->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 v2 2/2] avformat/dashenc: Added a warning for incorrect segment name extension
From: Karthick JeyapalApplicable only to webm output format. By default all the segment filenames end with .m4s extension. When someone chooses webm output format, we recommend they also override the relevant segment name options to end with .webm extension. This patch will issue a warning for he same --- libavformat/dashenc.c | 21 + 1 file changed, 21 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 9a57ad1..4e83112 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -195,6 +195,16 @@ static const char *get_format_str(SegmentType segment_type) { return NULL; } +static int check_file_extension(const char *filename, const char *extension) { +char *dot; +if (!filename || !extension) +return -1; +dot = strrchr(filename, '.'); +if (dot && !strcmp(dot + 1, extension)) +return 0; +return -1; +} + static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, AVRational *frame_rate, char *str, int size) { VPCC vpcc; @@ -986,6 +996,17 @@ static int dash_init(AVFormatContext *s) c->format_name = get_format_str(c->segment_type); if (!c->format_name) return AVERROR_MUXER_NOT_FOUND; +if (c->segment_type == SEGMENT_TYPE_WEBM) { +if ((!c->single_file && check_file_extension(c->init_seg_name, c->format_name) != 0) || +(!c->single_file && check_file_extension(c->media_seg_name, c->format_name) != 0) || +(c->single_file && check_file_extension(c->single_file_name, c->format_name) != 0)) { +av_log(s, AV_LOG_WARNING, + "One or many segment file names doesn't end with .webm. " + "Override -init_seg_name and/or -media_seg_name and/or " + "-single_file_name to end with the extension .webm\n"); +} +} + ctx->oformat = av_guess_format(c->format_name, NULL, NULL); if (!ctx->oformat) return AVERROR_MUXER_NOT_FOUND; -- 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: Added option for Segment file format
From: Karthick JeyapalRight now segment file format is chosen to be either mp4 or webm based on the codec format. This patch makes that choice configurable by the user, instead of being decided by the muxer. Also with this change per-stream choice segment file format(based on codec type) is not possible. All the output audio and video streams should be in the same file format. --- doc/muxers.texi | 8 +++ libavformat/dashenc.c | 66 +-- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 57948cf..ea80296 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -284,6 +284,14 @@ corrects that index value. Typically this logic is needed in live streaming use cases. The network bandwidth fluctuations are common during long run streaming. Each fluctuation can cause the segment indexes fall behind the expected real time position. + +@item dash_segment_type @var{dash_segment_type} +Possible values: +@item mp4 +If this flag is set, the dash segment files will be in in ISOBMFF format. This is the default format. + +@item webm +If this flag is set, the dash segment files will be in in WebM format. @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 1dd6333..9a57ad1 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -48,6 +48,12 @@ #include "vpcc.h" #include "dash.h" +typedef enum { +SEGMENT_TYPE_MP4 = 0, +SEGMENT_TYPE_WEBM, +SEGMENT_TYPE_NB +} SegmentType; + typedef struct Segment { char file[1024]; int64_t start_pos; @@ -69,7 +75,6 @@ typedef struct OutputStream { AVFormatContext *ctx; int ctx_inited, as_idx; AVIOContext *out; -char format_name[8]; int packets_written; char initfile[1024]; int64_t init_start_pos, pos; @@ -125,6 +130,8 @@ typedef struct DASHContext { int streaming; int64_t timeout; int index_correction; +SegmentType segment_type; +const char *format_name; } DASHContext; static struct codec_string { @@ -138,6 +145,15 @@ static struct codec_string { { 0, NULL } }; +static struct format_string { +SegmentType segment_type; +const char *str; +} formats[] = { +{ SEGMENT_TYPE_MP4, "mp4" }, +{ SEGMENT_TYPE_WEBM, "webm" }, +{ 0, NULL } +}; + static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, AVDictionary **options) { DASHContext *c = s->priv_data; @@ -171,6 +187,14 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam } } +static const char *get_format_str(SegmentType segment_type) { +int i; +for (i = 0; i < SEGMENT_TYPE_NB; i++) +if (formats[i].segment_type == segment_type) +return formats[i].str; +return NULL; +} + static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, AVRational *frame_rate, char *str, int size) { VPCC vpcc; @@ -581,13 +605,13 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind if (as->media_type == AVMEDIA_TYPE_VIDEO) { AVStream *st = s->streams[i]; avio_printf(out, "\t\t\tformat_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); +i, c->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); if (st->avg_frame_rate.num) avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den); avio_printf(out, ">\n"); } else { avio_printf(out, "\t\t\t\n", -i, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate); +i, c->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate); avio_printf(out, "\t\t\t\t\n", s->streams[i]->codecpar->channels); } @@ -959,27 +983,10 @@ static int dash_init(AVFormatContext *s) if (!ctx) return AVERROR(ENOMEM); -// choose muxer based on codec: webm for VP8 and opus, mp4 otherwise -// note: os->format_name is also used as part of the mimetype of the -// representation, e.g. video/ -if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 || -s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS || -s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) { -snprintf(os->format_name, sizeof(os->format_name), "webm"); - -if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { -av_log(s, AV_LOG_ERROR, - "WebM support in dashenc is experimental and has not " - "been validated. For testing purposes,
[FFmpeg-devel] [PATCH] avformat/dashenc: Add documentation for http method option
From: Karthick Jeyapal--- doc/muxers.texi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/muxers.texi b/doc/muxers.texi index 2429f8e..ea80296 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -252,6 +252,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 method @var{method} +Use the given HTTP method to create output files. Generally set to PUT or POST. @item -http_user_agent @var{user_agent} Override User-Agent field in HTTP header. Applicable only for HTTP output. @item -http_persistent @var{http_persistent} -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc: Added option for Segment file format
From: Karthick JeyapalRight now segment file format is chosen to be either mp4 or webm based on the codec format. This patch makes that choice configurable by the user, instead of being decided by the muxer. --- doc/muxers.texi | 8 libavformat/dashenc.c | 48 ++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 6f03bba..2429f8e 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -282,6 +282,14 @@ corrects that index value. Typically this logic is needed in live streaming use cases. The network bandwidth fluctuations are common during long run streaming. Each fluctuation can cause the segment indexes fall behind the expected real time position. + +@item dash_segment_type @var{dash_segment_type} +Possible values: +@item mp4 +If this flag is set, the dash segment files will be in in ISOBMFF format. This is the default format. + +@item webm +If this flag is set, the dash segment files will be in in WebM format. @end table @anchor{framecrc} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 1dd6333..412f074 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -48,6 +48,11 @@ #include "vpcc.h" #include "dash.h" +typedef enum { +SEGMENT_TYPE_MP4, +SEGMENT_TYPE_WEBM, +} SegmentType; + typedef struct Segment { char file[1024]; int64_t start_pos; @@ -69,7 +74,6 @@ typedef struct OutputStream { AVFormatContext *ctx; int ctx_inited, as_idx; AVIOContext *out; -char format_name[8]; int packets_written; char initfile[1024]; int64_t init_start_pos, pos; @@ -125,6 +129,8 @@ typedef struct DASHContext { int streaming; int64_t timeout; int index_correction; +SegmentType segment_type; +char format_name[8]; } DASHContext; static struct codec_string { @@ -581,13 +587,13 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind if (as->media_type == AVMEDIA_TYPE_VIDEO) { AVStream *st = s->streams[i]; avio_printf(out, "\t\t\tformat_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); +i, c->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); if (st->avg_frame_rate.num) avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den); avio_printf(out, ">\n"); } else { avio_printf(out, "\t\t\t\n", -i, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate); +i, c->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate); avio_printf(out, "\t\t\t\t\n", s->streams[i]->codecpar->channels); } @@ -959,27 +965,14 @@ static int dash_init(AVFormatContext *s) if (!ctx) return AVERROR(ENOMEM); -// choose muxer based on codec: webm for VP8 and opus, mp4 otherwise -// note: os->format_name is also used as part of the mimetype of the -// representation, e.g. video/ -if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 || -s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS || -s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) { -snprintf(os->format_name, sizeof(os->format_name), "webm"); - -if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { -av_log(s, AV_LOG_ERROR, - "WebM support in dashenc is experimental and has not " - "been validated. For testing purposes, make sure " - "to add -strict experimental and override " - "-init_seg_name and -media_seg_name to end with " - "the extension 'webm'.\n"); -return AVERROR(EINVAL); -} +if (c->segment_type == SEGMENT_TYPE_WEBM) { +snprintf(c->format_name, sizeof(c->format_name), "webm"); +} else if (c->segment_type == SEGMENT_TYPE_MP4) { +snprintf(c->format_name, sizeof(c->format_name), "mp4"); } else { -snprintf(os->format_name, sizeof(os->format_name), "mp4"); +return AVERROR_MUXER_NOT_FOUND; } -ctx->oformat = av_guess_format(os->format_name, NULL, NULL); +ctx->oformat = av_guess_format(c->format_name, NULL, NULL); if (!ctx->oformat) return AVERROR_MUXER_NOT_FOUND; os->ctx = ctx; @@ -1017,7 +1010,7 @@ static int dash_init(AVFormatContext *s) av_dict_free(); os->init_start_pos = 0; -if (!strcmp(os->format_name, "mp4")) { +if (c->segment_type == SEGMENT_TYPE_MP4) { if (c->streaming)
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Added a warning for incorrect segment name extension
From: Karthick JeyapalApplicable only to webm output format. By default all the segment filenames end with .m4s extension. When someone chooses webm output format, we recommend they also override the relevant segment name options to end with .webm extension. This patch will issue a warning for he same. --- libavformat/dashenc.c | 18 ++ 1 file changed, 18 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 412f074..bd374e2 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -177,6 +177,16 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam } } +static int check_file_extension(const char *filename, const char *extension) { +char *dot; +if (!filename || !extension) +return -1; +dot = strrchr(filename, '.'); +if (dot && !strcmp(dot + 1, extension)) +return 0; +return -1; +} + static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, AVRational *frame_rate, char *str, int size) { VPCC vpcc; @@ -967,6 +977,14 @@ static int dash_init(AVFormatContext *s) if (c->segment_type == SEGMENT_TYPE_WEBM) { snprintf(c->format_name, sizeof(c->format_name), "webm"); +if ((!c->single_file && check_file_extension(c->init_seg_name, c->format_name) != 0) || +(!c->single_file && check_file_extension(c->media_seg_name, c->format_name) != 0) || +(c->single_file && check_file_extension(c->single_file_name, c->format_name) != 0)) { +av_log(s, AV_LOG_WARNING, + "One or many segment file names doesn't end with .webm. " + "Override -init_seg_name and/or -media_seg_name and/or " + "-single_file_name to end with the extension .webm\n"); +} } else if (c->segment_type == SEGMENT_TYPE_MP4) { snprintf(c->format_name, sizeof(c->format_name), "mp4"); } else { -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 3/3] avformat/dashenc: Set mp4 as the default format for VP9
From: Karthick JeyapalThere is a separate muxer(webmdashenc.c) for supporting VP9+webm output in DASH. Hence in this muxer we will focus on supporting VP9 in MP4 Have verified playout support of VP9+MP4 in Chrome and Firefox. --- libavformat/dashenc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index a5f58d4..211ef23 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -959,11 +959,10 @@ static int dash_init(AVFormatContext *s) if (!ctx) return AVERROR(ENOMEM); -// choose muxer based on codec: webm for VP8/9 and opus, mp4 otherwise +// choose muxer based on codec: webm for VP8 and opus, mp4 otherwise // note: os->format_name is also used as part of the mimetype of the // representation, e.g. video/ if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 || -s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP9 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) { snprintf(os->format_name, sizeof(os->format_name), "webm"); -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 1/3] avformat/vpcc: Calculate VP9 level from Luma's Sample rate and Picture size
From: Karthick Jeyapal--- libavformat/vpcc.c | 53 ++--- libavformat/vpcc.h | 2 +- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/libavformat/vpcc.c b/libavformat/vpcc.c index 66d0df6..7951448 100644 --- a/libavformat/vpcc.c +++ b/libavformat/vpcc.c @@ -67,11 +67,58 @@ static int get_vpx_video_full_range_flag(enum AVColorRange color_range) return color_range == AVCOL_RANGE_JPEG; } +// Find approximate VP9 level based on the Luma's Sample rate and Picture size. +static int get_vp9_level(AVCodecParameters *par, AVRational *frame_rate) { +int picture_size = par->width * par->height; +int64_t sample_rate; + +// All decisions will be based on picture_size, if frame rate is missing/invalid +if (!frame_rate || !frame_rate->den) +sample_rate = 0; +else +sample_rate = ((int64_t)picture_size * frame_rate->num) / frame_rate->den; + +if (picture_size <= 0) { +return 0; +} else if (sample_rate <= 829440 && picture_size <= 36864) { +return 0x10; +} else if (sample_rate <= 2764800&& picture_size <= 73728) { +return 0x11; +} else if (sample_rate <= 4608000&& picture_size <= 122880) { +return 0x20; +} else if (sample_rate <= 9216000&& picture_size <= 245760) { +return 0x21; +} else if (sample_rate <= 20736000 && picture_size <= 552960) { +return 0x30; +} else if (sample_rate <= 36864000 && picture_size <= 983040) { +return 0x31; +} else if (sample_rate <= 83558400 && picture_size <= 2228224) { +return 0x40; +} else if (sample_rate <= 160432128 && picture_size <= 2228224) { +return 0x41; +} else if (sample_rate <= 311951360 && picture_size <= 8912896) { +return 0x50; +} else if (sample_rate <= 588251136 && picture_size <= 8912896) { +return 0x51; +} else if (sample_rate <= 1176502272 && picture_size <= 8912896) { +return 0x52; +} else if (sample_rate <= 1176502272 && picture_size <= 35651584) { +return 0x60; +} else if (sample_rate <= 2353004544 && picture_size <= 35651584) { +return 0x61; +} else if (sample_rate <= 4706009088 && picture_size <= 35651584) { +return 0x62; +} else { +return 0; +} +} + int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, - VPCC *vpcc) + AVRational *frame_rate, VPCC *vpcc) { int profile = par->profile; -int level = par->level == FF_LEVEL_UNKNOWN ? 0 : par->level; +int level = par->level == FF_LEVEL_UNKNOWN ? +get_vp9_level(par, frame_rate) : par->level; int bit_depth = get_bit_depth(s, par->format); int vpx_chroma_subsampling = get_vpx_chroma_subsampling(s, par->format, par->chroma_location); @@ -105,7 +152,7 @@ int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, VPCC vpcc; int ret; -ret = ff_isom_get_vpcc_features(s, par, ); +ret = ff_isom_get_vpcc_features(s, par, NULL, ); if (ret < 0) return ret; diff --git a/libavformat/vpcc.h b/libavformat/vpcc.h index d71ba05..e87bec5 100644 --- a/libavformat/vpcc.h +++ b/libavformat/vpcc.h @@ -53,6 +53,6 @@ int ff_isom_write_vpcc(AVFormatContext *s, AVIOContext *pb, AVCodecParameters *par); int ff_isom_get_vpcc_features(AVFormatContext *s, AVCodecParameters *par, - VPCC *vpcc); + AVRational *frame_rate, VPCC *vpcc); #endif /* AVFORMAT_VPCC_H */ -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v2 2/3] avformat/dashenc: Set VP9 codec string with profile, level and bitdepth
From: Karthick JeyapalOtherwise some versions of chrome browser returns "codec not supported" error --- libavformat/dashenc.c | 34 -- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index fefe3ce..a5f58d4 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -45,6 +45,7 @@ #include "isom.h" #include "os_support.h" #include "url.h" +#include "vpcc.h" #include "dash.h" typedef struct Segment { @@ -170,8 +171,23 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam } } +static void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, + AVRational *frame_rate, char *str, int size) { +VPCC vpcc; +int ret = ff_isom_get_vpcc_features(s, par, frame_rate, ); +if (ret == 0) { +av_strlcatf(str, size, "vp09.%02x.%02x.%02x", +vpcc.profile, vpcc.level, vpcc.bitdepth); +} else { +// Default to just vp9 in case of error while finding out profile or level +av_log(s, AV_LOG_WARNING, "Could not find VP9 profile and/or level\n"); +av_strlcpy(str, "vp9", size); +} +return; +} + static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, - char *str, int size) + AVRational *frame_rate, char *str, int size) { const AVCodecTag *tags[2] = { NULL, NULL }; uint32_t tag; @@ -180,7 +196,11 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, // common Webm codecs are not part of RFC 6381 for (i = 0; codecs[i].id; i++) if (codecs[i].id == par->codec_id) { -av_strlcpy(str, codecs[i].str, size); +if (codecs[i].id == AV_CODEC_ID_VP9) { +set_vp9_codec_str(s, par, frame_rate, str, size); +} else { +av_strlcpy(str, codecs[i].str, size); +} return; } @@ -1032,7 +1052,8 @@ static int dash_init(AVFormatContext *s) c->has_video = 1; } -set_codec_str(s, st->codecpar, os->codec_str, sizeof(os->codec_str)); +set_codec_str(s, st->codecpar, >avg_frame_rate, os->codec_str, + sizeof(os->codec_str)); os->first_pts = AV_NOPTS_VALUE; os->max_pts = AV_NOPTS_VALUE; os->last_dts = AV_NOPTS_VALUE; @@ -1132,7 +1153,8 @@ static void find_index_range(AVFormatContext *s, const char *full_path, } static int update_stream_extradata(AVFormatContext *s, OutputStream *os, - AVCodecParameters *par) + AVCodecParameters *par, + AVRational *frame_rate) { uint8_t *extradata; @@ -1149,7 +1171,7 @@ static int update_stream_extradata(AVFormatContext *s, OutputStream *os, os->ctx->streams[0]->codecpar->extradata = extradata; os->ctx->streams[0]->codecpar->extradata_size = par->extradata_size; -set_codec_str(s, par, os->codec_str, sizeof(os->codec_str)); +set_codec_str(s, par, frame_rate, os->codec_str, sizeof(os->codec_str)); return 0; } @@ -1298,7 +1320,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) int64_t seg_end_duration, elapsed_duration; int ret; -ret = update_stream_extradata(s, os, st->codecpar); +ret = update_stream_extradata(s, os, st->codecpar, >avg_frame_rate); if (ret < 0) return ret; -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/2] avformat/dashenc: Set mp4 as the default format for VP9
From: Karthick JeyapalThere is a separate muxer(webmdashenc.c) for supporting VP9+webm output in DASH. Hence in this muxer we will focus on supporting VP9 in MP4 Have verified playout support of VP9+MP4 in Chrome and Firefox. --- libavformat/dashenc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 5443e31..301ea1a 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1016,11 +1016,10 @@ static int dash_init(AVFormatContext *s) if (!ctx) return AVERROR(ENOMEM); -// choose muxer based on codec: webm for VP8/9 and opus, mp4 otherwise +// choose muxer based on codec: webm for VP8 and opus, mp4 otherwise // note: os->format_name is also used as part of the mimetype of the // representation, e.g. video/ if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 || -s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP9 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) { snprintf(os->format_name, sizeof(os->format_name), "webm"); -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc: Set VP9 codec string with profile, level and bitdepth
From: Karthick JeyapalOtherwise some versions of chrome browser returns "codec not supported" error --- libavformat/dashenc.c | 79 ++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 9e72636..5443e31 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -170,6 +170,79 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam } } +static void set_vp9_codec_str(AVCodecParameters *par, char *str, int size) { +int profile, level, bitdepth; +int picture_size = par->width * par->height; +switch (par->format) { +case AV_PIX_FMT_YUV420P: +case AV_PIX_FMT_YUVA420P: +bitdepth = 8; +profile = 0; +break; +case AV_PIX_FMT_YUV422P: +case AV_PIX_FMT_YUV440P: +case AV_PIX_FMT_GBRP: +case AV_PIX_FMT_YUV444P: +bitdepth = 8; +profile = 1; +break; +case AV_PIX_FMT_YUV420P10: +bitdepth = 10; +profile = 2; +break; +case AV_PIX_FMT_YUV420P12: +bitdepth = 12; +profile = 2; +break; +case AV_PIX_FMT_YUV422P10: +case AV_PIX_FMT_YUV440P10: +case AV_PIX_FMT_GBRP10: +case AV_PIX_FMT_YUV444P10: +bitdepth = 10; +profile = 3; +break; +case AV_PIX_FMT_YUV422P12: +case AV_PIX_FMT_YUV440P12: +case AV_PIX_FMT_GBRP12: +case AV_PIX_FMT_YUV444P12: +bitdepth = 12; +profile = 3; +break; +default: +goto error; +} +// Finding VP9 level accurately in a ffmpeg muxer is almost impossible. +// Hence we will set approximate levels based on the width and height. +if (picture_size <= 0) { +goto error; +} else if (picture_size <= 36864) { +level = 0x10; +} else if (picture_size <= 73728) { +level = 0x11; +} else if (picture_size <= 122880) { +level = 0x20; +} else if (picture_size <= 245760) { +level = 0x21; +} else if (picture_size <= 552960) { +level = 0x30; +} else if (picture_size <= 983040) { +level = 0x31; +} else if (picture_size <= 2228224) { +level = 0x40; +} else if (picture_size <= 8912896) { +level = 0x50; +} else if (picture_size <= 35651584) { +level = 0x60; +} else { +goto error; +} +av_strlcatf(str, size, "vp09.%02x.%02x.%02x", profile, level, bitdepth); +return; +error: +// Default to just vp9 in case of error while finding out profile or level +av_strlcpy(str, "vp9", size); +return; +} static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, char *str, int size) { @@ -180,7 +253,11 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, // common Webm codecs are not part of RFC 6381 for (i = 0; codecs[i].id; i++) if (codecs[i].id == par->codec_id) { -av_strlcpy(str, codecs[i].str, size); +if (codecs[i].id == AV_CODEC_ID_VP9) { +set_vp9_codec_str(par, str, size); +} else { +av_strlcpy(str, codecs[i].str, size); +} return; } -- 2.7.4 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/dashenc: Removed usage of deprecated 'filename' variable
From: Karthick Jeyapal--- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index d6474f3..ebff3c5 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1307,7 +1307,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) //open the output context when the first frame of a segment is ready if (!c->single_file && !os->out) { AVDictionary *opts = NULL; -const char *proto = avio_find_protocol_name(s->filename); +const char *proto = avio_find_protocol_name(s->url); int use_rename = proto && !strcmp(proto, "file"); os->filename[0] = os->full_path[0] = os->temp_path[0] = '\0'; ff_dash_fill_tmpl_params(os->filename, sizeof(os->filename), -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] MAINTAINERS: Adding myself as dashenc maintainer
From: Karthick JeyapalIf somebody else wants to maintain dashenc either now or in future, I am absolutely fine with giving up this responsibility anytime. But till then we need a maintainer for dashenc, and I am volunteering for that task. --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e583926..1785851 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -394,6 +394,7 @@ Muxers/Demuxers: cdxl.cPaul B Mahol crc.c Michael Niedermayer dashdec.c Steven Liu + dashenc.c Karthick Jeyapal daud.cReimar Doeffinger dss.c Oleksij Rempel dtsdec.c foo86 -- 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/dashenc: Fix a resource leak when http persistent in enabled
From: Karthick Jeyapal--- libavformat/dashenc.c | 4 1 file changed, 4 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 3345b89..2e4ff67 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1,6 +1,7 @@ /* * MPEG-DASH ISO BMFF segmenter * Copyright (c) 2014 Martin Storsjo + * Copyright (c) 2018 Akamai Technologies, Inc. * * This file is part of FFmpeg. * @@ -309,6 +310,9 @@ static void dash_free(AVFormatContext *s) av_free(os->segments); } av_freep(>streams); + +ff_format_io_close(s, >mpd_out); +ff_format_io_close(s, >m3u8_out); } static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatContext *s, -- 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: Signal http end of chunk(http_shutdown) explicitly
From: Karthick JeyapalCurrently http end of chunk is signalled implicitly in dashenc_io_open(). This mean playlists http writes would have to wait upto a segment duration to signal end of chunk causing delays. This patch will fix that problem and improve performance. --- libavformat/dashenc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 2e4ff67..c9cc389 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -149,7 +149,10 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam ff_format_io_close(s, pb); #if CONFIG_HTTP_PROTOCOL } else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); avio_flush(*pb); +ffurl_shutdown(http_url_context, AVIO_FLAG_WRITE); #endif } } -- 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: Signal http end of chunk(http_shutdown) explicitly
From: Karthick JeyapalCurrently http end of chunk is signalled implicitly in dashenc_io_open(). This mean playlists http writes would have to wait upto a segment duration to signal end of chunk causing delays. This patch will fix that problem and improve performance. --- libavformat/dashenc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index c4c112b..c328db2 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -149,7 +149,10 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam ff_format_io_close(s, pb); #if CONFIG_HTTP_PROTOCOL } else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); avio_flush(*pb); +ffurl_shutdown(http_url_context, AVIO_FLAG_WRITE); #endif } } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc: Fix a resource leak when http persistent in enabled
From: Karthick Jeyapal--- libavformat/dashenc.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 3345b89..c4c112b 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -1,6 +1,7 @@ /* * MPEG-DASH ISO BMFF segmenter * Copyright (c) 2014 Martin Storsjo + * Copyright (c) 2018 Akamai Technologies, Inc. * * This file is part of FFmpeg. * @@ -1317,6 +1318,16 @@ static int dash_write_trailer(AVFormatContext *s) } dash_flush(s, 1, -1); +if (c->http_persistent) { +int i; +for (i = 0; i < s->nb_streams; i++) { +OutputStream *os = >streams[i]; +ff_format_io_close(s, >out); +} +ff_format_io_close(s, >mpd_out); +ff_format_io_close(s, >m3u8_out); +} + if (c->remove_at_exit) { char filename[1024]; int i; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH v3] avformat/hlsenc: Add CODECS attribute to master playlist
From: Karthick Jeyapal--- libavformat/dashenc.c | 2 +- libavformat/hlsenc.c | 67 ++- libavformat/hlsplaylist.c | 5 +++- libavformat/hlsplaylist.h | 3 ++- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index a3eb522..400d767 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -780,7 +780,7 @@ static int write_manifest(AVFormatContext *s, int final) stream_bitrate += max_audio_bitrate; } 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, out, stream_bitrate, playlist_file, agroup, NULL); } avio_close(out); if (use_rename) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 5cff3b4..5fc907c 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -58,6 +58,11 @@ typedef enum { HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // MMDDhhmmss } StartSequenceSourceType; +typedef enum { +CODEC_ATTRIBUTE_WRITTEN = 0, +CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN, +} CodecAttributeStatus; + #define KEYSIZE 16 #define LINE_BUFFER_SIZE 1024 #define HLS_MICROSECOND_UNIT 100 @@ -142,6 +147,8 @@ typedef struct VariantStream { int fmp4_init_mode; AVStream **streams; +char codec_attr[128]; +CodecAttributeStatus attr_status; unsigned int nb_streams; int m3u8_created; /* status of media play-list creation */ char *agroup; /* audio group name */ @@ -296,6 +303,51 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } +static void write_codec_attr(AVStream *st, VariantStream *vs) { +int codec_strlen = strlen(vs->codec_attr); +char attr[32]; + +if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) +return; +if (vs->attr_status == CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN) +return; + +if (st->codecpar->codec_id == AV_CODEC_ID_H264) { +uint8_t *data = st->codecpar->extradata; +if ((data[0] | data[1] | data[2]) == 0 && data[3] == 1 && (data[4] & 0x1F) == 7) { +snprintf(attr, sizeof(attr), + "avc1.%02x%02x%02x", data[5], data[6], data[7]); +} else { +goto fail; +} +} else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { +snprintf(attr, sizeof(attr), "mp4a.40.33"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) { +snprintf(attr, sizeof(attr), "mp4a.40.34"); +} else if (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(attr, sizeof(attr), "mp4a.40.2"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { +snprintf(attr, sizeof(attr), "ac-3"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) { +snprintf(attr, sizeof(attr), "ec-3"); +} else { +goto fail; +} +// Don't write the same attribute multiple times +if (!av_stristr(vs->codec_attr, attr)) { +snprintf(vs->codec_attr + codec_strlen, + sizeof(vs->codec_attr) - codec_strlen, + "%s%s", codec_strlen ? "," : "", attr); +} +return; + +fail: +vs->codec_attr[0] = '\0'; +vs->attr_status = CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN; +return; +} + static int replace_int_data_in_filename(char *buf, int buf_size, const char *filename, char placeholder, int64_t number) { const char *p; @@ -1233,7 +1285,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += bandwidth / 10; ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, -aud_st ? vs->agroup : NULL); +aud_st ? vs->agroup : NULL, vs->codec_attr); av_freep(_rel_name); } @@ -1762,6 +1814,19 @@ static int hls_write_header(AVFormatContext *s) continue; } avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den); +write_codec_attr(outer_st, vs); + +} +/* Update the Codec Attr string for the mapped audio groups */ +if (vs->has_video && vs->agroup) { +for (j = 0; j < hls->nb_varstreams; j++) { +VariantStream *vs_agroup = &(hls->var_streams[j]); +if (!vs_agroup->has_video && !vs_agroup->has_subtitle && +vs_agroup->agroup && +!av_strcasecmp(vs_agroup->agroup, vs->agroup)) { +write_codec_attr(vs_agroup->streams[0], vs); +} +} } } fail: diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 098dc89..b8a3a14
[FFmpeg-devel] [PATCH v2] avformat/hlsenc: Add CODECS attribute to master playlist
From: Karthick Jeyapal--- libavformat/dashenc.c | 2 +- libavformat/hlsenc.c | 67 ++- libavformat/hlsplaylist.c | 5 +++- libavformat/hlsplaylist.h | 3 ++- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 478a384..8797959 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -760,7 +760,7 @@ static int write_manifest(AVFormatContext *s, int final) AVStream *st = s->streams[i]; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); ff_hls_write_stream_info(st, out, st->codecpar->bit_rate, -playlist_file, NULL); +playlist_file, NULL, NULL); } avio_close(out); if (use_rename) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 74f66ce..bb442f5 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -58,6 +58,11 @@ typedef enum { HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // MMDDhhmmss } StartSequenceSourceType; +typedef enum { +CODEC_ATTRIBUTE_WRITTEN = 0, +CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN, +} CodecAttributeStatus; + #define KEYSIZE 16 #define LINE_BUFFER_SIZE 1024 #define HLS_MICROSECOND_UNIT 100 @@ -142,6 +147,8 @@ typedef struct VariantStream { int fmp4_init_mode; AVStream **streams; +char codec_attr[128]; +CodecAttributeStatus attr_status; unsigned int nb_streams; int m3u8_created; /* status of media play-list creation */ char *agroup; /* audio group name */ @@ -296,6 +303,51 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } +static void write_codec_attr(AVStream *st, VariantStream *vs) { +int codec_strlen = strlen(vs->codec_attr); +char attr[32]; + +if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) +return; +if (vs->attr_status == CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN) +return; + +if (st->codecpar->codec_id == AV_CODEC_ID_H264) { +uint8_t *data = st->codecpar->extradata; +if ((data[0] | data[1] | data[2]) == 0 && data[3] == 1 && (data[4] & 0x1F) == 7) { +snprintf(attr, sizeof(attr), + "avc1.%02x%02x%02x", data[5], data[6], data[7]); +} else { +goto fail; +} +} else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { +snprintf(attr, sizeof(attr), "mp4a.40.33"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) { +snprintf(attr, sizeof(attr), "mp4a.40.34"); +} else if (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(attr, sizeof(attr), "mp4a.40.2"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { +snprintf(attr, sizeof(attr), "ac-3"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) { +snprintf(attr, sizeof(attr), "ec-3"); +} else { +goto fail; +} +// Don't write the same attribute multiple times +if (!av_stristr(vs->codec_attr, attr)) { +snprintf(vs->codec_attr + codec_strlen, + sizeof(vs->codec_attr) - codec_strlen, + "%s%s", codec_strlen ? "," : "", attr); +} +return; + +fail: +vs->codec_attr[0] = '\0'; +vs->attr_status = CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN; +return; +} + static int replace_int_data_in_filename(char *buf, int buf_size, const char *filename, char placeholder, int64_t number) { const char *p; @@ -1235,7 +1287,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += bandwidth / 10; ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, -aud_st ? vs->agroup : NULL); +aud_st ? vs->agroup : NULL, vs->codec_attr); av_freep(_rel_name); } @@ -1764,6 +1816,19 @@ static int hls_write_header(AVFormatContext *s) continue; } avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den); +write_codec_attr(outer_st, vs); + +} +/* Update the Codec Attr string for the mapped audio groups */ +if (vs->has_video && vs->agroup) { +for (j = 0; j < hls->nb_varstreams; j++) { +VariantStream *vs_agroup = &(hls->var_streams[j]); +if (!vs_agroup->has_video && !vs_agroup->has_subtitle && +vs_agroup->agroup && +!av_strcasecmp(vs_agroup->agroup, vs->agroup)) { +write_codec_attr(vs_agroup->streams[0], vs); +} +} } } fail: diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 42f059a..99231c9 100644 --- a/libavformat/hlsplaylist.c +++
[FFmpeg-devel] [PATCH] avformat/hlsenc: Add CODECS attribute to master playlist
From: Karthick Jeyapal--- libavformat/dashenc.c | 2 +- libavformat/hlsenc.c | 67 ++- libavformat/hlsplaylist.c | 5 +++- libavformat/hlsplaylist.h | 3 ++- 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 478a384..8797959 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -760,7 +760,7 @@ static int write_manifest(AVFormatContext *s, int final) AVStream *st = s->streams[i]; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); ff_hls_write_stream_info(st, out, st->codecpar->bit_rate, -playlist_file, NULL); +playlist_file, NULL, NULL); } avio_close(out); if (use_rename) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 74f66ce..1a84799 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -58,6 +58,11 @@ typedef enum { HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2, // MMDDhhmmss } StartSequenceSourceType; +typedef enum { +CODEC_ATTRIBUTE_WRITTEN = 0, +CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN, +} CodecAttributeStatus; + #define KEYSIZE 16 #define LINE_BUFFER_SIZE 1024 #define HLS_MICROSECOND_UNIT 100 @@ -142,6 +147,8 @@ typedef struct VariantStream { int fmp4_init_mode; AVStream **streams; +char codec_attr[128]; +CodecAttributeStatus attr_status; unsigned int nb_streams; int m3u8_created; /* status of media play-list creation */ char *agroup; /* audio group name */ @@ -296,6 +303,51 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont } +static void write_codec_attr(AVStream *st, VariantStream *vs) { +int codec_strlen = strlen(vs->codec_attr); +char attr[32]; + +if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) +return; +if (vs->attr_status == CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN) +return; + +if (st->codecpar->codec_id == AV_CODEC_ID_H264) { +uint8_t *data = st->codecpar->extradata; +if ((data[0] | data[1] | data[2]) == 0 && data[3] == 1 && (data[4] & 0x1F) == 7) { +snprintf(attr, sizeof(attr), + "avc1.%02x%02x%02x", data[5], data[6], data[7]); +} else { +goto fail; +} +} else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { +snprintf(attr, sizeof(attr), "mp4a.40.33"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) { +snprintf(attr, sizeof(attr), "mp4a.40.34"); +} else if (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(attr, sizeof(attr), "mp4a.40.2"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { +snprintf(attr, sizeof(attr), "ac-3"); +} else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) { +snprintf(attr, sizeof(attr), "ec-3"); +} else { +goto fail; +} +// Don't write the same attribute multiple times +if (!strstr(vs->codec_attr, attr)) { +snprintf(vs->codec_attr + codec_strlen, + sizeof(vs->codec_attr) - codec_strlen, + "%s%s", codec_strlen ? "," : "", attr); +} +return; + +fail: +vs->codec_attr[0] = '\0'; +vs->attr_status = CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN; +return; +} + static int replace_int_data_in_filename(char *buf, int buf_size, const char *filename, char placeholder, int64_t number) { const char *p; @@ -1235,7 +1287,7 @@ static int create_master_playlist(AVFormatContext *s, bandwidth += bandwidth / 10; ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name, -aud_st ? vs->agroup : NULL); +aud_st ? vs->agroup : NULL, vs->codec_attr); av_freep(_rel_name); } @@ -1764,6 +1816,19 @@ static int hls_write_header(AVFormatContext *s) continue; } avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den); +write_codec_attr(outer_st, vs); + +} +/* Update the Codec Attr string for the mapped audio groups */ +if (vs->has_video && vs->agroup) { +for (j = 0; j < hls->nb_varstreams; j++) { +VariantStream *vs_agroup = &(hls->var_streams[j]); +if (!vs_agroup->has_video && !vs_agroup->has_subtitle && +vs_agroup->agroup && +!av_strcasecmp(vs_agroup->agroup, vs->agroup)) { +write_codec_attr(vs_agroup->streams[0], vs); +} +} } } fail: diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 42f059a..99231c9 100644 --- a/libavformat/hlsplaylist.c +++
[FFmpeg-devel] [PATCH 3/3] avformat/dashenc: Addition of #EXT-X-MEDIA tag and AUDIO attribute
From: Karthick JeyapalThis is required for AV playout from master.m3u8. Otherwise master.m3u8 lists only video-only and/or audio-only streams. --- libavformat/dashenc.c | 24 ++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 478a384..a3eb522 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -737,6 +737,9 @@ 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"; +int is_default = 1; +int max_audio_bitrate = 0; if (*c->dirname) snprintf(filename_hls, sizeof(filename_hls), "%s/master.m3u8", c->dirname); @@ -758,9 +761,26 @@ static int write_manifest(AVFormatContext *s, int final) for (i = 0; i < s->nb_streams; i++) { char playlist_file[64]; AVStream *st = s->streams[i]; +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, + playlist_file, i, is_default); +max_audio_bitrate = FFMAX(st->codecpar->bit_rate, max_audio_bitrate); +is_default = 0; +} + +for (i = 0; i < s->nb_streams; i++) { +char playlist_file[64]; +AVStream *st = s->streams[i]; +char *agroup = NULL; +int stream_bitrate = st->codecpar->bit_rate; +if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && max_audio_bitrate) { +agroup = (char *)audio_group; +stream_bitrate += max_audio_bitrate; +} get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); -ff_hls_write_stream_info(st, out, st->codecpar->bit_rate, -playlist_file, NULL); +ff_hls_write_stream_info(st, out, stream_bitrate, playlist_file, agroup); } avio_close(out); if (use_rename) -- 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: Modularized audio rendition playlist write to allow reuse
From: Karthick Jeyapal--- libavformat/hlsenc.c | 6 ++ libavformat/hlsplaylist.c | 9 + libavformat/hlsplaylist.h | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 74f66ce..fe531fb 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1169,10 +1169,8 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -avio_printf(hls->m3u8_out, "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"group_%s\"", -vs->agroup); -avio_printf(hls->m3u8_out, ",NAME=\"audio_0\",DEFAULT=YES,URI=\"%s\"\n", -m3u8_rel_name); +ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name); + av_freep(_rel_name); } diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 42f059a..a065eda 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -35,6 +35,15 @@ void ff_hls_write_playlist_version(AVIOContext *out, int version) { avio_printf(out, "#EXT-X-VERSION:%d\n", version); } +void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, + char *filename) { +if (!out || !agroup || !filename) +return; + +avio_printf(out, "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"group_%s\"", agroup); +avio_printf(out, ",NAME=\"audio_0\",DEFAULT=YES,URI=\"%s\"\n", filename); +} + void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, char *filename, char *agroup) { if (!out || !filename) diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index ac03550..518cfc2 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -37,6 +37,7 @@ typedef enum { } PlaylistType; void ff_hls_write_playlist_version(AVIOContext *out, int version); +void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, char *filename); void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, char *filename, char *agroup); void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 2/3] avformat/hlsplaylist: Audio rendition's name and defaultness made configurable
From: Karthick Jeyapal--- libavformat/hlsenc.c | 2 +- libavformat/hlsplaylist.c | 5 +++-- libavformat/hlsplaylist.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index fe531fb..5cff3b4 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1169,7 +1169,7 @@ static int create_master_playlist(AVFormatContext *s, goto fail; } -ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name); +ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, 0, 1); av_freep(_rel_name); } diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index a065eda..098dc89 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -36,12 +36,13 @@ void ff_hls_write_playlist_version(AVIOContext *out, int version) { } void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, - char *filename) { + char *filename, int name_id, int is_default) { if (!out || !agroup || !filename) return; avio_printf(out, "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"group_%s\"", agroup); -avio_printf(out, ",NAME=\"audio_0\",DEFAULT=YES,URI=\"%s\"\n", filename); +avio_printf(out, ",NAME=\"audio_%d\",DEFAULT=%s,URI=\"%s\"\n", name_id, + is_default ? "YES" : "NO", filename); } void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index 518cfc2..9969315 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -37,7 +37,8 @@ typedef enum { } PlaylistType; void ff_hls_write_playlist_version(AVIOContext *out, int version); -void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, char *filename); +void ff_hls_write_audio_rendition(AVIOContext *out, char *agroup, + char *filename, int name_id, int is_default); void ff_hls_write_stream_info(AVStream *st, AVIOContext *out, int bandwidth, char *filename, char *agroup); void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache, -- 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/hlsenc: Signal http end of chunk(http_shutdown) during hlsenc_io_close()
From: Karthick JeyapalCurrently http end of chunk is signalled implicitly in hlsenc_io_open(). This mean playlists http writes would have to wait upto a segment duration to signal end of chunk causing delays. This patch will fix that problem and improve performance. --- libavformat/hlsenc.c | 5 + 1 file changed, 5 insertions(+) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 0095ca4..7b9bbc0 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -268,8 +268,13 @@ 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); +#if CONFIG_HTTP_PROTOCOL } else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); avio_flush(*pb); +ffurl_shutdown(http_url_context, AVIO_FLAG_WRITE); +#endif } } -- 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/http: Avoid calling http_shutdown() if end of chunk is signalled already
From: Karthick Jeyapal--- libavformat/http.c | 8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index cf86adc..4635a9a 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -307,9 +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; +if (!s->end_chunked_post) { +ret = http_shutdown(h, h->flags); +if (ret < 0) +return ret; +} s->end_chunked_post = 0; s->chunkend = 0; -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/http: Added a library-internal API for signalling end of chunk
From: Karthick JeyapalRight now there is no explicit way to signal end of chunk, when http_multiple is set. ff_http_do_new_request() function implicitly signals end of chunk. But that could be too late for certain applications. Hence added a new function ff_http_signal_end_of_chunk() which could be used internally within libavformat. --- libavformat/http.c | 17 ++--- libavformat/http.h | 9 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/libavformat/http.c b/libavformat/http.c index cf86adc..d8224de 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -307,9 +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; +if (!s->end_chunked_post) { +ret = http_shutdown(h, h->flags); +if (ret < 0) +return ret; +} s->end_chunked_post = 0; s->chunkend = 0; @@ -325,6 +327,15 @@ int ff_http_do_new_request(URLContext *h, const char *uri) return ret; } +int ff_http_signal_end_of_chunk(URLContext *h) { +HTTPContext *s = h->priv_data; +int ret = 0; +if (!s->end_chunked_post) { +ret = http_shutdown(h, h->flags); +} +return ret; +} + int ff_http_averror(int status_code, int default_averror) { switch (status_code) { diff --git a/libavformat/http.h b/libavformat/http.h index 7d02713..0eaeb48 100644 --- a/libavformat/http.h +++ b/libavformat/http.h @@ -47,6 +47,15 @@ void ff_http_init_auth_state(URLContext *dest, const URLContext *src); */ int ff_http_do_new_request(URLContext *h, const char *uri); +/** + * Send a end of chunk signal(sends a string "0\r\n\r\n"), if applicable. + * + * @param h pointer to the resource + * @return a negative value if an error condition occurred, 0 + * otherwise + */ +int ff_http_signal_end_of_chunk(URLContext *h); + int ff_http_averror(int status_code, int default_averror); #endif /* AVFORMAT_HTTP_H */ -- 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: Signal http end of chunk explicitly during hlsenc_io_close()
From: Karthick JeyapalCurrently http end of chunk is called implicitly in hlsenc_io_open(). This mean playlists http writes would have to wait upto a segment duration to signal end of chunk causing delays. This patch will fix that problem and improve performance. --- libavformat/hlsenc.c | 5 + 1 file changed, 5 insertions(+) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 0095ca4..65182c5 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -268,8 +268,13 @@ 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); +#if CONFIG_HTTP_PROTOCOL } else { +URLContext *http_url_context = ffio_geturlcontext(*pb); +av_assert0(http_url_context); avio_flush(*pb); +ff_http_signal_end_of_chunk(http_url_context); +#endif } } -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH 1/2] avformat/dashenc: Fix the EXT-X-TARGETDURATION as per the hls specification
From: Karthick JeyapalThe HLS specification states the following about EXT-X-TARGETDURATION 4.3.3.1. EXT-X-TARGETDURATION The EXT-X-TARGETDURATION tag specifies the maximum Media Segment duration. The EXTINF duration of each Media Segment in the Playlist file, when rounded to the nearest integer, MUST be less than or equal to the target duration; longer segments can trigger playback stalls or other errors. It applies to the entire Playlist file. Its format is: #EXT-X-TARGETDURATION: where s is a decimal-integer indicating the target duration in seconds. The EXT-X-TARGETDURATION tag is REQUIRED. Currently the dashenc rounds the duration to the next integer, rather than rounding the duration to the nearest integer. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 5687530..5368a23 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -358,7 +358,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext Segment *seg = os->segments[i]; double duration = (double) seg->duration / timescale; if (target_duration <= duration) -target_duration = hls_get_int_from_double(duration); +target_duration = lrint(duration); } ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, -- 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: Moved hls_get_int_from_double() function locally to hlsenc.c
From: Karthick Jeyapaldashenc no longer needs this function. --- libavformat/hlsenc.c | 5 + libavformat/hlsplaylist.h | 5 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 29fc1d4..14a9a12 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -240,6 +240,11 @@ static int mkdir_p(const char *path) { return ret; } +static int hls_get_int_from_double(double val) +{ +return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val; +} + 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; diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index 48d71b7..fe19f34 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -36,11 +36,6 @@ 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] avformat/dashenc: Fix the EXT-X-TARGETDURATION as per the hls specification
From: Karthick JeyapalThe HLS specification states the following about EXT-X-TARGETDURATION 4.3.3.1. EXT-X-TARGETDURATION The EXT-X-TARGETDURATION tag specifies the maximum Media Segment duration. The EXTINF duration of each Media Segment in the Playlist file, when rounded to the nearest integer, MUST be less than or equal to the target duration; longer segments can trigger playback stalls or other errors. It applies to the entire Playlist file. Its format is: #EXT-X-TARGETDURATION: where s is a decimal-integer indicating the target duration in seconds. The EXT-X-TARGETDURATION tag is REQUIRED. Currently the dashenc rounds the duration to the next integer, rather than rounding the duration to the nearest integer. --- libavformat/dashenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 5687530..5368a23 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -358,7 +358,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, DASHContext Segment *seg = os->segments[i]; double duration = (double) seg->duration / timescale; if (target_duration <= duration) -target_duration = hls_get_int_from_double(duration); +target_duration = lrint(duration); } ff_hls_write_playlist_header(out_hls, 6, -1, target_duration, -- 1.9.1 ___ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
[FFmpeg-devel] [PATCH] avformat/hlsenc: Fix a memory leak when http_persistent is 1
From: Karthick Jeyapal--- libavformat/hlsenc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index e3442c3..5ee28ea 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -1918,6 +1918,8 @@ static int hls_write_trailer(struct AVFormatContext *s) av_freep(>baseurl); } +ff_format_io_close(s, >m3u8_out); +ff_format_io_close(s, >sub_m3u8_out); av_freep(>key_basename); av_freep(>var_streams); av_freep(>master_m3u8_url); -- 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: Persistent HTTP connections supported as an option
From: Karthick Jeyapal--- doc/muxers.texi | 2 ++ libavformat/dashenc.c | 67 +-- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 3d0c7bf..d6415db 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -249,6 +249,8 @@ 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 -http_persistent @var{http_persistent} +Use persistent HTTP connections. 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. 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(>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(_opts, c); -avio_open2(_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, _opts); +dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); av_dict_free(_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,
[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
[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(>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(_opts, c); -avio_open2(_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, _opts); +dashenc_io_open(s, >m3u8_out, temp_filename_hls, _opts); av_dict_free(_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
[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] 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(>pb); vs->packets_written = 0; -ff_format_io_close(s, >out); hlsenc_io_close(s, >out, vs->base_output_dirname); } else { hlsenc_io_close(s, >pb, oc->filename); -- 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 JeyapalBefore 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, , hls); -ret = s->io_open(s, _pb, hls->master_m3u8_url, AVIO_FLAG_WRITE,\ - ); +ret = hlsenc_io_open(s, >m3u8_out, hls->master_m3u8_url, ); av_dict_free(); 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(_rel_name); } @@ -1164,7 +1164,7 @@ fail: if(ret >=0) hls->master_m3u8_created = 1; av_freep(_rel_name); -ff_format_io_close(s, _pb); +hlsenc_io_close(s, >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, , hls); snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", vs->m3u8_name); -if ((ret = s->io_open(s, , temp_filename, AVIO_FLAG_WRITE, )) < 0) +if ((ret = hlsenc_io_open(s, >m3u8_out, temp_filename, )) < 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 == SEGMENT_TYPE_FMP4) && (en == vs->segments)) { -
[FFmpeg-devel] [PATCH v2 1/3] avformat/hlsenc: Call avio_flush during persistent http connections
From: Karthick JeyapalSince 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 3/3] avformat/hlsenc: Extend persistent http connections to playlists
From: Karthick JeyapalBefore 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, , hls); -ret = s->io_open(s, _pb, hls->master_m3u8_url, AVIO_FLAG_WRITE,\ - ); +ret = hlsenc_io_open(s, >m3u8_out, hls->master_m3u8_url, ); av_dict_free(); 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(_rel_name); } @@ -1160,7 +1160,7 @@ fail: if(ret >=0) hls->master_m3u8_created = 1; av_freep(_rel_name); -ff_format_io_close(s, _pb); +hlsenc_io_close(s, >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, , hls); snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", vs->m3u8_name); -if ((ret = s->io_open(s, , temp_filename, AVIO_FLAG_WRITE, )) < 0) +if ((ret = hlsenc_io_open(s, >m3u8_out, temp_filename, )) < 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 == SEGMENT_TYPE_FMP4) && (en == vs->segments)) { -
[FFmpeg-devel] [PATCH 1/3] avformat/hlsenc: Call avio_flush during persistent http connections
From: Karthick JeyapalSince 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 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 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(>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(_opts, c); +avio_open2(_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, _opts); +av_dict_free(_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 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 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 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(>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(_opts, c); +avio_open2(_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, _opts); +av_dict_free(_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 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(>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(_opts, c); +avio_open2(_hls, temp_filename_hls, AVIO_FLAG_WRITE, NULL, _opts); +av_dict_free(_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,
[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(_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(_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) ? _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, int last,
[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, >pb, filename, AVIO_FLAG_WRITE, ); +err = hlsenc_io_open(s, >pb, filename, ); av_free(filename); av_dict_free(); if (err < 0) return err; } else -if ((err = s->io_open(s, >pb, oc->filename, AVIO_FLAG_WRITE, )) < 0) +if ((err = hlsenc_io_open(s, >pb, oc->filename, )) < 0) goto fail; if (vs->vtt_basename) { set_http_options(s, , c); -if ((err = s->io_open(s, _oc->pb, vtt_oc->filename, AVIO_FLAG_WRITE, )) < 0) +if ((err = hlsenc_io_open(s, _oc->pb, vtt_oc->filename, )) < 0) goto fail; } av_dict_free(); @@ -2165,11 +2197,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) avio_open_dyn_buf(>pb); vs->packets_written = 0; ff_format_io_close(s, >out); +hlsenc_io_close(s, >out, vs->base_output_dirname); } else { -ff_format_io_close(s, >pb); +hlsenc_io_close(s, >pb, oc->filename); } if (vs->vtt_avf) { -ff_format_io_close(s, >vtt_avf->pb); +hlsenc_io_close(s, >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}, 0, 0,E}, {"master_pl_publish_rate", "Publish master play list every after this many segment intervals",
[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 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