Bug 1795082 - Part 2/2: Drop post-processing from getURL() r=zombie
[gecko.git] / dom / media / webcodecs / EncoderAgent.cpp
blobbb02f1835fcc1121139fa136ba97c56b000ee1d6
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/. */
7 #include "EncoderAgent.h"
9 #include "PDMFactory.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/Logging.h"
12 #include "nsThreadUtils.h"
14 extern mozilla::LazyLogModule gWebCodecsLog;
16 namespace mozilla {
18 #ifdef LOG_INTERNAL
19 # undef LOG_INTERNAL
20 #endif // LOG_INTERNAL
21 #define LOG_INTERNAL(level, msg, ...) \
22 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
24 #ifdef LOG
25 # undef LOG
26 #endif // LOG
27 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
29 #ifdef LOGW
30 # undef LOGW
31 #endif // LOGE
32 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
34 #ifdef LOGE
35 # undef LOGE
36 #endif // LOGE
37 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
39 #ifdef LOGV
40 # undef LOGV
41 #endif // LOGV
42 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
44 EncoderAgent::EncoderAgent(WebCodecsId aId)
45 : mId(aId),
46 mOwnerThread(GetCurrentSerialEventTarget()),
47 mPEMFactory(MakeRefPtr<PEMFactory>()),
48 mEncoder(nullptr),
49 mState(State::Unconfigured) {
50 MOZ_ASSERT(mOwnerThread);
51 MOZ_ASSERT(mPEMFactory);
52 LOG("EncoderAgent #%zu (%p) ctor", mId, this);
55 EncoderAgent::~EncoderAgent() {
56 LOG("EncoderAgent #%zu (%p) dtor", mId, this);
57 MOZ_ASSERT(mState == State::Unconfigured, "encoder released in wrong state");
58 MOZ_ASSERT(!mEncoder, "encoder must be shutdown");
61 RefPtr<EncoderAgent::ConfigurePromise> EncoderAgent::Configure(
62 const EncoderConfig& aConfig) {
63 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
64 MOZ_ASSERT(mState == State::Unconfigured || mState == State::Error);
65 MOZ_ASSERT(mConfigurePromise.IsEmpty());
66 MOZ_ASSERT(!mCreateRequest.Exists());
67 MOZ_ASSERT(!mInitRequest.Exists());
69 if (mState == State::Error) {
70 LOGE("EncoderAgent #%zu (%p) tried to configure in error state", mId, this);
71 return ConfigurePromise::CreateAndReject(
72 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
73 "Cannot configure in error state"),
74 __func__);
77 MOZ_ASSERT(mState == State::Unconfigured);
78 MOZ_ASSERT(!mEncoder);
79 SetState(State::Configuring);
81 LOG("EncoderAgent #%zu (%p) is creating an encoder (%s)", mId, this,
82 GetCodecTypeString(aConfig.mCodec));
84 RefPtr<ConfigurePromise> p = mConfigurePromise.Ensure(__func__);
86 mPEMFactory->CreateEncoderAsync(aConfig, dom::GetWebCodecsEncoderTaskQueue())
87 ->Then(
88 mOwnerThread, __func__,
89 [self = RefPtr{this}](RefPtr<MediaDataEncoder>&& aEncoder) {
90 self->mCreateRequest.Complete();
92 // If EncoderAgent has been shut down, shut the created encoder down
93 // and return.
94 if (!self->mShutdownWhileCreationPromise.IsEmpty()) {
95 MOZ_ASSERT(self->mState == State::ShuttingDown);
96 MOZ_ASSERT(self->mConfigurePromise.IsEmpty(),
97 "configuration should have been rejected");
99 LOGW(
100 "EncoderAgent #%zu (%p) has been shut down. We need to shut "
101 "the newly created encoder down",
102 self->mId, self.get());
103 aEncoder->Shutdown()->Then(
104 self->mOwnerThread, __func__,
105 [self](const ShutdownPromise::ResolveOrRejectValue& aValue) {
106 MOZ_ASSERT(self->mState == State::ShuttingDown);
108 LOGW(
109 "EncoderAgent #%zu (%p), newly created encoder "
110 "shutdown "
111 "has been %s",
112 self->mId, self.get(),
113 aValue.IsResolve() ? "resolved" : "rejected");
115 self->SetState(State::Unconfigured);
117 self->mShutdownWhileCreationPromise.ResolveOrReject(
118 aValue, __func__);
120 return;
123 self->mEncoder = aEncoder.forget();
124 LOG("EncoderAgent #%zu (%p) has created a encoder, now initialize "
125 "it",
126 self->mId, self.get());
127 self->mEncoder->Init()
128 ->Then(
129 self->mOwnerThread, __func__,
130 [self]() {
131 self->mInitRequest.Complete();
132 LOG("EncoderAgent #%zu (%p) has initialized the encoder",
133 self->mId, self.get());
134 self->SetState(State::Configured);
135 self->mConfigurePromise.Resolve(true, __func__);
137 [self](const MediaResult& aError) {
138 self->mInitRequest.Complete();
139 LOGE(
140 "EncoderAgent #%zu (%p) failed to initialize the "
141 "encoder",
142 self->mId, self.get());
143 self->SetState(State::Error);
144 self->mConfigurePromise.Reject(aError, __func__);
146 ->Track(self->mInitRequest);
148 [self = RefPtr{this}](const MediaResult& aError) {
149 self->mCreateRequest.Complete();
150 LOGE("EncoderAgent #%zu (%p) failed to create a encoder", self->mId,
151 self.get());
153 // If EncoderAgent has been shut down, we need to resolve the
154 // shutdown promise.
155 if (!self->mShutdownWhileCreationPromise.IsEmpty()) {
156 MOZ_ASSERT(self->mState == State::ShuttingDown);
157 MOZ_ASSERT(self->mConfigurePromise.IsEmpty(),
158 "configuration should have been rejected");
160 LOGW(
161 "EncoderAgent #%zu (%p) has been shut down. Resolve the "
162 "shutdown promise right away since encoder creation failed",
163 self->mId, self.get());
165 self->SetState(State::Unconfigured);
166 self->mShutdownWhileCreationPromise.Resolve(true, __func__);
167 return;
170 self->SetState(State::Error);
171 self->mConfigurePromise.Reject(aError, __func__);
173 ->Track(mCreateRequest);
175 return p;
178 RefPtr<EncoderAgent::ReconfigurationPromise> EncoderAgent::Reconfigure(
179 const RefPtr<const EncoderConfigurationChangeList>& aConfigChanges) {
180 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
181 MOZ_ASSERT(mState == State::Configured || mState == State::Error);
182 MOZ_ASSERT(mReconfigurationPromise.IsEmpty());
184 if (mState == State::Error) {
185 LOGE("EncoderAgent #%zu (%p) tried to reconfigure in error state", mId,
186 this);
187 return ReconfigurationPromise::CreateAndReject(
188 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
189 "Cannot reconfigure in error state"),
190 __func__);
193 MOZ_ASSERT(mEncoder);
194 SetState(State::Configuring);
196 LOG("EncoderAgent #%zu (%p) is reconfiguring its encoder (%s)", mId, this,
197 NS_ConvertUTF16toUTF8(aConfigChanges->ToString().get()).get());
199 RefPtr<ReconfigurationPromise> p = mReconfigurationPromise.Ensure(__func__);
201 mEncoder->Reconfigure(aConfigChanges)
202 ->Then(
203 mOwnerThread, __func__,
204 [self = RefPtr{this}](bool) {
205 self->mReconfigurationRequest.Complete();
206 LOGE("EncoderAgent #%zu (%p) reconfigure success", self->mId,
207 self.get());
208 self->SetState(State::Configured);
209 self->mReconfigurationPromise.Resolve(true, __func__);
211 [self = RefPtr{this}](const MediaResult& aError) {
212 self->mReconfigurationRequest.Complete();
213 LOGE("EncoderAgent #%zu (%p) reconfigure failure", self->mId,
214 self.get());
215 // Not a a fatal error per se, the owner will deal with it.
216 self->mReconfigurationPromise.Reject(aError, __func__);
218 ->Track(mReconfigurationRequest);
220 return p;
223 RefPtr<ShutdownPromise> EncoderAgent::Shutdown() {
224 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
226 auto r =
227 MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, "Canceled by encoder shutdown");
229 // If the encoder creation has not been completed yet, wait until the encoder
230 // being created has been shut down.
231 if (mCreateRequest.Exists()) {
232 MOZ_ASSERT(!mInitRequest.Exists());
233 MOZ_ASSERT(!mConfigurePromise.IsEmpty());
234 MOZ_ASSERT(!mEncoder);
235 MOZ_ASSERT(mState == State::Configuring);
236 MOZ_ASSERT(mShutdownWhileCreationPromise.IsEmpty());
238 LOGW(
239 "EncoderAgent #%zu (%p) shutdown while the encoder creation for "
240 "configuration is in flight. Reject the configuration now and defer "
241 "the shutdown until the created encoder has been shut down",
242 mId, this);
244 // Reject the configuration in flight.
245 mConfigurePromise.Reject(r, __func__);
247 // Get the promise that will be resolved when the encoder being created has
248 // been destroyed.
249 SetState(State::ShuttingDown);
250 return mShutdownWhileCreationPromise.Ensure(__func__);
253 // If encoder creation has been completed, we must have the encoder now.
254 MOZ_ASSERT(mEncoder);
256 // Cancel pending initialization for configuration in flight if any.
257 mInitRequest.DisconnectIfExists();
258 mConfigurePromise.RejectIfExists(r, __func__);
260 mReconfigurationRequest.DisconnectIfExists();
261 mReconfigurationPromise.RejectIfExists(r, __func__);
263 // Cancel encoder in flight if any.
264 mEncodeRequest.DisconnectIfExists();
265 mEncodePromise.RejectIfExists(r, __func__);
267 // Cancel flush-out in flight if any.
268 mDrainRequest.DisconnectIfExists();
269 mEncodeRequest.DisconnectIfExists();
271 mDrainRequest.DisconnectIfExists();
272 mDrainPromise.RejectIfExists(r, __func__);
274 SetState(State::Unconfigured);
276 RefPtr<MediaDataEncoder> encoder = std::move(mEncoder);
277 return encoder->Shutdown();
280 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Encode(MediaData* aInput) {
281 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
282 MOZ_ASSERT(aInput);
283 MOZ_ASSERT(mState == State::Configured || mState == State::Error);
284 MOZ_ASSERT(mEncodePromise.IsEmpty());
285 MOZ_ASSERT(!mEncodeRequest.Exists());
287 if (mState == State::Error) {
288 LOGE("EncoderAgent #%zu (%p) tried to encoder in error state", mId, this);
289 return EncodePromise::CreateAndReject(
290 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
291 "Cannot encoder in error state"),
292 __func__);
295 MOZ_ASSERT(mState == State::Configured);
296 MOZ_ASSERT(mEncoder);
297 SetState(State::Encoding);
299 RefPtr<EncodePromise> p = mEncodePromise.Ensure(__func__);
301 mEncoder->Encode(aInput)
302 ->Then(
303 mOwnerThread, __func__,
304 [self = RefPtr{this}](MediaDataEncoder::EncodedData&& aData) {
305 self->mEncodeRequest.Complete();
306 LOGV("EncoderAgent #%zu (%p) encode successful", self->mId,
307 self.get());
308 self->SetState(State::Configured);
309 self->mEncodePromise.Resolve(std::move(aData), __func__);
311 [self = RefPtr{this}](const MediaResult& aError) {
312 self->mEncodeRequest.Complete();
313 LOGV("EncoderAgent #%zu (%p) failed to encode", self->mId,
314 self.get());
315 self->SetState(State::Error);
316 self->mEncodePromise.Reject(aError, __func__);
318 ->Track(mEncodeRequest);
320 return p;
323 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Drain() {
324 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
325 // This can be called when reconfiguring the encoder.
326 MOZ_ASSERT(mState == State::Configured || mState == State::Configuring);
327 MOZ_ASSERT(mDrainPromise.IsEmpty());
328 MOZ_ASSERT(mEncoder);
330 SetState(State::Flushing);
332 RefPtr<EncodePromise> p = mDrainPromise.Ensure(__func__);
333 DryUntilDrain();
334 return p;
337 void EncoderAgent::DryUntilDrain() {
338 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
339 MOZ_ASSERT(mState == State::Flushing);
340 MOZ_ASSERT(!mDrainRequest.Exists());
341 MOZ_ASSERT(mEncoder);
343 LOG("EncoderAgent #%zu (%p) is draining the encoder", mId, this);
344 mEncoder->Drain()
345 ->Then(
346 mOwnerThread, __func__,
347 [self = RefPtr{this}](MediaDataEncoder::EncodedData&& aData) {
348 self->mDrainRequest.Complete();
350 if (aData.IsEmpty()) {
351 LOG("EncoderAgent #%zu (%p) is dry now", self->mId, self.get());
352 self->SetState(State::Configured);
353 self->mDrainPromise.Resolve(std::move(self->mDrainData),
354 __func__);
355 return;
358 LOG("EncoderAgent #%zu (%p) drained %zu encoder data. Keep "
359 "draining "
360 "until dry",
361 self->mId, self.get(), aData.Length());
362 self->mDrainData.AppendElements(std::move(aData));
363 self->DryUntilDrain();
365 [self = RefPtr{this}](const MediaResult& aError) {
366 self->mDrainRequest.Complete();
368 LOGE("EncoderAgent %p failed to drain encoder", self.get());
369 self->mDrainData.Clear();
370 self->mDrainPromise.Reject(aError, __func__);
372 ->Track(mDrainRequest);
375 void EncoderAgent::SetState(State aState) {
376 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
378 auto validateStateTransition = [](State aOldState, State aNewState) {
379 switch (aOldState) {
380 case State::Unconfigured:
381 return aNewState == State::Configuring;
382 case State::Configuring:
383 return aNewState == State::Configured || aNewState == State::Error ||
384 aNewState == State::Flushing ||
385 aNewState == State::Unconfigured ||
386 aNewState == State::ShuttingDown;
387 case State::Configured:
388 return aNewState == State::Unconfigured ||
389 aNewState == State::Configuring ||
390 aNewState == State::Encoding || aNewState == State::Flushing;
391 case State::Encoding:
392 case State::Flushing:
393 return aNewState == State::Configured || aNewState == State::Error ||
394 aNewState == State::Unconfigured;
395 case State::ShuttingDown:
396 return aNewState == State::Unconfigured;
397 case State::Error:
398 return aNewState == State::Unconfigured;
399 default:
400 break;
402 MOZ_ASSERT_UNREACHABLE("Unhandled state transition");
403 return false;
406 auto stateToString = [](State aState) -> const char* {
407 switch (aState) {
408 case State::Unconfigured:
409 return "Unconfigured";
410 case State::Configuring:
411 return "Configuring";
412 case State::Configured:
413 return "Configured";
414 case State::Encoding:
415 return "Encoding";
416 case State::Flushing:
417 return "Flushing";
418 case State::ShuttingDown:
419 return "ShuttingDown";
420 case State::Error:
421 return "Error";
422 default:
423 break;
425 MOZ_ASSERT_UNREACHABLE("Unhandled state type");
426 return "Unknown";
429 DebugOnly<bool> isValid = validateStateTransition(mState, aState);
430 LOGV("EncoderAgent #%zu (%p) state change: %s -> %s", mId, this,
431 stateToString(mState), stateToString(aState));
432 MOZ_ASSERT(isValid);
433 mState = aState;
436 #undef LOG
437 #undef LOGW
438 #undef LOGE
439 #undef LOGV
440 #undef LOG_INTERNAL
442 } // namespace mozilla