From patchwork Mon Dec 12 21:42:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timo Rothenpieler X-Patchwork-Id: 39686 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:bc95:b0:ad:ade2:bfd2 with SMTP id fx21csp633169pzb; Mon, 12 Dec 2022 13:42:38 -0800 (PST) X-Google-Smtp-Source: AA0mqf6obuKkiQpKqPEHey6ufXKqJDn+X9WATPtB8qLsZoLyNufbdWez4WzAzac3l5Qw5qExtXfM X-Received: by 2002:a17:906:b159:b0:78d:f454:ba46 with SMTP id bt25-20020a170906b15900b0078df454ba46mr15001067ejb.69.1670881357831; Mon, 12 Dec 2022 13:42:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1670881357; cv=none; d=google.com; s=arc-20160816; b=jyQSgXkZ9ulkB7gtREMXYQNyXhVeG8l445UghgPM9k8p/MEmwp6rGbhVaeMrwaQjYQ GIHhfZNt0+BplmKUhBY21GESR/NEVBZzotpJEauTs1ikRBu1+24BhHIcvLHrO237s+cI E1q/inULFdw7aiuwcwF8bVsalg/XPR8+vSvzqp5xiRoF1fAijCa/MqQPiIAsNgv5i6nA R2hp9FLo0Uch04S6kZRRCt9IVqk//xRF2B4YdzXpeF6349PAbAfga8e2Ns+nsEzNjx6Y QZY0JiAo4Yp1haXayZid4oYVxQtxp4U8OWO1fzl3ziJviimn6XeghXk7heB/CV6xW8bR wBYw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc: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=p/kVGgKxQzIRU3yUz5+8fI5YaCDaw6x8coweTgxV/3Y=; b=R0p75QJ4vT7Z4qrreYBoJrrfROZGU1DLRNzAgiczbYJjZP9wxtvoW6t0k5VVgGhKRq nczr1Ywx0qr9mUYb/vZ8F0oT36I5CPbbTNTqrgk8fKR+xdBSTpEnlHWihxSTjcCaqDXc +7Oyh+7yarJulR1c5RVOuR95qR/eNeKh2ZtmK5Ia8Q5oCPRsizDFYAX84T7y8BXR7r46 fF/Nar04mFAgo6nl6BXPA6JJLHMeHNs1BHL7P/iovSryqJZrFPq7l+xf96Y1KtoK+TS4 JWaVvD2TqIBMTRjAiGm/NYyFAcJsViq5IjVgry9RTaRmFzUYeWEE4jChdrBHGDZwttBv 6tcA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@rothenpieler.org header.s=mail header.b=Bn+tnSug; 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=NONE dis=NONE) header.from=rothenpieler.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id sg16-20020a170907a41000b007c17da17783si1743989ejc.198.2022.12.12.13.42.36; Mon, 12 Dec 2022 13:42:37 -0800 (PST) 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=@rothenpieler.org header.s=mail header.b=Bn+tnSug; 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=NONE dis=NONE) header.from=rothenpieler.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id D94CC68BDC0; Mon, 12 Dec 2022 23:42:32 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from btbn.de (btbn.de [136.243.74.85]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 3067668BDC0 for ; Mon, 12 Dec 2022 23:42:26 +0200 (EET) Received: from [authenticated] by btbn.de (Postfix) with ESMTPSA id 80E822EC928; Mon, 12 Dec 2022 22:42:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rothenpieler.org; s=mail; t=1670881343; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FNEQkS+gT6yKQ5b9pF1/da0xFKmwvAZer5KjbK5b9k8=; b=Bn+tnSugSCg4stbcvNGxjqfwiJqcjYkSmwGUHfbKr9758CruqNtxyqlAoxWZvEKcOMvO9k OVJNBIsEcCfcr2imFA44IklDGjeikVf8qwWwDekaCtv6Bb8NuGNspu7k+bv5NGwteE1DWX ZMB/ioAvQVqCaAJEgk+LGodret5qy1SFJi8I7aOD2XhxqEsEQxzX6eu1Dl4WgqaNJxgA33 I2Z+/BW7FFM53fyt8JnnTU1EN6Ja3bZag1RrTk6sY7OLuwMV5wbFEaOijIUH72keV/rAZZ 6XCZTl8MUJHous3hBrY8VyJvT96wu0jWgzuyu/OZexFg/iJee+IiFlkukpJ1dw== From: Timo Rothenpieler To: ffmpeg-devel@ffmpeg.org Date: Mon, 12 Dec 2022 22:42:09 +0100 Message-Id: <20221212214210.2628-1-timo@rothenpieler.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221209234636.GH3806951@pb2> References: <20221209234636.GH3806951@pb2> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v5 1/2] lavc: convert frame threading to the receive_frame() pattern 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 Cc: Anton Khirnov Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: k6wz7HbmSMb/ From: Anton Khirnov Reorganize the code such that the frame threading code does not call the decoders directly, but instead calls back into the generic decoding code. This avoids duplicating the logic that wraps the decoder invocation and will be useful in the following commits. --- libavcodec/decode.c | 62 ++++++--- libavcodec/decode.h | 7 + libavcodec/internal.h | 7 + libavcodec/pthread_frame.c | 265 ++++++++++++++++++++++++------------- libavcodec/thread.h | 18 +-- 5 files changed, 236 insertions(+), 123 deletions(-) diff --git a/libavcodec/decode.c b/libavcodec/decode.c index b184c3f55b..672bf472dd 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -180,6 +180,11 @@ fail: return ret; } +#if !HAVE_THREADS +#define ff_thread_get_packet(avctx, pkt) (AVERROR_BUG) +#define ff_thread_receive_frame(avctx, frame) (AVERROR_BUG) +#endif + int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) { AVCodecInternal *avci = avctx->internal; @@ -188,7 +193,14 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) if (avci->draining) return AVERROR_EOF; - ret = av_bsf_receive_packet(avci->bsf, pkt); + /* If we are a worker thread, get the next packet from the threading + * context. Otherwise we are the main (user-facing) context, so we get the + * next packet from the input filterchain. + */ + if (avctx->internal->is_frame_mt) + ret = ff_thread_get_packet(avctx, pkt); + else + ret = av_bsf_receive_packet(avci->bsf, pkt); if (ret == AVERROR_EOF) avci->draining = 1; if (ret < 0) @@ -273,30 +285,25 @@ static inline int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame, return AVERROR_EOF; if (!pkt->data && - !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY || - avctx->active_thread_type & FF_THREAD_FRAME)) + !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) return AVERROR_EOF; got_frame = 0; - if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) { - ret = ff_thread_decode_frame(avctx, frame, &got_frame, pkt); - } else { - ret = codec->cb.decode(avctx, frame, &got_frame, pkt); - - if (!(codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS)) - frame->pkt_dts = pkt->dts; - if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { - if(!avctx->has_b_frames) - frame->pkt_pos = pkt->pos; - //FIXME these should be under if(!avctx->has_b_frames) - /* get_buffer is supposed to set frame parameters */ - if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) { - if (!frame->sample_aspect_ratio.num) frame->sample_aspect_ratio = avctx->sample_aspect_ratio; - if (!frame->width) frame->width = avctx->width; - if (!frame->height) frame->height = avctx->height; - if (frame->format == AV_PIX_FMT_NONE) frame->format = avctx->pix_fmt; - } + ret = codec->cb.decode(avctx, frame, &got_frame, pkt); + + if (!(codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS)) + frame->pkt_dts = pkt->dts; + if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { + if(!avctx->has_b_frames) + frame->pkt_pos = pkt->pos; + //FIXME these should be under if(!avctx->has_b_frames) + /* get_buffer is supposed to set frame parameters */ + if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) { + if (!frame->sample_aspect_ratio.num) frame->sample_aspect_ratio = avctx->sample_aspect_ratio; + if (!frame->width) frame->width = avctx->width; + if (!frame->height) frame->height = avctx->height; + if (frame->format == AV_PIX_FMT_NONE) frame->format = avctx->pix_fmt; } } emms_c(); @@ -546,7 +553,7 @@ static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame) return 0; } -static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) +int ff_decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) { AVCodecInternal *avci = avctx->internal; const FFCodec *const codec = ffcodec(avctx->codec); @@ -569,6 +576,17 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) return ok; } + return ret; +} + +static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) +{ + int ret; + if (avctx->active_thread_type & FF_THREAD_FRAME) + ret = ff_thread_receive_frame(avctx, frame); + else + ret = ff_decode_receive_frame_internal(avctx, frame); + if (!ret) { frame->best_effort_timestamp = guess_correct_pts(avctx, frame->pts, diff --git a/libavcodec/decode.h b/libavcodec/decode.h index 5d95369b5e..34beb70f97 100644 --- a/libavcodec/decode.h +++ b/libavcodec/decode.h @@ -58,6 +58,13 @@ typedef struct FrameDecodeData { */ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame); +/** + * Do the actual decoding and obtain a decoded frame from the decoder, if + * available. When frame threading is used, this is invoked by the worker + * threads, otherwise by the top layer directly. + */ +int ff_decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame); + /** * Called by decoders to get the next packet for decoding. * diff --git a/libavcodec/internal.h b/libavcodec/internal.h index a283c52e01..c87036efc4 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -56,6 +56,13 @@ typedef struct AVCodecInternal { */ int is_copy; + /** + * This field is set to 1 when frame threading is being used and the parent + * AVCodecContext of this AVCodecInternal is a worker-thread context (i.e. + * one of those actually doing the decoding), 0 otherwise. + */ + int is_frame_mt; + /** * An audio frame with less than required samples has been submitted (and * potentially padded with silence). Reject all subsequent frames. diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 62a0b18a8a..30f6cb95b0 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -46,6 +46,7 @@ #include "libavutil/log.h" #include "libavutil/mem.h" #include "libavutil/opt.h" +#include "libavutil/fifo.h" #include "libavutil/thread.h" enum { @@ -73,6 +74,12 @@ enum { INITIALIZED, ///< Thread has been properly set up }; +typedef struct DecodedFrames { + AVFrame **f; + size_t nb_f; + size_t nb_f_allocated; +} DecodedFrames; + /** * Context used by codec threads and stored in their AVCodecInternal thread_ctx. */ @@ -93,8 +100,10 @@ typedef struct PerThreadContext { AVPacket *avpkt; ///< Input packet (for decoding) or output (for encoding). - AVFrame *frame; ///< Output frame (for decoding) or input (for encoding). - int got_frame; ///< The output of got_picture_ptr from the last avcodec_decode_video() call. + /** + * Decoded frames from a single decode iteration. + */ + DecodedFrames df; int result; ///< The result of the last codec decode/encode() call. atomic_int state; @@ -141,6 +150,14 @@ typedef struct FrameThreadContext { pthread_cond_t async_cond; int async_lock; + DecodedFrames df; + int result; + + /** + * Packet to be submitted to the next thread for decoding. + */ + AVPacket *next_pkt; + int next_decoding; ///< The next context to submit a packet to. int next_finished; ///< The next context to return output from. @@ -190,6 +207,51 @@ static void thread_set_name(PerThreadContext *p) ff_thread_setname(name); } +// get a free frame to decode into +static AVFrame *decoded_frames_get_free(DecodedFrames *df) +{ + if (df->nb_f == df->nb_f_allocated) { + AVFrame **tmp = av_realloc_array(df->f, df->nb_f + 1, + sizeof(*df->f)); + if (!tmp) + return NULL; + df->f = tmp; + + df->f[df->nb_f] = av_frame_alloc(); + if (!df->f[df->nb_f]) + return NULL; + + df->nb_f_allocated++; + } + + av_frame_unref(df->f[df->nb_f]); + return df->f[df->nb_f]; +} + +static void decoded_frames_pop(DecodedFrames *df, AVFrame *dst) +{ + AVFrame *tmp_frame = df->f[0]; + av_frame_move_ref(dst, tmp_frame); + memmove(df->f, df->f + 1, (df->nb_f - 1) * sizeof(*df->f)); + df->f[--df->nb_f] = tmp_frame; +} + +static void decoded_frames_flush(DecodedFrames *df) +{ + for (int i = 0; i < df->nb_f; i++) + av_frame_unref(df->f[i]); + df->nb_f = 0; +} + +static void decoded_frames_free(DecodedFrames *df) +{ + for (int i = 0; i < df->nb_f_allocated; i++) + av_frame_free(&df->f[i]); + av_freep(&df->f); + df->nb_f = 0; + df->nb_f_allocated = 0; +} + /** * Codec worker thread. * @@ -202,6 +264,7 @@ static attribute_align_arg void *frame_worker_thread(void *arg) PerThreadContext *p = arg; AVCodecContext *avctx = p->avctx; const FFCodec *codec = ffcodec(avctx->codec); + int ret; thread_set_name(p); @@ -236,16 +299,31 @@ FF_ENABLE_DEPRECATION_WARNINGS p->hwaccel_serializing = 1; } - av_frame_unref(p->frame); - p->got_frame = 0; - p->result = codec->cb.decode(avctx, p->frame, &p->got_frame, p->avpkt); + ret = 0; + while (ret >= 0) { + AVFrame *frame; + + /* get the frame which will store the output */ + frame = decoded_frames_get_free(&p->df); + if (!frame) { + p->result = AVERROR(ENOMEM); + goto alloc_fail; + } - if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) - ff_thread_release_buffer(avctx, p->frame); + /* do the actual decoding */ + ret = ff_decode_receive_frame_internal(avctx, frame); + if (ret == 0) + p->df.nb_f++; + else if (ret < 0 && frame->buf[0]) + ff_thread_release_buffer(avctx, frame); + + p->result = (ret == AVERROR(EAGAIN)) ? 0 : ret; + } if (atomic_load(&p->state) == STATE_SETTING_UP) ff_thread_finish_setup(avctx); +alloc_fail: if (p->hwaccel_serializing) { /* wipe hwaccel state to avoid stale pointers lying around; * the state was transferred to FrameThreadContext in @@ -441,23 +519,25 @@ static void release_delayed_buffers(PerThreadContext *p) #endif static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, - AVPacket *avpkt) + AVPacket *in_pkt) { FrameThreadContext *fctx = p->parent; PerThreadContext *prev_thread = fctx->prev_thread; - const AVCodec *codec = p->avctx->codec; - int ret; - - if (!avpkt->size && !(codec->capabilities & AV_CODEC_CAP_DELAY)) - return 0; + int err; pthread_mutex_lock(&p->mutex); - ret = update_context_from_user(p->avctx, user_avctx); - if (ret) { + av_packet_unref(p->avpkt); + av_packet_move_ref(p->avpkt, in_pkt); + if (!p->avpkt->size) + p->avctx->internal->draining = 1; + + err = update_context_from_user(p->avctx, user_avctx); + if (err < 0) { pthread_mutex_unlock(&p->mutex); - return ret; + return err; } + atomic_store_explicit(&p->debug_threads, (p->avctx->debug & FF_DEBUG_THREADS) != 0, memory_order_relaxed); @@ -467,7 +547,6 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, #endif if (prev_thread) { - int err; if (atomic_load(&prev_thread->state) == STATE_SETTING_UP) { pthread_mutex_lock(&prev_thread->progress_mutex); while (atomic_load(&prev_thread->state) == STATE_SETTING_UP) @@ -488,14 +567,6 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); - av_packet_unref(p->avpkt); - ret = av_packet_ref(p->avpkt, avpkt); - if (ret < 0) { - pthread_mutex_unlock(&p->mutex); - av_log(p->avctx, AV_LOG_ERROR, "av_packet_ref() failed in submit_packet()\n"); - return ret; - } - atomic_store(&p->state, STATE_SETTING_UP); pthread_cond_signal(&p->input_cond); pthread_mutex_unlock(&p->mutex); @@ -539,57 +610,42 @@ FF_ENABLE_DEPRECATION_WARNINGS #endif fctx->prev_thread = p; - fctx->next_decoding++; + fctx->next_decoding = (fctx->next_decoding + 1) % p->avctx->thread_count; return 0; } -int ff_thread_decode_frame(AVCodecContext *avctx, - AVFrame *picture, int *got_picture_ptr, - AVPacket *avpkt) +int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame) { FrameThreadContext *fctx = avctx->internal->thread_ctx; - int finished = fctx->next_finished; - PerThreadContext *p; - int err; + int ret = 0; /* release the async lock, permitting blocked hwaccel threads to * go forward while we are in this function */ async_unlock(fctx); - /* - * Submit a packet to the next decoding thread. - */ - - p = &fctx->threads[fctx->next_decoding]; - err = submit_packet(p, avctx, avpkt); - if (err) - goto finish; + /* submit packets to threads while there are no buffered results to return */ + while (!fctx->df.nb_f && !fctx->result) { + PerThreadContext *p; - /* - * If we're still receiving the initial packets, don't return a frame. - */ - - if (fctx->next_decoding > (avctx->thread_count-1-(avctx->codec_id == AV_CODEC_ID_FFV1))) - fctx->delaying = 0; + /* get a packet to be submitted to the next thread */ + av_packet_unref(fctx->next_pkt); + ret = ff_decode_get_packet(avctx, fctx->next_pkt); + if (ret < 0 && ret != AVERROR_EOF) + goto finish; - if (fctx->delaying) { - *got_picture_ptr=0; - if (avpkt->size) { - err = avpkt->size; + ret = submit_packet(&fctx->threads[fctx->next_decoding], avctx, + fctx->next_pkt); + if (ret < 0) goto finish; - } - } - /* - * Return the next available frame from the oldest thread. - * If we're at the end of the stream, then we have to skip threads that - * didn't output a frame/error, because we don't want to accidentally signal - * EOF (avpkt->size == 0 && *got_picture_ptr == 0 && err >= 0). - */ + /* do not return any frames until all threads have something to do */ + if (fctx->next_decoding != fctx->next_finished && + !avctx->internal->draining) + continue; - do { - p = &fctx->threads[finished++]; + p = &fctx->threads[fctx->next_finished]; + fctx->next_finished = (fctx->next_finished + 1) % avctx->thread_count; if (atomic_load(&p->state) != STATE_INPUT_READY) { pthread_mutex_lock(&p->progress_mutex); @@ -598,35 +654,26 @@ int ff_thread_decode_frame(AVCodecContext *avctx, pthread_mutex_unlock(&p->progress_mutex); } - av_frame_move_ref(picture, p->frame); - *got_picture_ptr = p->got_frame; - picture->pkt_dts = p->avpkt->dts; - err = p->result; - - /* - * A later call with avkpt->size == 0 may loop over all threads, - * including this one, searching for a frame/error to return before being - * stopped by the "finished != fctx->next_finished" condition. - * Make sure we don't mistakenly return the same frame/error again. - */ - p->got_frame = 0; - p->result = 0; - - if (finished >= avctx->thread_count) finished = 0; - } while (!avpkt->size && !*got_picture_ptr && err >= 0 && finished != fctx->next_finished); + fctx->result = p->result; + p->result = 0; - update_context_from_thread(avctx, p->avctx, 1); - - if (fctx->next_decoding >= avctx->thread_count) fctx->next_decoding = 0; + if (p->df.nb_f) + FFSWAP(DecodedFrames, fctx->df, p->df); + } - fctx->next_finished = finished; + /* a thread may return multiple frames AND an error + * we first return all the frames, then the error */ + if (fctx->df.nb_f) { + decoded_frames_pop(&fctx->df, frame); + ret = 0; + } else { + ret = fctx->result; + fctx->result = 0; + } - /* return the size of the consumed packet if no error occurred */ - if (err >= 0) - err = avpkt->size; finish: async_lock(fctx); - return err; + return ret; } void ff_thread_report_progress(ThreadFrame *f, int n, int field) @@ -726,7 +773,6 @@ static void park_frame_worker_threads(FrameThreadContext *fctx, int thread_count pthread_cond_wait(&p->output_cond, &p->progress_mutex); pthread_mutex_unlock(&p->progress_mutex); } - p->got_frame = 0; } async_lock(fctx); @@ -784,11 +830,12 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) av_buffer_unref(&ctx->internal->pool); av_packet_free(&ctx->internal->last_pkt_props); + av_packet_free(&ctx->internal->in_pkt); av_freep(&ctx->internal); av_buffer_unref(&ctx->hw_frames_ctx); } - av_frame_free(&p->frame); + decoded_frames_free(&p->df); ff_pthread_free(p, per_thread_offsets); av_packet_free(&p->avpkt); @@ -796,6 +843,9 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) av_freep(&p->avctx); } + decoded_frames_free(&fctx->df); + av_packet_free(&fctx->next_pkt); + av_freep(&fctx->threads); ff_pthread_free(fctx, thread_ctx_offsets); @@ -854,13 +904,17 @@ static av_cold int init_thread(PerThreadContext *p, int *threads_to_free, if (err < 0) return err; - if (!(p->frame = av_frame_alloc()) || - !(p->avpkt = av_packet_alloc())) + if (!(p->avpkt = av_packet_alloc())) return AVERROR(ENOMEM); + copy->internal->is_frame_mt = 1; if (!first) copy->internal->is_copy = 1; + copy->internal->in_pkt = av_packet_alloc(); + if (!copy->internal->in_pkt) + return AVERROR(ENOMEM); + copy->internal->last_pkt_props = av_packet_alloc(); if (!copy->internal->last_pkt_props) return AVERROR(ENOMEM); @@ -920,6 +974,10 @@ int ff_frame_thread_init(AVCodecContext *avctx) return err; } + fctx->next_pkt = av_packet_alloc(); + if (!fctx->next_pkt) + return AVERROR(ENOMEM); + fctx->async_lock = 1; fctx->delaying = 1; @@ -964,17 +1022,28 @@ void ff_thread_flush(AVCodecContext *avctx) fctx->next_decoding = fctx->next_finished = 0; fctx->delaying = 1; fctx->prev_thread = NULL; + + decoded_frames_flush(&fctx->df); + for (i = 0; i < avctx->thread_count; i++) { PerThreadContext *p = &fctx->threads[i]; - // Make sure decode flush calls with size=0 won't return old frames - p->got_frame = 0; - av_frame_unref(p->frame); - p->result = 0; + + decoded_frames_flush(&p->df); #if FF_API_THREAD_SAFE_CALLBACKS release_delayed_buffers(p); #endif + av_packet_unref(p->avctx->internal->last_pkt_props); + av_packet_unref(p->avctx->internal->in_pkt); + + p->avctx->pts_correction_last_pts = + p->avctx->pts_correction_last_dts = INT64_MIN; + + p->avctx->internal->draining = 0; + p->avctx->internal->draining_done = 0; + p->avctx->internal->nb_draining_errors = 0; + if (ffcodec(avctx->codec)->flush) ffcodec(avctx->codec)->flush(p->avctx); } @@ -1193,3 +1262,15 @@ void ff_thread_release_ext_buffer(AVCodecContext *avctx, ThreadFrame *f) f->owner[0] = f->owner[1] = NULL; ff_thread_release_buffer(avctx, f->f); } + +int ff_thread_get_packet(AVCodecContext *avctx, AVPacket *pkt) +{ + PerThreadContext *p = avctx->internal->thread_ctx; + + if (p->avpkt->buf) { + av_packet_move_ref(pkt, p->avpkt); + return 0; + } + + return avctx->internal->draining ? AVERROR_EOF : AVERROR(EAGAIN); +} diff --git a/libavcodec/thread.h b/libavcodec/thread.h index d5673f25ea..7ae69990fb 100644 --- a/libavcodec/thread.h +++ b/libavcodec/thread.h @@ -40,17 +40,12 @@ void ff_thread_flush(AVCodecContext *avctx); /** - * Submit a new frame to a decoding thread. - * Returns the next available frame in picture. *got_picture_ptr - * will be 0 if none is available. - * The return value on success is the size of the consumed packet for - * compatibility with FFCodec.decode. This means the decoder - * has to consume the full packet. + * Submit available packets for decoding to worker threads, return a + * decoded frame if available. Returns AVERROR(EAGAIN) if none is available. * - * Parameters are the same as FFCodec.decode. + * Parameters are the same as FFCodec.receive_frame. */ -int ff_thread_decode_frame(AVCodecContext *avctx, AVFrame *picture, - int *got_picture_ptr, AVPacket *avpkt); +int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame); /** * If the codec defines update_thread_context(), call this @@ -99,6 +94,11 @@ int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f, int flags); */ void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f); +/** + * Get a packet for decoding. This gets invoked by the worker threads. + */ +int ff_thread_get_packet(AVCodecContext *avctx, AVPacket *pkt); + int ff_thread_init(AVCodecContext *s); int ff_slice_thread_execute_with_mainfunc(AVCodecContext *avctx, int (*action_func2)(AVCodecContext *c, void *arg, int jobnr, int threadnr), From patchwork Mon Dec 12 21:42:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Timo Rothenpieler X-Patchwork-Id: 39687 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:bc95:b0:ad:ade2:bfd2 with SMTP id fx21csp633259pzb; Mon, 12 Dec 2022 13:42:45 -0800 (PST) X-Google-Smtp-Source: AA0mqf7FR/i4F1brEQuDQFAG0vpiKPp3KiI+OMCCtMPibb/GCz83zMIVkq1D5KsqDqKZoCB3oZ8Y X-Received: by 2002:a05:6402:34d4:b0:470:1f1:257a with SMTP id w20-20020a05640234d400b0047001f1257amr4174157edc.25.1670881365400; Mon, 12 Dec 2022 13:42:45 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1670881365; cv=none; d=google.com; s=arc-20160816; b=kxobilN8b9KDytUxSyT0NeVbhY0d19LUG8zE+S1vUK8fcPM8GvApFYs/M6OnGBbj8n +PL0yg5F1fEjcsZZLyDzK7N09Nsr14LsPPjc0Lxt97am8sYAo/uw/gZ5AXxhZatBLJjJ Hf75Lk0mLFHbTeQQTQ7VTdisdhPMwS901SoxVkFqrHYIIfR5mJWUysMwrJawMuiRI+iC m03nR7+DZPcSdEKHw/BxX/ijhGxWfR0fSncRdj88SPTWWHPcHwBSi3tNrXpEJUytjWOa iBmn9hYH0FIhN1y9NTyVQOIsbDOPfa3TzN88/iFGhyYVVYfkinMIm2hqQfReAjjr9ke4 AzRg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc: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=Nyau8ndxx6iHbgcnJngh2kD6zOrZU8DJUm1Z+eU6Tiw=; b=VoczJ62WxoYFIGo7VHR5YxlOT0monerKtinOUplI+krUEjV4iv1HsDXYHsNhDFmZJU oQ2YN2kfF4gOcujr5Jw7Ow/i3T4c3zjneczpbjvpBaSIAM+vvPRoSE0CEasByuq85hKU 88eSnEGmtAmcqrWhyJTGG046R2O/ZKF0lHLvjf6dkji8hi+LI7dhSdvp/lPMYGy0rSrS JpccvtZqUV2aUZ0dG6Kvbh59ltW6pdQcmD2VyWvFf6K07FNcOGVkFYM2BSElVVg/N2s7 GwHKWICP8EePlC36SPKhMWBQIyaxk2kHrTWWd7Lu5fTxkhHpx4eY8snHhYNfx/aJ1717 bc7A== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@rothenpieler.org header.s=mail header.b=k6PX+y2q; 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=NONE dis=NONE) header.from=rothenpieler.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id j15-20020a50ed0f000000b004637f0abb77si7841993eds.487.2022.12.12.13.42.45; Mon, 12 Dec 2022 13:42:45 -0800 (PST) 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=@rothenpieler.org header.s=mail header.b=k6PX+y2q; 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=NONE dis=NONE) header.from=rothenpieler.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id E55C968BE2A; Mon, 12 Dec 2022 23:42:33 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from btbn.de (btbn.de [136.243.74.85]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5EC4368BDC0 for ; Mon, 12 Dec 2022 23:42:26 +0200 (EET) Received: from [authenticated] by btbn.de (Postfix) with ESMTPSA id DE4B22EC92C; Mon, 12 Dec 2022 22:42:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rothenpieler.org; s=mail; t=1670881345; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=170J0lt5XCY1bZ0gMow+hltQutt4/p4+ZdzjjMNQHwk=; b=k6PX+y2q6yapgRI2o4KuRVUXW7TH3a60Cf2OJ7tWv1qVfrG6XKI0hByAOGwqG8ZcMtZQMT NNo+e4uaqrj6ChrY29+Papw//um6I4ZYlWcCcuXffBNF+0TB1G7zIBqG92DyseRIOS/sGc dYKDVwOUxRYngxLhI0zVlvExXCjuTnv/6HtnTtQB8wZByr2vmtaqSPx9ljKadMoUqyr9UH n6zLeTI+ZdQsuXXmnqRB7VKLzn8RzvfBcgeMf2I0zu/SOFJxTQcM+Ml6XuBbnATg8uGVVr BWyGJ4o2dCOjHtrvrANmuECSO9yzwr5htYBa4sj7e2RofbGocUCo6IFbxM5rSA== From: Timo Rothenpieler To: ffmpeg-devel@ffmpeg.org Date: Mon, 12 Dec 2022 22:42:10 +0100 Message-Id: <20221212214210.2628-2-timo@rothenpieler.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221212214210.2628-1-timo@rothenpieler.org> References: <20221209234636.GH3806951@pb2> <20221212214210.2628-1-timo@rothenpieler.org> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v5 2/2] avcodec/mjpegdec: add support for frame threading 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 Cc: Timo Rothenpieler Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: HEibe3MEH+5Z In my tests, this lead to a notable speed increase with the amount of threads used. Decoding a 720p sample gave the following results: 1 Thread: 1428 FPS 2 Threads: 2501 FPS 8 Threads: 7575 FPS Automatic: 11326 FPS (On a 16 Core/32 Threads system) --- libavcodec/jpeglsdec.c | 2 +- libavcodec/mjpegdec.c | 11 ++++++----- libavcodec/sp5xdec.c | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index ec163b8964..6e75c9b406 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -559,6 +559,6 @@ const FFCodec ff_jpegls_decoder = { .init = ff_mjpeg_decode_init, .close = ff_mjpeg_decode_end, FF_CODEC_DECODE_CB(ff_mjpeg_decode_frame), - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index f33911e1a8..41d3f36940 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -54,6 +54,7 @@ #include "exif.h" #include "bytestream.h" #include "tiff_common.h" +#include "thread.h" static int init_default_huffman_tables(MJpegDecodeContext *s) @@ -712,7 +713,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) s->avctx->pix_fmt, AV_PIX_FMT_NONE, }; - s->hwaccel_pix_fmt = ff_get_format(s->avctx, pix_fmts); + s->hwaccel_pix_fmt = ff_thread_get_format(s->avctx, pix_fmts); if (s->hwaccel_pix_fmt < 0) return AVERROR(EINVAL); @@ -728,7 +729,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } av_frame_unref(s->picture_ptr); - if (ff_get_buffer(s->avctx, s->picture_ptr, AV_GET_BUFFER_FLAG_REF) < 0) + if (ff_thread_get_buffer(s->avctx, s->picture_ptr, AV_GET_BUFFER_FLAG_REF) < 0) return -1; s->picture_ptr->pict_type = AV_PICTURE_TYPE_I; s->picture_ptr->key_frame = 1; @@ -2954,7 +2955,7 @@ const FFCodec ff_mjpeg_decoder = { .close = ff_mjpeg_decode_end, FF_CODEC_DECODE_CB(ff_mjpeg_decode_frame), .flush = decode_flush, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .p.max_lowres = 3, .p.priv_class = &mjpegdec_class, .p.profiles = NULL_IF_CONFIG_SMALL(ff_mjpeg_profiles), @@ -2983,7 +2984,7 @@ const FFCodec ff_thp_decoder = { .close = ff_mjpeg_decode_end, FF_CODEC_DECODE_CB(ff_mjpeg_decode_frame), .flush = decode_flush, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .p.max_lowres = 3, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; @@ -3062,7 +3063,7 @@ const FFCodec ff_smvjpeg_decoder = { .close = ff_mjpeg_decode_end, FF_CODEC_RECEIVE_FRAME_CB(smvjpeg_receive_frame), .flush = decode_flush, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/sp5xdec.c b/libavcodec/sp5xdec.c index dfed725500..af1b6400e1 100644 --- a/libavcodec/sp5xdec.c +++ b/libavcodec/sp5xdec.c @@ -103,7 +103,7 @@ const FFCodec ff_sp5x_decoder = { .init = ff_mjpeg_decode_init, .close = ff_mjpeg_decode_end, FF_CODEC_DECODE_CB(sp5x_decode_frame), - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .p.max_lowres = 3, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; @@ -119,7 +119,7 @@ const FFCodec ff_amv_decoder = { .close = ff_mjpeg_decode_end, FF_CODEC_DECODE_CB(sp5x_decode_frame), .p.max_lowres = 3, - .p.capabilities = AV_CODEC_CAP_DR1, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; #endif