Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / media / encoder / MediaEncoder.cpp
blobcae5c4ab4e0b118ad63101aafe4946316deb9bab
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/. */
6 #include "MediaEncoder.h"
8 #include <algorithm>
9 #include "AudioNodeEngine.h"
10 #include "AudioNodeTrack.h"
11 #include "DriftCompensation.h"
12 #include "MediaDecoder.h"
13 #include "MediaTrackGraph.h"
14 #include "MediaTrackListener.h"
15 #include "mozilla/dom/AudioNode.h"
16 #include "mozilla/dom/AudioStreamTrack.h"
17 #include "mozilla/dom/Blob.h"
18 #include "mozilla/dom/BlobImpl.h"
19 #include "mozilla/dom/MediaStreamTrack.h"
20 #include "mozilla/dom/MutableBlobStorage.h"
21 #include "mozilla/dom/VideoStreamTrack.h"
22 #include "mozilla/gfx/Point.h" // IntSize
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/Unused.h"
30 #include "Muxer.h"
31 #include "nsMimeTypes.h"
32 #include "nsThreadUtils.h"
33 #include "OggWriter.h"
34 #include "OpusTrackEncoder.h"
35 #include "TimeUnits.h"
36 #include "Tracing.h"
38 #include "VP8TrackEncoder.h"
39 #include "WebMWriter.h"
41 mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder");
42 #define LOG(type, msg) MOZ_LOG(gMediaEncoderLog, type, msg)
44 namespace mozilla {
46 using namespace dom;
47 using namespace media;
49 namespace {
50 class BlobStorer : public MutableBlobStorageCallback {
51 MozPromiseHolder<MediaEncoder::BlobPromise> mHolder;
53 virtual ~BlobStorer() = default;
55 public:
56 BlobStorer() = default;
58 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobStorer, override)
60 void BlobStoreCompleted(MutableBlobStorage*, BlobImpl* aBlobImpl,
61 nsresult aRv) override {
62 MOZ_ASSERT(NS_IsMainThread());
63 if (NS_FAILED(aRv)) {
64 mHolder.Reject(aRv, __func__);
65 return;
68 mHolder.Resolve(aBlobImpl, __func__);
71 RefPtr<MediaEncoder::BlobPromise> Promise() {
72 return mHolder.Ensure(__func__);
75 } // namespace
77 class MediaEncoder::AudioTrackListener : public DirectMediaTrackListener {
78 public:
79 AudioTrackListener(RefPtr<DriftCompensator> aDriftCompensator,
80 RefPtr<MediaEncoder> aMediaEncoder)
81 : mDirectConnected(false),
82 mInitialized(false),
83 mRemoved(false),
84 mDriftCompensator(std::move(aDriftCompensator)),
85 mMediaEncoder(std::move(aMediaEncoder)),
86 mEncoderThread(mMediaEncoder->mEncoderThread),
87 mShutdownPromise(mShutdownHolder.Ensure(__func__)) {
88 MOZ_ASSERT(mMediaEncoder);
89 MOZ_ASSERT(mMediaEncoder->mAudioEncoder);
90 MOZ_ASSERT(mEncoderThread);
93 void NotifyDirectListenerInstalled(InstallationResult aResult) override {
94 if (aResult == InstallationResult::SUCCESS) {
95 LOG(LogLevel::Info, ("Audio track direct listener installed"));
96 mDirectConnected = true;
97 } else {
98 LOG(LogLevel::Info, ("Audio track failed to install direct listener"));
99 MOZ_ASSERT(!mDirectConnected);
103 void NotifyDirectListenerUninstalled() override {
104 mDirectConnected = false;
106 if (mRemoved) {
107 mMediaEncoder = nullptr;
108 mEncoderThread = nullptr;
112 void NotifyQueuedChanges(MediaTrackGraph* aGraph, TrackTime aTrackOffset,
113 const MediaSegment& aQueuedMedia) override {
114 TRACE_COMMENT("MediaEncoder::NotifyQueuedChanges", "%p",
115 mMediaEncoder.get());
116 MOZ_ASSERT(mMediaEncoder);
117 MOZ_ASSERT(mEncoderThread);
119 if (!mInitialized) {
120 mDriftCompensator->NotifyAudioStart(TimeStamp::Now());
121 mInitialized = true;
124 mDriftCompensator->NotifyAudio(aQueuedMedia.GetDuration());
126 const AudioSegment& audio = static_cast<const AudioSegment&>(aQueuedMedia);
128 AudioSegment copy;
129 copy.AppendSlice(audio, 0, audio.GetDuration());
131 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
132 "mozilla::AudioTrackEncoder::AppendAudioSegment",
133 [encoder = mMediaEncoder, copy = std::move(copy)]() mutable {
134 encoder->mAudioEncoder->AppendAudioSegment(std::move(copy));
135 }));
136 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
137 Unused << rv;
140 void NotifyEnded(MediaTrackGraph* aGraph) override {
141 MOZ_ASSERT(mMediaEncoder);
142 MOZ_ASSERT(mMediaEncoder->mAudioEncoder);
143 MOZ_ASSERT(mEncoderThread);
145 nsresult rv = mEncoderThread->Dispatch(
146 NS_NewRunnableFunction("mozilla::AudioTrackEncoder::NotifyEndOfStream",
147 [encoder = mMediaEncoder] {
148 encoder->mAudioEncoder->NotifyEndOfStream();
149 }));
150 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
151 Unused << rv;
154 void NotifyRemoved(MediaTrackGraph* aGraph) override {
155 nsresult rv = mEncoderThread->Dispatch(
156 NS_NewRunnableFunction("mozilla::AudioTrackEncoder::NotifyEndOfStream",
157 [encoder = mMediaEncoder] {
158 encoder->mAudioEncoder->NotifyEndOfStream();
159 }));
160 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
161 Unused << rv;
163 mRemoved = true;
165 if (!mDirectConnected) {
166 mMediaEncoder = nullptr;
167 mEncoderThread = nullptr;
170 mShutdownHolder.Resolve(true, __func__);
173 const RefPtr<GenericNonExclusivePromise>& OnShutdown() const {
174 return mShutdownPromise;
177 private:
178 bool mDirectConnected;
179 bool mInitialized;
180 bool mRemoved;
181 const RefPtr<DriftCompensator> mDriftCompensator;
182 RefPtr<MediaEncoder> mMediaEncoder;
183 RefPtr<TaskQueue> mEncoderThread;
184 MozPromiseHolder<GenericNonExclusivePromise> mShutdownHolder;
185 const RefPtr<GenericNonExclusivePromise> mShutdownPromise;
188 class MediaEncoder::VideoTrackListener : public DirectMediaTrackListener {
189 public:
190 explicit VideoTrackListener(RefPtr<MediaEncoder> aMediaEncoder)
191 : mDirectConnected(false),
192 mInitialized(false),
193 mRemoved(false),
194 mPendingAdvanceCurrentTime(false),
195 mMediaEncoder(std::move(aMediaEncoder)),
196 mEncoderThread(mMediaEncoder->mEncoderThread),
197 mShutdownPromise(mShutdownHolder.Ensure(__func__)) {
198 MOZ_ASSERT(mMediaEncoder);
199 MOZ_ASSERT(mEncoderThread);
202 void NotifyDirectListenerInstalled(InstallationResult aResult) override {
203 if (aResult == InstallationResult::SUCCESS) {
204 LOG(LogLevel::Info, ("Video track direct listener installed"));
205 mDirectConnected = true;
206 } else {
207 LOG(LogLevel::Info, ("Video track failed to install direct listener"));
208 MOZ_ASSERT(!mDirectConnected);
209 return;
213 void NotifyDirectListenerUninstalled() override {
214 mDirectConnected = false;
216 if (mRemoved) {
217 mMediaEncoder = nullptr;
218 mEncoderThread = nullptr;
222 void NotifyQueuedChanges(MediaTrackGraph* aGraph, TrackTime aTrackOffset,
223 const MediaSegment& aQueuedMedia) override {
224 TRACE_COMMENT("MediaEncoder::NotifyQueuedChanges", "%p",
225 mMediaEncoder.get());
226 MOZ_ASSERT(mMediaEncoder);
227 MOZ_ASSERT(mMediaEncoder->mVideoEncoder);
228 MOZ_ASSERT(mEncoderThread);
230 mCurrentTime = TimeStamp::Now();
231 if (!mInitialized) {
232 nsresult rv = mEncoderThread->Dispatch(
233 NS_NewRunnableFunction("mozilla::VideoTrackEncoder::SetStartOffset",
234 [encoder = mMediaEncoder, now = mCurrentTime] {
235 encoder->mVideoEncoder->SetStartOffset(now);
236 }));
237 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
238 Unused << rv;
239 mInitialized = true;
242 if (!mPendingAdvanceCurrentTime) {
243 mPendingAdvanceCurrentTime = true;
244 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
245 "mozilla::VideoTrackEncoder::AdvanceCurrentTime",
246 [encoder = mMediaEncoder, now = mCurrentTime] {
247 encoder->mVideoListener->mPendingAdvanceCurrentTime = false;
248 encoder->mVideoEncoder->AdvanceCurrentTime(now);
249 }));
250 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
251 Unused << rv;
255 void NotifyRealtimeTrackData(MediaTrackGraph* aGraph, TrackTime aTrackOffset,
256 const MediaSegment& aMedia) override {
257 TRACE_COMMENT("MediaEncoder::NotifyRealtimeTrackData", "%p",
258 mMediaEncoder.get());
259 MOZ_ASSERT(mMediaEncoder);
260 MOZ_ASSERT(mMediaEncoder->mVideoEncoder);
261 MOZ_ASSERT(mEncoderThread);
262 MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO);
264 const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
265 VideoSegment copy;
266 for (VideoSegment::ConstChunkIterator iter(video); !iter.IsEnded();
267 iter.Next()) {
268 copy.AppendFrame(do_AddRef(iter->mFrame.GetImage()),
269 iter->mFrame.GetIntrinsicSize(),
270 iter->mFrame.GetPrincipalHandle(),
271 iter->mFrame.GetForceBlack(), iter->mTimeStamp);
274 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
275 "mozilla::VideoTrackEncoder::AppendVideoSegment",
276 [encoder = mMediaEncoder, copy = std::move(copy)]() mutable {
277 encoder->mVideoEncoder->AppendVideoSegment(std::move(copy));
278 }));
279 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
280 Unused << rv;
283 void NotifyEnabledStateChanged(MediaTrackGraph* aGraph,
284 bool aEnabled) override {
285 MOZ_ASSERT(mMediaEncoder);
286 MOZ_ASSERT(mMediaEncoder->mVideoEncoder);
287 MOZ_ASSERT(mEncoderThread);
289 nsresult rv;
290 if (aEnabled) {
291 rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
292 "mozilla::VideoTrackEncoder::Enable",
293 [encoder = mMediaEncoder, now = TimeStamp::Now()] {
294 encoder->mVideoEncoder->Enable(now);
295 }));
296 } else {
297 rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
298 "mozilla::VideoTrackEncoder::Disable",
299 [encoder = mMediaEncoder, now = TimeStamp::Now()] {
300 encoder->mVideoEncoder->Disable(now);
301 }));
303 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
304 Unused << rv;
307 void NotifyEnded(MediaTrackGraph* aGraph) override {
308 MOZ_ASSERT(mMediaEncoder);
309 MOZ_ASSERT(mMediaEncoder->mVideoEncoder);
310 MOZ_ASSERT(mEncoderThread);
312 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
313 "mozilla::VideoTrackEncoder::NotifyEndOfStream",
314 [encoder = mMediaEncoder, now = mCurrentTime] {
315 if (!now.IsNull()) {
316 encoder->mVideoEncoder->AdvanceCurrentTime(now);
318 encoder->mVideoEncoder->NotifyEndOfStream();
319 }));
320 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
321 Unused << rv;
324 void NotifyRemoved(MediaTrackGraph* aGraph) override {
325 nsresult rv = mEncoderThread->Dispatch(NS_NewRunnableFunction(
326 "mozilla::VideoTrackEncoder::NotifyEndOfStream",
327 [encoder = mMediaEncoder, now = mCurrentTime] {
328 if (!now.IsNull()) {
329 encoder->mVideoEncoder->AdvanceCurrentTime(now);
331 encoder->mVideoEncoder->NotifyEndOfStream();
332 }));
333 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
334 Unused << rv;
336 mRemoved = true;
338 if (!mDirectConnected) {
339 mMediaEncoder = nullptr;
340 mEncoderThread = nullptr;
343 mShutdownHolder.Resolve(true, __func__);
346 const RefPtr<GenericNonExclusivePromise>& OnShutdown() const {
347 return mShutdownPromise;
350 private:
351 bool mDirectConnected;
352 bool mInitialized;
353 bool mRemoved;
354 TimeStamp mCurrentTime;
355 Atomic<bool> mPendingAdvanceCurrentTime;
356 RefPtr<MediaEncoder> mMediaEncoder;
357 RefPtr<TaskQueue> mEncoderThread;
358 MozPromiseHolder<GenericNonExclusivePromise> mShutdownHolder;
359 const RefPtr<GenericNonExclusivePromise> mShutdownPromise;
362 class MediaEncoder::EncoderListener : public TrackEncoderListener {
363 public:
364 EncoderListener(TaskQueue* aEncoderThread, MediaEncoder* aEncoder)
365 : mEncoderThread(aEncoderThread), mEncoder(aEncoder) {}
367 void Forget() {
368 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
369 mEncoder = nullptr;
372 void Initialized(TrackEncoder* aTrackEncoder) override {
373 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
374 MOZ_ASSERT(aTrackEncoder->IsInitialized());
376 if (!mEncoder) {
377 return;
380 mEncoder->UpdateInitialized();
383 void Started(TrackEncoder* aTrackEncoder) override {
384 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
385 MOZ_ASSERT(aTrackEncoder->IsStarted());
387 if (!mEncoder) {
388 return;
391 mEncoder->UpdateStarted();
394 void Error(TrackEncoder* aTrackEncoder) override {
395 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
397 if (!mEncoder) {
398 return;
401 mEncoder->SetError();
404 protected:
405 RefPtr<TaskQueue> mEncoderThread;
406 RefPtr<MediaEncoder> mEncoder;
409 MediaEncoder::MediaEncoder(
410 RefPtr<TaskQueue> aEncoderThread,
411 RefPtr<DriftCompensator> aDriftCompensator,
412 UniquePtr<ContainerWriter> aWriter,
413 UniquePtr<AudioTrackEncoder> aAudioEncoder,
414 UniquePtr<VideoTrackEncoder> aVideoEncoder,
415 UniquePtr<MediaQueue<EncodedFrame>> aEncodedAudioQueue,
416 UniquePtr<MediaQueue<EncodedFrame>> aEncodedVideoQueue,
417 TrackRate aTrackRate, const nsAString& aMimeType, uint64_t aMaxMemory,
418 TimeDuration aTimeslice)
419 : mMainThread(GetMainThreadSerialEventTarget()),
420 mEncoderThread(std::move(aEncoderThread)),
421 mEncodedAudioQueue(std::move(aEncodedAudioQueue)),
422 mEncodedVideoQueue(std::move(aEncodedVideoQueue)),
423 mMuxer(MakeUnique<Muxer>(std::move(aWriter), *mEncodedAudioQueue,
424 *mEncodedVideoQueue)),
425 mAudioEncoder(std::move(aAudioEncoder)),
426 mAudioListener(mAudioEncoder ? MakeAndAddRef<AudioTrackListener>(
427 std::move(aDriftCompensator), this)
428 : nullptr),
429 mVideoEncoder(std::move(aVideoEncoder)),
430 mVideoListener(mVideoEncoder ? MakeAndAddRef<VideoTrackListener>(this)
431 : nullptr),
432 mEncoderListener(MakeAndAddRef<EncoderListener>(mEncoderThread, this)),
433 mMimeType(aMimeType),
434 mMaxMemory(aMaxMemory),
435 mTimeslice(aTimeslice),
436 mStartTime(TimeStamp::Now()),
437 mInitialized(false),
438 mStarted(false),
439 mCompleted(false),
440 mError(false) {
441 if (!mAudioEncoder) {
442 mMuxedAudioEndTime = TimeUnit::FromInfinity();
443 mEncodedAudioQueue->Finish();
445 if (!mVideoEncoder) {
446 mMuxedVideoEndTime = TimeUnit::FromInfinity();
447 mEncodedVideoQueue->Finish();
451 void MediaEncoder::RegisterListeners() {
452 if (mAudioEncoder) {
453 mAudioPushListener = mEncodedAudioQueue->PushEvent().Connect(
454 mEncoderThread, this, &MediaEncoder::OnEncodedAudioPushed);
455 mAudioFinishListener = mEncodedAudioQueue->FinishEvent().Connect(
456 mEncoderThread, this, &MediaEncoder::MaybeShutdown);
457 MOZ_ALWAYS_SUCCEEDS(mEncoderThread->Dispatch(NS_NewRunnableFunction(
458 "mozilla::AudioTrackEncoder::RegisterListener",
459 [self = RefPtr<MediaEncoder>(this), this] {
460 mAudioEncoder->RegisterListener(mEncoderListener);
461 })));
463 if (mVideoEncoder) {
464 mVideoPushListener = mEncodedVideoQueue->PushEvent().Connect(
465 mEncoderThread, this, &MediaEncoder::OnEncodedVideoPushed);
466 mVideoFinishListener = mEncodedVideoQueue->FinishEvent().Connect(
467 mEncoderThread, this, &MediaEncoder::MaybeShutdown);
468 MOZ_ALWAYS_SUCCEEDS(mEncoderThread->Dispatch(NS_NewRunnableFunction(
469 "mozilla::VideoTrackEncoder::RegisterListener",
470 [self = RefPtr<MediaEncoder>(this), this] {
471 mVideoEncoder->RegisterListener(mEncoderListener);
472 })));
476 MediaEncoder::~MediaEncoder() {
477 MOZ_ASSERT(!mAudioTrack);
478 MOZ_ASSERT(!mVideoTrack);
479 MOZ_ASSERT(!mAudioNode);
480 MOZ_ASSERT(!mInputPort);
481 MOZ_ASSERT(!mPipeTrack);
484 void MediaEncoder::EnsureGraphTrackFrom(MediaTrack* aTrack) {
485 if (mGraphTrack) {
486 return;
488 MOZ_DIAGNOSTIC_ASSERT(!aTrack->IsDestroyed());
489 mGraphTrack = MakeAndAddRef<SharedDummyTrack>(
490 aTrack->Graph()->CreateSourceTrack(MediaSegment::VIDEO));
493 void MediaEncoder::Suspend() {
494 mGraphTrack->mTrack->QueueControlMessageWithNoShutdown(
495 [self = RefPtr<MediaEncoder>(this), this] {
496 TRACE("MediaEncoder::Suspend (graph)");
497 if (NS_FAILED(mEncoderThread->Dispatch(
498 NS_NewRunnableFunction("MediaEncoder::Suspend (encoder)",
499 [self, this, now = TimeStamp::Now()] {
500 if (mAudioEncoder) {
501 mAudioEncoder->Suspend();
503 if (mVideoEncoder) {
504 mVideoEncoder->Suspend(now);
506 })))) {
507 // QueueControlMessageWithNoShutdown added an extra async step, and
508 // now `thread` has shut down.
509 return;
514 void MediaEncoder::Resume() {
515 mGraphTrack->mTrack->QueueControlMessageWithNoShutdown(
516 [self = RefPtr<MediaEncoder>(this), this] {
517 TRACE("MediaEncoder::Resume (graph)");
518 if (NS_FAILED(mEncoderThread->Dispatch(
519 NS_NewRunnableFunction("MediaEncoder::Resume (encoder)",
520 [self, this, now = TimeStamp::Now()] {
521 if (mAudioEncoder) {
522 mAudioEncoder->Resume();
524 if (mVideoEncoder) {
525 mVideoEncoder->Resume(now);
527 })))) {
528 // QueueControlMessageWithNoShutdown added an extra async step, and
529 // now `thread` has shut down.
530 return;
535 void MediaEncoder::ConnectAudioNode(AudioNode* aNode, uint32_t aOutput) {
536 MOZ_ASSERT(NS_IsMainThread());
538 if (mAudioNode) {
539 MOZ_ASSERT(false, "Only one audio node supported");
540 return;
543 // Only AudioNodeTrack of kind EXTERNAL_OUTPUT stores output audio data in
544 // the track (see AudioNodeTrack::AdvanceOutputSegment()). That means
545 // forwarding input track in recorder session won't be able to copy data from
546 // the track of non-destination node. Create a pipe track in this case.
547 if (aNode->NumberOfOutputs() > 0) {
548 AudioContext* ctx = aNode->Context();
549 AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
550 AudioNodeTrack::Flags flags = AudioNodeTrack::EXTERNAL_OUTPUT |
551 AudioNodeTrack::NEED_MAIN_THREAD_ENDED;
552 mPipeTrack = AudioNodeTrack::Create(ctx, engine, flags, ctx->Graph());
553 AudioNodeTrack* ns = aNode->GetTrack();
554 if (ns) {
555 mInputPort = mPipeTrack->AllocateInputPort(aNode->GetTrack(), 0, aOutput);
559 mAudioNode = aNode;
561 if (mPipeTrack) {
562 mPipeTrack->AddListener(mAudioListener);
563 EnsureGraphTrackFrom(mPipeTrack);
564 } else {
565 mAudioNode->GetTrack()->AddListener(mAudioListener);
566 EnsureGraphTrackFrom(mAudioNode->GetTrack());
570 void MediaEncoder::ConnectMediaStreamTrack(MediaStreamTrack* aTrack) {
571 MOZ_ASSERT(NS_IsMainThread());
573 if (aTrack->Ended()) {
574 MOZ_ASSERT_UNREACHABLE("Cannot connect ended track");
575 return;
578 EnsureGraphTrackFrom(aTrack->GetTrack());
580 if (AudioStreamTrack* audio = aTrack->AsAudioStreamTrack()) {
581 if (!mAudioEncoder) {
582 // No audio encoder for this audio track. It could be disabled.
583 LOG(LogLevel::Warning, ("Cannot connect to audio track - no encoder"));
584 return;
587 MOZ_ASSERT(!mAudioTrack, "Only one audio track supported.");
588 MOZ_ASSERT(mAudioListener, "No audio listener for this audio track");
590 LOG(LogLevel::Info, ("Connected to audio track %p", aTrack));
592 mAudioTrack = audio;
593 audio->AddListener(mAudioListener);
594 } else if (VideoStreamTrack* video = aTrack->AsVideoStreamTrack()) {
595 if (!mVideoEncoder) {
596 // No video encoder for this video track. It could be disabled.
597 LOG(LogLevel::Warning, ("Cannot connect to video track - no encoder"));
598 return;
601 MOZ_ASSERT(!mVideoTrack, "Only one video track supported.");
602 MOZ_ASSERT(mVideoListener, "No video listener for this video track");
604 LOG(LogLevel::Info, ("Connected to video track %p", aTrack));
606 mVideoTrack = video;
607 video->AddDirectListener(mVideoListener);
608 video->AddListener(mVideoListener);
609 } else {
610 MOZ_ASSERT(false, "Unknown track type");
614 void MediaEncoder::RemoveMediaStreamTrack(MediaStreamTrack* aTrack) {
615 if (!aTrack) {
616 MOZ_ASSERT(false);
617 return;
620 if (AudioStreamTrack* audio = aTrack->AsAudioStreamTrack()) {
621 if (audio != mAudioTrack) {
622 MOZ_ASSERT(false, "Not connected to this audio track");
623 return;
626 if (mAudioListener) {
627 audio->RemoveDirectListener(mAudioListener);
628 audio->RemoveListener(mAudioListener);
630 mAudioTrack = nullptr;
631 } else if (VideoStreamTrack* video = aTrack->AsVideoStreamTrack()) {
632 if (video != mVideoTrack) {
633 MOZ_ASSERT(false, "Not connected to this video track");
634 return;
637 if (mVideoListener) {
638 video->RemoveDirectListener(mVideoListener);
639 video->RemoveListener(mVideoListener);
641 mVideoTrack = nullptr;
645 /* static */
646 already_AddRefed<MediaEncoder> MediaEncoder::CreateEncoder(
647 RefPtr<TaskQueue> aEncoderThread, const nsAString& aMimeType,
648 uint32_t aAudioBitrate, uint32_t aVideoBitrate, uint8_t aTrackTypes,
649 TrackRate aTrackRate, uint64_t aMaxMemory, TimeDuration aTimeslice) {
650 AUTO_PROFILER_LABEL("MediaEncoder::CreateEncoder", OTHER);
652 UniquePtr<ContainerWriter> writer;
653 UniquePtr<AudioTrackEncoder> audioEncoder;
654 UniquePtr<VideoTrackEncoder> videoEncoder;
655 auto encodedAudioQueue = MakeUnique<MediaQueue<EncodedFrame>>();
656 auto encodedVideoQueue = MakeUnique<MediaQueue<EncodedFrame>>();
657 auto driftCompensator =
658 MakeRefPtr<DriftCompensator>(aEncoderThread, aTrackRate);
660 Maybe<MediaContainerType> mimeType = MakeMediaContainerType(aMimeType);
661 if (!mimeType) {
662 return nullptr;
665 for (const auto& codec : mimeType->ExtendedType().Codecs().Range()) {
666 if (codec.EqualsLiteral("opus")) {
667 MOZ_ASSERT(!audioEncoder);
668 audioEncoder =
669 MakeUnique<OpusTrackEncoder>(aTrackRate, *encodedAudioQueue);
670 } else if (codec.EqualsLiteral("vp8") || codec.EqualsLiteral("vp8.0")) {
671 MOZ_ASSERT(!videoEncoder);
672 if (Preferences::GetBool("media.recorder.video.frame_drops", true)) {
673 videoEncoder = MakeUnique<VP8TrackEncoder>(driftCompensator, aTrackRate,
674 *encodedVideoQueue,
675 FrameDroppingMode::ALLOW);
676 } else {
677 videoEncoder = MakeUnique<VP8TrackEncoder>(driftCompensator, aTrackRate,
678 *encodedVideoQueue,
679 FrameDroppingMode::DISALLOW);
681 } else {
682 MOZ_CRASH("Unknown codec");
686 if (mimeType->Type() == MEDIAMIMETYPE(VIDEO_WEBM) ||
687 mimeType->Type() == MEDIAMIMETYPE(AUDIO_WEBM)) {
688 MOZ_ASSERT_IF(mimeType->Type() == MEDIAMIMETYPE(AUDIO_WEBM), !videoEncoder);
689 writer = MakeUnique<WebMWriter>();
690 } else if (mimeType->Type() == MEDIAMIMETYPE(AUDIO_OGG)) {
691 MOZ_ASSERT(audioEncoder);
692 MOZ_ASSERT(!videoEncoder);
693 writer = MakeUnique<OggWriter>();
695 NS_ENSURE_TRUE(writer, nullptr);
697 LOG(LogLevel::Info,
698 ("Create encoder result:a[%p](%u bps) v[%p](%u bps) w[%p] mimeType = "
699 "%s.",
700 audioEncoder.get(), aAudioBitrate, videoEncoder.get(), aVideoBitrate,
701 writer.get(), NS_ConvertUTF16toUTF8(aMimeType).get()));
703 if (audioEncoder) {
704 audioEncoder->SetWorkerThread(aEncoderThread);
705 if (aAudioBitrate != 0) {
706 audioEncoder->SetBitrate(aAudioBitrate);
709 if (videoEncoder) {
710 videoEncoder->SetWorkerThread(aEncoderThread);
711 if (aVideoBitrate != 0) {
712 videoEncoder->SetBitrate(aVideoBitrate);
715 RefPtr<MediaEncoder> encoder = new MediaEncoder(
716 std::move(aEncoderThread), std::move(driftCompensator), std::move(writer),
717 std::move(audioEncoder), std::move(videoEncoder),
718 std::move(encodedAudioQueue), std::move(encodedVideoQueue), aTrackRate,
719 aMimeType, aMaxMemory, aTimeslice);
721 encoder->RegisterListeners();
723 return encoder.forget();
726 nsresult MediaEncoder::GetEncodedData(
727 nsTArray<nsTArray<uint8_t>>* aOutputBufs) {
728 AUTO_PROFILER_LABEL("MediaEncoder::GetEncodedData", OTHER);
730 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
732 LOG(LogLevel::Verbose,
733 ("GetEncodedData TimeStamp = %f", GetEncodeTimeStamp()));
735 if (!mInitialized) {
736 return NS_ERROR_NOT_INITIALIZED;
739 nsresult rv = mMuxer->GetData(aOutputBufs);
740 if (mMuxer->IsFinished()) {
741 mCompleted = true;
744 LOG(LogLevel::Verbose,
745 ("END GetEncodedData TimeStamp=%f "
746 "mCompleted=%d, aComplete=%d, vComplete=%d",
747 GetEncodeTimeStamp(), mCompleted,
748 !mAudioEncoder || mAudioEncoder->IsEncodingComplete(),
749 !mVideoEncoder || mVideoEncoder->IsEncodingComplete()));
751 return rv;
754 void MediaEncoder::MaybeShutdown() {
755 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
756 if (!mEncodedAudioQueue->IsFinished()) {
757 LOG(LogLevel::Debug,
758 ("MediaEncoder %p not shutting down, audio is still live", this));
759 return;
762 if (!mEncodedVideoQueue->IsFinished()) {
763 LOG(LogLevel::Debug,
764 ("MediaEncoder %p not shutting down, video is still live", this));
765 return;
768 mShutdownEvent.Notify();
770 // Stop will Shutdown() gracefully.
771 Unused << InvokeAsync(mMainThread, this, __func__, &MediaEncoder::Stop);
774 RefPtr<GenericNonExclusivePromise> MediaEncoder::Shutdown() {
775 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
776 if (mShutdownPromise) {
777 return mShutdownPromise;
780 LOG(LogLevel::Info, ("MediaEncoder is shutting down."));
782 AutoTArray<RefPtr<GenericNonExclusivePromise>, 2> shutdownPromises;
783 if (mAudioListener) {
784 shutdownPromises.AppendElement(mAudioListener->OnShutdown());
786 if (mVideoListener) {
787 shutdownPromises.AppendElement(mVideoListener->OnShutdown());
790 mShutdownPromise =
791 GenericNonExclusivePromise::All(mEncoderThread, shutdownPromises)
792 ->Then(mEncoderThread, __func__,
793 [](const GenericNonExclusivePromise::AllPromiseType::
794 ResolveOrRejectValue& aValue) {
795 if (aValue.IsResolve()) {
796 return GenericNonExclusivePromise::CreateAndResolve(
797 true, __func__);
799 return GenericNonExclusivePromise::CreateAndReject(
800 aValue.RejectValue(), __func__);
803 mShutdownPromise->Then(
804 mEncoderThread, __func__, [self = RefPtr<MediaEncoder>(this), this] {
805 if (mAudioEncoder) {
806 mAudioEncoder->UnregisterListener(mEncoderListener);
808 if (mVideoEncoder) {
809 mVideoEncoder->UnregisterListener(mEncoderListener);
811 mEncoderListener->Forget();
812 mMuxer->Disconnect();
813 mAudioPushListener.DisconnectIfExists();
814 mAudioFinishListener.DisconnectIfExists();
815 mVideoPushListener.DisconnectIfExists();
816 mVideoFinishListener.DisconnectIfExists();
819 return mShutdownPromise;
822 RefPtr<GenericNonExclusivePromise> MediaEncoder::Stop() {
823 MOZ_ASSERT(NS_IsMainThread());
825 LOG(LogLevel::Info, ("MediaEncoder %p Stop", this));
827 DisconnectTracks();
829 return InvokeAsync(mEncoderThread, this, __func__, &MediaEncoder::Shutdown);
832 RefPtr<GenericNonExclusivePromise> MediaEncoder::Cancel() {
833 MOZ_ASSERT(NS_IsMainThread());
835 LOG(LogLevel::Info, ("MediaEncoder %p Cancel", this));
837 DisconnectTracks();
839 return InvokeAsync(mEncoderThread, __func__,
840 [self = RefPtr<MediaEncoder>(this), this]() {
841 if (mAudioEncoder) {
842 mAudioEncoder->Cancel();
844 if (mVideoEncoder) {
845 mVideoEncoder->Cancel();
847 return Shutdown();
851 bool MediaEncoder::HasError() {
852 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
853 return mError;
856 void MediaEncoder::SetError() {
857 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
859 if (mError) {
860 return;
863 mError = true;
864 mErrorEvent.Notify();
867 auto MediaEncoder::RequestData() -> RefPtr<BlobPromise> {
868 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
869 TimeUnit muxedEndTime = std::min(mMuxedAudioEndTime, mMuxedVideoEndTime);
870 mLastBlobTime = muxedEndTime;
871 mLastExtractTime = muxedEndTime;
872 return Extract()->Then(
873 mMainThread, __func__,
874 [this, self = RefPtr<MediaEncoder>(this)](
875 const GenericPromise::ResolveOrRejectValue& aValue) {
876 // Even if rejected, we want to gather what has already been
877 // extracted into the current blob and expose that.
878 Unused << NS_WARN_IF(aValue.IsReject());
879 return GatherBlob();
883 void MediaEncoder::MaybeCreateMutableBlobStorage() {
884 MOZ_ASSERT(NS_IsMainThread());
885 if (!mMutableBlobStorage) {
886 mMutableBlobStorage = new MutableBlobStorage(
887 MutableBlobStorage::eCouldBeInTemporaryFile, nullptr, mMaxMemory);
891 void MediaEncoder::OnEncodedAudioPushed(const RefPtr<EncodedFrame>& aFrame) {
892 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
893 mMuxedAudioEndTime = aFrame->GetEndTime();
894 MaybeExtractOrGatherBlob();
897 void MediaEncoder::OnEncodedVideoPushed(const RefPtr<EncodedFrame>& aFrame) {
898 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
899 mMuxedVideoEndTime = aFrame->GetEndTime();
900 MaybeExtractOrGatherBlob();
903 void MediaEncoder::MaybeExtractOrGatherBlob() {
904 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
906 TimeUnit muxedEndTime = std::min(mMuxedAudioEndTime, mMuxedVideoEndTime);
907 if ((muxedEndTime - mLastBlobTime).ToTimeDuration() >= mTimeslice) {
908 LOG(LogLevel::Verbose, ("MediaEncoder %p Muxed %.2fs of data since last "
909 "blob. Issuing new blob.",
910 this, (muxedEndTime - mLastBlobTime).ToSeconds()));
911 RequestData()->Then(mEncoderThread, __func__,
912 [this, self = RefPtr<MediaEncoder>(this)](
913 const BlobPromise::ResolveOrRejectValue& aValue) {
914 if (aValue.IsReject()) {
915 SetError();
916 return;
918 RefPtr<BlobImpl> blob = aValue.ResolveValue();
919 mDataAvailableEvent.Notify(std::move(blob));
923 if (muxedEndTime - mLastExtractTime > TimeUnit::FromSeconds(1)) {
924 // Extract data from the muxer at least every second.
925 LOG(LogLevel::Verbose,
926 ("MediaEncoder %p Muxed %.2fs of data since last "
927 "extract. Extracting more data into blob.",
928 this, (muxedEndTime - mLastExtractTime).ToSeconds()));
929 mLastExtractTime = muxedEndTime;
930 Unused << Extract();
934 // Pull encoded media data from MediaEncoder and put into MutableBlobStorage.
935 RefPtr<GenericPromise> MediaEncoder::Extract() {
936 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
938 LOG(LogLevel::Debug, ("MediaEncoder %p Extract", this));
940 AUTO_PROFILER_LABEL("MediaEncoder::Extract", OTHER);
942 // Pull encoded media data from MediaEncoder
943 nsTArray<nsTArray<uint8_t>> buffer;
944 nsresult rv = GetEncodedData(&buffer);
945 MOZ_ASSERT(rv != NS_ERROR_INVALID_ARG, "Invalid args can be prevented.");
946 if (NS_FAILED(rv)) {
947 MOZ_RELEASE_ASSERT(buffer.IsEmpty());
948 // Even if we failed to encode more data, it might be time to push a blob
949 // with already encoded data.
952 // To ensure Extract() promises are resolved in calling order, we always
953 // invoke the main thread. Even when the encoded buffer is empty.
954 return InvokeAsync(
955 mMainThread, __func__,
956 [self = RefPtr<MediaEncoder>(this), this, buffer = std::move(buffer)] {
957 MaybeCreateMutableBlobStorage();
958 for (const auto& part : buffer) {
959 if (part.IsEmpty()) {
960 continue;
963 nsresult rv =
964 mMutableBlobStorage->Append(part.Elements(), part.Length());
965 if (NS_WARN_IF(NS_FAILED(rv))) {
966 return GenericPromise::CreateAndReject(rv, __func__);
969 return GenericPromise::CreateAndResolve(true, __func__);
973 auto MediaEncoder::GatherBlob() -> RefPtr<BlobPromise> {
974 MOZ_ASSERT(NS_IsMainThread());
975 if (!mBlobPromise) {
976 return mBlobPromise = GatherBlobImpl();
978 return mBlobPromise = mBlobPromise->Then(mMainThread, __func__,
979 [self = RefPtr<MediaEncoder>(this)] {
980 return self->GatherBlobImpl();
984 auto MediaEncoder::GatherBlobImpl() -> RefPtr<BlobPromise> {
985 RefPtr<BlobStorer> storer = MakeAndAddRef<BlobStorer>();
986 MaybeCreateMutableBlobStorage();
987 mMutableBlobStorage->GetBlobImplWhenReady(NS_ConvertUTF16toUTF8(mMimeType),
988 storer);
989 mMutableBlobStorage = nullptr;
991 storer->Promise()->Then(
992 mMainThread, __func__,
993 [self = RefPtr<MediaEncoder>(this), p = storer->Promise()] {
994 if (self->mBlobPromise == p) {
995 // Reset BlobPromise.
996 self->mBlobPromise = nullptr;
1000 return storer->Promise();
1003 void MediaEncoder::DisconnectTracks() {
1004 MOZ_ASSERT(NS_IsMainThread());
1006 if (mAudioNode) {
1007 mAudioNode->GetTrack()->RemoveListener(mAudioListener);
1008 if (mInputPort) {
1009 mInputPort->Destroy();
1010 mInputPort = nullptr;
1012 if (mPipeTrack) {
1013 mPipeTrack->RemoveListener(mAudioListener);
1014 mPipeTrack->Destroy();
1015 mPipeTrack = nullptr;
1017 mAudioNode = nullptr;
1020 if (mAudioTrack) {
1021 RemoveMediaStreamTrack(mAudioTrack);
1024 if (mVideoTrack) {
1025 RemoveMediaStreamTrack(mVideoTrack);
1029 bool MediaEncoder::IsWebMEncoderEnabled() {
1030 return StaticPrefs::media_encoder_webm_enabled();
1033 void MediaEncoder::UpdateInitialized() {
1034 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1036 if (mInitialized) {
1037 // This could happen if an encoder re-inits due to a resolution change.
1038 return;
1041 if (mAudioEncoder && !mAudioEncoder->IsInitialized()) {
1042 LOG(LogLevel::Debug,
1043 ("MediaEncoder %p UpdateInitialized waiting for audio", this));
1044 return;
1047 if (mVideoEncoder && !mVideoEncoder->IsInitialized()) {
1048 LOG(LogLevel::Debug,
1049 ("MediaEncoder %p UpdateInitialized waiting for video", this));
1050 return;
1053 MOZ_ASSERT(mMuxer->NeedsMetadata());
1054 nsTArray<RefPtr<TrackMetadataBase>> meta;
1055 if (mAudioEncoder && !*meta.AppendElement(mAudioEncoder->GetMetadata())) {
1056 LOG(LogLevel::Error, ("Audio metadata is null"));
1057 SetError();
1058 return;
1060 if (mVideoEncoder && !*meta.AppendElement(mVideoEncoder->GetMetadata())) {
1061 LOG(LogLevel::Error, ("Video metadata is null"));
1062 SetError();
1063 return;
1066 if (NS_FAILED(mMuxer->SetMetadata(meta))) {
1067 LOG(LogLevel::Error, ("SetMetadata failed"));
1068 SetError();
1069 return;
1072 LOG(LogLevel::Info,
1073 ("MediaEncoder %p UpdateInitialized set metadata in muxer", this));
1075 mInitialized = true;
1078 void MediaEncoder::UpdateStarted() {
1079 MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1081 if (mStarted) {
1082 return;
1085 if (mAudioEncoder && !mAudioEncoder->IsStarted()) {
1086 return;
1089 if (mVideoEncoder && !mVideoEncoder->IsStarted()) {
1090 return;
1093 mStarted = true;
1095 // Start issuing timeslice-based blobs.
1096 MOZ_ASSERT(mLastBlobTime == TimeUnit::Zero());
1098 mStartedEvent.Notify();
1102 * SizeOfExcludingThis measures memory being used by the Media Encoder.
1103 * Currently it measures the size of the Encoder buffer and memory occupied
1104 * by mAudioEncoder, mVideoEncoder, and any current blob storage.
1106 auto MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
1107 -> RefPtr<SizeOfPromise> {
1108 MOZ_ASSERT(NS_IsMainThread());
1109 size_t blobStorageSize =
1110 mMutableBlobStorage ? mMutableBlobStorage->SizeOfCurrentMemoryBuffer()
1111 : 0;
1113 return InvokeAsync(
1114 mEncoderThread, __func__,
1115 [self = RefPtr<MediaEncoder>(this), this, blobStorageSize,
1116 aMallocSizeOf]() {
1117 size_t size = 0;
1118 if (mAudioEncoder) {
1119 size += mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf);
1121 if (mVideoEncoder) {
1122 size += mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf);
1124 return SizeOfPromise::CreateAndResolve(blobStorageSize + size,
1125 __func__);
1129 } // namespace mozilla
1131 #undef LOG