Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/encoder/MediaEncoder.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "AudioNodeEngine.h"
10
#include "AudioNodeStream.h"
11
#include "GeckoProfiler.h"
12
#include "MediaDecoder.h"
13
#include "MediaStreamVideoSink.h"
14
#include "mozilla/dom/AudioNode.h"
15
#include "mozilla/dom/AudioStreamTrack.h"
16
#include "mozilla/dom/MediaStreamTrack.h"
17
#include "mozilla/dom/VideoStreamTrack.h"
18
#include "mozilla/gfx/Point.h" // IntSize
19
#include "mozilla/Logging.h"
20
#include "mozilla/media/MediaUtils.h"
21
#include "mozilla/Preferences.h"
22
#include "mozilla/StaticPrefs.h"
23
#include "mozilla/StaticPtr.h"
24
#include "mozilla/TaskQueue.h"
25
#include "mozilla/Unused.h"
26
#include "nsIPrincipal.h"
27
#include "nsMimeTypes.h"
28
#include "OggWriter.h"
29
#include "OpusTrackEncoder.h"
30
#include "TimeUnits.h"
31
#include "Tracing.h"
32
33
#ifdef MOZ_WEBM_ENCODER
34
#include "VP8TrackEncoder.h"
35
#include "WebMWriter.h"
36
#endif
37
38
#ifdef LOG
39
#undef LOG
40
#endif
41
42
mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder");
43
0
#define LOG(type, msg) MOZ_LOG(gMediaEncoderLog, type, msg)
44
45
namespace mozilla {
46
47
using namespace dom;
48
using namespace media;
49
50
class MediaEncoder::AudioTrackListener : public DirectMediaStreamTrackListener
51
{
52
public:
53
  AudioTrackListener(AudioTrackEncoder* aEncoder,
54
                     TaskQueue* aEncoderThread)
55
    : mDirectConnected(false)
56
    , mInitialized(false)
57
    , mRemoved(false)
58
    , mEncoder(aEncoder)
59
    , mEncoderThread(aEncoderThread)
60
0
  {
61
0
    MOZ_ASSERT(mEncoder);
62
0
    MOZ_ASSERT(mEncoderThread);
63
0
  }
64
65
0
  void NotifyShutdown() {
66
0
    mShutdown = true;
67
0
  }
68
69
  void NotifyDirectListenerInstalled(InstallationResult aResult) override
70
0
  {
71
0
    if (aResult == InstallationResult::SUCCESS) {
72
0
      LOG(LogLevel::Info, ("Audio track direct listener installed"));
73
0
      mDirectConnected = true;
74
0
    } else {
75
0
      LOG(LogLevel::Info, ("Audio track failed to install direct listener"));
76
0
      MOZ_ASSERT(!mDirectConnected);
77
0
    }
78
0
  }
79
80
  void NotifyDirectListenerUninstalled() override
81
0
  {
82
0
    mDirectConnected = false;
83
0
84
0
    if (mRemoved) {
85
0
      mEncoder = nullptr;
86
0
      mEncoderThread = nullptr;
87
0
    }
88
0
  }
89
90
  void NotifyQueuedChanges(MediaStreamGraph* aGraph,
91
                           StreamTime aTrackOffset,
92
                           const MediaSegment& aQueuedMedia) override
93
0
  {
94
0
    TRACE_COMMENT("Encoder %p", mEncoder.get());
95
0
    MOZ_ASSERT(mEncoder);
96
0
    MOZ_ASSERT(mEncoderThread);
97
0
98
0
    if (mShutdown) {
99
0
      return;
100
0
    }
101
0
102
0
    if (!mInitialized) {
103
0
      nsresult rv =
104
0
        mEncoderThread->Dispatch(
105
0
          NewRunnableMethod<StreamTime>(
106
0
            "mozilla::AudioTrackEncoder::SetStartOffset",
107
0
            mEncoder, &AudioTrackEncoder::SetStartOffset, aTrackOffset));
108
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
109
0
      Unused << rv;
110
0
      mInitialized = true;
111
0
    }
112
0
113
0
    if (mDirectConnected) {
114
0
      if (aQueuedMedia.IsNull()) {
115
0
        nsresult rv =
116
0
          mEncoderThread->Dispatch(
117
0
            NewRunnableMethod<StreamTime>(
118
0
              "mozilla::AudioTrackEncoder::AdvanceBlockedInput",
119
0
              mEncoder, &AudioTrackEncoder::AdvanceBlockedInput,
120
0
              aQueuedMedia.GetDuration()));
121
0
        MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
122
0
        Unused << rv;
123
0
        return;
124
0
      }
125
0
    } else {
126
0
      NotifyRealtimeTrackData(aGraph, aTrackOffset, aQueuedMedia);
127
0
    }
128
0
129
0
    nsresult rv =
130
0
      mEncoderThread->Dispatch(
131
0
        NewRunnableMethod<StreamTime>(
132
0
          "mozilla::AudioTrackEncoder::AdvanceCurrentTime",
133
0
          mEncoder, &AudioTrackEncoder::AdvanceCurrentTime,
134
0
          aQueuedMedia.GetDuration()));
135
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
136
0
    Unused << rv;
137
0
  }
138
139
  void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
140
                               StreamTime aTrackOffset,
141
                               const MediaSegment& aMedia) override
142
0
  {
143
0
    TRACE_COMMENT("Encoder %p", mEncoder.get());
144
0
    MOZ_ASSERT(mEncoder);
145
0
    MOZ_ASSERT(mEncoderThread);
146
0
147
0
    if (mShutdown) {
148
0
      return;
149
0
    }
150
0
151
0
    const AudioSegment& audio = static_cast<const AudioSegment&>(aMedia);
152
0
153
0
    AudioSegment copy;
154
0
    copy.AppendSlice(audio, 0, audio.GetDuration());
155
0
156
0
    nsresult rv =
157
0
      mEncoderThread->Dispatch(
158
0
        NewRunnableMethod<StoreCopyPassByRRef<AudioSegment>>(
159
0
          "mozilla::AudioTrackEncoder::AppendAudioSegment",
160
0
          mEncoder, &AudioTrackEncoder::AppendAudioSegment, std::move(copy)));
161
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
162
0
    Unused << rv;
163
0
  }
164
165
  void NotifyEnded() override
166
0
  {
167
0
    MOZ_ASSERT(mEncoder);
168
0
    MOZ_ASSERT(mEncoderThread);
169
0
170
0
    if (mShutdown) {
171
0
      return;
172
0
    }
173
0
174
0
    nsresult rv =
175
0
      mEncoderThread->Dispatch(
176
0
        NewRunnableMethod("mozilla::AudioTrackEncoder::NotifyEndOfStream",
177
0
                          mEncoder, &AudioTrackEncoder::NotifyEndOfStream));
178
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
179
0
    Unused << rv;
180
0
  }
181
182
  void NotifyRemoved() override
183
0
  {
184
0
    if (!mShutdown) {
185
0
      nsresult rv =
186
0
        mEncoderThread->Dispatch(
187
0
          NewRunnableMethod("mozilla::AudioTrackEncoder::NotifyEndOfStream",
188
0
                            mEncoder, &AudioTrackEncoder::NotifyEndOfStream));
189
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
190
0
      Unused << rv;
191
0
    }
192
0
193
0
    mRemoved = true;
194
0
195
0
    if (!mDirectConnected) {
196
0
      mEncoder = nullptr;
197
0
      mEncoderThread = nullptr;
198
0
    }
199
0
  }
200
201
private:
202
  // True when MediaEncoder has shutdown and destroyed the TaskQueue.
203
  Atomic<bool> mShutdown;
204
  bool mDirectConnected;
205
  bool mInitialized;
206
  bool mRemoved;
207
  RefPtr<AudioTrackEncoder> mEncoder;
208
  RefPtr<TaskQueue> mEncoderThread;
209
};
210
211
class MediaEncoder::VideoTrackListener : public MediaStreamVideoSink
212
{
213
public:
214
  VideoTrackListener(VideoTrackEncoder* aEncoder,
215
                     TaskQueue* aEncoderThread)
216
    : mDirectConnected(false)
217
    , mInitialized(false)
218
    , mRemoved(false)
219
    , mEncoder(aEncoder)
220
    , mEncoderThread(aEncoderThread)
221
0
  {
222
0
    MOZ_ASSERT(mEncoder);
223
0
    MOZ_ASSERT(mEncoderThread);
224
0
  }
225
226
0
  void NotifyShutdown() {
227
0
    mShutdown = true;
228
0
  }
229
230
  void NotifyDirectListenerInstalled(InstallationResult aResult) override
231
0
  {
232
0
    if (aResult == InstallationResult::SUCCESS) {
233
0
      LOG(LogLevel::Info, ("Video track direct listener installed"));
234
0
      mDirectConnected = true;
235
0
    } else {
236
0
      LOG(LogLevel::Info, ("Video track failed to install direct listener"));
237
0
      MOZ_ASSERT(!mDirectConnected);
238
0
      return;
239
0
    }
240
0
  }
241
242
  void NotifyDirectListenerUninstalled() override
243
0
  {
244
0
    mDirectConnected = false;
245
0
246
0
    if (mRemoved) {
247
0
      mEncoder = nullptr;
248
0
      mEncoderThread = nullptr;
249
0
    }
250
0
  }
251
252
  void NotifyQueuedChanges(MediaStreamGraph* aGraph,
253
                           StreamTime aTrackOffset,
254
                           const MediaSegment& aQueuedMedia) override
255
0
  {
256
0
    TRACE_COMMENT("Encoder %p", mEncoder.get());
257
0
    MOZ_ASSERT(mEncoder);
258
0
    MOZ_ASSERT(mEncoderThread);
259
0
260
0
    if (mShutdown) {
261
0
      return;
262
0
    }
263
0
264
0
    if (!mInitialized) {
265
0
      nsresult rv =
266
0
        mEncoderThread->Dispatch(
267
0
          NewRunnableMethod<StreamTime>(
268
0
            "mozilla::VideoTrackEncoder::SetStartOffset",
269
0
            mEncoder, &VideoTrackEncoder::SetStartOffset, aTrackOffset));
270
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
271
0
      Unused << rv;
272
0
      mInitialized = true;
273
0
    }
274
0
275
0
    if (aQueuedMedia.IsNull()) {
276
0
      nsresult rv =
277
0
        mEncoderThread->Dispatch(
278
0
          NewRunnableMethod<StreamTime>(
279
0
            "mozilla::VideoTrackEncoder::AdvanceBlockedInput",
280
0
            mEncoder, &VideoTrackEncoder::AdvanceBlockedInput,
281
0
            aQueuedMedia.GetDuration()));
282
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
283
0
      Unused << rv;
284
0
      return;
285
0
    }
286
0
287
0
    nsresult rv =
288
0
      mEncoderThread->Dispatch(
289
0
        NewRunnableMethod<StreamTime>(
290
0
          "mozilla::VideoTrackEncoder::AdvanceCurrentTime",
291
0
          mEncoder, &VideoTrackEncoder::AdvanceCurrentTime,
292
0
          aQueuedMedia.GetDuration()));
293
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
294
0
    Unused << rv;
295
0
  }
296
297
  void SetCurrentFrames(const VideoSegment& aMedia) override
298
0
  {
299
0
    TRACE_COMMENT("Encoder %p", mEncoder.get());
300
0
    MOZ_ASSERT(mEncoder);
301
0
    MOZ_ASSERT(mEncoderThread);
302
0
303
0
    if (mShutdown) {
304
0
      return;
305
0
    }
306
0
307
0
    VideoSegment copy;
308
0
    copy.AppendSlice(aMedia, 0, aMedia.GetDuration());
309
0
310
0
    nsresult rv =
311
0
      mEncoderThread->Dispatch(
312
0
        NewRunnableMethod<StoreCopyPassByRRef<VideoSegment>>(
313
0
          "mozilla::VideoTrackEncoder::AppendVideoSegment",
314
0
          mEncoder, &VideoTrackEncoder::AppendVideoSegment, std::move(copy)));
315
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
316
0
    Unused << rv;
317
0
  }
318
319
0
  void ClearFrames() override {}
320
321
  void NotifyEnded() override
322
0
  {
323
0
    MOZ_ASSERT(mEncoder);
324
0
    MOZ_ASSERT(mEncoderThread);
325
0
326
0
    if (mShutdown) {
327
0
      return;
328
0
    }
329
0
330
0
    nsresult rv =
331
0
      mEncoderThread->Dispatch(
332
0
        NewRunnableMethod("mozilla::VideoTrackEncoder::NotifyEndOfStream",
333
0
                          mEncoder, &VideoTrackEncoder::NotifyEndOfStream));
334
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
335
0
    Unused << rv;
336
0
  }
337
338
  void NotifyRemoved() override
339
0
  {
340
0
    if (!mShutdown) {
341
0
      nsresult rv =
342
0
        mEncoderThread->Dispatch(
343
0
          NewRunnableMethod("mozilla::VideoTrackEncoder::NotifyEndOfStream",
344
0
                            mEncoder, &VideoTrackEncoder::NotifyEndOfStream));
345
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
346
0
      Unused << rv;
347
0
    }
348
0
349
0
    mRemoved = true;
350
0
351
0
    if (!mDirectConnected) {
352
0
      mEncoder = nullptr;
353
0
      mEncoderThread = nullptr;
354
0
    }
355
0
  }
356
357
private:
358
  // True when MediaEncoder has shutdown and destroyed the TaskQueue.
359
  Atomic<bool> mShutdown;
360
  bool mDirectConnected;
361
  bool mInitialized;
362
  bool mRemoved;
363
  RefPtr<VideoTrackEncoder> mEncoder;
364
  RefPtr<TaskQueue> mEncoderThread;
365
};
366
367
class MediaEncoder::EncoderListener : public TrackEncoderListener
368
{
369
public:
370
  EncoderListener(TaskQueue* aEncoderThread, MediaEncoder* aEncoder)
371
    : mEncoderThread(aEncoderThread)
372
    , mEncoder(aEncoder)
373
    , mPendingDataAvailable(false)
374
0
  {}
375
376
  void Forget()
377
0
  {
378
0
    MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
379
0
    mEncoder = nullptr;
380
0
  }
381
382
  void Initialized(TrackEncoder* aTrackEncoder) override
383
0
  {
384
0
    MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
385
0
    MOZ_ASSERT(aTrackEncoder->IsInitialized());
386
0
387
0
    if (!mEncoder) {
388
0
      return;
389
0
    }
390
0
391
0
    nsresult rv =
392
0
      mEncoderThread->Dispatch(
393
0
        NewRunnableMethod("mozilla::MediaEncoder::NotifyInitialized",
394
0
                          mEncoder, &MediaEncoder::NotifyInitialized));
395
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
396
0
    Unused << rv;
397
0
  }
398
399
  void DataAvailable(TrackEncoder* aTrackEncoder) override
400
0
  {
401
0
    MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
402
0
    MOZ_ASSERT(aTrackEncoder->IsInitialized());
403
0
404
0
    if (!mEncoder) {
405
0
      return;
406
0
    }
407
0
408
0
    if (mPendingDataAvailable) {
409
0
      return;
410
0
    }
411
0
412
0
    nsresult rv =
413
0
      mEncoderThread->Dispatch(
414
0
        NewRunnableMethod("mozilla::MediaEncoder::EncoderListener::DataAvailableImpl",
415
0
                          this, &EncoderListener::DataAvailableImpl));
416
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
417
0
    Unused << rv;
418
0
419
0
    mPendingDataAvailable = true;
420
0
  }
421
422
  void DataAvailableImpl()
423
0
  {
424
0
    MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
425
0
426
0
    if (!mEncoder) {
427
0
      return;
428
0
    }
429
0
430
0
    mEncoder->NotifyDataAvailable();
431
0
    mPendingDataAvailable = false;
432
0
  }
433
434
  void Error(TrackEncoder* aTrackEncoder) override
435
0
  {
436
0
    MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
437
0
438
0
    if (!mEncoder) {
439
0
      return;
440
0
    }
441
0
442
0
    nsresult rv =
443
0
      mEncoderThread->Dispatch(
444
0
        NewRunnableMethod("mozilla::MediaEncoder::SetError",
445
0
                          mEncoder, &MediaEncoder::SetError));
446
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
447
0
    Unused << rv;
448
0
  }
449
450
protected:
451
  RefPtr<TaskQueue> mEncoderThread;
452
  RefPtr<MediaEncoder> mEncoder;
453
  bool mPendingDataAvailable;
454
};
455
456
MediaEncoder::MediaEncoder(TaskQueue* aEncoderThread,
457
                           UniquePtr<ContainerWriter> aWriter,
458
                           AudioTrackEncoder* aAudioEncoder,
459
                           VideoTrackEncoder* aVideoEncoder,
460
                           const nsAString& aMIMEType)
