diff mbox

[FFmpeg-devel] lavc/cuvid: fail early if GPU can't handle video parameters

Message ID 1485019650-30152-1-git-send-email-pkoshevoy@gmail.com
State Superseded
Headers show

Commit Message

Pavel Koshevoy Jan. 21, 2017, 5:27 p.m. UTC
From: Pavel Koshevoy <pkoshevoy@gmail.com>

Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
and only a limited set of resolutions per codec are supported.

Unfortunately CUVID silently drops packets for video of unsupported
resolution.  However, it will error-out at cuvidCreateDecoder call
if the indicated video resolution is not supported.

Given that stream resolution and pixel format are typically known as
a result of probing it is better to use this information during
avcodec_open2 call to fail immediately, rather than proceeding to
decode and never receiving any frames from the decoder nor receiving
any indication of decode failure.
---
 libavcodec/cuvid.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 57 insertions(+), 5 deletions(-)

Comments

Pavel Koshevoy Jan. 21, 2017, 5:35 p.m. UTC | #1
On Sat, Jan 21, 2017 at 10:27 AM,  <pkoshevoy@gmail.com> wrote:
> From: Pavel Koshevoy <pkoshevoy@gmail.com>
>
> Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
> and only a limited set of resolutions per codec are supported.
>
> Unfortunately CUVID silently drops packets for video of unsupported
> resolution.  However, it will error-out at cuvidCreateDecoder call
> if the indicated video resolution is not supported.
>
> Given that stream resolution and pixel format are typically known as
> a result of probing it is better to use this information during
> avcodec_open2 call to fail immediately, rather than proceeding to
> decode and never receiving any frames from the decoder nor receiving
> any indication of decode failure.
> ---
>  libavcodec/cuvid.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 57 insertions(+), 5 deletions(-)
>
> diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
> index 8fc713d..f9c29a1 100644
> --- a/libavcodec/cuvid.c
> +++ b/libavcodec/cuvid.c
> @@ -612,7 +612,11 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx)
>      return 0;
>  }
>
> -static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cuparseinfo)
> +static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
> +                                    const CUVIDPARSERPARAMS *cuparseinfo,
> +                                    cudaVideoChromaFormat probed_chroma_format,
> +                                    int probed_width,
> +                                    int probed_height)
>  {
>      CuvidContext *ctx = avctx->priv_data;
>      CUVIDDECODECREATEINFO cuinfo;
> @@ -622,11 +626,11 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
>      memset(&cuinfo, 0, sizeof(cuinfo));
>
>      cuinfo.CodecType = cuparseinfo->CodecType;
> -    cuinfo.ChromaFormat = cudaVideoChromaFormat_420;
> +    cuinfo.ChromaFormat = probed_chroma_format;
>      cuinfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
>
> -    cuinfo.ulWidth = 1280;
> -    cuinfo.ulHeight = 720;
> +    cuinfo.ulWidth = probed_width;
> +    cuinfo.ulHeight = probed_height;
>      cuinfo.ulTargetWidth = cuinfo.ulWidth;
>      cuinfo.ulTargetHeight = cuinfo.ulHeight;
>
> @@ -653,6 +657,36 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
>      return 0;
>  }
>
> +static int convert_to_cuda_video_chroma_format(enum AVPixelFormat pix_fmt,
> +                                               cudaVideoChromaFormat *out)
> +{
> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
> +    if (!(out && desc &&
> +          (desc->nb_components == 1 || desc->nb_components == 3) &&
> +          (desc->log2_chroma_w < 2 && desc->log2_chroma_h < 2)))
> +    {
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (desc->nb_components == 1)
> +    {
> +        *out = cudaVideoChromaFormat_Monochrome;
> +    }
> +    else if (desc->flags == AV_PIX_FMT_FLAG_PLANAR)
> +    {
> +        *out = ((desc->log2_chroma_w == 0) ? cudaVideoChromaFormat_444 :
> +                (desc->log2_chroma_h == 0) ? cudaVideoChromaFormat_422 :
> +                cudaVideoChromaFormat_420);
> +    }
> +    else
> +    {
> +        return AVERROR(EINVAL);
> +    }
> +
> +    // unfortunately, 420 is the only one that works:
> +    return (*out == cudaVideoChromaFormat_420) ? 0 : AVERROR_EXTERNAL;
> +}
> +
>  static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>  {
>      CuvidContext *ctx = avctx->priv_data;
> @@ -663,12 +697,27 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>      CUcontext cuda_ctx = NULL;
>      CUcontext dummy;
>      const AVBitStreamFilter *bsf;
> +    cudaVideoChromaFormat probed_chroma_format;
> +    int probed_width;
> +    int probed_height;
>      int ret = 0;
>
>      enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
>                                         AV_PIX_FMT_NV12,
>                                         AV_PIX_FMT_NONE };
>
> +    enum AVPixelFormat probed_pix_fmt = (avctx->pix_fmt <= 0 ?
> +                                         AV_PIX_FMT_YUV420P :
> +                                         avctx->pix_fmt);
> +
> +    ret = convert_to_cuda_video_chroma_format(probed_pix_fmt, &probed_chroma_format);
> +    if (ret < 0) {
> +        // pixel format is not supported:
> +        return ret;
> +    }
> +    probed_width = avctx->coded_width ? avctx->coded_width : 1280;
> +    probed_height = avctx->coded_height ? avctx->coded_height : 720;
> +
>      // Accelerated transcoding scenarios with 'ffmpeg' require that the
>      // pix_fmt be set to AV_PIX_FMT_CUDA early. The sw_pix_fmt, and the
>      // pix_fmt for non-accelerated transcoding, do not need to be correct
> @@ -824,7 +873,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>      if (ret < 0)
>          goto error;
>
> -    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
> +    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
> +                                   probed_chroma_format,
> +                                   probed_width,
> +                                   probed_height);
>      if (ret < 0)
>          goto error;
>
> --
> 2.9.2
>


