diff mbox

[FFmpeg-devel,RFC] lavc/phtread_frame: update context in child thread in multi-thread mode

Message ID 1561575848-28763-1-git-send-email-linjie.fu@intel.com
State Superseded
Headers show

Commit Message

Fu, Linjie June 26, 2019, 7:04 p.m. UTC
Currently in ff_thread_decode_frame, context is updated from child thread
to main thread, and main thread releases the context in avcodec_close()
when decode finishes.

However, when resolution/format change in vp9, ff_get_format was called,
and hwaccel_uninit() and hwaccel_init will be used to destroy and re-create
the context. Due to the async between main-thread and child-thread,
main-thread updated its context from child earlier than the context was
refreshed in child-thread. And it will lead to:
    1. memory leak in child-thread.
    2. double free in main-thread while calling avcodec_close().

Can be reproduced with a resolution change case in vp9, and use -vframes
to terminate the decode between the dynamic resolution change frames:

ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -v
verbose -i ./test2360_1672_4980.ivf -pix_fmt p010le -f rawvideo -vsync
passthrough -vframes 6 -y out.yuv

Move update_context_from_thread from ff_thread_decode_frame(main thread)
to frame_worker_thread(child thread), update the context in child thread
right after the context was updated to avoid the async issue.

Signed-off-by: Linjie Fu <linjie.fu@intel.com>
---
Request for Comments, not quite familiar with the constraints.
Thanks in advance.

 libavcodec/internal.h      |   5 ++
 libavcodec/pthread_frame.c | 164 ++++++++++++++++++++++++---------------------
 2 files changed, 91 insertions(+), 78 deletions(-)

Comments

