X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fpthread_frame.c;h=c9d2e00ce37715599010b9f6760f2e27dfbd6398;hb=a83098ab03a47179d54a9b9c8bcefc81b9c6aafd;hp=36ac0ac1e57791db73272fde3ff72e52e92998ac;hpb=768b07e3bca7e81d2f9c60badb3973d3d88481dc;p=ffmpeg diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 36ac0ac1e57..c9d2e00ce37 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -28,7 +28,7 @@ #include #include "avcodec.h" -#include "hwaccel.h" +#include "hwconfig.h" #include "internal.h" #include "pthread_internal.h" #include "thread.h" @@ -89,19 +89,21 @@ typedef struct PerThreadContext { atomic_int state; +#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. */ - AVFrame *released_buffers; - int num_released_buffers; - int released_buffers_allocated; + AVFrame **released_buffers; + int num_released_buffers; + int released_buffers_allocated; AVFrame *requested_frame; ///< AVFrame the codec passed to get_buffer() int requested_flags; ///< flags passed to get_buffer() for requested_frame const enum AVPixelFormat *available_formats; ///< Format array for get_format() enum AVPixelFormat result_format; ///< get_format() result +#endif int die; ///< Set when the thread should exit. @@ -137,8 +139,10 @@ typedef struct FrameThreadContext { */ } FrameThreadContext; +#if FF_API_THREAD_SAFE_CALLBACKS #define THREAD_SAFE_CALLBACKS(avctx) \ ((avctx)->thread_safe_callbacks || (avctx)->get_buffer2 == avcodec_default_get_buffer2) +#endif static void async_lock(FrameThreadContext *fctx) { @@ -178,8 +182,14 @@ static attribute_align_arg void *frame_worker_thread(void *arg) if (p->die) break; - 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_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 @@ -201,7 +211,7 @@ static attribute_align_arg void *frame_worker_thread(void *arg) p->result = codec->decode(avctx, p->frame, &p->got_frame, &p->avpkt); if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) { - if (avctx->internal->allocate_progress) + if (avctx->codec->caps_internal & FF_CODEC_CAP_ALLOCATE_PROGRESS) av_log(avctx, AV_LOG_ERROR, "A frame threaded decoder did not " "free the frame on failure. This is a bug, please report it.\n"); av_frame_unref(p->frame); @@ -246,7 +256,7 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, { int err = 0; - if (dst != src && (for_user || !(src->codec_descriptor->props & AV_CODEC_PROP_INTRA_ONLY))) { + if (dst != src && (for_user || src->codec->update_thread_context)) { dst->time_base = src->time_base; dst->framerate = src->framerate; dst->width = src->width; @@ -296,10 +306,13 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, } dst->hwaccel_flags = src->hwaccel_flags; + + err = av_buffer_replace(&dst->internal->pool, src->internal->pool); + if (err < 0) + return err; } if (for_user) { - dst->delay = src->thread_count - 1; #if FF_API_CODED_FRAME FF_DISABLE_DEPRECATION_WARNINGS dst->coded_frame = src->coded_frame; @@ -322,7 +335,6 @@ FF_ENABLE_DEPRECATION_WARNINGS */ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) { -#define copy_fields(s, e) memcpy(&dst->s, &src->s, (char*)&dst->e - (char*)&dst->s); dst->flags = src->flags; dst->draw_horiz_band= src->draw_horiz_band; @@ -334,12 +346,19 @@ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) dst->slice_flags = src->slice_flags; dst->flags2 = src->flags2; + dst->export_side_data = src->export_side_data; - copy_fields(skip_loop_filter, subtitle_header); + dst->skip_loop_filter = src->skip_loop_filter; + dst->skip_idct = src->skip_idct; + dst->skip_frame = src->skip_frame; 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; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (src->slice_count && src->slice_offset) { if (dst->slice_count < src->slice_count) { @@ -353,9 +372,9 @@ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) } dst->slice_count = src->slice_count; return 0; -#undef copy_fields } +#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) { @@ -369,13 +388,14 @@ static void release_delayed_buffers(PerThreadContext *p) // fix extended data in case the caller screwed it up av_assert0(p->avctx->codec_type == AVMEDIA_TYPE_VIDEO || p->avctx->codec_type == AVMEDIA_TYPE_AUDIO); - f = &p->released_buffers[--p->num_released_buffers]; + f = p->released_buffers[--p->num_released_buffers]; f->extended_data = f->data; av_frame_unref(f); pthread_mutex_unlock(&fctx->buffer_mutex); } } +#endif static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, AVPacket *avpkt) @@ -399,7 +419,9 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, (p->avctx->debug & FF_DEBUG_THREADS) != 0, memory_order_relaxed); +#if FF_API_THREAD_SAFE_CALLBACKS release_delayed_buffers(p); +#endif if (prev_thread) { int err; @@ -429,6 +451,8 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, 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, @@ -462,6 +486,8 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, pthread_mutex_unlock(&p->progress_mutex); } } +FF_ENABLE_DEPRECATION_WARNINGS +#endif fctx->prev_thread = p; fctx->next_decoding++; @@ -657,6 +683,13 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) park_frame_worker_threads(fctx, thread_count); + if (fctx->prev_thread && avctx->internal->hwaccel_priv_data != + fctx->prev_thread->avctx->internal->hwaccel_priv_data) { + if (update_context_from_thread(avctx, fctx->prev_thread->avctx, 1) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to update user thread.\n"); + } + } + if (fctx->prev_thread && fctx->prev_thread != fctx->threads) if (update_context_from_thread(fctx->threads->avctx, fctx->prev_thread->avctx, 0) < 0) { av_log(avctx, AV_LOG_ERROR, "Final thread update failed\n"); @@ -679,7 +712,9 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) if (codec->close && p->avctx) codec->close(p->avctx); +#if FF_API_THREAD_SAFE_CALLBACKS release_delayed_buffers(p); +#endif av_frame_free(&p->frame); } @@ -692,14 +727,23 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) pthread_cond_destroy(&p->progress_cond); pthread_cond_destroy(&p->output_cond); av_packet_unref(&p->avpkt); + +#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); +#endif - if (i && p->avctx) { + if (p->avctx) { + if (codec->priv_class) + av_opt_free(p->avctx->priv_data); av_freep(&p->avctx->priv_data); + av_freep(&p->avctx->slice_offset); } if (p->avctx) { + av_buffer_unref(&p->avctx->internal->pool); av_freep(&p->avctx->internal); av_buffer_unref(&p->avctx->hw_frames_ctx); } @@ -764,6 +808,9 @@ int ff_frame_thread_init(AVCodecContext *avctx) fctx->async_lock = 1; fctx->delaying = 1; + if (codec->type == AVMEDIA_TYPE_VIDEO) + avctx->delay = src->thread_count - 1; + for (i = 0; i < thread_count; i++) { AVCodecContext *copy = av_malloc(sizeof(AVCodecContext)); PerThreadContext *p = &fctx->threads[i]; @@ -801,28 +848,34 @@ int ff_frame_thread_init(AVCodecContext *avctx) copy->internal->thread_ctx = p; copy->internal->last_pkt_props = &p->avpkt; - if (!i) { - src = copy; + copy->delay = avctx->delay; - if (codec->init) - err = codec->init(copy); - - update_context_from_thread(avctx, copy, 1); - } else { - copy->priv_data = av_malloc(codec->priv_data_size); + if (codec->priv_data_size) { + copy->priv_data = av_mallocz(codec->priv_data_size); if (!copy->priv_data) { err = AVERROR(ENOMEM); goto error; } - memcpy(copy->priv_data, src->priv_data, codec->priv_data_size); - copy->internal->is_copy = 1; - if (codec->init_thread_copy) - err = codec->init_thread_copy(copy); + if (codec->priv_class) { + *(const AVClass **)copy->priv_data = codec->priv_class; + err = av_opt_copy(copy->priv_data, src->priv_data); + if (err < 0) + goto error; + } } + if (i) + copy->internal->is_copy = 1; + + if (codec->init) + err = codec->init(copy); + if (err) goto error; + if (!i) + update_context_from_thread(avctx, copy, 1); + atomic_init(&p->debug_threads, (copy->debug & FF_DEBUG_THREADS) != 0); err = AVERROR(pthread_create(&p->thread, NULL, frame_worker_thread, p)); @@ -862,7 +915,9 @@ void ff_thread_flush(AVCodecContext *avctx) av_frame_unref(p->frame); p->result = 0; +#if FF_API_THREAD_SAFE_CALLBACKS release_delayed_buffers(p); +#endif if (avctx->codec->flush) avctx->codec->flush(p->avctx); @@ -872,10 +927,16 @@ void ff_thread_flush(AVCodecContext *avctx) 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 && - (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 + )) { return 0; } +FF_ENABLE_DEPRECATION_WARNINGS return 1; } @@ -889,13 +950,19 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, ThreadFrame *f, int 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 && - (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; } - if (avctx->internal->allocate_progress) { + if (avctx->codec->caps_internal & FF_CODEC_CAP_ALLOCATE_PROGRESS) { atomic_int *progress; f->progress = av_buffer_alloc(2 * sizeof(*progress)); if (!f->progress) { @@ -908,6 +975,10 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, ThreadFrame *f, int } 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 { @@ -927,6 +998,8 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, ThreadFrame *f, int } 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); @@ -935,6 +1008,8 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, ThreadFrame *f, int return err; } +#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; @@ -960,6 +1035,8 @@ enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixe return res; } +FF_ENABLE_DEPRECATION_WARNINGS +#endif int ff_thread_get_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags) { @@ -971,13 +1048,18 @@ int ff_thread_get_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags) 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, *tmp; + 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 - if (!f->f || !f->f->buf[0]) + if (!f->f) return; if (avctx->debug & FF_DEBUG_BUFFERS) @@ -986,7 +1068,11 @@ void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f) av_buffer_unref(&f->progress); f->owner[0] = f->owner[1] = NULL; - if (can_direct_free) { +#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); return; } @@ -994,20 +1080,37 @@ void ff_thread_release_buffer(AVCodecContext *avctx, ThreadFrame *f) fctx = p->parent; pthread_mutex_lock(&fctx->buffer_mutex); - if (p->num_released_buffers + 1 >= INT_MAX / sizeof(*p->released_buffers)) - goto fail; - tmp = av_fast_realloc(p->released_buffers, &p->released_buffers_allocated, - (p->num_released_buffers + 1) * - sizeof(*p->released_buffers)); - if (!tmp) - goto fail; - p->released_buffers = tmp; + if (p->num_released_buffers == p->released_buffers_allocated) { + AVFrame **tmp = av_realloc_array(p->released_buffers, p->released_buffers_allocated + 1, + sizeof(*p->released_buffers)); + if (tmp) { + tmp[p->released_buffers_allocated] = av_frame_alloc(); + p->released_buffers = tmp; + } + + if (!tmp || !tmp[p->released_buffers_allocated]) { + ret = AVERROR(ENOMEM); + goto fail; + } + p->released_buffers_allocated++; + } - dst = &p->released_buffers[p->num_released_buffers]; + dst = p->released_buffers[p->num_released_buffers]; av_frame_move_ref(dst, f->f); p->num_released_buffers++; fail: pthread_mutex_unlock(&fctx->buffer_mutex); + + // make sure the frame is clean even if we fail to free it + // this leaks, but it is better than crashing + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not queue a frame for freeing, this will leak\n"); + memset(f->f->buf, 0, sizeof(f->f->buf)); + if (f->f->extended_buf) + memset(f->f->extended_buf, 0, f->f->nb_extended_buf * sizeof(*f->f->extended_buf)); + av_frame_unref(f->f); + } +#endif }