diff mbox

[FFmpeg-devel] libavcodec/vp8dec: fix the multi-thread HWAccel decode error

Message ID 1533798542-2494-1-git-send-email-mypopydev@gmail.com
State Superseded
Headers show

Commit Message

Jun Zhao Aug. 9, 2018, 7:09 a.m. UTC
the root cause is update_dimentions call get_pixel_format will
trigger the hwaccel_uninit/hwaccel_init , in current context,
there are 3 situations in the update_dimentions():
1. First time calling. No matter single thread or multithread,
   get_pixel_format() should be called after dimentions were
   set;
2. Dimention changed at the runtime. Dimention need to be
   updated when macroblocks_base is already allocated,
   get_pixel_format() should be called to recreate new frames
   according to updated dimention;
3. Multithread first time calling. After decoder init, the
   other threads will call update_dimentions() at first time
   to allocate macroblocks_base and set dimentions.
   But get_pixel_format() is shouldn't be called due to low
   level frames and context are already created.
In this fix, we only call update_dimentions as need.

Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>
Reviewed-by: Jun, Zhao <jun.zhao@intel.com>
---
 libavcodec/vp8.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)

Comments

Xiang, Haihao Sept. 21, 2018, 4:07 a.m. UTC | #1
On Thu, 2018-08-09 at 15:09 +0800, Jun Zhao wrote:
> the root cause is update_dimentions call get_pixel_format will


Typo? s/dimentions/dimensions. And I see the same typo a few times in this log.

> trigger the hwaccel_uninit/hwaccel_init , in current context,


What is the decode error? Could you document the error a little or a link to the
issue?

> there are 3 situations in the update_dimentions():

> 1. First time calling. No matter single thread or multithread,

>    get_pixel_format() should be called after dimentions were

>    set;

> 2. Dimention changed at the runtime. Dimention need to be

>    updated when macroblocks_base is already allocated,


According to the code, ff_set_dimensions is called to overwrite previously setup
dimensions when height is changed no matter  
s->macroblocks_base is allocated or not. I think the HW frames and context
should be re-created as well for this case 

But I am curious why height and width are treated differently in this condition:

    if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||
(height+15)/16 != s->mb_height) && s->macroblocks_base ||
        height != s->avctx->height) {
       ...
       ff_set_dimensions(...);
}

The condition for updating dimensions is simple in VP9:

   if (!(s->pix_fmt == s->gf_fmt && w == s->w && h == s->h)) {
       ff_set_dimensions(...);
       ...
   }

>    get_pixel_format() should be called to recreate new frames

>    according to updated dimention;

> 3. Multithread first time calling. After decoder init, the

>    other threads will call update_dimentions() at first time

>    to allocate macroblocks_base and set dimentions.

>    But get_pixel_format() is shouldn't be called due to low

>    level frames and context are already created.

> In this fix, we only call update_dimentions as need.

> 

> Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>

> Reviewed-by: Jun, Zhao <jun.zhao@intel.com>

> ---

>  libavcodec/vp8.c |    7 +++++--

>  1 files changed, 5 insertions(+), 2 deletions(-)

> 

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

> index 3adfeac..18d1ada 100644

> --- a/libavcodec/vp8.c

> +++ b/libavcodec/vp8.c

> @@ -187,7 +187,7 @@ static av_always_inline

>  int update_dimensions(VP8Context *s, int width, int height, int is_vp7)

>  {

>      AVCodecContext *avctx = s->avctx;

> -    int i, ret;

> +    int i, ret, dim_reset = 0;

>  

>      if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||

> (height+15)/16 != s->mb_height) && s->macroblocks_base ||

>          height != s->avctx->height) {

> @@ -196,9 +196,12 @@ int update_dimensions(VP8Context *s, int width, int

> height, int is_vp7)

>          ret = ff_set_dimensions(s->avctx, width, height);

>          if (ret < 0)

>              return ret;

> +

> +        dim_reset = (s->macroblocks_base != NULL);



>      }

>  

