[FFmpeg-devel,v2] ffmpeg: Add option to force a specific decode format

Submitted by Mark Thompson on Nov. 13, 2018, 11:43 p.m.

Details

Message ID e9ad0342-3656-6292-9120-4803203a9e50@jkqxz.net
State New
Headers show

Commit Message

Mark Thompson Nov. 13, 2018, 11:43 p.m.
Fixes #7519.
---
 doc/ffmpeg.texi      | 13 ++++++++++++
 fftools/ffmpeg.c     | 10 ++++++++++
 fftools/ffmpeg.h     |  4 ++++
 fftools/ffmpeg_opt.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 74 insertions(+)

Comments

Linjie Fu Nov. 14, 2018, 12:50 a.m.
> -----Original Message-----

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

> Of Mark Thompson

> Sent: Wednesday, November 14, 2018 07:44

> To: ffmpeg-devel@ffmpeg.org

> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a specific

> decode format

> 

> Fixes #7519.

> ---

>  doc/ffmpeg.texi      | 13 ++++++++++++

>  fftools/ffmpeg.c     | 10 ++++++++++

>  fftools/ffmpeg.h     |  4 ++++

>  fftools/ffmpeg_opt.c | 47

> ++++++++++++++++++++++++++++++++++++++++++++

>  4 files changed, 74 insertions(+)

> 

> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi

> index 3717f22d42..d127bc0f0d 100644

> --- a/doc/ffmpeg.texi

> +++ b/doc/ffmpeg.texi

> @@ -920,6 +920,19 @@ would be more efficient.

>  When doing stream copy, copy also non-key frames found at the

>  beginning.

> 

> +@item -decode_format[:@var{stream_specifier}]

> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})

> +Set the possible output formats to be used by the decoder for this stream.

> +If the decoder does not natively support any format in the given list for

> +the input stream then decoding will fail rather than continuing with a

> +different format.

> +

> +In general this should not be set - the decoder will select an output

> +format based on the input stream parameters and available components,

> and

> +that will be automatically converted to whatever the output requires.  It

> +may be useful to force a hardware decoder supporting output in multiple

> +different memory types to pick a desired one, or to ensure that a hardware

> +decoder is used when software fallback is also available.

> +

>  @item -init_hw_device

> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]

>  Initialise a new hardware device of type @var{type} called @var{name},

> using the

>  given device parameters.

> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c

> index 38c21e944a..c651c8d3a8 100644

> --- a/fftools/ffmpeg.c

> +++ b/fftools/ffmpeg.c

> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)

>          avsubtitle_free(&ist->prev_sub.subtitle);

>          av_frame_free(&ist->sub2video.frame);

>          av_freep(&ist->filters);

> +        av_freep(&ist->decode_formats);

>          av_freep(&ist->hwaccel_device);

>          av_freep(&ist->dts_buffer);

> 

> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat

> get_format(AVCodecContext *s, const enum AVPixelFormat

>          const AVCodecHWConfig  *config = NULL;

>          int i;

> 

> +        if (ist->decode_formats) {

> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++) {

> +                if (ist->decode_formats[i] == *p)

> +                    break;

> +            }

> +            if (ist->decode_formats[i] != *p)

> +                continue;

> +        }

> +

>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))

>              break;

> 

> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h

> index eb1eaf6363..b06fd18b1c 100644

> --- a/fftools/ffmpeg.h

> +++ b/fftools/ffmpeg.h

> @@ -125,6 +125,8 @@ typedef struct OptionsContext {

>      int        nb_ts_scale;

>      SpecifierOpt *dump_attachment;

>      int        nb_dump_attachment;

> +    SpecifierOpt *decode_formats;

> +    int        nb_decode_formats;

>      SpecifierOpt *hwaccels;

>      int        nb_hwaccels;

>      SpecifierOpt *hwaccel_devices;

> @@ -334,6 +336,8 @@ typedef struct InputStream {

>      int top_field_first;

>      int guess_layout_max;

> 

> +    enum AVPixelFormat *decode_formats;

> +

>      int autorotate;

> 

>      int fix_sub_duration;

> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c

> index d4851a2cd8..63bb05053b 100644

> --- a/fftools/ffmpeg_opt.c

> +++ b/fftools/ffmpeg_opt.c

> @@ -701,6 +701,7 @@ static void add_input_streams(OptionsContext *o,

> AVFormatContext *ic)

>          AVStream *st = ic->streams[i];

>          AVCodecParameters *par = st->codecpar;

>          InputStream *ist = av_mallocz(sizeof(*ist));

> +        char *decode_formats = NULL;

>          char *framerate = NULL, *hwaccel_device = NULL;

>          const char *hwaccel = NULL;

>          char *hwaccel_output_format = NULL;

> @@ -797,6 +798,49 @@ static void add_input_streams(OptionsContext *o,

> AVFormatContext *ic)

>              ist->top_field_first = -1;

>              MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic,

> st);

> 

> +            MATCH_PER_STREAM_OPT(decode_formats, str, decode_formats, ic,

> st);

> +            if (decode_formats) {

> +                const char *p, *q;

> +                int i, nb_formats;

> +                char tmp[32];

> +

> +                nb_formats = 0;

> +                for (p = decode_formats; p; p = strchr(p + 1, ','))

> +                    ++nb_formats;

> +

> +                ist->decode_formats =

> +                    av_malloc_array(nb_formats + 1, sizeof(*ist->decode_formats));

> +                if (!ist->decode_formats)

> +                    exit_program(1);

> +

> +                p = decode_formats;

> +                for (i = 0; i < nb_formats; i++) {

> +                    q = strchr(p, ',');

> +                    if (!q) {

> +                        ist->decode_formats[i] = av_get_pix_fmt(p);

> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> +                            break;

> +                        continue;

> +                    }

> +                    if (q - p > sizeof(tmp) - 1)

> +                        break;

> +                    memcpy(tmp, p, q - p);

> +                    tmp[q - p] = 0;

> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);

> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> +                        break;

> +                    p = q + 1;

> +                }

> +                if (i < nb_formats) {

> +                    av_log(NULL, AV_LOG_FATAL,

> +                           "Unrecognised decode format: %s.\n", p);

> +                    exit_program(1);

> +                }

> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;

> +            } else {

> +                ist->decode_formats = NULL;

> +            }

> +

>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);

>              if (hwaccel) {

>                  // The NVDEC hwaccels use a CUDA device, so remap the name here.

> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {

>          "audio bitrate (please use -b:a)", "bitrate" },

>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,

> { .func_arg = opt_bitrate },

>          "video bitrate (please use -b:v)", "bitrate" },

> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |

> OPT_EXPERT |

> +                        OPT_SPEC | OPT_INPUT,                                    { .off =

> OFFSET(decode_formats) },

> +        "set output format(s) to be used by decoder, fail if none of these

> formats are available", "format" },

>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |

>                            OPT_SPEC | OPT_INPUT,                                  { .off =

> OFFSET(hwaccels) },

>          "use HW accelerated decoding", "hwaccel name" },

> --

> 2.19.1

> 


Is there any other conditions which will cause the hwaccel failed and fallback to software path? 
Like profile was not supported by hardware?
I applied the patch and tested some cases, like yuv422  VAAPI decoding (currently not supported), HEVC decoding with unsupported profile.
It reports an error like "hwaccel initialization returned error", and continue to decode and produce less frames than expected. (which I thought fallback still happened).

