lavc/videotoolboxenc: Add support for HEVC with Alpha.
This change supports the "HEVC Video with Alpha" profile introduced in WWDC 2019 <https://developer.apple.com/videos/play/wwdc2019/506/>. (This change is a partial fix for Ticket #7965.) For example, the following command converts an animation PNG file to an HEVC with Alpha video: ./ffmpeg -i fate-suite/apng/clock.png -c:v hevc_videotoolbox -allow_sw 1 -alpha_quality 0.75 -vtag hvc1 clock.mov (This change uses the "HEVC Video with Alpha" profile only when the '-alpha_quality' value is not 0 for backward compatibility.) Signed-off-by: Hironori Bono <bouno@rouge.plala.or.jp>
This commit is contained in:
parent
efece4442f
commit
4892060f50
2
configure
vendored
2
configure
vendored
@ -2290,6 +2290,7 @@ TOOLCHAIN_FEATURES="
|
|||||||
|
|
||||||
TYPES_LIST="
|
TYPES_LIST="
|
||||||
kCMVideoCodecType_HEVC
|
kCMVideoCodecType_HEVC
|
||||||
|
kCMVideoCodecType_HEVCWithAlpha
|
||||||
kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange
|
kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange
|
||||||
kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ
|
kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ
|
||||||
kCVImageBufferTransferFunction_ITU_R_2100_HLG
|
kCVImageBufferTransferFunction_ITU_R_2100_HLG
|
||||||
@ -6215,6 +6216,7 @@ enabled avfoundation && {
|
|||||||
enabled videotoolbox && {
|
enabled videotoolbox && {
|
||||||
check_lib coreservices CoreServices/CoreServices.h UTGetOSTypeFromString "-framework CoreServices"
|
check_lib coreservices CoreServices/CoreServices.h UTGetOSTypeFromString "-framework CoreServices"
|
||||||
check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVC "-framework CoreMedia"
|
check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVC "-framework CoreMedia"
|
||||||
|
check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVCWithAlpha "-framework CoreMedia"
|
||||||
check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange "-framework CoreVideo"
|
check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange "-framework CoreVideo"
|
||||||
check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ "-framework CoreVideo"
|
check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ "-framework CoreVideo"
|
||||||
check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_ITU_R_2100_HLG "-framework CoreVideo"
|
check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_ITU_R_2100_HLG "-framework CoreVideo"
|
||||||
|
@ -40,6 +40,10 @@
|
|||||||
enum { kCMVideoCodecType_HEVC = 'hvc1' };
|
enum { kCMVideoCodecType_HEVC = 'hvc1' };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !HAVE_KCMVIDEOCODECTYPE_HEVCWITHALPHA
|
||||||
|
enum { kCMVideoCodecType_HEVCWithAlpha = 'muxa' };
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
|
#if !HAVE_KCVPIXELFORMATTYPE_420YPCBCR10BIPLANARVIDEORANGE
|
||||||
enum { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange = 'xf20' };
|
enum { kCVPixelFormatType_420YpCbCr10BiPlanarFullRange = 'xf20' };
|
||||||
enum { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange = 'x420' };
|
enum { kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange = 'x420' };
|
||||||
@ -88,6 +92,7 @@ static struct{
|
|||||||
CFStringRef kVTProfileLevel_HEVC_Main10_AutoLevel;
|
CFStringRef kVTProfileLevel_HEVC_Main10_AutoLevel;
|
||||||
|
|
||||||
CFStringRef kVTCompressionPropertyKey_RealTime;
|
CFStringRef kVTCompressionPropertyKey_RealTime;
|
||||||
|
CFStringRef kVTCompressionPropertyKey_TargetQualityForAlpha;
|
||||||
|
|
||||||
CFStringRef kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder;
|
CFStringRef kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder;
|
||||||
CFStringRef kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder;
|
CFStringRef kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder;
|
||||||
@ -147,6 +152,8 @@ static void loadVTEncSymbols(){
|
|||||||
GET_SYM(kVTProfileLevel_HEVC_Main10_AutoLevel, "HEVC_Main10_AutoLevel");
|
GET_SYM(kVTProfileLevel_HEVC_Main10_AutoLevel, "HEVC_Main10_AutoLevel");
|
||||||
|
|
||||||
GET_SYM(kVTCompressionPropertyKey_RealTime, "RealTime");
|
GET_SYM(kVTCompressionPropertyKey_RealTime, "RealTime");
|
||||||
|
GET_SYM(kVTCompressionPropertyKey_TargetQualityForAlpha,
|
||||||
|
"TargetQualityForAlpha");
|
||||||
|
|
||||||
GET_SYM(kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,
|
GET_SYM(kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder,
|
||||||
"EnableHardwareAcceleratedVideoEncoder");
|
"EnableHardwareAcceleratedVideoEncoder");
|
||||||
@ -222,6 +229,7 @@ typedef struct VTEncContext {
|
|||||||
|
|
||||||
int64_t allow_sw;
|
int64_t allow_sw;
|
||||||
int64_t require_sw;
|
int64_t require_sw;
|
||||||
|
double alpha_quality;
|
||||||
|
|
||||||
bool flushing;
|
bool flushing;
|
||||||
int has_b_frames;
|
int has_b_frames;
|
||||||
@ -392,11 +400,17 @@ static int count_nalus(size_t length_code_size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CMVideoCodecType get_cm_codec_type(enum AVCodecID id)
|
static CMVideoCodecType get_cm_codec_type(enum AVCodecID id,
|
||||||
|
enum AVPixelFormat fmt,
|
||||||
|
double alpha_quality)
|
||||||
{
|
{
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case AV_CODEC_ID_H264: return kCMVideoCodecType_H264;
|
case AV_CODEC_ID_H264: return kCMVideoCodecType_H264;
|
||||||
case AV_CODEC_ID_HEVC: return kCMVideoCodecType_HEVC;
|
case AV_CODEC_ID_HEVC:
|
||||||
|
if (fmt == AV_PIX_FMT_BGRA && alpha_quality > 0.0) {
|
||||||
|
return kCMVideoCodecType_HEVCWithAlpha;
|
||||||
|
}
|
||||||
|
return kCMVideoCodecType_HEVC;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -786,6 +800,8 @@ static int get_cv_pixel_format(AVCodecContext* avctx,
|
|||||||
*av_pixel_format = range == AVCOL_RANGE_JPEG ?
|
*av_pixel_format = range == AVCOL_RANGE_JPEG ?
|
||||||
kCVPixelFormatType_420YpCbCr8PlanarFullRange :
|
kCVPixelFormatType_420YpCbCr8PlanarFullRange :
|
||||||
kCVPixelFormatType_420YpCbCr8Planar;
|
kCVPixelFormatType_420YpCbCr8Planar;
|
||||||
|
} else if (fmt == AV_PIX_FMT_BGRA) {
|
||||||
|
*av_pixel_format = kCVPixelFormatType_32BGRA;
|
||||||
} else if (fmt == AV_PIX_FMT_P010LE) {
|
} else if (fmt == AV_PIX_FMT_P010LE) {
|
||||||
*av_pixel_format = range == AVCOL_RANGE_JPEG ?
|
*av_pixel_format = range == AVCOL_RANGE_JPEG ?
|
||||||
kCVPixelFormatType_420YpCbCr10BiPlanarFullRange :
|
kCVPixelFormatType_420YpCbCr10BiPlanarFullRange :
|
||||||
@ -1140,6 +1156,20 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vtctx->codec_id == AV_CODEC_ID_HEVC) {
|
||||||
|
if (avctx->pix_fmt == AV_PIX_FMT_BGRA && vtctx->alpha_quality > 0.0) {
|
||||||
|
CFNumberRef alpha_quality_num = CFNumberCreate(kCFAllocatorDefault,
|
||||||
|
kCFNumberDoubleType,
|
||||||
|
&vtctx->alpha_quality);
|
||||||
|
if (!alpha_quality_num) return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
status = VTSessionSetProperty(vtctx->session,
|
||||||
|
compat_keys.kVTCompressionPropertyKey_TargetQualityForAlpha,
|
||||||
|
alpha_quality_num);
|
||||||
|
CFRelease(alpha_quality_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (profile_level) {
|
if (profile_level) {
|
||||||
status = VTSessionSetProperty(vtctx->session,
|
status = VTSessionSetProperty(vtctx->session,
|
||||||
kVTCompressionPropertyKey_ProfileLevel,
|
kVTCompressionPropertyKey_ProfileLevel,
|
||||||
@ -1352,7 +1382,7 @@ static int vtenc_configure_encoder(AVCodecContext *avctx)
|
|||||||
CFNumberRef gamma_level = NULL;
|
CFNumberRef gamma_level = NULL;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
codec_type = get_cm_codec_type(avctx->codec_id);
|
codec_type = get_cm_codec_type(avctx->codec_id, avctx->pix_fmt, vtctx->alpha_quality);
|
||||||
if (!codec_type) {
|
if (!codec_type) {
|
||||||
av_log(avctx, AV_LOG_ERROR, "Error: no mapping for AVCodecID %d\n", avctx->codec_id);
|
av_log(avctx, AV_LOG_ERROR, "Error: no mapping for AVCodecID %d\n", avctx->codec_id);
|
||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
@ -2066,6 +2096,14 @@ static int get_cv_pixel_info(
|
|||||||
strides[2] = frame ? frame->linesize[2] : (avctx->width + 1) / 2;
|
strides[2] = frame ? frame->linesize[2] : (avctx->width + 1) / 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AV_PIX_FMT_BGRA:
|
||||||
|
*plane_count = 1;
|
||||||
|
|
||||||
|
widths [0] = avctx->width;
|
||||||
|
heights[0] = avctx->height;
|
||||||
|
strides[0] = frame ? frame->linesize[0] : avctx->width * 4;
|
||||||
|
break;
|
||||||
|
|
||||||
case AV_PIX_FMT_P010LE:
|
case AV_PIX_FMT_P010LE:
|
||||||
*plane_count = 2;
|
*plane_count = 2;
|
||||||
widths[0] = avctx->width;
|
widths[0] = avctx->width;
|
||||||
@ -2564,6 +2602,7 @@ static const enum AVPixelFormat hevc_pix_fmts[] = {
|
|||||||
AV_PIX_FMT_VIDEOTOOLBOX,
|
AV_PIX_FMT_VIDEOTOOLBOX,
|
||||||
AV_PIX_FMT_NV12,
|
AV_PIX_FMT_NV12,
|
||||||
AV_PIX_FMT_YUV420P,
|
AV_PIX_FMT_YUV420P,
|
||||||
|
AV_PIX_FMT_BGRA,
|
||||||
AV_PIX_FMT_P010LE,
|
AV_PIX_FMT_P010LE,
|
||||||
AV_PIX_FMT_NONE
|
AV_PIX_FMT_NONE
|
||||||
};
|
};
|
||||||
@ -2641,6 +2680,8 @@ static const AVOption hevc_options[] = {
|
|||||||
{ "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" },
|
{ "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" },
|
||||||
{ "main10", "Main10 Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN10 }, INT_MIN, INT_MAX, VE, "profile" },
|
{ "main10", "Main10 Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN10 }, INT_MIN, INT_MAX, VE, "profile" },
|
||||||
|
|
||||||
|
{ "alpha_quality", "Compression quality for the alpha channel", OFFSET(alpha_quality), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0.0, 1.0, VE },
|
||||||
|
|
||||||
COMMON_OPTIONS
|
COMMON_OPTIONS
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user