On 2016-11-24 19:19:59 +0100, Anton Khirnov wrote:
> Only allow the decoding thread to run while the user is inside a lavc
> decode call (avcodec_send_packet/receive_frame).
> Hardware decoding APIs are often not thread-safe, so having the user
> access decoded hardware surfaces while the decoder is running in another
> thread can cause failures (this is mainly known to happen with DXVA2).

This looks a little extreme. How painful would it be to provide an 
option to share the mutex with the calling process?

> ---
>  libavcodec/pthread_frame.c | 58 
> +++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 52 insertions(+), 6 deletions(-)
> 
> diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c
> index 2736a81..430854f 100644
> --- a/libavcodec/pthread_frame.c
> +++ b/libavcodec/pthread_frame.c
> @@ -99,6 +99,8 @@ typedef struct PerThreadContext {
>      int      requested_flags;       ///< flags passed to get_buffer() for 
> requested_frame
>  
>      int die;                       ///< Set when the thread should exit.
> +
> +    int hwaccel_serializing;
>  } PerThreadContext;
>  
>  /**
> @@ -110,6 +112,14 @@ typedef struct FrameThreadContext {
>  
>      pthread_mutex_t buffer_mutex;  ///< Mutex used to protect 
> get/release_buffer().
>  
> +    /**
> +     * This lock is used for making sure hwaccel decoding does not run
> +     * concurrently with the calling thread. It is held by the calling thread
> +     * most of the time and released only while inside 
> ff_thread_decode_frame(),
> +     * at which time the worker threads are allowed to progress past 
> finish_setup().
> +     */
> +    pthread_mutex_t hwaccel_mutex;
> +
>      int next_decoding;             ///< The next context to submit a packet 
> to.
>      int next_finished;             ///< The next context to return output 
> from.
>  
> @@ -163,6 +173,11 @@ static attribute_align_arg void 
> *frame_worker_thread(void *arg)
>          if (atomic_load(&p->state) == STATE_SETTING_UP)
>              ff_thread_finish_setup(avctx);
>  
> +        if (p->hwaccel_serializing) {
> +            pthread_mutex_unlock(&p->parent->hwaccel_mutex);
> +            p->hwaccel_serializing = 0;

although protected by other mutexes it would make sense to protect 
hwaccel_serializing explicitly by hwaccel_mutex

> +        }
> +
>          atomic_store(&p->state, STATE_INPUT_READY);
>  
>          pthread_mutex_lock(&p->progress_mutex);
> @@ -386,7 +401,11 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
>      FrameThreadContext *fctx = avctx->internal->thread_ctx;
>      int finished = fctx->next_finished;
>      PerThreadContext *p;
> -    int err;
> +    int err, ret;
> +
> +    /* release the hwaccel lock, permitting blocked hwaccel threads to go
> +     * forward while we are inside this function */
> +    pthread_mutex_unlock(&fctx->hwaccel_mutex);
>  
>      /*
>       * Submit a packet to the next decoding thread.
> @@ -394,9 +413,11 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
>  
>      p = &fctx->threads[fctx->next_decoding];
>      err = update_context_from_user(p->avctx, avctx);
> -    if (err) return err;
> +    if (err)
> +        goto finish;
>      err = submit_packet(p, avpkt);
> -    if (err) return err;
> +    if (err)
> +        goto finish;
>  
>      /*
>       * If we're still receiving the initial packets, don't return a frame.
> @@ -406,8 +427,10 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
>          if (fctx->next_decoding >= (avctx->thread_count-1)) fctx->delaying = 
> 0;
>  
>          *got_picture_ptr=0;
> -        if (avpkt->size)
> -            return avpkt->size;
> +        if (avpkt->size) {
> +            ret = avpkt->size;
> +            goto finish;
> +        }
>      }
>  
>      /*
> @@ -448,8 +471,14 @@ int ff_thread_decode_frame(AVCodecContext *avctx,
>  
>      fctx->next_finished = finished;
>  
> +    ret = (p->result >= 0) ? avpkt->size : p->result;
> +finish:
> +    pthread_mutex_lock(&fctx->hwaccel_mutex);
> +    if (err < 0)
> +        return err;
> +
>      /* return the size of the consumed packet if no error occurred */
> -    return (p->result >= 0) ? avpkt->size : p->result;
> +    return ret;
>  }
>  
>  void ff_thread_report_progress(ThreadFrame *f, int n, int field)
> @@ -505,6 +534,11 @@ void ff_thread_finish_setup(AVCodecContext *avctx) {
>  
>      pthread_cond_broadcast(&p->progress_cond);
>      pthread_mutex_unlock(&p->progress_mutex);
> +
> +    if (avctx->hwaccel) {
> +        pthread_mutex_lock(&p->parent->hwaccel_mutex);
> +        p->hwaccel_serializing = 1;
> +    }

I would have expected the lock before the codecs decode call. Since we 
don't call finish_setup until after the frame is decode this can't 
guarantee serialized access to the hw decoding library.

Janne
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to