From patchwork Mon Apr 15 15:09:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Almer X-Patchwork-Id: 48069 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a21:670b:b0:1a9:af23:56c1 with SMTP id wh11csp1902911pzb; Mon, 15 Apr 2024 08:09:24 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCX8xMh2ouIbarUBCrvWfZm649v1dyzrJZnow/OO6B8BvdUuzTA1cXKtxv8U6PrTE7uE53cDlcGAq5ESAXixQml2iFWZO6jt3BpsVw== X-Google-Smtp-Source: AGHT+IHxu3nGRFmUXKqi7bkoR+UtkBjVVuvRpXgxUpSYCxuQb3/9G8PCr6Lha852/Il8scjMryBj X-Received: by 2002:ac2:5a0f:0:b0:518:d259:8542 with SMTP id q15-20020ac25a0f000000b00518d2598542mr2699300lfn.2.1713193764031; Mon, 15 Apr 2024 08:09:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1713193764; cv=none; d=google.com; s=arc-20160816; b=pXrx3s2SE55aEaL5q/U5IKjRzZWHe6RYu/27Fd+6qRayOKNXiaQ/BIq0LJaP/Ip5lU QJ4kqGq7UucFJhcxP6yFshnR+BUAwoFYzZNBYHklcEU9AwOQINSvozw0C+9H0mfcPPf5 5lFw2cn2mbLf2Jz60MzO+fpiCM4flHyckJheyO7s9mU4faALPMM83U90c1ZXVvhZCXcS O4NlvHh8SOGy+zO+pA1QCY8ovN9Kyy/mUoBBGPAqI18uHBQM30frrVx+Zr7JJpSjfnql Mqy5mBAN9ryiKWaoucpjhVRzZWoP9f67lldfyREDC+FxWfiewgNp5uv4iNPgQbl49QsT 5uSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=wjj79gnJSkhRevM3i5lR8fyczflZvCdkrlRdJRREilk=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=BnLubJ8xuUB+t6EQMUmX4rP0gkQmswSADijgjDrCzuFmgjBBewUPt6GQg4KDo8r8iu T5XPMPrSXsostA4JlEqYxFjx9aRM7pCkq810J5+9hNpkye0w2r3rU27gU9IWx1dLitlT gvEfssJAlF/nFgx4tzuVt0J+d7BEWobonJcfF6QdnKGW+zA0NZ7i4jR1WmbpAEmEsIkj AssesVPrpoSgKYGd/Ve2ot8pk6FhZ1We2atuqazsyYdMb5B6YnQ2FGtKdKmAsxxaAwUD 9ff7WXkbLD8hcUT58XpPFSziML0zB5ZXWvN7xenIqfgGafjv+e5ATjnVQzOzBHZB9EA2 E75g==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=m5bc5WX1; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id m21-20020a509995000000b0056e249c1609si4725791edb.235.2024.04.15.08.09.23; Mon, 15 Apr 2024 08:09:23 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20230601 header.b=m5bc5WX1; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 20BF968C942; Mon, 15 Apr 2024 18:09:20 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pl1-f179.google.com (mail-pl1-f179.google.com [209.85.214.179]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 27EC768C545 for ; Mon, 15 Apr 2024 18:09:14 +0300 (EEST) Received: by mail-pl1-f179.google.com with SMTP id d9443c01a7336-1e51398cc4eso32067805ad.2 for ; Mon, 15 Apr 2024 08:09:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1713193751; x=1713798551; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=SjdTWgWa/sKIiwri0xW70yvPnpeI2CcaPYaUKGKt+yc=; b=m5bc5WX1F76JHhemK2tHkTrKsQco7xSQR7lCupxjtF6XmNUHRSjPVgQ6rYIg/hXQwC Ofcmf4VGwPrzXcP2pzTmntnFttwZw7t+NvuaGUjxRGIkqwmt5uBUq9lJCVuaffU32y6a QrDMRHKcfM7d87Jh1FqNRQEACBHm648Up+iePeKC5l8tJ1MkDJ3st2/gRVRGizHOjZr2 tC/SO5gbct3cjg+d90EwoxVgn1OT7N/hDd5Q0LryEuMsq7ZvYzbCeLyN/o0ovgj3Y5nw qflGOtH6m2qhIlkkDI4cSLnOYpFSpQazK7wa+fhDYCRnEQBYbbScGthx/pKOqd6UGB0o QT6w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713193751; x=1713798551; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SjdTWgWa/sKIiwri0xW70yvPnpeI2CcaPYaUKGKt+yc=; b=eCJ9qfHPrslq1/v91NGxWFfnClzbgDNMnxH0Ua4JKx2QMCmpIOl1850PROL5FKW6TW /cphbANFF1AZuEnKe6xo2Ht1T+Zr3heaeJOYM6zhiO5i1nKuQ+oUbmrtHbk9+cIItnWf dPbUWAMCjQEqSHbpPbnpqRy/nPS2ex3SSmEoCY4wP6fWfycGNINGzKjJFIs/zWub3SXr UHa3QBh55L70nI0r6FujryuPWi7wJcMnqprnFbxGMbm/lRZMlpxURu75v7cErmdJLmO1 7MaIfF/9SWRXaMNyKBQTVFkrcLmTkEgQnhD5IIUEOCqmYHXkCOepIE7lVMngLL4oLsYi rOcQ== X-Gm-Message-State: AOJu0YwZeVgtfagaqQ/PGa241d2qrA0bqymk2bhBC4d1GS2DPJJF0o6i BFGfBtqaAIp2/jngf2dl3AUdsrATMtOi9Zcp1EIx5EwswIuJRh7I2R1PPw== X-Received: by 2002:a17:902:ce88:b0:1dc:7bc:d025 with SMTP id f8-20020a170902ce8800b001dc07bcd025mr14610864plg.4.1713193751127; Mon, 15 Apr 2024 08:09:11 -0700 (PDT) Received: from localhost.localdomain ([190.194.167.233]) by smtp.gmail.com with ESMTPSA id i5-20020a17090332c500b001e546a10c50sm8020123plr.286.2024.04.15.08.09.09 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Apr 2024 08:09:10 -0700 (PDT) From: James Almer To: ffmpeg-devel@ffmpeg.org Date: Mon, 15 Apr 2024 12:09:00 -0300 Message-ID: <20240415150900.1800-1-jamrial@gmail.com> X-Mailer: git-send-email 2.44.0 In-Reply-To: <20240412231658.4625-1-jamrial@gmail.com> References: <20240412231658.4625-1-jamrial@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 4/4] fftools/ffmpeg_mux_init: allow mapping a stream group from one of the inputs X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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" X-TUID: +tnUreotEs3X Do this by extending the -stream_group option to accept a map key where the value selects the input file and stream group. The can and should set the streams in the output that the copied group will reference, same as they'd do if they created a group from scratch. Example command line: ffmpeg -i input.iamf -map 0 -c:a copy -f null -stream_group \ map=0=0:st=0:st=1:st=2:st=3 -stream_group map=0=1:st=0:st=1:st=2:st=3 Signed-off-by: James Almer --- fftools/ffmpeg_mux_init.c | 154 +++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 6d8bd5bcdf..a46b0628d8 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -2232,11 +2232,137 @@ fail: return ret; } +static int of_serialize_options(Muxer *mux, void *obj, AVBPrint *bp) +{ + char *ptr; + int ret; + + ret = av_opt_serialize(obj, 0, AV_OPT_SERIALIZE_SKIP_DEFAULTS | AV_OPT_SERIALIZE_SEARCH_CHILDREN, + &ptr, '=', ':'); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Failed to serialize group\n"); + return ret; + } + + av_bprintf(bp, "%s", ptr); + ret = strlen(ptr); + av_free(ptr); + + return ret; +} + +#define SERIALIZE(parent, child) do { \ + ret = of_serialize_options(mux, parent->child, bp); \ + if (ret < 0) \ + return ret; \ +} while (0) + +#define SERIALIZE_LOOP(parent, child, suffix, separator) do { \ + for (int j = 0; j < parent->nb_## child ## suffix; j++) { \ + av_bprintf(bp, separator#child "="); \ + SERIALIZE(parent, child ## suffix[j]); \ + } \ +} while (0) + +static int64_t get_stream_group_index_from_id(Muxer *mux, int64_t id) +{ + AVFormatContext *oc = mux->fc; + + for (unsigned i = 0; i < oc->nb_stream_groups; i++) + if (oc->stream_groups[i]->id == id) + return oc->stream_groups[i]->index; + + return AVERROR(EINVAL); +} + +static int of_map_group(Muxer *mux, AVDictionary **dict, AVBPrint *bp, const char *map) +{ + AVStreamGroup *stg; + int ret, file_idx, stream_idx; + char *ptr; + + file_idx = strtol(map, &ptr, 0); + if (file_idx >= nb_input_files || file_idx < 0 || map == ptr) { + av_log(mux, AV_LOG_ERROR, "Invalid input file index: %d.\n", file_idx); + return AVERROR(EINVAL); + } + + stream_idx = strtol(*ptr == '=' ? ptr + 1 : ptr, &ptr, 0); + if (*ptr || stream_idx >= input_files[file_idx]->ctx->nb_stream_groups || stream_idx < 0) { + av_log(mux, AV_LOG_ERROR, "Invalid input stream group index: %d.\n", stream_idx); + return AVERROR(EINVAL); + } + + stg = input_files[file_idx]->ctx->stream_groups[stream_idx]; + ret = of_serialize_options(mux, stg, bp); + if (ret < 0) + return ret; + + ret = av_dict_parse_string(dict, bp->str, "=", ":", 0); + if (ret < 0) + av_log(mux, AV_LOG_ERROR, "Error parsing mapped group specification %s\n", ptr); + av_dict_set_int(dict, "type", stg->type, 0); + + av_log(mux, AV_LOG_VERBOSE, "stg %s\n", bp->str); + av_bprint_clear(bp); + switch(stg->type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: { + AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element; + + if (audio_element->demixing_info) { + av_bprintf(bp, ",demixing="); + SERIALIZE(audio_element, demixing_info); + } + if (audio_element->recon_gain_info) { + av_bprintf(bp, ",recon_gain="); + SERIALIZE(audio_element, recon_gain_info); + } + SERIALIZE_LOOP(audio_element, layer, s, ","); + break; + } + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: { + AVIAMFMixPresentation *mix = stg->params.iamf_mix_presentation; + + for (int i = 0; i < mix->nb_submixes; i++) { + AVIAMFSubmix *submix = mix->submixes[i]; + + av_bprintf(bp, ",submix="); + SERIALIZE(mix, submixes[i]); + for (int j = 0; j < submix->nb_elements; j++) { + AVIAMFSubmixElement *element = submix->elements[j]; + int64_t id = get_stream_group_index_from_id(mux, element->audio_element_id); + + if (id < 0) { + av_log(mux, AV_LOG_ERROR, "Invalid or missing stream group index in" + "submix element"); + return id; + } + + av_bprintf(bp, "|element="); + SERIALIZE(submix, elements[j]); + if (ret) + av_bprintf(bp, ":"); + av_bprintf(bp, "stg=%"PRId64, id); + } + SERIALIZE_LOOP(submix, layout, s, "|"); + } + break; + } + default: + av_log(mux, AV_LOG_ERROR, "Unsupported mapped group type %d.\n", stg->type); + ret = AVERROR(EINVAL); + break; + } + av_log(mux, AV_LOG_VERBOSE, "extra %s\n", bp->str); + return 0; +} + static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) { AVFormatContext *oc = mux->fc; AVStreamGroup *stg; AVDictionary *dict = NULL, *tmp = NULL; + char *mapped_string = NULL; const AVDictionaryEntry *e; const AVOption opts[] = { { "type", "Set group type", offsetof(AVStreamGroup, type), AV_OPT_TYPE_INT, @@ -2262,8 +2388,31 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) return ret; } + av_dict_copy(&tmp, dict, 0); + e = av_dict_get(dict, "map", NULL, 0); + if (e) { + AVBPrint bp; + + if (ptr) { + av_log(mux, AV_LOG_ERROR, "Unexpected extra parameters when mapping a" + " stream group\n"); + ret = AVERROR(EINVAL); + goto end; + } + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); + ret = of_map_group(mux, &tmp, &bp, e->value); + if (ret < 0) { + av_bprint_finalize(&bp, NULL); + goto end; + } + + av_bprint_finalize(&bp, &mapped_string); + ptr = mapped_string; + } + // "type" is not a user settable AVOption in AVStreamGroup, so handle it here - e = av_dict_get(dict, "type", NULL, 0); + e = av_dict_get(tmp, "type", NULL, 0); if (!e) { av_log(mux, AV_LOG_ERROR, "No type specified for Stream Group in \"%s\"\n", token); ret = AVERROR(EINVAL); @@ -2278,7 +2427,6 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) goto end; } - av_dict_copy(&tmp, dict, 0); stg = avformat_stream_group_create(oc, type, &tmp); if (!stg) { ret = AVERROR(ENOMEM); @@ -2331,6 +2479,7 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) // make sure that nothing but "st" and "stg" entries are left in the dict e = NULL; + av_dict_set(&tmp, "map", NULL, 0); av_dict_set(&tmp, "type", NULL, 0); while (e = av_dict_iterate(tmp, e)) { if (!strcmp(e->key, "st") || !strcmp(e->key, "stg")) @@ -2343,6 +2492,7 @@ static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) ret = 0; end: + av_free(mapped_string); av_dict_free(&dict); av_dict_free(&tmp);