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