461
  : mEncoderThread(aEncoderThread)
462
  , mWriter(std::move(aWriter))
463
  , mAudioEncoder(aAudioEncoder)
464
  , mVideoEncoder(aVideoEncoder)
465
  , mEncoderListener(MakeAndAddRef<EncoderListener>(mEncoderThread, this))
466
  , mStartTime(TimeStamp::Now())
467
  , mMIMEType(aMIMEType)
468
  , mInitialized(false)
469
  , mMetadataEncoded(false)
470
  , mCompleted(false)
471
  , mError(false)
472
  , mCanceled(false)
473
  , mShutdown(false)
474
0
{
475
0
  if (mAudioEncoder) {
476
0
    mAudioListener =
477
0
      MakeAndAddRef<AudioTrackListener>(mAudioEncoder, mEncoderThread);
478
0
    nsresult rv =
479
0
      mEncoderThread->Dispatch(
480
0
        NewRunnableMethod<RefPtr<EncoderListener>>(
481
0
          "mozilla::AudioTrackEncoder::RegisterListener",
482
0
          mAudioEncoder, &AudioTrackEncoder::RegisterListener,
483
0
          mEncoderListener));
484
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
485
0
    Unused << rv;
486
0
  }
487
0
  if (mVideoEncoder) {
488
0
    mVideoListener =
489
0
      MakeAndAddRef<VideoTrackListener>(mVideoEncoder, mEncoderThread);
490
0
    nsresult rv =
491
0
      mEncoderThread->Dispatch(
492
0
        NewRunnableMethod<RefPtr<EncoderListener>>(
493
0
          "mozilla::VideoTrackEncoder::RegisterListener",
494
0
          mVideoEncoder, &VideoTrackEncoder::RegisterListener,
495
0
          mEncoderListener));
496
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
497
0
    Unused << rv;
498
0
  }
499
0
}
500
501
MediaEncoder::~MediaEncoder()
502
0
{
503
0
  MOZ_ASSERT(mListeners.IsEmpty());
504
0
}
505
506
void
507
MediaEncoder::Suspend(TimeStamp aTime)
508
0
{
509
0
  auto& ae = mAudioEncoder;
510
0
  auto& ve = mVideoEncoder;
511
0
  nsresult rv =
512
0
    mEncoderThread->Dispatch(NewRunnableFrom([ae, ve, aTime]() {
513
0
      if (ae) {
514
0
        ae->Suspend(aTime);
515
0
      }
516
0
      if (ve) {
517
0
        ve->Suspend(aTime);
518
0
      }
519
0
      return NS_OK;
520
0
    }));
521
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
522
0
  Unused << rv;
523
0
}
524
525
void
526
MediaEncoder::Resume(TimeStamp aTime)
527
0
{
528
0
  auto& ae = mAudioEncoder;
529
0
  auto& ve = mVideoEncoder;
530
0
  nsresult rv =
531
0
    mEncoderThread->Dispatch(NewRunnableFrom([ae, ve, aTime]() {
532
0
      if (ae) {
533
0
        ae->Resume(aTime);
534
0
      }
535
0
      if (ve) {
536
0
        ve->Resume(aTime);
537
0
      }
538
0
      return NS_OK;
539
0
    }));
540
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
541
0
  Unused << rv;
542
0
}
543
544
void
545
MediaEncoder::ConnectAudioNode(AudioNode* aNode, uint32_t aOutput)
546
0
{
547
0
  MOZ_ASSERT(NS_IsMainThread());
548
0
549
0
  if (mAudioNode) {
550
0
    MOZ_ASSERT(false, "Only one audio node supported");
551
0
    return;
552
0
  }
553
0
554
0
  // Only AudioNodeStream of kind EXTERNAL_OUTPUT stores output audio data in
555
0
  // the track (see AudioNodeStream::AdvanceOutputSegment()). That means track
556
0
  // union stream in recorder session won't be able to copy data from the
557
0
  // stream of non-destination node. Create a pipe stream in this case.
558
0
  if (aNode->NumberOfOutputs() > 0) {
559
0
    AudioContext* ctx = aNode->Context();
560
0
    AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
561
0
    AudioNodeStream::Flags flags =
562
0
      AudioNodeStream::EXTERNAL_OUTPUT |
563
0
      AudioNodeStream::NEED_MAIN_THREAD_FINISHED;
564
0
    mPipeStream = AudioNodeStream::Create(ctx, engine, flags, ctx->Graph());
565
0
    AudioNodeStream* ns = aNode->GetStream();
566
0
    if (ns) {
567
0
      mInputPort =
568
0
        mPipeStream->AllocateInputPort(aNode->GetStream(),
569
0
                                       TRACK_ANY, TRACK_ANY,
570
0
                                       0, aOutput);
571
0
    }
572
0
  }
573
0
574
0
  mAudioNode = aNode;
575
0
576
0
  if (mPipeStream) {
577
0
    mPipeStream->AddTrackListener(mAudioListener, AudioNodeStream::AUDIO_TRACK);
578
0
  } else {
579
0
    mAudioNode->GetStream()->AddTrackListener(mAudioListener, AudioNodeStream::AUDIO_TRACK);
580
0
  }
581
0
}
582
583
void
584
MediaEncoder::ConnectMediaStreamTrack(MediaStreamTrack* aTrack)
585
0
{
586
0
  MOZ_ASSERT(NS_IsMainThread());
587
0
588
0
  if (aTrack->Ended()) {
589
0
    NS_ASSERTION(false, "Cannot connect ended track");
590
0
    return;
591
0
  }
592
0
593
0
  if (AudioStreamTrack* audio = aTrack->AsAudioStreamTrack()) {
594
0
    if (!mAudioEncoder) {
595
0
      MOZ_ASSERT(false, "No audio encoder for this audio track");
596
0
      return;
597
0
    }
598
0
    if (mAudioTrack) {
599
0
      MOZ_ASSERT(false, "Only one audio track supported.");
600
0
      return;
601
0
    }
602
0
    if (!mAudioListener) {
603
0
      MOZ_ASSERT(false, "No audio listener for this audio track");
604
0
      return;
605
0
    }
606
0
607
0
    mAudioTrack = audio;
608
0
    // With full duplex we don't risk having audio come in late to the MSG
609
0
    // so we won't need a direct listener.
610
0
    const bool enableDirectListener =
611
0
      !Preferences::GetBool("media.navigator.audio.full_duplex", false);
612
0
    if (enableDirectListener) {
613
0
      audio->AddDirectListener(mAudioListener);
614
0
    }
615
0
    audio->AddListener(mAudioListener);
616
0
  } else if (VideoStreamTrack* video = aTrack->AsVideoStreamTrack()) {
617
0
    if(!mVideoEncoder) {
618
0
      MOZ_ASSERT(false, "No video encoder for this video track");
619
0
      return;
620
0
    }
621
0
    if (mVideoTrack) {
622
0
      MOZ_ASSERT(false, "Only one video track supported.");
623
0
      return;
624
0
    }
625
0
    if (!mVideoListener) {
626
0
      MOZ_ASSERT(false, "No video listener for this audio track");
627
0
      return;
628
0
    }
629
0
630
0
    mVideoTrack = video;
631
0
    video->AddVideoOutput(mVideoListener);
632
0
    video->AddListener(mVideoListener);
633
0
  } else {
634
0
    MOZ_ASSERT(false, "Unknown track type");
635
0
  }
636
0
}
637
638
void
639
MediaEncoder::RemoveMediaStreamTrack(MediaStreamTrack* aTrack)
640
0
{
641
0
  if (!aTrack) {
642
0
    MOZ_ASSERT(false);
643
0
    return;
644
0
  }
645
0
646
0
  if (AudioStreamTrack* audio = aTrack->AsAudioStreamTrack()) {
647
0
    if (audio != mAudioTrack) {
648
0
      MOZ_ASSERT(false, "Not connected to this audio track");
649
0
      return;
650
0
    }
651
0
652
0
    if (mAudioListener) {
653
0
      audio->RemoveDirectListener(mAudioListener);
654
0
      audio->RemoveListener(mAudioListener);
655
0
    }
656
0
    mAudioTrack = nullptr;
657
0
  } else if (VideoStreamTrack* video = aTrack->AsVideoStreamTrack()) {
658
0
    if (video != mVideoTrack) {
659
0
      MOZ_ASSERT(false, "Not connected to this video track");
660
0
      return;
661
0
    }
662
0
663
0
    if (mVideoListener) {
664
0
      video->RemoveVideoOutput(mVideoListener);
665
0
      video->RemoveListener(mVideoListener);
666
0
    }
667
0
    mVideoTrack = nullptr;
668
0
  }
669
0
}
670
671
/* static */
672
already_AddRefed<MediaEncoder>
673
MediaEncoder::CreateEncoder(TaskQueue* aEncoderThread,
674
                            const nsAString& aMIMEType,
675
                            uint32_t aAudioBitrate,
676
                            uint32_t aVideoBitrate,
677
                            uint8_t aTrackTypes,
678
                            TrackRate aTrackRate)
