1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "MFTEncoder.h"
8 #include "mozilla/Logging.h"
9 #include "mozilla/WindowsProcessMitigations.h"
10 #include "mozilla/mscom/Utils.h"
11
12 // Missing from MinGW.
13 #ifndef CODECAPI_AVEncAdaptiveMode
14 # define STATIC_CODECAPI_AVEncAdaptiveMode \
15 0x4419b185, 0xda1f, 0x4f53, 0xbc, 0x76, 0x9, 0x7d, 0xc, 0x1e, 0xfb, 0x1e
16 DEFINE_CODECAPI_GUID(AVEncAdaptiveMode, "4419b185-da1f-4f53-bc76-097d0c1efb1e",
17 0x4419b185, 0xda1f, 0x4f53, 0xbc, 0x76, 0x9, 0x7d, 0xc,
18 0x1e, 0xfb, 0x1e)
19 # define CODECAPI_AVEncAdaptiveMode \
20 DEFINE_CODECAPI_GUIDNAMED(AVEncAdaptiveMode)
21 #endif
22 #ifndef MF_E_NO_EVENTS_AVAILABLE
23 # define MF_E_NO_EVENTS_AVAILABLE _HRESULT_TYPEDEF_(0xC00D3E80L)
24 #endif
25
26 #define MFT_ENC_LOGD(arg, ...) \
27 MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Debug, \
28 ("MFTEncoder(0x%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
29 #define MFT_ENC_LOGE(arg, ...) \
30 MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \
31 ("MFTEncoder(0x%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
32 #define MFT_ENC_SLOGD(arg, ...) \
33 MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Debug, \
34 ("MFTEncoder::%s: " arg, __func__, ##__VA_ARGS__))
35 #define MFT_ENC_SLOGE(arg, ...) \
36 MOZ_LOG(mozilla::sPEMLog, mozilla::LogLevel::Error, \
37 ("MFTEncoder::%s: " arg, __func__, ##__VA_ARGS__))
38
39 namespace mozilla {
40 extern LazyLogModule sPEMLog;
41
ErrorStr(HRESULT hr)42 static const char* ErrorStr(HRESULT hr) {
43 switch (hr) {
44 case S_OK:
45 return "OK";
46 case MF_E_INVALIDMEDIATYPE:
47 return "INVALIDMEDIATYPE";
48 case MF_E_INVALIDSTREAMNUMBER:
49 return "INVALIDSTREAMNUMBER";
50 case MF_E_INVALIDTYPE:
51 return "INVALIDTYPE";
52 case MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING:
53 return "TRANSFORM_PROCESSING";
54 case MF_E_TRANSFORM_TYPE_NOT_SET:
55 return "TRANSFORM_TYPE_NO_SET";
56 case MF_E_UNSUPPORTED_D3D_TYPE:
57 return "UNSUPPORTED_D3D_TYPE";
58 case E_INVALIDARG:
59 return "INVALIDARG";
60 case MF_E_NO_SAMPLE_DURATION:
61 return "NO_SAMPLE_DURATION";
62 case MF_E_NO_SAMPLE_TIMESTAMP:
63 return "NO_SAMPLE_TIMESTAMP";
64 case MF_E_NOTACCEPTING:
65 return "NOTACCEPTING";
66 case MF_E_ATTRIBUTENOTFOUND:
67 return "NOTFOUND";
68 case MF_E_BUFFERTOOSMALL:
69 return "BUFFERTOOSMALL";
70 case E_NOTIMPL:
71 return "NOTIMPL";
72 default:
73 return "OTHER";
74 }
75 }
76
CodecStr(const GUID & aGUID)77 static const char* CodecStr(const GUID& aGUID) {
78 if (IsEqualGUID(aGUID, MFVideoFormat_H264)) {
79 return "H.264";
80 } else if (IsEqualGUID(aGUID, MFVideoFormat_VP80)) {
81 return "VP8";
82 } else if (IsEqualGUID(aGUID, MFVideoFormat_VP90)) {
83 return "VP9";
84 } else {
85 return "Unsupported codec";
86 }
87 }
88
EnumHW(const GUID & aSubtype,IMFActivate ** & aActivates)89 static UINT32 EnumHW(const GUID& aSubtype, IMFActivate**& aActivates) {
90 if (IsWin32kLockedDown()) {
91 // Some HW encoders use system calls and crash when locked down.
92 // TODO: move HW encoding to RDD.
93 MFT_ENC_SLOGD("Don't use HW encoder when win32k locked down.");
94 return 0;
95 }
96
97 UINT32 num = 0;
98 MFT_REGISTER_TYPE_INFO inType = {.guidMajorType = MFMediaType_Video,
99 .guidSubtype = MFVideoFormat_NV12};
100 MFT_REGISTER_TYPE_INFO outType = {.guidMajorType = MFMediaType_Video,
101 .guidSubtype = aSubtype};
102 HRESULT hr =
103 wmf::MFTEnumEx(MFT_CATEGORY_VIDEO_ENCODER,
104 MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER,
105 &inType, &outType, &aActivates, &num);
106 if (FAILED(hr)) {
107 MFT_ENC_SLOGE("enumerate HW encoder for %s: error=%s", CodecStr(aSubtype),
108 ErrorStr(hr));
109 return 0;
110 }
111 if (num == 0) {
112 MFT_ENC_SLOGD("cannot find HW encoder for %s", CodecStr(aSubtype));
113 }
114 return num;
115 }
116
GetFriendlyName(IMFActivate * aAttributes,nsCString & aName)117 static HRESULT GetFriendlyName(IMFActivate* aAttributes, nsCString& aName) {
118 UINT32 len = 0;
119 HRESULT hr = aAttributes->GetStringLength(MFT_FRIENDLY_NAME_Attribute, &len);
120 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
121 if (len > 0) {
122 ++len; // '\0'.
123 WCHAR name[len];
124 if (SUCCEEDED(aAttributes->GetString(MFT_FRIENDLY_NAME_Attribute, name, len,
125 nullptr))) {
126 aName.Append(NS_ConvertUTF16toUTF8(name));
127 }
128 }
129
130 if (aName.Length() == 0) {
131 aName.Append("Unknown MFT");
132 }
133
134 return S_OK;
135 }
136
PopulateHWEncoderInfo(const GUID & aSubtype,nsTArray<MFTEncoder::Info> & aInfos)137 static void PopulateHWEncoderInfo(const GUID& aSubtype,
138 nsTArray<MFTEncoder::Info>& aInfos) {
139 IMFActivate** activates = nullptr;
140 UINT32 num = EnumHW(aSubtype, activates);
141 for (UINT32 i = 0; i < num; ++i) {
142 MFTEncoder::Info info = {.mSubtype = aSubtype};
143 GetFriendlyName(activates[i], info.mName);
144 aInfos.AppendElement(info);
145 MFT_ENC_SLOGD("<ENC> [%s] %s\n", CodecStr(aSubtype), info.mName.Data());
146 activates[i]->Release();
147 activates[i] = nullptr;
148 }
149 CoTaskMemFree(activates);
150 }
151
GetInfo(const GUID & aSubtype)152 Maybe<MFTEncoder::Info> MFTEncoder::GetInfo(const GUID& aSubtype) {
153 nsTArray<Info>& infos = Infos();
154
155 for (auto i : infos) {
156 if (IsEqualGUID(aSubtype, i.mSubtype)) {
157 return Some(i);
158 }
159 }
160 return Nothing();
161 }
162
GetFriendlyName(const GUID & aSubtype)163 nsCString MFTEncoder::GetFriendlyName(const GUID& aSubtype) {
164 Maybe<Info> info = GetInfo(aSubtype);
165
166 return info ? info.ref().mName : "???"_ns;
167 }
168
169 // Called only once by Infos().
Enumerate()170 nsTArray<MFTEncoder::Info> MFTEncoder::Enumerate() {
171 nsTArray<Info> infos;
172
173 if (FAILED(wmf::MFStartup())) {
174 MFT_ENC_SLOGE("cannot init Media Foundation");
175 return infos;
176 }
177
178 PopulateHWEncoderInfo(MFVideoFormat_H264, infos);
179 PopulateHWEncoderInfo(MFVideoFormat_VP90, infos);
180 PopulateHWEncoderInfo(MFVideoFormat_VP80, infos);
181
182 wmf::MFShutdown();
183 return infos;
184 }
185
Infos()186 nsTArray<MFTEncoder::Info>& MFTEncoder::Infos() {
187 static nsTArray<Info> infos = Enumerate();
188 return infos;
189 }
190
CreateFactory(const GUID & aSubtype)191 already_AddRefed<IMFActivate> MFTEncoder::CreateFactory(const GUID& aSubtype) {
192 Maybe<Info> info = GetInfo(aSubtype);
193 if (!info) {
194 return nullptr;
195 }
196
197 IMFActivate** activates = nullptr;
198 UINT32 num = EnumHW(aSubtype, activates);
199 if (num == 0) {
200 return nullptr;
201 }
202
203 // Keep the first and throw out others, if there is any.
204 RefPtr<IMFActivate> factory = activates[0];
205 activates[0] = nullptr;
206 for (UINT32 i = 1; i < num; ++i) {
207 activates[i]->Release();
208 activates[i] = nullptr;
209 }
210 CoTaskMemFree(activates);
211
212 return factory.forget();
213 }
214
Create(const GUID & aSubtype)215 HRESULT MFTEncoder::Create(const GUID& aSubtype) {
216 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
217 MOZ_ASSERT(!mEncoder);
218
219 RefPtr<IMFActivate> factory = CreateFactory(aSubtype);
220 if (!factory) {
221 return E_FAIL;
222 }
223
224 // Create MFT via the activation object.
225 RefPtr<IMFTransform> encoder;
226 HRESULT hr = factory->ActivateObject(
227 IID_PPV_ARGS(static_cast<IMFTransform**>(getter_AddRefs(encoder))));
228 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
229
230 RefPtr<ICodecAPI> config;
231 // Avoid IID_PPV_ARGS() here for MingGW fails to declare UUID for ICodecAPI.
232 hr = encoder->QueryInterface(IID_ICodecAPI, getter_AddRefs(config));
233 if (FAILED(hr)) {
234 encoder = nullptr;
235 factory->ShutdownObject();
236 return hr;
237 }
238
239 mFactory = std::move(factory);
240 mEncoder = std::move(encoder);
241 mConfig = std::move(config);
242 return S_OK;
243 }
244
245 HRESULT
Destroy()246 MFTEncoder::Destroy() {
247 if (!mEncoder) {
248 return S_OK;
249 }
250
251 mEventSource = nullptr;
252 mEncoder = nullptr;
253 mConfig = nullptr;
254 // Release MFT resources via activation object.
255 HRESULT hr = mFactory->ShutdownObject();
256 mFactory = nullptr;
257
258 return hr;
259 }
260
261 HRESULT
SetMediaTypes(IMFMediaType * aInputType,IMFMediaType * aOutputType)262 MFTEncoder::SetMediaTypes(IMFMediaType* aInputType, IMFMediaType* aOutputType) {
263 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
264 MOZ_ASSERT(aInputType && aOutputType);
265
266 HRESULT hr = EnableAsync();
267 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
268
269 hr = GetStreamIDs();
270 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
271
272 // Always set encoder output type before input.
273 hr = mEncoder->SetOutputType(mOutputStreamID, aOutputType, 0);
274 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
275
276 NS_ENSURE_TRUE(MatchInputSubtype(aInputType) != GUID_NULL,
277 MF_E_INVALIDMEDIATYPE);
278
279 hr = mEncoder->SetInputType(mInputStreamID, aInputType, 0);
280 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
281
282 hr = mEncoder->GetInputStreamInfo(mInputStreamID, &mInputStreamInfo);
283 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
284
285 hr = mEncoder->GetOutputStreamInfo(mInputStreamID, &mOutputStreamInfo);
286 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
287 mOutputStreamProvidesSample =
288 IsFlagSet(mOutputStreamInfo.dwFlags, MFT_OUTPUT_STREAM_PROVIDES_SAMPLES);
289
290 hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
291 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
292
293 hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
294 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
295
296 hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
297 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
298
299 RefPtr<IMFMediaEventGenerator> source;
300 hr = mEncoder->QueryInterface(IID_PPV_ARGS(
301 static_cast<IMFMediaEventGenerator**>(getter_AddRefs(source))));
302 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
303 mEventSource = std::move(source);
304 mNumNeedInput = 0;
305 return S_OK;
306 }
307
308 // Async MFT won't work without unlocking. See
309 // https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#unlocking-asynchronous-mfts
EnableAsync()310 HRESULT MFTEncoder::EnableAsync() {
311 IMFAttributes* pAttributes = nullptr;
312 HRESULT hr = mEncoder->GetAttributes(&pAttributes);
313 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
314
315 UINT32 async = MFGetAttributeUINT32(pAttributes, MF_TRANSFORM_ASYNC, FALSE);
316 if (async == TRUE) {
317 hr = pAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
318 } else {
319 hr = E_NOTIMPL;
320 }
321 pAttributes->Release();
322
323 return hr;
324 }
325
GetStreamIDs()326 HRESULT MFTEncoder::GetStreamIDs() {
327 DWORD numIns;
328 DWORD numOuts;
329 HRESULT hr = mEncoder->GetStreamCount(&numIns, &numOuts);
330 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
331 if (numIns < 1 || numOuts < 1) {
332 MFT_ENC_LOGE("stream count error");
333 return MF_E_INVALIDSTREAMNUMBER;
334 }
335
336 DWORD inIDs[numIns];
337 DWORD outIDs[numOuts];
338 hr = mEncoder->GetStreamIDs(numIns, inIDs, numOuts, outIDs);
339 if (SUCCEEDED(hr)) {
340 mInputStreamID = inIDs[0];
341 mOutputStreamID = outIDs[0];
342 } else if (hr == E_NOTIMPL) {
343 mInputStreamID = 0;
344 mOutputStreamID = 0;
345 } else {
346 MFT_ENC_LOGE("failed to get stream IDs");
347 return hr;
348 }
349 return S_OK;
350 }
351
MatchInputSubtype(IMFMediaType * aInputType)352 GUID MFTEncoder::MatchInputSubtype(IMFMediaType* aInputType) {
353 MOZ_ASSERT(mEncoder);
354 MOZ_ASSERT(aInputType);
355
356 GUID desired = GUID_NULL;
357 HRESULT hr = aInputType->GetGUID(MF_MT_SUBTYPE, &desired);
358 NS_ENSURE_TRUE(SUCCEEDED(hr), GUID_NULL);
359 MOZ_ASSERT(desired != GUID_NULL);
360
361 DWORD i = 0;
362 IMFMediaType* inputType = nullptr;
363 GUID preferred = GUID_NULL;
364 while (true) {
365 hr = mEncoder->GetInputAvailableType(mInputStreamID, i, &inputType);
366 if (hr == MF_E_NO_MORE_TYPES) {
367 break;
368 }
369 NS_ENSURE_TRUE(SUCCEEDED(hr), GUID_NULL);
370
371 GUID sub = GUID_NULL;
372 hr = inputType->GetGUID(MF_MT_SUBTYPE, &sub);
373 NS_ENSURE_TRUE(SUCCEEDED(hr), GUID_NULL);
374
375 if (IsEqualGUID(desired, sub)) {
376 preferred = desired;
377 break;
378 }
379 ++i;
380 }
381
382 return IsEqualGUID(preferred, desired) ? preferred : GUID_NULL;
383 }
384
385 HRESULT
SendMFTMessage(MFT_MESSAGE_TYPE aMsg,ULONG_PTR aData)386 MFTEncoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, ULONG_PTR aData) {
387 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
388 MOZ_ASSERT(mEncoder);
389
390 return mEncoder->ProcessMessage(aMsg, aData);
391 }
392
SetModes(UINT32 aBitsPerSec)393 HRESULT MFTEncoder::SetModes(UINT32 aBitsPerSec) {
394 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
395 MOZ_ASSERT(mConfig);
396
397 VARIANT var;
398 var.vt = VT_UI4;
399 var.ulVal = eAVEncCommonRateControlMode_CBR;
400 HRESULT hr = mConfig->SetValue(&CODECAPI_AVEncCommonRateControlMode, &var);
401 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
402
403 var.ulVal = aBitsPerSec;
404 hr = mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
405 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
406
407 if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVEncAdaptiveMode))) {
408 var.ulVal = eAVEncAdaptiveMode_Resolution;
409 hr = mConfig->SetValue(&CODECAPI_AVEncAdaptiveMode, &var);
410 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
411 }
412
413 if (SUCCEEDED(mConfig->IsModifiable(&CODECAPI_AVLowLatencyMode))) {
414 var.vt = VT_BOOL;
415 var.boolVal = VARIANT_TRUE;
416 hr = mConfig->SetValue(&CODECAPI_AVLowLatencyMode, &var);
417 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
418 }
419
420 return S_OK;
421 }
422
423 HRESULT
SetBitrate(UINT32 aBitsPerSec)424 MFTEncoder::SetBitrate(UINT32 aBitsPerSec) {
425 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
426 MOZ_ASSERT(mConfig);
427
428 VARIANT var = {.vt = VT_UI4, .ulVal = aBitsPerSec};
429 return mConfig->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
430 }
431
CreateSample(RefPtr<IMFSample> * aOutSample,DWORD aSize,DWORD aAlignment)432 static HRESULT CreateSample(RefPtr<IMFSample>* aOutSample, DWORD aSize,
433 DWORD aAlignment) {
434 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
435
436 HRESULT hr;
437 RefPtr<IMFSample> sample;
438 hr = wmf::MFCreateSample(getter_AddRefs(sample));
439 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
440
441 RefPtr<IMFMediaBuffer> buffer;
442 hr = wmf::MFCreateAlignedMemoryBuffer(aSize, aAlignment,
443 getter_AddRefs(buffer));
444 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
445
446 hr = sample->AddBuffer(buffer);
447 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
448
449 *aOutSample = sample.forget();
450
451 return S_OK;
452 }
453
454 HRESULT
CreateInputSample(RefPtr<IMFSample> * aSample,size_t aSize)455 MFTEncoder::CreateInputSample(RefPtr<IMFSample>* aSample, size_t aSize) {
456 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
457
458 return CreateSample(
459 aSample, aSize,
460 mInputStreamInfo.cbAlignment > 0 ? mInputStreamInfo.cbAlignment - 1 : 0);
461 }
462
463 HRESULT
PushInput(RefPtr<IMFSample> && aInput)464 MFTEncoder::PushInput(RefPtr<IMFSample>&& aInput) {
465 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
466 MOZ_ASSERT(mEncoder);
467 MOZ_ASSERT(aInput);
468
469 mPendingInputs.Push(aInput.forget());
470 HRESULT hr = ProcessInput();
471 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
472
473 return ProcessEvents();
474 }
475
ProcessInput()476 HRESULT MFTEncoder::ProcessInput() {
477 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
478 MOZ_ASSERT(mEncoder);
479
480 if (mNumNeedInput == 0 || mPendingInputs.GetSize() == 0) {
481 return S_OK;
482 }
483
484 RefPtr<IMFSample> input = mPendingInputs.PopFront();
485 HRESULT hr = mEncoder->ProcessInput(mInputStreamID, input, 0);
486 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
487 --mNumNeedInput;
488
489 return S_OK;
490 }
491
ProcessEvents()492 HRESULT MFTEncoder::ProcessEvents() {
493 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
494 MOZ_ASSERT(mEncoder);
495 MOZ_ASSERT(mEventSource, "no event generator");
496
497 HRESULT hr = E_FAIL;
498 while (true) {
499 RefPtr<IMFMediaEvent> event;
500 hr = mEventSource->GetEvent(MF_EVENT_FLAG_NO_WAIT, getter_AddRefs(event));
501 switch (hr) {
502 case S_OK:
503 break;
504 case MF_E_NO_EVENTS_AVAILABLE:
505 return S_OK;
506 case MF_E_MULTIPLE_SUBSCRIBERS:
507 default:
508 MFT_ENC_LOGE("failed to get event: %s", ErrorStr(hr));
509 return hr;
510 }
511
512 MediaEventType type = MEUnknown;
513 HRESULT hr = event->GetType(&type);
514 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
515 switch (type) {
516 case METransformNeedInput:
517 ++mNumNeedInput;
518 hr = ProcessInput();
519 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
520 break;
521 case METransformHaveOutput:
522 hr = ProcessOutput();
523 break;
524 case METransformDrainComplete:
525 mDrainState = DrainState::DRAINED;
526 break;
527 default:
528 MFT_ENC_LOGE("event: error=%s", ErrorStr(hr));
529 }
530 }
531
532 return hr;
533 }
534
ProcessOutput()535 HRESULT MFTEncoder::ProcessOutput() {
536 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
537 MOZ_ASSERT(mEncoder);
538
539 MFT_OUTPUT_DATA_BUFFER output = {.dwStreamID = mOutputStreamID,
540 .pSample = nullptr,
541 .dwStatus = 0,
542 .pEvents = nullptr};
543 RefPtr<IMFSample> sample;
544 HRESULT hr = E_FAIL;
545 if (!mOutputStreamProvidesSample) {
546 hr = CreateSample(&sample, mOutputStreamInfo.cbSize,
547 mOutputStreamInfo.cbAlignment > 1
548 ? mOutputStreamInfo.cbAlignment - 1
549 : 0);
550 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
551 output.pSample = sample;
552 }
553
554 DWORD status = 0;
555 hr = mEncoder->ProcessOutput(0, 1, &output, &status);
556 if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
557 MFT_ENC_LOGD("output stream change");
558 if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE) {
559 // Follow the instructions in Microsoft doc:
560 // https://docs.microsoft.com/en-us/windows/win32/medfound/handling-stream-changes#output-type
561 IMFMediaType* outputType = nullptr;
562 hr = mEncoder->GetOutputAvailableType(mOutputStreamID, 0, &outputType);
563 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
564 hr = mEncoder->SetOutputType(mOutputStreamID, outputType, 0);
565 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
566 }
567 return MF_E_TRANSFORM_STREAM_CHANGE;
568 }
569 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
570
571 mOutputs.AppendElement(output.pSample);
572 if (mOutputStreamProvidesSample) {
573 // Release MFT provided sample.
574 output.pSample->Release();
575 output.pSample = nullptr;
576 }
577
578 return S_OK;
579 }
580
TakeOutput(nsTArray<RefPtr<IMFSample>> & aOutput)581 HRESULT MFTEncoder::TakeOutput(nsTArray<RefPtr<IMFSample>>& aOutput) {
582 MOZ_ASSERT(aOutput.Length() == 0);
583 aOutput.SwapElements(mOutputs);
584 return S_OK;
585 }
586
Drain(nsTArray<RefPtr<IMFSample>> & aOutput)587 HRESULT MFTEncoder::Drain(nsTArray<RefPtr<IMFSample>>& aOutput) {
588 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
589 MOZ_ASSERT(mEncoder);
590 MOZ_ASSERT(aOutput.Length() == 0);
591
592 switch (mDrainState) {
593 case DrainState::DRAINABLE:
594 // Exhaust pending inputs.
595 while (mPendingInputs.GetSize() > 0) {
596 HRESULT hr = ProcessEvents();
597 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
598 }
599 SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
600 mDrainState = DrainState::DRAINING;
601 [[fallthrough]]; // To collect and return outputs.
602 case DrainState::DRAINING:
603 // Collect remaining outputs.
604 while (mOutputs.Length() == 0 && mDrainState != DrainState::DRAINED) {
605 HRESULT hr = ProcessEvents();
606 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
607 }
608 [[fallthrough]]; // To return outputs.
609 case DrainState::DRAINED:
610 aOutput.SwapElements(mOutputs);
611 return S_OK;
612 }
613 }
614
GetMPEGSequenceHeader(nsTArray<UINT8> & aHeader)615 HRESULT MFTEncoder::GetMPEGSequenceHeader(nsTArray<UINT8>& aHeader) {
616 MOZ_ASSERT(mscom::IsCurrentThreadMTA());
617 MOZ_ASSERT(mEncoder);
618 MOZ_ASSERT(aHeader.Length() == 0);
619
620 RefPtr<IMFMediaType> outputType;
621 HRESULT hr = mEncoder->GetOutputCurrentType(mOutputStreamID,
622 getter_AddRefs(outputType));
623 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
624
625 UINT32 length = 0;
626 hr = outputType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &length);
627 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
628
629 aHeader.SetCapacity(length);
630 hr = outputType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, aHeader.Elements(),
631 length, nullptr);
632 aHeader.SetLength(SUCCEEDED(hr) ? length : 0);
633
634 return hr;
635 }
636
637 } // namespace mozilla
638
639 #undef MFT_ENC_SLOGE
640 #undef MFT_ENC_SLOGD
641 #undef MFT_ENC_LOGE
642 #undef MFT_ENC_LOGD
643