> -    if (!s->actually_webp && !is_vp7) {

> +    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&

> +         !s->actually_webp && !is_vp7) {

>          s->pix_fmt = get_pixel_format(s);

>          if (s->pix_fmt < 0)

>              return AVERROR(EINVAL);
Jun Zhao Sept. 21, 2018, 8:05 a.m. UTC | #2
On Fri, Sep 21, 2018 at 12:08 PM Xiang, Haihao <haihao.xiang@intel.com> wrote:
>
> On Thu, 2018-08-09 at 15:09 +0800, Jun Zhao wrote:
> > the root cause is update_dimentions call get_pixel_format will
>
> Typo? s/dimentions/dimensions. And I see the same typo a few times in this log.
>
Ha, a good catch.

> > trigger the hwaccel_uninit/hwaccel_init , in current context,
>
> What is the decode error? Could you document the error a little or a link to the
> issue?
>
In mutil-thread decoding case with media-driver, Vp8 HWaccel decoder
will carsh in some case.

> > there are 3 situations in the update_dimentions():
> > 1. First time calling. No matter single thread or multithread,
> >    get_pixel_format() should be called after dimentions were
> >    set;
> > 2. Dimention changed at the runtime. Dimention need to be
> >    updated when macroblocks_base is already allocated,
>
> According to the code, ff_set_dimensions is called to overwrite previously setup
> dimensions when height is changed no matter
> s->macroblocks_base is allocated or not. I think the HW frames and context
> should be re-created as well for this case
>
> But I am curious why height and width are treated differently in this condition:
>
>     if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||
> (height+15)/16 != s->mb_height) && s->macroblocks_base ||
>         height != s->avctx->height) {
>        ...
>        ff_set_dimensions(...);
> }
>
> The condition for updating dimensions is simple in VP9:
Now I didn't test VP9 decoder in this case
>
>    if (!(s->pix_fmt == s->gf_fmt && w == s->w && h == s->h)) {
>        ff_set_dimensions(...);
>        ...
>    }
>
> >    get_pixel_format() should be called to recreate new frames
> >    according to updated dimention;
> > 3. Multithread first time calling. After decoder init, the
> >    other threads will call update_dimentions() at first time
> >    to allocate macroblocks_base and set dimentions.
> >    But get_pixel_format() is shouldn't be called due to low
> >    level frames and context are already created.
> > In this fix, we only call update_dimentions as need.
> >
> > Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>
> > Reviewed-by: Jun, Zhao <jun.zhao@intel.com>
> > ---
> >  libavcodec/vp8.c |    7 +++++--
> >  1 files changed, 5 insertions(+), 2 deletions(-)
> >
> > diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c
> > index 3adfeac..18d1ada 100644
> > --- a/libavcodec/vp8.c
> > +++ b/libavcodec/vp8.c
> > @@ -187,7 +187,7 @@ static av_always_inline
> >  int update_dimensions(VP8Context *s, int width, int height, int is_vp7)
> >  {
> >      AVCodecContext *avctx = s->avctx;
> > -    int i, ret;
> > +    int i, ret, dim_reset = 0;
> >
> >      if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||
> > (height+15)/16 != s->mb_height) && s->macroblocks_base ||
> >          height != s->avctx->height) {
> > @@ -196,9 +196,12 @@ int update_dimensions(VP8Context *s, int width, int
> > height, int is_vp7)
> >          ret = ff_set_dimensions(s->avctx, width, height);
> >          if (ret < 0)
> >              return ret;
> > +
> > +        dim_reset = (s->macroblocks_base != NULL);
>
>
> >      }
> >
> > -    if (!s->actually_webp && !is_vp7) {
> > +    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&
> > +         !s->actually_webp && !is_vp7) {
> >          s->pix_fmt = get_pixel_format(s);
> >          if (s->pix_fmt < 0)
> >              return AVERROR(EINVAL);
Xiang, Haihao Sept. 25, 2018, 4:14 a.m. UTC | #3
On Fri, 2018-09-21 at 16:05 +0800, mypopy@gmail.com wrote:
> On Fri, Sep 21, 2018 at 12:08 PM Xiang, Haihao <haihao.xiang@intel.com> wrote:

> > 

> > On Thu, 2018-08-09 at 15:09 +0800, Jun Zhao wrote:

> > > the root cause is update_dimentions call get_pixel_format will

> > 

> > Typo? s/dimentions/dimensions. And I see the same typo a few times in this

> > log.

> > 

> 

> Ha, a good catch.

> 

> > > trigger the hwaccel_uninit/hwaccel_init , in current context,

> > 

> > What is the decode error? Could you document the error a little or a link to

> > the

> > issue?

> > 

> 

> In mutil-thread decoding case with media-driver, Vp8 HWaccel decoder

> will carsh in some case.

> 

> > > there are 3 situations in the update_dimentions():

> > > 1. First time calling. No matter single thread or multithread,

> > >    get_pixel_format() should be called after dimentions were

> > >    set;

> > > 2. Dimention changed at the runtime. Dimention need to be

> > >    updated when macroblocks_base is already allocated,

> > 

> > According to the code, ff_set_dimensions is called to overwrite previously

> > setup

> > dimensions when height is changed no matter

> > s->macroblocks_base is allocated or not. I think the HW frames and context

> > should be re-created as well for this case

> > 

> > But I am curious why height and width are treated differently in this

> > condition:

> > 

> >     if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||

> > (height+15)/16 != s->mb_height) && s->macroblocks_base ||