Hendrik Leppkes June 26, 2019, 7:12 a.m. UTC | #1
On Wed, Jun 26, 2019 at 9:06 AM Linjie Fu <linjie.fu@intel.com> wrote:
>
> Currently in ff_thread_decode_frame, context is updated from child thread
> to main thread, and main thread releases the context in avcodec_close()
> when decode finishes.
>
> However, when resolution/format change in vp9, ff_get_format was called,
> and hwaccel_uninit() and hwaccel_init will be used to destroy and re-create
> the context. Due to the async between main-thread and child-thread,
> main-thread updated its context from child earlier than the context was
> refreshed in child-thread. And it will lead to:
>     1. memory leak in child-thread.
>     2. double free in main-thread while calling avcodec_close().
>
> Can be reproduced with a resolution change case in vp9, and use -vframes
> to terminate the decode between the dynamic resolution change frames:
>
> ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -v
> verbose -i ./test2360_1672_4980.ivf -pix_fmt p010le -f rawvideo -vsync
> passthrough -vframes 6 -y out.yuv
>
> Move update_context_from_thread from ff_thread_decode_frame(main thread)
> to frame_worker_thread(child thread), update the context in child thread
> right after the context was updated to avoid the async issue.
>
> Signed-off-by: Linjie Fu <linjie.fu@intel.com>
> ---
> Request for Comments, not quite familiar with the constraints.
> Thanks in advance.
>
>  libavcodec/internal.h      |   5 ++
>  libavcodec/pthread_frame.c | 164 ++++++++++++++++++++++++---------------------
>  2 files changed, 91 insertions(+), 78 deletions(-)
>
> diff --git a/libavcodec/internal.h b/libavcodec/internal.h
> index 5096ffa..9f4ed0b 100644
> --- a/libavcodec/internal.h
> +++ b/libavcodec/internal.h
> @@ -162,6 +162,11 @@ typedef struct AVCodecInternal {
>
>      void *thread_ctx;
>
> +    /**
> +     * Main thread AVCodecContext pointer
> +     */
> +    void *main_thread;
> +
>      DecodeSimpleContext ds;
>      DecodeFilterContext filter;
>
> diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c
> index 36ac0ac..be42e98 100644
> --- a/libavcodec/pthread_frame.c
> +++ b/libavcodec/pthread_frame.c
> @@ -159,82 +159,6 @@ static void async_unlock(FrameThreadContext *fctx)
>  }
>
>  /**
> - * Codec worker thread.
> - *
> - * Automatically calls ff_thread_finish_setup() if the codec does
> - * not provide an update_thread_context method, or if the codec returns
> - * before calling it.
> - */
> -static attribute_align_arg void *frame_worker_thread(void *arg)
> -{
> -    PerThreadContext *p = arg;
> -    AVCodecContext *avctx = p->avctx;
> -    const AVCodec *codec = avctx->codec;
> -
> -    pthread_mutex_lock(&p->mutex);
> -    while (1) {
> -        while (atomic_load(&p->state) == STATE_INPUT_READY && !p->die)
> -            pthread_cond_wait(&p->input_cond, &p->mutex);
> -
> -        if (p->die) break;
> -
> -        if (!codec->update_thread_context && THREAD_SAFE_CALLBACKS(avctx))
> -            ff_thread_finish_setup(avctx);
> -
> -        /* If a decoder supports hwaccel, then it must call ff_get_format().
> -         * Since that call must happen before ff_thread_finish_setup(), the
> -         * decoder is required to implement update_thread_context() and call
> -         * ff_thread_finish_setup() manually. Therefore the above
> -         * ff_thread_finish_setup() call did not happen and hwaccel_serializing
> -         * cannot be true here. */
> -        av_assert0(!p->hwaccel_serializing);
> -
> -        /* if the previous thread uses hwaccel then we take the lock to ensure
> -         * the threads don't run concurrently */
> -        if (avctx->hwaccel) {
> -            pthread_mutex_lock(&p->parent->hwaccel_mutex);
> -            p->hwaccel_serializing = 1;
> -        }
> -
> -        av_frame_unref(p->frame);
> -        p->got_frame = 0;
> -        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)
> -                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);
> -        }
> -
> -        if (atomic_load(&p->state) == STATE_SETTING_UP)
> -            ff_thread_finish_setup(avctx);
> -
> -        if (p->hwaccel_serializing) {
> -            p->hwaccel_serializing = 0;
> -            pthread_mutex_unlock(&p->parent->hwaccel_mutex);
> -        }
> -
> -        if (p->async_serializing) {
> -            p->async_serializing = 0;
> -
> -            async_unlock(p->parent);
> -        }
> -
> -        pthread_mutex_lock(&p->progress_mutex);
> -
> -        atomic_store(&p->state, STATE_INPUT_READY);
> -
> -        pthread_cond_broadcast(&p->progress_cond);
> -        pthread_cond_signal(&p->output_cond);
> -        pthread_mutex_unlock(&p->progress_mutex);
> -    }
> -    pthread_mutex_unlock(&p->mutex);
> -
> -    return NULL;
> -}
> -
> -/**
>   * Update the next thread's AVCodecContext with values from the reference thread's context.
>   *
>   * @param dst The destination context.
> @@ -313,6 +237,89 @@ FF_ENABLE_DEPRECATION_WARNINGS
>      return err;
>  }
>
> +
> +
> +/**
> + * Codec worker thread.
> + *
> + * Automatically calls ff_thread_finish_setup() if the codec does
> + * not provide an update_thread_context method, or if the codec returns
> + * before calling it.
> + */
> +static attribute_align_arg void *frame_worker_thread(void *arg)
> +{
> +    PerThreadContext *p = arg;
> +    AVCodecContext *avctx = p->avctx;
> +    const AVCodec *codec = avctx->codec;
> +
> +    pthread_mutex_lock(&p->mutex);
> +    while (1) {
> +        while (atomic_load(&p->state) == STATE_INPUT_READY && !p->die)
> +            pthread_cond_wait(&p->input_cond, &p->mutex);
> +
> +        if (p->die) break;
> +
> +        if (!codec->update_thread_context && THREAD_SAFE_CALLBACKS(avctx))
> +            ff_thread_finish_setup(avctx);
> +
> +        /* If a decoder supports hwaccel, then it must call ff_get_format().
> +         * Since that call must happen before ff_thread_finish_setup(), the
> +         * decoder is required to implement update_thread_context() and call
> +         * ff_thread_finish_setup() manually. Therefore the above
> +         * ff_thread_finish_setup() call did not happen and hwaccel_serializing
> +         * cannot be true here. */
> +        av_assert0(!p->hwaccel_serializing);
> +
> +        /* if the previous thread uses hwaccel then we take the lock to ensure
> +         * the threads don't run concurrently */
> +        if (avctx->hwaccel) {
> +            pthread_mutex_lock(&p->parent->hwaccel_mutex);
> +            p->hwaccel_serializing = 1;
> +        }
> +
> +        av_frame_unref(p->frame);
> +        p->got_frame = 0;
> +        p->result = codec->decode(avctx, p->frame, &p->got_frame, &p->avpkt);
> +
> +        if (p->avctx->internal->main_thread)
> +            update_context_from_thread((AVCodecContext *)p->avctx->internal->main_thread,
> +                                                                            p->avctx, 1);
> +
> +
> +        if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) {
> +            if (avctx->internal->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);
> +        }
> +
> +        if (atomic_load(&p->state) == STATE_SETTING_UP)
> +            ff_thread_finish_setup(avctx);
> +
> +        if (p->hwaccel_serializing) {
> +            p->hwaccel_serializing = 0;
> +            pthread_mutex_unlock(&p->parent->hwaccel_mutex);
> +        }
> +
> +        if (p->async_serializing) {
> +            p->async_serializing = 0;
> +
> +            async_unlock(p->parent);
> +        }
> +
> +        pthread_mutex_lock(&p->progress_mutex);
> +
> +        atomic_store(&p->state, STATE_INPUT_READY);
> +
> +        pthread_cond_broadcast(&p->progress_cond);
> +        pthread_cond_signal(&p->output_cond);
> +        pthread_mutex_unlock(&p->progress_mutex);
> +    }
> +    pthread_mutex_unlock(&p->mutex);
> +
> +    return NULL;
> +}
> +