Despite wm4 objections I am resubmitting this patch, because I can
find no other reasonable method for rejecting ffmpeg cuvid decoder
when it can't handle a given video stream.  An unreasonable
alternative would be to duplicate the work that cuvid.c does
internally on my application side, and that would be of little benefit
to me and to ffmpeg -- I might as well use the nvenc APIs directly
then.  I still see this patch as an improvement.

Pavel.
Pavel Koshevoy Jan. 21, 2017, 5:48 p.m. UTC | #2
On Sat, Jan 21, 2017 at 10:35 AM, Pavel Koshevoy <pkoshevoy@gmail.com> wrote:

<snip>

> Despite wm4 objections I am resubmitting this patch, because I can
> find no other reasonable method for rejecting ffmpeg cuvid decoder
> when it can't handle a given video stream.  An unreasonable
> alternative would be to duplicate the work that cuvid.c does
> internally on my application side, and that would be of little benefit
> to me and to ffmpeg -- I might as well use the nvenc APIs directly

I meant cuvid, not nvenc of course ... I get those mixed up sometimes,
sorry about that

> then.  I still see this patch as an improvement.
>
> Pavel.
wm4 Jan. 22, 2017, 11:09 a.m. UTC | #3
On Sat, 21 Jan 2017 10:35:50 -0700
Pavel Koshevoy <pkoshevoy@gmail.com> wrote:

> On Sat, Jan 21, 2017 at 10:27 AM,  <pkoshevoy@gmail.com> wrote:
> > From: Pavel Koshevoy <pkoshevoy@gmail.com>
> >
> > Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
> > and only a limited set of resolutions per codec are supported.
> >
> > Unfortunately CUVID silently drops packets for video of unsupported
> > resolution.  However, it will error-out at cuvidCreateDecoder call
> > if the indicated video resolution is not supported.
> >
> > Given that stream resolution and pixel format are typically known as
> > a result of probing it is better to use this information during
> > avcodec_open2 call to fail immediately, rather than proceeding to
> > decode and never receiving any frames from the decoder nor receiving
> > any indication of decode failure.
> > ---
> >  libavcodec/cuvid.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-----
> >  1 file changed, 57 insertions(+), 5 deletions(-)
> >
> > diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
> > index 8fc713d..f9c29a1 100644
> > --- a/libavcodec/cuvid.c
> > +++ b/libavcodec/cuvid.c
> > @@ -612,7 +612,11 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx)
> >      return 0;
> >  }
> >
> > -static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cuparseinfo)
> > +static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
> > +                                    const CUVIDPARSERPARAMS *cuparseinfo,
> > +                                    cudaVideoChromaFormat probed_chroma_format,
> > +                                    int probed_width,
> > +                                    int probed_height)
> >  {
> >      CuvidContext *ctx = avctx->priv_data;
> >      CUVIDDECODECREATEINFO cuinfo;
> > @@ -622,11 +626,11 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
> >      memset(&cuinfo, 0, sizeof(cuinfo));
> >
> >      cuinfo.CodecType = cuparseinfo->CodecType;
> > -    cuinfo.ChromaFormat = cudaVideoChromaFormat_420;
> > +    cuinfo.ChromaFormat = probed_chroma_format;
> >      cuinfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
> >
> > -    cuinfo.ulWidth = 1280;
> > -    cuinfo.ulHeight = 720;
> > +    cuinfo.ulWidth = probed_width;
> > +    cuinfo.ulHeight = probed_height;
> >      cuinfo.ulTargetWidth = cuinfo.ulWidth;
> >      cuinfo.ulTargetHeight = cuinfo.ulHeight;
> >
> > @@ -653,6 +657,36 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
> >      return 0;
> >  }
> >
> > +static int convert_to_cuda_video_chroma_format(enum AVPixelFormat pix_fmt,
> > +                                               cudaVideoChromaFormat *out)
> > +{
> > +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
> > +    if (!(out && desc &&
> > +          (desc->nb_components == 1 || desc->nb_components == 3) &&
> > +          (desc->log2_chroma_w < 2 && desc->log2_chroma_h < 2)))
> > +    {
> > +        return AVERROR(EINVAL);
> > +    }
> > +
> > +    if (desc->nb_components == 1)
> > +    {
> > +        *out = cudaVideoChromaFormat_Monochrome;
> > +    }
> > +    else if (desc->flags == AV_PIX_FMT_FLAG_PLANAR)
> > +    {
> > +        *out = ((desc->log2_chroma_w == 0) ? cudaVideoChromaFormat_444 :
> > +                (desc->log2_chroma_h == 0) ? cudaVideoChromaFormat_422 :
> > +                cudaVideoChromaFormat_420);
> > +    }
> > +    else
> > +    {
> > +        return AVERROR(EINVAL);
> > +    }
> > +
> > +    // unfortunately, 420 is the only one that works:
> > +    return (*out == cudaVideoChromaFormat_420) ? 0 : AVERROR_EXTERNAL;
> > +}
> > +
> >  static av_cold int cuvid_decode_init(AVCodecContext *avctx)
> >  {
> >      CuvidContext *ctx = avctx->priv_data;
> > @@ -663,12 +697,27 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
> >      CUcontext cuda_ctx = NULL;
> >      CUcontext dummy;
> >      const AVBitStreamFilter *bsf;
> > +    cudaVideoChromaFormat probed_chroma_format;
> > +    int probed_width;
> > +    int probed_height;
> >      int ret = 0;
> >
> >      enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
> >                                         AV_PIX_FMT_NV12,
> >                                         AV_PIX_FMT_NONE };
> >
> > +    enum AVPixelFormat probed_pix_fmt = (avctx->pix_fmt <= 0 ?
> > +                                         AV_PIX_FMT_YUV420P :
> > +                                         avctx->pix_fmt);
> > +
> > +    ret = convert_to_cuda_video_chroma_format(probed_pix_fmt, &probed_chroma_format);
> > +    if (ret < 0) {
> > +        // pixel format is not supported:
> > +        return ret;
> > +    }
> > +    probed_width = avctx->coded_width ? avctx->coded_width : 1280;
> > +    probed_height = avctx->coded_height ? avctx->coded_height : 720;
> > +
> >      // Accelerated transcoding scenarios with 'ffmpeg' require that the
> >      // pix_fmt be set to AV_PIX_FMT_CUDA early. The sw_pix_fmt, and the
> >      // pix_fmt for non-accelerated transcoding, do not need to be correct
> > @@ -824,7 +873,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
> >      if (ret < 0)
> >          goto error;
> >
> > -    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
> > +    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
> > +                                   probed_chroma_format,
> > +                                   probed_width,
> > +                                   probed_height);
> >      if (ret < 0)
> >          goto error;
> >
> > --
> > 2.9.2
> >  
> 
> 
> Despite wm4 objections I am resubmitting this patch, because I can
> find no other reasonable method for rejecting ffmpeg cuvid decoder
> when it can't handle a given video stream.  An unreasonable
> alternative would be to duplicate the work that cuvid.c does
> internally on my application side, and that would be of little benefit
> to me and to ffmpeg -- I might as well use the nvenc APIs directly
> then.  I still see this patch as an improvement.