Thanks,
- Linjie
Mark Thompson Nov. 14, 2018, 1:10 a.m.
On 14/11/18 00:50, Fu, Linjie wrote:
>> -----Original Message-----
>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf
>> Of Mark Thompson
>> Sent: Wednesday, November 14, 2018 07:44
>> To: ffmpeg-devel@ffmpeg.org
>> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a specific
>> decode format
>>
>> Fixes #7519.
>> ---
>>  doc/ffmpeg.texi      | 13 ++++++++++++
>>  fftools/ffmpeg.c     | 10 ++++++++++
>>  fftools/ffmpeg.h     |  4 ++++
>>  fftools/ffmpeg_opt.c | 47
>> ++++++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 74 insertions(+)
>>
>> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
>> index 3717f22d42..d127bc0f0d 100644
>> --- a/doc/ffmpeg.texi
>> +++ b/doc/ffmpeg.texi
>> @@ -920,6 +920,19 @@ would be more efficient.
>>  When doing stream copy, copy also non-key frames found at the
>>  beginning.
>>
>> +@item -decode_format[:@var{stream_specifier}]
>> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})
>> +Set the possible output formats to be used by the decoder for this stream.
>> +If the decoder does not natively support any format in the given list for
>> +the input stream then decoding will fail rather than continuing with a
>> +different format.
>> +
>> +In general this should not be set - the decoder will select an output
>> +format based on the input stream parameters and available components,
>> and
>> +that will be automatically converted to whatever the output requires.  It
>> +may be useful to force a hardware decoder supporting output in multiple
>> +different memory types to pick a desired one, or to ensure that a hardware
>> +decoder is used when software fallback is also available.
>> +
>>  @item -init_hw_device
>> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]
>>  Initialise a new hardware device of type @var{type} called @var{name},
>> using the
>>  given device parameters.
>> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
>> index 38c21e944a..c651c8d3a8 100644
>> --- a/fftools/ffmpeg.c
>> +++ b/fftools/ffmpeg.c
>> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)
>>          avsubtitle_free(&ist->prev_sub.subtitle);
>>          av_frame_free(&ist->sub2video.frame);
>>          av_freep(&ist->filters);
>> +        av_freep(&ist->decode_formats);
>>          av_freep(&ist->hwaccel_device);
>>          av_freep(&ist->dts_buffer);
>>
>> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat
>> get_format(AVCodecContext *s, const enum AVPixelFormat
>>          const AVCodecHWConfig  *config = NULL;
>>          int i;
>>
>> +        if (ist->decode_formats) {
>> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++) {
>> +                if (ist->decode_formats[i] == *p)
>> +                    break;
>> +            }
>> +            if (ist->decode_formats[i] != *p)
>> +                continue;
>> +        }
>> +
>>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
>>              break;
>>
>> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
>> index eb1eaf6363..b06fd18b1c 100644
>> --- a/fftools/ffmpeg.h
>> +++ b/fftools/ffmpeg.h
>> @@ -125,6 +125,8 @@ typedef struct OptionsContext {
>>      int        nb_ts_scale;
>>      SpecifierOpt *dump_attachment;
>>      int        nb_dump_attachment;
>> +    SpecifierOpt *decode_formats;
>> +    int        nb_decode_formats;
>>      SpecifierOpt *hwaccels;
>>      int        nb_hwaccels;
>>      SpecifierOpt *hwaccel_devices;
>> @@ -334,6 +336,8 @@ typedef struct InputStream {
>>      int top_field_first;
>>      int guess_layout_max;
>>
>> +    enum AVPixelFormat *decode_formats;
>> +
>>      int autorotate;
>>
>>      int fix_sub_duration;
>> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
>> index d4851a2cd8..63bb05053b 100644
>> --- a/fftools/ffmpeg_opt.c
>> +++ b/fftools/ffmpeg_opt.c
>> @@ -701,6 +701,7 @@ static void add_input_streams(OptionsContext *o,
>> AVFormatContext *ic)
>>          AVStream *st = ic->streams[i];
>>          AVCodecParameters *par = st->codecpar;
>>          InputStream *ist = av_mallocz(sizeof(*ist));
>> +        char *decode_formats = NULL;
>>          char *framerate = NULL, *hwaccel_device = NULL;
>>          const char *hwaccel = NULL;
>>          char *hwaccel_output_format = NULL;
>> @@ -797,6 +798,49 @@ static void add_input_streams(OptionsContext *o,
>> AVFormatContext *ic)
>>              ist->top_field_first = -1;
>>              MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic,
>> st);
>>
>> +            MATCH_PER_STREAM_OPT(decode_formats, str, decode_formats, ic,
>> st);
>> +            if (decode_formats) {
>> +                const char *p, *q;
>> +                int i, nb_formats;
>> +                char tmp[32];
>> +
>> +                nb_formats = 0;
>> +                for (p = decode_formats; p; p = strchr(p + 1, ','))
>> +                    ++nb_formats;
>> +
>> +                ist->decode_formats =
>> +                    av_malloc_array(nb_formats + 1, sizeof(*ist->decode_formats));
>> +                if (!ist->decode_formats)
>> +                    exit_program(1);
>> +
>> +                p = decode_formats;
>> +                for (i = 0; i < nb_formats; i++) {
>> +                    q = strchr(p, ',');
>> +                    if (!q) {
>> +                        ist->decode_formats[i] = av_get_pix_fmt(p);
>> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
>> +                            break;
>> +                        continue;
>> +                    }
>> +                    if (q - p > sizeof(tmp) - 1)
>> +                        break;
>> +                    memcpy(tmp, p, q - p);
>> +                    tmp[q - p] = 0;
>> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);
>> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
>> +                        break;
>> +                    p = q + 1;
>> +                }
>> +                if (i < nb_formats) {
>> +                    av_log(NULL, AV_LOG_FATAL,
>> +                           "Unrecognised decode format: %s.\n", p);
>> +                    exit_program(1);
>> +                }
>> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;
>> +            } else {
>> +                ist->decode_formats = NULL;
>> +            }
>> +
>>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);
>>              if (hwaccel) {
>>                  // The NVDEC hwaccels use a CUDA device, so remap the name here.
>> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {
>>          "audio bitrate (please use -b:a)", "bitrate" },
>>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,
>> { .func_arg = opt_bitrate },
>>          "video bitrate (please use -b:v)", "bitrate" },
>> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |
>> OPT_EXPERT |
>> +                        OPT_SPEC | OPT_INPUT,                                    { .off =
>> OFFSET(decode_formats) },
>> +        "set output format(s) to be used by decoder, fail if none of these
>> formats are available", "format" },
>>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
>>                            OPT_SPEC | OPT_INPUT,                                  { .off =
>> OFFSET(hwaccels) },
>>          "use HW accelerated decoding", "hwaccel name" },
>> --
>> 2.19.1
>>
> 
> Is there any other conditions which will cause the hwaccel failed and fallback to software path? 
> Like profile was not supported by hardware?

If this option is set then get_format will never choose a format not on the list you provide, so it shouldn't be able to use software decode unless a software format is on the list.

> I applied the patch and tested some cases, like yuv422  VAAPI decoding (currently not supported), HEVC decoding with unsupported profile.
> It reports an error like "hwaccel initialization returned error", and continue to decode and produce less frames than expected. (which I thought fallback still happened).

Can you give an example?  With -v debug there will be output saying exactly what happened in ff_get_format().

- Mark
Linjie Fu Nov. 14, 2018, 1:35 a.m.
> -----Original Message-----

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

> Of Mark Thompson

> Sent: Wednesday, November 14, 2018 09:11

> To: ffmpeg-devel@ffmpeg.org

> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> specific decode format

> 

> On 14/11/18 00:50, Fu, Linjie wrote:

> >> -----Original Message-----

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

> Behalf

> >> Of Mark Thompson

> >> Sent: Wednesday, November 14, 2018 07:44

> >> To: ffmpeg-devel@ffmpeg.org

> >> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> specific

> >> decode format

> >>

> >> Fixes #7519.

> >> ---

> >>  doc/ffmpeg.texi      | 13 ++++++++++++

> >>  fftools/ffmpeg.c     | 10 ++++++++++

> >>  fftools/ffmpeg.h     |  4 ++++

> >>  fftools/ffmpeg_opt.c | 47

> >> ++++++++++++++++++++++++++++++++++++++++++++

> >>  4 files changed, 74 insertions(+)

> >>

> >> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi

> >> index 3717f22d42..d127bc0f0d 100644

> >> --- a/doc/ffmpeg.texi

> >> +++ b/doc/ffmpeg.texi

> >> @@ -920,6 +920,19 @@ would be more efficient.

> >>  When doing stream copy, copy also non-key frames found at the

> >>  beginning.

> >>

> >> +@item -decode_format[:@var{stream_specifier}]

> >> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})

> >> +Set the possible output formats to be used by the decoder for this

> stream.

> >> +If the decoder does not natively support any format in the given list for

> >> +the input stream then decoding will fail rather than continuing with a

> >> +different format.

> >> +

> >> +In general this should not be set - the decoder will select an output

> >> +format based on the input stream parameters and available components,

> >> and

> >> +that will be automatically converted to whatever the output requires.  It

> >> +may be useful to force a hardware decoder supporting output in

> multiple

> >> +different memory types to pick a desired one, or to ensure that a

> hardware

> >> +decoder is used when software fallback is also available.

> >> +

> >>  @item -init_hw_device

> >> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]

> >>  Initialise a new hardware device of type @var{type} called @var{name},

> >> using the

> >>  given device parameters.

> >> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c

> >> index 38c21e944a..c651c8d3a8 100644

> >> --- a/fftools/ffmpeg.c

> >> +++ b/fftools/ffmpeg.c

> >> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)

> >>          avsubtitle_free(&ist->prev_sub.subtitle);

> >>          av_frame_free(&ist->sub2video.frame);

> >>          av_freep(&ist->filters);

> >> +        av_freep(&ist->decode_formats);

> >>          av_freep(&ist->hwaccel_device);

> >>          av_freep(&ist->dts_buffer);

> >>

> >> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat

> >> get_format(AVCodecContext *s, const enum AVPixelFormat

> >>          const AVCodecHWConfig  *config = NULL;

> >>          int i;

> >>

> >> +        if (ist->decode_formats) {

> >> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++) {

> >> +                if (ist->decode_formats[i] == *p)

> >> +                    break;

> >> +            }

> >> +            if (ist->decode_formats[i] != *p)

> >> +                continue;

> >> +        }

> >> +

> >>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))

> >>              break;

> >>

> >> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h

> >> index eb1eaf6363..b06fd18b1c 100644

> >> --- a/fftools/ffmpeg.h

> >> +++ b/fftools/ffmpeg.h

> >> @@ -125,6 +125,8 @@ typedef struct OptionsContext {

> >>      int        nb_ts_scale;

> >>      SpecifierOpt *dump_attachment;

> >>      int        nb_dump_attachment;

> >> +    SpecifierOpt *decode_formats;

> >> +    int        nb_decode_formats;

> >>      SpecifierOpt *hwaccels;

> >>      int        nb_hwaccels;

> >>      SpecifierOpt *hwaccel_devices;

> >> @@ -334,6 +336,8 @@ typedef struct InputStream {

> >>      int top_field_first;

> >>      int guess_layout_max;

> >>

> >> +    enum AVPixelFormat *decode_formats;

> >> +

> >>      int autorotate;

> >>

> >>      int fix_sub_duration;

> >> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c

> >> index d4851a2cd8..63bb05053b 100644

> >> --- a/fftools/ffmpeg_opt.c

> >> +++ b/fftools/ffmpeg_opt.c

> >> @@ -701,6 +701,7 @@ static void add_input_streams(OptionsContext *o,

> >> AVFormatContext *ic)

> >>          AVStream *st = ic->streams[i];

> >>          AVCodecParameters *par = st->codecpar;

> >>          InputStream *ist = av_mallocz(sizeof(*ist));

> >> +        char *decode_formats = NULL;

> >>          char *framerate = NULL, *hwaccel_device = NULL;

> >>          const char *hwaccel = NULL;

> >>          char *hwaccel_output_format = NULL;

> >> @@ -797,6 +798,49 @@ static void add_input_streams(OptionsContext

> *o,

> >> AVFormatContext *ic)

> >>              ist->top_field_first = -1;

> >>              MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic,

> >> st);

> >>

> >> +            MATCH_PER_STREAM_OPT(decode_formats, str,

> decode_formats, ic,

> >> st);

> >> +            if (decode_formats) {

> >> +                const char *p, *q;

> >> +                int i, nb_formats;

> >> +                char tmp[32];

> >> +

> >> +                nb_formats = 0;

> >> +                for (p = decode_formats; p; p = strchr(p + 1, ','))

> >> +                    ++nb_formats;

> >> +

> >> +                ist->decode_formats =

> >> +                    av_malloc_array(nb_formats + 1, sizeof(*ist-

> >decode_formats));

> >> +                if (!ist->decode_formats)

> >> +                    exit_program(1);

> >> +

> >> +                p = decode_formats;

> >> +                for (i = 0; i < nb_formats; i++) {

> >> +                    q = strchr(p, ',');

> >> +                    if (!q) {

> >> +                        ist->decode_formats[i] = av_get_pix_fmt(p);

> >> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> >> +                            break;

> >> +                        continue;

> >> +                    }

> >> +                    if (q - p > sizeof(tmp) - 1)

> >> +                        break;

> >> +                    memcpy(tmp, p, q - p);

> >> +                    tmp[q - p] = 0;

> >> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);

> >> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> >> +                        break;

> >> +                    p = q + 1;

> >> +                }

> >> +                if (i < nb_formats) {

> >> +                    av_log(NULL, AV_LOG_FATAL,

> >> +                           "Unrecognised decode format: %s.\n", p);

> >> +                    exit_program(1);

> >> +                }

> >> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;

> >> +            } else {

> >> +                ist->decode_formats = NULL;

> >> +            }

> >> +

> >>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);

> >>              if (hwaccel) {

> >>                  // The NVDEC hwaccels use a CUDA device, so remap the name

> here.

> >> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {

> >>          "audio bitrate (please use -b:a)", "bitrate" },

> >>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,

> >> { .func_arg = opt_bitrate },

> >>          "video bitrate (please use -b:v)", "bitrate" },

> >> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |

> >> OPT_EXPERT |

> >> +                        OPT_SPEC | OPT_INPUT,                                    { .off =

> >> OFFSET(decode_formats) },

> >> +        "set output format(s) to be used by decoder, fail if none of these

> >> formats are available", "format" },

> >>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT

> |

> >>                            OPT_SPEC | OPT_INPUT,                                  { .off =

> >> OFFSET(hwaccels) },

> >>          "use HW accelerated decoding", "hwaccel name" },

> >> --

> >> 2.19.1

> >>

> >

> > Is there any other conditions which will cause the hwaccel failed and

> fallback to software path?

> > Like profile was not supported by hardware?

> 

> If this option is set then get_format will never choose a format not on the list

> you provide, so it shouldn't be able to use software decode unless a

> software format is on the list.

> 

> > I applied the patch and tested some cases, like yuv422  VAAPI decoding

> (currently not supported), HEVC decoding with unsupported profile.

> > It reports an error like "hwaccel initialization returned error", and continue

> to decode and produce less frames than expected. (which I thought fallback

> still happened).

> 

> Can you give an example?  With -v debug there will be output saying exactly

> what happened in ff_get_format().

> 

> - Mark



Got information like this:

[hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0
[hevc @ 0x55b87457dbc0] Decoding SPS
[hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0
[hevc @ 0x55b87457dbc0] Decoding PPS
[hevc @ 0x55b87457dbc0] Format vaapi_vld chosen by get_format().
[hevc @ 0x55b87457dbc0] Format vaapi_vld requires hwaccel initialisation.
[hevc @ 0x55b87457dbc0] Codec hevc profile 0 not supported for hardware decode.
[hevc @ 0x55b87457dbc0] Failed setup for format vaapi_vld: hwaccel initialisation returned error.
[hevc @ 0x55b87457dbc0] Format vaapi_vld not usable, retrying get_format() without it.
[hevc @ 0x55b87457dbc0] Error parsing NAL unit #3.
cur_dts is invalid (this is harmless if it occurs once at the start per stream)
[hevc @ 0x55b87458b7c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x55b87458b7c0] Could not find ref with POC 0

See the attached report file  for more details.

Thanks,
- Linjie
Mark Thompson Nov. 14, 2018, 9:48 p.m.
On 14/11/18 01:35, Fu, Linjie wrote:
>> -----Original Message-----
>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf
>> Of Mark Thompson
>> Sent: Wednesday, November 14, 2018 09:11
>> To: ffmpeg-devel@ffmpeg.org
>> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a
>> specific decode format
>>
>> On 14/11/18 00:50, Fu, Linjie wrote:
>>>> -----Original Message-----
>>>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On
>> Behalf
>>>> Of Mark Thompson
>>>> Sent: Wednesday, November 14, 2018 07:44
>>>> To: ffmpeg-devel@ffmpeg.org
>>>> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a
>> specific
>>>> decode format
>>>>
>>>> Fixes #7519.
>>>> ---
>>>>  doc/ffmpeg.texi      | 13 ++++++++++++
>>>>  fftools/ffmpeg.c     | 10 ++++++++++
>>>>  fftools/ffmpeg.h     |  4 ++++
>>>>  fftools/ffmpeg_opt.c | 47
>>>> ++++++++++++++++++++++++++++++++++++++++++++
>>>>  4 files changed, 74 insertions(+)
>>>>
>>>> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
>>>> index 3717f22d42..d127bc0f0d 100644
>>>> --- a/doc/ffmpeg.texi
>>>> +++ b/doc/ffmpeg.texi
>>>> @@ -920,6 +920,19 @@ would be more efficient.
>>>>  When doing stream copy, copy also non-key frames found at the
>>>>  beginning.
>>>>
>>>> +@item -decode_format[:@var{stream_specifier}]
>>>> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})
>>>> +Set the possible output formats to be used by the decoder for this
>> stream.
>>>> +If the decoder does not natively support any format in the given list for
>>>> +the input stream then decoding will fail rather than continuing with a
>>>> +different format.
>>>> +
>>>> +In general this should not be set - the decoder will select an output
>>>> +format based on the input stream parameters and available components,
>>>> and
>>>> +that will be automatically converted to whatever the output requires.  It
>>>> +may be useful to force a hardware decoder supporting output in
>> multiple
>>>> +different memory types to pick a desired one, or to ensure that a
>> hardware
>>>> +decoder is used when software fallback is also available.
>>>> +
>>>>  @item -init_hw_device
>>>> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]
>>>>  Initialise a new hardware device of type @var{type} called @var{name},
>>>> using the
>>>>  given device parameters.
>>>> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
>>>> index 38c21e944a..c651c8d3a8 100644
>>>> --- a/fftools/ffmpeg.c
>>>> +++ b/fftools/ffmpeg.c
>>>> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)
>>>>          avsubtitle_free(&ist->prev_sub.subtitle);
>>>>          av_frame_free(&ist->sub2video.frame);
>>>>          av_freep(&ist->filters);
>>>> +        av_freep(&ist->decode_formats);
>>>>          av_freep(&ist->hwaccel_device);
>>>>          av_freep(&ist->dts_buffer);
>>>>
>>>> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat
>>>> get_format(AVCodecContext *s, const enum AVPixelFormat
>>>>          const AVCodecHWConfig  *config = NULL;
>>>>          int i;
>>>>
>>>> +        if (ist->decode_formats) {
>>>> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++) {
>>>> +                if (ist->decode_formats[i] == *p)
>>>> +                    break;
>>>> +            }
>>>> +            if (ist->decode_formats[i] != *p)
>>>> +                continue;
>>>> +        }
>>>> +
>>>>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
>>>>              break;
>>>>
>>>> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
>>>> index eb1eaf6363..b06fd18b1c 100644
>>>> --- a/fftools/ffmpeg.h
>>>> +++ b/fftools/ffmpeg.h
>>>> @@ -125,6 +125,8 @@ typedef struct OptionsContext {
>>>>      int        nb_ts_scale;
>>>>      SpecifierOpt *dump_attachment;
>>>>      int        nb_dump_attachment;
>>>> +    SpecifierOpt *decode_formats;
>>>> +    int        nb_decode_formats;
>>>>      SpecifierOpt *hwaccels;
>>>>      int        nb_hwaccels;
>>>>      SpecifierOpt *hwaccel_devices;
>>>> @@ -334,6 +336,8 @@ typedef struct InputStream {
>>>>      int top_field_first;
>>>>      int guess_layout_max;
>>>>
>>>> +    enum AVPixelFormat *decode_formats;
>>>> +
>>>>      int autorotate;
>>>>
>>>>      int fix_sub_duration;
>>>> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
>>>> index d4851a2cd8..63bb05053b 100644
>>>> --- a/fftools/ffmpeg_opt.c
>>>> +++ b/fftools/ffmpeg_opt.c
>>>> @@ -701,6 +701,7 @@ static void add_input_streams(OptionsContext *o,
>>>> AVFormatContext *ic)
>>>>          AVStream *st = ic->streams[i];
>>>>          AVCodecParameters *par = st->codecpar;
>>>>          InputStream *ist = av_mallocz(sizeof(*ist));
>>>> +        char *decode_formats = NULL;
>>>>          char *framerate = NULL, *hwaccel_device = NULL;
>>>>          const char *hwaccel = NULL;
>>>>          char *hwaccel_output_format = NULL;
>>>> @@ -797,6 +798,49 @@ static void add_input_streams(OptionsContext
>> *o,
>>>> AVFormatContext *ic)
>>>>              ist->top_field_first = -1;
>>>>              MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic,
>>>> st);
>>>>
>>>> +            MATCH_PER_STREAM_OPT(decode_formats, str,
>> decode_formats, ic,
>>>> st);
>>>> +            if (decode_formats) {
>>>> +                const char *p, *q;
>>>> +                int i, nb_formats;
>>>> +                char tmp[32];
>>>> +
>>>> +                nb_formats = 0;
>>>> +                for (p = decode_formats; p; p = strchr(p + 1, ','))
>>>> +                    ++nb_formats;
>>>> +
>>>> +                ist->decode_formats =
>>>> +                    av_malloc_array(nb_formats + 1, sizeof(*ist-
>>> decode_formats));
>>>> +                if (!ist->decode_formats)
>>>> +                    exit_program(1);
>>>> +
>>>> +                p = decode_formats;
>>>> +                for (i = 0; i < nb_formats; i++) {
>>>> +                    q = strchr(p, ',');
>>>> +                    if (!q) {
>>>> +                        ist->decode_formats[i] = av_get_pix_fmt(p);
>>>> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
>>>> +                            break;
>>>> +                        continue;
>>>> +                    }
>>>> +                    if (q - p > sizeof(tmp) - 1)
>>>> +                        break;
>>>> +                    memcpy(tmp, p, q - p);
>>>> +                    tmp[q - p] = 0;
>>>> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);
>>>> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
>>>> +                        break;
>>>> +                    p = q + 1;
>>>> +                }
>>>> +                if (i < nb_formats) {
>>>> +                    av_log(NULL, AV_LOG_FATAL,
>>>> +                           "Unrecognised decode format: %s.\n", p);
>>>> +                    exit_program(1);
>>>> +                }
>>>> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;
>>>> +            } else {
>>>> +                ist->decode_formats = NULL;
>>>> +            }
>>>> +
>>>>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);
>>>>              if (hwaccel) {
>>>>                  // The NVDEC hwaccels use a CUDA device, so remap the name
>> here.
>>>> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {
>>>>          "audio bitrate (please use -b:a)", "bitrate" },
>>>>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,
>>>> { .func_arg = opt_bitrate },
>>>>          "video bitrate (please use -b:v)", "bitrate" },
>>>> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |
>>>> OPT_EXPERT |
>>>> +                        OPT_SPEC | OPT_INPUT,                                    { .off =
>>>> OFFSET(decode_formats) },
>>>> +        "set output format(s) to be used by decoder, fail if none of these
>>>> formats are available", "format" },
>>>>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT
>> |
>>>>                            OPT_SPEC | OPT_INPUT,                                  { .off =
>>>> OFFSET(hwaccels) },
>>>>          "use HW accelerated decoding", "hwaccel name" },
>>>> --
>>>> 2.19.1
>>>>
>>>
>>> Is there any other conditions which will cause the hwaccel failed and
>> fallback to software path?
>>> Like profile was not supported by hardware?
>>
>> If this option is set then get_format will never choose a format not on the list
>> you provide, so it shouldn't be able to use software decode unless a
>> software format is on the list.
>>
>>> I applied the patch and tested some cases, like yuv422  VAAPI decoding
>> (currently not supported), HEVC decoding with unsupported profile.
>>> It reports an error like "hwaccel initialization returned error", and continue
>> to decode and produce less frames than expected. (which I thought fallback
>> still happened).
>>
>> Can you give an example?  With -v debug there will be output saying exactly
>> what happened in ff_get_format().
>>
>> - Mark
> 
> 
> Got information like this:
> 
> [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0
> [hevc @ 0x55b87457dbc0] Decoding SPS
> [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0
> [hevc @ 0x55b87457dbc0] Decoding PPS
> [hevc @ 0x55b87457dbc0] Format vaapi_vld chosen by get_format().
> [hevc @ 0x55b87457dbc0] Format vaapi_vld requires hwaccel initialisation.
> [hevc @ 0x55b87457dbc0] Codec hevc profile 0 not supported for hardware decode.
> [hevc @ 0x55b87457dbc0] Failed setup for format vaapi_vld: hwaccel initialisation returned error.
> [hevc @ 0x55b87457dbc0] Format vaapi_vld not usable, retrying get_format() without it.
> [hevc @ 0x55b87457dbc0] Error parsing NAL unit #3.
> cur_dts is invalid (this is harmless if it occurs once at the start per stream)
> [hevc @ 0x55b87458b7c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0, temporal_id: 0
> [hevc @ 0x55b87458b7c0] Could not find ref with POC 0
> 
> See the attached report file  for more details.

This is a decoder bug - get_format() is not correctly called again after it fails, and the decoder then uses the software pixfmt derived from the SPS on subsequent frames.

(I did pretty much all of my testing with H.264, which does not have this problem.)

- Mark
Linjie Fu Nov. 16, 2018, 8:36 a.m.
> -----Original Message-----

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

> Of Mark Thompson

> Sent: Thursday, November 15, 2018 05:48

> To: ffmpeg-devel@ffmpeg.org

> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> specific decode format

> 

> On 14/11/18 01:35, Fu, Linjie wrote:

> >> -----Original Message-----

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

> Behalf

> >> Of Mark Thompson

> >> Sent: Wednesday, November 14, 2018 09:11

> >> To: ffmpeg-devel@ffmpeg.org

> >> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> >> specific decode format

> >>

> >> On 14/11/18 00:50, Fu, Linjie wrote:

> >>>> -----Original Message-----

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

> >> Behalf

> >>>> Of Mark Thompson

> >>>> Sent: Wednesday, November 14, 2018 07:44

> >>>> To: ffmpeg-devel@ffmpeg.org

> >>>> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> >> specific

> >>>> decode format

> >>>>

> >>>> Fixes #7519.

> >>>> ---

> >>>>  doc/ffmpeg.texi      | 13 ++++++++++++

> >>>>  fftools/ffmpeg.c     | 10 ++++++++++

> >>>>  fftools/ffmpeg.h     |  4 ++++

> >>>>  fftools/ffmpeg_opt.c | 47

> >>>> ++++++++++++++++++++++++++++++++++++++++++++

> >>>>  4 files changed, 74 insertions(+)

> >>>>

> >>>> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi

> >>>> index 3717f22d42..d127bc0f0d 100644

> >>>> --- a/doc/ffmpeg.texi

> >>>> +++ b/doc/ffmpeg.texi

> >>>> @@ -920,6 +920,19 @@ would be more efficient.

> >>>>  When doing stream copy, copy also non-key frames found at the

> >>>>  beginning.

> >>>>

> >>>> +@item -decode_format[:@var{stream_specifier}]

> >>>> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})

> >>>> +Set the possible output formats to be used by the decoder for this

> >> stream.

> >>>> +If the decoder does not natively support any format in the given list

> for

> >>>> +the input stream then decoding will fail rather than continuing with a

> >>>> +different format.

> >>>> +

> >>>> +In general this should not be set - the decoder will select an output

> >>>> +format based on the input stream parameters and available

> components,

> >>>> and

> >>>> +that will be automatically converted to whatever the output requires.

> It

> >>>> +may be useful to force a hardware decoder supporting output in

> >> multiple

> >>>> +different memory types to pick a desired one, or to ensure that a

> >> hardware

> >>>> +decoder is used when software fallback is also available.

> >>>> +

> >>>>  @item -init_hw_device

> >>>> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]

> >>>>  Initialise a new hardware device of type @var{type} called

> @var{name},

> >>>> using the

> >>>>  given device parameters.

> >>>> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c

> >>>> index 38c21e944a..c651c8d3a8 100644

> >>>> --- a/fftools/ffmpeg.c

> >>>> +++ b/fftools/ffmpeg.c

> >>>> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)

> >>>>          avsubtitle_free(&ist->prev_sub.subtitle);

> >>>>          av_frame_free(&ist->sub2video.frame);

> >>>>          av_freep(&ist->filters);

> >>>> +        av_freep(&ist->decode_formats);

> >>>>          av_freep(&ist->hwaccel_device);

> >>>>          av_freep(&ist->dts_buffer);

> >>>>

> >>>> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat

> >>>> get_format(AVCodecContext *s, const enum AVPixelFormat

> >>>>          const AVCodecHWConfig  *config = NULL;

> >>>>          int i;

> >>>>

> >>>> +        if (ist->decode_formats) {

> >>>> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++) {

> >>>> +                if (ist->decode_formats[i] == *p)

> >>>> +                    break;

> >>>> +            }

> >>>> +            if (ist->decode_formats[i] != *p)

> >>>> +                continue;

> >>>> +        }

> >>>> +

> >>>>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))

> >>>>              break;

> >>>>

> >>>> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h

> >>>> index eb1eaf6363..b06fd18b1c 100644

> >>>> --- a/fftools/ffmpeg.h

> >>>> +++ b/fftools/ffmpeg.h

