From patchwork Thu Oct 17 18:59:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Almer X-Patchwork-Id: 15817 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 2120344A238 for ; Thu, 17 Oct 2019 22:00:11 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 09A6868AAC7; Thu, 17 Oct 2019 22:00:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-qt1-f196.google.com (mail-qt1-f196.google.com [209.85.160.196]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 10A9D689ADE for ; Thu, 17 Oct 2019 22:00:04 +0300 (EEST) Received: by mail-qt1-f196.google.com with SMTP id r5so5230658qtd.0 for ; Thu, 17 Oct 2019 12:00:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=Nz1okUtvu8LchBskKYNBRiHJkmfl62zkO1D3Ed4HhDg=; b=c61JqXeqe1cGv/ErOcEW+rPyovAybgenfqdMtXgwITgQMBFH5JVdCZkfNKbiM9ToES y5HInXTEfdaCDDdNbOm1Xoynb4vaRvQ4krrbFmxBcX/sqMcUs9ps6fO0P5QC3zs5schb BmPZ4/FBF2h6JLcfOPbg+u+3g6sc6S9gYq6kqbvKN7UxSkWYsNz+kVzVB4yJSMsjWzNj Ymigxw80sGzq4oSpCTgfkSzA0NeKkJ1KYuFhN8zfsIB/BwjDlR4XFShFTE2XROsH2t/V E0qtZW9XnEke2V60UOya+qiSawnuFpvo771XNqD3nkj6iYIMBdhFhTVPMj1hS/IMMRIF 71Lw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Nz1okUtvu8LchBskKYNBRiHJkmfl62zkO1D3Ed4HhDg=; b=XXApoXPco6tEGT+lr/4mmiSsWCnbATzJzSU868pOfHynShmtWcWi9VTYV2acURNXrQ rw6EepsYGfd/zpr0EVIw+MJ52FZqCfNekNjx18+P6F7PJYADUtIqjjYar4JBLT1iKo6E v3ULf+KXh9C51qBr1Lj7FWURf5oqGW/CBQF/yaTfsJdRISKAW699t2byB8ESCVtdo8VA 3ZEdhJBaJi/0JfsVumpn7wtMIpiVByB/3ueSWwfuAXzUsMDn23SugGdNOeJ7bFU0rQT/ ZQB7I8ijHsAb/HTGLksWV/o/q7Hg96u/W8OsYhb/+fIK5Utlep9APbwHqocRHrldJM4D S80g== X-Gm-Message-State: APjAAAWeUSXYcEhltbqFol/ngDooG16P+KdY2FDsqqjEC4fFoPlpyt35 tqsr+YOPdcDFs8glOsCyBE0IpkKX X-Google-Smtp-Source: APXvYqw5j3XkeVzJKkoQCGDWR/QZSxoDC4qV38cnyUJRaTsYTVuKOV1sfmLYNkZK39pxL5nkLj8sZA== X-Received: by 2002:aed:2ce7:: with SMTP id g94mr5437532qtd.305.1571338802405; Thu, 17 Oct 2019 12:00:02 -0700 (PDT) Received: from localhost.localdomain ([191.83.221.234]) by smtp.gmail.com with ESMTPSA id v141sm798933qka.59.2019.10.17.12.00.01 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 17 Oct 2019 12:00:02 -0700 (PDT) From: James Almer To: ffmpeg-devel@ffmpeg.org Date: Thu, 17 Oct 2019 15:59:06 -0300 Message-Id: <20191017185916.2957-2-jamrial@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191017185916.2957-1-jamrial@gmail.com> References: <20191017185916.2957-1-jamrial@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 01/11] avformat/dashenc: allow setting segment durations per AdaptationSet 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Implemented as as a seg_duration key=value entry in the adaptation_sets muxer option. It has the same syntax as the global seg_duration option, and has precedence over it if set. Signed-off-by: James Almer --- libavformat/dashenc.c | 49 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index a462876c13..664ae4b763 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/parseutils.h" #include "libavutil/rational.h" #include "libavutil/time.h" #include "libavutil/time_internal.h" @@ -70,6 +71,7 @@ typedef struct Segment { typedef struct AdaptationSet { char id[10]; char *descriptor; + int64_t seg_duration; enum AVMediaType media_type; AVDictionary *metadata; AVRational min_frame_rate, max_frame_rate; @@ -85,6 +87,7 @@ typedef struct OutputStream { int64_t init_start_pos, pos; int init_range_length; int nb_segments, segments_size, segment_index; + int64_t seg_duration; Segment **segments; int64_t first_pts, start_pts, max_pts; int64_t last_dts, last_pts; @@ -614,7 +617,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE; avio_printf(out, "\t\t\t\tuse_timeline) { - avio_printf(out, "duration=\"%"PRId64"\" ", c->seg_duration); + avio_printf(out, "duration=\"%"PRId64"\" ", os->seg_duration); if (c->streaming && os->availability_time_offset) avio_printf(out, "availabilityTimeOffset=\"%.3f\" ", os->availability_time_offset); @@ -840,7 +843,7 @@ static int parse_adaptation_sets(AVFormatContext *s) { DASHContext *c = s->priv_data; const char *p = c->adaptation_sets; - enum { new_set, parse_id, parsing_streams, parse_descriptor } state; + enum { new_set, parse_id, parsing_streams, parse_descriptor, parse_seg_duration } state; AdaptationSet *as; int i, n, ret; @@ -858,8 +861,11 @@ static int parse_adaptation_sets(AVFormatContext *s) // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on // option id=0,descriptor=descriptor_str,streams=0,1,2 and so on + // option id=0,seg_duration=2.5,streams=0,1,2 and so on // descriptor is useful to the scheme defined by ISO/IEC 23009-1:2014/Amd.2:2015 // descriptor_str should be a self-closing xml tag. + // seg_duration has the same syntax as the global seg_duration option, and has + // precedence over it if set. state = new_set; while (*p) { if (*p == ' ') { @@ -877,7 +883,25 @@ static int parse_adaptation_sets(AVFormatContext *s) if (*p) p++; state = parse_id; - } else if (state == parse_id && av_strstart(p, "descriptor=", &p)) { + } else if (state != new_set && av_strstart(p, "seg_duration=", &p)) { + char str[32]; + int64_t usecs = 0; + + n = strcspn(p, ","); + snprintf(str, sizeof(str), "%.*s", n, p); + p += n; + if (*p) + p++; + + ret = av_parse_time(&usecs, str, 1); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", str); + return ret; + } + + as->seg_duration = usecs; + state = parse_seg_duration; + } else if (state != new_set && av_strstart(p, "descriptor=", &p)) { n = strcspn(p, ">") + 1; //followed by one comma, so plus 1 if (n < strlen(p)) { as->descriptor = av_strndup(p, n); @@ -889,7 +913,7 @@ static int parse_adaptation_sets(AVFormatContext *s) if (*p) p++; state = parse_descriptor; - } else if ((state == parse_id || state == parse_descriptor) && av_strstart(p, "streams=", &p)) { //descriptor is optional + } else if ((state != new_set) && av_strstart(p, "streams=", &p)) { //descriptor and duration are optional state = parsing_streams; } else if (state == parsing_streams) { AdaptationSet *as = &c->as[c->nb_as - 1]; @@ -1361,6 +1385,7 @@ static int dash_init(AVFormatContext *s) os->first_pts = AV_NOPTS_VALUE; os->max_pts = AV_NOPTS_VALUE; os->last_dts = AV_NOPTS_VALUE; + os->seg_duration = as->seg_duration ? as->seg_duration : c->seg_duration; os->segment_index = 1; if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) @@ -1582,7 +1607,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) c->streams[stream].first_pts, s->streams[stream]->time_base, AV_TIME_BASE_Q); - next_exp_index = (pts_diff / c->seg_duration) + 1; + next_exp_index = (pts_diff / c->streams[stream].seg_duration) + 1; } } @@ -1598,6 +1623,9 @@ static int dash_flush(AVFormatContext *s, int final, int stream) // Flush all audio streams as well, in sync with video keyframes, // but not the other video streams. if (stream >= 0 && i != stream) { + if (s->streams[stream]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && + s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + continue; if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; // Make sure we don't flush audio streams multiple times, when @@ -1740,22 +1768,22 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) if (!os->availability_time_offset && pkt->duration) { int64_t frame_duration = av_rescale_q(pkt->duration, st->time_base, AV_TIME_BASE_Q); - os->availability_time_offset = ((double) c->seg_duration - + os->availability_time_offset = ((double) os->seg_duration - frame_duration) / AV_TIME_BASE; } if (c->use_template && !c->use_timeline) { elapsed_duration = pkt->pts - os->first_pts; - seg_end_duration = (int64_t) os->segment_index * c->seg_duration; + seg_end_duration = (int64_t) os->segment_index * os->seg_duration; } else { elapsed_duration = pkt->pts - os->start_pts; - seg_end_duration = c->seg_duration; + seg_end_duration = os->seg_duration; } - if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && - pkt->flags & AV_PKT_FLAG_KEY && os->packets_written && + if (pkt->flags & AV_PKT_FLAG_KEY && os->packets_written && av_compare_ts(elapsed_duration, st->time_base, seg_end_duration, AV_TIME_BASE_Q) >= 0) { + if (!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { int64_t prev_duration = c->last_duration; c->last_duration = av_rescale_q(pkt->pts - os->start_pts, @@ -1773,6 +1801,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) "and use_template, or keep a stricter keyframe interval\n"); } } + } if ((ret = dash_flush(s, 0, pkt->stream_index)) < 0) return ret;