aboutsummaryrefslogtreecommitdiffstats
path: root/libavcodec
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2023-04-28 13:37:28 +0200
committerLynne <dev@lynne.ee>2023-05-29 00:41:27 +0200
commit8b23644408ef4ea98184325d7a98f43c316143a1 (patch)
tree3099977f7c65f756eff5ea8e96549b838e567b57 /libavcodec
parent3d2e1aa3242d6c4dc2b3a004b316921ce7e0ba79 (diff)
downloadffmpeg-8b23644408ef4ea98184325d7a98f43c316143a1.tar.gz
lavc/pthread_frame: add support for thread-safe hwaccels
Diffstat (limited to 'libavcodec')
-rw-r--r--libavcodec/avcodec.h6
-rw-r--r--libavcodec/hwconfig.h1
-rw-r--r--libavcodec/pthread_frame.c101
3 files changed, 89 insertions, 19 deletions
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 06b1a120ab..dad443c818 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -2253,6 +2253,12 @@ typedef struct AVHWAccel {
* that avctx->hwaccel_priv_data is invalid.
*/
int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx);
+
+ /**
+ * Copy necessary context variables from a previous thread context to the current one.
+ * For thread-safe hwaccels only.
+ */
+ int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);
} AVHWAccel;
/**
diff --git a/libavcodec/hwconfig.h b/libavcodec/hwconfig.h
index f03b744cdf..d88dc37c8c 100644
--- a/libavcodec/hwconfig.h
+++ b/libavcodec/hwconfig.h
@@ -24,6 +24,7 @@
#define HWACCEL_CAP_ASYNC_SAFE (1 << 0)
+#define HWACCEL_CAP_THREAD_SAFE (1 << 1)
typedef struct AVCodecHWConfigInternal {
diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c
index 773e78ae34..28335231fd 100644
--- a/libavcodec/pthread_frame.c
+++ b/libavcodec/pthread_frame.c
@@ -104,6 +104,12 @@ typedef struct PerThreadContext {
int hwaccel_serializing;
int async_serializing;
+ // set to 1 in ff_thread_finish_setup() when a threadsafe hwaccel is used;
+ // cannot check hwaccel caps directly, because
+ // worked threads clear hwaccel state for thread-unsafe hwaccels
+ // after each decode call
+ int hwaccel_threadsafe;
+
atomic_int debug_threads; ///< Set if the FF_DEBUG_THREADS option is set.
} PerThreadContext;
@@ -117,8 +123,8 @@ typedef struct FrameThreadContext {
unsigned pthread_init_cnt; ///< Number of successfully initialized mutexes/conditions
pthread_mutex_t buffer_mutex; ///< Mutex used to protect get/release_buffer().
/**
- * This lock is used for ensuring threads run in serial when hwaccel
- * is used.
+ * This lock is used for ensuring threads run in serial when thread-unsafe
+ * hwaccel is used.
*/
pthread_mutex_t hwaccel_mutex;
pthread_mutex_t async_mutex;
@@ -133,13 +139,19 @@ typedef struct FrameThreadContext {
* While it is set, ff_thread_en/decode_frame won't return any results.
*/
- /* hwaccel state is temporarily stored here in order to transfer its ownership
- * to the next decoding thread without the need for extra synchronization */
+ /* hwaccel state for thread-unsafe hwaccels is temporarily stored here in
+ * order to transfer its ownership to the next decoding thread without the
+ * need for extra synchronization */
const AVHWAccel *stash_hwaccel;
void *stash_hwaccel_context;
void *stash_hwaccel_priv;
} FrameThreadContext;
+static int hwaccel_serial(const AVCodecContext *avctx)
+{
+ return avctx->hwaccel && !(avctx->hwaccel->caps_internal & HWACCEL_CAP_THREAD_SAFE);
+}
+
static void async_lock(FrameThreadContext *fctx)
{
pthread_mutex_lock(&fctx->async_mutex);
@@ -202,9 +214,9 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
* cannot be true here. */
av_assert0(!p->hwaccel_serializing);
- /* if the previous thread uses hwaccel then we take the lock to ensure
- * the threads don't run concurrently */
- if (avctx->hwaccel) {
+ /* if the previous thread uses thread-unsafe hwaccel then we take the
+ * lock to ensure the threads don't run concurrently */
+ if (hwaccel_serial(avctx)) {
pthread_mutex_lock(&p->parent->hwaccel_mutex);
p->hwaccel_serializing = 1;
}
@@ -220,7 +232,8 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
ff_thread_finish_setup(avctx);
if (p->hwaccel_serializing) {
- /* wipe hwaccel state to avoid stale pointers lying around;
+ /* wipe hwaccel state for thread-unsafe hwaccels to avoid stale
+ * pointers lying around;
* the state was transferred to FrameThreadContext in
* ff_thread_finish_setup(), so nothing is leaked */
avctx->hwaccel = NULL;
@@ -230,7 +243,8 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
p->hwaccel_serializing = 0;
pthread_mutex_unlock(&p->parent->hwaccel_mutex);
}
- av_assert0(!avctx->hwaccel);
+ av_assert0(!avctx->hwaccel ||
+ (avctx->hwaccel->caps_internal & HWACCEL_CAP_THREAD_SAFE));
if (p->async_serializing) {
p->async_serializing = 0;
@@ -332,8 +346,49 @@ FF_ENABLE_DEPRECATION_WARNINGS
if (codec->update_thread_context_for_user)
err = codec->update_thread_context_for_user(dst, src);
} else {
- if (codec->update_thread_context)
+ const PerThreadContext *p_src = src->internal->thread_ctx;
+ PerThreadContext *p_dst = dst->internal->thread_ctx;
+
+ if (codec->update_thread_context) {
err = codec->update_thread_context(dst, src);
+ if (err < 0)
+ return err;
+ }
+
+ // reset dst hwaccel state if needed
+ av_assert0(p_dst->hwaccel_threadsafe ||
+ (!dst->hwaccel && !dst->internal->hwaccel_priv_data));
+ if (p_dst->hwaccel_threadsafe &&
+ (!p_src->hwaccel_threadsafe || dst->hwaccel != src->hwaccel)) {
+ ff_hwaccel_uninit(dst);
+ p_dst->hwaccel_threadsafe = 0;
+ }
+
+ // propagate hwaccel state for threadsafe hwaccels
+ if (p_src->hwaccel_threadsafe) {
+ if (!dst->hwaccel) {
+ if (src->hwaccel->priv_data_size) {
+ av_assert0(src->hwaccel->update_thread_context);
+
+ dst->internal->hwaccel_priv_data =
+ av_mallocz(src->hwaccel->priv_data_size);
+ if (!dst->internal->hwaccel_priv_data)
+ return AVERROR(ENOMEM);
+ }
+ dst->hwaccel = src->hwaccel;
+ }
+ av_assert0(dst->hwaccel == src->hwaccel);
+
+ if (src->hwaccel->update_thread_context) {
+ err = src->hwaccel->update_thread_context(dst, src);
+ if (err < 0) {
+ av_log(dst, AV_LOG_ERROR, "Error propagating hwaccel state\n");
+ ff_hwaccel_uninit(dst);
+ return err;
+ }
+ }
+ p_dst->hwaccel_threadsafe = 1;
+ }
}
return err;
@@ -441,10 +496,12 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx,
}
/* transfer the stashed hwaccel state, if any */
- av_assert0(!p->avctx->hwaccel);
- FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel);
- FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context);
- FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv);
+ av_assert0(!p->avctx->hwaccel || p->hwaccel_threadsafe);
+ if (!p->hwaccel_threadsafe) {
+ FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel);
+ FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context);
+ FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv);
+ }
av_packet_unref(p->avpkt);
ret = av_packet_ref(p->avpkt, avpkt);
@@ -598,7 +655,10 @@ void ff_thread_finish_setup(AVCodecContext *avctx) {
if (!(avctx->active_thread_type&FF_THREAD_FRAME)) return;
- if (avctx->hwaccel && !p->hwaccel_serializing) {
+ p->hwaccel_threadsafe = avctx->hwaccel &&
+ (avctx->hwaccel->caps_internal & HWACCEL_CAP_THREAD_SAFE);
+
+ if (hwaccel_serial(avctx) && !p->hwaccel_serializing) {
pthread_mutex_lock(&p->parent->hwaccel_mutex);
p->hwaccel_serializing = 1;
}
@@ -611,13 +671,16 @@ void ff_thread_finish_setup(AVCodecContext *avctx) {
async_lock(p->parent);
}
- /* save hwaccel state for passing to the next thread;
+ /* thread-unsafe hwaccels share a single private data instance, so we
+ * save hwaccel state for passing to the next thread;
* this is done here so that this worker thread can wipe its own hwaccel
* state after decoding, without requiring synchronization */
av_assert0(!p->parent->stash_hwaccel);
- p->parent->stash_hwaccel = avctx->hwaccel;
- p->parent->stash_hwaccel_context = avctx->hwaccel_context;
- p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data;
+ if (hwaccel_serial(avctx)) {
+ p->parent->stash_hwaccel = avctx->hwaccel;
+ p->parent->stash_hwaccel_context = avctx->hwaccel_context;
+ p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data;
+ }
pthread_mutex_lock(&p->progress_mutex);
if(atomic_load(&p->state) == STATE_SETUP_FINISHED){