> >>>> @@ -125,6 +125,8 @@ typedef struct OptionsContext {

> >>>>      int        nb_ts_scale;

> >>>>      SpecifierOpt *dump_attachment;

> >>>>      int        nb_dump_attachment;

> >>>> +    SpecifierOpt *decode_formats;

> >>>> +    int        nb_decode_formats;

> >>>>      SpecifierOpt *hwaccels;

> >>>>      int        nb_hwaccels;

> >>>>      SpecifierOpt *hwaccel_devices;

> >>>> @@ -334,6 +336,8 @@ typedef struct InputStream {

> >>>>      int top_field_first;

> >>>>      int guess_layout_max;

> >>>>

> >>>> +    enum AVPixelFormat *decode_formats;

> >>>> +

> >>>>      int autorotate;

> >>>>

> >>>>      int fix_sub_duration;

> >>>> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c

> >>>> index d4851a2cd8..63bb05053b 100644

> >>>> --- a/fftools/ffmpeg_opt.c

> >>>> +++ b/fftools/ffmpeg_opt.c

> >>>> @@ -701,6 +701,7 @@ static void add_input_streams(OptionsContext

> *o,

> >>>> AVFormatContext *ic)

> >>>>          AVStream *st = ic->streams[i];

> >>>>          AVCodecParameters *par = st->codecpar;

> >>>>          InputStream *ist = av_mallocz(sizeof(*ist));

> >>>> +        char *decode_formats = NULL;

> >>>>          char *framerate = NULL, *hwaccel_device = NULL;

> >>>>          const char *hwaccel = NULL;

> >>>>          char *hwaccel_output_format = NULL;

> >>>> @@ -797,6 +798,49 @@ static void add_input_streams(OptionsContext

> >> *o,

> >>>> AVFormatContext *ic)

> >>>>              ist->top_field_first = -1;

> >>>>              MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first,

> ic,

> >>>> st);

> >>>>

> >>>> +            MATCH_PER_STREAM_OPT(decode_formats, str,

> >> decode_formats, ic,

> >>>> st);

> >>>> +            if (decode_formats) {

> >>>> +                const char *p, *q;

> >>>> +                int i, nb_formats;

> >>>> +                char tmp[32];

> >>>> +

> >>>> +                nb_formats = 0;

> >>>> +                for (p = decode_formats; p; p = strchr(p + 1, ','))

> >>>> +                    ++nb_formats;

> >>>> +

> >>>> +                ist->decode_formats =

> >>>> +                    av_malloc_array(nb_formats + 1, sizeof(*ist-

> >>> decode_formats));

> >>>> +                if (!ist->decode_formats)

> >>>> +                    exit_program(1);

> >>>> +

> >>>> +                p = decode_formats;

> >>>> +                for (i = 0; i < nb_formats; i++) {

> >>>> +                    q = strchr(p, ',');

> >>>> +                    if (!q) {

> >>>> +                        ist->decode_formats[i] = av_get_pix_fmt(p);

> >>>> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> >>>> +                            break;

> >>>> +                        continue;

> >>>> +                    }

> >>>> +                    if (q - p > sizeof(tmp) - 1)

> >>>> +                        break;

> >>>> +                    memcpy(tmp, p, q - p);

> >>>> +                    tmp[q - p] = 0;

> >>>> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);

> >>>> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> >>>> +                        break;

> >>>> +                    p = q + 1;

> >>>> +                }

> >>>> +                if (i < nb_formats) {

> >>>> +                    av_log(NULL, AV_LOG_FATAL,

> >>>> +                           "Unrecognised decode format: %s.\n", p);

> >>>> +                    exit_program(1);

> >>>> +                }

> >>>> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;

> >>>> +            } else {

> >>>> +                ist->decode_formats = NULL;

> >>>> +            }

> >>>> +

> >>>>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);

> >>>>              if (hwaccel) {

> >>>>                  // The NVDEC hwaccels use a CUDA device, so remap the

> name

> >> here.

> >>>> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {

> >>>>          "audio bitrate (please use -b:a)", "bitrate" },

> >>>>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,

> >>>> { .func_arg = opt_bitrate },

> >>>>          "video bitrate (please use -b:v)", "bitrate" },

> >>>> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |

> >>>> OPT_EXPERT |

> >>>> +                        OPT_SPEC | OPT_INPUT,                                    { .off =

> >>>> OFFSET(decode_formats) },

> >>>> +        "set output format(s) to be used by decoder, fail if none of these

> >>>> formats are available", "format" },

> >>>>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG |

> OPT_EXPERT

> >> |

> >>>>                            OPT_SPEC | OPT_INPUT,                                  { .off =

> >>>> OFFSET(hwaccels) },

> >>>>          "use HW accelerated decoding", "hwaccel name" },

> >>>> --

> >>>> 2.19.1

> >>>>

> >>>

> >>> Is there any other conditions which will cause the hwaccel failed and

> >> fallback to software path?

> >>> Like profile was not supported by hardware?

> >>

> >> If this option is set then get_format will never choose a format not on the

> list

> >> you provide, so it shouldn't be able to use software decode unless a

> >> software format is on the list.

> >>

> >>> I applied the patch and tested some cases, like yuv422  VAAPI decoding

> >> (currently not supported), HEVC decoding with unsupported profile.

> >>> It reports an error like "hwaccel initialization returned error", and

> continue

> >> to decode and produce less frames than expected. (which I thought

> fallback

> >> still happened).

> >>

> >> Can you give an example?  With -v debug there will be output saying

> exactly

> >> what happened in ff_get_format().

> >>

> >> - Mark

> >

> >

> > Got information like this:

> >

> > [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0

> > [hevc @ 0x55b87457dbc0] Decoding SPS

> > [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0

> > [hevc @ 0x55b87457dbc0] Decoding PPS

> > [hevc @ 0x55b87457dbc0] Format vaapi_vld chosen by get_format().

> > [hevc @ 0x55b87457dbc0] Format vaapi_vld requires hwaccel initialisation.

> > [hevc @ 0x55b87457dbc0] Codec hevc profile 0 not supported for hardware

> decode.

> > [hevc @ 0x55b87457dbc0] Failed setup for format vaapi_vld: hwaccel

> initialisation returned error.

> > [hevc @ 0x55b87457dbc0] Format vaapi_vld not usable, retrying

> get_format() without it.

> > [hevc @ 0x55b87457dbc0] Error parsing NAL unit #3.

> > cur_dts is invalid (this is harmless if it occurs once at the start per stream)

> > [hevc @ 0x55b87458b7c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0,

> temporal_id: 0

> > [hevc @ 0x55b87458b7c0] Could not find ref with POC 0

> >

> > See the attached report file  for more details.

> 

> This is a decoder bug - get_format() is not correctly called again after it fails,

> and the decoder then uses the software pixfmt derived from the SPS on

> subsequent frames.

> 

> (I did pretty much all of my testing with H.264, which does not have this

> problem.)

> 

Works for me on forcing hwaccel  in HEVC vaapi decoding after applying the hevcdec patch. 
Thanks for answering my questions.

- Linjie
Linjie Fu Feb. 18, 2019, 5:05 a.m.
> -----Original Message-----

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

> Of Fu, Linjie

> Sent: Friday, November 16, 2018 16:37

> To: FFmpeg development discussions and patches <ffmpeg-

> devel@ffmpeg.org>

> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> specific decode format

> 

> > -----Original Message-----

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

> Behalf

> > Of Mark Thompson

> > Sent: Thursday, November 15, 2018 05:48

> > To: ffmpeg-devel@ffmpeg.org

> > Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> > specific decode format

> >

> > On 14/11/18 01:35, Fu, Linjie wrote:

> > >> -----Original Message-----

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

> > Behalf

> > >> Of Mark Thompson

> > >> Sent: Wednesday, November 14, 2018 09:11

> > >> To: ffmpeg-devel@ffmpeg.org

> > >> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> > >> specific decode format

> > >>

> > >> On 14/11/18 00:50, Fu, Linjie wrote:

> > >>>> -----Original Message-----

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

> > >> Behalf

> > >>>> Of Mark Thompson

> > >>>> Sent: Wednesday, November 14, 2018 07:44

> > >>>> To: ffmpeg-devel@ffmpeg.org

> > >>>> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a

> > >> specific

> > >>>> decode format

> > >>>>

> > >>>> Fixes #7519.

> > >>>> ---

> > >>>>  doc/ffmpeg.texi      | 13 ++++++++++++

> > >>>>  fftools/ffmpeg.c     | 10 ++++++++++

> > >>>>  fftools/ffmpeg.h     |  4 ++++

> > >>>>  fftools/ffmpeg_opt.c | 47

> > >>>> ++++++++++++++++++++++++++++++++++++++++++++

> > >>>>  4 files changed, 74 insertions(+)

> > >>>>

> > >>>> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi

> > >>>> index 3717f22d42..d127bc0f0d 100644

> > >>>> --- a/doc/ffmpeg.texi

> > >>>> +++ b/doc/ffmpeg.texi

> > >>>> @@ -920,6 +920,19 @@ would be more efficient.

> > >>>>  When doing stream copy, copy also non-key frames found at the

> > >>>>  beginning.

> > >>>>

> > >>>> +@item -decode_format[:@var{stream_specifier}]

> > >>>> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})

> > >>>> +Set the possible output formats to be used by the decoder for this

> > >> stream.

> > >>>> +If the decoder does not natively support any format in the given list

> > for

> > >>>> +the input stream then decoding will fail rather than continuing with a

> > >>>> +different format.

> > >>>> +

> > >>>> +In general this should not be set - the decoder will select an output

> > >>>> +format based on the input stream parameters and available

> > components,

> > >>>> and

> > >>>> +that will be automatically converted to whatever the output

> requires.

> > It

> > >>>> +may be useful to force a hardware decoder supporting output in

> > >> multiple

> > >>>> +different memory types to pick a desired one, or to ensure that a

> > >> hardware

> > >>>> +decoder is used when software fallback is also available.

> > >>>> +

> > >>>>  @item -init_hw_device

> > >>>> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]

> > >>>>  Initialise a new hardware device of type @var{type} called

> > @var{name},

> > >>>> using the