> >         height != s->avctx->height) {

> >        ...

> >        ff_set_dimensions(...);

> > }

> > 

> > The condition for updating dimensions is simple in VP9:

> 

> Now I didn't test VP9 decoder in this case


Yes, this patch is for VP8 decoder only. What I mean is that ff_set_dimensions()
is called too when {width, height} are changed without allocated
macroblocks_base. So dim_reset should be 1 instead of (s->macroblocks_base !=
NULL) in the patch.

> > 

> >    if (!(s->pix_fmt == s->gf_fmt && w == s->w && h == s->h)) {

> >        ff_set_dimensions(...);

> >        ...

> >    }

> > 

> > >    get_pixel_format() should be called to recreate new frames

> > >    according to updated dimention;

> > > 3. Multithread first time calling. After decoder init, the

> > >    other threads will call update_dimentions() at first time

> > >    to allocate macroblocks_base and set dimentions.

> > >    But get_pixel_format() is shouldn't be called due to low

> > >    level frames and context are already created.

> > > In this fix, we only call update_dimentions as need.

> > > 

> > > Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>

> > > Reviewed-by: Jun, Zhao <jun.zhao@intel.com>

> > > ---

> > >  libavcodec/vp8.c |    7 +++++--

> > >  1 files changed, 5 insertions(+), 2 deletions(-)

> > > 

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

> > > index 3adfeac..18d1ada 100644

> > > --- a/libavcodec/vp8.c

> > > +++ b/libavcodec/vp8.c

> > > @@ -187,7 +187,7 @@ static av_always_inline

> > >  int update_dimensions(VP8Context *s, int width, int height, int is_vp7)

> > >  {

> > >      AVCodecContext *avctx = s->avctx;

> > > -    int i, ret;

> > > +    int i, ret, dim_reset = 0;

> > > 

> > >      if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||

> > > (height+15)/16 != s->mb_height) && s->macroblocks_base ||

> > >          height != s->avctx->height) {

> > > @@ -196,9 +196,12 @@ int update_dimensions(VP8Context *s, int width, int

> > > height, int is_vp7)

> > >          ret = ff_set_dimensions(s->avctx, width, height);

> > >          if (ret < 0)

> > >              return ret;

> > > +

> > > +        dim_reset = (s->macroblocks_base != NULL);

> > 

> > 

> > >      }

> > > 

> > > -    if (!s->actually_webp && !is_vp7) {

> > > +    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&

> > > +         !s->actually_webp && !is_vp7) {

> > >          s->pix_fmt = get_pixel_format(s);

> > >          if (s->pix_fmt < 0)

> > >              return AVERROR(EINVAL);

> 

> _______________________________________________

> ffmpeg-devel mailing list

> ffmpeg-devel@ffmpeg.org

> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Zhong Li March 6, 2019, 7:01 a.m. UTC | #4
> > >     if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||

> > > (height+15)/16 != s->mb_height) && s->macroblocks_base ||

> > >         height != s->avctx->height) {

> > >        ...

> > >        ff_set_dimensions(...);

> > > }

> > >

> > > The condition for updating dimensions is simple in VP9:

> >

> > Now I didn't test VP9 decoder in this case

> 

> Yes, this patch is for VP8 decoder only. What I mean is that

> ff_set_dimensions()

> is called too when {width, height} are changed without allocated

> macroblocks_base. So dim_reset should be 1 instead of

> (s->macroblocks_base !=

> NULL) in the patch.


