On 11/12/18 5:20 PM, Andrey Semashev wrote: > On 11/12/18 8:20 AM, Jeyapal, Karthick wrote: >> >> On 11/8/18 10:27 PM, Andrey Semashev wrote: >>> This commit restores the ability to create DASH streams with codecs >>> that require different containers that was lost after commit >>> 2efdbf7367989cf9d296c25fa3d2aff8d6e25fdd. It extends the dash_segment_type >>> option syntax to allow to specify segment container types for individual >>> streams, in addition to the default container type that is applied to >>> all streams. The extended syntax is backward compatible with the previous >>> syntax. >> Thanks for sending the patch. I understand your requirement completely. >> But I feel that this option for mapping streams with container format is >> little confusing. Also, the relevant code is relatively big, and thus >> difficult to maintain in future. >> I have a middle ground suggestion. If your goal is to achieve the earlier >> behavior broken commits, then I propose the following. >> Option "dash_segment_type" could take one more option "auto" (instead of mp4 >> or webm). >> When "auto" is chosen, the muxer could choose webm format for VP8, VP9, >> vorbis, opus streams and mp4 format for all other streams. >> In this method the previous behavior of dashenc muxer could be restored with >> little addition to the overall code. Also it's usage will be simpler and >> easier to understand. > > This solution might be ok for just restoring the previous capability, but I > think the ability for selecting the container format by the user is still > more useful. For example, Opus can be muxed in both mp4 (although, with > experimental flag) and webm, and it may make sense to some users to select > mp4. (In my case, though, I wanted webm, hence the patch.) In that case they could select "dash_segment_type" as "mp4", in which case all streams including opus will be muxed in mp4 format. I don't see a use-case where somebody wants opus in mp4 and would want other streams in webm format. > > Besides the parser, it doesn't add much code, and if I can improve the patch, > please let me know how. If you absolutely don't want this functionality, > that's ok, I'll keep this patch for my local builds and submit an "auto" > option patch instead. I am not absolutely against this patch. But I am just trying to find if there is a use-case for such an advanced option. If there is a practical requirement for such a use-case, then I agree that we should review and push this patch for sure. But if the requirement is just theoretical at this point, then I would prefer the "auto" option patch. Maybe we could revisit this advanced options patch when a real requirement comes up. > >>> --- >>> doc/muxers.texi | 8 ++- >>> libavformat/dashenc.c | 161 +++++++++++++++++++++++++++++++++++------- >>> 2 files changed, 140 insertions(+), 29 deletions(-) >>> >>> diff --git a/doc/muxers.texi b/doc/muxers.texi >>> index 62f4091e31..4418b38c76 100644 >>> --- a/doc/muxers.texi >>> +++ b/doc/muxers.texi >>> @@ -289,8 +289,12 @@ Set container format (mp4/webm) options using a >>> @code{:} separated list of >>> key=value parameters. Values containing @code{:} special characters must >>> be >>> escaped. >>> -@item dash_segment_type @var{dash_segment_type} >>> -Possible values: >>> +@item -dash_segment_type @var{dash_segment_type} >>> +Sets the container type for dash segment files. Syntax is "<type> >>> <type>:a,b,c <type>:d,e" where <type> is >>> +the container type and a, b, c, d and e are the indices of the mapped >>> streams. When no indices are specified, >>> +the container type is set for all streams. >>> + >>> +Possible container type values: >>> @item mp4 >>> If this flag is set, the dash segment files will be in in ISOBMFF format. >>> This is the default format. >>> diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c >>> index f8b3d106d5..626dc76413 100644 >>> --- a/libavformat/dashenc.c >>> +++ b/libavformat/dashenc.c >>> @@ -84,6 +84,8 @@ typedef struct OutputStream { >>> int64_t first_pts, start_pts, max_pts; >>> int64_t last_dts, last_pts; >>> int bit_rate; >>> + SegmentType segment_type; >>> + const char *format_name; >>> char codec_str[100]; >>> int written_len; >>> @@ -131,8 +133,7 @@ typedef struct DASHContext { >>> int64_t timeout; >>> int index_correction; >>> char *format_options_str; >>> - SegmentType segment_type; >>> - const char *format_name; >>> + const char *segment_types_str; >>> } DASHContext; >>> static struct codec_string { >>> @@ -188,14 +189,6 @@ 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 int check_file_extension(const char *filename, const char >>> *extension) { >>> char *dot; >>> if (!filename || !extension) >>> @@ -375,6 +368,8 @@ static void dash_free(AVFormatContext *s) >>> c->nb_as = 0; >>> } >>> + av_freep(&c->segment_types_str); >>> + >>> if (!c->streams) >>> return; >>> for (i = 0; i < s->nb_streams; i++) { >>> @@ -621,13 +616,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\t<Representation id=\"%d\" >>> mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"", >>> - i, c->format_name, os->codec_str, bandwidth_str, >>> s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); >>> + i, os->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<Representation id=\"%d\" >>> mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n", >>> - i, c->format_name, os->codec_str, bandwidth_str, >>> s->streams[i]->codecpar->sample_rate); >>> + i, os->format_name, os->codec_str, bandwidth_str, >>> s->streams[i]->codecpar->sample_rate); >>> avio_printf(out, "\t\t\t\t<AudioChannelConfiguration >>> schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" >>> value=\"%d\" />\n", >>> s->streams[i]->codecpar->channels); >>> } >>> @@ -773,6 +768,120 @@ end: >>> return 0; >>> } >>> +static inline void set_all_segment_types(AVFormatContext *s, int >>> format_idx) >>> +{ >>> + DASHContext *c = s->priv_data; >>> + for (int i = 0; i < s->nb_streams; ++i) { >>> + OutputStream *os = &c->streams[i]; >>> + os->segment_type = formats[format_idx].segment_type; >>> + os->format_name = formats[format_idx].str; >>> + } >>> +} >>> + >>> +static int parse_segment_types(AVFormatContext *s) >>> +{ >>> + DASHContext *c = s->priv_data; >>> + const char *p = c->segment_types_str; >>> + enum { type_expected, type_parsed, parsing_streams } state; >>> + int i, format_idx; >>> + >>> + // Set the default container type in case if some streams are not >>> mentioned in the string >>> + set_all_segment_types(s, 0); >>> + >>> + if (!p) >>> + return 0; >>> + >>> + // Parse per-stream container types: mp4 webm:0,1,2 and so on >>> + state = type_expected; >>> + format_idx = 0; >>> + while (*p) { >>> + if (*p == ' ') { >>> + if (state == type_parsed) >>> + set_all_segment_types(s, format_idx); >>> + >>> + state = type_expected; >>> + ++p; >>> + continue; >>> + } >>> + >>> + if (state == type_expected) { >>> + for (i = 0; i < SEGMENT_TYPE_NB; i++) { >>> + if (av_strstart(p, formats[i].str, &p)) { >>> + state = type_parsed; >>> + format_idx = i; >>> + break; >>> + } >>> + } >>> + >>> + if (state != type_parsed) { >>> + av_log(s, AV_LOG_ERROR, "DASH segment type not recognized >>> at position %d of \"%s\"\n", (int)(p - c->segment_types_str + 1), >>> c->segment_types_str); >>> + return AVERROR_MUXER_NOT_FOUND; >>> + } >>> + >>> + continue; >>> + } >>> + >>> + if (state == type_parsed) { >>> + if (*p != ':') { >>> + av_log(s, AV_LOG_ERROR, "Unexpected character at position >>> %d of \"%s\", a colon is expected\n", (int)(p - c->segment_types_str + 1), >>> c->segment_types_str); >>> + return AVERROR(EINVAL); >>> + } >>> + >>> + state = parsing_streams; >>> + ++p; >>> + continue; >>> + } >>> + >>> + if (state == parsing_streams) { >>> + while (*p && *p != ' ') { >>> + if (*p == ',') { >>> + ++p; >>> + } else if (*p == 'a' || *p == 'v') { >>> + // Select all streams of the given type >>> + enum AVMediaType type = (*p == 'v') ? >>> AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO; >>> + >>> + for (i = 0; i < s->nb_streams; ++i) { >>> + if (s->streams[i]->codecpar->codec_type == type) { >>> + OutputStream *os = &c->streams[i]; >>> + os->segment_type = >>> formats[format_idx].segment_type; >>> + os->format_name = formats[format_idx].str; >>> + } >>> + } >>> + >>> + ++p; >>> + } else { >>> + // Select a stream by index >>> + OutputStream *os; >>> + const char* end_p = p; >>> + i = strtol(p, (char**)&end_p, 10); >>> + if (p == end_p) { >>> + av_log(s, AV_LOG_ERROR, "Failed to parse stream >>> index at position %d of \"%s\"\n", (int)(p - c->segment_types_str + 1), >>> c->segment_types_str); >>> + return AVERROR(EINVAL); >>> + } >>> + if (i < 0 || i >= s->nb_streams) { >>> + av_log(s, AV_LOG_ERROR, "Selected stream %d not >>> found!\n", i); >>> + return AVERROR(EINVAL); >>> + } >>> + >>> + os = &c->streams[i]; >>> + os->segment_type = formats[format_idx].segment_type; >>> + os->format_name = formats[format_idx].str; >>> + >>> + p = end_p; >>> + } >>> + } >>> + >>> + state = type_expected; >>> + } >>> + } >>> + >>> + // Complete segment type setup if no streams are specified after the >>> container type >>> + if (state == type_parsed) >>> + set_all_segment_types(s, format_idx); >>> + >>> + return 0; >>> +} >>> + >>> static int write_manifest(AVFormatContext *s, int final) >>> { >>> DASHContext *c = s->priv_data; >>> @@ -992,6 +1101,9 @@ static int dash_init(AVFormatContext *s) >>> if ((ret = parse_adaptation_sets(s)) < 0) >>> return ret; >>> + if ((ret = parse_segment_types(s)) < 0) >>> + return ret; >>> + >>> for (i = 0; i < s->nb_streams; i++) { >>> OutputStream *os = &c->streams[i]; >>> AdaptationSet *as = &c->as[os->as_idx - 1]; >>> @@ -1017,13 +1129,10 @@ static int dash_init(AVFormatContext *s) >>> if (!ctx) >>> return AVERROR(ENOMEM); >>> - 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)) { >>> + if (os->segment_type == SEGMENT_TYPE_WEBM) { >>> + if ((!c->single_file && check_file_extension(c->init_seg_name, >>> os->format_name) != 0) || >>> + (!c->single_file && >>> check_file_extension(c->media_seg_name, os->format_name) != 0) || >>> + (c->single_file && >>> check_file_extension(c->single_file_name, os->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 " >>> @@ -1031,7 +1140,7 @@ static int dash_init(AVFormatContext *s) >>> } >>> } >>> - ctx->oformat = av_guess_format(c->format_name, NULL, NULL); >>> + ctx->oformat = av_guess_format(os->format_name, NULL, NULL); >>> if (!ctx->oformat) >>> return AVERROR_MUXER_NOT_FOUND; >>> os->ctx = ctx; >>> @@ -1075,7 +1184,7 @@ static int dash_init(AVFormatContext *s) >>> return ret; >>> } >>> - if (c->segment_type == SEGMENT_TYPE_MP4) { >>> + if (os->segment_type == SEGMENT_TYPE_MP4) { >>> if (c->streaming) >>> av_dict_set(&opts, "movflags", >>> "frag_every_frame+dash+delay_moov+global_sidx", 0); >>> else >>> @@ -1140,7 +1249,7 @@ static int dash_write_header(AVFormatContext *s) >>> // Flush init segment >>> // Only for WebM segment, since for mp4 delay_moov is set and >>> // the init segment is thus flushed after the first packets. >>> - if (c->segment_type == SEGMENT_TYPE_WEBM && >>> + if (os->segment_type == SEGMENT_TYPE_WEBM && >>> (ret = flush_init_segment(s, os)) < 0) >>> return ret; >>> } >>> @@ -1311,7 +1420,7 @@ static int dash_flush(AVFormatContext *s, int final, >>> int stream) >>> } >>> if (!c->single_file) { >>> - if (c->segment_type == SEGMENT_TYPE_MP4 && !os->written_len) >>> + if (os->segment_type == SEGMENT_TYPE_MP4 && !os->written_len) >>> write_styp(os->ctx->pb); >>> } else { >>> snprintf(os->full_path, sizeof(os->full_path), "%s%s", >>> c->dirname, os->initfile); >>> @@ -1501,7 +1610,7 @@ static int dash_write_packet(AVFormatContext *s, >>> AVPacket *pkt) >>> } >>> //write out the data immediately in streaming mode >>> - if (c->streaming && c->segment_type == SEGMENT_TYPE_MP4) { >>> + if (c->streaming && os->segment_type == SEGMENT_TYPE_MP4) { >>> int len = 0; >>> uint8_t *buf = NULL; >>> if (!os->written_len) >>> @@ -1597,9 +1706,7 @@ static const AVOption options[] = { >>> { "timeout", "set timeout for socket I/O operations", >>> OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = >>> E }, >>> { "index_correction", "Enable/Disable segment index correction >>> logic", OFFSET(index_correction), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, >>> { "format_options","set list of options for the container format >>> (mp4/webm) used for dash", OFFSET(format_options_str), AV_OPT_TYPE_STRING, >>> {.str = NULL}, 0, 0, E}, >>> - { "dash_segment_type", "set dash segment files type", >>> OFFSET(segment_type), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_MP4 }, 0, >>> SEGMENT_TYPE_NB - 1, 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"}, >>> + { "dash_segment_type", "DASH segment files type. Syntax: \"<type> >>> <type>:<ids>\" where <type> is one of mp4 or webm and <ids> are >>> comma-separated stream ids", OFFSET(segment_types_str), AV_OPT_TYPE_STRING, >>> { .str = NULL }, 0, 0, E }, >>> { NULL }, >>> }; >>> >> > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel