Skip to content

Commit

Permalink
ffprobe: add show_vt_info option
Browse files Browse the repository at this point in the history
  • Loading branch information
gnattu committed Sep 20, 2024
1 parent 3d25b92 commit 79d9f6f
Show file tree
Hide file tree
Showing 2 changed files with 360 additions and 0 deletions.
359 changes: 359 additions & 0 deletions debian/patches/0075-ffprobe-add-vt-info.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
Index: FFmpeg/fftools/Makefile
===================================================================
--- FFmpeg.orig/fftools/Makefile
+++ FFmpeg/fftools/Makefile
@@ -38,6 +38,10 @@ $(1)$(PROGSSUF)_g$(EXESUF): FF_EXTRALIBS
-include $$(OBJS-$(1):.o=.d)
endef

+ifdef CONFIG_VIDEOTOOLBOX
+ OBJS-ffprobe += fftools/opt_vtinfo.o
+endif
+
$(foreach P,$(AVPROGS-yes),$(eval $(call DOFFTOOL,$(P))))

ifdef HAVE_GNU_WINDRES
Index: FFmpeg/fftools/opt_vtinfo.h
===================================================================
--- /dev/null
+++ FFmpeg/fftools/opt_vtinfo.h
@@ -0,0 +1,6 @@
+/**
+ * Print the VideoToolbox Hardware decoder/encoder info
+ * For decoders, only the decoder name the system claims to support will be printed
+ * This option processing function does not utilize the arguments.
+ */
+int show_vt_info(void *optctx, const char *opt, const char *arg);
Index: FFmpeg/fftools/opt_vtinfo.m
===================================================================
--- /dev/null
+++ FFmpeg/fftools/opt_vtinfo.m
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2024 Gnattu OC
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#import <Foundation/Foundation.h>
+#import <VideoToolbox/VideoToolbox.h>
+#import <CoreMedia/CoreMedia.h>
+#include "opt_vtinfo.h"
+
+CMVideoCodecType decoderCodecs[] = {
+ kCMVideoCodecType_MPEG2Video,
+ kCMVideoCodecType_MPEG4Video,
+ kCMVideoCodecType_H264,
+ kCMVideoCodecType_HEVC,
+ kCMVideoCodecType_VP9,
+ kCMVideoCodecType_AV1,
+};
+
+CFStringRef avProfiles[] = {
+ CFSTR("FF_PROFILE_H264_BASELINE"),
+ CFSTR("FF_PROFILE_H264_CONSTRAINED_BASELINE"),
+ CFSTR("FF_PROFILE_H264_MAIN"),
+ CFSTR("FF_PROFILE_H264_EXTENDED"),
+ CFSTR("FF_PROFILE_H264_HIGH"),
+ CFSTR("FF_PROFILE_H264_HIGH_422"),
+ CFSTR("FF_PROFILE_H264_HIGH_444_PREDICTIVE"),
+ CFSTR("FF_PROFILE_HEVC_MAIN"),
+ CFSTR("FF_PROFILE_HEVC_MAIN_10"),
+ CFSTR("FF_PROFILE_HEVC_MAIN_STILL_PICTURE"),
+ CFSTR("FF_PROFILE_HEVC_REXT"),
+};
+
+typedef struct ResCombo {
+ const int width;
+ const int height;
+} ResCombo;
+
+static const ResCombo res_combos[] = {
+ { 64, 64 },
+ { 128, 128 },
+ { 144, 144 },
+ { 256, 256 },
+ { 720, 480 },
+ { 1280, 720 },
+ { 2048, 1024 },
+ { 1920, 1080 },
+ { 1920, 1088 },
+ { 2560, 1440 },
+ { 2048, 2048 },
+ { 3840, 2160 },
+ { 4096, 2160 },
+ { 4096, 2304 },
+ { 4096, 2318 },
+ { 3840, 3840 },
+ { 4080, 4080 },
+ { 4096, 4096 },
+ { 7680, 4320 },
+ { 8192, 4320 },
+ { 8192, 4352 },
+ { 8192, 8192 },
+ { 0, 0 },
+};
+
+#define DLog(FORMAT, ...) fprintf(stdout,"%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
+static void printDictAsJSON(CFDictionaryRef dict) {
+ NSDictionary *nsDict = (__bridge NSDictionary *)dict;
+ NSError *error = nil;
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:nsDict options:0 error:&error];
+
+ if (jsonData) {
+ NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ DLog(@"%@", jsonString)
+ } else {
+ DLog(@"Error serializing dictionary to JSON: %@", error)
+ }
+}
+
+static CFStringRef codecTypeName(CMVideoCodecType codecType) {
+ switch(codecType) {
+ case kCMVideoCodecType_MPEG2Video: return CFSTR("AV_CODEC_ID_MPEG2VIDEO");
+ case kCMVideoCodecType_MPEG4Video: return CFSTR("AV_CODEC_ID_MPEG4");
+ case kCMVideoCodecType_VP9: return CFSTR("AV_CODEC_ID_VP9");
+ case kCMVideoCodecType_JPEG: return CFSTR("AV_CODEC_ID_MJPEG");
+ case kCMVideoCodecType_H264: return CFSTR("AV_CODEC_ID_H264");
+ case kCMVideoCodecType_HEVC: return CFSTR("AV_CODEC_ID_HEVC");
+ case kCMVideoCodecType_AV1: return CFSTR("AV_CODEC_ID_AV1");
+ default: return CFSTR("AV_CODEC_ID_NONE");
+ }
+}
+
+static CFStringRef profileLevelName(CFStringRef profileLevel) {
+ CFStringRef vtProfiles[] = {
+ kVTProfileLevel_H264_Baseline_AutoLevel,
+ kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel,
+ kVTProfileLevel_H264_Main_AutoLevel,
+ kVTProfileLevel_H264_Extended_AutoLevel,
+ kVTProfileLevel_H264_High_AutoLevel,
+ CFSTR("H264_High422_AutoLevel"),
+ CFSTR("H264_High444Predictive_AutoLevel"),
+ kVTProfileLevel_HEVC_Main_AutoLevel,
+ kVTProfileLevel_HEVC_Main10_AutoLevel,
+ CFSTR("HEVC_MainStill_AutoLevel"),
+ CFSTR("HEVC_Main42210_AutoLevel"), // 444 and 8 bit variants also exists, use this to indicate rext support
+ };
+
+ CFDictionaryRef vtProfileToAvProfile = CFDictionaryCreate(
+ kCFAllocatorDefault,
+ (const void **)vtProfiles,
+ (const void **)avProfiles,
+ sizeof(vtProfiles) / sizeof(vtProfiles[0]),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks
+ );
+
+ CFStringRef avProfile = CFDictionaryGetValue(vtProfileToAvProfile, profileLevel);
+
+ CFRelease(vtProfileToAvProfile);
+
+ return avProfile;
+}
+
+static void addEncoderCapability(CFDictionaryRef encInfo, CFMutableArrayRef capabilities) {
+ CFNumberRef codecTypeNum = CFDictionaryGetValue(encInfo, kVTVideoEncoderList_CodecType);
+ CFBooleanRef isIsHardwareAccelerated = CFDictionaryGetValue(encInfo, kVTVideoEncoderList_IsHardwareAccelerated);
+ CMVideoCodecType codecType;
+ CFBooleanRef supports10bitEncode = kCFBooleanFalse;
+ CFBooleanRef supportsHdrEncode = kCFBooleanFalse;
+ CFBooleanRef supportsYuv444Encode = kCFBooleanFalse;
+ CFDictionaryRef supportedProps, profileLevels, transferFunctions = NULL;
+ CFMutableDictionaryRef encCapability, encSpec = NULL;
+ CFStringRef encoderID = NULL;
+ CFNumberRef x, y = NULL;
+ int maxWidth = 0;
+ int maxHeight = 0;
+
+ if (!CFNumberGetValue(codecTypeNum, kCFNumberSInt32Type, &codecType)) {
+ // encoder with unknown type, return
+ return;
+ }
+
+ if (!isIsHardwareAccelerated || !CFBooleanGetValue(isIsHardwareAccelerated)) {
+ // We don't care about software encoders
+ return;
+ }
+
+ if (codecType != kCMVideoCodecType_H264
+ && codecType != kCMVideoCodecType_HEVC
+ && codecType != kCMVideoCodecType_JPEG
+ && codecType != kCMVideoCodecType_AV1) {
+ // The only encoders we care about
+ return;
+ }
+
+ encoderID = CFDictionaryGetValue(encInfo, kVTVideoEncoderList_EncoderID);
+ encSpec = CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ if (encSpec == NULL) {
+ exit(ENOMEM);
+ }
+
+ CFDictionaryAddValue(encSpec, kVTVideoEncoderList_EncoderID, encoderID);
+
+ for (const ResCombo *r = &res_combos[0]; r->width > 0; r++) {
+ OSStatus status = VTCopySupportedPropertyDictionaryForEncoder(r->width,
+ r->height,
+ codecType,
+ encSpec,
+ NULL,
+ &supportedProps);
+ if (status != 0 || supportedProps == NULL) {
+ break;
+ }
+
+ maxWidth = r->width;
+ maxHeight = r->height;
+ }
+
+ CFRelease(encSpec);
+
+ encCapability = CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (encCapability == NULL) {
+ exit(ENOMEM);
+ }
+
+ x = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &maxWidth);
+ y = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &maxHeight);
+
+ CFDictionaryAddValue(encCapability, CFSTR("Codec"), codecTypeName(codecType));
+ CFDictionaryAddValue(encCapability, CFSTR("MaxWidth"), x);
+ CFDictionaryAddValue(encCapability, CFSTR("MaxHeight"), y);
+
+ CFRelease(x);
+ CFRelease(y);
+
+ profileLevels = CFDictionaryGetValue(supportedProps, kVTCompressionPropertyKey_ProfileLevel);
+
+ if (profileLevels) {
+ CFArrayRef listOfProfiles = CFDictionaryGetValue(profileLevels, kVTPropertySupportedValueListKey);
+ if (listOfProfiles) {
+ CFMutableArrayRef encProfiles = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ if (encProfiles == NULL) {
+ exit(ENOMEM);
+ }
+
+ {
+ CFIndex len = CFArrayGetCount(listOfProfiles);
+ for (CFIndex i = 0; i < len; i++) {
+ CFTypeRef profile = CFArrayGetValueAtIndex(listOfProfiles, i);
+ if (CFStringCompare(profile, CFSTR("H264_High422_AutoLevel"), 0) == kCFCompareEqualTo ||
+ CFStringCompare(profile, kVTProfileLevel_HEVC_Main10_AutoLevel, 0) == kCFCompareEqualTo) {
+ supports10bitEncode = kCFBooleanTrue;
+ }
+ if (CFStringCompare(profile, CFSTR("H264_High444Predictive_AutoLevel"), 0) == kCFCompareEqualTo ||
+ CFStringCompare(profile, CFSTR("HEVC_Main44410_AutoLevel"), 0) == kCFCompareEqualTo) {
+ supportsYuv444Encode = kCFBooleanTrue;
+ }
+ {
+ const CFStringRef profileAvName = profileLevelName(profile);
+ if (profileAvName) {
+ CFArrayAppendValue(encProfiles, profileAvName);
+ }
+ }
+ }
+ }
+ CFDictionaryAddValue(encCapability, CFSTR("Profiles"), encProfiles);
+ CFRelease(encProfiles);
+ }
+ }
+
+ transferFunctions = CFDictionaryGetValue(supportedProps, kVTCompressionPropertyKey_TransferFunction);
+ if (transferFunctions) {
+ CFArrayRef listOfTransferFunctions = CFDictionaryGetValue(transferFunctions, kVTPropertySupportedValueListKey);
+ if (listOfTransferFunctions) {
+ bool supportsPQ = CFArrayContainsValue(listOfTransferFunctions,
+ CFRangeMake(0,CFArrayGetCount(listOfTransferFunctions)),
+ kCMFormatDescriptionTransferFunction_SMPTE_ST_2084_PQ);
+ if (supportsPQ) {
+ supportsHdrEncode = kCFBooleanTrue;
+ }
+ }
+ }
+
+ CFDictionaryAddValue(encCapability, CFSTR("Support10bitEncode"), supports10bitEncode);
+ CFDictionaryAddValue(encCapability, CFSTR("SupportHDREncode"), supportsHdrEncode);
+ CFDictionaryAddValue(encCapability, CFSTR("SupportYuv444Encode"), supportsYuv444Encode);
+
+ CFRelease(supportsYuv444Encode);
+ CFRelease(supports10bitEncode);
+ CFRelease(supportsHdrEncode);
+
+ CFArrayAppendValue(capabilities, encCapability);
+ CFRelease(encCapability);
+}
+
+int show_vt_info(void *optctx, const char *opt, const char *arg) {
+ @autoreleasepool {
+ CFMutableDictionaryRef vtInfo = CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+ CFArrayRef encoders = NULL;
+ int encoderCount;
+ OSStatus status;
+ CFMutableArrayRef encCapabilities = NULL;
+ CFMutableArrayRef decCapabilities = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ if (decCapabilities == NULL) {
+ exit(ENOMEM);
+ }
+
+ for (CFIndex i = 0; i < 5; i ++) {
+ CMVideoCodecType c = decoderCodecs[i];
+ VTRegisterSupplementalVideoDecoderIfAvailable(c);
+ if (VTIsHardwareDecodeSupported(c)) {
+ CFArrayAppendValue(decCapabilities, codecTypeName(c));
+ }
+ }
+
+ CFDictionaryAddValue(vtInfo, CFSTR("Decoders"), decCapabilities);
+ CFRelease(decCapabilities);
+
+ encCapabilities = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ if (encCapabilities == NULL) {
+ exit(ENOMEM);
+ }
+
+ status = VTCopyVideoEncoderList(NULL, &encoders);
+ if (status != 0 || encoders == NULL) {
+ // fprintf(stderr, "Get encoder list failed: %d\n", status);
+ return 1;
+ }
+
+ encoderCount = (int)CFArrayGetCount(encoders);
+ for (int i = 0; i < encoderCount; i++) {
+ CFDictionaryRef encInfo = CFArrayGetValueAtIndex(encoders, i);
+ addEncoderCapability(encInfo, encCapabilities);
+ }
+
+ CFDictionaryAddValue(vtInfo, CFSTR("Encoders"), encCapabilities);
+ CFRelease(encCapabilities);
+ CFRelease(encoders);
+ printDictAsJSON(vtInfo);
+ CFRelease(vtInfo);
+ }
+ return 0;
+}
1 change: 1 addition & 0 deletions debian/patches/series
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@
0072-add-bwdif-videotoolbox-filter.patch
0073-add-12bit-decoding-on-videotoolbox.patch
0074-fix-the-sub2video-perf-regressions.patch
0075-ffprobe-add-vt-info.patch

0 comments on commit 79d9f6f

Please sign in to comment.