mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-07 05:39:49 +00:00
avcodec: allow bypassing frame threading with an optional flag
Normally, this function tries to make sure all threads are saturated with work to do before returning any frames; and will continue requesting packets until that is the case. However, this significantly slows down initial decoding latency when only requesting a single frame (to e.g. configure the filter graph), and also wastes a lot of unnecessary memory in the event that the user does not intend to decode more frames until later. By introducing a new `flags` paramater and a new flag `AV_CODEC_RECEIVE_FRAME_FLAG_SYNCHRONOUS` to go along with it, we can allow users to temporarily bypass this logic.
This commit is contained in:
parent
077864dfd6
commit
5e56937b74
7 changed files with 44 additions and 12 deletions
|
|
@ -2,6 +2,10 @@ The last version increases of all libraries were on 2025-03-28
|
|||
|
||||
API changes, most recent first:
|
||||
|
||||
2025-12-xx - xxxxxxxxxx - lavc 62.22.100 - avcodec.h
|
||||
Add avcodec_receive_frame2().
|
||||
Add AV_CODEC_RECEIVE_FRAME_FLAG_SYNCHRONOUS.
|
||||
|
||||
2025-09-xx - xxxxxxxxxx - lavfi 11.10.100 - buffersrc.h
|
||||
Add av_buffersrc_get_status().
|
||||
|
||||
|
|
|
|||
|
|
@ -705,7 +705,8 @@ int avcodec_is_open(AVCodecContext *s)
|
|||
return !!s->internal;
|
||||
}
|
||||
|
||||
int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
||||
int attribute_align_arg avcodec_receive_frame2(AVCodecContext *avctx,
|
||||
AVFrame *frame, unsigned flags)
|
||||
{
|
||||
av_frame_unref(frame);
|
||||
|
||||
|
|
@ -713,10 +714,15 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
|
|||
return AVERROR(EINVAL);
|
||||
|
||||
if (ff_codec_is_decoder(avctx->codec))
|
||||
return ff_decode_receive_frame(avctx, frame);
|
||||
return ff_decode_receive_frame(avctx, frame, flags);
|
||||
return ff_encode_receive_frame(avctx, frame);
|
||||
}
|
||||
|
||||
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
||||
{
|
||||
return avcodec_receive_frame2(avctx, frame, 0);
|
||||
}
|
||||
|
||||
#define WRAP_CONFIG(allowed_type, field, var, field_type, sentinel_check) \
|
||||
do { \
|
||||
if (codec->type != (allowed_type)) \
|
||||
|
|
|
|||
|
|
@ -415,6 +415,14 @@ typedef struct RcOverride{
|
|||
*/
|
||||
#define AV_GET_ENCODE_BUFFER_FLAG_REF (1 << 0)
|
||||
|
||||
/**
|
||||
* The decoder will bypass frame threading and return the next frame as soon as
|
||||
* possible. Note that this may deliver frames earlier than the advertised
|
||||
* `AVCodecContext.delay`. No effect when frame threading is disabled, or on
|
||||
* encoding.
|
||||
*/
|
||||
#define AV_CODEC_RECEIVE_FRAME_FLAG_SYNCHRONOUS (1 << 0)
|
||||
|
||||
/**
|
||||
* main external API structure.
|
||||
* New fields can be added to the end with minor version bumps.
|
||||
|
|
@ -2360,6 +2368,7 @@ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
|
|||
* frame (depending on the decoder type) allocated by the
|
||||
* codec. Note that the function will always call
|
||||
* av_frame_unref(frame) before doing anything else.
|
||||
* @param flags Combination of AV_CODEC_RECEIVE_FRAME_FLAG_* flags.
|
||||
*
|
||||
* @retval 0 success, a frame was returned
|
||||
* @retval AVERROR(EAGAIN) output is not available in this state - user must
|
||||
|
|
@ -2370,6 +2379,11 @@ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
|
|||
* @ref AV_CODEC_FLAG_RECON_FRAME flag enabled
|
||||
* @retval "other negative error code" legitimate decoding errors
|
||||
*/
|
||||
int avcodec_receive_frame2(AVCodecContext *avctx, AVFrame *frame, unsigned flags);
|
||||
|
||||
/**
|
||||
* Alias for `avcodec_receive_frame2(avctx, frame, 0)`.
|
||||
*/
|
||||
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ extern const SideDataMap ff_sd_global_map[];
|
|||
/**
|
||||
* avcodec_receive_frame() implementation for decoders.
|
||||
*/
|
||||
int ff_decode_receive_frame(struct AVCodecContext *avctx, struct AVFrame *frame);
|
||||
int ff_decode_receive_frame(struct AVCodecContext *avctx, struct AVFrame *frame,
|
||||
unsigned flags);
|
||||
|
||||
/**
|
||||
* avcodec_receive_frame() implementation for encoders.
|
||||
|
|
@ -91,9 +92,10 @@ void ff_thread_flush(struct AVCodecContext *avctx);
|
|||
* 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.receive_frame.
|
||||
* Parameters are the same as FFCodec.receive_frame, plus flags.
|
||||
*/
|
||||
int ff_thread_receive_frame(struct AVCodecContext *avctx, AVFrame *frame);
|
||||
int ff_thread_receive_frame(struct AVCodecContext *avctx, AVFrame *frame,
|
||||
unsigned flags);
|
||||
|
||||
/**
|
||||
* Do the actual decoding and obtain a decoded frame from the decoder, if
|
||||
|
|
|
|||
|
|
@ -645,14 +645,15 @@ int ff_decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
|
||||
static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame,
|
||||
unsigned flags)
|
||||
{
|
||||
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);
|
||||
ret = ff_thread_receive_frame(avctx, frame, flags);
|
||||
else
|
||||
ret = ff_decode_receive_frame_internal(avctx, frame);
|
||||
|
||||
|
|
@ -730,7 +731,7 @@ int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacke
|
|||
dc->draining_started = 1;
|
||||
|
||||
if (!avci->buffer_frame->buf[0] && !dc->draining_started) {
|
||||
ret = decode_receive_frame_internal(avctx, avci->buffer_frame);
|
||||
ret = decode_receive_frame_internal(avctx, avci->buffer_frame, 0);
|
||||
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -792,7 +793,7 @@ fail:
|
|||
return AVERROR_BUG;
|
||||
}
|
||||
|
||||
int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
||||
int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame, unsigned flags)
|
||||
{
|
||||
AVCodecInternal *avci = avctx->internal;
|
||||
int ret;
|
||||
|
|
@ -800,7 +801,7 @@ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
|||
if (avci->buffer_frame->buf[0]) {
|
||||
av_frame_move_ref(frame, avci->buffer_frame);
|
||||
} else {
|
||||
ret = decode_receive_frame_internal(avctx, frame);
|
||||
ret = decode_receive_frame_internal(avctx, frame, flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -559,7 +559,7 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
||||
int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame, unsigned flags)
|
||||
{
|
||||
FrameThreadContext *fctx = avctx->internal->thread_ctx;
|
||||
int ret = 0;
|
||||
|
|
@ -572,6 +572,10 @@ int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
|||
while (!fctx->df.nb_f && !fctx->result) {
|
||||
PerThreadContext *p;
|
||||
|
||||
if (fctx->next_decoding != fctx->next_finished &&
|
||||
(flags & AV_CODEC_RECEIVE_FRAME_FLAG_SYNCHRONOUS))
|
||||
goto wait_for_result;
|
||||
|
||||
/* 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);
|
||||
|
|
@ -588,6 +592,7 @@ int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
|||
!avctx->internal->draining)
|
||||
continue;
|
||||
|
||||
wait_for_result:
|
||||
p = &fctx->threads[fctx->next_finished];
|
||||
fctx->next_finished = (fctx->next_finished + 1) % avctx->thread_count;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
#include "version_major.h"
|
||||
|
||||
#define LIBAVCODEC_VERSION_MINOR 21
|
||||
#define LIBAVCODEC_VERSION_MINOR 22
|
||||
#define LIBAVCODEC_VERSION_MICRO 100
|
||||
|
||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue