Skip to content

Commit ed66df4

Browse files
committed
Bug 1978232 - Enable batch encoding in MFTEncoder r=media-playback-reviewers,jolin
This patch enables batch encoding in `MFTEncoder`. For sync MFTs and async MFTs w/o callbacks, the encoder consumes all pending inputs, producing output until it can no longer accept more. For async MFTs with callbacks, the encoder accepts inputs opportunistically. In this case, the `EncodePromise` is resolved as soon as the encoder signals either need-more-input or have-output, allowing the caller to feed new samples as quickly as possible. Differential Revision: https://phabricator.services.mozilla.com/D257941
1 parent 840f5c2 commit ed66df4

File tree

3 files changed

+77
-51
lines changed

3 files changed

+77
-51
lines changed

dom/media/platforms/wmf/MFTEncoder.cpp

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -796,17 +796,18 @@ static auto ResultToPromise(Result<T, E>&& aResult) {
796796
__func__);
797797
};
798798

799-
RefPtr<MFTEncoder::EncodePromise> MFTEncoder::Encode(InputSample&& aInput) {
799+
RefPtr<MFTEncoder::EncodePromise> MFTEncoder::Encode(
800+
nsTArray<InputSample>&& aInputs) {
800801
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
801802
MOZ_ASSERT(mEncoder);
802803

803804
if (!IsAsync()) {
804-
return ResultToPromise(EncodeSync(std::move(aInput)));
805+
return ResultToPromise(EncodeSync(std::move(aInputs)));
805806
}
806807
if (!mIsRealtime) {
807-
return ResultToPromise(EncodeAsync(std::move(aInput)));
808+
return ResultToPromise(EncodeAsync(std::move(aInputs)));
808809
}
809-
return EncodeWithAsyncCallback(std::move(aInput));
810+
return EncodeWithAsyncCallback(std::move(aInputs));
810811
}
811812

812813
RefPtr<MFTEncoder::EncodePromise> MFTEncoder::Drain() {
@@ -850,44 +851,44 @@ MFTEncoder::CreateInputSample(RefPtr<IMFSample>* aSample, size_t aSize) {
850851
}
851852

852853
Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::EncodeSync(
853-
InputSample&& aInput) {
854+
nsTArray<InputSample>&& aInputs) {
854855
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
855856
MOZ_ASSERT(mEncoder);
856857
MOZ_ASSERT(mState == State::Inited);
857858

858859
auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
859860
SetState(State::Encoding);
860861

862+
EncodedData outputs;
863+
861864
// Follow steps in
862865
// https://learn.microsoft.com/en-us/windows/win32/medfound/basic-mft-processing-model#process-data
863-
HRESULT hr = ProcessInput(std::move(aInput));
864-
if (FAILED(hr)) {
865-
return Err(MediaResult(
866-
NS_ERROR_DOM_MEDIA_FATAL_ERR,
867-
RESULT_DETAIL("ProcessInput error: %s", ErrorMessage(hr).get())));
868-
}
866+
for (auto& input : aInputs) {
867+
HRESULT hr = ProcessInput(std::move(input));
868+
if (FAILED(hr)) {
869+
return Err(MediaResult(
870+
NS_ERROR_DOM_MEDIA_FATAL_ERR,
871+
RESULT_DETAIL("ProcessInput error: %s", ErrorMessage(hr).get())));
872+
}
869873

870-
DWORD flags = 0;
871-
hr = mEncoder->GetOutputStatus(&flags);
872-
if (FAILED(hr) && hr != E_NOTIMPL) {
873-
return Err(MediaResult(
874-
NS_ERROR_DOM_MEDIA_FATAL_ERR,
875-
RESULT_DETAIL("GetOutputStatus error: %s", ErrorMessage(hr).get())));
876-
}
874+
DWORD flags = 0;
875+
hr = mEncoder->GetOutputStatus(&flags);
876+
if (FAILED(hr) && hr != E_NOTIMPL) {
877+
return Err(MediaResult(
878+
NS_ERROR_DOM_MEDIA_FATAL_ERR,
879+
RESULT_DETAIL("GetOutputStatus error: %s", ErrorMessage(hr).get())));
880+
}
877881

878-
if (hr == S_OK && !(flags & MFT_OUTPUT_STATUS_SAMPLE_READY)) {
879-
exitWithError.release();
880-
SetState(State::Inited);
881-
return EncodedData{};
882+
if (hr == E_NOTIMPL ||
883+
(hr == S_OK && (flags & MFT_OUTPUT_STATUS_SAMPLE_READY))) {
884+
outputs.AppendElements(MOZ_TRY(PullOutputs().mapErr([](HRESULT e) {
885+
return MediaResult(
886+
NS_ERROR_DOM_MEDIA_FATAL_ERR,
887+
RESULT_DETAIL("PullOutputs error: %s", ErrorMessage(e).get()));
888+
})));
889+
}
882890
}
883891

884-
MOZ_ASSERT(hr == E_NOTIMPL ||
885-
(hr == S_OK && (flags & MFT_OUTPUT_STATUS_SAMPLE_READY)));
886-
EncodedData outputs = MOZ_TRY(PullOutputs().mapErr([](HRESULT e) {
887-
return MediaResult(
888-
NS_ERROR_DOM_MEDIA_FATAL_ERR,
889-
RESULT_DETAIL("PullOutputs error: %s", ErrorMessage(e).get()));
890-
}));
891892
exitWithError.release();
892893
SetState(State::Inited);
893894
return outputs;
@@ -961,21 +962,27 @@ Result<MFTEncoder::EncodedData, HRESULT> MFTEncoder::PullOutputs() {
961962
}
962963

963964
Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::EncodeAsync(
964-
InputSample&& aInput) {
965+
nsTArray<InputSample>&& aInputs) {
965966
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
966967
MOZ_ASSERT(mEncoder);
967968
MOZ_ASSERT(mState == State::Inited);
968969

969970
auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
970971
SetState(State::Encoding);
971972

972-
mPendingInputs.push_back(std::move(aInput));
973-
auto r = MOZ_TRY(ProcessInput().mapErr([](HRESULT hr) {
974-
return MediaResult(
975-
NS_ERROR_DOM_MEDIA_FATAL_ERR,
976-
RESULT_DETAIL("ProcessInput error: %s", ErrorMessage(hr).get()));
973+
size_t inputCounts = aInputs.Length();
974+
for (auto& input : aInputs) {
975+
mPendingInputs.push_back(std::move(input));
976+
}
977+
978+
MOZ_TRY(ProcessPendingInputs().mapErr([](HRESULT hr) {
979+
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
980+
RESULT_DETAIL("ProcessPendingInputs error: %s",
981+
ErrorMessage(hr).get()));
977982
}));
978-
MFT_ENC_LOGV("input processed: %s", MFTEncoder::EnumValueToString(r));
983+
MFT_ENC_LOGV("%zu inputs processed, %zu inputs remain, inputs needed: %zu",
984+
inputCounts - mPendingInputs.size(), mPendingInputs.size(),
985+
mNumNeedInput);
979986

980987
// If the underlying system signaled that more input is needed, continue
981988
// processing inputs until either no more input is required or there are no
@@ -1045,7 +1052,7 @@ Result<MFTEncoder::EncodedData, MediaResult> MFTEncoder::DrainAsync() {
10451052
}
10461053

10471054
RefPtr<MFTEncoder::EncodePromise> MFTEncoder::EncodeWithAsyncCallback(
1048-
InputSample&& aInput) {
1055+
nsTArray<InputSample>&& aInputs) {
10491056
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
10501057
MOZ_ASSERT(mEncoder);
10511058
MOZ_ASSERT(mEncodePromise.IsEmpty());
@@ -1054,18 +1061,23 @@ RefPtr<MFTEncoder::EncodePromise> MFTEncoder::EncodeWithAsyncCallback(
10541061
auto exitWithError = MakeScopeExit([&] { SetState(State::Error); });
10551062
SetState(State::Encoding);
10561063

1057-
mPendingInputs.push_back(std::move(aInput));
1058-
auto inputProcessed = ProcessInput();
1059-
if (inputProcessed.isErr()) {
1064+
size_t inputCounts = aInputs.Length();
1065+
for (auto& input : aInputs) {
1066+
mPendingInputs.push_back(std::move(input));
1067+
}
1068+
1069+
auto inputsProcessed = ProcessPendingInputs();
1070+
if (inputsProcessed.isErr()) {
10601071
return EncodePromise::CreateAndReject(
10611072
MediaResult(
10621073
NS_ERROR_DOM_MEDIA_FATAL_ERR,
1063-
RESULT_DETAIL("ProcessInput error: %s",
1064-
ErrorMessage(inputProcessed.unwrapErr()).get())),
1074+
RESULT_DETAIL("ProcessPendingInputs error: %s",
1075+
ErrorMessage(inputsProcessed.unwrapErr()).get())),
10651076
__func__);
10661077
}
1067-
MFT_ENC_LOGV("input processed: %s",
1068-
MFTEncoder::EnumValueToString(inputProcessed.inspect()));
1078+
MFT_ENC_LOGV("%zu inputs processed, %zu inputs remain, inputs needed: %zu",
1079+
inputCounts - mPendingInputs.size(), mPendingInputs.size(),
1080+
mNumNeedInput);
10691081

10701082
RefPtr<MFTEncoder::EncodePromise> p = mEncodePromise.Ensure(__func__);
10711083
exitWithError.release();
@@ -1472,6 +1484,17 @@ MFTEncoder::ProcessDrainComplete() {
14721484
return ProcessedResult::DrainComplete;
14731485
}
14741486

1487+
Result<MFTEncoder::ProcessedResult, HRESULT>
1488+
MFTEncoder::ProcessPendingInputs() {
1489+
while (!mPendingInputs.empty()) {
1490+
auto r = MOZ_TRY(ProcessInput());
1491+
if (r == ProcessedResult::AllAvailableInputsProcessed) {
1492+
break;
1493+
}
1494+
}
1495+
return ProcessedResult::AllAvailableInputsProcessed;
1496+
}
1497+
14751498
Result<MediaEventType, HRESULT> MFTEncoder::GetPendingEvent() {
14761499
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
14771500
MOZ_ASSERT(mEncoder);

dom/media/platforms/wmf/MFTEncoder.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class MFTEncoder final {
6464
HRESULT SetBitrate(UINT32 aBitsPerSec);
6565
bool IsHardwareAccelerated() const;
6666

67-
RefPtr<EncodePromise> Encode(InputSample&& aInput);
67+
RefPtr<EncodePromise> Encode(nsTArray<InputSample>&& aInputs);
6868
RefPtr<EncodePromise> Drain();
6969

7070
HRESULT CreateInputSample(RefPtr<IMFSample>* aSample, size_t aSize);
@@ -106,12 +106,12 @@ class MFTEncoder final {
106106
static Maybe<Info> GetInfo(const GUID& aSubtype);
107107

108108
// APIs for synchronous processing model.
109-
Result<EncodedData, MediaResult> EncodeSync(InputSample&& aInput);
109+
Result<EncodedData, MediaResult> EncodeSync(nsTArray<InputSample>&& aInputs);
110110
Result<EncodedData, MediaResult> DrainSync();
111111
Result<EncodedData, HRESULT> PullOutputs();
112112

113113
// APIs for asynchronous processing model for regular usage.
114-
Result<EncodedData, MediaResult> EncodeAsync(InputSample&& aInput);
114+
Result<EncodedData, MediaResult> EncodeAsync(nsTArray<InputSample>&& aInputs);
115115
Result<EncodedData, MediaResult> DrainAsync();
116116

117117
MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING_AT_CLASS_SCOPE(
@@ -122,7 +122,8 @@ class MFTEncoder final {
122122
Result<MediaEventType, HRESULT> GetPendingEvent();
123123

124124
// For realtime usage in asynchronous processing model only.
125-
RefPtr<EncodePromise> EncodeWithAsyncCallback(InputSample&& aInput);
125+
RefPtr<EncodePromise> EncodeWithAsyncCallback(
126+
nsTArray<InputSample>&& aInputs);
126127
RefPtr<EncodePromise> DrainWithAsyncCallback();
127128
RefPtr<EncodePromise> PrepareForDrain();
128129
RefPtr<EncodePromise> StartDraining();
@@ -138,6 +139,7 @@ class MFTEncoder final {
138139
Result<ProcessedResult, HRESULT> ProcessInput();
139140
Result<ProcessedResult, HRESULT> ProcessOutput();
140141
Result<ProcessedResult, HRESULT> ProcessDrainComplete();
142+
Result<ProcessedResult, HRESULT> ProcessPendingInputs();
141143

142144
// Utilities for both processing models.
143145
class OutputResult {

dom/media/platforms/wmf/WMFMediaDataEncoder.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,10 @@ RefPtr<EncodePromise> WMFMediaDataEncoder::ProcessEncode(
226226

227227
RefPtr<EncodePromise> p = mEncodePromise.Ensure(__func__);
228228

229-
MFTEncoder::InputSample inputSample{.mSample = nv12.forget(),
230-
.mKeyFrameRequested = aSample->mKeyframe};
231-
mEncoder->Encode(std::move(inputSample))
229+
nsTArray<MFTEncoder::InputSample> inputs;
230+
inputs.AppendElement(MFTEncoder::InputSample{
231+
.mSample = nv12.forget(), .mKeyFrameRequested = aSample->mKeyframe});
232+
mEncoder->Encode(std::move(inputs))
232233
->Then(
233234
GetCurrentSerialEventTarget(), __func__,
234235
[self = RefPtr<WMFMediaDataEncoder>(this)](

0 commit comments

Comments
 (0)