I'm still against this.

Also, I have the same problem (due to ffmpeg.c being so low level and
essentially unsuitable to support hardware transcoding on arbitrary
hardware). I'm telling you you won't get far with this anyway, because
you need different filter graphs for sw and hw.

I also dislike this because it's complex and requires creating the
decoder twice (which it shouldn't do in the first place).
wm4 Jan. 22, 2017, 11:58 a.m. UTC | #4
On Sat, 21 Jan 2017 10:35:50 -0700
Pavel Koshevoy <pkoshevoy@gmail.com> wrote:

> On Sat, Jan 21, 2017 at 10:27 AM,  <pkoshevoy@gmail.com> wrote:
> > From: Pavel Koshevoy <pkoshevoy@gmail.com>
> >
> > Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
> > and only a limited set of resolutions per codec are supported.
> >
> > Unfortunately CUVID silently drops packets for video of unsupported
> > resolution.  However, it will error-out at cuvidCreateDecoder call
> > if the indicated video resolution is not supported.
> >
> > Given that stream resolution and pixel format are typically known as
> > a result of probing it is better to use this information during
> > avcodec_open2 call to fail immediately, rather than proceeding to
> > decode and never receiving any frames from the decoder nor receiving
> > any indication of decode failure.
> > ---
> >  libavcodec/cuvid.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-----
> >  1 file changed, 57 insertions(+), 5 deletions(-)
> >
> > diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
> > index 8fc713d..f9c29a1 100644
> > --- a/libavcodec/cuvid.c
> > +++ b/libavcodec/cuvid.c
> > @@ -612,7 +612,11 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx)
> >      return 0;
> >  }
> >
> > -static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cuparseinfo)
> > +static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
> > +                                    const CUVIDPARSERPARAMS *cuparseinfo,
> > +                                    cudaVideoChromaFormat probed_chroma_format,
> > +                                    int probed_width,
> > +                                    int probed_height)
> >  {
> >      CuvidContext *ctx = avctx->priv_data;
> >      CUVIDDECODECREATEINFO cuinfo;
> > @@ -622,11 +626,11 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
> >      memset(&cuinfo, 0, sizeof(cuinfo));
> >
> >      cuinfo.CodecType = cuparseinfo->CodecType;
> > -    cuinfo.ChromaFormat = cudaVideoChromaFormat_420;
> > +    cuinfo.ChromaFormat = probed_chroma_format;
> >      cuinfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
> >
> > -    cuinfo.ulWidth = 1280;
> > -    cuinfo.ulHeight = 720;
> > +    cuinfo.ulWidth = probed_width;
> > +    cuinfo.ulHeight = probed_height;
> >      cuinfo.ulTargetWidth = cuinfo.ulWidth;
> >      cuinfo.ulTargetHeight = cuinfo.ulHeight;
> >
> > @@ -653,6 +657,36 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
> >      return 0;
> >  }
> >
> > +static int convert_to_cuda_video_chroma_format(enum AVPixelFormat pix_fmt,
> > +                                               cudaVideoChromaFormat *out)
> > +{
> > +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
> > +    if (!(out && desc &&
> > +          (desc->nb_components == 1 || desc->nb_components == 3) &&
> > +          (desc->log2_chroma_w < 2 && desc->log2_chroma_h < 2)))
> > +    {
> > +        return AVERROR(EINVAL);
> > +    }
> > +
> > +    if (desc->nb_components == 1)
> > +    {
> > +        *out = cudaVideoChromaFormat_Monochrome;
> > +    }
> > +    else if (desc->flags == AV_PIX_FMT_FLAG_PLANAR)
> > +    {
> > +        *out = ((desc->log2_chroma_w == 0) ? cudaVideoChromaFormat_444 :
> > +                (desc->log2_chroma_h == 0) ? cudaVideoChromaFormat_422 :
> > +                cudaVideoChromaFormat_420);
> > +    }
> > +    else
> > +    {
> > +        return AVERROR(EINVAL);
> > +    }
> > +
> > +    // unfortunately, 420 is the only one that works:
> > +    return (*out == cudaVideoChromaFormat_420) ? 0 : AVERROR_EXTERNAL;
> > +}
> > +
> >  static av_cold int cuvid_decode_init(AVCodecContext *avctx)
> >  {
> >      CuvidContext *ctx = avctx->priv_data;
> > @@ -663,12 +697,27 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
> >      CUcontext cuda_ctx = NULL;
> >      CUcontext dummy;
> >      const AVBitStreamFilter *bsf;
> > +    cudaVideoChromaFormat probed_chroma_format;
> > +    int probed_width;
> > +    int probed_height;
> >      int ret = 0;
> >
> >      enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
> >                                         AV_PIX_FMT_NV12,
> >                                         AV_PIX_FMT_NONE };
> >
> > +    enum AVPixelFormat probed_pix_fmt = (avctx->pix_fmt <= 0 ?
> > +                                         AV_PIX_FMT_YUV420P :
> > +                                         avctx->pix_fmt);
> > +
> > +    ret = convert_to_cuda_video_chroma_format(probed_pix_fmt, &probed_chroma_format);
> > +    if (ret < 0) {
> > +        // pixel format is not supported:
> > +        return ret;
> > +    }
> > +    probed_width = avctx->coded_width ? avctx->coded_width : 1280;
> > +    probed_height = avctx->coded_height ? avctx->coded_height : 720;
> > +
> >      // Accelerated transcoding scenarios with 'ffmpeg' require that the
> >      // pix_fmt be set to AV_PIX_FMT_CUDA early. The sw_pix_fmt, and the
> >      // pix_fmt for non-accelerated transcoding, do not need to be correct
> > @@ -824,7 +873,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
> >      if (ret < 0)
> >          goto error;
> >
> > -    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
> > +    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
> > +                                   probed_chroma_format,
> > +                                   probed_width,
> > +                                   probed_height);
> >      if (ret < 0)
> >          goto error;
> >
> > --
> > 2.9.2
> >  
> 
> 
> Despite wm4 objections I am resubmitting this patch, because I can
> find no other reasonable method for rejecting ffmpeg cuvid decoder
> when it can't handle a given video stream.  An unreasonable
> alternative would be to duplicate the work that cuvid.c does
> internally on my application side, and that would be of little benefit
> to me and to ffmpeg -- I might as well use the nvenc APIs directly
> then.  I still see this patch as an improvement.

