EncoderAgent.cpp (16040B)
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 "EncoderAgent.h" 8 9 #include "PDMFactory.h" 10 #include "mozilla/DebugOnly.h" 11 #include "mozilla/Logging.h" 12 #include "nsThreadUtils.h" 13 14 extern mozilla::LazyLogModule gWebCodecsLog; 15 16 namespace mozilla { 17 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__)) 23 24 #ifdef LOG 25 # undef LOG 26 #endif // LOG 27 #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) 28 29 #ifdef LOGW 30 # undef LOGW 31 #endif // LOGE 32 #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__) 33 34 #ifdef LOGE 35 # undef LOGE 36 #endif // LOGE 37 #define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__) 38 39 #ifdef LOGV 40 # undef LOGV 41 #endif // LOGV 42 #define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__) 43 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); 53 } 54 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"); 59 } 60 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()); 68 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__); 75 } 76 77 MOZ_ASSERT(mState == State::Unconfigured); 78 MOZ_ASSERT(!mEncoder); 79 SetState(State::Configuring); 80 81 LOG("EncoderAgent #%zu (%p) is creating an encoder (%s)", mId, this, 82 mozilla::EnumValueToString(aConfig.mCodec)); 83 84 RefPtr<ConfigurePromise> p = mConfigurePromise.Ensure(__func__); 85 86 mPEMFactory->CreateEncoderAsync(aConfig, dom::GetWebCodecsEncoderTaskQueue()) 87 ->Then( 88 mOwnerThread, __func__, 89 [self = RefPtr{this}](RefPtr<MediaDataEncoder>&& aEncoder) { 90 self->mCreateRequest.Complete(); 91 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"); 98 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); 107 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"); 114 115 self->SetState(State::Unconfigured); 116 117 self->mShutdownWhileCreationPromise.ResolveOrReject( 118 aValue, __func__); 119 }); 120 return; 121 } 122 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__); 136 }, 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__); 145 }) 146 ->Track(self->mInitRequest); 147 }, 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()); 152 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"); 159 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()); 164 165 self->SetState(State::Unconfigured); 166 self->mShutdownWhileCreationPromise.Resolve(true, __func__); 167 return; 168 } 169 170 self->SetState(State::Error); 171 self->mConfigurePromise.Reject(aError, __func__); 172 }) 173 ->Track(mCreateRequest); 174 175 return p; 176 } 177 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()); 183 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__); 191 } 192 193 MOZ_ASSERT(mEncoder); 194 SetState(State::Configuring); 195 196 LOG("EncoderAgent #%zu (%p) is reconfiguring its encoder (%s)", mId, this, 197 NS_ConvertUTF16toUTF8(aConfigChanges->ToString().get()).get()); 198 199 RefPtr<ReconfigurationPromise> p = mReconfigurationPromise.Ensure(__func__); 200 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__); 210 }, 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__); 217 }) 218 ->Track(mReconfigurationRequest); 219 220 return p; 221 } 222 223 RefPtr<ShutdownPromise> EncoderAgent::Shutdown() { 224 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread()); 225 226 LOG("EncoderAgent #%zu (%p) shutdown in %s state", mId, this, 227 EncoderAgent::EnumValueToString(mState)); 228 229 MOZ_ASSERT(mShutdownWhileCreationPromise.IsEmpty(), 230 "Shutdown while shutting down is prohibited"); 231 232 auto r = 233 MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, "Canceled by encoder shutdown"); 234 235 // If the encoder creation has not been completed yet, wait until the encoder 236 // being created has been shut down. 237 if (mCreateRequest.Exists()) { 238 MOZ_ASSERT(!mInitRequest.Exists()); 239 MOZ_ASSERT(!mConfigurePromise.IsEmpty()); 240 MOZ_ASSERT(!mEncoder); 241 MOZ_ASSERT(mState == State::Configuring); 242 243 LOGW( 244 "EncoderAgent #%zu (%p) shutdown while the encoder creation for " 245 "configuration is in flight. Reject the configuration now and defer " 246 "the shutdown until the created encoder has been shut down", 247 mId, this); 248 249 // Reject the configuration in flight. 250 mConfigurePromise.Reject(r, __func__); 251 252 // Get the promise that will be resolved when the encoder being created has 253 // been destroyed. 254 SetState(State::ShuttingDown); 255 return mShutdownWhileCreationPromise.Ensure(__func__); 256 } 257 258 // If encoder creation has been completed but failed, no encoder is set. 259 if (!mEncoder) { 260 LOG("EncoderAgent #%zu (%p) shutdown without an active encoder", mId, this); 261 MOZ_ASSERT(mState == State::Error); 262 MOZ_ASSERT(!mInitRequest.Exists()); 263 MOZ_ASSERT(mConfigurePromise.IsEmpty()); 264 MOZ_ASSERT(!mReconfigurationRequest.Exists()); 265 MOZ_ASSERT(mReconfigurationPromise.IsEmpty()); 266 MOZ_ASSERT(!mEncodeRequest.Exists()); 267 MOZ_ASSERT(mEncodePromise.IsEmpty()); 268 MOZ_ASSERT(!mDrainRequest.Exists()); 269 MOZ_ASSERT(mDrainPromise.IsEmpty()); 270 // ~EncoderAgent() will ensure that the encoder is shutdown. 271 SetState(State::Unconfigured); 272 return ShutdownPromise::CreateAndResolve(true, __func__); 273 } 274 275 // If encoder creation has succeeded, we must have the encoder now. 276 277 // Cancel pending initialization for configuration in flight if any. 278 mInitRequest.DisconnectIfExists(); 279 mConfigurePromise.RejectIfExists(r, __func__); 280 281 mReconfigurationRequest.DisconnectIfExists(); 282 mReconfigurationPromise.RejectIfExists(r, __func__); 283 284 // Cancel encode in flight if any. 285 mEncodeRequest.DisconnectIfExists(); 286 mEncodePromise.RejectIfExists(r, __func__); 287 288 // Cancel drain in flight if any. 289 mDrainRequest.DisconnectIfExists(); 290 mDrainPromise.RejectIfExists(r, __func__); 291 292 SetState(State::Unconfigured); 293 294 RefPtr<MediaDataEncoder> encoder = std::move(mEncoder); 295 return encoder->Shutdown(); 296 } 297 298 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Encode( 299 nsTArray<RefPtr<MediaData>>&& aInputs) { 300 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread()); 301 MOZ_ASSERT(!aInputs.IsEmpty()); 302 MOZ_ASSERT(mState == State::Configured || mState == State::Error); 303 MOZ_ASSERT(mEncodePromise.IsEmpty()); 304 MOZ_ASSERT(!mEncodeRequest.Exists()); 305 306 if (mState == State::Error) { 307 LOGE("EncoderAgent #%zu (%p) tried to encode in error state", mId, this); 308 return EncodePromise::CreateAndReject( 309 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 310 "Cannot encode in error state"), 311 __func__); 312 } 313 314 MOZ_ASSERT(mState == State::Configured); 315 MOZ_ASSERT(mEncoder); 316 SetState(State::Encoding); 317 318 RefPtr<EncodePromise> p = mEncodePromise.Ensure(__func__); 319 320 LOGV("EncoderAgent #%zu (%p) is encoding %zu samples", mId, this, 321 aInputs.Length()); 322 mEncoder->Encode(std::move(aInputs)) 323 ->Then( 324 mOwnerThread, __func__, 325 [self = RefPtr{this}](MediaDataEncoder::EncodedData&& aData) { 326 self->mEncodeRequest.Complete(); 327 LOGV("EncoderAgent #%zu (%p) encode a batch successful", self->mId, 328 self.get()); 329 self->SetState(State::Configured); 330 self->mEncodePromise.Resolve(std::move(aData), __func__); 331 }, 332 [self = RefPtr{this}](const MediaResult& aError) { 333 self->mEncodeRequest.Complete(); 334 LOGV("EncoderAgent #%zu (%p) failed to encode a batch", self->mId, 335 self.get()); 336 self->SetState(State::Error); 337 self->mEncodePromise.Reject(aError, __func__); 338 }) 339 ->Track(mEncodeRequest); 340 341 return p; 342 } 343 344 RefPtr<EncoderAgent::EncodePromise> EncoderAgent::Drain() { 345 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread()); 346 // This can be called when reconfiguring the encoder. 347 MOZ_ASSERT(mState == State::Configured || mState == State::Configuring); 348 MOZ_ASSERT(mDrainPromise.IsEmpty()); 349 MOZ_ASSERT(mEncoder); 350 351 SetState(State::Draining); 352 353 RefPtr<EncodePromise> p = mDrainPromise.Ensure(__func__); 354 Dry(MediaDataEncoder::EncodedData()); 355 return p; 356 } 357 358 void EncoderAgent::Dry(MediaDataEncoder::EncodedData&& aPendingOutputs) { 359 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread()); 360 MOZ_ASSERT(mState == State::Draining); 361 MOZ_ASSERT(!mDrainPromise.IsEmpty()); 362 MOZ_ASSERT(!mDrainRequest.Exists()); 363 MOZ_ASSERT(mEncoder); 364 365 LOG("EncoderAgent #%zu (%p) is draining the encoder", mId, this); 366 mEncoder->Drain() 367 ->Then( 368 mOwnerThread, __func__, 369 [self = RefPtr{this}, outputs = std::move(aPendingOutputs)]( 370 MediaDataEncoder::EncodedData&& aData) mutable { 371 self->mDrainRequest.Complete(); 372 373 if (aData.IsEmpty()) { 374 LOG("EncoderAgent #%zu (%p) is dry now", self->mId, self.get()); 375 self->SetState(State::Configured); 376 self->mDrainPromise.Resolve(std::move(outputs), __func__); 377 return; 378 } 379 380 LOG("EncoderAgent #%zu (%p) drained %zu encoder data. Keep " 381 "draining until dry", 382 self->mId, self.get(), aData.Length()); 383 outputs.AppendElements(std::move(aData)); 384 self->Dry(std::move(outputs)); 385 }, 386 [self = RefPtr{this}](const MediaResult& aError) { 387 self->mDrainRequest.Complete(); 388 389 LOGE("EncoderAgent %p failed to drain encoder", self.get()); 390 self->mDrainPromise.Reject(aError, __func__); 391 }) 392 ->Track(mDrainRequest); 393 } 394 395 void EncoderAgent::SetState(State aState) { 396 MOZ_ASSERT(mOwnerThread->IsOnCurrentThread()); 397 398 auto validateStateTransition = [](State aOldState, State aNewState) { 399 switch (aOldState) { 400 case State::Unconfigured: 401 return aNewState == State::Configuring; 402 case State::Configuring: 403 return aNewState == State::Configured || aNewState == State::Error || 404 aNewState == State::Draining || 405 aNewState == State::Unconfigured || 406 aNewState == State::ShuttingDown; 407 case State::Configured: 408 return aNewState == State::Unconfigured || 409 aNewState == State::Configuring || 410 aNewState == State::Encoding || aNewState == State::Draining; 411 case State::Encoding: 412 case State::Draining: 413 return aNewState == State::Configured || aNewState == State::Error || 414 aNewState == State::Unconfigured; 415 case State::ShuttingDown: 416 return aNewState == State::Unconfigured; 417 case State::Error: 418 return aNewState == State::Unconfigured; 419 default: 420 break; 421 } 422 MOZ_ASSERT_UNREACHABLE("Unhandled state transition"); 423 return false; 424 }; 425 426 DebugOnly<bool> isValid = validateStateTransition(mState, aState); 427 LOGV("EncoderAgent #%zu (%p) state change: %s -> %s", mId, this, 428 EncoderAgent::EnumValueToString(mState), 429 EncoderAgent::EnumValueToString(aState)); 430 MOZ_ASSERT(isValid); 431 mState = aState; 432 } 433 434 #undef LOG 435 #undef LOGW 436 #undef LOGE 437 #undef LOGV 438 #undef LOG_INTERNAL 439 440 } // namespace mozilla