encoder.c 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  1. #include <obs-module.h>
  2. #include <util/darray.h>
  3. #include <util/platform.h>
  4. #include <obs-avc.h>
  5. #include <CoreFoundation/CoreFoundation.h>
  6. #include <VideoToolbox/VideoToolbox.h>
  7. #include <VideoToolbox/VTVideoEncoderList.h>
  8. #include <CoreMedia/CoreMedia.h>
  9. #include <util/apple/cfstring-utils.h>
  10. #include <assert.h>
  11. #define VT_LOG(level, format, ...) blog(level, "[VideoToolbox encoder]: " format, ##__VA_ARGS__)
  12. #define VT_LOG_ENCODER(encoder, codec_type, level, format, ...) \
  13. blog(level, "[VideoToolbox %s: '%s']: " format, obs_encoder_get_name(encoder), \
  14. codec_type_to_print_fmt(codec_type), ##__VA_ARGS__)
  15. #define VT_BLOG(level, format, ...) VT_LOG_ENCODER(enc->encoder, enc->codec_type, level, format, ##__VA_ARGS__)
  16. struct vt_encoder_type_data {
  17. const char *disp_name;
  18. const char *id;
  19. CMVideoCodecType codec_type;
  20. bool hardware_accelerated;
  21. };
  22. struct vt_prores_encoder_data {
  23. FourCharCode codec_type;
  24. CFStringRef encoder_id;
  25. };
  26. static DARRAY(struct vt_prores_encoder_data) vt_prores_hardware_encoder_list;
  27. static DARRAY(struct vt_prores_encoder_data) vt_prores_software_encoder_list;
  28. #ifdef __aarch64__
  29. bool is_apple_silicon = true;
  30. #else
  31. bool is_apple_silicon = false;
  32. #endif
  33. struct vt_encoder {
  34. obs_encoder_t *encoder;
  35. const char *vt_encoder_id;
  36. uint32_t width;
  37. uint32_t height;
  38. uint32_t keyint;
  39. uint32_t fps_num;
  40. uint32_t fps_den;
  41. const char *rate_control;
  42. uint32_t bitrate;
  43. float quality;
  44. bool limit_bitrate;
  45. uint32_t rc_max_bitrate;
  46. double rc_max_bitrate_window;
  47. const char *profile;
  48. CMVideoCodecType codec_type;
  49. bool bframes;
  50. int vt_pix_fmt;
  51. enum video_colorspace colorspace;
  52. VTCompressionSessionRef session;
  53. CMSimpleQueueRef queue;
  54. bool hw_enc;
  55. DARRAY(uint8_t) packet_data;
  56. DARRAY(uint8_t) extra_data;
  57. };
  58. static const char *codec_type_to_print_fmt(CMVideoCodecType codec_type)
  59. {
  60. switch (codec_type) {
  61. case kCMVideoCodecType_H264:
  62. return "h264";
  63. case kCMVideoCodecType_HEVC:
  64. return "hevc";
  65. case kCMVideoCodecType_AppleProRes4444XQ:
  66. return "ap4x";
  67. case kCMVideoCodecType_AppleProRes4444:
  68. return "ap4h";
  69. case kCMVideoCodecType_AppleProRes422Proxy:
  70. return "apco";
  71. case kCMVideoCodecType_AppleProRes422LT:
  72. return "apcs";
  73. case kCMVideoCodecType_AppleProRes422:
  74. return "apcn";
  75. case kCMVideoCodecType_AppleProRes422HQ:
  76. return "apch";
  77. default:
  78. return "";
  79. }
  80. }
  81. static void log_osstatus(int log_level, struct vt_encoder *enc, const char *context, OSStatus code)
  82. {
  83. char *c_str = NULL;
  84. CFErrorRef err = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, code, NULL);
  85. CFStringRef str = CFErrorCopyDescription(err);
  86. c_str = cfstr_copy_cstr(str, kCFStringEncodingUTF8);
  87. if (c_str) {
  88. if (enc)
  89. VT_BLOG(log_level, "Error in %s: %s", context, c_str);
  90. else
  91. VT_LOG(log_level, "Error in %s: %s", context, c_str);
  92. }
  93. bfree(c_str);
  94. CFRelease(str);
  95. CFRelease(err);
  96. }
  97. static CFStringRef obs_to_vt_profile(CMVideoCodecType codec_type, const char *profile, enum video_format format)
  98. {
  99. if (codec_type == kCMVideoCodecType_H264) {
  100. if (strcmp(profile, "baseline") == 0)
  101. return kVTProfileLevel_H264_Baseline_AutoLevel;
  102. else if (strcmp(profile, "main") == 0)
  103. return kVTProfileLevel_H264_Main_AutoLevel;
  104. else if (strcmp(profile, "high") == 0)
  105. return kVTProfileLevel_H264_High_AutoLevel;
  106. else
  107. return kVTProfileLevel_H264_Main_AutoLevel;
  108. #ifdef ENABLE_HEVC
  109. } else if (codec_type == kCMVideoCodecType_HEVC) {
  110. if (strcmp(profile, "main") == 0) {
  111. if (format == VIDEO_FORMAT_P010) {
  112. VT_LOG(LOG_WARNING, "Forcing main10 for P010");
  113. return kVTProfileLevel_HEVC_Main10_AutoLevel;
  114. } else {
  115. return kVTProfileLevel_HEVC_Main_AutoLevel;
  116. }
  117. }
  118. if (strcmp(profile, "main10") == 0)
  119. return kVTProfileLevel_HEVC_Main10_AutoLevel;
  120. if (__builtin_available(macOS 12.3, *)) {
  121. if (strcmp(profile, "main42210") == 0)
  122. return kVTProfileLevel_HEVC_Main42210_AutoLevel;
  123. }
  124. return kVTProfileLevel_HEVC_Main_AutoLevel;
  125. #else
  126. (void)format;
  127. #endif // ENABLE_HEVC
  128. } else {
  129. return kVTProfileLevel_H264_Baseline_AutoLevel;
  130. }
  131. }
  132. static CFStringRef obs_to_vt_colorspace(enum video_colorspace cs)
  133. {
  134. switch (cs) {
  135. case VIDEO_CS_601:
  136. return kCVImageBufferYCbCrMatrix_ITU_R_601_4;
  137. case VIDEO_CS_2100_PQ:
  138. case VIDEO_CS_2100_HLG:
  139. return kCVImageBufferYCbCrMatrix_ITU_R_2020;
  140. default:
  141. return kCVImageBufferYCbCrMatrix_ITU_R_709_2;
  142. }
  143. }
  144. static CFStringRef obs_to_vt_primaries(enum video_colorspace cs)
  145. {
  146. switch (cs) {
  147. case VIDEO_CS_601:
  148. return kCVImageBufferColorPrimaries_SMPTE_C;
  149. case VIDEO_CS_2100_PQ:
  150. case VIDEO_CS_2100_HLG:
  151. return kCVImageBufferColorPrimaries_ITU_R_2020;
  152. default:
  153. return kCVImageBufferColorPrimaries_ITU_R_709_2;
  154. }
  155. }
  156. static CFStringRef obs_to_vt_transfer(enum video_colorspace cs)
  157. {
  158. switch (cs) {
  159. case VIDEO_CS_SRGB:
  160. return kCVImageBufferTransferFunction_sRGB;
  161. case VIDEO_CS_2100_PQ:
  162. return kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ;
  163. case VIDEO_CS_2100_HLG:
  164. return kCVImageBufferTransferFunction_ITU_R_2100_HLG;
  165. default:
  166. return kCVImageBufferTransferFunction_ITU_R_709_2;
  167. }
  168. }
  169. /* Adapted from Chromium GenerateMasteringDisplayColorVolume */
  170. static CFDataRef obs_to_vt_masteringdisplay(uint32_t hdr_nominal_peak_level)
  171. {
  172. struct mastering_display_colour_volume {
  173. uint16_t display_primaries[3][2];
  174. uint16_t white_point[2];
  175. uint32_t max_display_mastering_luminance;
  176. uint32_t min_display_mastering_luminance;
  177. };
  178. static_assert(sizeof(struct mastering_display_colour_volume) == 24, "May need to adjust struct packing");
  179. struct mastering_display_colour_volume mdcv;
  180. mdcv.display_primaries[0][0] = __builtin_bswap16(13250);
  181. mdcv.display_primaries[0][1] = __builtin_bswap16(34500);
  182. mdcv.display_primaries[1][0] = __builtin_bswap16(7500);
  183. mdcv.display_primaries[1][1] = __builtin_bswap16(3000);
  184. mdcv.display_primaries[2][0] = __builtin_bswap16(34000);
  185. mdcv.display_primaries[2][1] = __builtin_bswap16(16000);
  186. mdcv.white_point[0] = __builtin_bswap16(15635);
  187. mdcv.white_point[1] = __builtin_bswap16(16450);
  188. mdcv.max_display_mastering_luminance = __builtin_bswap32(hdr_nominal_peak_level * 10000);
  189. mdcv.min_display_mastering_luminance = 0;
  190. UInt8 bytes[sizeof(struct mastering_display_colour_volume)];
  191. memcpy(bytes, &mdcv, sizeof(bytes));
  192. return CFDataCreate(kCFAllocatorDefault, bytes, sizeof(bytes));
  193. }
  194. /* Adapted from Chromium GenerateContentLightLevelInfo */
  195. static CFDataRef obs_to_vt_contentlightlevelinfo(uint16_t hdr_nominal_peak_level)
  196. {
  197. struct content_light_level_info {
  198. uint16_t max_content_light_level;
  199. uint16_t max_pic_average_light_level;
  200. };
  201. static_assert(sizeof(struct content_light_level_info) == 4, "May need to adjust struct packing");
  202. struct content_light_level_info clli;
  203. clli.max_content_light_level = __builtin_bswap16(hdr_nominal_peak_level);
  204. clli.max_pic_average_light_level = __builtin_bswap16(hdr_nominal_peak_level);
  205. UInt8 bytes[sizeof(struct content_light_level_info)];
  206. memcpy(bytes, &clli, sizeof(bytes));
  207. return CFDataCreate(kCFAllocatorDefault, bytes, sizeof(bytes));
  208. }
  209. static OSStatus session_set_prop_float(VTCompressionSessionRef session, CFStringRef key, float val)
  210. {
  211. CFNumberRef n = CFNumberCreate(NULL, kCFNumberFloat32Type, &val);
  212. OSStatus code = VTSessionSetProperty(session, key, n);
  213. CFRelease(n);
  214. return code;
  215. }
  216. static OSStatus session_set_prop_int(VTCompressionSessionRef session, CFStringRef key, int32_t val)
  217. {
  218. CFNumberRef n = CFNumberCreate(NULL, kCFNumberSInt32Type, &val);
  219. OSStatus code = VTSessionSetProperty(session, key, n);
  220. CFRelease(n);
  221. return code;
  222. }
  223. static OSStatus session_set_prop_str(VTCompressionSessionRef session, CFStringRef key, char *val)
  224. {
  225. CFStringRef s = CFStringCreateWithFileSystemRepresentation(NULL, val);
  226. OSStatus code = VTSessionSetProperty(session, key, s);
  227. CFRelease(s);
  228. return code;
  229. }
  230. static OSStatus session_set_prop(VTCompressionSessionRef session, CFStringRef key, CFTypeRef val)
  231. {
  232. return VTSessionSetProperty(session, key, val);
  233. }
  234. static OSStatus session_set_bitrate(VTCompressionSessionRef session, const char *rate_control, int new_bitrate,
  235. float quality, bool limit_bitrate, int max_bitrate, double max_bitrate_window)
  236. {
  237. OSStatus code;
  238. bool can_limit_bitrate;
  239. CFStringRef compressionPropertyKey;
  240. if (strcmp(rate_control, "CBR") == 0) {
  241. compressionPropertyKey = kVTCompressionPropertyKey_AverageBitRate;
  242. can_limit_bitrate = true;
  243. if (__builtin_available(macOS 13.0, *)) {
  244. if (is_apple_silicon) {
  245. compressionPropertyKey = kVTCompressionPropertyKey_ConstantBitRate;
  246. can_limit_bitrate = false;
  247. } else {
  248. VT_LOG(LOG_WARNING, "CBR support for VideoToolbox encoder requires Apple Silicon. "
  249. "Will use ABR instead.");
  250. }
  251. } else {
  252. VT_LOG(LOG_WARNING, "CBR support for VideoToolbox encoder requires macOS 13 or newer. "
  253. "Will use ABR instead.");
  254. }
  255. } else if (strcmp(rate_control, "ABR") == 0) {
  256. compressionPropertyKey = kVTCompressionPropertyKey_AverageBitRate;
  257. can_limit_bitrate = true;
  258. } else if (strcmp(rate_control, "CRF") == 0) {
  259. if (is_apple_silicon) {
  260. compressionPropertyKey = kVTCompressionPropertyKey_Quality;
  261. code = session_set_prop_float(session, compressionPropertyKey, quality);
  262. if (code != noErr) {
  263. return code;
  264. }
  265. } else {
  266. VT_LOG(LOG_WARNING, "CRF support for VideoToolbox encoder requires Apple Silicon. "
  267. "Will use ABR instead.");
  268. compressionPropertyKey = kVTCompressionPropertyKey_AverageBitRate;
  269. }
  270. can_limit_bitrate = true;
  271. } else {
  272. VT_LOG(LOG_ERROR, "Selected rate control method is not supported: %s", rate_control);
  273. return kVTParameterErr;
  274. }
  275. if (compressionPropertyKey != kVTCompressionPropertyKey_Quality) {
  276. code = session_set_prop_int(session, compressionPropertyKey, new_bitrate * 1000);
  277. if (code != noErr) {
  278. return code;
  279. }
  280. }
  281. if (limit_bitrate && can_limit_bitrate) {
  282. double cpb_size = max_bitrate * 125 * max_bitrate_window;
  283. CFNumberRef cf_cpb_size = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &cpb_size);
  284. CFNumberRef cf_cpb_window_size =
  285. CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &max_bitrate_window);
  286. CFTypeRef values[2] = {cf_cpb_size, cf_cpb_window_size};
  287. CFArrayRef rate_control_data = CFArrayCreate(kCFAllocatorDefault, values, 2, &kCFTypeArrayCallBacks);
  288. code = session_set_prop(session, kVTCompressionPropertyKey_DataRateLimits, rate_control_data);
  289. CFRelease(cf_cpb_size);
  290. CFRelease(cf_cpb_window_size);
  291. CFRelease(rate_control_data);
  292. if (code == kVTPropertyNotSupportedErr) {
  293. log_osstatus(LOG_WARNING, NULL, "setting DataRateLimits on session", code);
  294. return noErr;
  295. }
  296. }
  297. return noErr;
  298. }
  299. static OSStatus session_set_colorspace(VTCompressionSessionRef session, enum video_colorspace cs)
  300. {
  301. OSStatus code;
  302. CFTypeRef keys[5] = {kVTCompressionPropertyKey_ColorPrimaries, kVTCompressionPropertyKey_TransferFunction,
  303. kVTCompressionPropertyKey_YCbCrMatrix, NULL, NULL};
  304. CFTypeRef values[5] = {obs_to_vt_primaries(cs), obs_to_vt_transfer(cs), obs_to_vt_colorspace(cs), NULL, NULL};
  305. CFDataRef masteringDisplayColorVolume = NULL;
  306. CFDataRef contentLightLevel = NULL;
  307. if (cs == VIDEO_CS_2100_PQ) {
  308. const uint16_t hdr_nominal_peak_level = (uint16_t)obs_get_video_hdr_nominal_peak_level();
  309. masteringDisplayColorVolume = obs_to_vt_masteringdisplay(hdr_nominal_peak_level);
  310. contentLightLevel = obs_to_vt_contentlightlevelinfo(hdr_nominal_peak_level);
  311. keys[3] = kVTCompressionPropertyKey_MasteringDisplayColorVolume;
  312. keys[4] = kVTCompressionPropertyKey_ContentLightLevelInfo;
  313. values[3] = masteringDisplayColorVolume;
  314. values[4] = contentLightLevel;
  315. } else if (cs == VIDEO_CS_2100_HLG) {
  316. masteringDisplayColorVolume = obs_to_vt_masteringdisplay(1000);
  317. contentLightLevel = obs_to_vt_contentlightlevelinfo(1000);
  318. keys[3] = kVTCompressionPropertyKey_MasteringDisplayColorVolume;
  319. keys[4] = kVTCompressionPropertyKey_ContentLightLevelInfo;
  320. values[3] = masteringDisplayColorVolume;
  321. values[4] = contentLightLevel;
  322. }
  323. CFDictionaryRef session_properties = CFDictionaryCreate(
  324. kCFAllocatorDefault, keys, values, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  325. code = VTSessionSetProperties(session, session_properties);
  326. CFRelease(session_properties);
  327. if (masteringDisplayColorVolume != NULL) {
  328. CFRelease(masteringDisplayColorVolume);
  329. }
  330. if (contentLightLevel != NULL) {
  331. CFRelease(contentLightLevel);
  332. }
  333. return code;
  334. }
  335. void sample_encoded_callback(void *data, void *source, OSStatus status, VTEncodeInfoFlags info_flags,
  336. CMSampleBufferRef buffer)
  337. {
  338. if (status != noErr) {
  339. log_osstatus(LOG_ERROR, NULL, "encoder callback", status);
  340. return;
  341. }
  342. if (info_flags == kVTEncodeInfo_FrameDropped) {
  343. VT_LOG(LOG_INFO, "Frame dropped by encoder");
  344. }
  345. CMSimpleQueueRef queue = data;
  346. CVPixelBufferRef pixbuf = source;
  347. if (buffer != NULL) {
  348. CFRetain(buffer);
  349. CMSimpleQueueEnqueue(queue, buffer);
  350. }
  351. CFRelease(pixbuf);
  352. }
  353. static inline CFDictionaryRef create_encoder_spec(const char *vt_encoder_id)
  354. {
  355. CFStringRef id = CFStringCreateWithFileSystemRepresentation(NULL, vt_encoder_id);
  356. CFTypeRef keys[1] = {kVTVideoEncoderSpecification_EncoderID};
  357. CFTypeRef values[1] = {id};
  358. CFDictionaryRef encoder_spec = CFDictionaryCreate(
  359. kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  360. CFRelease(id);
  361. return encoder_spec;
  362. }
  363. static inline CFDictionaryRef create_prores_encoder_spec(CMVideoCodecType target_codec_type, bool hardware_accelerated)
  364. {
  365. CFStringRef encoder_id = NULL;
  366. size_t size = 0;
  367. struct vt_prores_encoder_data *encoder_list = NULL;
  368. if (hardware_accelerated) {
  369. size = vt_prores_hardware_encoder_list.num;
  370. encoder_list = vt_prores_hardware_encoder_list.array;
  371. } else {
  372. size = vt_prores_software_encoder_list.num;
  373. encoder_list = vt_prores_software_encoder_list.array;
  374. }
  375. for (size_t i = 0; i < size; ++i) {
  376. if (target_codec_type == encoder_list[i].codec_type) {
  377. encoder_id = encoder_list[i].encoder_id;
  378. }
  379. }
  380. CFTypeRef keys[1] = {kVTVideoEncoderSpecification_EncoderID};
  381. CFTypeRef values[1] = {encoder_id};
  382. CFDictionaryRef encoder_spec = CFDictionaryCreate(
  383. kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  384. return encoder_spec;
  385. }
  386. static inline CFDictionaryRef create_pixbuf_spec(struct vt_encoder *enc)
  387. {
  388. CFNumberRef PixelFormat = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &enc->vt_pix_fmt);
  389. CFNumberRef Width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &enc->width);
  390. CFNumberRef Height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &enc->height);
  391. CFTypeRef keys[3] = {kCVPixelBufferPixelFormatTypeKey, kCVPixelBufferWidthKey, kCVPixelBufferHeightKey};
  392. CFTypeRef values[3] = {PixelFormat, Width, Height};
  393. CFDictionaryRef pixbuf_spec = CFDictionaryCreate(
  394. kCFAllocatorDefault, keys, values, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  395. CFRelease(PixelFormat);
  396. CFRelease(Width);
  397. CFRelease(Height);
  398. return pixbuf_spec;
  399. }
  400. static OSStatus create_encoder(struct vt_encoder *enc)
  401. {
  402. OSStatus code;
  403. VTCompressionSessionRef s;
  404. const char *codec_name = obs_encoder_get_codec(enc->encoder);
  405. CFDictionaryRef encoder_spec;
  406. if (strcmp(codec_name, "prores") == 0) {
  407. struct vt_encoder_type_data *type_data =
  408. (struct vt_encoder_type_data *)obs_encoder_get_type_data(enc->encoder);
  409. encoder_spec = create_prores_encoder_spec(enc->codec_type, type_data->hardware_accelerated);
  410. } else {
  411. encoder_spec = create_encoder_spec(enc->vt_encoder_id);
  412. }
  413. CFDictionaryRef pixbuf_spec = create_pixbuf_spec(enc);
  414. code = VTCompressionSessionCreate(kCFAllocatorDefault, enc->width, enc->height, enc->codec_type, encoder_spec,
  415. pixbuf_spec, NULL, &sample_encoded_callback, enc->queue, &s);
  416. if (code != noErr) {
  417. log_osstatus(LOG_ERROR, enc, "VTCompressionSessionCreate", code);
  418. }
  419. CFRelease(encoder_spec);
  420. CFRelease(pixbuf_spec);
  421. CFBooleanRef b = NULL;
  422. code = VTSessionCopyProperty(s, kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder, NULL, &b);
  423. if (code == noErr && (enc->hw_enc = CFBooleanGetValue(b)))
  424. VT_BLOG(LOG_INFO, "session created with hardware encoding");
  425. else
  426. enc->hw_enc = false;
  427. if (b != NULL)
  428. CFRelease(b);
  429. if (enc->codec_type == kCMVideoCodecType_H264 || enc->codec_type == kCMVideoCodecType_HEVC) {
  430. // This can fail when using GPU hardware encoding
  431. code = session_set_prop_int(s, kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, enc->keyint);
  432. if (code != noErr)
  433. log_osstatus(LOG_WARNING, enc,
  434. "setting kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration failed, "
  435. "keyframe interval might be incorrect",
  436. code);
  437. CFTypeRef session_keys[4] = {kVTCompressionPropertyKey_MaxKeyFrameInterval,
  438. kVTCompressionPropertyKey_ExpectedFrameRate,
  439. kVTCompressionPropertyKey_AllowFrameReordering,
  440. kVTCompressionPropertyKey_ProfileLevel};
  441. SInt32 key_frame_interval = (SInt32)(enc->keyint * ((float)enc->fps_num / enc->fps_den));
  442. float expected_framerate = (float)enc->fps_num / enc->fps_den;
  443. CFNumberRef MaxKeyFrameInterval =
  444. CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &key_frame_interval);
  445. CFNumberRef ExpectedFrameRate =
  446. CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &expected_framerate);
  447. CFTypeRef AllowFrameReordering = enc->bframes ? kCFBooleanTrue : kCFBooleanFalse;
  448. video_t *video = obs_encoder_video(enc->encoder);
  449. const struct video_output_info *voi = video_output_get_info(video);
  450. CFTypeRef ProfileLevel = obs_to_vt_profile(enc->codec_type, enc->profile, voi->format);
  451. CFTypeRef session_values[4] = {MaxKeyFrameInterval, ExpectedFrameRate, AllowFrameReordering,
  452. ProfileLevel};
  453. CFDictionaryRef session_properties =
  454. CFDictionaryCreate(kCFAllocatorDefault, session_keys, session_values, 4,
  455. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  456. code = VTSessionSetProperties(s, session_properties);
  457. CFRelease(MaxKeyFrameInterval);
  458. CFRelease(ExpectedFrameRate);
  459. CFRelease(AllowFrameReordering);
  460. CFRelease(ProfileLevel);
  461. CFRelease(session_properties);
  462. if (code != noErr) {
  463. return code;
  464. }
  465. code = session_set_bitrate(s, enc->rate_control, enc->bitrate, enc->quality, enc->limit_bitrate,
  466. enc->rc_max_bitrate, enc->rc_max_bitrate_window);
  467. if (code != noErr) {
  468. return code;
  469. }
  470. }
  471. // This can fail depending on hardware configuration
  472. code = session_set_prop(s, kVTCompressionPropertyKey_RealTime, kCFBooleanFalse);
  473. if (code != noErr)
  474. log_osstatus(LOG_WARNING, enc,
  475. "setting kVTCompressionPropertyKey_RealTime failed, "
  476. "frame delay might be increased",
  477. code);
  478. code = session_set_colorspace(s, enc->colorspace);
  479. if (code != noErr) {
  480. return code;
  481. }
  482. code = VTCompressionSessionPrepareToEncodeFrames(s);
  483. if (code != noErr) {
  484. return code;
  485. }
  486. enc->session = s;
  487. return noErr;
  488. }
  489. static void vt_destroy(void *data)
  490. {
  491. struct vt_encoder *enc = data;
  492. if (enc) {
  493. if (enc->session != NULL) {
  494. VTCompressionSessionInvalidate(enc->session);
  495. CFRelease(enc->session);
  496. }
  497. da_free(enc->packet_data);
  498. da_free(enc->extra_data);
  499. bfree(enc);
  500. }
  501. }
  502. static void dump_encoder_info(struct vt_encoder *enc)
  503. {
  504. VT_BLOG(LOG_INFO,
  505. "settings:\n"
  506. "\tvt_encoder_id %s\n"
  507. "\trate_control: %s\n"
  508. "\tbitrate: %d (kbps)\n"
  509. "\tquality: %f\n"
  510. "\tfps_num: %d\n"
  511. "\tfps_den: %d\n"
  512. "\twidth: %d\n"
  513. "\theight: %d\n"
  514. "\tkeyint: %d (s)\n"
  515. "\tlimit_bitrate: %s\n"
  516. "\trc_max_bitrate: %d (kbps)\n"
  517. "\trc_max_bitrate_window: %f (s)\n"
  518. "\thw_enc: %s\n"
  519. "\tprofile: %s\n"
  520. "\tcodec_type: %.4s\n",
  521. enc->vt_encoder_id, enc->rate_control, enc->bitrate, enc->quality, enc->fps_num, enc->fps_den,
  522. enc->width, enc->height, enc->keyint, enc->limit_bitrate ? "on" : "off", enc->rc_max_bitrate,
  523. enc->rc_max_bitrate_window, enc->hw_enc ? "on" : "off",
  524. (enc->profile != NULL && !!strlen(enc->profile)) ? enc->profile : "default",
  525. codec_type_to_print_fmt(enc->codec_type));
  526. }
  527. typedef enum {
  528. kResultSuccess = 0,
  529. kResultColorFormatUnsupported = 1,
  530. kResultFullRangeUnsupported = 2,
  531. } SetVideoFormatResult;
  532. static SetVideoFormatResult set_video_format(struct vt_encoder *enc, enum video_format format,
  533. enum video_range_type range)
  534. {
  535. bool full_range = range == VIDEO_RANGE_FULL;
  536. switch (format) {
  537. case VIDEO_FORMAT_I420:
  538. enc->vt_pix_fmt = full_range ? kCVPixelFormatType_420YpCbCr8PlanarFullRange
  539. : kCVPixelFormatType_420YpCbCr8Planar;
  540. return kResultSuccess;
  541. case VIDEO_FORMAT_NV12:
  542. enc->vt_pix_fmt = full_range ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
  543. : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
  544. return kResultSuccess;
  545. case VIDEO_FORMAT_P010:
  546. if (enc->codec_type == kCMVideoCodecType_HEVC) {
  547. enc->vt_pix_fmt = full_range ? kCVPixelFormatType_420YpCbCr10BiPlanarFullRange
  548. : kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange;
  549. return kResultSuccess;
  550. }
  551. break;
  552. case VIDEO_FORMAT_P216:
  553. if (!full_range) {
  554. enc->vt_pix_fmt = kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange;
  555. return kResultSuccess;
  556. } else {
  557. return kResultFullRangeUnsupported;
  558. }
  559. break;
  560. case VIDEO_FORMAT_P416:
  561. if (!full_range) {
  562. enc->vt_pix_fmt = kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange;
  563. return kResultSuccess;
  564. } else {
  565. return kResultFullRangeUnsupported;
  566. }
  567. break;
  568. default:
  569. return kResultColorFormatUnsupported;
  570. }
  571. return kResultColorFormatUnsupported;
  572. }
  573. static bool update_params(struct vt_encoder *enc, obs_data_t *settings)
  574. {
  575. video_t *video = obs_encoder_video(enc->encoder);
  576. const struct video_output_info *voi = video_output_get_info(video);
  577. const char *codec = obs_encoder_get_codec(enc->encoder);
  578. if (strcmp(codec, "h264") == 0) {
  579. enc->codec_type = kCMVideoCodecType_H264;
  580. obs_data_set_int(settings, "codec_type", enc->codec_type);
  581. #ifdef ENABLE_HEVC
  582. } else if (strcmp(codec, "hevc") == 0) {
  583. enc->codec_type = kCMVideoCodecType_HEVC;
  584. obs_data_set_int(settings, "codec_type", enc->codec_type);
  585. #endif
  586. } else {
  587. enc->codec_type = (CMVideoCodecType)obs_data_get_int(settings, "codec_type");
  588. }
  589. SetVideoFormatResult res = set_video_format(enc, voi->format, voi->range);
  590. if (res == kResultColorFormatUnsupported) {
  591. obs_encoder_set_last_error(enc->encoder, obs_module_text("ColorFormatUnsupported"));
  592. VT_BLOG(LOG_WARNING, "Unsupported color format selected");
  593. return false;
  594. } else if (res == kResultFullRangeUnsupported) {
  595. obs_encoder_set_last_error(enc->encoder, obs_module_text("FullRangeUnsupported"));
  596. VT_BLOG(LOG_WARNING, "Unsupported color range (full) selected");
  597. return false;
  598. }
  599. enc->colorspace = voi->colorspace;
  600. enc->width = obs_encoder_get_width(enc->encoder);
  601. enc->height = obs_encoder_get_height(enc->encoder);
  602. enc->fps_num = voi->fps_num;
  603. enc->fps_den = voi->fps_den;
  604. enc->keyint = (uint32_t)obs_data_get_int(settings, "keyint_sec");
  605. enc->rate_control = obs_data_get_string(settings, "rate_control");
  606. enc->bitrate = (uint32_t)obs_data_get_int(settings, "bitrate");
  607. enc->quality = ((float)obs_data_get_int(settings, "quality")) / 100;
  608. enc->profile = obs_data_get_string(settings, "profile");
  609. enc->limit_bitrate = obs_data_get_bool(settings, "limit_bitrate");
  610. enc->rc_max_bitrate = (uint32_t)obs_data_get_int(settings, "max_bitrate");
  611. enc->rc_max_bitrate_window = obs_data_get_double(settings, "max_bitrate_window");
  612. enc->bframes = obs_data_get_bool(settings, "bframes");
  613. return true;
  614. }
  615. static bool vt_update(void *data, obs_data_t *settings)
  616. {
  617. struct vt_encoder *enc = data;
  618. uint32_t old_bitrate = enc->bitrate;
  619. bool old_limit_bitrate = enc->limit_bitrate;
  620. update_params(enc, settings);
  621. if (old_bitrate == enc->bitrate && old_limit_bitrate == enc->limit_bitrate)
  622. return true;
  623. OSStatus code = session_set_bitrate(enc->session, enc->rate_control, enc->bitrate, enc->quality,
  624. enc->limit_bitrate, enc->rc_max_bitrate, enc->rc_max_bitrate_window);
  625. if (code != noErr)
  626. VT_BLOG(LOG_WARNING, "Failed to set bitrate to session");
  627. dump_encoder_info(enc);
  628. return true;
  629. }
  630. static void *vt_create(obs_data_t *settings, obs_encoder_t *encoder)
  631. {
  632. struct vt_encoder *enc = bzalloc(sizeof(struct vt_encoder));
  633. OSStatus code;
  634. enc->encoder = encoder;
  635. enc->vt_encoder_id = obs_encoder_get_id(encoder);
  636. if (!update_params(enc, settings))
  637. goto fail;
  638. code = CMSimpleQueueCreate(NULL, 100, &enc->queue);
  639. if (code != noErr) {
  640. goto fail;
  641. }
  642. code = create_encoder(enc);
  643. if (code != noErr) {
  644. goto fail;
  645. }
  646. dump_encoder_info(enc);
  647. return enc;
  648. fail:
  649. vt_destroy(enc);
  650. return NULL;
  651. }
  652. static const uint8_t annexb_startcode[4] = {0, 0, 0, 1};
  653. static void packet_put(struct darray *packet, const uint8_t *buf, size_t size)
  654. {
  655. darray_push_back_array(sizeof(uint8_t), packet, buf, size);
  656. }
  657. static void packet_put_startcode(struct darray *packet, int size)
  658. {
  659. assert(size == 3 || size == 4);
  660. packet_put(packet, &annexb_startcode[4 - size], size);
  661. }
  662. static bool handle_prores_packet(struct vt_encoder *enc, CMSampleBufferRef buffer)
  663. {
  664. OSStatus err = 0;
  665. size_t block_size = 0;
  666. uint8_t *block_buf = NULL;
  667. CMBlockBufferRef block = CMSampleBufferGetDataBuffer(buffer);
  668. if (block == NULL) {
  669. VT_BLOG(LOG_ERROR, "Failed to get block buffer for ProRes frame.");
  670. return false;
  671. }
  672. err = CMBlockBufferGetDataPointer(block, 0, NULL, &block_size, (char **)&block_buf);
  673. if (err != 0) {
  674. VT_BLOG(LOG_ERROR, "Failed to get data buffer pointer for ProRes frame.");
  675. return false;
  676. }
  677. packet_put(&enc->packet_data.da, block_buf, block_size);
  678. return true;
  679. }
  680. static void convert_block_nals_to_annexb(struct vt_encoder *enc, struct darray *packet, CMBlockBufferRef block,
  681. int nal_length_bytes)
  682. {
  683. size_t block_size;
  684. uint8_t *block_buf;
  685. CMBlockBufferGetDataPointer(block, 0, NULL, &block_size, (char **)&block_buf);
  686. size_t bytes_remaining = block_size;
  687. while (bytes_remaining > 0) {
  688. uint32_t nal_size;
  689. if (nal_length_bytes == 1)
  690. nal_size = block_buf[0];
  691. else if (nal_length_bytes == 2)
  692. nal_size = CFSwapInt16BigToHost(((uint16_t *)block_buf)[0]);
  693. else if (nal_length_bytes == 4)
  694. nal_size = CFSwapInt32BigToHost(((uint32_t *)block_buf)[0]);
  695. else
  696. return;
  697. bytes_remaining -= nal_length_bytes;
  698. block_buf += nal_length_bytes;
  699. if (bytes_remaining < nal_size) {
  700. VT_BLOG(LOG_ERROR, "invalid nal block");
  701. return;
  702. }
  703. packet_put_startcode(packet, 3);
  704. packet_put(packet, block_buf, nal_size);
  705. bytes_remaining -= nal_size;
  706. block_buf += nal_size;
  707. }
  708. }
  709. static bool handle_keyframe(struct vt_encoder *enc, CMFormatDescriptionRef format_desc, size_t param_count,
  710. struct darray *packet, struct darray *extra_data)
  711. {
  712. OSStatus code;
  713. const uint8_t *param;
  714. size_t param_size;
  715. for (size_t i = 0; i < param_count; i++) {
  716. if (enc->codec_type == kCMVideoCodecType_H264) {
  717. code = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format_desc, i, &param, &param_size,
  718. NULL, NULL);
  719. #ifdef ENABLE_HEVC
  720. } else if (enc->codec_type == kCMVideoCodecType_HEVC) {
  721. code = CMVideoFormatDescriptionGetHEVCParameterSetAtIndex(format_desc, i, &param, &param_size,
  722. NULL, NULL);
  723. #endif
  724. }
  725. if (code != noErr) {
  726. log_osstatus(LOG_ERROR, enc,
  727. "getting NAL parameter "
  728. "at index",
  729. code);
  730. return false;
  731. }
  732. packet_put_startcode(packet, 4);
  733. packet_put(packet, param, param_size);
  734. }
  735. // if we were passed an extra_data array, fill it with
  736. // SPS, PPS, etc.
  737. if (extra_data != NULL)
  738. packet_put(extra_data, packet->array, packet->num);
  739. return true;
  740. }
  741. static bool convert_sample_to_annexb(struct vt_encoder *enc, struct darray *packet, struct darray *extra_data,
  742. CMSampleBufferRef buffer, bool keyframe)
  743. {
  744. OSStatus code;
  745. CMFormatDescriptionRef format_desc = CMSampleBufferGetFormatDescription(buffer);
  746. size_t param_count;
  747. int nal_length_bytes;
  748. if (enc->codec_type == kCMVideoCodecType_H264) {
  749. code = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format_desc, 0, NULL, NULL, &param_count,
  750. &nal_length_bytes);
  751. #ifdef ENABLE_HEVC
  752. } else if (enc->codec_type == kCMVideoCodecType_HEVC) {
  753. code = CMVideoFormatDescriptionGetHEVCParameterSetAtIndex(format_desc, 0, NULL, NULL, &param_count,
  754. &nal_length_bytes);
  755. #endif
  756. } else {
  757. log_osstatus(LOG_ERROR, enc, "invalid codec type", kCMFormatDescriptionError_ValueNotAvailable);
  758. return false;
  759. }
  760. // it is not clear what errors this function can return
  761. // so we check the two most reasonable
  762. if (code == kCMFormatDescriptionBridgeError_InvalidParameter ||
  763. code == kCMFormatDescriptionError_InvalidParameter) {
  764. VT_BLOG(LOG_WARNING, "assuming 2 parameter sets "
  765. "and 4 byte NAL length header");
  766. param_count = 2;
  767. nal_length_bytes = 4;
  768. } else if (code != noErr) {
  769. log_osstatus(LOG_ERROR, enc, "getting parameter count from sample", code);
  770. return false;
  771. }
  772. if (keyframe && !handle_keyframe(enc, format_desc, param_count, packet, extra_data))
  773. return false;
  774. CMBlockBufferRef block = CMSampleBufferGetDataBuffer(buffer);
  775. convert_block_nals_to_annexb(enc, packet, block, nal_length_bytes);
  776. return true;
  777. }
  778. static bool is_sample_keyframe(CMSampleBufferRef buffer)
  779. {
  780. CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(buffer, false);
  781. if (attachments != NULL) {
  782. CFDictionaryRef attachment;
  783. CFBooleanRef has_dependencies;
  784. attachment = (CFDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
  785. has_dependencies =
  786. (CFBooleanRef)CFDictionaryGetValue(attachment, kCMSampleAttachmentKey_DependsOnOthers);
  787. return has_dependencies == kCFBooleanFalse;
  788. }
  789. return false;
  790. }
  791. static bool parse_sample(struct vt_encoder *enc, CMSampleBufferRef buffer, struct encoder_packet *packet, CMTime off)
  792. {
  793. CMTime pts = CMSampleBufferGetPresentationTimeStamp(buffer);
  794. CMTime dts = CMSampleBufferGetDecodeTimeStamp(buffer);
  795. if (CMTIME_IS_INVALID(dts))
  796. dts = pts;
  797. // imitate x264's negative dts when bframes might have pts < dts
  798. else if (enc->bframes)
  799. dts = CMTimeSubtract(dts, off);
  800. pts = CMTimeMultiply(pts, enc->fps_num);
  801. dts = CMTimeMultiply(dts, enc->fps_num);
  802. const bool is_avc = enc->codec_type == kCMVideoCodecType_H264;
  803. const bool has_annexb = is_avc || (enc->codec_type == kCMVideoCodecType_HEVC);
  804. // All ProRes frames are "keyframes"
  805. const bool keyframe = !has_annexb || is_sample_keyframe(buffer);
  806. da_resize(enc->packet_data, 0);
  807. // If we are still looking for extra data
  808. struct darray *extra_data = NULL;
  809. if (enc->extra_data.num == 0)
  810. extra_data = &enc->extra_data.da;
  811. if (has_annexb) {
  812. if (!convert_sample_to_annexb(enc, &enc->packet_data.da, extra_data, buffer, keyframe))
  813. goto fail;
  814. } else {
  815. if (!handle_prores_packet(enc, buffer))
  816. goto fail;
  817. }
  818. packet->type = OBS_ENCODER_VIDEO;
  819. packet->pts = (int64_t)(CMTimeGetSeconds(pts));
  820. packet->dts = (int64_t)(CMTimeGetSeconds(dts));
  821. packet->data = enc->packet_data.array;
  822. packet->size = enc->packet_data.num;
  823. packet->keyframe = keyframe;
  824. if (is_avc) {
  825. // VideoToolbox produces packets with priority lower than the RTMP code
  826. // expects, which causes it to be unable to recover from frame drops.
  827. // Fix this by manually adjusting the priority.
  828. uint8_t *start = enc->packet_data.array;
  829. uint8_t *end = start + enc->packet_data.num;
  830. start = (uint8_t *)obs_avc_find_startcode(start, end);
  831. while (true) {
  832. while (start < end && !*(start++))
  833. ;
  834. if (start == end)
  835. break;
  836. const int type = start[0] & 0x1F;
  837. if (type == OBS_NAL_SLICE_IDR || type == OBS_NAL_SLICE) {
  838. uint8_t prev_type = (start[0] >> 5) & 0x3;
  839. start[0] &= ~(3 << 5);
  840. if (type == OBS_NAL_SLICE_IDR)
  841. start[0] |= OBS_NAL_PRIORITY_HIGHEST << 5;
  842. else if (type == OBS_NAL_SLICE && prev_type != OBS_NAL_PRIORITY_DISPOSABLE)
  843. start[0] |= OBS_NAL_PRIORITY_HIGH << 5;
  844. else
  845. start[0] |= prev_type << 5;
  846. }
  847. start = (uint8_t *)obs_avc_find_startcode(start, end);
  848. }
  849. }
  850. CFRelease(buffer);
  851. return true;
  852. fail:
  853. CFRelease(buffer);
  854. return false;
  855. }
  856. bool get_cached_pixel_buffer(struct vt_encoder *enc, CVPixelBufferRef *buf)
  857. {
  858. OSStatus code;
  859. CVPixelBufferPoolRef pool = VTCompressionSessionGetPixelBufferPool(enc->session);
  860. if (!pool)
  861. return kCVReturnError;
  862. CVPixelBufferRef pixbuf;
  863. code = CVPixelBufferPoolCreatePixelBuffer(NULL, pool, &pixbuf);
  864. if (code != noErr) {
  865. goto fail;
  866. }
  867. // Why aren't these already set on the pixel buffer?
  868. // I would have expected pixel buffers from the session's
  869. // pool to have the correct color space stuff set
  870. const enum video_colorspace cs = enc->colorspace;
  871. CVBufferSetAttachment(pixbuf, kCVImageBufferYCbCrMatrixKey, obs_to_vt_colorspace(cs),
  872. kCVAttachmentMode_ShouldPropagate);
  873. CVBufferSetAttachment(pixbuf, kCVImageBufferColorPrimariesKey, obs_to_vt_primaries(cs),
  874. kCVAttachmentMode_ShouldPropagate);
  875. CVBufferSetAttachment(pixbuf, kCVImageBufferTransferFunctionKey, obs_to_vt_transfer(cs),
  876. kCVAttachmentMode_ShouldPropagate);
  877. const bool pq = cs == VIDEO_CS_2100_PQ;
  878. const bool hlg = cs == VIDEO_CS_2100_HLG;
  879. if (pq || hlg) {
  880. const uint16_t hdr_nominal_peak_level = pq ? (uint16_t)obs_get_video_hdr_nominal_peak_level()
  881. : (hlg ? 1000 : 0);
  882. CFDataRef masteringDisplayColorVolume = obs_to_vt_masteringdisplay(hdr_nominal_peak_level);
  883. CFDataRef contentLightLevel = obs_to_vt_contentlightlevelinfo(hdr_nominal_peak_level);
  884. CVBufferSetAttachment(pixbuf, kCVImageBufferMasteringDisplayColorVolumeKey, masteringDisplayColorVolume,
  885. kCVAttachmentMode_ShouldPropagate);
  886. CVBufferSetAttachment(pixbuf, kCVImageBufferContentLightLevelInfoKey, contentLightLevel,
  887. kCVAttachmentMode_ShouldPropagate);
  888. CFRelease(masteringDisplayColorVolume);
  889. CFRelease(contentLightLevel);
  890. }
  891. *buf = pixbuf;
  892. return true;
  893. fail:
  894. return false;
  895. }
  896. static bool vt_encode(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet)
  897. {
  898. struct vt_encoder *enc = data;
  899. OSStatus code;
  900. CMTime dur = CMTimeMake(enc->fps_den, enc->fps_num);
  901. CMTime off = CMTimeMultiply(dur, 2);
  902. CMTime pts = CMTimeMake(frame->pts, enc->fps_num);
  903. CVPixelBufferRef pixbuf = NULL;
  904. if (!get_cached_pixel_buffer(enc, &pixbuf)) {
  905. VT_BLOG(LOG_ERROR, "Unable to create pixel buffer");
  906. goto fail;
  907. }
  908. code = CVPixelBufferLockBaseAddress(pixbuf, 0);
  909. if (code != noErr) {
  910. goto fail;
  911. }
  912. for (int i = 0; i < MAX_AV_PLANES; i++) {
  913. if (frame->data[i] == NULL)
  914. break;
  915. uint8_t *p = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixbuf, i);
  916. uint8_t *f = frame->data[i];
  917. size_t plane_linesize = CVPixelBufferGetBytesPerRowOfPlane(pixbuf, i);
  918. size_t plane_height = CVPixelBufferGetHeightOfPlane(pixbuf, i);
  919. for (size_t j = 0; j < plane_height; j++) {
  920. memcpy(p, f, frame->linesize[i]);
  921. p += plane_linesize;
  922. f += frame->linesize[i];
  923. }
  924. }
  925. code = CVPixelBufferUnlockBaseAddress(pixbuf, 0);
  926. if (code != noErr) {
  927. goto fail;
  928. }
  929. code = VTCompressionSessionEncodeFrame(enc->session, pixbuf, pts, dur, NULL, pixbuf, NULL);
  930. if (code != noErr) {
  931. goto fail;
  932. }
  933. CMSampleBufferRef buffer = (CMSampleBufferRef)CMSimpleQueueDequeue(enc->queue);
  934. // No samples waiting in the queue
  935. if (buffer == NULL)
  936. return true;
  937. *received_packet = true;
  938. return parse_sample(enc, buffer, packet, off);
  939. fail:
  940. return false;
  941. }
  942. static bool vt_extra_data(void *data, uint8_t **extra_data, size_t *size)
  943. {
  944. struct vt_encoder *enc = (struct vt_encoder *)data;
  945. *extra_data = enc->extra_data.array;
  946. *size = enc->extra_data.num;
  947. return true;
  948. }
  949. static const char *vt_getname(void *data)
  950. {
  951. struct vt_encoder_type_data *type_data = data;
  952. if (strcmp("Apple H.264 (HW)", type_data->disp_name) == 0) {
  953. return obs_module_text("VTH264EncHW");
  954. } else if (strcmp("Apple H.264 (SW)", type_data->disp_name) == 0) {
  955. return obs_module_text("VTH264EncSW");
  956. #ifdef ENABLE_HEVC
  957. } else if (strcmp("Apple HEVC (HW)", type_data->disp_name) == 0) {
  958. return obs_module_text("VTHEVCEncHW");
  959. } else if (strcmp("Apple HEVC (AVE)", type_data->disp_name) == 0) {
  960. return obs_module_text("VTHEVCEncT2");
  961. } else if (strcmp("Apple HEVC (SW)", type_data->disp_name) == 0) {
  962. return obs_module_text("VTHEVCEncSW");
  963. #endif
  964. } else if (strncmp("AppleProResHW", type_data->disp_name, 13) == 0) {
  965. return obs_module_text("VTProResEncHW");
  966. } else if (strncmp("Apple ProRes", type_data->disp_name, 12) == 0) {
  967. return obs_module_text("VTProResEncSW");
  968. }
  969. return type_data->disp_name;
  970. }
  971. static bool rate_control_limit_bitrate_modified(obs_properties_t *ppts, obs_property_t *p, obs_data_t *settings)
  972. {
  973. bool has_bitrate = true;
  974. bool can_limit_bitrate = true;
  975. bool use_limit_bitrate = obs_data_get_bool(settings, "limit_bitrate");
  976. const char *rate_control = obs_data_get_string(settings, "rate_control");
  977. if (strcmp(rate_control, "CBR") == 0) {
  978. can_limit_bitrate = false;
  979. has_bitrate = true;
  980. } else if (strcmp(rate_control, "CRF") == 0) {
  981. can_limit_bitrate = true;
  982. has_bitrate = false;
  983. } else if (strcmp(rate_control, "ABR") == 0) {
  984. can_limit_bitrate = true;
  985. has_bitrate = true;
  986. }
  987. p = obs_properties_get(ppts, "limit_bitrate");
  988. obs_property_set_visible(p, can_limit_bitrate);
  989. p = obs_properties_get(ppts, "max_bitrate");
  990. obs_property_set_visible(p, can_limit_bitrate && use_limit_bitrate);
  991. p = obs_properties_get(ppts, "max_bitrate_window");
  992. obs_property_set_visible(p, can_limit_bitrate && use_limit_bitrate);
  993. p = obs_properties_get(ppts, "bitrate");
  994. obs_property_set_visible(p, has_bitrate);
  995. p = obs_properties_get(ppts, "quality");
  996. obs_property_set_visible(p, !has_bitrate);
  997. return true;
  998. }
  999. static obs_properties_t *vt_properties_h26x(void *data __unused, void *type_data)
  1000. {
  1001. struct vt_encoder_type_data *encoder_type_data = type_data;
  1002. obs_properties_t *props = obs_properties_create();
  1003. obs_property_t *p;
  1004. p = obs_properties_add_list(props, "rate_control", obs_module_text("RateControl"), OBS_COMBO_TYPE_LIST,
  1005. OBS_COMBO_FORMAT_STRING);
  1006. if (__builtin_available(macOS 13.0, *)) {
  1007. if (encoder_type_data->hardware_accelerated && is_apple_silicon) {
  1008. obs_property_list_add_string(p, "CBR", "CBR");
  1009. }
  1010. }
  1011. obs_property_list_add_string(p, "ABR", "ABR");
  1012. if (encoder_type_data->hardware_accelerated && is_apple_silicon) {
  1013. obs_property_list_add_string(p, "CRF", "CRF");
  1014. }
  1015. obs_property_set_modified_callback(p, rate_control_limit_bitrate_modified);
  1016. p = obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 50, 10000000, 50);
  1017. obs_property_int_set_suffix(p, " Kbps");
  1018. obs_properties_add_int_slider(props, "quality", obs_module_text("Quality"), 0, 100, 1);
  1019. p = obs_properties_add_bool(props, "limit_bitrate", obs_module_text("UseMaxBitrate"));
  1020. obs_property_set_modified_callback(p, rate_control_limit_bitrate_modified);
  1021. p = obs_properties_add_int(props, "max_bitrate", obs_module_text("MaxBitrate"), 50, 10000000, 50);
  1022. obs_property_int_set_suffix(p, " Kbps");
  1023. p = obs_properties_add_float(props, "max_bitrate_window", obs_module_text("MaxBitrateWindow"), 0.10f, 10.0f,
  1024. 0.25f);
  1025. obs_property_float_set_suffix(p, " s");
  1026. p = obs_properties_add_int(props, "keyint_sec", obs_module_text("KeyframeIntervalSec"), 0, 20, 1);
  1027. obs_property_int_set_suffix(p, " s");
  1028. p = obs_properties_add_list(props, "profile", obs_module_text("Profile"), OBS_COMBO_TYPE_LIST,
  1029. OBS_COMBO_FORMAT_STRING);
  1030. if (encoder_type_data->codec_type == kCMVideoCodecType_H264) {
  1031. obs_property_list_add_string(p, "baseline", "baseline");
  1032. obs_property_list_add_string(p, "main", "main");
  1033. obs_property_list_add_string(p, "high", "high");
  1034. #ifdef ENABLE_HEVC
  1035. } else if (encoder_type_data->codec_type == kCMVideoCodecType_HEVC) {
  1036. obs_property_list_add_string(p, "main", "main");
  1037. obs_property_list_add_string(p, "main10", "main10");
  1038. if (__builtin_available(macOS 12.3, *)) {
  1039. obs_property_list_add_string(p, "main 4:2:2 10", "main42210");
  1040. }
  1041. #endif
  1042. }
  1043. obs_properties_add_bool(props, "bframes", obs_module_text("UseBFrames"));
  1044. return props;
  1045. }
  1046. static obs_properties_t *vt_properties_prores(void *data __unused, void *type_data)
  1047. {
  1048. struct vt_encoder_type_data *encoder_type_data = type_data;
  1049. obs_properties_t *props = obs_properties_create();
  1050. obs_property_t *p;
  1051. p = obs_properties_add_list(props, "codec_type", obs_module_text("ProResCodec"), OBS_COMBO_TYPE_LIST,
  1052. OBS_COMBO_FORMAT_INT);
  1053. uint32_t codec_availability_flags = 0;
  1054. size_t size = 0;
  1055. struct vt_prores_encoder_data *encoder_list = NULL;
  1056. if (encoder_type_data->hardware_accelerated) {
  1057. size = vt_prores_hardware_encoder_list.num;
  1058. encoder_list = vt_prores_hardware_encoder_list.array;
  1059. } else {
  1060. size = vt_prores_software_encoder_list.num;
  1061. encoder_list = vt_prores_software_encoder_list.array;
  1062. }
  1063. for (size_t i = 0; i < size; ++i) {
  1064. switch (encoder_list[i].codec_type) {
  1065. case kCMVideoCodecType_AppleProRes4444XQ:
  1066. codec_availability_flags |= (1 << 0);
  1067. break;
  1068. case kCMVideoCodecType_AppleProRes4444:
  1069. codec_availability_flags |= (1 << 1);
  1070. break;
  1071. case kCMVideoCodecType_AppleProRes422Proxy:
  1072. codec_availability_flags |= (1 << 2);
  1073. break;
  1074. case kCMVideoCodecType_AppleProRes422LT:
  1075. codec_availability_flags |= (1 << 3);
  1076. break;
  1077. case kCMVideoCodecType_AppleProRes422:
  1078. codec_availability_flags |= (1 << 4);
  1079. break;
  1080. case kCMVideoCodecType_AppleProRes422HQ:
  1081. codec_availability_flags |= (1 << 5);
  1082. break;
  1083. }
  1084. }
  1085. if (codec_availability_flags & (1 << 0))
  1086. obs_property_list_add_int(p, obs_module_text("ProRes4444XQ"), kCMVideoCodecType_AppleProRes4444XQ);
  1087. if (codec_availability_flags & (1 << 1))
  1088. obs_property_list_add_int(p, obs_module_text("ProRes4444"), kCMVideoCodecType_AppleProRes4444);
  1089. if (codec_availability_flags & (1 << 2))
  1090. obs_property_list_add_int(p, obs_module_text("ProRes422Proxy"), kCMVideoCodecType_AppleProRes422Proxy);
  1091. if (codec_availability_flags & (1 << 3))
  1092. obs_property_list_add_int(p, obs_module_text("ProRes422LT"), kCMVideoCodecType_AppleProRes422LT);
  1093. if (codec_availability_flags & (1 << 4))
  1094. obs_property_list_add_int(p, obs_module_text("ProRes422"), kCMVideoCodecType_AppleProRes422);
  1095. if (codec_availability_flags & (1 << 5))
  1096. obs_property_list_add_int(p, obs_module_text("ProRes422HQ"), kCMVideoCodecType_AppleProRes422HQ);
  1097. return props;
  1098. }
  1099. static void vt_defaults(obs_data_t *settings, void *data)
  1100. {
  1101. struct vt_encoder_type_data *type_data = data;
  1102. obs_data_set_default_string(settings, "rate_control", "ABR");
  1103. if (__builtin_available(macOS 13.0, *)) {
  1104. if (type_data->hardware_accelerated && is_apple_silicon) {
  1105. obs_data_set_default_string(settings, "rate_control", "CBR");
  1106. }
  1107. }
  1108. obs_data_set_default_int(settings, "bitrate", 2500);
  1109. obs_data_set_default_int(settings, "quality", 60);
  1110. obs_data_set_default_bool(settings, "limit_bitrate", false);
  1111. obs_data_set_default_int(settings, "max_bitrate", 2500);
  1112. obs_data_set_default_double(settings, "max_bitrate_window", 1.5f);
  1113. obs_data_set_default_int(settings, "keyint_sec", 2);
  1114. obs_data_set_default_string(settings, "profile",
  1115. type_data->codec_type == kCMVideoCodecType_H264 ? "high" : "main");
  1116. obs_data_set_default_int(settings, "codec_type", kCMVideoCodecType_AppleProRes422);
  1117. obs_data_set_default_bool(settings, "bframes", true);
  1118. }
  1119. static void vt_free_type_data(void *data)
  1120. {
  1121. struct vt_encoder_type_data *type_data = data;
  1122. bfree((char *)type_data->disp_name);
  1123. bfree((char *)type_data->id);
  1124. bfree(type_data);
  1125. }
  1126. static inline void vt_add_prores_encoder_data_to_list(CFDictionaryRef encoder_dict, FourCharCode codec_type)
  1127. {
  1128. struct vt_prores_encoder_data *encoder_data = NULL;
  1129. CFBooleanRef hardware_accelerated =
  1130. CFDictionaryGetValue(encoder_dict, kVTVideoEncoderList_IsHardwareAccelerated);
  1131. if (hardware_accelerated == kCFBooleanTrue)
  1132. encoder_data = da_push_back_new(vt_prores_hardware_encoder_list);
  1133. else
  1134. encoder_data = da_push_back_new(vt_prores_software_encoder_list);
  1135. encoder_data->encoder_id = CFDictionaryGetValue(encoder_dict, kVTVideoEncoderList_EncoderID);
  1136. encoder_data->codec_type = codec_type;
  1137. }
  1138. OBS_DECLARE_MODULE()
  1139. OBS_MODULE_USE_DEFAULT_LOCALE("mac-videotoolbox", "en-US")
  1140. dispatch_group_t encoder_list_dispatch_group;
  1141. CFArrayRef encoder_list;
  1142. bool obs_module_load(void)
  1143. {
  1144. dispatch_queue_t queue = dispatch_queue_create("Encoder list load queue", NULL);
  1145. encoder_list_dispatch_group = dispatch_group_create();
  1146. dispatch_group_async(encoder_list_dispatch_group, queue, ^{
  1147. VTCopyVideoEncoderList(NULL, &encoder_list);
  1148. });
  1149. // The group dispatch keeps a reference until it's finished
  1150. dispatch_release(queue);
  1151. #ifndef __aarch64__
  1152. is_apple_silicon = os_get_emulation_status();
  1153. #endif
  1154. return true;
  1155. }
  1156. void obs_module_post_load(void)
  1157. {
  1158. struct obs_encoder_info info = {
  1159. .type = OBS_ENCODER_VIDEO,
  1160. .get_name = vt_getname,
  1161. .create = vt_create,
  1162. .destroy = vt_destroy,
  1163. .encode = vt_encode,
  1164. .update = vt_update,
  1165. .get_defaults2 = vt_defaults,
  1166. .get_extra_data = vt_extra_data,
  1167. .free_type_data = vt_free_type_data,
  1168. .caps = OBS_ENCODER_CAP_DYN_BITRATE,
  1169. };
  1170. da_init(vt_prores_hardware_encoder_list);
  1171. da_init(vt_prores_software_encoder_list);
  1172. dispatch_group_wait(encoder_list_dispatch_group, DISPATCH_TIME_FOREVER);
  1173. dispatch_release(encoder_list_dispatch_group);
  1174. CFIndex size = CFArrayGetCount(encoder_list);
  1175. for (CFIndex i = 0; i < size; i++) {
  1176. CFDictionaryRef encoder_dict = CFArrayGetValueAtIndex(encoder_list, i);
  1177. CMVideoCodecType codec_type = 0;
  1178. {
  1179. CFNumberRef codec_type_num = CFDictionaryGetValue(encoder_dict, kVTVideoEncoderList_CodecType);
  1180. CFNumberGetValue(codec_type_num, kCFNumberSInt32Type, &codec_type);
  1181. }
  1182. switch (codec_type) {
  1183. case kCMVideoCodecType_H264:
  1184. info.get_properties2 = vt_properties_h26x;
  1185. info.codec = "h264";
  1186. break;
  1187. #ifdef ENABLE_HEVC
  1188. case kCMVideoCodecType_HEVC:
  1189. info.get_properties2 = vt_properties_h26x;
  1190. info.codec = "hevc";
  1191. break;
  1192. #endif
  1193. // 422 is used as a marker for all ProRes types,
  1194. // since the type is stored as a profile
  1195. case kCMVideoCodecType_AppleProRes422:
  1196. info.get_properties2 = vt_properties_prores;
  1197. info.codec = "prores";
  1198. vt_add_prores_encoder_data_to_list(encoder_dict, codec_type);
  1199. break;
  1200. case kCMVideoCodecType_AppleProRes4444XQ:
  1201. case kCMVideoCodecType_AppleProRes4444:
  1202. case kCMVideoCodecType_AppleProRes422Proxy:
  1203. case kCMVideoCodecType_AppleProRes422LT:
  1204. case kCMVideoCodecType_AppleProRes422HQ:
  1205. vt_add_prores_encoder_data_to_list(encoder_dict, codec_type);
  1206. continue;
  1207. default:
  1208. continue;
  1209. }
  1210. CFStringRef EncoderID = CFDictionaryGetValue(encoder_dict, kVTVideoEncoderList_EncoderID);
  1211. CFIndex id_len = CFStringGetMaximumSizeOfFileSystemRepresentation(EncoderID);
  1212. char *id = bzalloc(id_len + 1);
  1213. CFStringGetFileSystemRepresentation(EncoderID, id, id_len);
  1214. CFStringRef DisplayName = CFDictionaryGetValue(encoder_dict, kVTVideoEncoderList_DisplayName);
  1215. CFIndex disp_name_len = CFStringGetMaximumSizeOfFileSystemRepresentation(DisplayName);
  1216. char *disp_name = bzalloc(disp_name_len + 1);
  1217. CFStringGetFileSystemRepresentation(DisplayName, disp_name, disp_name_len);
  1218. CFBooleanRef hardware_ref =
  1219. CFDictionaryGetValue(encoder_dict, kVTVideoEncoderList_IsHardwareAccelerated);
  1220. bool hardware_accelerated = (hardware_ref) ? CFBooleanGetValue(hardware_ref) : false;
  1221. info.id = id;
  1222. struct vt_encoder_type_data *type_data = bzalloc(sizeof(struct vt_encoder_type_data));
  1223. type_data->disp_name = disp_name;
  1224. type_data->id = id;
  1225. type_data->codec_type = codec_type;
  1226. type_data->hardware_accelerated = hardware_accelerated;
  1227. info.type_data = type_data;
  1228. obs_register_encoder(&info);
  1229. }
  1230. CFRelease(encoder_list);
  1231. VT_LOG(LOG_INFO, "Added VideoToolbox encoders");
  1232. }
  1233. void obs_module_unload(void)
  1234. {
  1235. da_free(vt_prores_hardware_encoder_list);
  1236. da_free(vt_prores_software_encoder_list);
  1237. }