Please don't move the entire function around, as it makes it
impossible to spot any differences in the function itself. Instead,
use a forward declaration of whichever function you want to call, so
the diff is more clear.

- Hendrik
Fu, Linjie June 26, 2019, 8:28 a.m. UTC | #2
> -----Original Message-----

> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf

> Of Hendrik Leppkes

> Sent: Wednesday, June 26, 2019 15:12

> To: FFmpeg development discussions and patches <ffmpeg-

> devel@ffmpeg.org>

> Subject: Re: [FFmpeg-devel] [PATCH, RFC] lavc/phtread_frame: update

> context in child thread in multi-thread mode

> 

> On Wed, Jun 26, 2019 at 9:06 AM Linjie Fu <linjie.fu@intel.com> wrote:

> >

> > Currently in ff_thread_decode_frame, context is updated from child

> thread

> > to main thread, and main thread releases the context in avcodec_close()

> > when decode finishes.

> >

> > However, when resolution/format change in vp9, ff_get_format was called,

> > and hwaccel_uninit() and hwaccel_init will be used to destroy and re-

> create

> > the context. Due to the async between main-thread and child-thread,

> > main-thread updated its context from child earlier than the context was

> > refreshed in child-thread. And it will lead to:

> >     1. memory leak in child-thread.

> >     2. double free in main-thread while calling avcodec_close().

> >

> > Can be reproduced with a resolution change case in vp9, and use -vframes

> > to terminate the decode between the dynamic resolution change frames:

> >

> > ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -v

> > verbose -i ./test2360_1672_4980.ivf -pix_fmt p010le -f rawvideo -vsync

> > passthrough -vframes 6 -y out.yuv