PS:

Actually, cuvid is a bit different from normal hwaccel and I confused
that (blame Sunday "morning").

In this case, do you just want the cuvid to gracefully exit the ffmpeg.c
process if decoding is not supported? In that case, this would be much
simpler:

1. Return a special error code from cuvid that signals unsupported
   format
2. Make ffmpeg.c check for this error code, and stop transcoding with
   an error if it's encountered
Mark Thompson Jan. 22, 2017, 12:28 p.m. UTC | #5
On 21/01/17 17:35, Pavel Koshevoy wrote:
> On Sat, Jan 21, 2017 at 10:27 AM,  <pkoshevoy@gmail.com> wrote:
>> From: Pavel Koshevoy <pkoshevoy@gmail.com>
>>
>> Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
>> and only a limited set of resolutions per codec are supported.
>>
>> Unfortunately CUVID silently drops packets for video of unsupported
>> resolution.  However, it will error-out at cuvidCreateDecoder call
>> if the indicated video resolution is not supported.
>>
>> Given that stream resolution and pixel format are typically known as
>> a result of probing it is better to use this information during
>> avcodec_open2 call to fail immediately, rather than proceeding to
>> decode and never receiving any frames from the decoder nor receiving
>> any indication of decode failure.
>> ---
>>  libavcodec/cuvid.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-----
>>  1 file changed, 57 insertions(+), 5 deletions(-)
>>
>> diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
>> index 8fc713d..f9c29a1 100644
>> --- a/libavcodec/cuvid.c
>> +++ b/libavcodec/cuvid.c
>> @@ -612,7 +612,11 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx)
>>      return 0;
>>  }
>>
>> -static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cuparseinfo)
>> +static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
>> +                                    const CUVIDPARSERPARAMS *cuparseinfo,
>> +                                    cudaVideoChromaFormat probed_chroma_format,
>> +                                    int probed_width,
>> +                                    int probed_height)
>>  {
>>      CuvidContext *ctx = avctx->priv_data;
>>      CUVIDDECODECREATEINFO cuinfo;
>> @@ -622,11 +626,11 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
>>      memset(&cuinfo, 0, sizeof(cuinfo));
>>
>>      cuinfo.CodecType = cuparseinfo->CodecType;
>> -    cuinfo.ChromaFormat = cudaVideoChromaFormat_420;
>> +    cuinfo.ChromaFormat = probed_chroma_format;
>>      cuinfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
>>
>> -    cuinfo.ulWidth = 1280;
>> -    cuinfo.ulHeight = 720;
>> +    cuinfo.ulWidth = probed_width;
>> +    cuinfo.ulHeight = probed_height;
>>      cuinfo.ulTargetWidth = cuinfo.ulWidth;
>>      cuinfo.ulTargetHeight = cuinfo.ulHeight;
>>
>> @@ -653,6 +657,36 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
>>      return 0;
>>  }
>>
>> +static int convert_to_cuda_video_chroma_format(enum AVPixelFormat pix_fmt,
>> +                                               cudaVideoChromaFormat *out)
>> +{
>> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
>> +    if (!(out && desc &&
>> +          (desc->nb_components == 1 || desc->nb_components == 3) &&
>> +          (desc->log2_chroma_w < 2 && desc->log2_chroma_h < 2)))
>> +    {
>> +        return AVERROR(EINVAL);
>> +    }
>> +
>> +    if (desc->nb_components == 1)
>> +    {
>> +        *out = cudaVideoChromaFormat_Monochrome;
>> +    }
>> +    else if (desc->flags == AV_PIX_FMT_FLAG_PLANAR)
>> +    {
>> +        *out = ((desc->log2_chroma_w == 0) ? cudaVideoChromaFormat_444 :
>> +                (desc->log2_chroma_h == 0) ? cudaVideoChromaFormat_422 :
>> +                cudaVideoChromaFormat_420);
>> +    }
>> +    else
>> +    {
>> +        return AVERROR(EINVAL);
>> +    }
>> +
>> +    // unfortunately, 420 is the only one that works:
>> +    return (*out == cudaVideoChromaFormat_420) ? 0 : AVERROR_EXTERNAL;
>> +}
>> +
>>  static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>>  {
>>      CuvidContext *ctx = avctx->priv_data;
>> @@ -663,12 +697,27 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>>      CUcontext cuda_ctx = NULL;
>>      CUcontext dummy;
>>      const AVBitStreamFilter *bsf;
>> +    cudaVideoChromaFormat probed_chroma_format;
>> +    int probed_width;
>> +    int probed_height;
>>      int ret = 0;
>>
>>      enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
>>                                         AV_PIX_FMT_NV12,
>>                                         AV_PIX_FMT_NONE };
>>
>> +    enum AVPixelFormat probed_pix_fmt = (avctx->pix_fmt <= 0 ?
>> +                                         AV_PIX_FMT_YUV420P :
>> +                                         avctx->pix_fmt);
>> +
>> +    ret = convert_to_cuda_video_chroma_format(probed_pix_fmt, &probed_chroma_format);
>> +    if (ret < 0) {
>> +        // pixel format is not supported:
>> +        return ret;
>> +    }
>> +    probed_width = avctx->coded_width ? avctx->coded_width : 1280;
>> +    probed_height = avctx->coded_height ? avctx->coded_height : 720;
>> +
>>      // Accelerated transcoding scenarios with 'ffmpeg' require that the
>>      // pix_fmt be set to AV_PIX_FMT_CUDA early. The sw_pix_fmt, and the
>>      // pix_fmt for non-accelerated transcoding, do not need to be correct
>> @@ -824,7 +873,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>>      if (ret < 0)
>>          goto error;
>>
>> -    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
>> +    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
>> +                                   probed_chroma_format,
>> +                                   probed_width,
>> +                                   probed_height);
>>      if (ret < 0)
>>          goto error;
>>
>> --
>> 2.9.2
>>
> 
> 
> Despite wm4 objections I am resubmitting this patch, because I can
> find no other reasonable method for rejecting ffmpeg cuvid decoder
> when it can't handle a given video stream.

