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.
|
* that avctx->hwaccel_priv_data is invalid.
|
||||||
*/
|
*/
|
||||||
int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx);
|
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;
|
} AVHWAccel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
|
|
||||||
#define HWACCEL_CAP_ASYNC_SAFE (1 << 0)
|
#define HWACCEL_CAP_ASYNC_SAFE (1 << 0)
|
||||||
|
#define HWACCEL_CAP_THREAD_SAFE (1 << 1)
|
||||||
|
|
||||||
|
|
||||||
typedef struct AVCodecHWConfigInternal {
|
typedef struct AVCodecHWConfigInternal {
|
||||||
|
|
|
@ -104,6 +104,12 @@ typedef struct PerThreadContext {
|
||||||
int hwaccel_serializing;
|
int hwaccel_serializing;
|
||||||
int async_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.
|
atomic_int debug_threads; ///< Set if the FF_DEBUG_THREADS option is set.
|
||||||
} PerThreadContext;
|
} PerThreadContext;
|
||||||
|
|
||||||
|
@ -117,8 +123,8 @@ typedef struct FrameThreadContext {
|
||||||
unsigned pthread_init_cnt; ///< Number of successfully initialized mutexes/conditions
|
unsigned pthread_init_cnt; ///< Number of successfully initialized mutexes/conditions
|
||||||
pthread_mutex_t buffer_mutex; ///< Mutex used to protect get/release_buffer().
|
pthread_mutex_t buffer_mutex; ///< Mutex used to protect get/release_buffer().
|
||||||
/**
|
/**
|
||||||
* This lock is used for ensuring threads run in serial when hwaccel
|
* This lock is used for ensuring threads run in serial when thread-unsafe
|
||||||
* is used.
|
* hwaccel is used.
|
||||||
*/
|
*/
|
||||||
pthread_mutex_t hwaccel_mutex;
|
pthread_mutex_t hwaccel_mutex;
|
||||||
pthread_mutex_t async_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.
|
* 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
|
/* hwaccel state for thread-unsafe hwaccels is temporarily stored here in
|
||||||
* to the next decoding thread without the need for extra synchronization */
|
* order to transfer its ownership to the next decoding thread without the
|
||||||
|
* need for extra synchronization */
|
||||||
const AVHWAccel *stash_hwaccel;
|
const AVHWAccel *stash_hwaccel;
|
||||||
void *stash_hwaccel_context;
|
void *stash_hwaccel_context;
|
||||||
void *stash_hwaccel_priv;
|
void *stash_hwaccel_priv;
|
||||||
} FrameThreadContext;
|
} 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)
|
static void async_lock(FrameThreadContext *fctx)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&fctx->async_mutex);
|
pthread_mutex_lock(&fctx->async_mutex);
|
||||||
|
@ -202,9 +214,9 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
|
||||||
* cannot be true here. */
|
* cannot be true here. */
|
||||||
av_assert0(!p->hwaccel_serializing);
|
av_assert0(!p->hwaccel_serializing);
|
||||||
|
|
||||||
/* if the previous thread uses hwaccel then we take the lock to ensure
|
/* if the previous thread uses thread-unsafe hwaccel then we take the
|
||||||
* the threads don't run concurrently */
|
* lock to ensure the threads don't run concurrently */
|
||||||
if (avctx->hwaccel) {
|
if (hwaccel_serial(avctx)) {
|
||||||
pthread_mutex_lock(&p->parent->hwaccel_mutex);
|
pthread_mutex_lock(&p->parent->hwaccel_mutex);
|
||||||
p->hwaccel_serializing = 1;
|
p->hwaccel_serializing = 1;
|
||||||
}
|
}
|
||||||
|
@ -220,7 +232,8 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
|
||||||
ff_thread_finish_setup(avctx);
|
ff_thread_finish_setup(avctx);
|
||||||
|
|
||||||
if (p->hwaccel_serializing) {
|
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
|
* the state was transferred to FrameThreadContext in
|
||||||
* ff_thread_finish_setup(), so nothing is leaked */
|
* ff_thread_finish_setup(), so nothing is leaked */
|
||||||
avctx->hwaccel = NULL;
|
avctx->hwaccel = NULL;
|
||||||
|
@ -230,7 +243,8 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
|
||||||
p->hwaccel_serializing = 0;
|
p->hwaccel_serializing = 0;
|
||||||
pthread_mutex_unlock(&p->parent->hwaccel_mutex);
|
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) {
|
if (p->async_serializing) {
|
||||||
p->async_serializing = 0;
|
p->async_serializing = 0;
|
||||||
|
@ -332,8 +346,49 @@ FF_ENABLE_DEPRECATION_WARNINGS
|
||||||
if (codec->update_thread_context_for_user)
|
if (codec->update_thread_context_for_user)
|
||||||
err = codec->update_thread_context_for_user(dst, src);
|
err = codec->update_thread_context_for_user(dst, src);
|
||||||
} else {
|
} 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);
|
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;
|
return err;
|
||||||
|
@ -441,10 +496,12 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* transfer the stashed hwaccel state, if any */
|
/* transfer the stashed hwaccel state, if any */
|
||||||
av_assert0(!p->avctx->hwaccel);
|
av_assert0(!p->avctx->hwaccel || p->hwaccel_threadsafe);
|
||||||
FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel);
|
if (!p->hwaccel_threadsafe) {
|
||||||
FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context);
|
FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel);
|
||||||
FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv);
|
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);
|
av_packet_unref(p->avpkt);
|
||||||
ret = av_packet_ref(p->avpkt, 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->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);
|
pthread_mutex_lock(&p->parent->hwaccel_mutex);
|
||||||
p->hwaccel_serializing = 1;
|
p->hwaccel_serializing = 1;
|
||||||
}
|
}
|
||||||
|
@ -611,13 +671,16 @@ void ff_thread_finish_setup(AVCodecContext *avctx) {
|
||||||
async_lock(p->parent);
|
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
|
* this is done here so that this worker thread can wipe its own hwaccel
|
||||||
* state after decoding, without requiring synchronization */
|
* state after decoding, without requiring synchronization */
|
||||||
av_assert0(!p->parent->stash_hwaccel);
|
av_assert0(!p->parent->stash_hwaccel);
|
||||||
p->parent->stash_hwaccel = avctx->hwaccel;
|
if (hwaccel_serial(avctx)) {
|
||||||
p->parent->stash_hwaccel_context = avctx->hwaccel_context;
|
p->parent->stash_hwaccel = avctx->hwaccel;
|
||||||
p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data;
|
p->parent->stash_hwaccel_context = avctx->hwaccel_context;
|
||||||
|
p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data;
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&p->progress_mutex);
|
pthread_mutex_lock(&p->progress_mutex);
|
||||||
if(atomic_load(&p->state) == STATE_SETUP_FINISHED){
|
if(atomic_load(&p->state) == STATE_SETUP_FINISHED){
|
||||||
|
|
Loading…
Reference in New Issue