> >

> > Move update_context_from_thread from ff_thread_decode_frame(main

> thread)

> > to frame_worker_thread(child thread), update the context in child thread

> > right after the context was updated to avoid the async issue.

> >

> > Signed-off-by: Linjie Fu <linjie.fu@intel.com>

> > ---

> > Request for Comments, not quite familiar with the constraints.

> > Thanks in advance.

> >

> >  libavcodec/internal.h      |   5 ++

> >  libavcodec/pthread_frame.c | 164 ++++++++++++++++++++++++----------

> -----------

> >  2 files changed, 91 insertions(+), 78 deletions(-)

> >

> > diff --git a/libavcodec/internal.h b/libavcodec/internal.h

> > index 5096ffa..9f4ed0b 100644

> > --- a/libavcodec/internal.h

> > +++ b/libavcodec/internal.h

> > @@ -162,6 +162,11 @@ typedef struct AVCodecInternal {

> >

> >      void *thread_ctx;

> >

> > +    /**

> > +     * Main thread AVCodecContext pointer

> > +     */

> > +    void *main_thread;

> > +

> >      DecodeSimpleContext ds;

> >      DecodeFilterContext filter;

> >

> > diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c

> > index 36ac0ac..be42e98 100644

> > --- a/libavcodec/pthread_frame.c

> > +++ b/libavcodec/pthread_frame.c

> > @@ -159,82 +159,6 @@ static void async_unlock(FrameThreadContext

> *fctx)

> >  }

> >

> >  /**

> > - * Codec worker thread.

> > - *

> > - * Automatically calls ff_thread_finish_setup() if the codec does

> > - * not provide an update_thread_context method, or if the codec returns

> > - * before calling it.

> > - */

> > -static attribute_align_arg void *frame_worker_thread(void *arg)

> > -{

> > -    PerThreadContext *p = arg;

> > -    AVCodecContext *avctx = p->avctx;

> > -    const AVCodec *codec = avctx->codec;

> > -

> > -    pthread_mutex_lock(&p->mutex);

> > -    while (1) {

> > -        while (atomic_load(&p->state) == STATE_INPUT_READY && !p->die)

> > -            pthread_cond_wait(&p->input_cond, &p->mutex);

> > -

> > -        if (p->die) break;

> > -

> > -        if (!codec->update_thread_context &&

> THREAD_SAFE_CALLBACKS(avctx))

> > -            ff_thread_finish_setup(avctx);

> > -

> > -        /* If a decoder supports hwaccel, then it must call ff_get_format().

> > -         * Since that call must happen before ff_thread_finish_setup(), the

> > -         * decoder is required to implement update_thread_context() and call

> > -         * ff_thread_finish_setup() manually. Therefore the above

> > -         * ff_thread_finish_setup() call did not happen and hwaccel_serializing

> > -         * cannot be true here. */

> > -        av_assert0(!p->hwaccel_serializing);

> > -

> > -        /* if the previous thread uses hwaccel then we take the lock to ensure

> > -         * the threads don't run concurrently */

> > -        if (avctx->hwaccel) {

> > -            pthread_mutex_lock(&p->parent->hwaccel_mutex);

> > -            p->hwaccel_serializing = 1;

> > -        }

> > -

> > -        av_frame_unref(p->frame);

> > -        p->got_frame = 0;

> > -        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)

> > -                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);

> > -        }

> > -

> > -        if (atomic_load(&p->state) == STATE_SETTING_UP)

> > -            ff_thread_finish_setup(avctx);

> > -

> > -        if (p->hwaccel_serializing) {

> > -            p->hwaccel_serializing = 0;

> > -            pthread_mutex_unlock(&p->parent->hwaccel_mutex);

> > -        }

> > -

> > -        if (p->async_serializing) {

> > -            p->async_serializing = 0;

> > -

> > -            async_unlock(p->parent);

> > -        }

> > -

> > -        pthread_mutex_lock(&p->progress_mutex);

> > -

> > -        atomic_store(&p->state, STATE_INPUT_READY);

> > -

> > -        pthread_cond_broadcast(&p->progress_cond);

> > -        pthread_cond_signal(&p->output_cond);

> > -        pthread_mutex_unlock(&p->progress_mutex);

> > -    }

> > -    pthread_mutex_unlock(&p->mutex);

> > -

> > -    return NULL;

> > -}

> > -

> > -/**

> >   * Update the next thread's AVCodecContext with values from the

> reference thread's context.

> >   *

> >   * @param dst The destination context.

> > @@ -313,6 +237,89 @@ FF_ENABLE_DEPRECATION_WARNINGS

> >      return err;

> >  }

> >

> > +

> > +

> > +/**

> > + * Codec worker thread.

> > + *

> > + * Automatically calls ff_thread_finish_setup() if the codec does

> > + * not provide an update_thread_context method, or if the codec returns

> > + * before calling it.

> > + */

> > +static attribute_align_arg void *frame_worker_thread(void *arg)

> > +{

> > +    PerThreadContext *p = arg;

> > +    AVCodecContext *avctx = p->avctx;

> > +    const AVCodec *codec = avctx->codec;

> > +

> > +    pthread_mutex_lock(&p->mutex);

> > +    while (1) {

> > +        while (atomic_load(&p->state) == STATE_INPUT_READY && !p->die)

> > +            pthread_cond_wait(&p->input_cond, &p->mutex);

> > +

> > +        if (p->die) break;

> > +

> > +        if (!codec->update_thread_context &&

> THREAD_SAFE_CALLBACKS(avctx))

> > +            ff_thread_finish_setup(avctx);

> > +

> > +        /* If a decoder supports hwaccel, then it must call ff_get_format().

> > +         * Since that call must happen before ff_thread_finish_setup(), the

> > +         * decoder is required to implement update_thread_context() and

> call

> > +         * ff_thread_finish_setup() manually. Therefore the above

> > +         * ff_thread_finish_setup() call did not happen and

> hwaccel_serializing

> > +         * cannot be true here. */

> > +        av_assert0(!p->hwaccel_serializing);

> > +

> > +        /* if the previous thread uses hwaccel then we take the lock to

> ensure

> > +         * the threads don't run concurrently */

> > +        if (avctx->hwaccel) {

> > +            pthread_mutex_lock(&p->parent->hwaccel_mutex);

> > +            p->hwaccel_serializing = 1;

> > +        }

> > +

> > +        av_frame_unref(p->frame);

> > +        p->got_frame = 0;

> > +        p->result = codec->decode(avctx, p->frame, &p->got_frame, &p-

> >avpkt);

> > +

> > +        if (p->avctx->internal->main_thread)

> > +            update_context_from_thread((AVCodecContext *)p->avctx-

> >internal->main_thread,

> > +                                                                            p->avctx, 1);

> > +

> > +

> > +        if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) {

> > +            if (avctx->internal->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);

> > +        }

> > +

> > +        if (atomic_load(&p->state) == STATE_SETTING_UP)

> > +            ff_thread_finish_setup(avctx);

> > +

> > +        if (p->hwaccel_serializing) {

> > +            p->hwaccel_serializing = 0;

> > +            pthread_mutex_unlock(&p->parent->hwaccel_mutex);

> > +        }

> > +

> > +        if (p->async_serializing) {

> > +            p->async_serializing = 0;

> > +

> > +            async_unlock(p->parent);

> > +        }

> > +

> > +        pthread_mutex_lock(&p->progress_mutex);

> > +

> > +        atomic_store(&p->state, STATE_INPUT_READY);

> > +

> > +        pthread_cond_broadcast(&p->progress_cond);

> > +        pthread_cond_signal(&p->output_cond);

> > +        pthread_mutex_unlock(&p->progress_mutex);

> > +    }