An alternative method would be to follow the approach of the qsv decoder.  There, it doesn't open anything until enough information has been parsed from the stream to have all the appropriate parameters.  That entails making a parser inside the decoder and ensuring that the parser returns all of the information you need to pass to the decoder creation, but it avoids any possibility that the user doesn't supply the necessary information and also allows stream format changes to be handled without external support.

Another alternative method would be to add a new API which would test "does decoder X support a stream with Y parameters", which could then be called from your application to answer this sort of question directly.  (If the only way to then do that in the CUVID case is to open a dummy decoder then I guess it has to do that, other APIs have query functions for this sort of thing.)

>                                             An unreasonable
> alternative would be to duplicate the work that cuvid.c does
> internally on my application side, and that would be of little benefit
> to me and to ffmpeg -- I might as well use the nvenc APIs directly
> then.

That's fair.

>        I still see this patch as an improvement.

I agree with wm4's sentiments here.

- Mark
Timo Rothenpieler Jan. 22, 2017, 2:05 p.m. UTC | #6
> Despite wm4 objections I am resubmitting this patch, because I can
> find no other reasonable method for rejecting ffmpeg cuvid decoder
> when it can't handle a given video stream.  An unreasonable
> alternative would be to duplicate the work that cuvid.c does
> internally on my application side, and that would be of little benefit
> to me and to ffmpeg -- I might as well use the nvenc APIs directly
> then.  I still see this patch as an improvement.

Cuvid should still return an error, just not on init, while some stream
parameters are still unknown, but when decoding the first (couple) frame(s).
If there is a case where it doesn't, I'd much rather fix that.
Pavel Koshevoy Jan. 22, 2017, 4:37 p.m. UTC | #7
On 01/22/2017 07:05 AM, Timo Rothenpieler wrote:
>> Despite wm4 objections I am resubmitting this patch, because I can
>> find no other reasonable method for rejecting ffmpeg cuvid decoder
>> when it can't handle a given video stream.  An unreasonable
>> alternative would be to duplicate the work that cuvid.c does
>> internally on my application side, and that would be of little benefit
>> to me and to ffmpeg -- I might as well use the nvenc APIs directly
>> then.  I still see this patch as an improvement.
> Cuvid should still return an error, just not on init, while some stream
> parameters are still unknown, but when decoding the first (couple) frame(s).
> If there is a case where it doesn't, I'd much rather fix that.


It didn't return an error.  I was testing with 5K and 8K h264 samples mentioned 
at http://kodi.wiki/view/Samples on a GeForce GT 730 which only supports 4K 
8-bit h264 -- ctx->cvdl->cuvidParseVideoData never returned an error, and 
cuvid_handle_video_sequence callback was never called.   The only indication I 
ever got from cuvid that it won't work was during ctx->cvdl->cuvidCreateDecoder 
call (after my patch).

Pavel.
Pavel Koshevoy Jan. 22, 2017, 5:01 p.m. UTC | #8
On 01/22/2017 04:58 AM, wm4 wrote:
> On Sat, 21 Jan 2017 10:35:50 -0700
> Pavel Koshevoy <pkoshevoy@gmail.com> wrote:
>
>> On Sat, Jan 21, 2017 at 10:27 AM,  <pkoshevoy@gmail.com> wrote:
>>> From: Pavel Koshevoy <pkoshevoy@gmail.com>

<snip>

>>> -    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
>>> +    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
>>> +                                   probed_chroma_format,
>>> +                                   probed_width,
>>> +                                   probed_height);
>>>       if (ret < 0)
>>>           goto error;
>>>
>>> --
>>> 2.9.2
>>>   
>>
>> Despite wm4 objections I am resubmitting this patch, because I can
>> find no other reasonable method for rejecting ffmpeg cuvid decoder
>> when it can't handle a given video stream.  An unreasonable
>> alternative would be to duplicate the work that cuvid.c does
>> internally on my application side, and that would be of little benefit
>> to me and to ffmpeg -- I might as well use the nvenc APIs directly
>> then.  I still see this patch as an improvement.
> PS:
>
> Actually, cuvid is a bit different from normal hwaccel and I confused
> that (blame Sunday "morning").
>
> In this case, do you just want the cuvid to gracefully exit the ffmpeg.c
> process if decoding is not supported? In that case, this would be much
> simpler:
>
> 1. Return a special error code from cuvid that signals unsupported
>     format
> 2. Make ffmpeg.c check for this error code, and stop transcoding with
>     an error if it's encountered


I am not working with ffmpeg.c -- I am calling ffmpeg public APIs from my 
application.  The 1. point above is essentially what my patch does.  The real 
problem is that CUVID only reports an error at cuvidCreateDecoder time, and not 
later when I attempt to decode 8K or 5K video -- it just swallows packets and 
never outputs anything (no errors, no callbacks, nothing).