> > >>>>  given device parameters.

> > >>>> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c

> > >>>> index 38c21e944a..c651c8d3a8 100644

> > >>>> --- a/fftools/ffmpeg.c

> > >>>> +++ b/fftools/ffmpeg.c

> > >>>> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)

> > >>>>          avsubtitle_free(&ist->prev_sub.subtitle);

> > >>>>          av_frame_free(&ist->sub2video.frame);

> > >>>>          av_freep(&ist->filters);

> > >>>> +        av_freep(&ist->decode_formats);

> > >>>>          av_freep(&ist->hwaccel_device);

> > >>>>          av_freep(&ist->dts_buffer);

> > >>>>

> > >>>> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat

> > >>>> get_format(AVCodecContext *s, const enum AVPixelFormat

> > >>>>          const AVCodecHWConfig  *config = NULL;

> > >>>>          int i;

> > >>>>

> > >>>> +        if (ist->decode_formats) {

> > >>>> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++)

> {

> > >>>> +                if (ist->decode_formats[i] == *p)

> > >>>> +                    break;

> > >>>> +            }

> > >>>> +            if (ist->decode_formats[i] != *p)

> > >>>> +                continue;

> > >>>> +        }

> > >>>> +

> > >>>>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))

> > >>>>              break;

> > >>>>

> > >>>> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h

> > >>>> index eb1eaf6363..b06fd18b1c 100644

> > >>>> --- a/fftools/ffmpeg.h

> > >>>> +++ b/fftools/ffmpeg.h

> > >>>> @@ -125,6 +125,8 @@ typedef struct OptionsContext {

> > >>>>      int        nb_ts_scale;

> > >>>>      SpecifierOpt *dump_attachment;

> > >>>>      int        nb_dump_attachment;

> > >>>> +    SpecifierOpt *decode_formats;

> > >>>> +    int        nb_decode_formats;

> > >>>>      SpecifierOpt *hwaccels;

> > >>>>      int        nb_hwaccels;

> > >>>>      SpecifierOpt *hwaccel_devices;

> > >>>> @@ -334,6 +336,8 @@ typedef struct InputStream {

> > >>>>      int top_field_first;

> > >>>>      int guess_layout_max;

> > >>>>

> > >>>> +    enum AVPixelFormat *decode_formats;

> > >>>> +

> > >>>>      int autorotate;

> > >>>>

> > >>>>      int fix_sub_duration;

> > >>>> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c

> > >>>> index d4851a2cd8..63bb05053b 100644

> > >>>> --- a/fftools/ffmpeg_opt.c

> > >>>> +++ b/fftools/ffmpeg_opt.c

> > >>>> @@ -701,6 +701,7 @@ static void

> add_input_streams(OptionsContext

> > *o,

> > >>>> AVFormatContext *ic)

> > >>>>          AVStream *st = ic->streams[i];

> > >>>>          AVCodecParameters *par = st->codecpar;

> > >>>>          InputStream *ist = av_mallocz(sizeof(*ist));

> > >>>> +        char *decode_formats = NULL;

> > >>>>          char *framerate = NULL, *hwaccel_device = NULL;

> > >>>>          const char *hwaccel = NULL;

> > >>>>          char *hwaccel_output_format = NULL;

> > >>>> @@ -797,6 +798,49 @@ static void

> add_input_streams(OptionsContext

> > >> *o,

> > >>>> AVFormatContext *ic)

> > >>>>              ist->top_field_first = -1;

> > >>>>              MATCH_PER_STREAM_OPT(top_field_first, i, ist-

> >top_field_first,

> > ic,

> > >>>> st);

> > >>>>

> > >>>> +            MATCH_PER_STREAM_OPT(decode_formats, str,

> > >> decode_formats, ic,

> > >>>> st);

> > >>>> +            if (decode_formats) {

> > >>>> +                const char *p, *q;

> > >>>> +                int i, nb_formats;

> > >>>> +                char tmp[32];

> > >>>> +

> > >>>> +                nb_formats = 0;

> > >>>> +                for (p = decode_formats; p; p = strchr(p + 1, ','))

> > >>>> +                    ++nb_formats;

> > >>>> +

> > >>>> +                ist->decode_formats =

> > >>>> +                    av_malloc_array(nb_formats + 1, sizeof(*ist-

> > >>> decode_formats));

> > >>>> +                if (!ist->decode_formats)

> > >>>> +                    exit_program(1);

> > >>>> +

> > >>>> +                p = decode_formats;

> > >>>> +                for (i = 0; i < nb_formats; i++) {

> > >>>> +                    q = strchr(p, ',');

> > >>>> +                    if (!q) {

> > >>>> +                        ist->decode_formats[i] = av_get_pix_fmt(p);

> > >>>> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> > >>>> +                            break;

> > >>>> +                        continue;

> > >>>> +                    }

> > >>>> +                    if (q - p > sizeof(tmp) - 1)

> > >>>> +                        break;

> > >>>> +                    memcpy(tmp, p, q - p);

> > >>>> +                    tmp[q - p] = 0;

> > >>>> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);

> > >>>> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)

> > >>>> +                        break;

> > >>>> +                    p = q + 1;

> > >>>> +                }

> > >>>> +                if (i < nb_formats) {

> > >>>> +                    av_log(NULL, AV_LOG_FATAL,

> > >>>> +                           "Unrecognised decode format: %s.\n", p);

> > >>>> +                    exit_program(1);

> > >>>> +                }

> > >>>> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;

> > >>>> +            } else {

> > >>>> +                ist->decode_formats = NULL;

> > >>>> +            }

> > >>>> +

> > >>>>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);

> > >>>>              if (hwaccel) {

> > >>>>                  // The NVDEC hwaccels use a CUDA device, so remap the

> > name

> > >> here.

> > >>>> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {

> > >>>>          "audio bitrate (please use -b:a)", "bitrate" },

> > >>>>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,

> > >>>> { .func_arg = opt_bitrate },

> > >>>>          "video bitrate (please use -b:v)", "bitrate" },

> > >>>> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |

> > >>>> OPT_EXPERT |

> > >>>> +                        OPT_SPEC | OPT_INPUT,                                    { .off =

> > >>>> OFFSET(decode_formats) },

> > >>>> +        "set output format(s) to be used by decoder, fail if none of

> these

> > >>>> formats are available", "format" },

> > >>>>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG |

> > OPT_EXPERT

> > >> |

> > >>>>                            OPT_SPEC | OPT_INPUT,                                  { .off =

> > >>>> OFFSET(hwaccels) },

> > >>>>          "use HW accelerated decoding", "hwaccel name" },

> > >>>> --

> > >>>> 2.19.1

> > >>>>

> > >>>

> > >>> Is there any other conditions which will cause the hwaccel failed and

> > >> fallback to software path?

> > >>> Like profile was not supported by hardware?

> > >>

> > >> If this option is set then get_format will never choose a format not on

> the

> > list

> > >> you provide, so it shouldn't be able to use software decode unless a

> > >> software format is on the list.

> > >>

> > >>> I applied the patch and tested some cases, like yuv422  VAAPI

> decoding

> > >> (currently not supported), HEVC decoding with unsupported profile.

> > >>> It reports an error like "hwaccel initialization returned error", and

> > continue

> > >> to decode and produce less frames than expected. (which I thought

> > fallback

> > >> still happened).

> > >>

> > >> Can you give an example?  With -v debug there will be output saying

> > exactly

> > >> what happened in ff_get_format().

> > >>

> > >> - Mark

> > >

> > >

> > > Got information like this:

> > >

