RemoteMediaDataEncoderChild.cpp (19770B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 "RemoteMediaDataEncoderChild.h" 8 9 #include "RemoteDecodeUtils.h" 10 #include "RemoteMediaManagerChild.h" 11 #include "mozilla/dom/WebCodecsUtils.h" 12 13 namespace mozilla { 14 15 extern LazyLogModule sPEMLog; 16 17 #define AUTO_MARKER(var, postfix) \ 18 AutoWebCodecsMarker var("RemoteMediaDataEncoderChild", postfix); 19 20 #define LOGE(fmt, ...) \ 21 MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Error, \ 22 "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__) 23 #define LOGW(fmt, ...) \ 24 MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Warning, \ 25 "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__) 26 #define LOGD(fmt, ...) \ 27 MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Debug, \ 28 "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__) 29 #define LOGV(fmt, ...) \ 30 MOZ_LOG_FMT(sPEMLog, mozilla::LogLevel::Verbose, \ 31 "[RemoteMediaDataEncoderChild] {}: " fmt, __func__, __VA_ARGS__) 32 33 RemoteMediaDataEncoderChild::RemoteMediaDataEncoderChild( 34 nsCOMPtr<nsISerialEventTarget>&& aThread, RemoteMediaIn aLocation) 35 : ShmemRecycleAllocator(this), 36 mThread(std::move(aThread)), 37 mLocation(aLocation) { 38 LOGV("[{}]", fmt::ptr(this)); 39 } 40 41 RemoteMediaDataEncoderChild::~RemoteMediaDataEncoderChild() { 42 LOGV("[{}]", fmt::ptr(this)); 43 } 44 45 void RemoteMediaDataEncoderChild::MaybeDestroyActor() { 46 // If this is the last reference, and we still have an actor, then we know 47 // that the last reference is solely due to the IPDL reference. Dispatch to 48 // the owning thread to delete that so that we can clean up. 49 MutexAutoLock lock(mMutex); 50 if (mNeedsShutdown) { 51 mNeedsShutdown = false; 52 mThread->Dispatch(NS_NewRunnableFunction(__func__, [self = RefPtr{this}]() { 53 if (self->CanSend()) { 54 LOGD("[{}] destroying final self reference", fmt::ptr(self.get())); 55 self->Send__delete__(self); 56 } 57 })); 58 } 59 } 60 61 void RemoteMediaDataEncoderChild::ActorDestroy(ActorDestroyReason aWhy) { 62 LOGD("[{}]", fmt::ptr(this)); 63 64 { 65 MutexAutoLock lock(mMutex); 66 mNeedsShutdown = false; 67 } 68 69 mRemoteCrashed = aWhy == ActorDestroyReason::AbnormalShutdown; 70 CleanupShmemRecycleAllocator(); 71 } 72 73 RefPtr<PlatformEncoderModule::CreateEncoderPromise> 74 RemoteMediaDataEncoderChild::Construct() { 75 { 76 MutexAutoLock lock(mMutex); 77 mNeedsShutdown = CanSend(); 78 } 79 80 LOGD("[{}] send", fmt::ptr(this)); 81 SendConstruct()->Then( 82 mThread, __func__, 83 [self = RefPtr{this}](MediaResult aResult) { 84 LOGD("[{}] Construct resolved code={}", fmt::ptr(self.get()), 85 aResult.Description()); 86 self->mHasConstructed = true; 87 self->mConstructPromise.Resolve(self, __func__); 88 if (!self->mInitPromise.IsEmpty()) { 89 self->DoSendInit(); 90 } 91 }, 92 [self = RefPtr{this}](const mozilla::ipc::ResponseRejectReason& aReason) { 93 LOGE("[{}] Construct ipc failed", fmt::ptr(self.get())); 94 RemoteMediaManagerChild::HandleRejectionError( 95 self->GetManager(), self->mLocation, aReason, 96 [self](const MediaResult& aError) { 97 self->mConstructPromise.RejectIfExists(aError, __func__); 98 self->mInitPromise.RejectIfExists(aError, __func__); 99 }); 100 }); 101 return mConstructPromise.Ensure(__func__); 102 } 103 104 void RemoteMediaDataEncoderChild::DoSendInit() { 105 MOZ_ASSERT(mHasConstructed); 106 107 LOGD("[{}] Init send", fmt::ptr(this)); 108 SendInit()->Then( 109 mThread, __func__, 110 [self = RefPtr{this}](EncodeInitResultIPDL&& aResponse) { 111 if (aResponse.type() == EncodeInitResultIPDL::TMediaResult) { 112 LOGE("[{}] Init resolved code={}", fmt::ptr(self.get()), 113 aResponse.get_MediaResult().Description()); 114 self->mInitPromise.Reject(aResponse.get_MediaResult(), __func__); 115 return; 116 } 117 118 const auto& initResponse = aResponse.get_EncodeInitCompletionIPDL(); 119 120 LOGD("[{}] Init resolved hwAccel={} desc=\"{}\"", fmt::ptr(self.get()), 121 initResponse.hardware(), initResponse.description().get()); 122 MutexAutoLock lock(self->mMutex); 123 self->mDescription = initResponse.description(); 124 self->mDescription.AppendFmt( 125 " ({})", RemoteMediaInToStr(self->GetManager()->Location())); 126 127 self->mIsHardwareAccelerated = initResponse.hardware(); 128 self->mHardwareAcceleratedReason = initResponse.hardwareReason(); 129 self->mInitPromise.ResolveIfExists(true, __func__); 130 }, 131 [self = RefPtr{this}](const mozilla::ipc::ResponseRejectReason& aReason) { 132 LOGE("[{}] Init ipc failed", fmt::ptr(self.get())); 133 RemoteMediaManagerChild::HandleRejectionError( 134 self->GetManager(), self->mLocation, aReason, 135 [self](const MediaResult& aError) { 136 self->mInitPromise.RejectIfExists(aError, __func__); 137 }); 138 }); 139 } 140 141 RefPtr<MediaDataEncoder::InitPromise> RemoteMediaDataEncoderChild::Init() { 142 return InvokeAsync( 143 mThread, __func__, 144 [self = RefPtr{this}]() -> RefPtr<MediaDataEncoder::InitPromise> { 145 // If the owner called Init before the Construct response, then just 146 // create promise and wait for that first. This can happen if the owner 147 // created the encoder via RemoteEncoderModule's CreateAudioEncoder or 148 // CreateVideoEncoder instead of AsyncCreateEncoder. 149 // 150 // mConstructPromise might not have been created yet either because we 151 // may have delayed dispatching related to the process launching. 152 // mHasConstructed will be set when the construct IPDL call returns. 153 if (self->mHasConstructed) { 154 self->DoSendInit(); 155 } else { 156 LOGD("[{}] Init deferred, still constructing", fmt::ptr(self.get())); 157 } 158 return self->mInitPromise.Ensure(__func__); 159 }); 160 } 161 162 RefPtr<PRemoteEncoderChild::EncodePromise> 163 RemoteMediaDataEncoderChild::DoSendEncode( 164 const nsTArray<RefPtr<MediaData>>& aSamples, ShmemRecycleTicket* aTicket) { 165 if (mRemoteCrashed) { 166 LOGE("[{}] remote crashed", fmt::ptr(this)); 167 nsresult err = NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_UTILITY_ERR; 168 if (mLocation == RemoteMediaIn::GpuProcess || 169 mLocation == RemoteMediaIn::RddProcess) { 170 err = NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_RDD_OR_GPU_ERR; 171 } else if (mLocation == RemoteMediaIn::UtilityProcess_MFMediaEngineCDM) { 172 err = NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_MF_CDM_ERR; 173 } 174 return PRemoteEncoderChild::EncodePromise::CreateAndResolve( 175 MediaResult(err, "Remote process crashed"), __func__); 176 } 177 178 if (aSamples.IsEmpty()) { 179 LOGE("[{}] no samples to encode", fmt::ptr(this)); 180 return PRemoteEncoderChild::EncodePromise::CreateAndResolve( 181 MediaResult(NS_ERROR_INVALID_ARG), __func__); 182 } 183 184 MediaData::Type type = aSamples[0]->mType; 185 186 if (type == MediaData::Type::AUDIO_DATA) { 187 nsTArray<RefPtr<AudioData>> audioSamples; 188 for (auto& sample : aSamples) { 189 audioSamples.AppendElement(sample->As<AudioData>()); 190 } 191 192 auto samples = MakeRefPtr<ArrayOfRemoteAudioData>(); 193 if (!samples->Fill(audioSamples, [&](size_t aSize) { 194 return AllocateBuffer(aSize, aTicket); 195 })) { 196 LOGE("[{}] buffer audio failed", fmt::ptr(this)); 197 return PRemoteEncoderChild::EncodePromise::CreateAndResolve( 198 MediaResult(NS_ERROR_OUT_OF_MEMORY), __func__); 199 } 200 LOGD("[{}] send {} audio samples", fmt::ptr(this), audioSamples.Length()); 201 return SendEncode(std::move(samples)); 202 } 203 204 if (type == MediaData::Type::VIDEO_DATA) { 205 nsTArray<RefPtr<VideoData>> videoSamples; 206 for (auto& sample : aSamples) { 207 videoSamples.AppendElement(sample->As<VideoData>()); 208 } 209 210 auto samples = MakeRefPtr<ArrayOfRemoteVideoData>(); 211 for (const auto& videoSample : videoSamples) { 212 if (layers::Image* videoImage = videoSample->mImage) { 213 // We don't need to supply a working deallocator because the ticket is 214 // responsible for that cleanup. 215 layers::SurfaceDescriptor sd; 216 nsresult rv = videoImage->BuildSurfaceDescriptorGPUVideoOrBuffer( 217 sd, layers::Image::BuildSdbFlags::Default, 218 Some(GetVideoBridgeSourceFromRemoteMediaIn(mLocation)), 219 [&](uint32_t aBufferSize) { 220 ShmemBuffer buffer = AllocateBuffer(aBufferSize, aTicket); 221 if (buffer.Valid()) { 222 return layers::MemoryOrShmem(std::move(buffer.Get())); 223 } 224 return layers::MemoryOrShmem(); 225 }, 226 [&](layers::MemoryOrShmem&&) {}); 227 228 if (NS_WARN_IF(NS_FAILED(rv))) { 229 LOGE("[{}] buffer video failed, code={}", fmt::ptr(this), 230 fmt::enums::format_as(rv)); 231 return PRemoteEncoderChild::EncodePromise::CreateAndResolve( 232 MediaResult(rv), __func__); 233 } 234 235 samples->Append(RemoteVideoData( 236 MediaDataIPDL(videoSample->mOffset, videoSample->mTime, 237 videoSample->mTimecode, videoSample->mDuration, 238 videoSample->mKeyframe), 239 videoSample->mDisplay, RemoteImageHolder(std::move(sd)), 240 videoSample->mFrameID)); 241 } 242 } 243 LOGD("[{}] send {} video samples", fmt::ptr(this), videoSamples.Length()); 244 return SendEncode(std::move(samples)); 245 } 246 247 return PRemoteEncoderChild::EncodePromise::CreateAndResolve( 248 MediaResult(NS_ERROR_INVALID_ARG), __func__); 249 } 250 251 RefPtr<MediaDataEncoder::EncodePromise> RemoteMediaDataEncoderChild::Encode( 252 const MediaData* aSample) { 253 return Encode(nsTArray<RefPtr<MediaData>>{const_cast<MediaData*>(aSample)}); 254 } 255 256 RefPtr<MediaDataEncoder::EncodePromise> RemoteMediaDataEncoderChild::Encode( 257 nsTArray<RefPtr<MediaData>>&& aSamples) { 258 return InvokeAsync( 259 mThread, __func__, 260 [self = RefPtr{this}, samples = std::move(aSamples)]() 261 -> RefPtr<MediaDataEncoder::EncodePromise> { 262 auto promise = 263 MakeRefPtr<MediaDataEncoder::EncodePromise::Private>(__func__); 264 auto ticket = MakeRefPtr<ShmemRecycleTicket>(); 265 self->DoSendEncode(samples, ticket) 266 ->Then( 267 self->mThread, __func__, 268 [self, promise, ticket](EncodeResultIPDL&& aResponse) { 269 self->ReleaseTicket(ticket); 270 271 if (aResponse.type() == EncodeResultIPDL::TMediaResult) { 272 LOGD("[{}] Encode resolved, code={}", fmt::ptr(self.get()), 273 aResponse.get_MediaResult().Description()); 274 promise->Reject(aResponse.get_MediaResult(), __func__); 275 return; 276 } 277 278 const auto& encodeResponse = 279 aResponse.get_EncodeCompletionIPDL(); 280 281 nsTArray<RefPtr<MediaRawData>> samples; 282 if (auto remoteSamples = encodeResponse.samples()) { 283 size_t count = remoteSamples->Count(); 284 samples.SetCapacity(count); 285 for (size_t i = 0; i < count; ++i) { 286 AUTO_MARKER(marker, ".Encode.ElementAt"); 287 if (RefPtr<MediaRawData> sample = 288 remoteSamples->ElementAt(i)) { 289 marker.End(); 290 samples.AppendElement(std::move(sample)); 291 } else { 292 LOGE( 293 "[{}] Encode resolved, failed to buffer " 294 "samples", 295 fmt::ptr(self.get())); 296 promise->Reject(MediaResult(NS_ERROR_OUT_OF_MEMORY), 297 __func__); 298 return; 299 } 300 } 301 } 302 303 LOGV("[{}] Encode resolved, {} samples", fmt::ptr(self.get()), 304 samples.Length()); 305 promise->Resolve(std::move(samples), __func__); 306 self->SendReleaseTicket(encodeResponse.ticketId()); 307 }, 308 [self, promise, 309 ticket](const mozilla::ipc::ResponseRejectReason& aReason) { 310 LOGE("[{}] Encode ipc failed", fmt::ptr(self.get())); 311 self->ReleaseTicket(ticket); 312 RemoteMediaManagerChild::HandleRejectionError( 313 self->GetManager(), self->mLocation, aReason, 314 [promise](const MediaResult& aError) { 315 promise->Reject(aError, __func__); 316 }); 317 }); 318 return promise; 319 }); 320 } 321 322 RefPtr<MediaDataEncoder::EncodePromise> RemoteMediaDataEncoderChild::Drain() { 323 return InvokeAsync( 324 mThread, __func__, 325 [self = RefPtr{this}]() -> RefPtr<MediaDataEncoder::EncodePromise> { 326 LOGD("[{}] Drain send", fmt::ptr(self.get())); 327 self->SendDrain()->Then( 328 self->mThread, __func__, 329 [self](EncodeResultIPDL&& aResponse) { 330 if (aResponse.type() == EncodeResultIPDL::TMediaResult) { 331 LOGE("[{}] Drain resolved, code={}", fmt::ptr(self.get()), 332 aResponse.get_MediaResult().Description()); 333 self->mDrainPromise.Reject(aResponse.get_MediaResult(), 334 __func__); 335 return; 336 } 337 338 const auto& encodeResponse = aResponse.get_EncodeCompletionIPDL(); 339 340 nsTArray<RefPtr<MediaRawData>> samples; 341 if (auto remoteSamples = encodeResponse.samples()) { 342 size_t count = remoteSamples->Count(); 343 samples.SetCapacity(count); 344 for (size_t i = 0; i < count; ++i) { 345 if (RefPtr<MediaRawData> sample = 346 remoteSamples->ElementAt(i)) { 347 samples.AppendElement(std::move(sample)); 348 } else { 349 LOGE("[{}] Drain resolved, failed to buffer samples", 350 fmt::ptr(self.get())); 351 self->mDrainPromise.Reject( 352 MediaResult(NS_ERROR_OUT_OF_MEMORY), __func__); 353 return; 354 } 355 } 356 } 357 358 LOGD("[{}] Drain resolved, {} samples", fmt::ptr(self.get()), 359 samples.Length()); 360 self->mDrainPromise.Resolve(std::move(samples), __func__); 361 self->SendReleaseTicket(encodeResponse.ticketId()); 362 }, 363 [self](const mozilla::ipc::ResponseRejectReason& aReason) { 364 LOGE("[{}] Drain ipc failed", fmt::ptr(self.get())); 365 RemoteMediaManagerChild::HandleRejectionError( 366 self->GetManager(), self->mLocation, aReason, 367 [self](const MediaResult& aError) { 368 self->mDrainPromise.RejectIfExists(aError, __func__); 369 }); 370 }); 371 return self->mDrainPromise.Ensure(__func__); 372 }); 373 } 374 375 RefPtr<MediaDataEncoder::ReconfigurationPromise> 376 RemoteMediaDataEncoderChild::Reconfigure( 377 const RefPtr<const EncoderConfigurationChangeList>& aConfigurationChanges) { 378 return InvokeAsync( 379 mThread, __func__, 380 [self = RefPtr{this}, changes = RefPtr{aConfigurationChanges}]() 381 -> RefPtr<MediaDataEncoder::ReconfigurationPromise> { 382 LOGD("[{}] Reconfigure send", fmt::ptr(self.get())); 383 self->SendReconfigure( 384 const_cast<EncoderConfigurationChangeList*>(changes.get())) 385 ->Then( 386 self->mThread, __func__, 387 [self](const MediaResult& aResult) { 388 if (NS_SUCCEEDED(aResult)) { 389 LOGD("[{}] Reconfigure resolved", fmt::ptr(self.get())); 390 self->mReconfigurePromise.ResolveIfExists(true, __func__); 391 } else { 392 LOGD("[{}] Reconfigure resolved, code={}", 393 fmt::ptr(self.get()), aResult.Description()); 394 self->mReconfigurePromise.RejectIfExists(aResult, __func__); 395 } 396 }, 397 [self](const mozilla::ipc::ResponseRejectReason& aReason) { 398 LOGE("[{}] Reconfigure ipc failed", fmt::ptr(self.get())); 399 RemoteMediaManagerChild::HandleRejectionError( 400 self->GetManager(), self->mLocation, aReason, 401 [self](const MediaResult& aError) { 402 self->mReconfigurePromise.RejectIfExists(aError, 403 __func__); 404 }); 405 }); 406 return self->mReconfigurePromise.Ensure(__func__); 407 }); 408 } 409 410 RefPtr<mozilla::ShutdownPromise> RemoteMediaDataEncoderChild::Shutdown() { 411 { 412 MutexAutoLock lock(mMutex); 413 mNeedsShutdown = false; 414 } 415 416 return InvokeAsync( 417 mThread, __func__, 418 [self = RefPtr{this}]() -> RefPtr<mozilla::ShutdownPromise> { 419 LOGD("[{}] Shutdown send", fmt::ptr(self.get())); 420 self->SendShutdown()->Then( 421 self->mThread, __func__, 422 [self](PRemoteEncoderChild::ShutdownPromise::ResolveOrRejectValue&& 423 aValue) { 424 LOGD("[{}] Shutdown resolved", fmt::ptr(self.get())); 425 if (self->CanSend()) { 426 self->Send__delete__(self); 427 } 428 self->mShutdownPromise.Resolve(aValue.IsResolve(), __func__); 429 }); 430 return self->mShutdownPromise.Ensure(__func__); 431 }); 432 } 433 434 RefPtr<GenericPromise> RemoteMediaDataEncoderChild::SetBitrate( 435 uint32_t aBitsPerSec) { 436 return InvokeAsync( 437 mThread, __func__, 438 [self = RefPtr{this}, aBitsPerSec]() -> RefPtr<GenericPromise> { 439 auto promise = MakeRefPtr<GenericPromise::Private>(__func__); 440 self->SendSetBitrate(aBitsPerSec) 441 ->Then( 442 self->mThread, __func__, 443 [promise](const nsresult& aRv) { 444 if (NS_SUCCEEDED(aRv)) { 445 promise->Resolve(true, __func__); 446 } else { 447 promise->Reject(aRv, __func__); 448 } 449 }, 450 [self, 451 promise](const mozilla::ipc::ResponseRejectReason& aReason) { 452 LOGE("[{}] SetBitrate ipc failed", fmt::ptr(self.get())); 453 RemoteMediaManagerChild::HandleRejectionError( 454 self->GetManager(), self->mLocation, aReason, 455 [promise](const MediaResult& aError) { 456 promise->Reject(aError.Code(), __func__); 457 }); 458 }); 459 return promise.forget(); 460 }); 461 } 462 463 RemoteMediaManagerChild* RemoteMediaDataEncoderChild::GetManager() { 464 if (!CanSend()) { 465 return nullptr; 466 } 467 return static_cast<RemoteMediaManagerChild*>(Manager()); 468 } 469 470 bool RemoteMediaDataEncoderChild::IsHardwareAccelerated( 471 nsACString& aFailureReason) const { 472 MutexAutoLock lock(mMutex); 473 aFailureReason = mHardwareAcceleratedReason; 474 return mIsHardwareAccelerated; 475 } 476 477 nsCString RemoteMediaDataEncoderChild::GetDescriptionName() const { 478 MutexAutoLock lock(mMutex); 479 return mDescription; 480 } 481 482 #undef AUTO_MARKER 483 484 } // namespace mozilla