Pavel.
Pavel Koshevoy Jan. 22, 2017, 5:09 p.m. UTC | #9
On 01/22/2017 05:28 AM, Mark Thompson wrote:
> On 21/01/17 17:35, Pavel Koshevoy wrote:
>> On Sat, Jan 21, 2017 at 10:27 AM,  <pkoshevoy@gmail.com> wrote:
>>> From: Pavel Koshevoy <pkoshevoy@gmail.com>
>>>
>>> Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
>>> and only a limited set of resolutions per codec are supported.
>>>
>>> Unfortunately CUVID silently drops packets for video of unsupported
>>> resolution.  However, it will error-out at cuvidCreateDecoder call
>>> if the indicated video resolution is not supported.
>>>
>>> Given that stream resolution and pixel format are typically known as
>>> a result of probing it is better to use this information during
>>> avcodec_open2 call to fail immediately, rather than proceeding to
>>> decode and never receiving any frames from the decoder nor receiving
>>> any indication of decode failure.
>>> ---
>>>   libavcodec/cuvid.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++-----
>>>   1 file changed, 57 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
>>> index 8fc713d..f9c29a1 100644
>>> --- a/libavcodec/cuvid.c
>>> +++ b/libavcodec/cuvid.c
>>> @@ -612,7 +612,11 @@ static av_cold int cuvid_decode_end(AVCodecContext *avctx)
>>>       return 0;
>>>   }
>>>
>>> -static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cuparseinfo)
>>> +static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
>>> +                                    const CUVIDPARSERPARAMS *cuparseinfo,
>>> +                                    cudaVideoChromaFormat probed_chroma_format,
>>> +                                    int probed_width,
>>> +                                    int probed_height)
>>>   {
>>>       CuvidContext *ctx = avctx->priv_data;
>>>       CUVIDDECODECREATEINFO cuinfo;
>>> @@ -622,11 +626,11 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
>>>       memset(&cuinfo, 0, sizeof(cuinfo));
>>>
>>>       cuinfo.CodecType = cuparseinfo->CodecType;
>>> -    cuinfo.ChromaFormat = cudaVideoChromaFormat_420;
>>> +    cuinfo.ChromaFormat = probed_chroma_format;
>>>       cuinfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
>>>
>>> -    cuinfo.ulWidth = 1280;
>>> -    cuinfo.ulHeight = 720;
>>> +    cuinfo.ulWidth = probed_width;
>>> +    cuinfo.ulHeight = probed_height;
>>>       cuinfo.ulTargetWidth = cuinfo.ulWidth;
>>>       cuinfo.ulTargetHeight = cuinfo.ulHeight;
>>>
>>> @@ -653,6 +657,36 @@ static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
>>>       return 0;
>>>   }
>>>
>>> +static int convert_to_cuda_video_chroma_format(enum AVPixelFormat pix_fmt,
>>> +                                               cudaVideoChromaFormat *out)
>>> +{
>>> +    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
>>> +    if (!(out && desc &&
>>> +          (desc->nb_components == 1 || desc->nb_components == 3) &&
>>> +          (desc->log2_chroma_w < 2 && desc->log2_chroma_h < 2)))
>>> +    {
>>> +        return AVERROR(EINVAL);
>>> +    }
>>> +
>>> +    if (desc->nb_components == 1)
>>> +    {
>>> +        *out = cudaVideoChromaFormat_Monochrome;
>>> +    }
>>> +    else if (desc->flags == AV_PIX_FMT_FLAG_PLANAR)
>>> +    {
>>> +        *out = ((desc->log2_chroma_w == 0) ? cudaVideoChromaFormat_444 :
>>> +                (desc->log2_chroma_h == 0) ? cudaVideoChromaFormat_422 :
>>> +                cudaVideoChromaFormat_420);
>>> +    }
>>> +    else
>>> +    {
>>> +        return AVERROR(EINVAL);
>>> +    }
>>> +
>>> +    // unfortunately, 420 is the only one that works:
>>> +    return (*out == cudaVideoChromaFormat_420) ? 0 : AVERROR_EXTERNAL;
>>> +}
>>> +
>>>   static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>>>   {
>>>       CuvidContext *ctx = avctx->priv_data;
>>> @@ -663,12 +697,27 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>>>       CUcontext cuda_ctx = NULL;
>>>       CUcontext dummy;
>>>       const AVBitStreamFilter *bsf;
>>> +    cudaVideoChromaFormat probed_chroma_format;
>>> +    int probed_width;
>>> +    int probed_height;
>>>       int ret = 0;
>>>
>>>       enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
>>>                                          AV_PIX_FMT_NV12,
>>>                                          AV_PIX_FMT_NONE };
>>>
>>> +    enum AVPixelFormat probed_pix_fmt = (avctx->pix_fmt <= 0 ?
>>> +                                         AV_PIX_FMT_YUV420P :
>>> +                                         avctx->pix_fmt);
>>> +
>>> +    ret = convert_to_cuda_video_chroma_format(probed_pix_fmt, &probed_chroma_format);
>>> +    if (ret < 0) {
>>> +        // pixel format is not supported:
>>> +        return ret;
>>> +    }
>>> +    probed_width = avctx->coded_width ? avctx->coded_width : 1280;
>>> +    probed_height = avctx->coded_height ? avctx->coded_height : 720;
>>> +
>>>       // Accelerated transcoding scenarios with 'ffmpeg' require that the
>>>       // pix_fmt be set to AV_PIX_FMT_CUDA early. The sw_pix_fmt, and the
>>>       // pix_fmt for non-accelerated transcoding, do not need to be correct
>>> @@ -824,7 +873,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
>>>       if (ret < 0)
>>>           goto error;
>>>
>>> -    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
>>> +    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
>>> +                                   probed_chroma_format,
>>> +                                   probed_width,
>>> +                                   probed_height);
>>>       if (ret < 0)
>>>           goto error;
>>>
>>> --
>>> 2.9.2
>>>
>>
>> Despite wm4 objections I am resubmitting this patch, because I can
>> find no other reasonable method for rejecting ffmpeg cuvid decoder
>> when it can't handle a given video stream.
> An alternative method would be to follow the approach of the qsv decoder.  There, it doesn't open anything until enough information has been parsed from the stream to have all the appropriate parameters.  That entails making a parser inside the decoder and ensuring that the parser returns all of the information you need to pass to the decoder creation, but it avoids any possibility that the user doesn't supply the necessary information and also allows stream format changes to be handled without external support.
>


