This fixes a large class of race conditions (coded_frame works again) and 
should improve frame dropping.
---
 libavcodec/mpegvideo.c |    1 -
 libavcodec/pthread.c   |   79 ++++++++++++++++++++++++++++++++++++++++-------
 libavcodec/utils.c     |    2 +-
 mt-work/todo.txt       |    4 +--
 4 files changed, 69 insertions(+), 17 deletions(-)
diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c
index 9d75981..ccc4c51 100644
--- a/libavcodec/mpegvideo.c
+++ b/libavcodec/mpegvideo.c
@@ -426,7 +426,6 @@ int ff_mpeg_update_context(AVCodecContext *dst, AVCodecContext *src)
     s->next_p_frame_damaged= s1->next_p_frame_damaged;
 
     s->max_b_frames        = s1->max_b_frames;
-    s->avctx->has_b_frames = s1->avctx->has_b_frames;
     s->low_delay           = s1->low_delay;
     s->dropable            = s1->dropable;
 
diff --git a/libavcodec/pthread.c b/libavcodec/pthread.c
index b44065e..705af3d 100644
--- a/libavcodec/pthread.c
+++ b/libavcodec/pthread.c
@@ -251,6 +251,7 @@ static attribute_align_arg void *decode_frame_thread(void *arg)
 static int ff_frame_thread_init(AVCodecContext *avctx)
 {
     FrameThreadContext *fctx;
+    AVCodecContext *first_context;
     int i, thread_count = avctx->thread_count;
 
     fctx = av_mallocz(sizeof(FrameThreadContext));
@@ -259,6 +260,7 @@ static int ff_frame_thread_init(AVCodecContext *avctx)
     fctx->threads = av_mallocz(sizeof(PerThreadContext) * thread_count);
 
     for (i = 0; i < thread_count; i++) {
+        AVCodecContext *copy = av_malloc(sizeof(AVCodecContext));
         PerThreadContext *p = &fctx->threads[i];
 
         pthread_mutex_init(&p->mutex, NULL);
@@ -269,24 +271,23 @@ static int ff_frame_thread_init(AVCodecContext *avctx)
         pthread_cond_init(&p->output_cond, NULL);
 
         p->parent = fctx;
+        p->context = copy;
 
         if (!i) {
-            p->context = avctx;
             avctx->thread_opaque = p;
+            *copy = *avctx;
+            first_context = copy;
 
             if (avctx->codec->init)
                 avctx->codec->init(p->context);
         } else {
-            AVCodecContext *copy = av_malloc(sizeof(AVCodecContext));
+            *copy = *first_context;
+            copy->thread_opaque = p;
 
-            *copy = *avctx;
             copy->is_copy = 1;
             copy->priv_data = av_malloc(avctx->codec->priv_data_size);
             memcpy(copy->priv_data, avctx->priv_data, copy->codec->priv_data_size);
             if (copy->codec->init_copy) copy->codec->init_copy(copy);
-
-            p->context = copy;
-            copy->thread_opaque = p;
         }
 
         pthread_create(&p->thread, NULL, decode_frame_thread, p);
@@ -295,6 +296,58 @@ static int ff_frame_thread_init(AVCodecContext *avctx)
     return 0;
 }
 
+/**
+ * Update a thread's context from the last thread. This is used for returning
+ * frames and for starting new decoding jobs after the previous one finishes
+ * predecoding.
+ *
+ * @param dst The destination context.
+ * @param src The source context.
+ * @param for_user Whether or not dst is the user-visible context. update_context won't be called and some pointers will be copied.
+ */
+static void update_context_from_copy(AVCodecContext *dst, AVCodecContext *src, int for_user)
+{
+#define COPY(f) dst->f = src->f;
+#define COPY_FIELDS(s, e) memcpy(&dst->s, &src->s, (char*)&dst->e - (char*)&dst->s);
+
+    //coded_width/height are not copied here, so that codecs' update_context can see when they change
+    //many encoding parameters could be theoretically changed during encode, but aren't copied ATM
+
+    COPY(sub_id);
+    COPY(pix_fmt);
+    COPY(real_pict_num); //necessary?
+    COPY(delay);
+    COPY(max_b_frames);
+    COPY(hurry_up);
+
+    COPY_FIELDS(mv_bits, opaque);
+
+    COPY(has_b_frames);
+    COPY(bits_per_sample);
+    if (for_user) COPY(coded_frame);
+    memcpy(dst->error, src->error, sizeof(src->error));
+    COPY(last_predictor_count); //necessary?
+    COPY(color_table_id);
+    COPY(profile);
+    COPY(level);
+    COPY_FIELDS(skip_loop_filter, bidir_refine);
+
+    if (!for_user) {
+        COPY(frame_number);
+
+        if (dst->codec->update_context)
+            dst->codec->update_context(dst, src);
+    }
+}
+
+///Update the next decoding thread with values set by the user
+static void update_context_from_user(AVCodecContext *dst, AVCodecContext *src)
+{
+    COPY(hurry_up);
+    COPY_FIELDS(skip_loop_filter, bidir_refine);
+    COPY(frame_number);
+}
+
 static void submit_frame(PerThreadContext * volatile p, const uint8_t *buf, int buf_size)
 {
     AVCodec *codec = p->context->codec;
@@ -305,9 +358,7 @@ static void submit_frame(PerThreadContext * volatile p, const uint8_t *buf, int
 
     pthread_mutex_lock(&p->mutex);
 
-    if (codec->update_context && prev_thread) {
-        codec->update_context(p->context, prev_thread->context);
-    }
+    if (prev_thread) update_context_from_copy(p->context, prev_thread->context, 0);
 
     p->input = av_fast_realloc(p->input, &p->input_size, buf_size + FF_INPUT_BUFFER_PADDING_SIZE);
     memcpy(p->input, buf, buf_size);
@@ -345,6 +396,7 @@ int ff_decode_frame_threaded(AVCodecContext *avctx,
     fctx = ((PerThreadContext*)avctx->thread_opaque)->parent;
 
     p = &fctx->threads[fctx->next_available++];
+    update_context_from_user(p->context, avctx);
     submit_frame(p, buf, buf_size);
 
     if (fctx->delaying) {
@@ -360,6 +412,7 @@ int ff_decode_frame_threaded(AVCodecContext *avctx,
     while (p->state != STATE_INPUT_READY) {pthread_cond_wait(&p->output_cond, &p->progress_mutex);}
     pthread_mutex_unlock(&p->progress_mutex);
 
+    update_context_from_copy(avctx, p->context, 1);
     *(AVFrame*)data = p->result;
     *data_size = p->output_size;
 
@@ -432,8 +485,8 @@ static void ff_frame_thread_free(AVCodecContext *avctx)
 
     park_frame_decode_threads(fctx, avctx->thread_count);
 
-    if (fctx->prev_thread != &fctx->threads[0] && codec->update_context)
-        codec->update_context(fctx->threads[0].context, fctx->prev_thread->context);
+    if (fctx->prev_thread != &fctx->threads[0])
+        update_context_from_copy(fctx->threads[0].context, fctx->prev_thread->context, 0);
 
     fctx->die = 1;
 
@@ -466,11 +519,13 @@ static void ff_frame_thread_free(AVCodecContext *avctx)
     if (codec->close)
         codec->close(fctx->threads->context);
 
+    av_freep(&fctx->threads->context);
     av_freep(&fctx->threads);
     av_free(fctx);
 
     avctx->thread_opaque = NULL;
     avctx->thread_algorithm = 0;
+    avctx->codec = NULL;
 }
 
 void ff_frame_thread_flush(AVCodecContext *avctx)
@@ -481,7 +536,7 @@ void ff_frame_thread_flush(AVCodecContext *avctx)
     park_frame_decode_threads(fctx, avctx->thread_count);
 
     if (fctx->prev_thread != fctx->threads)
-        avctx->codec->update_context(fctx->threads->context, fctx->prev_thread->context);
+        update_context_from_copy(fctx->threads->context, fctx->prev_thread->context, 0);
 
     for (i = 0; i < avctx->thread_count; i++) {
         PerThreadContext *p = &fctx->threads[i];
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index bbee69b..7b31257 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -1002,7 +1002,7 @@ int avcodec_close(AVCodecContext *avctx)
 
     if (ENABLE_THREADS && avctx->thread_opaque)
         avcodec_thread_free(avctx);
-    if (avctx->codec->close && !USE_FRAME_THREADING(avctx))
+    if (avctx->codec && avctx->codec->close)
         avctx->codec->close(avctx);
     avcodec_default_free_buffers(avctx);
     av_freep(&avctx->priv_data);
diff --git a/mt-work/todo.txt b/mt-work/todo.txt
index bcbeacd..3866608 100644
--- a/mt-work/todo.txt
+++ b/mt-work/todo.txt
@@ -1,10 +1,8 @@
 Todo
 - Maybe write a guide to converting codecs, if the API documentation itself isn't enough
-- Don't use the user's AVCodecContext for the first decoding thread, since it will read from it while user code is running and cause a race condition. This is easier than forbidding reading/writing to avctx after calling predecode_done.
-- Files with packed B-frames don't decode some frames properly in mplayer, possibly because of the above.
+- Files with packed B-frames don't decode some frames properly in mplayer but do in ffplay.
 - Packed B-frames are not parallelized
 - Encoding support. This is easy, but adapting the mpegvideo encoder, especially ratecontrol, isn't.
-- Have a generic update_context which is always called and keeps the AVCodecContext copies in sync
 - Handle stream reinits (closing and reopening the internal state due to width/height change) properly
 - Sort out h264/mpeg's update_context into some reasonable order and comment them
 
_______________________________________________
FFmpeg-soc mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc

Reply via email to