Agree dim-reset should be 1, and this comment should be addressed.
Carl Eugen Hoyos March 6, 2019, 7:48 a.m. UTC | #5
2018-08-09 9:09 GMT+02:00, Jun Zhao <mypopydev@gmail.com>:
> the root cause is update_dimentions call get_pixel_format will
> trigger the hwaccel_uninit/hwaccel_init , in current context,
> there are 3 situations in the update_dimentions():
> 1. First time calling. No matter single thread or multithread,
>    get_pixel_format() should be called after dimentions were
>    set;
> 2. Dimention changed at the runtime. Dimention need to be
>    updated when macroblocks_base is already allocated,
>    get_pixel_format() should be called to recreate new frames
>    according to updated dimention;
> 3. Multithread first time calling. After decoder init, the
>    other threads will call update_dimentions() at first time
>    to allocate macroblocks_base and set dimentions.
>    But get_pixel_format() is shouldn't be called due to low
>    level frames and context are already created.
> In this fix, we only call update_dimentions as need.
>
> Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>
> Reviewed-by: Jun, Zhao <jun.zhao@intel.com>
> ---
>  libavcodec/vp8.c |    7 +++++--
>  1 files changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c
> index 3adfeac..18d1ada 100644
> --- a/libavcodec/vp8.c
> +++ b/libavcodec/vp8.c
> @@ -187,7 +187,7 @@ static av_always_inline
>  int update_dimensions(VP8Context *s, int width, int height, int is_vp7)
>  {
>      AVCodecContext *avctx = s->avctx;
> -    int i, ret;
> +    int i, ret, dim_reset = 0;
>
>      if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||
> (height+15)/16 != s->mb_height) && s->macroblocks_base ||
>          height != s->avctx->height) {
> @@ -196,9 +196,12 @@ int update_dimensions(VP8Context *s, int width, int
> height, int is_vp7)
>          ret = ff_set_dimensions(s->avctx, width, height);
>          if (ret < 0)
>              return ret;
> +
> +        dim_reset = (s->macroblocks_base != NULL);
>      }
>
> -    if (!s->actually_webp && !is_vp7) {
> +    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&
> +         !s->actually_webp && !is_vp7) {

Why is the new variable dim_reset needed?
Wouldn't the patch be simpler if you used s->macroblocks_base
here?

Carl Eugen
Shaofei Wang March 6, 2019, 8:25 a.m. UTC | #6
> -----Original Message-----

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

> Carl Eugen Hoyos

> Sent: Wednesday, March 6, 2019 3:49 PM

> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>

> Subject: Re: [FFmpeg-devel] [PATCH] libavcodec/vp8dec: fix the multi-thread

> HWAccel decode error

> 

> 2018-08-09 9:09 GMT+02:00, Jun Zhao <mypopydev@gmail.com>:

> > the root cause is update_dimentions call get_pixel_format will trigger

> > the hwaccel_uninit/hwaccel_init , in current context, there are 3

> > situations in the update_dimentions():

> > 1. First time calling. No matter single thread or multithread,

> >    get_pixel_format() should be called after dimentions were

> >    set;

> > 2. Dimention changed at the runtime. Dimention need to be

> >    updated when macroblocks_base is already allocated,

> >    get_pixel_format() should be called to recreate new frames

> >    according to updated dimention;

> > 3. Multithread first time calling. After decoder init, the

> >    other threads will call update_dimentions() at first time

> >    to allocate macroblocks_base and set dimentions.

> >    But get_pixel_format() is shouldn't be called due to low

> >    level frames and context are already created.

> > In this fix, we only call update_dimentions as need.

> >

> > Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>

> > Reviewed-by: Jun, Zhao <jun.zhao@intel.com>

> > ---

> >  libavcodec/vp8.c |    7 +++++--

> >  1 files changed, 5 insertions(+), 2 deletions(-)

> >

> > diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c index

> > 3adfeac..18d1ada 100644

> > --- a/libavcodec/vp8.c

> > +++ b/libavcodec/vp8.c

> > @@ -187,7 +187,7 @@ static av_always_inline  int

> > update_dimensions(VP8Context *s, int width, int height, int is_vp7)  {

> >      AVCodecContext *avctx = s->avctx;

> > -    int i, ret;

> > +    int i, ret, dim_reset = 0;

> >

> >      if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||

> > (height+15)/16 != s->mb_height) && s->macroblocks_base ||

> >          height != s->avctx->height) { @@ -196,9 +196,12 @@ int

> > update_dimensions(VP8Context *s, int width, int height, int is_vp7)

> >          ret = ff_set_dimensions(s->avctx, width, height);

> >          if (ret < 0)

> >              return ret;

> > +

> > +        dim_reset = (s->macroblocks_base != NULL);

> >      }

> >

