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
;
20 #endif // LOG_INTERNAL
21 #define LOG_INTERNAL(level, msg, ...) \
22 MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))
27 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)
32 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)
37 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)
42 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)
44 EncoderAgent::EncoderAgent(WebCodecsId aId
)
46 mOwnerThread(GetCurrentSerialEventTarget()),
47 mPEMFactory(MakeRefPtr
<PEMFactory
>()),
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"),
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())
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
94 if (!self
->mShutdownWhileCreationPromise
.IsEmpty()) {
95 MOZ_ASSERT(self
->mState
== State::ShuttingDown
);
96 MOZ_ASSERT(self
->mConfigurePromise
.IsEmpty(),
97 "configuration should have been rejected");
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
);
109 "EncoderAgent #%zu (%p), newly created encoder "
112 self
->mId
, self
.get(),
113 aValue
.IsResolve() ? "resolved" : "rejected");
115 self
->SetState(State::Unconfigured
);
117 self
->mShutdownWhileCreationPromise
.ResolveOrReject(
123 self
->mEncoder
= aEncoder
.forget();
124 LOG("EncoderAgent #%zu (%p) has created a encoder, now initialize "
126 self
->mId
, self
.get());
127 self
->mEncoder
->Init()
129 self
->mOwnerThread
, __func__
,
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();
140 "EncoderAgent #%zu (%p) failed to initialize the "
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
,
153 // If EncoderAgent has been shut down, we need to resolve the
155 if (!self
->mShutdownWhileCreationPromise
.IsEmpty()) {
156 MOZ_ASSERT(self
->mState
== State::ShuttingDown
);
157 MOZ_ASSERT(self
->mConfigurePromise
.IsEmpty(),
158 "configuration should have been rejected");
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__
);
170 self
->SetState(State::Error
);
171 self
->mConfigurePromise
.Reject(aError
, __func__
);
173 ->Track(mCreateRequest
);
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
,
187 return ReconfigurationPromise::CreateAndReject(
188 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR
,
189 "Cannot reconfigure in error state"),
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
)
203 mOwnerThread
, __func__
,
204 [self
= RefPtr
{this}](bool) {
205 self
->mReconfigurationRequest
.Complete();
206 LOGE("EncoderAgent #%zu (%p) reconfigure success", self
->mId
,
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
,
215 // Not a a fatal error per se, the owner will deal with it.
216 self
->mReconfigurationPromise
.Reject(aError
, __func__
);
218 ->Track(mReconfigurationRequest
);
223 RefPtr
<ShutdownPromise
> EncoderAgent::Shutdown() {
224 MOZ_ASSERT(mOwnerThread
->IsOnCurrentThread());
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());
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",
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
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());
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"),
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
)
303 mOwnerThread
, __func__
,
304 [self
= RefPtr
{this}](MediaDataEncoder::EncodedData
&& aData
) {
305 self
->mEncodeRequest
.Complete();
306 LOGV("EncoderAgent #%zu (%p) encode successful", self
->mId
,
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
,
315 self
->SetState(State::Error
);
316 self
->mEncodePromise
.Reject(aError
, __func__
);
318 ->Track(mEncodeRequest
);
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__
);
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);
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
),
358 LOG("EncoderAgent #%zu (%p) drained %zu encoder data. Keep "
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
) {
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
;
398 return aNewState
== State::Unconfigured
;
402 MOZ_ASSERT_UNREACHABLE("Unhandled state transition");
406 auto stateToString
= [](State aState
) -> const char* {
408 case State::Unconfigured
:
409 return "Unconfigured";
410 case State::Configuring
:
411 return "Configuring";
412 case State::Configured
:
414 case State::Encoding
:
416 case State::Flushing
:
418 case State::ShuttingDown
:
419 return "ShuttingDown";
425 MOZ_ASSERT_UNREACHABLE("Unhandled state type");
429 DebugOnly
<bool> isValid
= validateStateTransition(mState
, aState
);
430 LOGV("EncoderAgent #%zu (%p) state change: %s -> %s", mId
, this,
431 stateToString(mState
), stateToString(aState
));
442 } // namespace mozilla