> > > [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0

> > > [hevc @ 0x55b87457dbc0] Decoding SPS

> > > [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0

> > > [hevc @ 0x55b87457dbc0] Decoding PPS

> > > [hevc @ 0x55b87457dbc0] Format vaapi_vld chosen by get_format().

> > > [hevc @ 0x55b87457dbc0] Format vaapi_vld requires hwaccel initialisation.

> > > [hevc @ 0x55b87457dbc0] Codec hevc profile 0 not supported for

> hardware

> > decode.

> > > [hevc @ 0x55b87457dbc0] Failed setup for format vaapi_vld: hwaccel

> > initialisation returned error.

> > > [hevc @ 0x55b87457dbc0] Format vaapi_vld not usable, retrying

> > get_format() without it.

> > > [hevc @ 0x55b87457dbc0] Error parsing NAL unit #3.

> > > cur_dts is invalid (this is harmless if it occurs once at the start per stream)

> > > [hevc @ 0x55b87458b7c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0,

> > temporal_id: 0

> > > [hevc @ 0x55b87458b7c0] Could not find ref with POC 0

> > >

> > > See the attached report file  for more details.

> >

> > This is a decoder bug - get_format() is not correctly called again after it fails,

> > and the decoder then uses the software pixfmt derived from the SPS on

> > subsequent frames.

> >

> > (I did pretty much all of my testing with H.264, which does not have this

> > problem.)

> >

> Works for me on forcing hwaccel  in HEVC vaapi decoding after applying the

> hevcdec patch.

> Thanks for answering my questions.

> 

Ping?
Can this be merged?
https://trac.ffmpeg.org/ticket/7519#comment:4

---
Linjie
Michael Niedermayer Feb. 18, 2019, 9:45 p.m.
On Tue, Nov 13, 2018 at 11:43:31PM +0000, Mark Thompson wrote:
> Fixes #7519.
> ---
>  doc/ffmpeg.texi      | 13 ++++++++++++
>  fftools/ffmpeg.c     | 10 ++++++++++
>  fftools/ffmpeg.h     |  4 ++++
>  fftools/ffmpeg_opt.c | 47 ++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 74 insertions(+)

i did already "LGTM" this IIRC, but as its being pinged ...
LGTM

thx

[...]
Mark Thompson Feb. 20, 2019, 9:52 p.m.
On 18/02/2019 05:05, Fu, Linjie wrote:
>> -----Original Message-----
>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf
>> Of Fu, Linjie
>> Sent: Friday, November 16, 2018 16:37
>> To: FFmpeg development discussions and patches <ffmpeg-
>> devel@ffmpeg.org>
>> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a
>> specific decode format
>>
>>> -----Original Message-----
>>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On
>> Behalf
>>> Of Mark Thompson
>>> Sent: Thursday, November 15, 2018 05:48
>>> To: ffmpeg-devel@ffmpeg.org
>>> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a
>>> specific decode format
>>>
>>> On 14/11/18 01:35, Fu, Linjie wrote:
>>>>> -----Original Message-----
>>>>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On
>>> Behalf
>>>>> Of Mark Thompson
>>>>> Sent: Wednesday, November 14, 2018 09:11
>>>>> To: ffmpeg-devel@ffmpeg.org
>>>>> Subject: Re: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a
>>>>> specific decode format
>>>>>
>>>>> On 14/11/18 00:50, Fu, Linjie wrote:
>>>>>>> -----Original Message-----
>>>>>>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On
>>>>> Behalf
>>>>>>> Of Mark Thompson
>>>>>>> Sent: Wednesday, November 14, 2018 07:44
>>>>>>> To: ffmpeg-devel@ffmpeg.org
>>>>>>> Subject: [FFmpeg-devel] [PATCH v2] ffmpeg: Add option to force a
>>>>> specific
>>>>>>> decode format
>>>>>>>
>>>>>>> Fixes #7519.
>>>>>>> ---
>>>>>>>  doc/ffmpeg.texi      | 13 ++++++++++++
>>>>>>>  fftools/ffmpeg.c     | 10 ++++++++++
>>>>>>>  fftools/ffmpeg.h     |  4 ++++
>>>>>>>  fftools/ffmpeg_opt.c | 47
>>>>>>> ++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>  4 files changed, 74 insertions(+)
>>>>>>>
>>>>>>> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
>>>>>>> index 3717f22d42..d127bc0f0d 100644
>>>>>>> --- a/doc/ffmpeg.texi
>>>>>>> +++ b/doc/ffmpeg.texi
>>>>>>> @@ -920,6 +920,19 @@ would be more efficient.
>>>>>>>  When doing stream copy, copy also non-key frames found at the
>>>>>>>  beginning.
>>>>>>>
>>>>>>> +@item -decode_format[:@var{stream_specifier}]
>>>>>>> @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})
>>>>>>> +Set the possible output formats to be used by the decoder for this
>>>>> stream.
>>>>>>> +If the decoder does not natively support any format in the given list
>>> for
>>>>>>> +the input stream then decoding will fail rather than continuing with a
>>>>>>> +different format.
>>>>>>> +
>>>>>>> +In general this should not be set - the decoder will select an output
>>>>>>> +format based on the input stream parameters and available
>>> components,
>>>>>>> and
>>>>>>> +that will be automatically converted to whatever the output
>> requires.
>>> It
>>>>>>> +may be useful to force a hardware decoder supporting output in
>>>>> multiple
>>>>>>> +different memory types to pick a desired one, or to ensure that a
>>>>> hardware
>>>>>>> +decoder is used when software fallback is also available.
>>>>>>> +
>>>>>>>  @item -init_hw_device
>>>>>>> @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]
>>>>>>>  Initialise a new hardware device of type @var{type} called
>>> @var{name},
>>>>>>> using the
>>>>>>>  given device parameters.
>>>>>>> diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
>>>>>>> index 38c21e944a..c651c8d3a8 100644
>>>>>>> --- a/fftools/ffmpeg.c
>>>>>>> +++ b/fftools/ffmpeg.c
>>>>>>> @@ -598,6 +598,7 @@ static void ffmpeg_cleanup(int ret)
>>>>>>>          avsubtitle_free(&ist->prev_sub.subtitle);
>>>>>>>          av_frame_free(&ist->sub2video.frame);
>>>>>>>          av_freep(&ist->filters);
>>>>>>> +        av_freep(&ist->decode_formats);
>>>>>>>          av_freep(&ist->hwaccel_device);
>>>>>>>          av_freep(&ist->dts_buffer);
>>>>>>>
>>>>>>> @@ -2800,6 +2801,15 @@ static enum AVPixelFormat
>>>>>>> get_format(AVCodecContext *s, const enum AVPixelFormat
>>>>>>>          const AVCodecHWConfig  *config = NULL;
>>>>>>>          int i;
>>>>>>>
>>>>>>> +        if (ist->decode_formats) {
>>>>>>> +            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++)
>> {
>>>>>>> +                if (ist->decode_formats[i] == *p)
>>>>>>> +                    break;
>>>>>>> +            }
>>>>>>> +            if (ist->decode_formats[i] != *p)
>>>>>>> +                continue;
>>>>>>> +        }
>>>>>>> +
>>>>>>>          if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
>>>>>>>              break;
>>>>>>>
>>>>>>> diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
>>>>>>> index eb1eaf6363..b06fd18b1c 100644
>>>>>>> --- a/fftools/ffmpeg.h
>>>>>>> +++ b/fftools/ffmpeg.h
>>>>>>> @@ -125,6 +125,8 @@ typedef struct OptionsContext {
>>>>>>>      int        nb_ts_scale;
>>>>>>>      SpecifierOpt *dump_attachment;
>>>>>>>      int        nb_dump_attachment;
>>>>>>> +    SpecifierOpt *decode_formats;
>>>>>>> +    int        nb_decode_formats;
>>>>>>>      SpecifierOpt *hwaccels;
>>>>>>>      int        nb_hwaccels;
>>>>>>>      SpecifierOpt *hwaccel_devices;
>>>>>>> @@ -334,6 +336,8 @@ typedef struct InputStream {
>>>>>>>      int top_field_first;
>>>>>>>      int guess_layout_max;
>>>>>>>
>>>>>>> +    enum AVPixelFormat *decode_formats;
>>>>>>> +
>>>>>>>      int autorotate;
>>>>>>>
>>>>>>>      int fix_sub_duration;
>>>>>>> diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
>>>>>>> index d4851a2cd8..63bb05053b 100644
>>>>>>> --- a/fftools/ffmpeg_opt.c
>>>>>>> +++ b/fftools/ffmpeg_opt.c
>>>>>>> @@ -701,6 +701,7 @@ static void
>> add_input_streams(OptionsContext
>>> *o,
>>>>>>> AVFormatContext *ic)
>>>>>>>          AVStream *st = ic->streams[i];
>>>>>>>          AVCodecParameters *par = st->codecpar;
>>>>>>>          InputStream *ist = av_mallocz(sizeof(*ist));
>>>>>>> +        char *decode_formats = NULL;
>>>>>>>          char *framerate = NULL, *hwaccel_device = NULL;
>>>>>>>          const char *hwaccel = NULL;
>>>>>>>          char *hwaccel_output_format = NULL;
>>>>>>> @@ -797,6 +798,49 @@ static void
>> add_input_streams(OptionsContext
>>>>> *o,
>>>>>>> AVFormatContext *ic)
>>>>>>>              ist->top_field_first = -1;
>>>>>>>              MATCH_PER_STREAM_OPT(top_field_first, i, ist-
>>> top_field_first,
>>> ic,
>>>>>>> st);
>>>>>>>
>>>>>>> +            MATCH_PER_STREAM_OPT(decode_formats, str,
>>>>> decode_formats, ic,
>>>>>>> st);
>>>>>>> +            if (decode_formats) {
>>>>>>> +                const char *p, *q;
>>>>>>> +                int i, nb_formats;
>>>>>>> +                char tmp[32];
>>>>>>> +
>>>>>>> +                nb_formats = 0;
>>>>>>> +                for (p = decode_formats; p; p = strchr(p + 1, ','))
>>>>>>> +                    ++nb_formats;
>>>>>>> +
>>>>>>> +                ist->decode_formats =
>>>>>>> +                    av_malloc_array(nb_formats + 1, sizeof(*ist-
>>>>>> decode_formats));
>>>>>>> +                if (!ist->decode_formats)
>>>>>>> +                    exit_program(1);
>>>>>>> +
>>>>>>> +                p = decode_formats;
>>>>>>> +                for (i = 0; i < nb_formats; i++) {
>>>>>>> +                    q = strchr(p, ',');
>>>>>>> +                    if (!q) {
>>>>>>> +                        ist->decode_formats[i] = av_get_pix_fmt(p);
>>>>>>> +                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
>>>>>>> +                            break;
>>>>>>> +                        continue;
>>>>>>> +                    }
>>>>>>> +                    if (q - p > sizeof(tmp) - 1)
>>>>>>> +                        break;
>>>>>>> +                    memcpy(tmp, p, q - p);
>>>>>>> +                    tmp[q - p] = 0;
>>>>>>> +                    ist->decode_formats[i] = av_get_pix_fmt(tmp);
>>>>>>> +                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
>>>>>>> +                        break;
>>>>>>> +                    p = q + 1;
>>>>>>> +                }
>>>>>>> +                if (i < nb_formats) {
>>>>>>> +                    av_log(NULL, AV_LOG_FATAL,
>>>>>>> +                           "Unrecognised decode format: %s.\n", p);
>>>>>>> +                    exit_program(1);
>>>>>>> +                }
>>>>>>> +                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;
>>>>>>> +            } else {
>>>>>>> +                ist->decode_formats = NULL;
>>>>>>> +            }
>>>>>>> +
>>>>>>>              MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);
>>>>>>>              if (hwaccel) {
>>>>>>>                  // The NVDEC hwaccels use a CUDA device, so remap the
>>> name
>>>>> here.
>>>>>>> @@ -3583,6 +3627,9 @@ const OptionDef options[] = {
>>>>>>>          "audio bitrate (please use -b:a)", "bitrate" },
>>>>>>>      { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,
>>>>>>> { .func_arg = opt_bitrate },
>>>>>>>          "video bitrate (please use -b:v)", "bitrate" },
>>>>>>> +    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG |
>>>>>>> OPT_EXPERT |
>>>>>>> +                        OPT_SPEC | OPT_INPUT,                                    { .off =
>>>>>>> OFFSET(decode_formats) },
>>>>>>> +        "set output format(s) to be used by decoder, fail if none of
>> these
>>>>>>> formats are available", "format" },
>>>>>>>      { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG |
>>> OPT_EXPERT
>>>>> |
>>>>>>>                            OPT_SPEC | OPT_INPUT,                                  { .off =
>>>>>>> OFFSET(hwaccels) },
>>>>>>>          "use HW accelerated decoding", "hwaccel name" },
>>>>>>> --
>>>>>>> 2.19.1
>>>>>>>
>>>>>>
>>>>>> Is there any other conditions which will cause the hwaccel failed and
>>>>> fallback to software path?
>>>>>> Like profile was not supported by hardware?
>>>>>
>>>>> If this option is set then get_format will never choose a format not on
>> the
>>> list
>>>>> you provide, so it shouldn't be able to use software decode unless a
>>>>> software format is on the list.
>>>>>
>>>>>> I applied the patch and tested some cases, like yuv422  VAAPI
>> decoding
>>>>> (currently not supported), HEVC decoding with unsupported profile.
>>>>>> It reports an error like "hwaccel initialization returned error", and
>>> continue
>>>>> to decode and produce less frames than expected. (which I thought
>>> fallback
>>>>> still happened).
>>>>>
>>>>> Can you give an example?  With -v debug there will be output saying
>>> exactly
>>>>> what happened in ff_get_format().
>>>>>
>>>>> - Mark
>>>>
>>>>
>>>> Got information like this:
>>>>
>>>> [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0
>>>> [hevc @ 0x55b87457dbc0] Decoding SPS
>>>> [hevc @ 0x55b87457dbc0] Unknown HEVC profile: 0
>>>> [hevc @ 0x55b87457dbc0] Decoding PPS
>>>> [hevc @ 0x55b87457dbc0] Format vaapi_vld chosen by get_format().
>>>> [hevc @ 0x55b87457dbc0] Format vaapi_vld requires hwaccel initialisation.
>>>> [hevc @ 0x55b87457dbc0] Codec hevc profile 0 not supported for
>> hardware
>>> decode.
>>>> [hevc @ 0x55b87457dbc0] Failed setup for format vaapi_vld: hwaccel
>>> initialisation returned error.
>>>> [hevc @ 0x55b87457dbc0] Format vaapi_vld not usable, retrying
>>> get_format() without it.
>>>> [hevc @ 0x55b87457dbc0] Error parsing NAL unit #3.
>>>> cur_dts is invalid (this is harmless if it occurs once at the start per stream)
>>>> [hevc @ 0x55b87458b7c0] nal_unit_type: 1(TRAIL_R), nuh_layer_id: 0,
>>> temporal_id: 0
>>>> [hevc @ 0x55b87458b7c0] Could not find ref with POC 0
>>>>
>>>> See the attached report file  for more details.
>>>
>>> This is a decoder bug - get_format() is not correctly called again after it fails,
>>> and the decoder then uses the software pixfmt derived from the SPS on
>>> subsequent frames.
>>>
>>> (I did pretty much all of my testing with H.264, which does not have this
>>> problem.)
>>>
>> Works for me on forcing hwaccel  in HEVC vaapi decoding after applying the
>> hevcdec patch.
>> Thanks for answering my questions.
>>
> Ping?
> Can this be merged?
> https://trac.ffmpeg.org/ticket/7519#comment:4

I think this got lost because I was feeling it loosely depended on <https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2018-November/236376.html> in order to actually work in the intended use-case.  I wasn't particularly confident in that patch being the best way to do it and it isn't my code, so I was hoping for any thoughts from someone more familiar with it.

Shall I apply this part anyway?

Thanks,

- Mark

Patch hide | download patch | download mbox

diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index 3717f22d42..d127bc0f0d 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -920,6 +920,19 @@  would be more efficient.
 When doing stream copy, copy also non-key frames found at the
 beginning.
 
+@item -decode_format[:@var{stream_specifier}] @var{pixfmt}[,@var{pixfmt}...] (@emph{input,per-stream})
+Set the possible output formats to be used by the decoder for this stream.
+If the decoder does not natively support any format in the given list for
+the input stream then decoding will fail rather than continuing with a
+different format.
+
+In general this should not be set - the decoder will select an output
+format based on the input stream parameters and available components, and
+that will be automatically converted to whatever the output requires.  It
+may be useful to force a hardware decoder supporting output in multiple
+different memory types to pick a desired one, or to ensure that a hardware
+decoder is used when software fallback is also available.
+
 @item -init_hw_device @var{type}[=@var{name}][:@var{device}[,@var{key=value}...]]
 Initialise a new hardware device of type @var{type} called @var{name}, using the
 given device parameters.
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 38c21e944a..c651c8d3a8 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -598,6 +598,7 @@  static void ffmpeg_cleanup(int ret)
         avsubtitle_free(&ist->prev_sub.subtitle);
         av_frame_free(&ist->sub2video.frame);
         av_freep(&ist->filters);
+        av_freep(&ist->decode_formats);
         av_freep(&ist->hwaccel_device);
         av_freep(&ist->dts_buffer);
 
@@ -2800,6 +2801,15 @@  static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat
         const AVCodecHWConfig  *config = NULL;
         int i;
 
+        if (ist->decode_formats) {
+            for (i = 0; ist->decode_formats[i] != AV_PIX_FMT_NONE; i++) {
+                if (ist->decode_formats[i] == *p)
+                    break;
+            }
+            if (ist->decode_formats[i] != *p)
+                continue;
+        }
+
         if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
             break;
 
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index eb1eaf6363..b06fd18b1c 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -125,6 +125,8 @@  typedef struct OptionsContext {
     int        nb_ts_scale;
     SpecifierOpt *dump_attachment;
     int        nb_dump_attachment;
+    SpecifierOpt *decode_formats;
+    int        nb_decode_formats;
     SpecifierOpt *hwaccels;
     int        nb_hwaccels;
     SpecifierOpt *hwaccel_devices;
@@ -334,6 +336,8 @@  typedef struct InputStream {
     int top_field_first;
     int guess_layout_max;
 
+    enum AVPixelFormat *decode_formats;
+
     int autorotate;
 
     int fix_sub_duration;
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index d4851a2cd8..63bb05053b 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -701,6 +701,7 @@  static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
         AVStream *st = ic->streams[i];
         AVCodecParameters *par = st->codecpar;
         InputStream *ist = av_mallocz(sizeof(*ist));
+        char *decode_formats = NULL;
         char *framerate = NULL, *hwaccel_device = NULL;
         const char *hwaccel = NULL;
         char *hwaccel_output_format = NULL;
@@ -797,6 +798,49 @@  static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
             ist->top_field_first = -1;
             MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st);
 
+            MATCH_PER_STREAM_OPT(decode_formats, str, decode_formats, ic, st);
+            if (decode_formats) {
+                const char *p, *q;
+                int i, nb_formats;
+                char tmp[32];
+
+                nb_formats = 0;
+                for (p = decode_formats; p; p = strchr(p + 1, ','))
+                    ++nb_formats;
+
+                ist->decode_formats =
+                    av_malloc_array(nb_formats + 1, sizeof(*ist->decode_formats));
+                if (!ist->decode_formats)
+                    exit_program(1);
+
+                p = decode_formats;
+                for (i = 0; i < nb_formats; i++) {
+                    q = strchr(p, ',');
+                    if (!q) {
+                        ist->decode_formats[i] = av_get_pix_fmt(p);
+                        if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
+                            break;
+                        continue;
+                    }
+                    if (q - p > sizeof(tmp) - 1)
+                        break;
+                    memcpy(tmp, p, q - p);
+                    tmp[q - p] = 0;
+                    ist->decode_formats[i] = av_get_pix_fmt(tmp);
+                    if (ist->decode_formats[i] == AV_PIX_FMT_NONE)
+                        break;
+                    p = q + 1;
+                }
+                if (i < nb_formats) {
+                    av_log(NULL, AV_LOG_FATAL,
+                           "Unrecognised decode format: %s.\n", p);
+                    exit_program(1);
+                }
+                ist->decode_formats[nb_formats] = AV_PIX_FMT_NONE;
+            } else {
+                ist->decode_formats = NULL;
+            }
+
             MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);
             if (hwaccel) {
                 // The NVDEC hwaccels use a CUDA device, so remap the name here.
@@ -3583,6 +3627,9 @@  const OptionDef options[] = {
         "audio bitrate (please use -b:a)", "bitrate" },
     { "b",            OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT,            { .func_arg = opt_bitrate },
         "video bitrate (please use -b:v)", "bitrate" },
+    { "decode_format",  OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
+                        OPT_SPEC | OPT_INPUT,                                    { .off = OFFSET(decode_formats) },
+        "set output format(s) to be used by decoder, fail if none of these formats are available", "format" },
     { "hwaccel",          OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
                           OPT_SPEC | OPT_INPUT,                                  { .off = OFFSET(hwaccels) },
         "use HW accelerated decoding", "hwaccel name" },