Currently, AVHWAccels are looked up using a (codec_id, pixfmt) tuple. This means it's impossible to have 2 decoders for the same codec and using the same opaque hardware pixel format. This breaks merging Libav's CUVID hwaccel. FFmpeg has its own CUVID support, but it's a full stream decoder, using NVIDIA's codec parser. The Libav one is a true hwaccel, which is based on the builtin software decoders. Fix this by introducing another field to disambiguate AVHWAccels, and use it for our CUVID decoders. FF_CODEC_CAP_HWACCEL_REQUIRE_CLASS makes this mechanism backwards compatible and optional.tags/n4.0
@@ -3532,6 +3532,13 @@ typedef struct AVHWAccel { | |||||
* Internal hwaccel capabilities. | * Internal hwaccel capabilities. | ||||
*/ | */ | ||||
int caps_internal; | int caps_internal; | ||||
/** | |||||
* Some hwaccels are ambiguous if only the id and pix_fmt fields are used. | |||||
* If non-NULL, the associated AVCodec must have | |||||
* FF_CODEC_CAP_HWACCEL_REQUIRE_CLASS set. | |||||
*/ | |||||
const AVClass *decoder_class; | |||||
} AVHWAccel; | } AVHWAccel; | ||||
/** | /** | ||||
@@ -1106,6 +1106,7 @@ static const AVOption options[] = { | |||||
.type = AVMEDIA_TYPE_VIDEO, \ | .type = AVMEDIA_TYPE_VIDEO, \ | ||||
.id = AV_CODEC_ID_##X, \ | .id = AV_CODEC_ID_##X, \ | ||||
.pix_fmt = AV_PIX_FMT_CUDA, \ | .pix_fmt = AV_PIX_FMT_CUDA, \ | ||||
.decoder_class = &x##_cuvid_class, \ | |||||
}; \ | }; \ | ||||
AVCodec ff_##x##_cuvid_decoder = { \ | AVCodec ff_##x##_cuvid_decoder = { \ | ||||
.name = #x "_cuvid", \ | .name = #x "_cuvid", \ | ||||
@@ -1120,6 +1121,7 @@ static const AVOption options[] = { | |||||
.receive_frame = cuvid_output_frame, \ | .receive_frame = cuvid_output_frame, \ | ||||
.flush = cuvid_flush, \ | .flush = cuvid_flush, \ | ||||
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ | .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ | ||||
.caps_internal = FF_CODEC_CAP_HWACCEL_REQUIRE_CLASS, \ | |||||
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \ | .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \ | ||||
AV_PIX_FMT_NV12, \ | AV_PIX_FMT_NV12, \ | ||||
AV_PIX_FMT_P010, \ | AV_PIX_FMT_P010, \ | ||||
@@ -1090,15 +1090,19 @@ enum AVPixelFormat avcodec_default_get_format(struct AVCodecContext *s, const en | |||||
return fmt[0]; | return fmt[0]; | ||||
} | } | ||||
static AVHWAccel *find_hwaccel(enum AVCodecID codec_id, | |||||
static AVHWAccel *find_hwaccel(AVCodecContext *avctx, | |||||
enum AVPixelFormat pix_fmt) | enum AVPixelFormat pix_fmt) | ||||
{ | { | ||||
AVHWAccel *hwaccel = NULL; | AVHWAccel *hwaccel = NULL; | ||||
const AVClass *av_class = | |||||
(avctx->codec->caps_internal & FF_CODEC_CAP_HWACCEL_REQUIRE_CLASS) | |||||
? avctx->codec->priv_class : NULL; | |||||
while ((hwaccel = av_hwaccel_next(hwaccel))) | |||||
if (hwaccel->id == codec_id | |||||
while ((hwaccel = av_hwaccel_next(hwaccel))) { | |||||
if (hwaccel->decoder_class == av_class && hwaccel->id == avctx->codec_id | |||||
&& hwaccel->pix_fmt == pix_fmt) | && hwaccel->pix_fmt == pix_fmt) | ||||
return hwaccel; | return hwaccel; | ||||
} | |||||
return NULL; | return NULL; | ||||
} | } | ||||
@@ -1106,7 +1110,7 @@ static int setup_hwaccel(AVCodecContext *avctx, | |||||
const enum AVPixelFormat fmt, | const enum AVPixelFormat fmt, | ||||
const char *name) | const char *name) | ||||
{ | { | ||||
AVHWAccel *hwa = find_hwaccel(avctx->codec_id, fmt); | |||||
AVHWAccel *hwa = find_hwaccel(avctx, fmt); | |||||
int ret = 0; | int ret = 0; | ||||
if (!hwa) { | if (!hwa) { | ||||
@@ -69,6 +69,11 @@ | |||||
*/ | */ | ||||
#define FF_CODEC_CAP_SLICE_THREAD_HAS_MF (1 << 5) | #define FF_CODEC_CAP_SLICE_THREAD_HAS_MF (1 << 5) | ||||
/** | |||||
* Allow only AVHWAccels which have a matching decoder_class field. | |||||
*/ | |||||
#define FF_CODEC_CAP_HWACCEL_REQUIRE_CLASS (1 << 6) | |||||
#ifdef TRACE | #ifdef TRACE | ||||
# define ff_tlog(ctx, ...) av_log(ctx, AV_LOG_TRACE, __VA_ARGS__) | # define ff_tlog(ctx, ...) av_log(ctx, AV_LOG_TRACE, __VA_ARGS__) | ||||
#else | #else | ||||