679
0
{
680
0
  AUTO_PROFILER_LABEL("MediaEncoder::CreateEncoder", OTHER);
681
0
682
0
  UniquePtr<ContainerWriter> writer;
683
0
  RefPtr<AudioTrackEncoder> audioEncoder;
684
0
  RefPtr<VideoTrackEncoder> videoEncoder;
685
0
  nsString mimeType;
686
0
687
0
  if (!aTrackTypes) {
688
0
    MOZ_ASSERT(false);
689
0
    LOG(LogLevel::Error, ("No TrackTypes"));
690
0
    return nullptr;
691
0
  }
692
0
#ifdef MOZ_WEBM_ENCODER
693
0
  else if (MediaEncoder::IsWebMEncoderEnabled() &&
694
0
      (aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
695
0
       (aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
696
0
    if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK &&
697
0
        MediaDecoder::IsOpusEnabled()) {
698
0
      audioEncoder = MakeAndAddRef<OpusTrackEncoder>(aTrackRate);
699
0
      NS_ENSURE_TRUE(audioEncoder, nullptr);
700
0
    }
701
0
    if (Preferences::GetBool("media.recorder.video.frame_drops", true)) {
702
0
      videoEncoder = MakeAndAddRef<VP8TrackEncoder>(aTrackRate, FrameDroppingMode::ALLOW);
703
0
    } else {
704
0
      videoEncoder = MakeAndAddRef<VP8TrackEncoder>(aTrackRate, FrameDroppingMode::DISALLOW);
705
0
    }
706
0
    writer = MakeUnique<WebMWriter>(aTrackTypes);
707
0
    NS_ENSURE_TRUE(writer, nullptr);
708
0
    NS_ENSURE_TRUE(videoEncoder, nullptr);
709
0
    mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
710
0
  }
711
0
#endif //MOZ_WEBM_ENCODER
712
0
  else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
713
0
           (aMIMEType.EqualsLiteral(AUDIO_OGG) ||
714
0
            (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
715
0
    writer = MakeUnique<OggWriter>();
716
0
    audioEncoder = MakeAndAddRef<OpusTrackEncoder>(aTrackRate);
717
0
    NS_ENSURE_TRUE(writer, nullptr);
718
0
    NS_ENSURE_TRUE(audioEncoder, nullptr);
719
0
    mimeType = NS_LITERAL_STRING(AUDIO_OGG);
720
0
  }
721
0
  else {
722
0
    LOG(LogLevel::Error, ("Can not find any encoder to record this media stream"));
723
0
    return nullptr;
724
0
  }
725
0
726
0
  LOG(LogLevel::Info, ("Create encoder result:a[%p](%u bps) v[%p](%u bps) w[%p] mimeType = %s.",
727
0
                       audioEncoder.get(), aAudioBitrate,
728
0
                       videoEncoder.get(), aVideoBitrate,
729
0
                       writer.get(), NS_ConvertUTF16toUTF8(mimeType).get()));
730
0
731
0
  if (audioEncoder) {
732
0
    audioEncoder->SetWorkerThread(aEncoderThread);
733
0
    if (aAudioBitrate != 0) {
734
0
      audioEncoder->SetBitrate(aAudioBitrate);
735
0
    }
736
0
  }
737
0
  if (videoEncoder) {
738
0
    videoEncoder->SetWorkerThread(aEncoderThread);
739
0
    if (aVideoBitrate != 0) {
740
0
      videoEncoder->SetBitrate(aVideoBitrate);
741
0
    }
742
0
  }
743
0
  return MakeAndAddRef<MediaEncoder>(aEncoderThread,
744
0
                                     std::move(writer),
745
0
                                     audioEncoder,
746
0
                                     videoEncoder,
747
0
                                     mimeType);
748
0
}
749
750
nsresult
751
MediaEncoder::GetEncodedMetadata(nsTArray<nsTArray<uint8_t>>* aOutputBufs,
752
                                 nsAString& aMIMEType)
753
0
{
754
0
  AUTO_PROFILER_LABEL("MediaEncoder::GetEncodedMetadata", OTHER);
755
0
756
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
757
0
758
0
  if (mShutdown) {
759
0
    MOZ_ASSERT(false);
760
0
    return NS_ERROR_FAILURE;
761
0
  }
762
0
763
0
  if (!mInitialized) {
764
0
    MOZ_ASSERT(false);
765
0
    return NS_ERROR_FAILURE;
766
0
  }
767
0
768
0
  if (mMetadataEncoded) {
769
0
    MOZ_ASSERT(false);
770
0
    return NS_ERROR_FAILURE;
771
0
  }
772
0
773
0
  aMIMEType = mMIMEType;
774
0
775
0
  LOG(LogLevel::Verbose, ("GetEncodedMetadata TimeStamp = %f", GetEncodeTimeStamp()));
776
0
777
0
  nsresult rv;
778
0
779
0
  if (mAudioEncoder) {
780
0
    if (!mAudioEncoder->IsInitialized()) {
781
0
      LOG(LogLevel::Error, ("GetEncodedMetadata Audio encoder not initialized"));
782
0
      MOZ_ASSERT(false);
783
0
      return NS_ERROR_FAILURE;
784
0
    }
785
0
    rv = CopyMetadataToMuxer(mAudioEncoder);
786
0
    if (NS_FAILED(rv)) {
787
0
      LOG(LogLevel::Error, ("Failed to Set Audio Metadata"));
788
0
      SetError();
789
0
      return rv;
790
0
    }
791
0
  }
792
0
  if (mVideoEncoder) {
793
0
    if (!mVideoEncoder->IsInitialized()) {
794
0
      LOG(LogLevel::Error, ("GetEncodedMetadata Video encoder not initialized"));
795
0
      MOZ_ASSERT(false);
796
0
      return NS_ERROR_FAILURE;
797
0
    }
798
0
    rv = CopyMetadataToMuxer(mVideoEncoder.get());
799
0
    if (NS_FAILED(rv)) {
800
0
      LOG(LogLevel::Error, ("Failed to Set Video Metadata"));
801
0
      SetError();
802
0
      return rv;
803
0
    }
804
0
  }
805
0
806
0
  rv = mWriter->GetContainerData(aOutputBufs,
807
0
                                 ContainerWriter::GET_HEADER);
808
0
  if (NS_FAILED(rv)) {
809
0
    LOG(LogLevel::Error,("Writer fail to generate header!"));
810
0
    SetError();
811
0
    return rv;
812
0
  }
813
0
  LOG(LogLevel::Verbose, ("Finish GetEncodedMetadata TimeStamp = %f", GetEncodeTimeStamp()));
814
0
  mMetadataEncoded = true;
815
0
816
0
  return NS_OK;
817
0
}
818
819
nsresult
820
MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs)
821
0
{
822
0
  AUTO_PROFILER_LABEL("MediaEncoder::GetEncodedData", OTHER);
823
0
824
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
825
0
826
0
  if (!mMetadataEncoded) {
827
0
    MOZ_ASSERT(false);
828
0
    return NS_ERROR_FAILURE;
829
0
  }
830
0
831
0
  nsresult rv;
832
0
  LOG(LogLevel::Verbose, ("GetEncodedData TimeStamp = %f", GetEncodeTimeStamp()));
833
0
  EncodedFrameContainer encodedData;
834
0
835
0
  if (mVideoEncoder) {
836
0
    // We're most likely to actually wait for a video frame, so do that first
837
0
    // to minimize capture offset/lipsync issues.
838
0
    rv = WriteEncodedDataToMuxer(mVideoEncoder);
839
0
    LOG(LogLevel::Verbose, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
840
0
    if (NS_FAILED(rv)) {
841
0
      LOG(LogLevel::Warning, ("Failed to write encoded video data to muxer"));
842
0
      return rv;
843
0
    }
844
0
  }
845
0
846
0
  if (mAudioEncoder) {
847
0
    rv = WriteEncodedDataToMuxer(mAudioEncoder);
848
0
    LOG(LogLevel::Verbose, ("Audio encoded TimeStamp = %f", GetEncodeTimeStamp()));
849
0
    if (NS_FAILED(rv)) {
850
0
      LOG(LogLevel::Warning, ("Failed to write encoded audio data to muxer"));
851
0
      return rv;
852
0
    }
853
0
  }
854
0
855
0
  // In audio only or video only case, let unavailable track's flag to be true.
856
0
  bool isAudioCompleted = !mAudioEncoder || mAudioEncoder->IsEncodingComplete();
857
0
  bool isVideoCompleted = !mVideoEncoder || mVideoEncoder->IsEncodingComplete();
858
0
  rv = mWriter->GetContainerData(aOutputBufs,
859
0
                                 isAudioCompleted && isVideoCompleted ?
860
0
                                 ContainerWriter::FLUSH_NEEDED : 0);
861
0
  if (mWriter->IsWritingComplete()) {
862
0
    mCompleted = true;
863
0
    Shutdown();
864
0
  }
865
0
866
0
  LOG(LogLevel::Verbose, ("END GetEncodedData TimeStamp=%f "
867
0
      "mCompleted=%d, aComplete=%d, vComplete=%d",
868
0
      GetEncodeTimeStamp(), mCompleted, isAudioCompleted, isVideoCompleted));
869
0
870
0
  return rv;
871
0
}
872
873
void
874
MediaEncoder::Shutdown()
875
0
{
876
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
877
0
  if (mShutdown) {
878
0
    return;
879
0
  }
880
0
  mShutdown = true;
881
0
882
0
  LOG(LogLevel::Info, ("MediaEncoder has been shut down."));
883
0
  if (mAudioEncoder) {
884
0
    mAudioEncoder->UnregisterListener(mEncoderListener);
885
0
  }
886
0
  if (mAudioListener) {
887
0
    mAudioListener->NotifyShutdown();
888
0
  }
889
0
  if (mVideoEncoder) {
890
0
    mVideoEncoder->UnregisterListener(mEncoderListener);
891
0
  }
892
0
  if (mVideoListener) {
893
0
    mVideoListener->NotifyShutdown();
894
0
  }
895
0
  mEncoderListener->Forget();
896
0
897
0
  if (mCanceled) {
898
0
    // Shutting down after being canceled. We cannot use the encoder thread.
899
0
    return;
900
0
  }
901
0
902
0
  auto listeners(mListeners);
903
0
  for (auto& l : listeners) {
904
0
    // We dispatch here since this method is typically called from
905
0
    // a DataAvailable() handler.
906
0
    nsresult rv =
907
0
      mEncoderThread->Dispatch(
908
0
        NewRunnableMethod("mozilla::MediaEncoderListener::Shutdown",
909
0
                          l, &MediaEncoderListener::Shutdown));
910
0
    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
911
0
    Unused << rv;
912
0
  }
913
0
}
914
915
nsresult
916
MediaEncoder::WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder)
917
0
{
918
0
  AUTO_PROFILER_LABEL("MediaEncoder::WriteEncodedDataToMuxer", OTHER);
919
0
920
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
921
0
922
0
  if (!aTrackEncoder) {
923
0
    NS_ERROR("No track encoder to get data from");
924
0
    return NS_ERROR_FAILURE;
925
0
  }
926
0
927
0
  if (aTrackEncoder->IsEncodingComplete()) {
928
0
    return NS_OK;
929
0
  }
930
0
931
0
  EncodedFrameContainer encodedData;
932
0
  nsresult rv = aTrackEncoder->GetEncodedTrack(encodedData);
933
0
  if (NS_FAILED(rv)) {
934
0
    // Encoding might be canceled.
935
0
    LOG(LogLevel::Error, ("Failed to get encoded data from encoder."));
936
0
    SetError();
937
0
    return rv;
938
0
  }
939
0
  rv = mWriter->WriteEncodedTrack(encodedData,
940
0
                                  aTrackEncoder->IsEncodingComplete() ?
941
0
                                  ContainerWriter::END_OF_STREAM : 0);
942
0
  if (NS_FAILED(rv)) {
943
0
    LOG(LogLevel::Error, ("Failed to write encoded track to the media container."));
944
0
    SetError();
945
0
  }
946
0
  return rv;
947
0
}
948
949
nsresult
950
MediaEncoder::CopyMetadataToMuxer(TrackEncoder *aTrackEncoder)
951
0
{
952
0
  AUTO_PROFILER_LABEL("MediaEncoder::CopyMetadataToMuxer", OTHER);
953
0
954
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
955
0
956
0
  if (!aTrackEncoder) {
957
0
    NS_ERROR("No track encoder to get metadata from");
958
0
    return NS_ERROR_FAILURE;
959
0
  }
960
0
961
0
  RefPtr<TrackMetadataBase> meta = aTrackEncoder->GetMetadata();
962
0
  if (meta == nullptr) {
963
0
    LOG(LogLevel::Error, ("metadata == null"));
964
0
    SetError();
965
0
    return NS_ERROR_ABORT;
966
0
  }
967
0
968
0
  nsresult rv = mWriter->SetMetadata(meta);
969
0
  if (NS_FAILED(rv)) {
970
0
   LOG(LogLevel::Error, ("SetMetadata failed"));
971
0
   SetError();
972
0
  }
973
0
  return rv;
974
0
}
975
976
bool
977
MediaEncoder::IsShutdown()
978
0
{
979
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
980
0
  return mShutdown;
981
0
}
982
983
void
984
MediaEncoder::Cancel()
985
0
{
986
0
  MOZ_ASSERT(NS_IsMainThread());
987
0
988
0
  RefPtr<MediaEncoder> self = this;
989
0
  nsresult rv =
990
0
    mEncoderThread->Dispatch(NewRunnableFrom([self]() mutable {
991
0
      self->mCanceled = true;
992
0
993
0
      if (self->mAudioEncoder) {
994
0
        self->mAudioEncoder->Cancel();
995
0
      }
996
0
      if (self->mVideoEncoder) {
997
0
        self->mVideoEncoder->Cancel();
998
0
      }
999
0
      self->Shutdown();
1000
0
      return NS_OK;
1001
0
    }));
1002
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1003
0
  Unused << rv;
1004
0
}
1005
1006
bool
1007
MediaEncoder::HasError()
1008
0
{
1009
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1010
0
  return mError;
1011
0
}
1012
1013
void
1014
MediaEncoder::SetError()
1015
0
{
1016
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1017
0
1018
0
  if (mError) {
1019
0
    return;
1020
0
  }
1021
0
1022
0
  mError = true;
1023
0
  auto listeners(mListeners);
1024
0
  for (auto& l : listeners) {
1025
0
    l->Error();
1026
0
  }
1027
0
}
1028
1029
void
1030
MediaEncoder::Stop()
1031
0
{
1032
0
  MOZ_ASSERT(NS_IsMainThread());
1033
0
1034
0
  if (mAudioNode) {
1035
0
    mAudioNode->GetStream()->RemoveTrackListener(mAudioListener, AudioNodeStream::AUDIO_TRACK);
1036
0
    if (mInputPort) {
1037
0
      mInputPort->Destroy();
1038
0
      mInputPort = nullptr;
1039
0
    }
1040
0
    if (mPipeStream) {
1041
0
      mPipeStream->RemoveTrackListener(mAudioListener, AudioNodeStream::AUDIO_TRACK);
1042
0
      mPipeStream->Destroy();
1043
0
      mPipeStream = nullptr;
1044
0
    }
1045
0
    mAudioNode = nullptr;
1046
0
  }
1047
0
1048
0
  if (mAudioTrack) {
1049
0
    RemoveMediaStreamTrack(mAudioTrack);
1050
0
  }
1051
0
1052
0
  if (mVideoTrack) {
1053
0
    RemoveMediaStreamTrack(mVideoTrack);
1054
0
  }
1055
0
}
1056
1057
#ifdef MOZ_WEBM_ENCODER
1058
bool
1059
MediaEncoder::IsWebMEncoderEnabled()
1060
0
{
1061
0
  return StaticPrefs::MediaEncoderWebMEnabled();
1062
0
}
1063
#endif
1064
1065
void
1066
MediaEncoder::NotifyInitialized()
1067
0
{
1068
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1069
0
1070
0
  if (mInitialized) {
1071
0
    // This could happen if an encoder re-inits due to a resolution change.
1072
0
    return;
1073
0
  }
1074
0
1075
0
  if (mAudioEncoder && !mAudioEncoder->IsInitialized()) {
1076
0
    return;
1077
0
  }
1078
0
1079
0
  if (mVideoEncoder && !mVideoEncoder->IsInitialized()) {
1080
0
    return;
1081
0
  }
1082
0
1083
0
  mInitialized = true;
1084
0
1085
0
  auto listeners(mListeners);
1086
0
  for (auto& l : listeners) {
1087
0
    l->Initialized();
1088
0
  }
1089
0
}
1090
1091
void
1092
MediaEncoder::NotifyDataAvailable()
1093
0
{
1094
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1095
0
1096
0
  if (!mInitialized) {
1097
0
    return;
1098
0
  }
1099
0
1100
0
  auto listeners(mListeners);
1101
0
  for (auto& l : listeners) {
1102
0
    l->DataAvailable();
1103
0
  }
1104
0
}
1105
1106
void
1107
MediaEncoder::RegisterListener(MediaEncoderListener* aListener)
1108
0
{
1109
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1110
0
  MOZ_ASSERT(!mListeners.Contains(aListener));
1111
0
  mListeners.AppendElement(aListener);
1112
0
}
1113
1114
bool
1115
MediaEncoder::UnregisterListener(MediaEncoderListener* aListener)
1116
0
{
1117
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1118
0
  return mListeners.RemoveElement(aListener);
1119
0
}
1120
1121
/*
1122
 * SizeOfExcludingThis measures memory being used by the Media Encoder.
1123
 * Currently it measures the size of the Encoder buffer and memory occupied
1124
 * by mAudioEncoder and mVideoEncoder.
1125
 */
1126
size_t
1127
MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
1128
0
{
1129
0
  MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
1130
0
1131
0
  size_t size = 0;
1132
0
  if (mAudioEncoder) {
1133
0
    size += mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf);
1134
0
  }
1135
0
  if (mVideoEncoder) {
1136
0
    size += mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf);
1137
0
  }
1138
0
  return size;
1139
0
}
1140
1141
void
1142
MediaEncoder::SetVideoKeyFrameInterval(int32_t aVideoKeyFrameInterval)
1143
0
{
1144
0
  if (!mVideoEncoder) {
1145
0
    return;
1146
0
  }
1147
0
1148
0
  MOZ_ASSERT(mEncoderThread);
1149
0
  nsresult rv =
1150
0
    mEncoderThread->Dispatch(
1151
0
      NewRunnableMethod<int32_t>(
1152
0
        "mozilla::VideoTrackEncoder::SetKeyFrameInterval",
1153
0
        mVideoEncoder, &VideoTrackEncoder::SetKeyFrameInterval,
1154
0
        aVideoKeyFrameInterval));
1155
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1156
0
  Unused << rv;
1157
0
}
1158
1159
} // namespace mozilla