mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-12-07 13:49:53 +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:
|
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
|
2025-09-xx - xxxxxxxxxx - lavfi 11.10.100 - buffersrc.h
|
||||||
Add av_buffersrc_get_status().
|
Add av_buffersrc_get_status().
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -705,7 +705,8 @@ int avcodec_is_open(AVCodecContext *s)
|
||||||
return !!s->internal;
|
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);
|
av_frame_unref(frame);
|
||||||
|
|
||||||
|
|
@ -713,10 +714,15 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
if (ff_codec_is_decoder(avctx->codec))
|
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);
|
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) \
|
#define WRAP_CONFIG(allowed_type, field, var, field_type, sentinel_check) \
|
||||||
do { \
|
do { \
|
||||||
if (codec->type != (allowed_type)) \
|
if (codec->type != (allowed_type)) \
|
||||||
|
|
|
||||||
|
|
@ -415,6 +415,14 @@ typedef struct RcOverride{
|
||||||
*/
|
*/
|
||||||
#define AV_GET_ENCODE_BUFFER_FLAG_REF (1 << 0)
|
#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.
|
* main external API structure.
|
||||||
* New fields can be added to the end with minor version bumps.
|
* 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
|
* frame (depending on the decoder type) allocated by the
|
||||||
* codec. Note that the function will always call
|
* codec. Note that the function will always call
|
||||||
* av_frame_unref(frame) before doing anything else.
|
* 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 0 success, a frame was returned
|
||||||
* @retval AVERROR(EAGAIN) output is not available in this state - user must
|
* @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
|
* @ref AV_CODEC_FLAG_RECON_FRAME flag enabled
|
||||||
* @retval "other negative error code" legitimate decoding errors
|
* @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);
|
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.
|
* 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.
|
* 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
|
* Submit available packets for decoding to worker threads, return a
|
||||||
* decoded frame if available. Returns AVERROR(EAGAIN) if none is available.
|
* 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
|
* 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;
|
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;
|
AVCodecInternal *avci = avctx->internal;
|
||||||
DecodeContext *dc = decode_ctx(avci);
|
DecodeContext *dc = decode_ctx(avci);
|
||||||
int ret, ok;
|
int ret, ok;
|
||||||
|
|
||||||
if (avctx->active_thread_type & FF_THREAD_FRAME)
|
if (avctx->active_thread_type & FF_THREAD_FRAME)
|
||||||
ret = ff_thread_receive_frame(avctx, frame);
|
ret = ff_thread_receive_frame(avctx, frame, flags);
|
||||||
else
|
else
|
||||||
ret = ff_decode_receive_frame_internal(avctx, frame);
|
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;
|
dc->draining_started = 1;
|
||||||
|
|
||||||
if (!avci->buffer_frame->buf[0] && !dc->draining_started) {
|
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)
|
if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -792,7 +793,7 @@ fail:
|
||||||
return AVERROR_BUG;
|
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;
|
AVCodecInternal *avci = avctx->internal;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -800,7 +801,7 @@ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
||||||
if (avci->buffer_frame->buf[0]) {
|
if (avci->buffer_frame->buf[0]) {
|
||||||
av_frame_move_ref(frame, avci->buffer_frame);
|
av_frame_move_ref(frame, avci->buffer_frame);
|
||||||
} else {
|
} else {
|
||||||
ret = decode_receive_frame_internal(avctx, frame);
|
ret = decode_receive_frame_internal(avctx, frame, flags);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -559,7 +559,7 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx,
|
||||||
return 0;
|
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;
|
FrameThreadContext *fctx = avctx->internal->thread_ctx;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
@ -572,6 +572,10 @@ int ff_thread_receive_frame(AVCodecContext *avctx, AVFrame *frame)
|
||||||
while (!fctx->df.nb_f && !fctx->result) {
|
while (!fctx->df.nb_f && !fctx->result) {
|
||||||
PerThreadContext *p;
|
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 */
|
/* get a packet to be submitted to the next thread */
|
||||||
av_packet_unref(fctx->next_pkt);
|
av_packet_unref(fctx->next_pkt);
|
||||||
ret = ff_decode_get_packet(avctx, 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)
|
!avctx->internal->draining)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
wait_for_result:
|
||||||
p = &fctx->threads[fctx->next_finished];
|
p = &fctx->threads[fctx->next_finished];
|
||||||
fctx->next_finished = (fctx->next_finished + 1) % avctx->thread_count;
|
fctx->next_finished = (fctx->next_finished + 1) % avctx->thread_count;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
#include "version_major.h"
|
#include "version_major.h"
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_MINOR 21
|
#define LIBAVCODEC_VERSION_MINOR 22
|
||||||
#define LIBAVCODEC_VERSION_MICRO 100
|
#define LIBAVCODEC_VERSION_MICRO 100
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue