diff --git a/doc/APIchanges b/doc/APIchanges index 239137f1f2..f308fd40a2 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -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(). diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index dacdfba8da..8b4b305fcd 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -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)) \ diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 91bd7cb7ef..92c0207cf6 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -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); /** diff --git a/libavcodec/avcodec_internal.h b/libavcodec/avcodec_internal.h index 184d7b526c..06645e91a0 100644 --- a/libavcodec/avcodec_internal.h +++ b/libavcodec/avcodec_internal.h @@ -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 diff --git a/libavcodec/decode.c b/libavcodec/decode.c index f0646901c8..89a55c1cd8 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -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; } diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index ff015c00b3..2115d4204d 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -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; diff --git a/libavcodec/version.h b/libavcodec/version.h index da6f3a84ac..9411511e04 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -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, \