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