1 #include <obs-module.h>
2 #include <util/profiler.hpp>
3
4 #include <memory>
5 #include <chrono>
6
7 #include "mf-h264-encoder.hpp"
8 #include "mf-encoder-descriptor.hpp"
9 #include <VersionHelpers.h>
10
11 using namespace MF;
12
13 struct MFH264_Encoder {
14 obs_encoder_t *encoder;
15 std::shared_ptr<EncoderDescriptor> descriptor;
16 std::unique_ptr<H264Encoder> h264Encoder;
17 uint32_t width;
18 uint32_t height;
19 uint32_t framerateNum;
20 uint32_t framerateDen;
21 uint32_t keyint;
22 bool advanced;
23 uint32_t bitrate;
24 uint32_t maxBitrate;
25 bool useMaxBitrate;
26 uint32_t bufferSize;
27 bool useBufferSize;
28 H264Profile profile;
29 H264RateControl rateControl;
30 H264QP qp;
31 uint32_t minQp;
32 uint32_t maxQp;
33 bool lowLatency;
34 uint32_t bFrames;
35
36 const char *profiler_encode = nullptr;
37 };
38
39 #define MFTEXT(x) obs_module_text("MF.H264." x)
40 #define TEXT_ADVANCED MFTEXT("Advanced")
41 #define TEXT_LOW_LAT MFTEXT("LowLatency")
42 #define TEXT_B_FRAMES MFTEXT("BFrames")
43 #define TEXT_BITRATE MFTEXT("Bitrate")
44 #define TEXT_CUSTOM_BUF MFTEXT("CustomBufsize")
45 #define TEXT_BUF_SIZE MFTEXT("BufferSize")
46 #define TEXT_USE_MAX_BITRATE MFTEXT("CustomMaxBitrate")
47 #define TEXT_MAX_BITRATE MFTEXT("MaxBitrate")
48 #define TEXT_KEYINT_SEC MFTEXT("KeyframeIntervalSec")
49 #define TEXT_RATE_CONTROL MFTEXT("RateControl")
50 #define TEXT_MIN_QP MFTEXT("MinQP")
51 #define TEXT_MAX_QP MFTEXT("MaxQP")
52 #define TEXT_QPI MFTEXT("QPI")
53 #define TEXT_QPP MFTEXT("QPP")
54 #define TEXT_QPB MFTEXT("QPB")
55 #define TEXT_PROFILE MFTEXT("Profile")
56 #define TEXT_CBR MFTEXT("CBR")
57 #define TEXT_VBR MFTEXT("VBR")
58 #define TEXT_CQP MFTEXT("CQP")
59
60 #define MFP(x) "mf_h264_" ## x
61 #define MFP_USE_ADVANCED MFP("use_advanced")
62 #define MFP_USE_LOWLAT MFP("use_low_latency")
63 #define MFP_B_FRAMES MFP("b_frames")
64 #define MFP_BITRATE MFP("bitrate")
65 #define MFP_USE_BUF_SIZE MFP("use_buf_size")
66 #define MFP_BUF_SIZE MFP("buf_size")
67 #define MFP_USE_MAX_BITRATE MFP("use_max_bitrate")
68 #define MFP_MAX_BITRATE MFP("max_bitrate")
69 #define MFP_KEY_INT MFP("key_int")
70 #define MFP_RATE_CONTROL MFP("rate_control")
71 #define MFP_MIN_QP MFP("min_qp")
72 #define MFP_MAX_QP MFP("max_qp")
73 #define MFP_QP_I MFP("qp_i")
74 #define MFP_QP_P MFP("qp_p")
75 #define MFP_QP_B MFP("qp_b")
76 #define MFP_PROFILE MFP("profile")
77
78 struct TypeData {
79 std::shared_ptr<EncoderDescriptor> descriptor;
80
TypeDataTypeData81 inline TypeData(std::shared_ptr<EncoderDescriptor> descriptor_)
82 : descriptor(descriptor_)
83 {}
84 };
85
MFH264_GetName(void * type_data)86 static const char *MFH264_GetName(void *type_data)
87 {
88 TypeData &typeData = *reinterpret_cast<TypeData*>(type_data);
89 return obs_module_text(typeData.descriptor->Name());
90 }
91
set_visible(obs_properties_t * ppts,const char * name,bool visible)92 static void set_visible(obs_properties_t *ppts, const char *name, bool visible)
93 {
94 obs_property_t *p = obs_properties_get(ppts, name);
95 obs_property_set_visible(p, visible);
96 }
97
use_bufsize_modified(obs_properties_t * ppts,obs_property_t * p,obs_data_t * settings)98 static bool use_bufsize_modified(obs_properties_t *ppts, obs_property_t *p,
99 obs_data_t *settings)
100 {
101 UNUSED_PARAMETER(p);
102
103 bool use_bufsize = obs_data_get_bool(settings, MFP_USE_BUF_SIZE);
104
105 set_visible(ppts, MFP_BUF_SIZE, use_bufsize);
106
107 return true;
108 }
109
use_max_bitrate_modified(obs_properties_t * ppts,obs_property_t * p,obs_data_t * settings)110 static bool use_max_bitrate_modified(obs_properties_t *ppts, obs_property_t *p,
111 obs_data_t *settings)
112 {
113 UNUSED_PARAMETER(p);
114
115 bool advanced = obs_data_get_bool(settings, MFP_USE_ADVANCED);
116 bool use_max_bitrate = obs_data_get_bool(settings, MFP_USE_MAX_BITRATE);
117
118 set_visible(ppts, MFP_MAX_BITRATE, advanced && use_max_bitrate);
119
120 return true;
121 }
122
use_advanced_modified(obs_properties_t * ppts,obs_property_t * p,obs_data_t * settings)123 static bool use_advanced_modified(obs_properties_t *ppts, obs_property_t *p,
124 obs_data_t *settings)
125 {
126 UNUSED_PARAMETER(p);
127
128 bool advanced = obs_data_get_bool(settings, MFP_USE_ADVANCED);
129
130 set_visible(ppts, MFP_MIN_QP, advanced);
131 set_visible(ppts, MFP_MAX_QP, advanced);
132 set_visible(ppts, MFP_USE_LOWLAT, advanced);
133 set_visible(ppts, MFP_B_FRAMES, advanced);
134
135 H264RateControl rateControl = (H264RateControl)obs_data_get_int(
136 settings, MFP_RATE_CONTROL);
137
138 if (rateControl == H264RateControlCBR ||
139 rateControl == H264RateControlVBR) {
140 set_visible(ppts, MFP_USE_MAX_BITRATE, advanced);
141 use_max_bitrate_modified(ppts, NULL, settings);
142 }
143
144 return true;
145 }
146
rate_control_modified(obs_properties_t * ppts,obs_property_t * p,obs_data_t * settings)147 static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p,
148 obs_data_t *settings)
149 {
150 UNUSED_PARAMETER(p);
151
152 H264RateControl rateControl = (H264RateControl)obs_data_get_int(
153 settings, MFP_RATE_CONTROL);
154
155 bool advanced = obs_data_get_bool(settings, MFP_USE_ADVANCED);
156
157 set_visible(ppts, MFP_BITRATE, false);
158 set_visible(ppts, MFP_USE_BUF_SIZE, false);
159 set_visible(ppts, MFP_BUF_SIZE, false);
160 set_visible(ppts, MFP_USE_MAX_BITRATE, false);
161 set_visible(ppts, MFP_MAX_BITRATE, false);
162 set_visible(ppts, MFP_QP_I, false);
163 set_visible(ppts, MFP_QP_P, false);
164 set_visible(ppts, MFP_QP_B, false);
165
166 switch (rateControl) {
167 case H264RateControlCBR:
168 use_bufsize_modified(ppts, NULL, settings);
169 use_max_bitrate_modified(ppts, NULL, settings);
170
171 set_visible(ppts, MFP_BITRATE, true);
172 set_visible(ppts, MFP_USE_BUF_SIZE, true);
173 set_visible(ppts, MFP_USE_MAX_BITRATE, advanced);
174
175 break;
176 case H264RateControlVBR:
177 use_bufsize_modified(ppts, NULL, settings);
178 use_max_bitrate_modified(ppts, NULL, settings);
179
180 set_visible(ppts, MFP_BITRATE, true);
181 set_visible(ppts, MFP_USE_BUF_SIZE, true);
182 set_visible(ppts, MFP_USE_MAX_BITRATE, advanced);
183
184 break;
185 case H264RateControlCQP:
186 set_visible(ppts, MFP_QP_I, true);
187 set_visible(ppts, MFP_QP_P, true);
188 set_visible(ppts, MFP_QP_B, true);
189
190 break;
191 default: break;
192 }
193
194 return true;
195 }
196
MFH264_GetProperties(void *)197 static obs_properties_t *MFH264_GetProperties(void *)
198 {
199 obs_properties_t *props = obs_properties_create();
200 obs_property_t *p;
201
202 obs_property_t *list = obs_properties_add_list(props, MFP_PROFILE,
203 TEXT_PROFILE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
204
205 obs_property_list_add_int(list, "baseline", H264ProfileBaseline);
206 obs_property_list_add_int(list, "main", H264ProfileMain);
207 obs_property_list_add_int(list, "high", H264ProfileHigh);
208
209 obs_properties_add_int(props, MFP_KEY_INT, TEXT_KEYINT_SEC, 0, 20, 1);
210
211 list = obs_properties_add_list(props, MFP_RATE_CONTROL,
212 TEXT_RATE_CONTROL, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
213
214 obs_property_list_add_int(list, TEXT_CBR, H264RateControlCBR);
215 obs_property_list_add_int(list, TEXT_VBR, H264RateControlVBR);
216 obs_property_list_add_int(list, TEXT_CQP, H264RateControlCQP);
217
218 obs_property_set_modified_callback(list, rate_control_modified);
219
220 obs_properties_add_int(props, MFP_BITRATE, TEXT_BITRATE, 50, 10000000,
221 1);
222
223 p = obs_properties_add_bool(props, MFP_USE_BUF_SIZE, TEXT_CUSTOM_BUF);
224 obs_property_set_modified_callback(p, use_bufsize_modified);
225 obs_properties_add_int(props, MFP_BUF_SIZE, TEXT_BUF_SIZE, 0,
226 10000000, 1);
227
228 obs_properties_add_int(props, MFP_QP_I, TEXT_QPI, 0, 51, 1);
229 obs_properties_add_int(props, MFP_QP_P, TEXT_QPP, 0, 51, 1);
230 obs_properties_add_int(props, MFP_QP_B, TEXT_QPB, 0, 51, 1);
231
232 p = obs_properties_add_bool(props, MFP_USE_ADVANCED, TEXT_ADVANCED);
233 obs_property_set_modified_callback(p, use_advanced_modified);
234
235 p = obs_properties_add_bool(props, MFP_USE_MAX_BITRATE,
236 TEXT_USE_MAX_BITRATE);
237 obs_property_set_modified_callback(p, use_max_bitrate_modified);
238 obs_properties_add_int(props, MFP_MAX_BITRATE, TEXT_MAX_BITRATE, 50,
239 10000000, 1);
240
241 obs_properties_add_bool(props, MFP_USE_LOWLAT, TEXT_LOW_LAT);
242 obs_properties_add_int(props, MFP_B_FRAMES, TEXT_B_FRAMES, 0, 16, 1);
243 obs_properties_add_int(props, MFP_MIN_QP, TEXT_MIN_QP, 1, 51, 1);
244 obs_properties_add_int(props, MFP_MAX_QP, TEXT_MAX_QP, 1, 51, 1);
245 return props;
246 }
247
MFH264_GetDefaults(obs_data_t * settings)248 static void MFH264_GetDefaults(obs_data_t *settings)
249 {
250 #define PROP_DEF(x, y, z) obs_data_set_default_ ## x(settings, y, z)
251 PROP_DEF(int, MFP_BITRATE, 2500);
252 PROP_DEF(bool, MFP_USE_LOWLAT, true);
253 PROP_DEF(int, MFP_B_FRAMES, 2);
254 PROP_DEF(bool, MFP_USE_BUF_SIZE, false);
255 PROP_DEF(int, MFP_BUF_SIZE, 2500);
256 PROP_DEF(bool, MFP_USE_MAX_BITRATE, false);
257 PROP_DEF(int, MFP_MAX_BITRATE, 2500);
258 PROP_DEF(int, MFP_KEY_INT, 2);
259 PROP_DEF(int, MFP_RATE_CONTROL, H264RateControlCBR);
260 PROP_DEF(int, MFP_PROFILE, H264ProfileMain);
261 PROP_DEF(int, MFP_MIN_QP, 1);
262 PROP_DEF(int, MFP_MAX_QP, 51);
263 PROP_DEF(int, MFP_QP_I, 26);
264 PROP_DEF(int, MFP_QP_B, 26);
265 PROP_DEF(int, MFP_QP_P, 26);
266 PROP_DEF(bool, MFP_USE_ADVANCED, false);
267 #undef DEF
268 }
269
UpdateParams(MFH264_Encoder * enc,obs_data_t * settings)270 static void UpdateParams(MFH264_Encoder *enc, obs_data_t *settings)
271 {
272 video_t *video = obs_encoder_video(enc->encoder);
273 const struct video_output_info *voi = video_output_get_info(video);
274 TypeData &typeData = *reinterpret_cast<TypeData*>(
275 obs_encoder_get_type_data(enc->encoder));
276
277 enc->width = (uint32_t)obs_encoder_get_width(enc->encoder);
278 enc->height = (uint32_t)obs_encoder_get_height(enc->encoder);
279 enc->framerateNum = voi->fps_num;
280 enc->framerateDen = voi->fps_den;
281
282 enc->descriptor = typeData.descriptor;
283
284 #define PROP_GET(x, y, z) (z)obs_data_get_ ## x(settings, y)
285 enc->profile = PROP_GET(int, MFP_PROFILE, H264Profile);
286 enc->rateControl = PROP_GET(int, MFP_RATE_CONTROL, H264RateControl);
287 enc->keyint = PROP_GET(int, MFP_KEY_INT, uint32_t);
288 enc->bitrate = PROP_GET(int, MFP_BITRATE, uint32_t);
289 enc->useBufferSize = PROP_GET(bool, MFP_USE_BUF_SIZE, bool);
290 enc->bufferSize = PROP_GET(int, MFP_BUF_SIZE, uint32_t);
291 enc->useMaxBitrate = PROP_GET(bool, MFP_USE_MAX_BITRATE, uint32_t);
292 enc->maxBitrate = PROP_GET(int, MFP_MAX_BITRATE, uint32_t);
293 enc->minQp = PROP_GET(int, MFP_MIN_QP, uint16_t);
294 enc->maxQp = PROP_GET(int, MFP_MAX_QP, uint16_t);
295 enc->qp.defaultQp = PROP_GET(int, MFP_QP_I, uint16_t);
296 enc->qp.i = PROP_GET(int, MFP_QP_I, uint16_t);
297 enc->qp.p = PROP_GET(int, MFP_QP_P, uint16_t);
298 enc->qp.b = PROP_GET(int, MFP_QP_B, uint16_t);
299 enc->lowLatency = PROP_GET(bool, MFP_USE_LOWLAT, bool);
300 enc->bFrames = PROP_GET(int, MFP_B_FRAMES, uint32_t);
301 enc->advanced = PROP_GET(bool, MFP_USE_ADVANCED, bool);
302 #undef PROP_GET
303 }
304
305 #undef MFTEXT
306 #undef MFP
307
ApplyCBR(MFH264_Encoder * enc)308 static bool ApplyCBR(MFH264_Encoder *enc)
309 {
310 enc->h264Encoder->SetBitrate(enc->bitrate);
311
312 if (enc->useMaxBitrate)
313 enc->h264Encoder->SetMaxBitrate(enc->maxBitrate);
314 else
315 enc->h264Encoder->SetMaxBitrate(enc->bitrate);
316
317 if (enc->useBufferSize)
318 enc->h264Encoder->SetBufferSize(enc->bufferSize);
319
320 return true;
321 }
322
ApplyCVBR(MFH264_Encoder * enc)323 static bool ApplyCVBR(MFH264_Encoder *enc)
324 {
325 enc->h264Encoder->SetBitrate(enc->bitrate);
326
327 if (enc->advanced && enc->useMaxBitrate)
328 enc->h264Encoder->SetMaxBitrate(enc->maxBitrate);
329 else
330 enc->h264Encoder->SetMaxBitrate(enc->bitrate);
331
332 if (enc->useBufferSize)
333 enc->h264Encoder->SetBufferSize(enc->bufferSize);
334
335 return true;
336 }
337
ApplyVBR(MFH264_Encoder * enc)338 static bool ApplyVBR(MFH264_Encoder *enc)
339 {
340 enc->h264Encoder->SetBitrate(enc->bitrate);
341
342 if (enc->useBufferSize)
343 enc->h264Encoder->SetBufferSize(enc->bufferSize);
344
345 return true;
346 }
347
ApplyCQP(MFH264_Encoder * enc)348 static bool ApplyCQP(MFH264_Encoder *enc)
349 {
350 enc->h264Encoder->SetQP(enc->qp);
351
352 return true;
353 }
354
MFH264_Create(obs_data_t * settings,obs_encoder_t * encoder)355 static void *MFH264_Create(obs_data_t *settings, obs_encoder_t *encoder)
356 {
357 ProfileScope("MFH264_Create");
358
359 std::unique_ptr<MFH264_Encoder> enc(new MFH264_Encoder());
360 enc->encoder = encoder;
361
362 UpdateParams(enc.get(), settings);
363
364 ProfileScope(enc->descriptor->Name());
365
366 enc->h264Encoder.reset(new H264Encoder(encoder,
367 enc->descriptor,
368 enc->width,
369 enc->height,
370 enc->framerateNum,
371 enc->framerateDen,
372 enc->profile,
373 enc->bitrate));
374
375 auto applySettings = [&]() {
376 enc.get()->h264Encoder->SetRateControl(enc->rateControl);
377 enc.get()->h264Encoder->SetKeyframeInterval(enc->keyint);
378
379 enc.get()->h264Encoder->SetEntropyEncoding(
380 H264EntropyEncodingCABAC);
381
382 if (enc->advanced) {
383 enc.get()->h264Encoder->SetLowLatency(enc->lowLatency);
384 enc.get()->h264Encoder->SetBFrameCount(enc->bFrames);
385
386 enc.get()->h264Encoder->SetMinQP(enc->minQp);
387 enc.get()->h264Encoder->SetMaxQP(enc->maxQp);
388 }
389
390 if (enc->rateControl == H264RateControlVBR &&
391 enc->advanced &&
392 enc->useMaxBitrate)
393 enc->rateControl = H264RateControlConstrainedVBR;
394
395
396 switch (enc->rateControl) {
397 case H264RateControlCBR:
398 return ApplyCBR(enc.get());
399 case H264RateControlConstrainedVBR:
400 return ApplyCVBR(enc.get());
401 case H264RateControlVBR:
402 return ApplyVBR(enc.get());
403 case H264RateControlCQP:
404 return ApplyCQP(enc.get());
405 default: return false;
406 }
407 };
408
409 if (!enc->h264Encoder->Initialize(applySettings))
410 return nullptr;
411
412 return enc.release();
413 }
414
MFH264_Destroy(void * data)415 static void MFH264_Destroy(void *data)
416 {
417 MFH264_Encoder *enc = static_cast<MFH264_Encoder *>(data);
418 delete enc;
419 }
420
MFH264_Encode(void * data,struct encoder_frame * frame,struct encoder_packet * packet,bool * received_packet)421 static bool MFH264_Encode(void *data, struct encoder_frame *frame,
422 struct encoder_packet *packet, bool *received_packet)
423 {
424 MFH264_Encoder *enc = static_cast<MFH264_Encoder *>(data);
425 Status status;
426
427 if (!enc->profiler_encode)
428 enc->profiler_encode = profile_store_name(
429 obs_get_profiler_name_store(),
430 "MFH264_Encode(%s)", enc->descriptor->Name());
431
432 ProfileScope(enc->profiler_encode);
433
434 *received_packet = false;
435
436 if (!enc->h264Encoder->ProcessInput(frame->data, frame->linesize,
437 frame->pts, &status))
438 return false;
439
440 UINT8 *outputData;
441 UINT32 outputDataLength;
442 UINT64 outputPts;
443 UINT64 outputDts;
444 bool keyframe;
445
446 if (!enc->h264Encoder->ProcessOutput(&outputData, &outputDataLength,
447 &outputPts, &outputDts, &keyframe, &status))
448 return false;
449
450 // Needs more input, not a failure case
451 if (status == NEED_MORE_INPUT)
452 return true;
453
454 packet->type = OBS_ENCODER_VIDEO;
455 packet->pts = outputPts;
456 packet->dts = outputPts;
457 packet->data = outputData;
458 packet->size = outputDataLength;
459 packet->keyframe = keyframe;
460
461 *received_packet = true;
462 return true;
463 }
464
MFH264_GetExtraData(void * data,uint8_t ** extra_data,size_t * size)465 static bool MFH264_GetExtraData(void *data, uint8_t **extra_data, size_t *size)
466 {
467 MFH264_Encoder *enc = static_cast<MFH264_Encoder *>(data);
468
469 uint8_t *extraData;
470 UINT32 extraDataLength;
471
472 if (!enc->h264Encoder->ExtraData(&extraData, &extraDataLength))
473 return false;
474
475 *extra_data = extraData;
476 *size = extraDataLength;
477
478 return true;
479 }
480
MFH264_GetSEIData(void * data,uint8_t ** sei_data,size_t * size)481 static bool MFH264_GetSEIData(void *data, uint8_t **sei_data, size_t *size)
482 {
483 UNUSED_PARAMETER(data);
484 UNUSED_PARAMETER(sei_data);
485 UNUSED_PARAMETER(size);
486
487 return false;
488 }
489
MFH264_GetVideoInfo(void *,struct video_scale_info * info)490 static void MFH264_GetVideoInfo(void *, struct video_scale_info *info)
491 {
492 info->format = VIDEO_FORMAT_NV12;
493 }
494
MFH264_Update(void * data,obs_data_t * settings)495 static bool MFH264_Update(void *data, obs_data_t *settings)
496 {
497 MFH264_Encoder *enc = static_cast<MFH264_Encoder *>(data);
498
499 UpdateParams(enc, settings);
500
501 enc->h264Encoder->SetBitrate(enc->bitrate);
502 enc->h264Encoder->SetQP(enc->qp);
503
504 return true;
505 }
506
CanSpawnEncoder(std::shared_ptr<EncoderDescriptor> descriptor)507 static bool CanSpawnEncoder(std::shared_ptr<EncoderDescriptor> descriptor)
508 {
509 HRESULT hr;
510 ComPtr<IMFTransform> transform;
511
512 hr = CoCreateInstance(descriptor->Guid(), nullptr,
513 CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&transform));
514 return hr == S_OK;
515 }
516
RegisterMFH264Encoders()517 void RegisterMFH264Encoders()
518 {
519 obs_encoder_info info = { 0 };
520 info.type = OBS_ENCODER_VIDEO;
521 info.get_name = MFH264_GetName;
522 info.create = MFH264_Create;
523 info.destroy = MFH264_Destroy;
524 info.encode = MFH264_Encode;
525 info.update = MFH264_Update;
526 info.get_properties = MFH264_GetProperties;
527 info.get_defaults = MFH264_GetDefaults;
528 info.get_extra_data = MFH264_GetExtraData;
529 info.get_sei_data = MFH264_GetSEIData;
530 info.get_video_info = MFH264_GetVideoInfo;
531 info.codec = "h264";
532
533 auto encoders = EncoderDescriptor::Enumerate();
534 for (auto e : encoders) {
535 /* ignore the software encoder due to the fact that we already
536 * have an objectively superior software encoder available */
537 if (e->Type() == EncoderType::H264_SOFTWARE)
538 continue;
539
540 /* certain encoders such as quicksync will be "available" but
541 * not usable with certain processors */
542 if (!CanSpawnEncoder(e))
543 continue;
544
545 info.caps = OBS_ENCODER_CAP_DEPRECATED;
546 info.id = e->Id();
547 info.type_data = new TypeData(e);
548 info.free_type_data = [] (void *type_data) {
549 delete reinterpret_cast<TypeData*>(type_data);
550 };
551
552 obs_register_encoder(&info);
553 }
554 }
555