Skip to content

Commit

Permalink
lavc: convert frame threading to the receive_frame() pattern
Browse files Browse the repository at this point in the history
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 allows receive_frame()-based decoders to use frame
threading.

Further work by Timo Rothenpieler <timo@rothenpieler.org>.
  • Loading branch information
elenril committed Aug 12, 2024
1 parent 4d209da commit 5acbdd2
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 124 deletions.
9 changes: 6 additions & 3 deletions libavcodec/avcodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,13 @@ void avcodec_flush_buffers(AVCodecContext *avctx)

avci->draining = 0;
avci->draining_done = 0;
av_frame_unref(avci->buffer_frame);
av_packet_unref(avci->buffer_pkt);
if (avci->buffer_frame)
av_frame_unref(avci->buffer_frame);
if (avci->buffer_pkt)
av_packet_unref(avci->buffer_pkt);

if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME)
if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME &&
!avci->is_frame_mt)
ff_thread_flush(avctx);
else if (ffcodec(avctx->codec)->flush)
ffcodec(avctx->codec)->flush(avctx);
Expand Down
25 changes: 16 additions & 9 deletions libavcodec/avcodec_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,23 @@ void ff_thread_free(struct AVCodecContext *s);
void ff_thread_flush(struct 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(struct AVCodecContext *avctx, struct AVFrame *frame,
int *got_picture_ptr, struct AVPacket *avpkt);
int ff_thread_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);

/**
* Get a packet for decoding. This gets invoked by the worker threads.
*/
int ff_thread_get_packet(AVCodecContext *avctx, AVPacket *pkt);

#endif // AVCODEC_AVCODEC_INTERNAL_H
40 changes: 31 additions & 9 deletions libavcodec/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ static int decode_bsfs_init(AVCodecContext *avctx)
return ret;
}

#if !HAVE_THREADS
#define ff_thread_get_packet(avctx, pkt) (AVERROR_BUG)
#define ff_thread_receive_frame(avctx, frame) (AVERROR_BUG)
#endif

static int decode_get_packet(AVCodecContext *avctx, AVPacket *pkt)
{
AVCodecInternal *avci = avctx->internal;
Expand Down Expand Up @@ -240,6 +245,13 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt)
if (avci->draining)
return AVERROR_EOF;

/* 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)
return ff_thread_get_packet(avctx, pkt);

while (1) {
int ret = decode_get_packet(avctx, pkt);
if (ret == AVERROR(EAGAIN) &&
Expand Down Expand Up @@ -413,15 +425,11 @@ 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) {
consumed = ff_thread_decode_frame(avctx, frame, &got_frame, pkt);
} else {
frame->pict_type = dc->initial_pict_type;
frame->flags |= dc->intra_only_flag;
consumed = codec->cb.decode(avctx, frame, &got_frame, pkt);
Expand All @@ -436,7 +444,6 @@ FF_DISABLE_DEPRECATION_WARNINGS
FF_ENABLE_DEPRECATION_WARNINGS
#endif
}
}
emms_c();

if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
Expand Down Expand Up @@ -603,12 +610,12 @@ 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;
DecodeContext *dc = decode_ctx(avci);
const FFCodec *const codec = ffcodec(avctx->codec);
int ret, ok;
int ret;

av_assert0(!frame->buf[0]);

Expand Down Expand Up @@ -636,6 +643,20 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
if (ret == AVERROR_EOF)
avci->draining_done = 1;

return ret;
}

static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
{
AVCodecInternal *avci = avctx->internal;
DecodeContext *dc = decode_ctx(avci);
int ret, ok;

if (avctx->active_thread_type & FF_THREAD_FRAME)
ret = ff_thread_receive_frame(avctx, frame);
else
ret = ff_decode_receive_frame_internal(avctx, frame);

/* preserve ret */
ok = detect_colorspace(avctx, frame);
if (ok < 0) {
Expand Down Expand Up @@ -2151,7 +2172,8 @@ void ff_decode_flush_buffers(AVCodecContext *avctx)
dc->pts_correction_last_pts =
dc->pts_correction_last_dts = INT64_MIN;

av_bsf_flush(avci->bsf);
if (avci->bsf)
av_bsf_flush(avci->bsf);

dc->nb_draining_errors = 0;
dc->draining_started = 0;
Expand Down
7 changes: 7 additions & 0 deletions libavcodec/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,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;

/**
* Audio encoders can set this flag during init to indicate that they
* want the small last frame to be padded to a multiple of pad_samples.
Expand Down
Loading

0 comments on commit 5acbdd2

Please sign in to comment.