From patchwork Sat Nov 17 17:40:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrey Semashev X-Patchwork-Id: 11053 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id F3F6444E07F for ; Sat, 17 Nov 2018 19:41:19 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 778C4680D4F; Sat, 17 Nov 2018 19:41:20 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-lj1-f193.google.com (mail-lj1-f193.google.com [209.85.208.193]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 55120680974 for ; Sat, 17 Nov 2018 19:41:14 +0200 (EET) Received: by mail-lj1-f193.google.com with SMTP id s5-v6so22943334ljd.12 for ; Sat, 17 Nov 2018 09:41:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=S86uytfNpOktzIOHdVBWW9zmiO5WBVfCclGOTZW9UWQ=; b=rvJgjEYx/gjZl/VT6cfJxfGfhEi5W4WZz6rJqbAxQcu6NUH6LRwsIZMcTXZHrgf8zP rm4JI8VKhuSk79Drr52JBLfASFGxwCstbi+SYtNe5AgTxOrckIbvinUcRA9BlES+382r b61dmdmELOEoPjHMBv7chrIO4hXEW3bfUQb6vssDxoGYLV3qcSDQtu1JwSq3F/Zp5XLV O/Q2fys7qhzjDJuLEsoXdqadqvcwYSUdAKJxQRUl6Tky6KeZurya9eFgWtfrnwta8Sc3 AEG14WIE9GmNZISFeSWm9qIOZ9lLtS6R7YE3Funlev43HFxVYB2Cp8YqpBew0Jx8BOIP K+Tw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=S86uytfNpOktzIOHdVBWW9zmiO5WBVfCclGOTZW9UWQ=; b=VtdcOucPzcIR1cVeZlyKJa9B14C8+E5qWXlfe9dJ5Vni5Kq3A5esdSmwlALrr+jvBA +P+bV3M+tZHocKZeSRqoJ8LV4mxLQCjhxumAD4imzBIFUOmTD/iAJSPqr9g4Hyffdpfs KUfyO2LyhdtDMH9iypI8gii931nnHj3VIyy/BHD/1W9xFqvOBMCJDV3Pp/A/qFuQS9oB 3B1TcuKPbuhqRmBpGCmlCqZh+RV1WnOlWMPqFmVxhUCscGKd5besPg8yF+8xXf3U613G xqAh+PoVSc5jjoC56QB2qExS5d1aSNLAdHjZyaZY63fSvpnWzBqAmAmVh6LhY4nzQNYE wh3A== X-Gm-Message-State: AGRZ1gKxfs0jhhEYUWOARlne1NOEcNqJs+X0UjnF9nNus34O+1A+Z/Eo JpJGBT0tKiR2HjqOvZqkS/lL4jZI X-Google-Smtp-Source: AJdET5cARBSLmxVdovocXHhtZLEz4L6qfjJLtqk/BvyXV6HZt2T2gESEqIUWOUh1vx+y92xmbw3VOg== X-Received: by 2002:a2e:5109:: with SMTP id f9-v6mr9159069ljb.52.1542476474163; Sat, 17 Nov 2018 09:41:14 -0800 (PST) Received: from localhost.localdomain (broadband-37-110-31-10.ip.moscow.rt.ru. [37.110.31.10]) by smtp.gmail.com with ESMTPSA id l63sm5095884lfl.76.2018.11.17.09.41.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 17 Nov 2018 09:41:13 -0800 (PST) From: Andrey Semashev To: ffmpeg-devel@ffmpeg.org Date: Sat, 17 Nov 2018 20:40:50 +0300 Message-Id: <20181117174053.19457-1-andrey.semashev@gmail.com> X-Mailer: git-send-email 2.19.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/4] lavf/dashenc: Add DASH segment type auto and make it the default X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Andrey Semashev Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" This commit restores the ability to create DASH streams with codecs that require different containers that was lost after commit 2efdbf7367989cf9d296c25fa3d2aff8d6e25fdd. It adds a new "auto" value for the dash_segment_type option and makes it the default. When in this mode, the segment format will be chosen based on the codec used in the stream: webm for Vorbis, Opus, VP8 or VP9, mp4 otherwise. --- doc/muxers.texi | 7 +++-- libavformat/dashenc.c | 72 ++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 62f4091e31..2fed5cf3d4 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -289,10 +289,13 @@ 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} +@item -dash_segment_type @var{dash_segment_type} Possible values: +@item auto +If this flag is set, the dash segment files format will be selected based on the stream codec. This is the default mode. + @item mp4 -If this flag is set, the dash segment files will be in in ISOBMFF format. This is the default format. +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. diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index d151921175..0af7b85c5f 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -49,7 +49,8 @@ #include "dash.h" typedef enum { - SEGMENT_TYPE_MP4 = 0, + SEGMENT_TYPE_AUTO = 0, + SEGMENT_TYPE_MP4, SEGMENT_TYPE_WEBM, SEGMENT_TYPE_NB } SegmentType; @@ -84,6 +85,8 @@ typedef struct OutputStream { int64_t first_pts, start_pts, max_pts; int64_t last_dts, last_pts; int bit_rate; + SegmentType segment_type; /* segment type selected for this particular stream */ + const char *format_name; char codec_str[100]; int written_len; @@ -131,8 +134,7 @@ typedef struct DASHContext { int64_t timeout; int index_correction; char *format_options_str; - SegmentType segment_type; - const char *format_name; + SegmentType segment_type_option; /* segment type as specified in options */ } DASHContext; static struct codec_string { @@ -151,6 +153,7 @@ static struct format_string { SegmentType segment_type; const char *str; } formats[] = { + { SEGMENT_TYPE_AUTO, "auto" }, { SEGMENT_TYPE_MP4, "mp4" }, { SEGMENT_TYPE_WEBM, "webm" }, { 0, NULL } @@ -197,6 +200,38 @@ static const char *get_format_str(SegmentType segment_type) { return NULL; } +static inline SegmentType select_segment_type(SegmentType segment_type, AVCodecID codec_id) +{ + if (segment_type == SEGMENT_TYPE_AUTO) { + if (codec_id == AV_CODEC_ID_OPUS || codec_id == AV_CODEC_ID_VORBIS || + codec_id == AV_CODEC_ID_VP8 || codec_id == AV_CODEC_ID_VP9) { + segment_type = SEGMENT_TYPE_WEBM; + } else { + segment_type = SEGMENT_TYPE_MP4; + } + } + + return segment_type; +} + +static int init_segment_types(AVFormatContext *s) +{ + DASHContext *c = s->priv_data; + for (int i = 0; i < s->nb_streams; ++i) { + OutputStream *os = &c->streams[i]; + SegmentType segment_type = select_segment_type( + c->segment_type_option, s->streams[i]->codecpar->codec_id); + os->segment_type = segment_type; + os->format_name = get_format_str(segment_type); + if (!os->format_name) { + av_log(s, AV_LOG_ERROR, "Could not select DASH segment type for stream %d\n", i); + return AVERROR_MUXER_NOT_FOUND; + } + } + + return 0; +} + static int check_file_extension(const char *filename, const char *extension) { char *dot; if (!filename || !extension) @@ -622,13 +657,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, 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\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\n", s->streams[i]->codecpar->channels); } @@ -993,6 +1028,9 @@ static int dash_init(AVFormatContext *s) if ((ret = parse_adaptation_sets(s)) < 0) return ret; + if ((ret = init_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]; @@ -1018,13 +1056,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 " @@ -1032,7 +1067,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; @@ -1076,7 +1111,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 @@ -1141,7 +1176,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; } @@ -1312,7 +1347,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); @@ -1502,7 +1537,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) @@ -1598,7 +1633,8 @@ 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"}, + { "dash_segment_type", "set dash segment files type", OFFSET(segment_type_option), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_AUTO }, 0, SEGMENT_TYPE_NB - 1, E, "segment_type"}, + { "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"}, { NULL },