They add considerable complexity to frame-threading implementation,
which includes an unavoidably leaking error path, while the advantages
of this option to the users are highly dubious.
It should be always possible and desirable for the callers to make their
get_buffer2() implementation thread-safe, so deprecate this option.
API changes, most recent first:
API changes, most recent first:
+2020-xx-xx - xxxxxxxxxx - lavc 58.114.100 - avcodec.h
+ Deprecate AVCodecContext.thread_safe_callbacks. Starting with
+ LIBAVCODEC_VERSION_MAJOR=60, user callbacks must always be
+ thread-safe when frame threading is used.
+
2020-11-25 - xxxxxxxxxx - lavc 58.113.100 - avcodec.h
Adds a new flag AV_CODEC_EXPORT_DATA_FILM_GRAIN for export_side_data.
2020-11-25 - xxxxxxxxxx - lavc 58.113.100 - avcodec.h
Adds a new flag AV_CODEC_EXPORT_DATA_FILM_GRAIN for export_side_data.
Frame threading -
* Restrictions with slice threading also apply.
Frame threading -
* Restrictions with slice threading also apply.
-* For best performance, the client should set thread_safe_callbacks if it
- provides a thread-safe get_buffer() callback.
+* Custom get_buffer2() and get_format() callbacks must be thread-safe.
* There is one frame of delay added for every thread beyond the first one.
Clients must be able to handle this; the pkt_dts and pkt_pts fields in
AVFrame will work as usual.
* There is one frame of delay added for every thread beyond the first one.
Clients must be able to handle this; the pkt_dts and pkt_pts fields in
AVFrame will work as usual.
ist->dec_ctx->opaque = ist;
ist->dec_ctx->get_format = get_format;
ist->dec_ctx->get_buffer2 = get_buffer;
ist->dec_ctx->opaque = ist;
ist->dec_ctx->get_format = get_format;
ist->dec_ctx->get_buffer2 = get_buffer;
+#if LIBAVCODEC_VERSION_MAJOR < 60
ist->dec_ctx->thread_safe_callbacks = 1;
ist->dec_ctx->thread_safe_callbacks = 1;
av_opt_set_int(ist->dec_ctx, "refcounted_frames", 1, 0);
if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&
av_opt_set_int(ist->dec_ctx, "refcounted_frames", 1, 0);
if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&
*
* Some decoders do not support linesizes changing between frames.
*
*
* Some decoders do not support linesizes changing between frames.
*
- * If frame multithreading is used and thread_safe_callbacks is set,
- * this callback may be called from a different thread, but not from more
- * than one at once. Does not need to be reentrant.
+ * If frame multithreading is used, this callback may be called from a
+ * different thread, but not from more than one at once. Does not need to be
+ * reentrant.
*
* @see avcodec_align_dimensions2()
*
*
* @see avcodec_align_dimensions2()
*
*/
int active_thread_type;
*/
int active_thread_type;
+#if FF_API_THREAD_SAFE_CALLBACKS
/**
* Set by the client if its custom get_buffer() callback can be called
* synchronously from another thread, which allows faster multithreaded decoding.
/**
* Set by the client if its custom get_buffer() callback can be called
* synchronously from another thread, which allows faster multithreaded decoding.
* Ignored if the default get_buffer() is used.
* - encoding: Set by user.
* - decoding: Set by user.
* Ignored if the default get_buffer() is used.
* - encoding: Set by user.
* - decoding: Set by user.
+ *
+ * @deprecated the custom get_buffer2() callback should always be
+ * thread-safe. Thread-unsafe get_buffer2() implementations will be
+ * invalid once this field is removed.
int thread_safe_callbacks;
int thread_safe_callbacks;
/**
* The codec may call this to execute several independent things.
/**
* The codec may call this to execute several independent things.
+#if FF_API_THREAD_SAFE_CALLBACKS
/**
* Array of frames passed to ff_thread_release_buffer().
* Frames are released after all threads referencing them are finished.
/**
* Array of frames passed to ff_thread_release_buffer().
* Frames are released after all threads referencing them are finished.
const enum AVPixelFormat *available_formats; ///< Format array for get_format()
enum AVPixelFormat result_format; ///< get_format() result
const enum AVPixelFormat *available_formats; ///< Format array for get_format()
enum AVPixelFormat result_format; ///< get_format() result
int die; ///< Set when the thread should exit.
int die; ///< Set when the thread should exit.
+#if FF_API_THREAD_SAFE_CALLBACKS
#define THREAD_SAFE_CALLBACKS(avctx) \
((avctx)->thread_safe_callbacks || (avctx)->get_buffer2 == avcodec_default_get_buffer2)
#define THREAD_SAFE_CALLBACKS(avctx) \
((avctx)->thread_safe_callbacks || (avctx)->get_buffer2 == avcodec_default_get_buffer2)
static void async_lock(FrameThreadContext *fctx)
{
static void async_lock(FrameThreadContext *fctx)
{
- if (!codec->update_thread_context && THREAD_SAFE_CALLBACKS(avctx))
+FF_DISABLE_DEPRECATION_WARNINGS
+ if (!codec->update_thread_context
+#if FF_API_THREAD_SAFE_CALLBACKS
+ && THREAD_SAFE_CALLBACKS(avctx)
+#endif
+ )
ff_thread_finish_setup(avctx);
ff_thread_finish_setup(avctx);
+FF_ENABLE_DEPRECATION_WARNINGS
/* If a decoder supports hwaccel, then it must call ff_get_format().
* Since that call must happen before ff_thread_finish_setup(), the
/* If a decoder supports hwaccel, then it must call ff_get_format().
* Since that call must happen before ff_thread_finish_setup(), the
dst->frame_number = src->frame_number;
dst->reordered_opaque = src->reordered_opaque;
dst->frame_number = src->frame_number;
dst->reordered_opaque = src->reordered_opaque;
+#if FF_API_THREAD_SAFE_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
dst->thread_safe_callbacks = src->thread_safe_callbacks;
dst->thread_safe_callbacks = src->thread_safe_callbacks;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
if (src->slice_count && src->slice_offset) {
if (dst->slice_count < src->slice_count) {
if (src->slice_count && src->slice_offset) {
if (dst->slice_count < src->slice_count) {
+#if FF_API_THREAD_SAFE_CALLBACKS
/// Releases the buffers that this decoding thread was the last user of.
static void release_delayed_buffers(PerThreadContext *p)
{
/// Releases the buffers that this decoding thread was the last user of.
static void release_delayed_buffers(PerThreadContext *p)
{
pthread_mutex_unlock(&fctx->buffer_mutex);
}
}
pthread_mutex_unlock(&fctx->buffer_mutex);
}
}
static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx,
AVPacket *avpkt)
static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx,
AVPacket *avpkt)
(p->avctx->debug & FF_DEBUG_THREADS) != 0,
memory_order_relaxed);
(p->avctx->debug & FF_DEBUG_THREADS) != 0,
memory_order_relaxed);
+#if FF_API_THREAD_SAFE_CALLBACKS
release_delayed_buffers(p);
release_delayed_buffers(p);
if (prev_thread) {
int err;
if (prev_thread) {
int err;
pthread_cond_signal(&p->input_cond);
pthread_mutex_unlock(&p->mutex);
pthread_cond_signal(&p->input_cond);
pthread_mutex_unlock(&p->mutex);
+#if FF_API_THREAD_SAFE_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
/*
* If the client doesn't have a thread-safe get_buffer(),
* then decoding threads call back to the main thread,
/*
* If the client doesn't have a thread-safe get_buffer(),
* then decoding threads call back to the main thread,
pthread_mutex_unlock(&p->progress_mutex);
}
}
pthread_mutex_unlock(&p->progress_mutex);
}
}
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
fctx->prev_thread = p;
fctx->next_decoding++;
fctx->prev_thread = p;
fctx->next_decoding++;
{
FrameThreadContext *fctx = avctx->internal->thread_ctx;
const AVCodec *codec = avctx->codec;
{
FrameThreadContext *fctx = avctx->internal->thread_ctx;
const AVCodec *codec = avctx->codec;
park_frame_worker_threads(fctx, thread_count);
park_frame_worker_threads(fctx, thread_count);
if (codec->close && p->avctx)
codec->close(p->avctx);
if (codec->close && p->avctx)
codec->close(p->avctx);
+#if FF_API_THREAD_SAFE_CALLBACKS
release_delayed_buffers(p);
release_delayed_buffers(p);
av_frame_free(&p->frame);
}
av_frame_free(&p->frame);
}
pthread_cond_destroy(&p->output_cond);
av_packet_unref(&p->avpkt);
pthread_cond_destroy(&p->output_cond);
av_packet_unref(&p->avpkt);
- for (j = 0; j < p->released_buffers_allocated; j++)
+#if FF_API_THREAD_SAFE_CALLBACKS
+ for (int j = 0; j < p->released_buffers_allocated; j++)
av_frame_free(&p->released_buffers[j]);
av_freep(&p->released_buffers);
av_frame_free(&p->released_buffers[j]);
av_freep(&p->released_buffers);
if (p->avctx) {
if (codec->priv_class)
if (p->avctx) {
if (codec->priv_class)
av_frame_unref(p->frame);
p->result = 0;
av_frame_unref(p->frame);
p->result = 0;
+#if FF_API_THREAD_SAFE_CALLBACKS
release_delayed_buffers(p);
release_delayed_buffers(p);
if (avctx->codec->flush)
avctx->codec->flush(p->avctx);
if (avctx->codec->flush)
avctx->codec->flush(p->avctx);
int ff_thread_can_start_frame(AVCodecContext *avctx)
{
PerThreadContext *p = avctx->internal->thread_ctx;
int ff_thread_can_start_frame(AVCodecContext *avctx)
{
PerThreadContext *p = avctx->internal->thread_ctx;
+FF_DISABLE_DEPRECATION_WARNINGS
if ((avctx->active_thread_type&FF_THREAD_FRAME) && atomic_load(&p->state) != STATE_SETTING_UP &&
if ((avctx->active_thread_type&FF_THREAD_FRAME) && atomic_load(&p->state) != STATE_SETTING_UP &&
- (avctx->codec->update_thread_context || !THREAD_SAFE_CALLBACKS(avctx))) {
+ (avctx->codec->update_thread_context
+#if FF_API_THREAD_SAFE_CALLBACKS
+ || !THREAD_SAFE_CALLBACKS(avctx)
+#endif
+ )) {
+FF_ENABLE_DEPRECATION_WARNINGS
if (!(avctx->active_thread_type & FF_THREAD_FRAME))
return ff_get_buffer(avctx, f->f, flags);
if (!(avctx->active_thread_type & FF_THREAD_FRAME))
return ff_get_buffer(avctx, f->f, flags);
+FF_DISABLE_DEPRECATION_WARNINGS
if (atomic_load(&p->state) != STATE_SETTING_UP &&
if (atomic_load(&p->state) != STATE_SETTING_UP &&
- (avctx->codec->update_thread_context || !THREAD_SAFE_CALLBACKS(avctx))) {
+ (avctx->codec->update_thread_context
+#if FF_API_THREAD_SAFE_CALLBACKS
+ || !THREAD_SAFE_CALLBACKS(avctx)
+#endif
+ )) {
+FF_ENABLE_DEPRECATION_WARNINGS
av_log(avctx, AV_LOG_ERROR, "get_buffer() cannot be called after ff_thread_finish_setup()\n");
return -1;
}
av_log(avctx, AV_LOG_ERROR, "get_buffer() cannot be called after ff_thread_finish_setup()\n");
return -1;
}
}
pthread_mutex_lock(&p->parent->buffer_mutex);
}
pthread_mutex_lock(&p->parent->buffer_mutex);
+#if !FF_API_THREAD_SAFE_CALLBACKS
+ err = ff_get_buffer(avctx, f->f, flags);
+#else
+FF_DISABLE_DEPRECATION_WARNINGS
if (THREAD_SAFE_CALLBACKS(avctx)) {
err = ff_get_buffer(avctx, f->f, flags);
} else {
if (THREAD_SAFE_CALLBACKS(avctx)) {
err = ff_get_buffer(avctx, f->f, flags);
} else {
}
if (!THREAD_SAFE_CALLBACKS(avctx) && !avctx->codec->update_thread_context)
ff_thread_finish_setup(avctx);
}
if (!THREAD_SAFE_CALLBACKS(avctx) && !avctx->codec->update_thread_context)
ff_thread_finish_setup(avctx);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
if (err)
av_buffer_unref(&f->progress);
if (err)
av_buffer_unref(&f->progress);
+#if FF_API_THREAD_SAFE_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt)
{
enum AVPixelFormat res;
enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt)
{
enum AVPixelFormat res;
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
int ff_thread_get_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags)
{
int ff_thread_get_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags)
{
void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f)
{
void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f)
{
+#if FF_API_THREAD_SAFE_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
PerThreadContext *p = avctx->internal->thread_ctx;
FrameThreadContext *fctx;
AVFrame *dst;
int ret = 0;
int can_direct_free = !(avctx->active_thread_type & FF_THREAD_FRAME) ||
THREAD_SAFE_CALLBACKS(avctx);
PerThreadContext *p = avctx->internal->thread_ctx;
FrameThreadContext *fctx;
AVFrame *dst;
int ret = 0;
int can_direct_free = !(avctx->active_thread_type & FF_THREAD_FRAME) ||
THREAD_SAFE_CALLBACKS(avctx);
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
av_buffer_unref(&f->progress);
f->owner[0] = f->owner[1] = NULL;
av_buffer_unref(&f->progress);
f->owner[0] = f->owner[1] = NULL;
+#if !FF_API_THREAD_SAFE_CALLBACKS
+ av_frame_unref(f->f);
+#else
// when the frame buffers are not allocated, just reset it to clean state
if (can_direct_free || !f->f->buf[0]) {
av_frame_unref(f->f);
// when the frame buffers are not allocated, just reset it to clean state
if (can_direct_free || !f->f->buf[0]) {
av_frame_unref(f->f);
memset(f->f->extended_buf, 0, f->f->nb_extended_buf * sizeof(*f->f->extended_buf));
av_frame_unref(f->f);
}
memset(f->f->extended_buf, 0, f->f->nb_extended_buf * sizeof(*f->f->extended_buf));
av_frame_unref(f->f);
}
*/
void ff_thread_await_progress(ThreadFrame *f, int progress, int field);
*/
void ff_thread_await_progress(ThreadFrame *f, int progress, int field);
+#if FF_API_THREAD_SAFE_CALLBACKS
/**
* Wrapper around get_format() for frame-multithreaded codecs.
* Call this function instead of avctx->get_format().
/**
* Wrapper around get_format() for frame-multithreaded codecs.
* Call this function instead of avctx->get_format().
* @param fmt The list of available formats.
*/
enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt);
* @param fmt The list of available formats.
*/
enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt);
+#else
+#define ff_thread_get_format ff_get_format
+#endif
/**
* Wrapper around get_buffer() for frame-multithreaded codecs.
/**
* Wrapper around get_buffer() for frame-multithreaded codecs.
+#if FF_API_THREAD_SAFE_CALLBACKS
+FF_DISABLE_DEPRECATION_WARNINGS
+ if ((avctx->thread_type & FF_THREAD_FRAME) &&
+ avctx->get_buffer2 != avcodec_default_get_buffer2 &&
+ !avctx->thread_safe_callbacks) {
+ av_log(avctx, AV_LOG_WARNING, "Requested frame threading with a "
+ "custom get_buffer2() implementation which is not marked as "
+ "thread safe. This is not supported anymore, make your "
+ "callback thread-safe.\n");
+ }
+FF_ENABLE_DEPRECATION_WARNINGS
+#endif
+
avctx->codec = codec;
if ((avctx->codec_type == AVMEDIA_TYPE_UNKNOWN || avctx->codec_type == codec->type) &&
avctx->codec_id == AV_CODEC_ID_NONE) {
avctx->codec = codec;
if ((avctx->codec_type == AVMEDIA_TYPE_UNKNOWN || avctx->codec_type == codec->type) &&
avctx->codec_id == AV_CODEC_ID_NONE) {
#include "libavutil/version.h"
#define LIBAVCODEC_VERSION_MAJOR 58
#include "libavutil/version.h"
#define LIBAVCODEC_VERSION_MAJOR 58
-#define LIBAVCODEC_VERSION_MINOR 113
+#define LIBAVCODEC_VERSION_MINOR 114
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
#ifndef FF_API_AVPRIV_PUT_BITS
#define FF_API_AVPRIV_PUT_BITS (LIBAVCODEC_VERSION_MAJOR < 59)
#endif
#ifndef FF_API_AVPRIV_PUT_BITS
#define FF_API_AVPRIV_PUT_BITS (LIBAVCODEC_VERSION_MAJOR < 59)
#endif
+#ifndef FF_API_THREAD_SAFE_CALLBACKS
+#define FF_API_THREAD_SAFE_CALLBACKS (LIBAVCODEC_VERSION_MAJOR < 60)
+#endif
#endif /* AVCODEC_VERSION_H */
#endif /* AVCODEC_VERSION_H */