From: Aman Gupta <a...@tmm1.net> --- configure | 2 + libavcodec/allcodecs.c | 1 + libavcodec/videotoolboxenc.c | 296 ++++++++++++++++++++++++++++++------------- 3 files changed, 214 insertions(+), 85 deletions(-)
diff --git a/configure b/configure index 2cf18ecc12..39b9d4cb0c 100755 --- a/configure +++ b/configure @@ -2928,6 +2928,8 @@ pcm_mulaw_at_encoder_select="audio_frame_queue" chromaprint_muxer_deps="chromaprint" h264_videotoolbox_encoder_deps="pthreads" h264_videotoolbox_encoder_select="videotoolbox_encoder" +hevc_videotoolbox_encoder_deps="pthreads" +hevc_videotoolbox_encoder_select="videotoolbox_encoder" libcelt_decoder_deps="libcelt" libfdk_aac_decoder_deps="libfdk_aac" libfdk_aac_encoder_deps="libfdk_aac" diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index c817003693..d8be53a52a 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -670,6 +670,7 @@ static void register_all(void) REGISTER_ENCODER(HEVC_QSV, hevc_qsv); REGISTER_ENCODER(HEVC_V4L2M2M, hevc_v4l2m2m); REGISTER_ENCODER(HEVC_VAAPI, hevc_vaapi); + REGISTER_ENCODER(HEVC_VIDEOTOOLBOX, hevc_videotoolbox); REGISTER_ENCODER(LIBKVAZAAR, libkvazaar); REGISTER_DECODER(MJPEG_CUVID, mjpeg_cuvid); REGISTER_ENCODER(MJPEG_QSV, mjpeg_qsv); diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c index eba6cc672f..9725964e87 100644 --- a/libavcodec/videotoolboxenc.c +++ b/libavcodec/videotoolboxenc.c @@ -35,6 +35,17 @@ #include "h264_sei.h" #include <dlfcn.h> +#if !HAVE_KCMVIDEOCODECTYPE_HEVC +enum { kCMVideoCodecType_HEVC = 'hvc1' }; +#endif + +typedef OSStatus (*getParameterSetAtIndex)(CMFormatDescriptionRef videoDesc, + size_t parameterSetIndex, + const uint8_t * _Nullable *parameterSetPointerOut, + size_t *parameterSetSizeOut, + size_t *parameterSetCountOut, + int *NALUnitHeaderLengthOut); + //These symbols may not be present static struct{ CFStringRef kCVImageBufferColorPrimaries_ITU_R_2020; @@ -65,10 +76,15 @@ static struct{ CFStringRef kVTProfileLevel_H264_High_5_2; CFStringRef kVTProfileLevel_H264_High_AutoLevel; + CFStringRef kVTProfileLevel_HEVC_Main_AutoLevel; + CFStringRef kVTProfileLevel_HEVC_Main10_AutoLevel; + CFStringRef kVTCompressionPropertyKey_RealTime; CFStringRef kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder; CFStringRef kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder; + + getParameterSetAtIndex CMVideoFormatDescriptionGetHEVCParameterSetAtIndex; } compat_keys; #define GET_SYM(symbol, defaultVal) \ @@ -83,6 +99,12 @@ do{ \ static pthread_once_t once_ctrl = PTHREAD_ONCE_INIT; static void loadVTEncSymbols(){ + compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex = + (getParameterSetAtIndex)dlsym( + RTLD_DEFAULT, + "CMVideoFormatDescriptionGetHEVCParameterSetAtIndex" + ); + GET_SYM(kCVImageBufferColorPrimaries_ITU_R_2020, "ITU_R_2020"); GET_SYM(kCVImageBufferTransferFunction_ITU_R_2020, "ITU_R_2020"); GET_SYM(kCVImageBufferYCbCrMatrix_ITU_R_2020, "ITU_R_2020"); @@ -111,6 +133,9 @@ static void loadVTEncSymbols(){ GET_SYM(kVTProfileLevel_H264_High_5_2, "H264_High_5_2"); GET_SYM(kVTProfileLevel_H264_High_AutoLevel, "H264_High_AutoLevel"); + GET_SYM(kVTProfileLevel_HEVC_Main_AutoLevel, "HEVC_Main_AutoLevel"); + GET_SYM(kVTProfileLevel_HEVC_Main10_AutoLevel, "HEVC_Main10_AutoLevel"); + GET_SYM(kVTCompressionPropertyKey_RealTime, "RealTime"); GET_SYM(kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, @@ -133,6 +158,13 @@ typedef enum VTH264Entropy{ VT_CABAC } VTH264Entropy; +typedef enum VT_HEVCProfile { + HEVC_PROF_AUTO, + HEVC_PROF_MAIN, + HEVC_PROF_MAIN10, + HEVC_PROF_COUNT +} VT_HEVCProfile; + static const uint8_t start_code[] = { 0, 0, 0, 1 }; typedef struct ExtraSEI { @@ -149,10 +181,12 @@ typedef struct BufNode { typedef struct VTEncContext { AVClass *class; + enum AVCodecID codec_id; VTCompressionSessionRef session; CFStringRef ycbcr_matrix; CFStringRef color_primaries; CFStringRef transfer_function; + getParameterSetAtIndex get_param_set_func; pthread_mutex_t lock; pthread_cond_t cv_sample_sent; @@ -348,6 +382,7 @@ static CMVideoCodecType get_cm_codec_type(enum AVCodecID id) { switch (id) { case AV_CODEC_ID_H264: return kCMVideoCodecType_H264; + case AV_CODEC_ID_HEVC: return kCMVideoCodecType_HEVC; default: return 0; } } @@ -365,17 +400,18 @@ static int get_params_size( CMVideoFormatDescriptionRef vid_fmt, size_t *size) { + VTEncContext *vtctx = avctx->priv_data; size_t total_size = 0; size_t ps_count; int is_count_bad = 0; size_t i; int status; - status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt, - 0, - NULL, - NULL, - &ps_count, - NULL); + status = vtctx->get_param_set_func(vid_fmt, + 0, + NULL, + NULL, + &ps_count, + NULL); if (status) { is_count_bad = 1; ps_count = 0; @@ -385,12 +421,12 @@ static int get_params_size( for (i = 0; i < ps_count || is_count_bad; i++) { const uint8_t *ps; size_t ps_size; - status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt, - i, - &ps, - &ps_size, - NULL, - NULL); + status = vtctx->get_param_set_func(vid_fmt, + i, + &ps, + &ps_size, + NULL, + NULL); if (status) { /* * When ps_count is invalid, status != 0 ends the loop normally @@ -419,18 +455,19 @@ static int copy_param_sets( uint8_t *dst, size_t dst_size) { + VTEncContext *vtctx = avctx->priv_data; size_t ps_count; int is_count_bad = 0; int status; size_t offset = 0; size_t i; - status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt, - 0, - NULL, - NULL, - &ps_count, - NULL); + status = vtctx->get_param_set_func(vid_fmt, + 0, + NULL, + NULL, + &ps_count, + NULL); if (status) { is_count_bad = 1; ps_count = 0; @@ -443,12 +480,12 @@ static int copy_param_sets( size_t ps_size; size_t next_offset; - status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt, - i, - &ps, - &ps_size, - NULL, - NULL); + status = vtctx->get_param_set_func(vid_fmt, + i, + &ps, + &ps_size, + NULL, + NULL); if (status) { if (i > 0 && is_count_bad) status = 0; @@ -548,6 +585,7 @@ static int get_length_code_size( CMSampleBufferRef sample_buffer, size_t *size) { + VTEncContext *vtctx = avctx->priv_data; CMVideoFormatDescriptionRef vid_fmt; int isize; int status; @@ -558,12 +596,12 @@ static int get_length_code_size( return AVERROR_EXTERNAL; } - status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(vid_fmt, - 0, - NULL, - NULL, - NULL, - &isize); + status = vtctx->get_param_set_func(vid_fmt, + 0, + NULL, + NULL, + NULL, + &isize); if (status) { av_log(avctx, AV_LOG_ERROR, "Error getting length code size: %d\n", status); return AVERROR_EXTERNAL; @@ -579,8 +617,8 @@ static int get_length_code_size( * If profile_level_val is NULL and this method returns true, don't specify the * profile/level to the encoder. */ -static bool get_vt_profile_level(AVCodecContext *avctx, - CFStringRef *profile_level_val) +static bool get_vt_h264_profile_level(AVCodecContext *avctx, + CFStringRef *profile_level_val) { VTEncContext *vtctx = avctx->priv_data; int64_t profile = vtctx->profile; @@ -670,6 +708,41 @@ static bool get_vt_profile_level(AVCodecContext *avctx, return true; } +/* + * Returns true on success. + * + * If profile_level_val is NULL and this method returns true, don't specify the + * profile/level to the encoder. + */ +static bool get_vt_hevc_profile_level(AVCodecContext *avctx, + CFStringRef *profile_level_val) +{ + VTEncContext *vtctx = avctx->priv_data; + int64_t profile = vtctx->profile; + + *profile_level_val = NULL; + + switch (profile) { + case HEVC_PROF_AUTO: + return true; + case HEVC_PROF_MAIN: + *profile_level_val = + compat_keys.kVTProfileLevel_HEVC_Main_AutoLevel; + break; + case HEVC_PROF_MAIN10: + *profile_level_val = + compat_keys.kVTProfileLevel_HEVC_Main10_AutoLevel; + break; + } + + if (!*profile_level_val) { + av_log(avctx, AV_LOG_ERROR, "Invalid Profile/Level.\n"); + return false; + } + + return true; +} + static int get_cv_pixel_format(AVCodecContext* avctx, enum AVPixelFormat fmt, enum AVColorRange range, @@ -944,52 +1017,54 @@ static int vtenc_create_encoder(AVCodecContext *avctx, return AVERROR_EXTERNAL; } - bytes_per_second_value = max_rate >> 3; - bytes_per_second = CFNumberCreate(kCFAllocatorDefault, - kCFNumberSInt64Type, - &bytes_per_second_value); - if (!bytes_per_second) { - return AVERROR(ENOMEM); - } - one_second_value = 1; - one_second = CFNumberCreate(kCFAllocatorDefault, - kCFNumberSInt64Type, - &one_second_value); - if (!one_second) { - CFRelease(bytes_per_second); - return AVERROR(ENOMEM); - } - nums[0] = bytes_per_second; - nums[1] = one_second; - data_rate_limits = CFArrayCreate(kCFAllocatorDefault, - nums, - 2, - &kCFTypeArrayCallBacks); + if (vtctx->codec_id == AV_CODEC_ID_H264) { + bytes_per_second_value = max_rate >> 3; + bytes_per_second = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt64Type, + &bytes_per_second_value); + if (!bytes_per_second) { + return AVERROR(ENOMEM); + } + one_second_value = 1; + one_second = CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt64Type, + &one_second_value); + if (!one_second) { + CFRelease(bytes_per_second); + return AVERROR(ENOMEM); + } + nums[0] = (void *)bytes_per_second; + nums[1] = (void *)one_second; + data_rate_limits = CFArrayCreate(kCFAllocatorDefault, + (const void **)nums, + 2, + &kCFTypeArrayCallBacks); + + if (!data_rate_limits) { + CFRelease(bytes_per_second); + CFRelease(one_second); + return AVERROR(ENOMEM); + } + status = VTSessionSetProperty(vtctx->session, + kVTCompressionPropertyKey_DataRateLimits, + data_rate_limits); - if (!data_rate_limits) { CFRelease(bytes_per_second); CFRelease(one_second); - return AVERROR(ENOMEM); - } - status = VTSessionSetProperty(vtctx->session, - kVTCompressionPropertyKey_DataRateLimits, - data_rate_limits); - - CFRelease(bytes_per_second); - CFRelease(one_second); - CFRelease(data_rate_limits); + CFRelease(data_rate_limits); - if (status) { - av_log(avctx, AV_LOG_ERROR, "Error setting max bitrate property: %d\n", status); - return AVERROR_EXTERNAL; - } - - if (profile_level) { - status = VTSessionSetProperty(vtctx->session, - kVTCompressionPropertyKey_ProfileLevel, - profile_level); if (status) { - av_log(avctx, AV_LOG_ERROR, "Error setting profile/level property: %d\n", status); + av_log(avctx, AV_LOG_ERROR, "Error setting max bitrate property: %d\n", status); + return AVERROR_EXTERNAL; + } + + if (profile_level) { + status = VTSessionSetProperty(vtctx->session, + kVTCompressionPropertyKey_ProfileLevel, + profile_level); + if (status) { + av_log(avctx, AV_LOG_ERROR, "Error setting profile/level property: %d\n", status); + } } } @@ -1205,18 +1280,27 @@ static av_cold int vtenc_init(AVCodecContext *avctx) return AVERROR(EINVAL); } - vtctx->has_b_frames = avctx->max_b_frames > 0; - if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){ - av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n"); - vtctx->has_b_frames = false; - } + vtctx->codec_id = avctx->codec_id; - if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) { - av_log(avctx, AV_LOG_WARNING, "CABAC entropy requires 'main' or 'high' profile, but baseline was requested. Encode will not use CABAC entropy.\n"); - vtctx->entropy = VT_ENTROPY_NOT_SET; - } + if (vtctx->codec_id == AV_CODEC_ID_H264) { + vtctx->get_param_set_func = CMVideoFormatDescriptionGetH264ParameterSetAtIndex; + + vtctx->has_b_frames = avctx->max_b_frames > 0; + if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){ + av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n"); + vtctx->has_b_frames = false; + } - if (!get_vt_profile_level(avctx, &profile_level)) return AVERROR(EINVAL); + if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) { + av_log(avctx, AV_LOG_WARNING, "CABAC entropy requires 'main' or 'high' profile, but baseline was requested. Encode will not use CABAC entropy.\n"); + vtctx->entropy = VT_ENTROPY_NOT_SET; + } + + if (!get_vt_h264_profile_level(avctx, &profile_level)) return AVERROR(EINVAL); + } else { + vtctx->get_param_set_func = compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex; + if (!get_vt_hevc_profile_level(avctx, &profile_level)) return AVERROR(EINVAL); + } vtctx->session = NULL; @@ -2426,7 +2510,7 @@ static const enum AVPixelFormat pix_fmts[] = { #define OFFSET(x) offsetof(VTEncContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM -static const AVOption options[] = { +static const AVOption h264_options[] = { { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = H264_PROF_AUTO }, H264_PROF_AUTO, H264_PROF_COUNT, VE, "profile" }, { "baseline", "Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_BASELINE }, INT_MIN, INT_MAX, VE, "profile" }, { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, @@ -2469,7 +2553,7 @@ static const AVOption options[] = { static const AVClass h264_videotoolbox_class = { .class_name = "h264_videotoolbox", .item_name = av_default_item_name, - .option = options, + .option = h264_options, .version = LIBAVUTIL_VERSION_INT, }; @@ -2488,3 +2572,45 @@ AVCodec ff_h264_videotoolbox_encoder = { .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, }; + +static const AVOption hevc_options[] = { + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = HEVC_PROF_AUTO }, HEVC_PROF_AUTO, HEVC_PROF_COUNT, 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" }, + + { "allow_sw", "Allow software encoding", OFFSET(allow_sw), AV_OPT_TYPE_BOOL, + { .i64 = 0 }, 0, 1, VE }, + + { "realtime", "Hint that encoding should happen in real-time if not faster (e.g. capturing from camera).", + OFFSET(realtime), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + + { "frames_before", "Other frames will come before the frames in this session. This helps smooth concatenation issues.", + OFFSET(frames_before), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "frames_after", "Other frames will come after the frames in this session. This helps smooth concatenation issues.", + OFFSET(frames_after), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + + { NULL }, +}; + +static const AVClass hevc_videotoolbox_class = { + .class_name = "hevc_videotoolbox", + .item_name = av_default_item_name, + .option = hevc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_hevc_videotoolbox_encoder = { + .name = "hevc_videotoolbox", + .long_name = NULL_IF_CONFIG_SMALL("VideoToolbox H.265 Encoder"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_HEVC, + .priv_data_size = sizeof(VTEncContext), + .pix_fmts = pix_fmts, + .init = vtenc_init, + .encode2 = vtenc_frame, + .close = vtenc_close, + .capabilities = AV_CODEC_CAP_DELAY, + .priv_class = &hevc_videotoolbox_class, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, +}; -- 2.14.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel