From patchwork Tue Aug 27 15:05:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 51165 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a59:c944:0:b0:48e:c0f8:d0de with SMTP id k4csp350490vqt; Tue, 27 Aug 2024 09:00:16 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCVpS9w3QI8YDFyY8Y42WwBiciKjv3feClxNYw7GBqfwflCuO196bCA3JZG3mtzfcM6lcdCHzavS7Bz7sBYmA2ly@gmail.com X-Google-Smtp-Source: AGHT+IGVsepQG3Ajd2zO3h3pzWvc/SCDe7m/x5uq8uhfV7SLE8tx/xrCCp3WjrcV7nTezSu1Zqhs X-Received: by 2002:a05:6402:5216:b0:5be:ee5a:b3b8 with SMTP id 4fb4d7f45d1cf-5c0891aa4c0mr5937787a12.3.1724774416543; Tue, 27 Aug 2024 09:00:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1724774416; cv=none; d=google.com; s=arc-20240605; b=SoJuTncXVvQDzOslbl4TH6ni6G0k5tkoUcbAXD0zu5JnhAQP9x/hv5torZ7UXhVoJa RNLeMRhCMY7l9YXTWr+0uMbvJEDoTmKhU9kcdxpTCoFsbgHLUW082FUPQ6c70x48+xep lRu7+R1FwKyjtyDk3zqV1Rq/UXz0YWO46NuS0Go1ZG3JMPajHdLeyuGkhsyt9iRH4Lnb meqnzoo11nYBXvHZezhCCWYx1GaEpW0XXSWLVIeLXp5HcsmAFvyxXbNg/9ZT9gI58WiB pjJX+gNqIwGA/1YjO/Z9QiIiJifsb4jDO0A9tDS42I46sIRUhtbGd1G0rRYOS1P3vUrH PHbA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; 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=2IG/yTZv4iOe5ewd1uW+jorypwz4tru2aBZqT/tJchk=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=H3SrPJ5A1BwQ/2IMI8xbw7SYsUUS406R6z+oEINBfts5A/MntHFdf01cIUKkUGw/0c Y0pCv6DJ7qu6d9FGn/SeJByDziVEA3lBp5pE4I9jG33RZevZebxj2qv9O4RwB8oFF+Ip TbdPsgU52r7gk3ZMToYbEx6B9Zao5tbtFrgdwSAt1QgUzU/AxdRu6a0i6318oFrl6zBd 2AJ/oSFlpeyGbRtJnfwLFqAtx7ndNFTjn/FRGlKGhe7INa7vKK7X6kGDEPqMD13nnQ1k PT9J5aap1QD4VPSTaQYITYwQDqn1YLiE1aCKmM3SmcGB2+emwVOfJ6MhxjIZFFxebxU7 JCeA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=Qa0vKRhz; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 4fb4d7f45d1cf-5c0bb4c0cbdsi1551681a12.646.2024.08.27.09.00.14; Tue, 27 Aug 2024 09:00:16 -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=@khirnov.net header.s=mail header.b=Qa0vKRhz; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7302C68DE1B; Tue, 27 Aug 2024 18:50:41 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id A764268DD77 for ; Tue, 27 Aug 2024 18:50:33 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=Qa0vKRhz; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id 4E7B94E02 for ; Tue, 27 Aug 2024 17:50:33 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id wLrjFkZ0jAEu for ; Tue, 27 Aug 2024 17:50:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1724773832; bh=ri98cZNdDJm25znApRQmjZKGeXWr5vPV7wJMjpZTqHA=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Qa0vKRhzgBlgT94p1DgVqW4WcrujB+NCahJP4zytkHX5XnwFh44EjgtnX+sY/4BS9 E2amXKrXM5msiVnXMVHOjYZLWVR/HzGEGcAz6/B1xuf2sKRvhLT2cadCzJMdmNBvil HQn2kasHGF0shijB260sp84p1KqdKMNX3mAG3nIOIs9QdUxFkho8hKyK3T7wAiKtUz O+dk/rsQ/mZi81bsZi9mCfjqFD7G9LsoSw965hfY590GxCf959IWMspJKMj8mHPyCm EBoIaEos7TMXau4Y3g02JmI8Fza5tZk+Z3gERd75EFc/iolTKkD0/NhgcH4Rf5p4OC xeeRvTQWqznMQ== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id 864114DEE for ; Tue, 27 Aug 2024 17:50:32 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 6D2F83A3589 for ; Tue, 27 Aug 2024 17:42:43 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Tue, 27 Aug 2024 17:05:17 +0200 Message-ID: <20240827154041.13846-39-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240827154041.13846-2-anton@khirnov.net> References: <20240827154041.13846-2-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 37/42] fftools/ffmpeg_sched: allow decoders to have multiple outputs 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: RDyzldPEj+Z/ Will be useful for multilayer video. --- fftools/ffmpeg_dec.c | 10 ++-- fftools/ffmpeg_demux.c | 2 +- fftools/ffmpeg_filter.c | 4 +- fftools/ffmpeg_mux_init.c | 2 +- fftools/ffmpeg_sched.c | 102 ++++++++++++++++++++++++++++---------- fftools/ffmpeg_sched.h | 21 ++++++-- 6 files changed, 100 insertions(+), 41 deletions(-) diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c index c2bcf784b0..54f7223f0f 100644 --- a/fftools/ffmpeg_dec.c +++ b/fftools/ffmpeg_dec.c @@ -578,7 +578,7 @@ static int process_subtitle(DecoderPriv *dp, AVFrame *frame) if (!subtitle) return 0; - ret = sch_dec_send(dp->sch, dp->sch_idx, frame); + ret = sch_dec_send(dp->sch, dp->sch_idx, 0, frame); if (ret < 0) av_frame_unref(frame); @@ -620,7 +620,7 @@ static int transcode_subtitles(DecoderPriv *dp, const AVPacket *pkt, frame->time_base = pkt->time_base; frame->opaque = (void*)(intptr_t)FRAME_OPAQUE_SUB_HEARTBEAT; - ret = sch_dec_send(dp->sch, dp->sch_idx, frame); + ret = sch_dec_send(dp->sch, dp->sch_idx, 0, frame); return ret == AVERROR_EOF ? AVERROR_EXIT : ret; } else if (pkt && (intptr_t)pkt->opaque == PKT_OPAQUE_FIX_SUB_DURATION) { return fix_sub_duration_heartbeat(dp, av_rescale_q(pkt->pts, pkt->time_base, @@ -773,7 +773,7 @@ static int packet_decode(DecoderPriv *dp, AVPacket *pkt, AVFrame *frame) dp->dec.frames_decoded++; - ret = sch_dec_send(dp->sch, dp->sch_idx, frame); + ret = sch_dec_send(dp->sch, dp->sch_idx, 0, frame); if (ret < 0) { av_frame_unref(frame); return ret == AVERROR_EOF ? AVERROR_EXIT : ret; @@ -951,7 +951,7 @@ static int decoder_thread(void *arg) dp->last_frame_pts + dp->last_frame_duration_est; dt.frame->time_base = dp->last_frame_tb; - ret = sch_dec_send(dp->sch, dp->sch_idx, dt.frame); + ret = sch_dec_send(dp->sch, dp->sch_idx, 0, dt.frame); if (ret < 0 && ret != AVERROR_EOF) { av_log(dp, AV_LOG_FATAL, "Error signalling EOF timestamp: %s\n", av_err2str(ret)); @@ -1355,7 +1355,7 @@ int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch) return ret; enc_idx = ret; - ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC(dp->sch_idx)); + ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC_IN(dp->sch_idx)); if (ret < 0) return ret; diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 039ee0c785..476efff127 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -954,7 +954,7 @@ static int ist_use(InputStream *ist, int decoding_needed) ds->sch_idx_dec = ret; ret = sch_connect(d->sch, SCH_DSTREAM(d->f.index, ds->sch_idx_stream), - SCH_DEC(ds->sch_idx_dec)); + SCH_DEC_IN(ds->sch_idx_dec)); if (ret < 0) return ret; diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index b562e8417c..fb2b1a5b32 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -703,7 +703,7 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) if (dec_idx < 0) return dec_idx; - ret = sch_connect(fgp->sch, SCH_DEC(dec_idx), + ret = sch_connect(fgp->sch, SCH_DEC_OUT(dec_idx, 0), SCH_FILTER_IN(fgp->sch_idx, ifp->index)); if (ret < 0) return ret; @@ -749,7 +749,7 @@ static int ifilter_bind_dec(InputFilterPriv *ifp, Decoder *dec) if (dec_idx < 0) return dec_idx; - ret = sch_connect(fgp->sch, SCH_DEC(dec_idx), + ret = sch_connect(fgp->sch, SCH_DEC_OUT(dec_idx, 0), SCH_FILTER_IN(fgp->sch_idx, ifp->index)); if (ret < 0) return ret; diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index e84fa9719f..5ee2a9685b 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -1458,7 +1458,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ms->sch_idx_src = sched_idx; if (ost->enc) { - ret = sch_connect(mux->sch, SCH_DEC(sched_idx), + ret = sch_connect(mux->sch, SCH_DEC_OUT(sched_idx, 0), SCH_ENC(ms->sch_idx_enc)); if (ret < 0) goto fail; diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c index cff824340b..ef0b6e2897 100644 --- a/fftools/ffmpeg_sched.c +++ b/fftools/ffmpeg_sched.c @@ -71,13 +71,19 @@ typedef struct SchTask { int thread_running; } SchTask; +typedef struct SchDecOutput { + SchedulerNode *dst; + uint8_t *dst_finished; + unsigned nb_dst; +} SchDecOutput; + typedef struct SchDec { const AVClass *class; SchedulerNode src; - SchedulerNode *dst; - uint8_t *dst_finished; - unsigned nb_dst; + + SchDecOutput *outputs; + unsigned nb_outputs; SchTask task; // Queue for receiving input packets, one stream. @@ -513,8 +519,14 @@ void sch_free(Scheduler **psch) av_thread_message_queue_free(&dec->queue_end_ts); - av_freep(&dec->dst); - av_freep(&dec->dst_finished); + for (unsigned j = 0; j < dec->nb_outputs; j++) { + SchDecOutput *o = &dec->outputs[j]; + + av_freep(&o->dst); + av_freep(&o->dst_finished); + } + + av_freep(&dec->outputs); av_frame_free(&dec->send_frame); } @@ -712,14 +724,28 @@ int sch_add_demux_stream(Scheduler *sch, unsigned demux_idx) return ret < 0 ? ret : d->nb_streams - 1; } +int sch_add_dec_output(Scheduler *sch, unsigned dec_idx) +{ + SchDec *dec; + int ret; + + av_assert0(dec_idx < sch->nb_dec); + dec = &sch->dec[dec_idx]; + + ret = GROW_ARRAY(dec->outputs, dec->nb_outputs); + if (ret < 0) + return ret; + + return dec->nb_outputs - 1; +} + static const AVClass sch_dec_class = { .class_name = "SchDec", .version = LIBAVUTIL_VERSION_INT, .parent_log_context_offset = offsetof(SchDec, task.func_arg), }; -int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, - int send_end_ts) +int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, int send_end_ts) { const unsigned idx = sch->nb_dec; @@ -739,6 +765,10 @@ int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, if (!dec->send_frame) return AVERROR(ENOMEM); + ret = sch_add_dec_output(sch, idx); + if (ret < 0) + return ret; + ret = queue_alloc(&dec->queue, 1, 0, QUEUE_PACKETS); if (ret < 0) return ret; @@ -943,15 +973,19 @@ int sch_connect(Scheduler *sch, SchedulerNode src, SchedulerNode dst) } case SCH_NODE_TYPE_DEC: { SchDec *dec; + SchDecOutput *o; av_assert0(src.idx < sch->nb_dec); dec = &sch->dec[src.idx]; - ret = GROW_ARRAY(dec->dst, dec->nb_dst); + av_assert0(src.idx_stream < dec->nb_outputs); + o = &dec->outputs[src.idx_stream]; + + ret = GROW_ARRAY(o->dst, o->nb_dst); if (ret < 0) return ret; - dec->dst[dec->nb_dst - 1] = dst; + o->dst[o->nb_dst - 1] = dst; // decoded frames go to filters or encoding switch (dst.type) { @@ -1417,15 +1451,20 @@ static int start_prepare(Scheduler *sch) "Decoder not connected to a source\n"); return AVERROR(EINVAL); } - if (!dec->nb_dst) { - av_log(dec, AV_LOG_ERROR, - "Decoder not connected to any sink\n"); - return AVERROR(EINVAL); - } - dec->dst_finished = av_calloc(dec->nb_dst, sizeof(*dec->dst_finished)); - if (!dec->dst_finished) - return AVERROR(ENOMEM); + for (unsigned j = 0; j < dec->nb_outputs; j++) { + SchDecOutput *o = &dec->outputs[j]; + + if (!o->nb_dst) { + av_log(dec, AV_LOG_ERROR, + "Decoder output %u not connected to any sink\n", j); + return AVERROR(EINVAL); + } + + o->dst_finished = av_calloc(o->nb_dst, sizeof(*o->dst_finished)); + if (!o->dst_finished) + return AVERROR(ENOMEM); + } } for (unsigned i = 0; i < sch->nb_enc; i++) { @@ -2171,21 +2210,26 @@ finish: return AVERROR_EOF; } -int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame) +int sch_dec_send(Scheduler *sch, unsigned dec_idx, + unsigned out_idx, AVFrame *frame) { SchDec *dec; + SchDecOutput *o; int ret; unsigned nb_done = 0; av_assert0(dec_idx < sch->nb_dec); dec = &sch->dec[dec_idx]; - for (unsigned i = 0; i < dec->nb_dst; i++) { - uint8_t *finished = &dec->dst_finished[i]; + av_assert0(out_idx < dec->nb_outputs); + o = &dec->outputs[out_idx]; + + for (unsigned i = 0; i < o->nb_dst; i++) { + uint8_t *finished = &o->dst_finished[i]; AVFrame *to_send = frame; // sending a frame consumes it, so make a temporary reference if needed - if (i < dec->nb_dst - 1) { + if (i < o->nb_dst - 1) { to_send = dec->send_frame; // frame may sometimes contain props only, @@ -2196,7 +2240,7 @@ int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame) return ret; } - ret = dec_send_to_dst(sch, dec->dst[i], finished, to_send); + ret = dec_send_to_dst(sch, o->dst[i], finished, to_send); if (ret < 0) { av_frame_unref(to_send); if (ret == AVERROR_EOF) { @@ -2207,7 +2251,7 @@ int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame) } } - return (nb_done == dec->nb_dst) ? AVERROR_EOF : 0; + return (nb_done == o->nb_dst) ? AVERROR_EOF : 0; } static int dec_done(Scheduler *sch, unsigned dec_idx) @@ -2222,10 +2266,14 @@ static int dec_done(Scheduler *sch, unsigned dec_idx) if (dec->queue_end_ts) av_thread_message_queue_set_err_recv(dec->queue_end_ts, AVERROR_EOF); - for (unsigned i = 0; i < dec->nb_dst; i++) { - int err = dec_send_to_dst(sch, dec->dst[i], &dec->dst_finished[i], NULL); - if (err < 0 && err != AVERROR_EOF) - ret = err_merge(ret, err); + for (unsigned i = 0; i < dec->nb_outputs; i++) { + SchDecOutput *o = &dec->outputs[i]; + + for (unsigned j = 0; j < o->nb_dst; j++) { + int err = dec_send_to_dst(sch, o->dst[j], &o->dst_finished[j], NULL); + if (err < 0 && err != AVERROR_EOF) + ret = err_merge(ret, err); + } } return ret; diff --git a/fftools/ffmpeg_sched.h b/fftools/ffmpeg_sched.h index 7cd839016c..3062c4a6ec 100644 --- a/fftools/ffmpeg_sched.h +++ b/fftools/ffmpeg_sched.h @@ -114,9 +114,12 @@ typedef int (*SchThreadFunc)(void *arg); #define SCH_MSTREAM(file, stream) \ (SchedulerNode){ .type = SCH_NODE_TYPE_MUX, \ .idx = file, .idx_stream = stream } -#define SCH_DEC(decoder) \ +#define SCH_DEC_IN(decoder) \ (SchedulerNode){ .type = SCH_NODE_TYPE_DEC, \ - .idx = decoder } + .idx = decoder } +#define SCH_DEC_OUT(decoder, out_idx) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_DEC, \ + .idx = decoder, .idx_stream = out_idx } #define SCH_ENC(encoder) \ (SchedulerNode){ .type = SCH_NODE_TYPE_ENC, \ .idx = encoder } @@ -178,8 +181,15 @@ int sch_add_demux_stream(Scheduler *sch, unsigned demux_idx); * @retval ">=0" Index of the newly-created decoder. * @retval "<0" Error code. */ -int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, - int send_end_ts); +int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, int send_end_ts); + +/** + * Add another output to decoder (e.g. for multiview video). + * + * @retval ">=0" Index of the newly-added decoder output. + * @retval "<0" Error code. + */ +int sch_add_dec_output(Scheduler *sch, unsigned dec_idx); /** * Add a filtergraph to the scheduler. @@ -379,7 +389,8 @@ int sch_dec_receive(Scheduler *sch, unsigned dec_idx, struct AVPacket *pkt); * @retval AVERROR_EOF all consumers are done, should terminate decoding * @retval "another negative error code" other failure */ -int sch_dec_send(Scheduler *sch, unsigned dec_idx, struct AVFrame *frame); +int sch_dec_send(Scheduler *sch, unsigned dec_idx, + unsigned out_idx, struct AVFrame *frame); /** * Called by filtergraph tasks to obtain frames for filtering. Will wait for a