mirror of https://git.ffmpeg.org/ffmpeg.git
lavc/pthread_frame: add support for thread-safe hwaccels
This commit is contained in:
parent
3d2e1aa324
commit
8b23644408
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
|
||||
#define HWACCEL_CAP_ASYNC_SAFE (1 << 0)
|
||||
#define HWACCEL_CAP_THREAD_SAFE (1 << 1)
|
||||
|
||||
|
||||
typedef struct AVCodecHWConfigInternal {
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
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){
|
||||
|
|
Loading…
Reference in New Issue