> > -    if (!s->actually_webp && !is_vp7) {

> > +    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&

> > +         !s->actually_webp && !is_vp7) {

> 

> Why is the new variable dim_reset needed?

> Wouldn't the patch be simpler if you used s->macroblocks_base here?

Since dim_reset was set in the "if" segment, it equal to
(width != s->avctx->width || ((width+15)/16 != s->mb_width ||
(height+15)/16 != s->mb_height) || height != s->avctx->height) && s->macroblocks_base

> 

> Carl Eugen

> _______________________________________________

> ffmpeg-devel mailing list

> ffmpeg-devel@ffmpeg.org

> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Carl Eugen Hoyos March 6, 2019, 8:29 a.m. UTC | #7
2019-03-06 9:25 GMT+01:00, Wang, Shaofei <shaofei.wang@intel.com>:
>> -----Original Message-----
>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf Of
>> Carl Eugen Hoyos
>> Sent: Wednesday, March 6, 2019 3:49 PM
>> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org>
>> Subject: Re: [FFmpeg-devel] [PATCH] libavcodec/vp8dec: fix the
>> multi-thread
>> HWAccel decode error
>>
>> 2018-08-09 9:09 GMT+02:00, Jun Zhao <mypopydev@gmail.com>:
>> > the root cause is update_dimentions call get_pixel_format will trigger
>> > the hwaccel_uninit/hwaccel_init , in current context, there are 3
>> > situations in the update_dimentions():
>> > 1. First time calling. No matter single thread or multithread,
>> >    get_pixel_format() should be called after dimentions were
>> >    set;
>> > 2. Dimention changed at the runtime. Dimention need to be
>> >    updated when macroblocks_base is already allocated,
>> >    get_pixel_format() should be called to recreate new frames
>> >    according to updated dimention;
>> > 3. Multithread first time calling. After decoder init, the
>> >    other threads will call update_dimentions() at first time
>> >    to allocate macroblocks_base and set dimentions.
>> >    But get_pixel_format() is shouldn't be called due to low
>> >    level frames and context are already created.
>> > In this fix, we only call update_dimentions as need.
>> >
>> > Signed-off-by: Wang, Shaofei <shaofei.wang@intel.com>
>> > Reviewed-by: Jun, Zhao <jun.zhao@intel.com>
>> > ---
>> >  libavcodec/vp8.c |    7 +++++--
>> >  1 files changed, 5 insertions(+), 2 deletions(-)
>> >
>> > diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c index
>> > 3adfeac..18d1ada 100644
>> > --- a/libavcodec/vp8.c
>> > +++ b/libavcodec/vp8.c
>> > @@ -187,7 +187,7 @@ static av_always_inline  int
>> > update_dimensions(VP8Context *s, int width, int height, int is_vp7)  {
>> >      AVCodecContext *avctx = s->avctx;
>> > -    int i, ret;
>> > +    int i, ret, dim_reset = 0;
>> >
>> >      if (width  != s->avctx->width || ((width+15)/16 != s->mb_width ||
>> > (height+15)/16 != s->mb_height) && s->macroblocks_base ||
>> >          height != s->avctx->height) { @@ -196,9 +196,12 @@ int
>> > update_dimensions(VP8Context *s, int width, int height, int is_vp7)
>> >          ret = ff_set_dimensions(s->avctx, width, height);
>> >          if (ret < 0)
>> >              return ret;
>> > +
>> > +        dim_reset = (s->macroblocks_base != NULL);
>> >      }
>> >
>> > -    if (!s->actually_webp && !is_vp7) {
>> > +    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&
>> > +         !s->actually_webp && !is_vp7) {
>>
>> Why is the new variable dim_reset needed?
>> Wouldn't the patch be simpler if you used s->macroblocks_base here?
> Since dim_reset was set in the "if" segment, it equal to
> (width != s->avctx->width || ((width+15)/16 != s->mb_width ||
> (height+15)/16 != s->mb_height) || height != s->avctx->height) &&
> s->macroblocks_base

Thank you!

Carl Eugen
diff mbox

Patch

diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c
index 3adfeac..18d1ada 100644
--- a/libavcodec/vp8.c
+++ b/libavcodec/vp8.c
@@ -187,7 +187,7 @@  static av_always_inline
 int update_dimensions(VP8Context *s, int width, int height, int is_vp7)
 {
     AVCodecContext *avctx = s->avctx;
-    int i, ret;
+    int i, ret, dim_reset = 0;
 
     if (width  != s->avctx->width || ((width+15)/16 != s->mb_width || (height+15)/16 != s->mb_height) && s->macroblocks_base ||
         height != s->avctx->height) {
@@ -196,9 +196,12 @@  int update_dimensions(VP8Context *s, int width, int height, int is_vp7)
         ret = ff_set_dimensions(s->avctx, width, height);
         if (ret < 0)
             return ret;
+
+        dim_reset = (s->macroblocks_base != NULL);
     }
 
-    if (!s->actually_webp && !is_vp7) {
+    if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) &&
+         !s->actually_webp && !is_vp7) {
         s->pix_fmt = get_pixel_format(s);
         if (s->pix_fmt < 0)
             return AVERROR(EINVAL);