[FFmpeg-devel] [PATCH] pthread: Fix ff_thread_get_format issues when called outside frame decode.

Reimar Döffinger Reimar.Doeffinger at gmx.de
Thu Feb 26 23:21:37 CET 2015


When ff_thread_get_format is called from the main thread, e.g.
during codec init it will access the thread_ctx as a PerThreadContext
even though it is a FrameThreadContext.
Catch this case and add asserts to protect against similar issues
in other places.
In addition, when ff_thread_get_format is called during the context
update it would get confused because the state is not as expected.
Make it handle that case by adding a new state for this, signalling
that we are actually executing in the main thread and thus can
call get_format directly in all cases.
These fix multithreaded decoding in MPlayer for example.

Signed-off-by: Reimar Döffinger <Reimar.Doeffinger at gmx.de>
---
 libavcodec/pthread_frame.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c
index 5a4ab84..b0d3469 100644
--- a/libavcodec/pthread_frame.c
+++ b/libavcodec/pthread_frame.c
@@ -53,6 +53,7 @@
  * Context used by codec threads and stored in their AVCodecInternal thread_ctx.
  */
 typedef struct PerThreadContext {
+    int main_thread;
     struct FrameThreadContext *parent;
 
     pthread_t      thread;
@@ -83,7 +84,8 @@ typedef struct PerThreadContext {
                                      * Set when the codec calls get_format().
                                      * State is returned to STATE_SETTING_UP afterwards.
                                      */
-        STATE_SETUP_FINISHED        ///< Set after the codec has called ff_thread_finish_setup().
+        STATE_SETUP_FINISHED,       ///< Set after the codec has called ff_thread_finish_setup().
+        STATE_UPDATE_CONTEXT,       ///< Main thread is updating its context
     } state;
 
     /**
@@ -105,6 +107,7 @@ typedef struct PerThreadContext {
  * Context stored in the client AVCodecInternal thread_ctx.
  */
 typedef struct FrameThreadContext {
+    int main_thread;
     PerThreadContext *threads;     ///< The contexts for each thread.
     PerThreadContext *prev_thread; ///< The last thread submit_packet() was called on.
 
@@ -143,6 +146,7 @@ static attribute_align_arg void *frame_worker_thread(void *arg)
     AVCodecContext *avctx = p->avctx;
     const AVCodec *codec = avctx->codec;
 
+    av_assert0(!p->main_thread);
     pthread_mutex_lock(&p->mutex);
     while (1) {
             while (p->state == STATE_INPUT_READY && !fctx->die)
@@ -330,6 +334,8 @@ static int submit_packet(PerThreadContext *p, AVPacket *avpkt)
 
     pthread_mutex_lock(&p->mutex);
 
+    p->state = STATE_UPDATE_CONTEXT;
+
     release_delayed_buffers(p);
 
     if (prev_thread) {
@@ -408,6 +414,7 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
     int finished = fctx->next_finished;
     PerThreadContext *p;
     int err;
+    av_assert0(fctx->main_thread);
 
     /*
      * Submit a packet to the next decoding thread.
@@ -515,6 +522,7 @@ void ff_thread_finish_setup(AVCodecContext *avctx) {
 
     if (!(avctx->active_thread_type&FF_THREAD_FRAME)) return;
 
+    av_assert0(!p->main_thread);
     if(p->state == STATE_SETUP_FINISHED){
         av_log(avctx, AV_LOG_WARNING, "Multiple ff_thread_finish_setup() calls\n");
     }
@@ -549,6 +557,7 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count)
     const AVCodec *codec = avctx->codec;
     int i;
 
+    av_assert0(fctx->main_thread);
     park_frame_worker_threads(fctx, thread_count);
 
     if (fctx->prev_thread && fctx->prev_thread != fctx->threads)
@@ -634,6 +643,7 @@ int ff_frame_thread_init(AVCodecContext *avctx)
     }
 
     avctx->internal->thread_ctx = fctx = av_mallocz(sizeof(FrameThreadContext));
+    fctx->main_thread = 1;
 
     fctx->threads = av_mallocz_array(thread_count, sizeof(PerThreadContext));
     pthread_mutex_init(&fctx->buffer_mutex, NULL);
@@ -718,6 +728,7 @@ void ff_thread_flush(AVCodecContext *avctx)
 
     if (!fctx) return;
 
+    av_assert0(fctx->main_thread);
     park_frame_worker_threads(fctx, avctx->thread_count);
     if (fctx->prev_thread) {
         if (fctx->prev_thread != &fctx->threads[0])
@@ -743,7 +754,10 @@ void ff_thread_flush(AVCodecContext *avctx)
 int ff_thread_can_start_frame(AVCodecContext *avctx)
 {
     PerThreadContext *p = avctx->internal->thread_ctx;
-    if ((avctx->active_thread_type&FF_THREAD_FRAME) && p->state != STATE_SETTING_UP &&
+    if (avctx->active_thread_type&FF_THREAD_FRAME)
+        return 0;
+    av_assert0(!p->main_thread);
+    if (p->state != STATE_SETTING_UP &&
         (avctx->codec->update_thread_context || !THREAD_SAFE_CALLBACKS(avctx))) {
         return 0;
     }
@@ -762,6 +776,7 @@ 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);
 
+    av_assert0(!p->main_thread);
     if (p->state != STATE_SETTING_UP &&
         (avctx->codec->update_thread_context || !THREAD_SAFE_CALLBACKS(avctx))) {
         av_log(avctx, AV_LOG_ERROR, "get_buffer() cannot be called after ff_thread_finish_setup()\n");
@@ -819,7 +834,8 @@ enum AVPixelFormat ff_thread_get_format(AVCodecContext *avctx, const enum AVPixe
     enum AVPixelFormat res;
     PerThreadContext *p = avctx->internal->thread_ctx;
     if (!(avctx->active_thread_type & FF_THREAD_FRAME) || avctx->thread_safe_callbacks ||
-        avctx->get_format == avcodec_default_get_format)
+        avctx->get_format == avcodec_default_get_format ||
+        p->main_thread || p->state == STATE_UPDATE_CONTEXT)
         return ff_get_format(avctx, fmt);
     if (p->state != STATE_SETTING_UP) {
         av_log(avctx, AV_LOG_ERROR, "get_format() cannot be called after ff_thread_finish_setup()\n");
-- 
2.1.4



More information about the ffmpeg-devel mailing list