Re: [FFmpeg-devel] [PATCH] ffmpeg: Don't require a known device to pass a frames context to an encoder

2020-05-03 Thread Mark Thompson
On 29/04/2020 06:34, Fu, Linjie wrote:
>> From: ffmpeg-devel  On Behalf Of
>> Mark Thompson
>> Sent: Wednesday, April 29, 2020 06:57
>> To: FFmpeg development discussions and patches > de...@ffmpeg.org>
>> Subject: [FFmpeg-devel] [PATCH] ffmpeg: Don't require a known device to
>> pass a frames context to an encoder
>>
>> The previous version here did not handle passing a frames context when
>> ffmpeg itself did not know about the device it came from (for example,
>> because it was created by device derivation inside a filter graph), which
>> would break encoders requiring that input.  Fix that by checking for HW
>> frames and device context methods independently, and prefer to use a
>> frames context method if possible.  At the same time, revert the encoding
>> additions to the device matching function because the additional
>> complexity was not relevant to decoding.
>> ---
>>  fftools/ffmpeg_hw.c | 75 +
>>  1 file changed, 42 insertions(+), 33 deletions(-)
>> diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
>> index c5c8aa97ef..fc4a5d31d6 100644
>> --- a/fftools/ffmpeg_hw.c
>> +++ b/fftools/ffmpeg_hw.c
>> @@ -19,6 +19,7 @@
>>  #include 
>>
>>  #include "libavutil/avstring.h"
>> +#include "libavutil/pixdesc.h"
>>  #include "libavfilter/buffersink.h"
>>
>>  #include "ffmpeg.h"
>> @@ -282,10 +283,7 @@ void hw_device_free_all(void)
>>  nb_hw_devices = 0;
>>  }
>>
>> -static HWDevice *hw_device_match_by_codec(const AVCodec *codec,
>> -  enum AVPixelFormat format,
>> -  int possible_methods,
>> -  int *matched_methods)
>> +static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
>>  {
>>  const AVCodecHWConfig *config;
>>  HWDevice *dev;
>> @@ -294,18 +292,11 @@ static HWDevice
>> *hw_device_match_by_codec(const AVCodec *codec,
>>  config = avcodec_get_hw_config(codec, i);
>>  if (!config)
>>  return NULL;
>> -if (format != AV_PIX_FMT_NONE &&
>> -config->pix_fmt != AV_PIX_FMT_NONE &&
>> -config->pix_fmt != format)
>> -continue;
>> -if (!(config->methods & possible_methods))
>> +if (!(config->methods &
>> AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
>>  continue;
>>  dev = hw_device_get_by_type(config->device_type);
>> -if (dev) {
>> -if (matched_methods)
>> -*matched_methods = config->methods & possible_methods;
>> +if (dev)
>>  return dev;
>> -}
>>  }
>>  }
>>
>> @@ -351,9 +342,7 @@ int hw_device_setup_for_decode(InputStream *ist)
>>  if (!dev)
>>  err = hw_device_init_from_type(type, NULL, );
>>  } else {
>> -dev = hw_device_match_by_codec(ist->dec, AV_PIX_FMT_NONE,
>> -   
>> AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
>> -   NULL);
>> +dev = hw_device_match_by_codec(ist->dec);
>>  if (!dev) {
>>  // No device for this codec, but not using generic hwaccel
>>  // and therefore may well not need one - ignore.
>> @@ -429,37 +418,57 @@ int hw_device_setup_for_decode(InputStream
>> *ist)
>>
>>  int hw_device_setup_for_encode(OutputStream *ost)
>>  {
>> -HWDevice *dev;
>> -AVBufferRef *frames_ref;
>> -int methods = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
>> -int matched_methods;
>> +const AVCodecHWConfig *config;
>> +HWDevice *dev = NULL;
>> +AVBufferRef *frames_ref = NULL;
>> +int i;
>>
>>  if (ost->filter) {
>>  frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
>>  if (frames_ref &&
>>  ((AVHWFramesContext*)frames_ref->data)->format ==
>> -ost->enc_ctx->pix_fmt)
>> -methods |= AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX;
>> +ost->enc_ctx->pix_fmt) {
>> +// Matching format, will try to use hw_frames_ctx.
>> +} else {
>> +frames_ref = NULL;
>> +}
>>  }
>>
>> -dev = hw_device_match_by_codec(ost->enc, ost->enc_ctx->pix_fmt,
>> -   methods, _methods);
>> -if (dev) {
>> -if (matched_methods &
>> AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) {
>> -ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
>> -if (!ost->enc_ctx->hw_device_ctx)
>> -return AVERROR(ENOMEM);
>> -}
>> -if (matched_methods &
>> AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) {
>> +for (i = 0;; i++) {
>> +config = avcodec_get_hw_config(ost->enc, i);
>> +if (!config)
>> +break;
>> +
>> +if (frames_ref &&
>> +config->methods &
>> AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
>> +(config->pix_fmt == 

Re: [FFmpeg-devel] [PATCH] ffmpeg: Don't require a known device to pass a frames context to an encoder

2020-04-28 Thread Fu, Linjie
> From: ffmpeg-devel  On Behalf Of
> Mark Thompson
> Sent: Wednesday, April 29, 2020 06:57
> To: FFmpeg development discussions and patches  de...@ffmpeg.org>
> Subject: [FFmpeg-devel] [PATCH] ffmpeg: Don't require a known device to
> pass a frames context to an encoder
> 
> The previous version here did not handle passing a frames context when
> ffmpeg itself did not know about the device it came from (for example,
> because it was created by device derivation inside a filter graph), which
> would break encoders requiring that input.  Fix that by checking for HW
> frames and device context methods independently, and prefer to use a
> frames context method if possible.  At the same time, revert the encoding
> additions to the device matching function because the additional
> complexity was not relevant to decoding.
> ---
>  fftools/ffmpeg_hw.c | 75 +
>  1 file changed, 42 insertions(+), 33 deletions(-)
> diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
> index c5c8aa97ef..fc4a5d31d6 100644
> --- a/fftools/ffmpeg_hw.c
> +++ b/fftools/ffmpeg_hw.c
> @@ -19,6 +19,7 @@
>  #include 
> 
>  #include "libavutil/avstring.h"
> +#include "libavutil/pixdesc.h"
>  #include "libavfilter/buffersink.h"
> 
>  #include "ffmpeg.h"
> @@ -282,10 +283,7 @@ void hw_device_free_all(void)
>  nb_hw_devices = 0;
>  }
> 
> -static HWDevice *hw_device_match_by_codec(const AVCodec *codec,
> -  enum AVPixelFormat format,
> -  int possible_methods,
> -  int *matched_methods)
> +static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
>  {
>  const AVCodecHWConfig *config;
>  HWDevice *dev;
> @@ -294,18 +292,11 @@ static HWDevice
> *hw_device_match_by_codec(const AVCodec *codec,
>  config = avcodec_get_hw_config(codec, i);
>  if (!config)
>  return NULL;
> -if (format != AV_PIX_FMT_NONE &&
> -config->pix_fmt != AV_PIX_FMT_NONE &&
> -config->pix_fmt != format)
> -continue;
> -if (!(config->methods & possible_methods))
> +if (!(config->methods &
> AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
>  continue;
>  dev = hw_device_get_by_type(config->device_type);
> -if (dev) {
> -if (matched_methods)
> -*matched_methods = config->methods & possible_methods;
> +if (dev)
>  return dev;
> -}
>  }
>  }
> 
> @@ -351,9 +342,7 @@ int hw_device_setup_for_decode(InputStream *ist)
>  if (!dev)
>  err = hw_device_init_from_type(type, NULL, );
>  } else {
> -dev = hw_device_match_by_codec(ist->dec, AV_PIX_FMT_NONE,
> -   
> AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
> -   NULL);
> +dev = hw_device_match_by_codec(ist->dec);
>  if (!dev) {
>  // No device for this codec, but not using generic hwaccel
>  // and therefore may well not need one - ignore.
> @@ -429,37 +418,57 @@ int hw_device_setup_for_decode(InputStream
> *ist)
> 
>  int hw_device_setup_for_encode(OutputStream *ost)
>  {
> -HWDevice *dev;
> -AVBufferRef *frames_ref;
> -int methods = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
> -int matched_methods;
> +const AVCodecHWConfig *config;
> +HWDevice *dev = NULL;
> +AVBufferRef *frames_ref = NULL;
> +int i;
> 
>  if (ost->filter) {
>  frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
>  if (frames_ref &&
>  ((AVHWFramesContext*)frames_ref->data)->format ==
> -ost->enc_ctx->pix_fmt)
> -methods |= AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX;
> +ost->enc_ctx->pix_fmt) {
> +// Matching format, will try to use hw_frames_ctx.
> +} else {
> +frames_ref = NULL;
> +}
>  }
> 
> -dev = hw_device_match_by_codec(ost->enc, ost->enc_ctx->pix_fmt,
> -   methods, _methods);
> -if (dev) {
> -if (matched_methods &
> AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) {
> -ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
> -if (!ost->enc_ctx->hw_device_ctx)
> -return AVERROR(ENOMEM);
> -}
> -if (matched_methods &
> AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) {
> +for (i = 0;; i++) {
> +config = avcodec_get_hw_config(ost->enc, i);
> +if (!config)
> +break;
> +
> +if (frames_ref &&
> +config->methods &
> AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
> +(config->pix_fmt == AV_PIX_FMT_NONE ||
> + config->pix_fmt == ost->enc_ctx->pix_fmt)) {
> +av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input "
> +   

[FFmpeg-devel] [PATCH] ffmpeg: Don't require a known device to pass a frames context to an encoder

2020-04-28 Thread Mark Thompson
The previous version here did not handle passing a frames context when
ffmpeg itself did not know about the device it came from (for example,
because it was created by device derivation inside a filter graph), which
would break encoders requiring that input.  Fix that by checking for HW
frames and device context methods independently, and prefer to use a
frames context method if possible.  At the same time, revert the encoding
additions to the device matching function because the additional
complexity was not relevant to decoding.
---
 fftools/ffmpeg_hw.c | 75 +
 1 file changed, 42 insertions(+), 33 deletions(-)

diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c
index c5c8aa97ef..fc4a5d31d6 100644
--- a/fftools/ffmpeg_hw.c
+++ b/fftools/ffmpeg_hw.c
@@ -19,6 +19,7 @@
 #include 
 
 #include "libavutil/avstring.h"
+#include "libavutil/pixdesc.h"
 #include "libavfilter/buffersink.h"
 
 #include "ffmpeg.h"
@@ -282,10 +283,7 @@ void hw_device_free_all(void)
 nb_hw_devices = 0;
 }
 
-static HWDevice *hw_device_match_by_codec(const AVCodec *codec,
-  enum AVPixelFormat format,
-  int possible_methods,
-  int *matched_methods)
+static HWDevice *hw_device_match_by_codec(const AVCodec *codec)
 {
 const AVCodecHWConfig *config;
 HWDevice *dev;
@@ -294,18 +292,11 @@ static HWDevice *hw_device_match_by_codec(const AVCodec 
*codec,
 config = avcodec_get_hw_config(codec, i);
 if (!config)
 return NULL;
-if (format != AV_PIX_FMT_NONE &&
-config->pix_fmt != AV_PIX_FMT_NONE &&
-config->pix_fmt != format)
-continue;
-if (!(config->methods & possible_methods))
+if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX))
 continue;
 dev = hw_device_get_by_type(config->device_type);
-if (dev) {
-if (matched_methods)
-*matched_methods = config->methods & possible_methods;
+if (dev)
 return dev;
-}
 }
 }
 
@@ -351,9 +342,7 @@ int hw_device_setup_for_decode(InputStream *ist)
 if (!dev)
 err = hw_device_init_from_type(type, NULL, );
 } else {
-dev = hw_device_match_by_codec(ist->dec, AV_PIX_FMT_NONE,
-   
AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
-   NULL);
+dev = hw_device_match_by_codec(ist->dec);
 if (!dev) {
 // No device for this codec, but not using generic hwaccel
 // and therefore may well not need one - ignore.
@@ -429,37 +418,57 @@ int hw_device_setup_for_decode(InputStream *ist)
 
 int hw_device_setup_for_encode(OutputStream *ost)
 {
-HWDevice *dev;
-AVBufferRef *frames_ref;
-int methods = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX;
-int matched_methods;
+const AVCodecHWConfig *config;
+HWDevice *dev = NULL;
+AVBufferRef *frames_ref = NULL;
+int i;
 
 if (ost->filter) {
 frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter);
 if (frames_ref &&
 ((AVHWFramesContext*)frames_ref->data)->format ==
-ost->enc_ctx->pix_fmt)
-methods |= AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX;
+ost->enc_ctx->pix_fmt) {
+// Matching format, will try to use hw_frames_ctx.
+} else {
+frames_ref = NULL;
+}
 }
 
-dev = hw_device_match_by_codec(ost->enc, ost->enc_ctx->pix_fmt,
-   methods, _methods);
-if (dev) {
-if (matched_methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) {
-ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref);
-if (!ost->enc_ctx->hw_device_ctx)
-return AVERROR(ENOMEM);
-}
-if (matched_methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) {
+for (i = 0;; i++) {
+config = avcodec_get_hw_config(ost->enc, i);
+if (!config)
+break;
+
+if (frames_ref &&
+config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX &&
+(config->pix_fmt == AV_PIX_FMT_NONE ||
+ config->pix_fmt == ost->enc_ctx->pix_fmt)) {
+av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input "
+   "frames context (format %s) with %s encoder.\n",
+   av_get_pix_fmt_name(ost->enc_ctx->pix_fmt),
+   ost->enc->name);
 ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref);
 if (!ost->enc_ctx->hw_frames_ctx)
 return AVERROR(ENOMEM);
+return 0;
 }
-return 0;
+
+if (!dev &&
+config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
+dev =