> > +    pthread_mutex_unlock(&p->mutex);

> > +:

> > +    return NULL;

> > +}

> > +

> 

> Please don't move the entire function around, as it makes it

> impossible to spot any differences in the function itself. Instead,

> use a forward declaration of whichever function you want to call, so

> the diff is more clear.

> 

Thanks, updated to make it easier for review.
diff mbox

Patch

diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 5096ffa..9f4ed0b 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -162,6 +162,11 @@  typedef struct AVCodecInternal {
 
     void *thread_ctx;
 
+    /**
+     * Main thread AVCodecContext pointer
+     */
+    void *main_thread;
+
     DecodeSimpleContext ds;
     DecodeFilterContext filter;
 
diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c
index 36ac0ac..be42e98 100644
--- a/libavcodec/pthread_frame.c
+++ b/libavcodec/pthread_frame.c
@@ -159,82 +159,6 @@  static void async_unlock(FrameThreadContext *fctx)
 }
 
 /**
- * Codec worker thread.
- *
- * Automatically calls ff_thread_finish_setup() if the codec does
- * not provide an update_thread_context method, or if the codec returns
- * before calling it.
- */
-static attribute_align_arg void *frame_worker_thread(void *arg)
-{
-    PerThreadContext *p = arg;
-    AVCodecContext *avctx = p->avctx;
-    const AVCodec *codec = avctx->codec;
-
-    pthread_mutex_lock(&p->mutex);
-    while (1) {
-        while (atomic_load(&p->state) == STATE_INPUT_READY && !p->die)
-            pthread_cond_wait(&p->input_cond, &p->mutex);
-
-        if (p->die) break;
-
-        if (!codec->update_thread_context && THREAD_SAFE_CALLBACKS(avctx))
-            ff_thread_finish_setup(avctx);
-
-        /* If a decoder supports hwaccel, then it must call ff_get_format().
-         * Since that call must happen before ff_thread_finish_setup(), the
-         * decoder is required to implement update_thread_context() and call
-         * ff_thread_finish_setup() manually. Therefore the above
-         * ff_thread_finish_setup() call did not happen and hwaccel_serializing
-         * cannot be true here. */
-        av_assert0(!p->hwaccel_serializing);
-
-        /* if the previous thread uses hwaccel then we take the lock to ensure
-         * the threads don't run concurrently */
-        if (avctx->hwaccel) {
-            pthread_mutex_lock(&p->parent->hwaccel_mutex);
-            p->hwaccel_serializing = 1;
-        }
-
-        av_frame_unref(p->frame);
-        p->got_frame = 0;
-        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)
-                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);
-        }
-
-        if (atomic_load(&p->state) == STATE_SETTING_UP)
-            ff_thread_finish_setup(avctx);
-
-        if (p->hwaccel_serializing) {
-            p->hwaccel_serializing = 0;
-            pthread_mutex_unlock(&p->parent->hwaccel_mutex);
-        }
-
-        if (p->async_serializing) {
-            p->async_serializing = 0;
-
-            async_unlock(p->parent);
-        }
-
-        pthread_mutex_lock(&p->progress_mutex);
-
-        atomic_store(&p->state, STATE_INPUT_READY);
-
-        pthread_cond_broadcast(&p->progress_cond);
-        pthread_cond_signal(&p->output_cond);
-        pthread_mutex_unlock(&p->progress_mutex);
-    }
-    pthread_mutex_unlock(&p->mutex);
-
-    return NULL;
-}
-
-/**
  * Update the next thread's AVCodecContext with values from the reference thread's context.
  *
  * @param dst The destination context.
@@ -313,6 +237,89 @@  FF_ENABLE_DEPRECATION_WARNINGS
     return err;
 }
 
+
+
+/**
+ * Codec worker thread.
+ *
+ * Automatically calls ff_thread_finish_setup() if the codec does
+ * not provide an update_thread_context method, or if the codec returns
+ * before calling it.
+ */
+static attribute_align_arg void *frame_worker_thread(void *arg)
+{
+    PerThreadContext *p = arg;
+    AVCodecContext *avctx = p->avctx;
+    const AVCodec *codec = avctx->codec;
+
+    pthread_mutex_lock(&p->mutex);
+    while (1) {
+        while (atomic_load(&p->state) == STATE_INPUT_READY && !p->die)
+            pthread_cond_wait(&p->input_cond, &p->mutex);
+
+        if (p->die) break;
+
+        if (!codec->update_thread_context && THREAD_SAFE_CALLBACKS(avctx))
+            ff_thread_finish_setup(avctx);
+
+        /* If a decoder supports hwaccel, then it must call ff_get_format().
+         * Since that call must happen before ff_thread_finish_setup(), the
+         * decoder is required to implement update_thread_context() and call
+         * ff_thread_finish_setup() manually. Therefore the above
+         * ff_thread_finish_setup() call did not happen and hwaccel_serializing
+         * cannot be true here. */
+        av_assert0(!p->hwaccel_serializing);
+
+        /* if the previous thread uses hwaccel then we take the lock to ensure
+         * the threads don't run concurrently */
+        if (avctx->hwaccel) {
+            pthread_mutex_lock(&p->parent->hwaccel_mutex);
+            p->hwaccel_serializing = 1;
+        }
+
+        av_frame_unref(p->frame);
+        p->got_frame = 0;
+        p->result = codec->decode(avctx, p->frame, &p->got_frame, &p->avpkt);
+
+        if (p->avctx->internal->main_thread)
+            update_context_from_thread((AVCodecContext *)p->avctx->internal->main_thread,
+                                                                            p->avctx, 1);
+
+
+        if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) {
+            if (avctx->internal->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);
+        }
+
+        if (atomic_load(&p->state) == STATE_SETTING_UP)
+            ff_thread_finish_setup(avctx);
+
+        if (p->hwaccel_serializing) {
+            p->hwaccel_serializing = 0;
+            pthread_mutex_unlock(&p->parent->hwaccel_mutex);
+        }
+
+        if (p->async_serializing) {
+            p->async_serializing = 0;
+
+            async_unlock(p->parent);
+        }
+
+        pthread_mutex_lock(&p->progress_mutex);
+
+        atomic_store(&p->state, STATE_INPUT_READY);
+
+        pthread_cond_broadcast(&p->progress_cond);
+        pthread_cond_signal(&p->output_cond);
+        pthread_mutex_unlock(&p->progress_mutex);
+    }
+    pthread_mutex_unlock(&p->mutex);
+
+    return NULL;
+}
+
 /**
  * Update the next thread's AVCodecContext with values set by the user.
  *
@@ -540,8 +547,6 @@  int ff_thread_decode_frame(AVCodecContext *avctx,
         if (finished >= avctx->thread_count) finished = 0;
     } while (!avpkt->size && !*got_picture_ptr && err >= 0 && finished != fctx->next_finished);
 
-    update_context_from_thread(avctx, p->avctx, 1);
-
     if (fctx->next_decoding >= avctx->thread_count) fctx->next_decoding = 0;
 
     fctx->next_finished = finished;
@@ -728,6 +733,8 @@  int ff_frame_thread_init(AVCodecContext *avctx)
     FrameThreadContext *fctx;
     int i, err = 0;
 
+    avctx->internal->main_thread = avctx;
+
     if (!thread_count) {
         int nb_cpus = av_cpu_count();
 #if FF_API_DEBUG_MV
@@ -800,6 +807,7 @@  int ff_frame_thread_init(AVCodecContext *avctx)
         *copy->internal = *src->internal;
         copy->internal->thread_ctx = p;
         copy->internal->last_pkt_props = &p->avpkt;
+        copy->internal->main_thread= avctx;
 
         if (!i) {
             src = copy;