That sounds interesting, although it's an overkill for my problem -- I already 
have the probed stream codec parameters, and it is wasteful not to use them to 
initialize the codec.  Your proposed solution sounds robust, but also more 
difficult to implement.  I may attempt it (I'll have to see how QSV does it), 
but I my patch would still be an improvement if it were applied now.

Pavel.
Philip Langdale Jan. 22, 2017, 6:13 p.m. UTC | #10
On Sat, 21 Jan 2017 10:27:30 -0700
pkoshevoy@gmail.com wrote:

> From: Pavel Koshevoy <pkoshevoy@gmail.com>
> 
> Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
> and only a limited set of resolutions per codec are supported.
> 
> Unfortunately CUVID silently drops packets for video of unsupported
> resolution.  However, it will error-out at cuvidCreateDecoder call
> if the indicated video resolution is not supported.
> 
> Given that stream resolution and pixel format are typically known as
> a result of probing it is better to use this information during
> avcodec_open2 call to fail immediately, rather than proceeding to
> decode and never receiving any frames from the decoder nor receiving
> any indication of decode failure.

So I'm confused. I agree that these errors do not emerge early enough
to induce failure at decoder init time, but they should all trigger
errors at first-frame decode time. That's certainly true for the
chroma format error, because I added that logic.

Assuming that is true, you should just be dealing with first-frame
failure and then falling back to SW decoding, or whatever policy
you want in your app (I assume 8K SW decoding won't go well...).

Sure, it's not great that you can't get failures at init time, but
you have to be prepared to encounter fatal errors on frame decode
with dealing with hardware decoders. Most, if not all, have failure
cases that only emerge when decoding a real frame.

There's a useful discussion to have about clearly indicating how
fatal an error is from that frame decode failure (so we know that
it's due to unsupported characteristics rather than a single bad
frame), but this change won't help us get there.

And I'll repeat wm4's warning that these tests depend on the presence
of probed data that cannot be assumed in the general case; it may work
for your app, but other applications may not fill in those fields, so
it cannot be considered reliable functionality.

Thanks,

--phil
Pavel Koshevoy Jan. 22, 2017, 6:16 p.m. UTC | #11
On Sun, Jan 22, 2017 at 9:37 AM, Pavel Koshevoy <pkoshevoy@gmail.com> wrote:
> On 01/22/2017 07:05 AM, Timo Rothenpieler wrote:
>>>
>>> Despite wm4 objections I am resubmitting this patch, because I can
>>> find no other reasonable method for rejecting ffmpeg cuvid decoder
>>> when it can't handle a given video stream.  An unreasonable
>>> alternative would be to duplicate the work that cuvid.c does
>>> internally on my application side, and that would be of little benefit
>>> to me and to ffmpeg -- I might as well use the nvenc APIs directly
>>> then.  I still see this patch as an improvement.
>>
>> Cuvid should still return an error, just not on init, while some stream
>> parameters are still unknown, but when decoding the first (couple)
>> frame(s).
>> If there is a case where it doesn't, I'd much rather fix that.
>
>
>
> It didn't return an error.  I was testing with 5K and 8K h264 samples
> mentioned at http://kodi.wiki/view/Samples on a GeForce GT 730 which only
> supports 4K 8-bit h264 -- ctx->cvdl->cuvidParseVideoData never returned an
> error, and cuvid_handle_video_sequence callback was never called.   The only
> indication I ever got from cuvid that it won't work was during
> ctx->cvdl->cuvidCreateDecoder call (after my patch).
>

I've just tested on GeForce GTX 1060 with PATAGONIA 8K mp4 sample and
I see the same behavior -- packets are accepted but the callbacks are
not called.

With the Snow Monkeys in Japan 5K Retina 60p (Ultra HD).mp4 on GeForce
1060 it actually does call the callbacks and logs errors, so that's
different from GeForce 730:

[h264_cuvid @ 0x7fff9c000980]
ctx->cvdl->cuvidDecodePicture(ctx->cudecoder, picparams) failed ->
CUDA_ERROR_INVALID_HANDLE: invalid resource handle
[h264_cuvid @ 0x7fff9c000980] cuvid decode callback error

Pavel.
Pavel Koshevoy Jan. 22, 2017, 7:12 p.m. UTC | #12
On 1/22/17 11:13 AM, Philip Langdale wrote:
> On Sat, 21 Jan 2017 10:27:30 -0700
> pkoshevoy@gmail.com wrote:
>
>> From: Pavel Koshevoy <pkoshevoy@gmail.com>
>>
>> Evidently CUVID doesn't support decoding 422 or 444 chroma formats,
>> and only a limited set of resolutions per codec are supported.
>>
>> Unfortunately CUVID silently drops packets for video of unsupported
>> resolution.  However, it will error-out at cuvidCreateDecoder call
>> if the indicated video resolution is not supported.
>>
>> Given that stream resolution and pixel format are typically known as
>> a result of probing it is better to use this information during
>> avcodec_open2 call to fail immediately, rather than proceeding to
>> decode and never receiving any frames from the decoder nor receiving
>> any indication of decode failure.
> So I'm confused. I agree that these errors do not emerge early enough
> to induce failure at decoder init time, but they should all trigger
> errors at first-frame decode time. That's certainly true for the
> chroma format error, because I added that logic.
>
> Assuming that is true, you should just be dealing with first-frame
> failure and then falling back to SW decoding, or whatever policy
> you want in your app (I assume 8K SW decoding won't go well...).
>

Yeah, but I don't even get a 1st frame failure with YUV420P 8K from 
h264_cuvid -- all I ever get is a 0 from avcodec_send_packet and 
AVERROR(EAGAIN) from avcodec_receive_frame.  This is what I've observed 
with GeForce GT 730 and GeForce GTX 1060 on openSUSE 42.2 and Ubuntu 
16.04 with latest nvidia drivers.

I will try to make my application more robust too -- I will have to 
buffer all the packets sent to the decoder until the 1st frame either 
succeeds or fails, so I can re-send the same packets to another decoder 
in case of 1st frame failure.

   Pavel.
Philip Langdale Jan. 22, 2017, 7:40 p.m. UTC | #13
On Sun, 22 Jan 2017 12:12:26 -0700
Pavel Koshevoy <pkoshevoy@gmail.com> wrote:

> Yeah, but I don't even get a 1st frame failure with YUV420P 8K from 
> h264_cuvid -- all I ever get is a 0 from avcodec_send_packet and 
> AVERROR(EAGAIN) from avcodec_receive_frame.  This is what I've
> observed with GeForce GT 730 and GeForce GTX 1060 on openSUSE 42.2
> and Ubuntu 16.04 with latest nvidia drivers.
>
I tested the 8K h.264 sample, and my experience is the same as yours.
It really does just stall out.

Given that, it looks like the dimensions check can only be done in the
way that you've done it. But we shouldn't add the chroma format check;
we already have the check after the parser calls handle_video_sequence
so that the first frame will fail - and that's reliable because it uses
the cuvid parser and doesn't rely on the externally probed values.

So, can you update the patch to just check the dimensions?

 
> I will try to make my application more robust too -- I will have to 
> buffer all the packets sent to the decoder until the 1st frame either 
> succeeds or fails, so I can re-send the same packets to another
> decoder in case of 1st frame failure.

Yeah. It's not fun but it is necessary. wm4 did this for mpv.

Thanks,

--phil
diff mbox

Patch

diff --git a/libavcodec/cuvid.c b/libavcodec/cuvid.c
index 8fc713d..f9c29a1 100644
--- a/libavcodec/cuvid.c
+++ b/libavcodec/cuvid.c
@@ -612,7 +612,11 @@  static av_cold int cuvid_decode_end(AVCodecContext *avctx)
     return 0;
 }
 
-static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cuparseinfo)
+static int cuvid_test_dummy_decoder(AVCodecContext *avctx,
+                                    const CUVIDPARSERPARAMS *cuparseinfo,
+                                    cudaVideoChromaFormat probed_chroma_format,
+                                    int probed_width,
+                                    int probed_height)
 {
     CuvidContext *ctx = avctx->priv_data;
     CUVIDDECODECREATEINFO cuinfo;
@@ -622,11 +626,11 @@  static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
     memset(&cuinfo, 0, sizeof(cuinfo));
 
     cuinfo.CodecType = cuparseinfo->CodecType;
-    cuinfo.ChromaFormat = cudaVideoChromaFormat_420;
+    cuinfo.ChromaFormat = probed_chroma_format;
     cuinfo.OutputFormat = cudaVideoSurfaceFormat_NV12;
 
-    cuinfo.ulWidth = 1280;
-    cuinfo.ulHeight = 720;
+    cuinfo.ulWidth = probed_width;
+    cuinfo.ulHeight = probed_height;
     cuinfo.ulTargetWidth = cuinfo.ulWidth;
     cuinfo.ulTargetHeight = cuinfo.ulHeight;
 
@@ -653,6 +657,36 @@  static int cuvid_test_dummy_decoder(AVCodecContext *avctx, CUVIDPARSERPARAMS *cu
     return 0;
 }
 
+static int convert_to_cuda_video_chroma_format(enum AVPixelFormat pix_fmt,
+                                               cudaVideoChromaFormat *out)
+{
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
+    if (!(out && desc &&
+          (desc->nb_components == 1 || desc->nb_components == 3) &&
+          (desc->log2_chroma_w < 2 && desc->log2_chroma_h < 2)))
+    {
+        return AVERROR(EINVAL);
+    }
+
+    if (desc->nb_components == 1)
+    {
+        *out = cudaVideoChromaFormat_Monochrome;
+    }
+    else if (desc->flags == AV_PIX_FMT_FLAG_PLANAR)
+    {
+        *out = ((desc->log2_chroma_w == 0) ? cudaVideoChromaFormat_444 :
+                (desc->log2_chroma_h == 0) ? cudaVideoChromaFormat_422 :
+                cudaVideoChromaFormat_420);
+    }
+    else
+    {
+        return AVERROR(EINVAL);
+    }
+
+    // unfortunately, 420 is the only one that works:
+    return (*out == cudaVideoChromaFormat_420) ? 0 : AVERROR_EXTERNAL;
+}
+
 static av_cold int cuvid_decode_init(AVCodecContext *avctx)
 {
     CuvidContext *ctx = avctx->priv_data;
@@ -663,12 +697,27 @@  static av_cold int cuvid_decode_init(AVCodecContext *avctx)
     CUcontext cuda_ctx = NULL;
     CUcontext dummy;
     const AVBitStreamFilter *bsf;
+    cudaVideoChromaFormat probed_chroma_format;
+    int probed_width;
+    int probed_height;
     int ret = 0;
 
     enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_CUDA,
                                        AV_PIX_FMT_NV12,
                                        AV_PIX_FMT_NONE };
 
+    enum AVPixelFormat probed_pix_fmt = (avctx->pix_fmt <= 0 ?
+                                         AV_PIX_FMT_YUV420P :
+                                         avctx->pix_fmt);
+
+    ret = convert_to_cuda_video_chroma_format(probed_pix_fmt, &probed_chroma_format);
+    if (ret < 0) {
+        // pixel format is not supported:
+        return ret;
+    }
+    probed_width = avctx->coded_width ? avctx->coded_width : 1280;
+    probed_height = avctx->coded_height ? avctx->coded_height : 720;
+
     // Accelerated transcoding scenarios with 'ffmpeg' require that the
     // pix_fmt be set to AV_PIX_FMT_CUDA early. The sw_pix_fmt, and the
     // pix_fmt for non-accelerated transcoding, do not need to be correct
@@ -824,7 +873,10 @@  static av_cold int cuvid_decode_init(AVCodecContext *avctx)
     if (ret < 0)
         goto error;
 
-    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
+    ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo,
+                                   probed_chroma_format,
+                                   probed_width,
+                                   probed_height);
     if (ret < 0)
         goto error;