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

Reply via email to