MediaEncoder.cpp (37384B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "MediaEncoder.h" 7 8 #include <algorithm> 9 10 #include "AudioNodeEngine.h" 11 #include "AudioNodeTrack.h" 12 #include "DriftCompensation.h" 13 #include "MediaDecoder.h" 14 #include "MediaTrackGraph.h" 15 #include "MediaTrackListener.h" 16 #include "Muxer.h" 17 #include "OggWriter.h" 18 #include "OpusTrackEncoder.h" 19 #include "TimeUnits.h" 20 #include "Tracing.h" 21 #include "VP8TrackEncoder.h" 22 #include "WebMWriter.h" 23 #include "mozilla/Logging.h" 24 #include "mozilla/Preferences.h" 25 #include "mozilla/ProfilerLabels.h" 26 #include "mozilla/StaticPrefs_media.h" 27 #include "mozilla/StaticPtr.h" 28 #include "mozilla/TaskQueue.h" 29 #include "mozilla/dom/AudioNode.h" 30 #include "mozilla/dom/AudioStreamTrack.h" 31 #include "mozilla/dom/Blob.h" 32 #include "mozilla/dom/BlobImpl.h" 33 #include "mozilla/dom/MediaStreamTrack.h" 34 #include "mozilla/dom/MutableBlobStorage.h" 35 #include "mozilla/dom/VideoStreamTrack.h" 36 #include "mozilla/gfx/Point.h" // IntSize 37 #include "nsMimeTypes.h" 38 #include "nsThreadUtils.h" 39 40 mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder"); 41 #define LOG(type, msg) MOZ_LOG(gMediaEncoderLog, type, msg) 42 43 namespace mozilla { 44 45 using namespace dom; 46 using namespace media; 47 48 namespace { 49 class BlobStorer : public MutableBlobStorageCallback { 50 MozPromiseHolder<MediaEncoder::BlobPromise> mHolder; 51 52 virtual ~BlobStorer() = default; 53 54 public: 55 BlobStorer() = default; 56 57 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobStorer, override) 58 59 void BlobStoreCompleted(MutableBlobStorage*, BlobImpl* aBlobImpl, 60 nsresult aRv) override { 61 MOZ_ASSERT(NS_IsMainThread()); 62 if (NS_FAILED(aRv)) { 63 mHolder.Reject(aRv, __func__); 64 return; 65 } 66 67 mHolder.Resolve(aBlobImpl, __func__); 68 } 69 70 RefPtr<MediaEncoder::BlobPromise> Promise() { 71 return mHolder.Ensure(__func__); 72 } 73 }; 74 } // namespace 75 76 class MediaEncoder::AudioTrackListener : public DirectMediaTrackListener { 77 public: 78 AudioTrackListener(RefPtr<DriftCompensator> aDriftCompensator, 79 RefPtr<MediaEncoder> aMediaEncoder) 80 : mDirectConnected(false), 81 mInitialized(false), 82 mRemoved(false), 83 mDriftCompensator(std::move(aDriftCompensator)), 84 mMediaEncoder(std::move(aMediaEncoder)), 85 mEncoderThread(mMediaEncoder->mEncoderThread), 86 mShutdownPromise(mShutdownHolder.Ensure(__func__)) { 87 MOZ_ASSERT(mMediaEncoder); 88 MOZ_ASSERT(mMediaEncoder->mAudioEncoder); 89 MOZ_ASSERT(mEncoderThread); 90 } 91 92 void NotifyDirectListenerInstalled(InstallationResult aResult) override { 93 if (aResult == InstallationResult::SUCCESS) { 94 LOG(LogLevel::Info, ("Audio track direct listener installed")); 95 mDirectConnected = true; 96 } else { 97 LOG(LogLevel::Info, ("Audio track failed to install direct listener")); 98 MOZ_ASSERT(!mDirectConnected); 99 } 100 } 101 102 void NotifyDirectListenerUninstalled() override { 103 mDirectConnected = false; 104 105 if (mRemoved) { 106 mMediaEncoder = nullptr; 107 mEncoderThread = nullptr; 108 } 109 } 110 111 void NotifyQueuedChanges(MediaTrackGraph* aGraph, TrackTime aTrackOffset, 112 const MediaSegment& aQueuedMedia) override { 113 TRACE_COMMENT("MediaEncoder::NotifyQueuedChanges", "%p", 114 mMediaEncoder.get()); 115 MOZ_ASSERT(mMediaEncoder); 116 MOZ_ASSERT(mEncoderThread); 117 118 if (!mInitialized) { 119 mDriftCompensator->NotifyAudioStart(TimeStamp::Now()); 120 mInitialized = true; 121 } 122 123 mDriftCompensator->NotifyAudio(aQueuedMedia.GetDuration()); 124 125 const AudioSegment& audio = static_cast<const AudioSegment&>(aQueuedMedia); 126 127 AudioSegment copy; 128 copy.AppendSlice(audio, 0, audio.GetDuration()); 129 130 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 131 "mozilla::AudioTrackEncoder::AppendAudioSegment", 132 [encoder = mMediaEncoder, copy = std::move(copy)]() mutable { 133 encoder->mAudioEncoder->AppendAudioSegment(std::move(copy)); 134 })); 135 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 136 (void)rv; 137 } 138 139 void NotifyEnded(MediaTrackGraph* aGraph) override { 140 MOZ_ASSERT(mMediaEncoder); 141 MOZ_ASSERT(mMediaEncoder->mAudioEncoder); 142 MOZ_ASSERT(mEncoderThread); 143 144 nsresult rv = mEncoderThread->Dispatch( 145 NS_NewRunnableFunction("mozilla::AudioTrackEncoder::NotifyEndOfStream", 146 [encoder = mMediaEncoder] { 147 encoder->mAudioEncoder->NotifyEndOfStream(); 148 })); 149 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 150 (void)rv; 151 } 152 153 void NotifyRemoved(MediaTrackGraph* aGraph) override { 154 nsresult rv = mEncoderThread->Dispatch( 155 NS_NewRunnableFunction("mozilla::AudioTrackEncoder::NotifyEndOfStream", 156 [encoder = mMediaEncoder] { 157 encoder->mAudioEncoder->NotifyEndOfStream(); 158 })); 159 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 160 (void)rv; 161 162 mRemoved = true; 163 164 if (!mDirectConnected) { 165 mMediaEncoder = nullptr; 166 mEncoderThread = nullptr; 167 } 168 169 mShutdownHolder.Resolve(true, __func__); 170 } 171 172 const RefPtr<GenericNonExclusivePromise>& OnShutdown() const { 173 return mShutdownPromise; 174 } 175 176 private: 177 bool mDirectConnected; 178 bool mInitialized; 179 bool mRemoved; 180 const RefPtr<DriftCompensator> mDriftCompensator; 181 RefPtr<MediaEncoder> mMediaEncoder; 182 RefPtr<TaskQueue> mEncoderThread; 183 MozPromiseHolder<GenericNonExclusivePromise> mShutdownHolder; 184 const RefPtr<GenericNonExclusivePromise> mShutdownPromise; 185 }; 186 187 class MediaEncoder::VideoTrackListener : public DirectMediaTrackListener { 188 public: 189 explicit VideoTrackListener(RefPtr<MediaEncoder> aMediaEncoder) 190 : mDirectConnected(false), 191 mInitialized(false), 192 mRemoved(false), 193 mPendingAdvanceCurrentTime(false), 194 mMediaEncoder(std::move(aMediaEncoder)), 195 mEncoderThread(mMediaEncoder->mEncoderThread), 196 mShutdownPromise(mShutdownHolder.Ensure(__func__)) { 197 MOZ_ASSERT(mMediaEncoder); 198 MOZ_ASSERT(mEncoderThread); 199 } 200 201 void NotifyDirectListenerInstalled(InstallationResult aResult) override { 202 if (aResult == InstallationResult::SUCCESS) { 203 LOG(LogLevel::Info, ("Video track direct listener installed")); 204 mDirectConnected = true; 205 } else { 206 LOG(LogLevel::Info, ("Video track failed to install direct listener")); 207 MOZ_ASSERT(!mDirectConnected); 208 return; 209 } 210 } 211 212 void NotifyDirectListenerUninstalled() override { 213 mDirectConnected = false; 214 215 if (mRemoved) { 216 mMediaEncoder = nullptr; 217 mEncoderThread = nullptr; 218 } 219 } 220 221 void NotifyQueuedChanges(MediaTrackGraph* aGraph, TrackTime aTrackOffset, 222 const MediaSegment& aQueuedMedia) override { 223 TRACE_COMMENT("MediaEncoder::NotifyQueuedChanges", "%p", 224 mMediaEncoder.get()); 225 MOZ_ASSERT(mMediaEncoder); 226 MOZ_ASSERT(mMediaEncoder->mVideoEncoder); 227 MOZ_ASSERT(mEncoderThread); 228 229 mCurrentTime = TimeStamp::Now(); 230 if (!mInitialized) { 231 nsresult rv = mEncoderThread->Dispatch( 232 NS_NewRunnableFunction("mozilla::VideoTrackEncoder::SetStartOffset", 233 [encoder = mMediaEncoder, now = mCurrentTime] { 234 encoder->mVideoEncoder->SetStartOffset(now); 235 })); 236 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 237 (void)rv; 238 mInitialized = true; 239 } 240 241 if (!mPendingAdvanceCurrentTime) { 242 mPendingAdvanceCurrentTime = true; 243 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 244 "mozilla::VideoTrackEncoder::AdvanceCurrentTime", 245 [encoder = mMediaEncoder, now = mCurrentTime] { 246 encoder->mVideoListener->mPendingAdvanceCurrentTime = false; 247 encoder->mVideoEncoder->AdvanceCurrentTime(now); 248 })); 249 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 250 (void)rv; 251 } 252 } 253 254 void NotifyRealtimeTrackData(MediaTrackGraph* aGraph, TrackTime aTrackOffset, 255 const MediaSegment& aMedia) override { 256 TRACE_COMMENT("MediaEncoder::NotifyRealtimeTrackData", "%p", 257 mMediaEncoder.get()); 258 MOZ_ASSERT(mMediaEncoder); 259 MOZ_ASSERT(mMediaEncoder->mVideoEncoder); 260 MOZ_ASSERT(mEncoderThread); 261 MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO); 262 263 const VideoSegment& video = static_cast<const VideoSegment&>(aMedia); 264 VideoSegment copy; 265 for (VideoSegment::ConstChunkIterator iter(video); !iter.IsEnded(); 266 iter.Next()) { 267 copy.AppendFrame(do_AddRef(iter->mFrame.GetImage()), 268 iter->mFrame.GetIntrinsicSize(), 269 iter->mFrame.GetPrincipalHandle(), 270 iter->mFrame.GetForceBlack(), iter->mTimeStamp); 271 } 272 273 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 274 "mozilla::VideoTrackEncoder::AppendVideoSegment", 275 [encoder = mMediaEncoder, copy = std::move(copy)]() mutable { 276 encoder->mVideoEncoder->AppendVideoSegment(std::move(copy)); 277 })); 278 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 279 (void)rv; 280 } 281 282 void NotifyEnabledStateChanged(MediaTrackGraph* aGraph, 283 bool aEnabled) override { 284 MOZ_ASSERT(mMediaEncoder); 285 MOZ_ASSERT(mMediaEncoder->mVideoEncoder); 286 MOZ_ASSERT(mEncoderThread); 287 288 nsresult rv; 289 if (aEnabled) { 290 rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 291 "mozilla::VideoTrackEncoder::Enable", 292 [encoder = mMediaEncoder, now = TimeStamp::Now()] { 293 encoder->mVideoEncoder->Enable(now); 294 })); 295 } else { 296 rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 297 "mozilla::VideoTrackEncoder::Disable", 298 [encoder = mMediaEncoder, now = TimeStamp::Now()] { 299 encoder->mVideoEncoder->Disable(now); 300 })); 301 } 302 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 303 (void)rv; 304 } 305 306 void NotifyEnded(MediaTrackGraph* aGraph) override { 307 MOZ_ASSERT(mMediaEncoder); 308 MOZ_ASSERT(mMediaEncoder->mVideoEncoder); 309 MOZ_ASSERT(mEncoderThread); 310 311 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 312 "mozilla::VideoTrackEncoder::NotifyEndOfStream", 313 [encoder = mMediaEncoder, now = mCurrentTime] { 314 if (!now.IsNull()) { 315 encoder->mVideoEncoder->AdvanceCurrentTime(now); 316 } 317 encoder->mVideoEncoder->NotifyEndOfStream(); 318 })); 319 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 320 (void)rv; 321 } 322 323 void NotifyRemoved(MediaTrackGraph* aGraph) override { 324 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction( 325 "mozilla::VideoTrackEncoder::NotifyEndOfStream", 326 [encoder = mMediaEncoder, now = mCurrentTime] { 327 if (!now.IsNull()) { 328 encoder->mVideoEncoder->AdvanceCurrentTime(now); 329 } 330 encoder->mVideoEncoder->NotifyEndOfStream(); 331 })); 332 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 333 (void)rv; 334 335 mRemoved = true; 336 337 if (!mDirectConnected) { 338 mMediaEncoder = nullptr; 339 mEncoderThread = nullptr; 340 } 341 342 mShutdownHolder.Resolve(true, __func__); 343 } 344 345 const RefPtr<GenericNonExclusivePromise>& OnShutdown() const { 346 return mShutdownPromise; 347 } 348 349 private: 350 bool mDirectConnected; 351 bool mInitialized; 352 bool mRemoved; 353 TimeStamp mCurrentTime; 354 Atomic<bool> mPendingAdvanceCurrentTime; 355 RefPtr<MediaEncoder> mMediaEncoder; 356 RefPtr<TaskQueue> mEncoderThread; 357 MozPromiseHolder<GenericNonExclusivePromise> mShutdownHolder; 358 const RefPtr<GenericNonExclusivePromise> mShutdownPromise; 359 }; 360 361 class MediaEncoder::EncoderListener : public TrackEncoderListener { 362 public: 363 EncoderListener(TaskQueue* aEncoderThread, MediaEncoder* aEncoder) 364 : mEncoderThread(aEncoderThread), mEncoder(aEncoder) {} 365 366 void Forget() { 367 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 368 mEncoder = nullptr; 369 } 370 371 void Initialized(TrackEncoder* aTrackEncoder) override { 372 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 373 MOZ_ASSERT(aTrackEncoder->IsInitialized()); 374 375 if (!mEncoder) { 376 return; 377 } 378 379 mEncoder->UpdateInitialized(); 380 } 381 382 void Started(TrackEncoder* aTrackEncoder) override { 383 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 384 MOZ_ASSERT(aTrackEncoder->IsStarted()); 385 386 if (!mEncoder) { 387 return; 388 } 389 390 mEncoder->UpdateStarted(); 391 } 392 393 void Error(TrackEncoder* aTrackEncoder) override { 394 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 395 396 if (!mEncoder) { 397 return; 398 } 399 400 mEncoder->SetError(); 401 } 402 403 protected: 404 RefPtr<TaskQueue> mEncoderThread; 405 RefPtr<MediaEncoder> mEncoder; 406 }; 407 408 MediaEncoder::MediaEncoder( 409 RefPtr<TaskQueue> aEncoderThread, 410 RefPtr<DriftCompensator> aDriftCompensator, 411 UniquePtr<ContainerWriter> aWriter, 412 UniquePtr<AudioTrackEncoder> aAudioEncoder, 413 UniquePtr<VideoTrackEncoder> aVideoEncoder, 414 UniquePtr<MediaQueue<EncodedFrame>> aEncodedAudioQueue, 415 UniquePtr<MediaQueue<EncodedFrame>> aEncodedVideoQueue, 416 TrackRate aTrackRate, const nsAString& aMimeType, uint64_t aMaxMemory, 417 TimeDuration aTimeslice) 418 : mMainThread(GetMainThreadSerialEventTarget()), 419 mEncoderThread(std::move(aEncoderThread)), 420 mEncodedAudioQueue(std::move(aEncodedAudioQueue)), 421 mEncodedVideoQueue(std::move(aEncodedVideoQueue)), 422 mMuxer(MakeUnique<Muxer>(std::move(aWriter), *mEncodedAudioQueue, 423 *mEncodedVideoQueue)), 424 mAudioEncoder(std::move(aAudioEncoder)), 425 mAudioListener(mAudioEncoder ? MakeAndAddRef<AudioTrackListener>( 426 std::move(aDriftCompensator), this) 427 : nullptr), 428 mVideoEncoder(std::move(aVideoEncoder)), 429 mVideoListener(mVideoEncoder ? MakeAndAddRef<VideoTrackListener>(this) 430 : nullptr), 431 mEncoderListener(MakeAndAddRef<EncoderListener>(mEncoderThread, this)), 432 mMimeType(aMimeType), 433 mMaxMemory(aMaxMemory), 434 mTimeslice(aTimeslice), 435 mStartTime(TimeStamp::Now()), 436 mInitialized(false), 437 mStarted(false), 438 mCompleted(false), 439 mError(false) { 440 if (!mAudioEncoder) { 441 mMuxedAudioEndTime = TimeUnit::FromInfinity(); 442 mEncodedAudioQueue->Finish(); 443 } 444 if (!mVideoEncoder) { 445 mMuxedVideoEndTime = TimeUnit::FromInfinity(); 446 mEncodedVideoQueue->Finish(); 447 } 448 } 449 450 void MediaEncoder::RegisterListeners() { 451 if (mAudioEncoder) { 452 mAudioPushListener = mEncodedAudioQueue->PushEvent().Connect( 453 mEncoderThread, this, &MediaEncoder::OnEncodedAudioPushed); 454 mAudioFinishListener = mEncodedAudioQueue->FinishEvent().Connect( 455 mEncoderThread, this, &MediaEncoder::MaybeShutdown); 456 MOZ_ALWAYS_SUCCEEDS(mEncoderThread->Dispatch(NS_NewRunnableFunction( 457 "mozilla::AudioTrackEncoder::RegisterListener", 458 [self = RefPtr<MediaEncoder>(this), this] { 459 mAudioEncoder->RegisterListener(mEncoderListener); 460 }))); 461 } 462 if (mVideoEncoder) { 463 mVideoPushListener = mEncodedVideoQueue->PushEvent().Connect( 464 mEncoderThread, this, &MediaEncoder::OnEncodedVideoPushed); 465 mVideoFinishListener = mEncodedVideoQueue->FinishEvent().Connect( 466 mEncoderThread, this, &MediaEncoder::MaybeShutdown); 467 MOZ_ALWAYS_SUCCEEDS(mEncoderThread->Dispatch(NS_NewRunnableFunction( 468 "mozilla::VideoTrackEncoder::RegisterListener", 469 [self = RefPtr<MediaEncoder>(this), this] { 470 mVideoEncoder->RegisterListener(mEncoderListener); 471 }))); 472 } 473 } 474 475 MediaEncoder::~MediaEncoder() { 476 MOZ_ASSERT(!mAudioTrack); 477 MOZ_ASSERT(!mVideoTrack); 478 MOZ_ASSERT(!mAudioNode); 479 MOZ_ASSERT(!mInputPort); 480 MOZ_ASSERT(!mPipeTrack); 481 } 482 483 void MediaEncoder::EnsureGraphTrackFrom(MediaTrack* aTrack) { 484 if (mGraphTrack) { 485 return; 486 } 487 MOZ_DIAGNOSTIC_ASSERT(!aTrack->IsDestroyed()); 488 mGraphTrack = MakeAndAddRef<SharedDummyTrack>( 489 aTrack->Graph()->CreateSourceTrack(MediaSegment::VIDEO)); 490 } 491 492 void MediaEncoder::Suspend() { 493 mGraphTrack->mTrack->QueueControlMessageWithNoShutdown( 494 [self = RefPtr<MediaEncoder>(this), this] { 495 TRACE("MediaEncoder::Suspend (graph)"); 496 if (NS_FAILED(mEncoderThread->Dispatch( 497 NS_NewRunnableFunction("MediaEncoder::Suspend (encoder)", 498 [self, this, now = TimeStamp::Now()] { 499 if (mAudioEncoder) { 500 mAudioEncoder->Suspend(); 501 } 502 if (mVideoEncoder) { 503 mVideoEncoder->Suspend(now); 504 } 505 })))) { 506 // QueueControlMessageWithNoShutdown added an extra async step, and 507 // now `thread` has shut down. 508 return; 509 } 510 }); 511 } 512 513 void MediaEncoder::Resume() { 514 mGraphTrack->mTrack->QueueControlMessageWithNoShutdown( 515 [self = RefPtr<MediaEncoder>(this), this] { 516 TRACE("MediaEncoder::Resume (graph)"); 517 if (NS_FAILED(mEncoderThread->Dispatch( 518 NS_NewRunnableFunction("MediaEncoder::Resume (encoder)", 519 [self, this, now = TimeStamp::Now()] { 520 if (mAudioEncoder) { 521 mAudioEncoder->Resume(); 522 } 523 if (mVideoEncoder) { 524 mVideoEncoder->Resume(now); 525 } 526 })))) { 527 // QueueControlMessageWithNoShutdown added an extra async step, and 528 // now `thread` has shut down. 529 return; 530 } 531 }); 532 } 533 534 void MediaEncoder::ConnectAudioNode(AudioNode* aNode, uint32_t aOutput) { 535 MOZ_ASSERT(NS_IsMainThread()); 536 537 if (mAudioNode) { 538 MOZ_ASSERT(false, "Only one audio node supported"); 539 return; 540 } 541 542 // Only AudioNodeTrack of kind EXTERNAL_OUTPUT stores output audio data in 543 // the track (see AudioNodeTrack::AdvanceOutputSegment()). That means 544 // forwarding input track in recorder session won't be able to copy data from 545 // the track of non-destination node. Create a pipe track in this case. 546 if (aNode->NumberOfOutputs() > 0) { 547 AudioContext* ctx = aNode->Context(); 548 AudioNodeEngine* engine = new AudioNodeEngine(nullptr); 549 AudioNodeTrack::Flags flags = AudioNodeTrack::EXTERNAL_OUTPUT | 550 AudioNodeTrack::NEED_MAIN_THREAD_ENDED; 551 mPipeTrack = AudioNodeTrack::Create(ctx, engine, flags, ctx->Graph()); 552 AudioNodeTrack* ns = aNode->GetTrack(); 553 if (ns) { 554 mInputPort = mPipeTrack->AllocateInputPort(aNode->GetTrack(), 0, aOutput); 555 } 556 } 557 558 mAudioNode = aNode; 559 560 if (mPipeTrack) { 561 mPipeTrack->AddListener(mAudioListener); 562 EnsureGraphTrackFrom(mPipeTrack); 563 } else { 564 mAudioNode->GetTrack()->AddListener(mAudioListener); 565 EnsureGraphTrackFrom(mAudioNode->GetTrack()); 566 } 567 } 568 569 void MediaEncoder::ConnectMediaStreamTrack(MediaStreamTrack* aTrack) { 570 MOZ_ASSERT(NS_IsMainThread()); 571 572 if (aTrack->Ended()) { 573 MOZ_ASSERT_UNREACHABLE("Cannot connect ended track"); 574 return; 575 } 576 577 EnsureGraphTrackFrom(aTrack->GetTrack()); 578 579 if (AudioStreamTrack* audio = aTrack->AsAudioStreamTrack()) { 580 if (!mAudioEncoder) { 581 // No audio encoder for this audio track. It could be disabled. 582 LOG(LogLevel::Warning, ("Cannot connect to audio track - no encoder")); 583 return; 584 } 585 586 MOZ_ASSERT(!mAudioTrack, "Only one audio track supported."); 587 MOZ_ASSERT(mAudioListener, "No audio listener for this audio track"); 588 589 LOG(LogLevel::Info, ("Connected to audio track %p", aTrack)); 590 591 mAudioTrack = audio; 592 audio->AddListener(mAudioListener); 593 } else if (VideoStreamTrack* video = aTrack->AsVideoStreamTrack()) { 594 if (!mVideoEncoder) { 595 // No video encoder for this video track. It could be disabled. 596 LOG(LogLevel::Warning, ("Cannot connect to video track - no encoder")); 597 return; 598 } 599 600 MOZ_ASSERT(!mVideoTrack, "Only one video track supported."); 601 MOZ_ASSERT(mVideoListener, "No video listener for this video track"); 602 603 LOG(LogLevel::Info, ("Connected to video track %p", aTrack)); 604 605 mVideoTrack = video; 606 video->AddDirectListener(mVideoListener); 607 video->AddListener(mVideoListener); 608 } else { 609 MOZ_ASSERT(false, "Unknown track type"); 610 } 611 } 612 613 void MediaEncoder::RemoveMediaStreamTrack(MediaStreamTrack* aTrack) { 614 if (!aTrack) { 615 MOZ_ASSERT(false); 616 return; 617 } 618 619 if (AudioStreamTrack* audio = aTrack->AsAudioStreamTrack()) { 620 if (audio != mAudioTrack) { 621 MOZ_ASSERT(false, "Not connected to this audio track"); 622 return; 623 } 624 625 if (mAudioListener) { 626 audio->RemoveDirectListener(mAudioListener); 627 audio->RemoveListener(mAudioListener); 628 } 629 mAudioTrack = nullptr; 630 } else if (VideoStreamTrack* video = aTrack->AsVideoStreamTrack()) { 631 if (video != mVideoTrack) { 632 MOZ_ASSERT(false, "Not connected to this video track"); 633 return; 634 } 635 636 if (mVideoListener) { 637 video->RemoveDirectListener(mVideoListener); 638 video->RemoveListener(mVideoListener); 639 } 640 mVideoTrack = nullptr; 641 } 642 } 643 644 /* static */ 645 already_AddRefed<MediaEncoder> MediaEncoder::CreateEncoder( 646 RefPtr<TaskQueue> aEncoderThread, const nsAString& aMimeType, 647 uint32_t aAudioBitrate, uint32_t aVideoBitrate, uint8_t aTrackTypes, 648 TrackRate aTrackRate, uint64_t aMaxMemory, TimeDuration aTimeslice) { 649 AUTO_PROFILER_LABEL("MediaEncoder::CreateEncoder", OTHER); 650 651 UniquePtr<ContainerWriter> writer; 652 UniquePtr<AudioTrackEncoder> audioEncoder; 653 UniquePtr<VideoTrackEncoder> videoEncoder; 654 auto encodedAudioQueue = MakeUnique<MediaQueue<EncodedFrame>>(); 655 auto encodedVideoQueue = MakeUnique<MediaQueue<EncodedFrame>>(); 656 auto driftCompensator = 657 MakeRefPtr<DriftCompensator>(aEncoderThread, aTrackRate); 658 659 Maybe<MediaContainerType> mimeType = MakeMediaContainerType(aMimeType); 660 if (!mimeType) { 661 return nullptr; 662 } 663 664 for (const auto& codec : mimeType->ExtendedType().Codecs().Range()) { 665 if (codec.EqualsLiteral("opus")) { 666 MOZ_ASSERT(!audioEncoder); 667 audioEncoder = 668 MakeUnique<OpusTrackEncoder>(aTrackRate, *encodedAudioQueue); 669 } else if (codec.EqualsLiteral("vp8") || codec.EqualsLiteral("vp8.0")) { 670 MOZ_ASSERT(!videoEncoder); 671 if (Preferences::GetBool("media.recorder.video.frame_drops", true)) { 672 videoEncoder = MakeUnique<VP8TrackEncoder>(driftCompensator, aTrackRate, 673 *encodedVideoQueue, 674 FrameDroppingMode::ALLOW); 675 } else { 676 videoEncoder = MakeUnique<VP8TrackEncoder>(driftCompensator, aTrackRate, 677 *encodedVideoQueue, 678 FrameDroppingMode::DISALLOW); 679 } 680 } else { 681 MOZ_CRASH("Unknown codec"); 682 } 683 } 684 685 if (mimeType->Type() == MEDIAMIMETYPE(VIDEO_WEBM) || 686 mimeType->Type() == MEDIAMIMETYPE(AUDIO_WEBM)) { 687 MOZ_ASSERT_IF(mimeType->Type() == MEDIAMIMETYPE(AUDIO_WEBM), !videoEncoder); 688 writer = MakeUnique<WebMWriter>(); 689 } else if (mimeType->Type() == MEDIAMIMETYPE(AUDIO_OGG)) { 690 MOZ_ASSERT(audioEncoder); 691 MOZ_ASSERT(!videoEncoder); 692 writer = MakeUnique<OggWriter>(); 693 } 694 NS_ENSURE_TRUE(writer, nullptr); 695 696 LOG(LogLevel::Info, 697 ("Create encoder result:a[%p](%u bps) v[%p](%u bps) w[%p] mimeType = " 698 "%s.", 699 audioEncoder.get(), aAudioBitrate, videoEncoder.get(), aVideoBitrate, 700 writer.get(), NS_ConvertUTF16toUTF8(aMimeType).get())); 701 702 if (audioEncoder) { 703 audioEncoder->SetWorkerThread(aEncoderThread); 704 if (aAudioBitrate != 0) { 705 audioEncoder->SetBitrate(aAudioBitrate); 706 } 707 } 708 if (videoEncoder) { 709 videoEncoder->SetWorkerThread(aEncoderThread); 710 if (aVideoBitrate != 0) { 711 videoEncoder->SetBitrate(aVideoBitrate); 712 } 713 } 714 RefPtr<MediaEncoder> encoder = new MediaEncoder( 715 std::move(aEncoderThread), std::move(driftCompensator), std::move(writer), 716 std::move(audioEncoder), std::move(videoEncoder), 717 std::move(encodedAudioQueue), std::move(encodedVideoQueue), aTrackRate, 718 aMimeType, aMaxMemory, aTimeslice); 719 720 encoder->RegisterListeners(); 721 722 return encoder.forget(); 723 } 724 725 nsresult MediaEncoder::GetEncodedData( 726 nsTArray<nsTArray<uint8_t>>* aOutputBufs) { 727 AUTO_PROFILER_LABEL("MediaEncoder::GetEncodedData", OTHER); 728 729 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 730 731 LOG(LogLevel::Verbose, 732 ("GetEncodedData TimeStamp = %f", GetEncodeTimeStamp())); 733 734 if (!mInitialized) { 735 return NS_ERROR_NOT_INITIALIZED; 736 } 737 738 nsresult rv = mMuxer->GetData(aOutputBufs); 739 if (mMuxer->IsFinished()) { 740 mCompleted = true; 741 } 742 743 LOG(LogLevel::Verbose, 744 ("END GetEncodedData TimeStamp=%f " 745 "mCompleted=%d, aComplete=%d, vComplete=%d", 746 GetEncodeTimeStamp(), mCompleted, 747 !mAudioEncoder || mAudioEncoder->IsEncodingComplete(), 748 !mVideoEncoder || mVideoEncoder->IsEncodingComplete())); 749 750 return rv; 751 } 752 753 void MediaEncoder::MaybeShutdown() { 754 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 755 if (!mEncodedAudioQueue->IsFinished()) { 756 LOG(LogLevel::Debug, 757 ("MediaEncoder %p not shutting down, audio is still live", this)); 758 return; 759 } 760 761 if (!mEncodedVideoQueue->IsFinished()) { 762 LOG(LogLevel::Debug, 763 ("MediaEncoder %p not shutting down, video is still live", this)); 764 return; 765 } 766 767 mShutdownEvent.Notify(); 768 769 // Stop will Shutdown() gracefully. 770 (void)InvokeAsync(mMainThread, this, __func__, &MediaEncoder::Stop); 771 } 772 773 RefPtr<GenericNonExclusivePromise> MediaEncoder::Shutdown() { 774 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 775 if (mShutdownPromise) { 776 return mShutdownPromise; 777 } 778 779 LOG(LogLevel::Info, ("MediaEncoder is shutting down.")); 780 781 AutoTArray<RefPtr<GenericNonExclusivePromise>, 2> shutdownPromises; 782 if (mAudioListener) { 783 shutdownPromises.AppendElement(mAudioListener->OnShutdown()); 784 } 785 if (mVideoListener) { 786 shutdownPromises.AppendElement(mVideoListener->OnShutdown()); 787 } 788 789 mShutdownPromise = 790 GenericNonExclusivePromise::All(mEncoderThread, shutdownPromises) 791 ->Then(mEncoderThread, __func__, 792 [](const GenericNonExclusivePromise::AllPromiseType:: 793 ResolveOrRejectValue& aValue) { 794 if (aValue.IsResolve()) { 795 return GenericNonExclusivePromise::CreateAndResolve( 796 true, __func__); 797 } 798 return GenericNonExclusivePromise::CreateAndReject( 799 aValue.RejectValue(), __func__); 800 }); 801 802 mShutdownPromise->Then( 803 mEncoderThread, __func__, [self = RefPtr<MediaEncoder>(this), this] { 804 if (mAudioEncoder) { 805 mAudioEncoder->UnregisterListener(mEncoderListener); 806 } 807 if (mVideoEncoder) { 808 mVideoEncoder->UnregisterListener(mEncoderListener); 809 } 810 mEncoderListener->Forget(); 811 mMuxer->Disconnect(); 812 mAudioPushListener.DisconnectIfExists(); 813 mAudioFinishListener.DisconnectIfExists(); 814 mVideoPushListener.DisconnectIfExists(); 815 mVideoFinishListener.DisconnectIfExists(); 816 }); 817 818 return mShutdownPromise; 819 } 820 821 RefPtr<GenericNonExclusivePromise> MediaEncoder::Stop() { 822 MOZ_ASSERT(NS_IsMainThread()); 823 824 LOG(LogLevel::Info, ("MediaEncoder %p Stop", this)); 825 826 DisconnectTracks(); 827 828 return InvokeAsync(mEncoderThread, this, __func__, &MediaEncoder::Shutdown); 829 } 830 831 RefPtr<GenericNonExclusivePromise> MediaEncoder::Cancel() { 832 MOZ_ASSERT(NS_IsMainThread()); 833 834 LOG(LogLevel::Info, ("MediaEncoder %p Cancel", this)); 835 836 DisconnectTracks(); 837 838 return InvokeAsync(mEncoderThread, __func__, 839 [self = RefPtr<MediaEncoder>(this), this]() { 840 if (mAudioEncoder) { 841 mAudioEncoder->Cancel(); 842 } 843 if (mVideoEncoder) { 844 mVideoEncoder->Cancel(); 845 } 846 return Shutdown(); 847 }); 848 } 849 850 bool MediaEncoder::HasError() { 851 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 852 return mError; 853 } 854 855 void MediaEncoder::SetError() { 856 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 857 858 if (mError) { 859 return; 860 } 861 862 mError = true; 863 mErrorEvent.Notify(); 864 } 865 866 auto MediaEncoder::RequestData() -> RefPtr<BlobPromise> { 867 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 868 TimeUnit muxedEndTime = std::min(mMuxedAudioEndTime, mMuxedVideoEndTime); 869 mLastBlobTime = muxedEndTime; 870 mLastExtractTime = muxedEndTime; 871 return Extract()->Then( 872 mMainThread, __func__, 873 [this, self = RefPtr<MediaEncoder>(this)]( 874 const GenericPromise::ResolveOrRejectValue& aValue) { 875 // Even if rejected, we want to gather what has already been 876 // extracted into the current blob and expose that. 877 (void)NS_WARN_IF(aValue.IsReject()); 878 return GatherBlob(); 879 }); 880 } 881 882 void MediaEncoder::MaybeCreateMutableBlobStorage() { 883 MOZ_ASSERT(NS_IsMainThread()); 884 if (!mMutableBlobStorage) { 885 mMutableBlobStorage = new MutableBlobStorage( 886 MutableBlobStorage::eCouldBeInTemporaryFile, nullptr, mMaxMemory); 887 } 888 } 889 890 void MediaEncoder::OnEncodedAudioPushed(const RefPtr<EncodedFrame>& aFrame) { 891 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 892 mMuxedAudioEndTime = aFrame->GetEndTime(); 893 MaybeExtractOrGatherBlob(); 894 } 895 896 void MediaEncoder::OnEncodedVideoPushed(const RefPtr<EncodedFrame>& aFrame) { 897 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 898 mMuxedVideoEndTime = aFrame->GetEndTime(); 899 MaybeExtractOrGatherBlob(); 900 } 901 902 void MediaEncoder::MaybeExtractOrGatherBlob() { 903 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 904 905 TimeUnit muxedEndTime = std::min(mMuxedAudioEndTime, mMuxedVideoEndTime); 906 if ((muxedEndTime - mLastBlobTime).ToTimeDuration() >= mTimeslice) { 907 LOG(LogLevel::Verbose, ("MediaEncoder %p Muxed %.2fs of data since last " 908 "blob. Issuing new blob.", 909 this, (muxedEndTime - mLastBlobTime).ToSeconds())); 910 RequestData()->Then(mEncoderThread, __func__, 911 [this, self = RefPtr<MediaEncoder>(this)]( 912 const BlobPromise::ResolveOrRejectValue& aValue) { 913 if (aValue.IsReject()) { 914 SetError(); 915 return; 916 } 917 RefPtr<BlobImpl> blob = aValue.ResolveValue(); 918 mDataAvailableEvent.Notify(std::move(blob)); 919 }); 920 } 921 922 if (muxedEndTime - mLastExtractTime > TimeUnit::FromSeconds(1)) { 923 // Extract data from the muxer at least every second. 924 LOG(LogLevel::Verbose, 925 ("MediaEncoder %p Muxed %.2fs of data since last " 926 "extract. Extracting more data into blob.", 927 this, (muxedEndTime - mLastExtractTime).ToSeconds())); 928 mLastExtractTime = muxedEndTime; 929 (void)Extract(); 930 } 931 } 932 933 // Pull encoded media data from MediaEncoder and put into MutableBlobStorage. 934 RefPtr<GenericPromise> MediaEncoder::Extract() { 935 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 936 937 LOG(LogLevel::Debug, ("MediaEncoder %p Extract", this)); 938 939 AUTO_PROFILER_LABEL("MediaEncoder::Extract", OTHER); 940 941 // Pull encoded media data from MediaEncoder 942 nsTArray<nsTArray<uint8_t>> buffer; 943 nsresult rv = GetEncodedData(&buffer); 944 MOZ_ASSERT(rv != NS_ERROR_INVALID_ARG, "Invalid args can be prevented."); 945 if (NS_FAILED(rv)) { 946 MOZ_RELEASE_ASSERT(buffer.IsEmpty()); 947 // Even if we failed to encode more data, it might be time to push a blob 948 // with already encoded data. 949 } 950 951 // To ensure Extract() promises are resolved in calling order, we always 952 // invoke the main thread. Even when the encoded buffer is empty. 953 return InvokeAsync( 954 mMainThread, __func__, 955 [self = RefPtr<MediaEncoder>(this), this, buffer = std::move(buffer)] { 956 MaybeCreateMutableBlobStorage(); 957 for (const auto& part : buffer) { 958 if (part.IsEmpty()) { 959 continue; 960 } 961 962 nsresult rv = 963 mMutableBlobStorage->Append(part.Elements(), part.Length()); 964 if (NS_WARN_IF(NS_FAILED(rv))) { 965 return GenericPromise::CreateAndReject(rv, __func__); 966 } 967 } 968 return GenericPromise::CreateAndResolve(true, __func__); 969 }); 970 } 971 972 auto MediaEncoder::GatherBlob() -> RefPtr<BlobPromise> { 973 MOZ_ASSERT(NS_IsMainThread()); 974 if (!mBlobPromise) { 975 return mBlobPromise = GatherBlobImpl(); 976 } 977 return mBlobPromise = mBlobPromise->Then(mMainThread, __func__, 978 [self = RefPtr<MediaEncoder>(this)] { 979 return self->GatherBlobImpl(); 980 }); 981 } 982 983 auto MediaEncoder::GatherBlobImpl() -> RefPtr<BlobPromise> { 984 RefPtr<BlobStorer> storer = MakeAndAddRef<BlobStorer>(); 985 MaybeCreateMutableBlobStorage(); 986 mMutableBlobStorage->GetBlobImplWhenReady(NS_ConvertUTF16toUTF8(mMimeType), 987 storer); 988 mMutableBlobStorage = nullptr; 989 990 storer->Promise()->Then( 991 mMainThread, __func__, 992 [self = RefPtr<MediaEncoder>(this), p = storer->Promise()] { 993 if (self->mBlobPromise == p) { 994 // Reset BlobPromise. 995 self->mBlobPromise = nullptr; 996 } 997 }); 998 999 return storer->Promise(); 1000 } 1001 1002 void MediaEncoder::DisconnectTracks() { 1003 MOZ_ASSERT(NS_IsMainThread()); 1004 1005 if (mAudioNode) { 1006 mAudioNode->GetTrack()->RemoveListener(mAudioListener); 1007 if (mInputPort) { 1008 mInputPort->Destroy(); 1009 mInputPort = nullptr; 1010 } 1011 if (mPipeTrack) { 1012 mPipeTrack->RemoveListener(mAudioListener); 1013 mPipeTrack->Destroy(); 1014 mPipeTrack = nullptr; 1015 } 1016 mAudioNode = nullptr; 1017 } 1018 1019 if (mAudioTrack) { 1020 RemoveMediaStreamTrack(mAudioTrack); 1021 } 1022 1023 if (mVideoTrack) { 1024 RemoveMediaStreamTrack(mVideoTrack); 1025 } 1026 } 1027 1028 bool MediaEncoder::IsWebMEncoderEnabled() { 1029 return StaticPrefs::media_encoder_webm_enabled(); 1030 } 1031 1032 void MediaEncoder::UpdateInitialized() { 1033 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 1034 1035 if (mInitialized) { 1036 // This could happen if an encoder re-inits due to a resolution change. 1037 return; 1038 } 1039 1040 if (mAudioEncoder && !mAudioEncoder->IsInitialized()) { 1041 LOG(LogLevel::Debug, 1042 ("MediaEncoder %p UpdateInitialized waiting for audio", this)); 1043 return; 1044 } 1045 1046 if (mVideoEncoder && !mVideoEncoder->IsInitialized()) { 1047 LOG(LogLevel::Debug, 1048 ("MediaEncoder %p UpdateInitialized waiting for video", this)); 1049 return; 1050 } 1051 1052 MOZ_ASSERT(mMuxer->NeedsMetadata()); 1053 nsTArray<RefPtr<TrackMetadataBase>> meta; 1054 if (mAudioEncoder && !*meta.AppendElement(mAudioEncoder->GetMetadata())) { 1055 LOG(LogLevel::Error, ("Audio metadata is null")); 1056 SetError(); 1057 return; 1058 } 1059 if (mVideoEncoder && !*meta.AppendElement(mVideoEncoder->GetMetadata())) { 1060 LOG(LogLevel::Error, ("Video metadata is null")); 1061 SetError(); 1062 return; 1063 } 1064 1065 if (NS_FAILED(mMuxer->SetMetadata(meta))) { 1066 LOG(LogLevel::Error, ("SetMetadata failed")); 1067 SetError(); 1068 return; 1069 } 1070 1071 LOG(LogLevel::Info, 1072 ("MediaEncoder %p UpdateInitialized set metadata in muxer", this)); 1073 1074 mInitialized = true; 1075 } 1076 1077 void MediaEncoder::UpdateStarted() { 1078 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); 1079 1080 if (mStarted) { 1081 return; 1082 } 1083 1084 if (mAudioEncoder && !mAudioEncoder->IsStarted()) { 1085 return; 1086 } 1087 1088 if (mVideoEncoder && !mVideoEncoder->IsStarted()) { 1089 return; 1090 } 1091 1092 mStarted = true; 1093 1094 // Start issuing timeslice-based blobs. 1095 MOZ_ASSERT(mLastBlobTime == TimeUnit::Zero()); 1096 1097 mStartedEvent.Notify(); 1098 } 1099 1100 /* 1101 * SizeOfExcludingThis measures memory being used by the Media Encoder. 1102 * Currently it measures the size of the Encoder buffer and memory occupied 1103 * by mAudioEncoder, mVideoEncoder, and any current blob storage. 1104 */ 1105 auto MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) 1106 -> RefPtr<SizeOfPromise> { 1107 MOZ_ASSERT(NS_IsMainThread()); 1108 size_t blobStorageSize = 1109 mMutableBlobStorage ? mMutableBlobStorage->SizeOfCurrentMemoryBuffer() 1110 : 0; 1111 1112 return InvokeAsync( 1113 mEncoderThread, __func__, 1114 [self = RefPtr<MediaEncoder>(this), this, blobStorageSize, 1115 aMallocSizeOf]() { 1116 size_t size = 0; 1117 if (mAudioEncoder) { 1118 size += mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf); 1119 } 1120 if (mVideoEncoder) { 1121 size += mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf); 1122 } 1123 return SizeOfPromise::CreateAndResolve(blobStorageSize + size, 1124 __func__); 1125 }); 1126 } 1127 1128 } // namespace mozilla 1129 1130 #undef LOG