Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / media / webcodecs / EncoderAgent.cpp
blobe6af17a0be453f087cda719ef9a998234fe687d8
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->mReconfigurationPromise.Resolve(true, __func__);
210 [self = RefPtr{this}](const MediaResult& aError) {
211 self->mReconfigurationRequest.Complete();
212 LOGE("EncoderAgent #%zu (%p) reconfigure failure", self->mId,
213 self.get());
214 // Not a a fatal error per se, the owner will deal with it.
215 self->mReconfigurationPromise.Reject(aError, __func__);
217 ->Track(mReconfigurationRequest);
219 return p;
222 RefPtr<ShutdownPromise> EncoderAgent::Shutdown() {
223 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
225 auto r =
226 MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, "Canceled by encoder shutdown");
228 // If the encoder creation has not been completed yet, wait until the encoder
229 // being created has been shut down.
230 if (mCreateRequest.Exists()) {
231 MOZ_ASSERT(!mInitRequest.Exists());
232 MOZ_ASSERT(!mConfigurePromise.IsEmpty());
233 MOZ_ASSERT(!mEncoder);
234 MOZ_ASSERT(mState == State::Configuring);
235 MOZ_ASSERT(mShutdownWhileCreationPromise.IsEmpty());
237 LOGW(
238 "EncoderAgent #%zu (%p) shutdown while the encoder creation for "
239 "configuration is in flight. Reject the configuration now and defer "
240 "the shutdown until the created encoder has been shut down",
241 mId, this);
243 // Reject the configuration in flight.
244 mConfigurePromise.Reject(r, __func__);
246 // Get the promise that will be resolved when the encoder being created has
247 // been destroyed.
248 SetState(State::ShuttingDown);
249 return mShutdownWhileCreationPromise.Ensure(__func__);
252 // If encoder creation has been completed, we must have the encoder now.
253 MOZ_ASSERT(mEncoder);
255 // Cancel pending initialization for configuration in flight if any.
256 mInitRequest.DisconnectIfExists();
257 mConfigurePromise.RejectIfExists(r, __func__);
259 mReconfigurationRequest.DisconnectIfExists();
260 mReconfigurationPromise.RejectIfExists(r, __func__);
262 // Cancel encoder in flight if any.
263 mEncodeRequest.DisconnectIfExists();
264 mEncodePromise.RejectIfExists(r, __func__);
266 // Cancel flush-out in flight if any.
267 mDrainRequest.DisconnectIfExists();
268 mEncodeRequest.DisconnectIfExists();
270 mDrainRequest.DisconnectIfExists();
271 mDrainPromise.RejectIfExists(r, __func__);
273 SetState(State::Unconfigured);
275 RefPtr<MediaDataEncoder> encoder = std::move(mEncoder);
276 return encoder->Shutdown();
279 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Encode(MediaData* aInput) {
280 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
281 MOZ_ASSERT(aInput);
282 MOZ_ASSERT(mState == State::Configured || mState == State::Error);
283 MOZ_ASSERT(mEncodePromise.IsEmpty());
284 MOZ_ASSERT(!mEncodeRequest.Exists());
286 if (mState == State::Error) {
287 LOGE("EncoderAgent #%zu (%p) tried to encoder in error state", mId, this);
288 return EncodePromise::CreateAndReject(
289 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
290 "Cannot encoder in error state"),
291 __func__);
294 MOZ_ASSERT(mState == State::Configured);
295 MOZ_ASSERT(mEncoder);
296 SetState(State::Encoding);
298 RefPtr<EncodePromise> p = mEncodePromise.Ensure(__func__);
300 mEncoder->Encode(aInput)
301 ->Then(
302 mOwnerThread, __func__,
303 [self = RefPtr{this}](MediaDataEncoder::EncodedData&& aData) {
304 self->mEncodeRequest.Complete();
305 LOGV("EncoderAgent #%zu (%p) encode successful", self->mId,
306 self.get());
307 self->SetState(State::Configured);
308 self->mEncodePromise.Resolve(std::move(aData), __func__);
310 [self = RefPtr{this}](const MediaResult& aError) {
311 self->mEncodeRequest.Complete();
312 LOGV("EncoderAgent #%zu (%p) failed to encode", self->mId,
313 self.get());
314 self->SetState(State::Error);
315 self->mEncodePromise.Reject(aError, __func__);
317 ->Track(mEncodeRequest);
319 return p;
322 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Drain() {
323 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
324 // This can be called when reconfiguring the encoder.
325 MOZ_ASSERT(mState == State::Configured || mState == State::Configuring);
326 MOZ_ASSERT(mDrainPromise.IsEmpty());
327 MOZ_ASSERT(mEncoder);
329 SetState(State::Flushing);
331 RefPtr<EncodePromise> p = mDrainPromise.Ensure(__func__);
332 DryUntilDrain();
333 return p;
336 void EncoderAgent::DryUntilDrain() {
337 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
338 MOZ_ASSERT(mState == State::Flushing);
339 MOZ_ASSERT(!mDrainRequest.Exists());
340 MOZ_ASSERT(mEncoder);
342 LOG("EncoderAgent #%zu (%p) is draining the encoder", mId, this);
343 mEncoder->Drain()
344 ->Then(
345 mOwnerThread, __func__,
346 [self = RefPtr{this}](MediaDataEncoder::EncodedData&& aData) {
347 self->mDrainRequest.Complete();
349 if (aData.IsEmpty()) {
350 LOG("EncoderAgent #%zu (%p) is dry now", self->mId, self.get());
351 self->SetState(State::Configured);
352 self->mDrainPromise.Resolve(std::move(self->mDrainData),
353 __func__);
354 return;
357 LOG("EncoderAgent #%zu (%p) drained %zu encoder data. Keep "
358 "draining "
359 "until dry",
360 self->mId, self.get(), aData.Length());
361 self->mDrainData.AppendElements(std::move(aData));
362 self->DryUntilDrain();
364 [self = RefPtr{this}](const MediaResult& aError) {
365 self->mDrainRequest.Complete();
367 LOGE("EncoderAgent %p failed to drain encoder", self.get());
368 self->mDrainData.Clear();
369 self->mDrainPromise.Reject(aError, __func__);
371 ->Track(mDrainRequest);
374 void EncoderAgent::SetState(State aState) {
375 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
377 auto validateStateTransition = [](State aOldState, State aNewState) {
378 switch (aOldState) {
379 case State::Unconfigured:
380 return aNewState == State::Configuring;
381 case State::Configuring:
382 return aNewState == State::Configured || aNewState == State::Error ||
383 aNewState == State::Flushing ||
384 aNewState == State::Unconfigured ||
385 aNewState == State::ShuttingDown;
386 case State::Configured:
387 return aNewState == State::Unconfigured ||
388 aNewState == State::Configuring ||
389 aNewState == State::Encoding || aNewState == State::Flushing;
390 case State::Encoding:
391 case State::Flushing:
392 return aNewState == State::Configured || aNewState == State::Error ||
393 aNewState == State::Unconfigured;
394 case State::ShuttingDown:
395 return aNewState == State::Unconfigured;
396 case State::Error:
397 return aNewState == State::Unconfigured;
398 default:
399 break;
401 MOZ_ASSERT_UNREACHABLE("Unhandled state transition");
402 return false;
405 auto stateToString = [](State aState) -> const char* {
406 switch (aState) {
407 case State::Unconfigured:
408 return "Unconfigured";
409 case State::Configuring:
410 return "Configuring";
411 case State::Configured:
412 return "Configured";
413 case State::Encoding:
414 return "Encoding";
415 case State::Flushing:
416 return "Flushing";
417 case State::ShuttingDown:
418 return "ShuttingDown";
419 case State::Error:
420 return "Error";
421 default:
422 break;
424 MOZ_ASSERT_UNREACHABLE("Unhandled state type");
425 return "Unknown";
428 DebugOnly<bool> isValid = validateStateTransition(mState, aState);
429 LOGV("EncoderAgent #%zu (%p) state change: %s -> %s", mId, this,
430 stateToString(mState), stateToString(aState));
431 MOZ_ASSERT(isValid);
432 mState = aState;
435 #undef LOG
436 #undef LOGW
437 #undef LOGE
438 #undef LOGV
439 #undef LOG_INTERNAL
441 } // namespace mozilla