Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/encoder/TrackEncoder.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 "TrackEncoder.h"
7
8
#include "AudioChannelFormat.h"
9
#include "GeckoProfiler.h"
10
#include "MediaStreamGraph.h"
11
#include "MediaStreamListener.h"
12
#include "mozilla/AbstractThread.h"
13
#include "mozilla/Logging.h"
14
#include "VideoUtils.h"
15
#include "mozilla/Logging.h"
16
17
namespace mozilla {
18
19
LazyLogModule gTrackEncoderLog("TrackEncoder");
20
0
#define TRACK_LOG(type, msg) MOZ_LOG(gTrackEncoderLog, type, msg)
21
22
static const int DEFAULT_CHANNELS = 1;
23
static const int DEFAULT_SAMPLING_RATE = 16000;
24
static const int DEFAULT_FRAME_WIDTH = 640;
25
static const int DEFAULT_FRAME_HEIGHT = 480;
26
// 1 second threshold if the audio encoder cannot be initialized.
27
static const int AUDIO_INIT_FAILED_DURATION = 1;
28
// 30 second threshold if the video encoder cannot be initialized.
29
static const int VIDEO_INIT_FAILED_DURATION = 30;
30
// A maximal key frame interval allowed to set.
31
// Longer values will be shorten to this value.
32
static const int DEFAULT_KEYFRAME_INTERVAL_MS = 1000;
33
34
TrackEncoder::TrackEncoder(TrackRate aTrackRate)
35
  : mEncodingComplete(false)
36
  , mEosSetInEncoder(false)
37
  , mInitialized(false)
38
  , mEndOfStream(false)
39
  , mCanceled(false)
40
  , mCurrentTime(0)
41
  , mInitCounter(0)
42
  , mNotInitDuration(0)
43
  , mSuspended(false)
44
  , mTrackRate(aTrackRate)
45
0
{
46
0
}
47
48
bool
49
TrackEncoder::IsInitialized()
50
0
{
51
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
52
0
  return mInitialized;
53
0
}
54
55
bool
56
TrackEncoder::IsEncodingComplete()
57
0
{
58
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
59
0
  return mEncodingComplete;
60
0
}
61
62
void
63
TrackEncoder::SetInitialized()
64
0
{
65
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
66
0
67
0
  if (mInitialized) {
68
0
    return;
69
0
  }
70
0
71
0
  mInitialized = true;
72
0
73
0
  auto listeners(mListeners);
74
0
  for (auto& l : listeners) {
75
0
    l->Initialized(this);
76
0
  }
77
0
}
78
79
void
80
TrackEncoder::OnDataAvailable()
81
0
{
82
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
83
0
84
0
  auto listeners(mListeners);
85
0
  for (auto& l : listeners) {
86
0
    l->DataAvailable(this);
87
0
  }
88
0
}
89
90
void
91
TrackEncoder::OnError()
92
0
{
93
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
94
0
95
0
  Cancel();
96
0
97
0
  auto listeners(mListeners);
98
0
  for (auto& l : listeners) {
99
0
    l->Error(this);
100
0
  }
101
0
}
102
103
void
104
TrackEncoder::RegisterListener(TrackEncoderListener* aListener)
105
0
{
106
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
107
0
  MOZ_ASSERT(!mListeners.Contains(aListener));
108
0
  mListeners.AppendElement(aListener);
109
0
}
110
111
bool
112
TrackEncoder::UnregisterListener(TrackEncoderListener* aListener)
113
0
{
114
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
115
0
  return mListeners.RemoveElement(aListener);
116
0
}
117
118
void
119
TrackEncoder::SetWorkerThread(AbstractThread* aWorkerThread)
120
0
{
121
0
  mWorkerThread = aWorkerThread;
122
0
}
123
124
void
125
AudioTrackEncoder::Suspend(TimeStamp)
126
0
{
127
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
128
0
  TRACK_LOG(LogLevel::Info,
129
0
    ("[AudioTrackEncoder %p]: Suspend(), was %s",
130
0
     this, mSuspended ? "suspended" : "live"));
131
0
132
0
  if (mSuspended) {
133
0
    return;
134
0
  }
135
0
136
0
  mSuspended = true;
137
0
}
138
139
void
140
AudioTrackEncoder::Resume(TimeStamp)
141
0
{
142
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
143
0
  TRACK_LOG(LogLevel::Info,
144
0
    ("[AudioTrackEncoder %p]: Resume(), was %s",
145
0
     this, mSuspended ? "suspended" : "live"));
146
0
147
0
  if (!mSuspended) {
148
0
    return;
149
0
  }
150
0
151
0
  mSuspended = false;
152
0
}
153
154
void
155
AudioTrackEncoder::AppendAudioSegment(AudioSegment&& aSegment)
156
0
{
157
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
158
0
  TRACK_LOG(LogLevel::Verbose,
159
0
    ("[AudioTrackEncoder %p]: AppendAudioSegment() duration=%" PRIu64,
160
0
     this, aSegment.GetDuration()));
161
0
162
0
  if (mCanceled) {
163
0
    return;
164
0
  }
165
0
166
0
  if (mEndOfStream) {
167
0
    return;
168
0
  }
169
0
170
0
  mIncomingBuffer.AppendFrom(&aSegment);
171
0
}
172
173
void
174
AudioTrackEncoder::TakeTrackData(AudioSegment& aSegment)
175
0
{
176
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
177
0
178
0
  if (mCanceled) {
179
0
    return;
180
0
  }
181
0
182
0
  aSegment.AppendFrom(&mOutgoingBuffer);
183
0
}
184
185
void
186
AudioTrackEncoder::TryInit(const AudioSegment& aSegment, StreamTime aDuration)
187
0
{
188
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
189
0
190
0
  if (mInitialized) {
191
0
    return;
192
0
  }
193
0
194
0
  mInitCounter++;
195
0
  TRACK_LOG(LogLevel::Debug,
196
0
    ("[AudioTrackEncoder %p]: Inited the audio encoder %d times",
197
0
     this, mInitCounter));
198
0
199
0
  for (AudioSegment::ConstChunkIterator iter(aSegment); !iter.IsEnded(); iter.Next()) {
200
0
    // The number of channels is determined by the first non-null chunk, and
201
0
    // thus the audio encoder is initialized at this time.
202
0
    if (iter->IsNull()) {
203
0
      continue;
204
0
    }
205
0
206
0
    nsresult rv = Init(iter->mChannelData.Length(), mTrackRate);
207
0
208
0
    if (NS_SUCCEEDED(rv)) {
209
0
      TRACK_LOG(LogLevel::Info,
210
0
        ("[AudioTrackEncoder %p]: Successfully initialized!", this));
211
0
      return;
212
0
    } else {
213
0
      TRACK_LOG(LogLevel::Error,
214
0
        ("[AudioTrackEncoder %p]: Failed to initialize the encoder!", this));
215
0
      OnError();
216
0
      return;
217
0
    }
218
0
    break;
219
0
  }
220
0
221
0
  mNotInitDuration += aDuration;
222
0
  if (!mInitialized &&
223
0
      (mNotInitDuration / mTrackRate > AUDIO_INIT_FAILED_DURATION) &&
224
0
      mInitCounter > 1) {
225
0
    // Perform a best effort initialization since we haven't gotten any
226
0
    // data yet. Motivated by issues like Bug 1336367
227
0
    TRACK_LOG(LogLevel::Warning,
228
0
              ("[AudioTrackEncoder]: Initialize failed "
229
0
               "for %ds. Attempting to init with %d "
230
0
               "(default) channels!",
231
0
               AUDIO_INIT_FAILED_DURATION,
232
0
               DEFAULT_CHANNELS));
233
0
    nsresult rv = Init(DEFAULT_CHANNELS, mTrackRate);
234
0
    Telemetry::Accumulate(
235
0
      Telemetry::MEDIA_RECORDER_TRACK_ENCODER_INIT_TIMEOUT_TYPE, 0);
236
0
    if (NS_FAILED(rv)) {
237
0
      TRACK_LOG(LogLevel::Error,
238
0
                ("[AudioTrackEncoder %p]: Default-channel-init failed.", this));
239
0
      OnError();
240
0
      return;
241
0
    }
242
0
  }
243
0
}
244
245
void
246
AudioTrackEncoder::Cancel()
247
0
{
248
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
249
0
  TRACK_LOG(LogLevel::Info,
250
0
    ("[AudioTrackEncoder %p]: Cancel(), currentTime=%" PRIu64,
251
0
     this, mCurrentTime));
252
0
  mCanceled = true;
253
0
  mIncomingBuffer.Clear();
254
0
  mOutgoingBuffer.Clear();
255
0
}
256
257
void
258
AudioTrackEncoder::NotifyEndOfStream()
259
0
{
260
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
261
0
  TRACK_LOG(LogLevel::Info,
262
0
    ("[AudioTrackEncoder %p]: NotifyEndOfStream(), currentTime=%" PRIu64,
263
0
     this, mCurrentTime));
264
0
265
0
  if (!mCanceled && !mInitialized) {
266
0
    // If source audio track is completely silent till the end of encoding,
267
0
    // initialize the encoder with default channel counts and sampling rate.
268
0
    Init(DEFAULT_CHANNELS, DEFAULT_SAMPLING_RATE);
269
0
  }
270
0
271
0
  mEndOfStream = true;
272
0
273
0
  mIncomingBuffer.Clear();
274
0
275
0
  if (mInitialized && !mCanceled) {
276
0
    OnDataAvailable();
277
0
  }
278
0
}
279
280
void
281
AudioTrackEncoder::SetStartOffset(StreamTime aStartOffset)
282
0
{
283
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
284
0
  MOZ_ASSERT(mCurrentTime == 0);
285
0
  TRACK_LOG(LogLevel::Info,
286
0
    ("[AudioTrackEncoder %p]: SetStartOffset(), aStartOffset=%" PRIu64,
287
0
     this, aStartOffset));
288
0
  mIncomingBuffer.InsertNullDataAtStart(aStartOffset);
289
0
  mCurrentTime = aStartOffset;
290
0
}
291
292
void
293
AudioTrackEncoder::AdvanceBlockedInput(StreamTime aDuration)
294
0
{
295
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
296
0
  TRACK_LOG(LogLevel::Verbose,
297
0
    ("[AudioTrackEncoder %p]: AdvanceBlockedInput(), aDuration=%" PRIu64,
298
0
     this, aDuration));
299
0
300
0
  // We call Init here so it can account for aDuration towards the Init timeout
301
0
  TryInit(mOutgoingBuffer, aDuration);
302
0
303
0
  mIncomingBuffer.InsertNullDataAtStart(aDuration);
304
0
  mCurrentTime += aDuration;
305
0
}
306
307
void
308
AudioTrackEncoder::AdvanceCurrentTime(StreamTime aDuration)
309
0
{
310
0
  AUTO_PROFILER_LABEL("AudioTrackEncoder::AdvanceCurrentTime", OTHER);
311
0
312
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
313
0
314
0
  if (mCanceled) {
315
0
    return;
316
0
  }
317
0
318
0
  if (mEndOfStream) {
319
0
    return;
320
0
  }
321
0
322
0
  TRACK_LOG(LogLevel::Verbose,
323
0
    ("[AudioTrackEncoder %p]: AdvanceCurrentTime() %" PRIu64,
324
0
     this, aDuration));
325
0
326
0
  StreamTime currentTime = mCurrentTime + aDuration;
327
0
328
0
  if (mSuspended) {
329
0
    mCurrentTime = currentTime;
330
0
    mIncomingBuffer.ForgetUpTo(mCurrentTime);
331
0
    return;
332
0
  }
333
0
334
0
  if (currentTime <= mIncomingBuffer.GetDuration()) {
335
0
    mOutgoingBuffer.AppendSlice(mIncomingBuffer, mCurrentTime, currentTime);
336
0
337
0
    TryInit(mOutgoingBuffer, aDuration);
338
0
    if (mInitialized && mOutgoingBuffer.GetDuration() >= GetPacketDuration()) {
339
0
      OnDataAvailable();
340
0
    }
341
0
  } else {
342
0
    NS_ASSERTION(false, "AudioTrackEncoder::AdvanceCurrentTime Not enough data");
343
0
    TRACK_LOG(LogLevel::Error,
344
0
      ("[AudioTrackEncoder %p]: AdvanceCurrentTime() Not enough data. "
345
0
       "In incoming=%" PRIu64 ", aDuration=%" PRIu64 ", currentTime=%" PRIu64,
346
0
       this, mIncomingBuffer.GetDuration(), aDuration, currentTime));
347
0
    OnError();
348
0
  }
349
0
350
0
  mCurrentTime = currentTime;
351
0
  mIncomingBuffer.ForgetUpTo(mCurrentTime);
352
0
}
353
354
/*static*/
355
void
356
AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
357
                                       int32_t aDuration,
358
                                       uint32_t aOutputChannels,
359
                                       AudioDataValue* aOutput)
360
0
{
361
0
  uint32_t numChannelsToCopy = std::min(aOutputChannels,
362
0
                                        static_cast<uint32_t>(aChunk.mChannelData.Length()));
363
0
  switch(aChunk.mBufferFormat) {
364
0
    case AUDIO_FORMAT_S16: {
365
0
      AutoTArray<const int16_t*, 2> array;
366
0
      array.SetLength(numChannelsToCopy);
367
0
      for (uint32_t i = 0; i < array.Length(); i++) {
368
0
        array[i] = static_cast<const int16_t*>(aChunk.mChannelData[i]);
369
0
      }
370
0
      InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
371
0
      break;
372
0
    }
373
0
    case AUDIO_FORMAT_FLOAT32: {
374
0
      AutoTArray<const float*, 2> array;
375
0
      array.SetLength(numChannelsToCopy);
376
0
      for (uint32_t i = 0; i < array.Length(); i++) {
377
0
        array[i] = static_cast<const float*>(aChunk.mChannelData[i]);
378
0
      }
379
0
      InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
380
0
      break;
381
0
   }
382
0
   case AUDIO_FORMAT_SILENCE: {
383
0
      MOZ_ASSERT(false, "To implement.");
384
0
    }
385
0
  };
386
0
}
387
388
/*static*/
389
void
390
AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput,
391
                                         int32_t aDuration,
392
                                         int32_t aChannels,
393
                                         AudioDataValue* aOutput)
394
0
{
395
0
  for (int32_t i = 0; i < aChannels; ++i) {
396
0
    for(int32_t j = 0; j < aDuration; ++j) {
397
0
      aOutput[i * aDuration + j] = aInput[i + j * aChannels];
398
0
    }
399
0
  }
400
0
}
401
402
size_t
403
AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
404
0
{
405
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
406
0
  return mIncomingBuffer.SizeOfExcludingThis(aMallocSizeOf) +
407
0
         mOutgoingBuffer.SizeOfExcludingThis(aMallocSizeOf);
408
0
}
409
410
VideoTrackEncoder::VideoTrackEncoder(TrackRate aTrackRate, FrameDroppingMode aFrameDroppingMode)
411
  : TrackEncoder(aTrackRate)
412
  , mFrameWidth(0)
413
  , mFrameHeight(0)
414
  , mDisplayWidth(0)
415
  , mDisplayHeight(0)
416
  , mEncodedTicks(0)
417
  , mVideoBitrate(0)
418
  , mFrameDroppingMode(aFrameDroppingMode)
419
  , mKeyFrameInterval(DEFAULT_KEYFRAME_INTERVAL_MS)
420
0
{
421
0
  mLastChunk.mDuration = 0;
422
0
}
423
424
void
425
VideoTrackEncoder::Suspend(TimeStamp aTime)
426
0
{
427
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
428
0
  TRACK_LOG(LogLevel::Info,
429
0
    ("[VideoTrackEncoder %p]: Suspend(), was %s",
430
0
     this, mSuspended ? "suspended" : "live"));
431
0
432
0
  if (mSuspended) {
433
0
    return;
434
0
  }
435
0
436
0
  mSuspended = true;
437
0
  mSuspendTime = aTime;
438
0
}
439
440
void
441
VideoTrackEncoder::Resume(TimeStamp aTime)
442
0
{
443
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
444
0
  TRACK_LOG(LogLevel::Info,
445
0
    ("[VideoTrackEncoder %p]: Resume(), was %s",
446
0
     this, mSuspended ? "suspended" : "live"));
447
0
448
0
  if (!mSuspended) {
449
0
    return;
450
0
  }
451
0
452
0
  mSuspended = false;
453
0
454
0
  TimeDuration suspendDuration = aTime - mSuspendTime;
455
0
  if (!mLastChunk.mTimeStamp.IsNull()) {
456
0
    VideoChunk* nextChunk = mIncomingBuffer.FindChunkContaining(mCurrentTime);
457
0
    if (nextChunk && nextChunk->mTimeStamp < aTime) {
458
0
      nextChunk->mTimeStamp = aTime;
459
0
    }
460
0
    mLastChunk.mTimeStamp += suspendDuration;
461
0
  }
462
0
  if (!mStartTime.IsNull()) {
463
0
    mStartTime += suspendDuration;
464
0
  }
465
0
466
0
  mSuspendTime = TimeStamp();
467
0
}
468
469
void
470
VideoTrackEncoder::AppendVideoSegment(VideoSegment&& aSegment)
471
0
{
472
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
473
0
  TRACK_LOG(LogLevel::Verbose,
474
0
    ("[VideoTrackEncoder %p]: AppendVideoSegment() duration=%" PRIu64,
475
0
     this, aSegment.GetDuration()));
476
0
477
0
  if (mCanceled) {
478
0
    return;
479
0
  }
480
0
481
0
  if (mEndOfStream) {
482
0
    return;
483
0
  }
484
0
485
0
  mIncomingBuffer.AppendFrom(&aSegment);
486
0
}
487
488
void
489
VideoTrackEncoder::TakeTrackData(VideoSegment& aSegment)
490
0
{
491
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
492
0
493
0
  if (mCanceled) {
494
0
    return;
495
0
  }
496
0
497
0
  aSegment.AppendFrom(&mOutgoingBuffer);
498
0
  mOutgoingBuffer.Clear();
499
0
}
500
501
void
502
VideoTrackEncoder::Init(const VideoSegment& aSegment, StreamTime aDuration)
503
0
{
504
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
505
0
506
0
  if (mInitialized) {
507
0
    return;
508
0
  }
509
0
510
0
  mInitCounter++;
511
0
  TRACK_LOG(LogLevel::Debug,
512
0
    ("[VideoTrackEncoder %p]: Init the video encoder %d times",
513
0
     this, mInitCounter));
514
0
515
0
  for (VideoSegment::ConstChunkIterator iter(aSegment); !iter.IsEnded(); iter.Next()) {
516
0
    if (iter->IsNull()) {
517
0
      continue;
518
0
    }
519
0
520
0
    gfx::IntSize imgsize = iter->mFrame.GetImage()->GetSize();
521
0
    gfx::IntSize intrinsicSize = iter->mFrame.GetIntrinsicSize();
522
0
    nsresult rv = Init(imgsize.width, imgsize.height,
523
0
                       intrinsicSize.width, intrinsicSize.height);
524
0
525
0
    if (NS_SUCCEEDED(rv)) {
526
0
      TRACK_LOG(LogLevel::Info,
527
0
        ("[VideoTrackEncoder %p]: Successfully initialized!", this));
528
0
      return;
529
0
    } else {
530
0
      TRACK_LOG(LogLevel::Error,
531
0
        ("[VideoTrackEncoder %p]: Failed to initialize the encoder!", this));
532
0
      OnError();
533
0
    }
534
0
    break;
535
0
  }
536
0
537
0
  mNotInitDuration += aDuration;
538
0
  if ((mNotInitDuration / mTrackRate > VIDEO_INIT_FAILED_DURATION) &&
539
0
      mInitCounter > 1) {
540
0
    TRACK_LOG(LogLevel::Warning,
541
0
      ("[VideoTrackEncoder %p]: No successful init for %ds.",
542
0
       this, VIDEO_INIT_FAILED_DURATION));
543
0
    Telemetry::Accumulate(
544
0
      Telemetry::MEDIA_RECORDER_TRACK_ENCODER_INIT_TIMEOUT_TYPE, 1);
545
0
    OnError();
546
0
    return;
547
0
  }
548
0
}
549
550
void
551
VideoTrackEncoder::Cancel()
552
0
{
553
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
554
0
  TRACK_LOG(LogLevel::Info,
555
0
    ("[VideoTrackEncoder %p]: Cancel(), currentTime=%" PRIu64,
556
0
     this, mCurrentTime));
557
0
  mCanceled = true;
558
0
  mIncomingBuffer.Clear();
559
0
  mOutgoingBuffer.Clear();
560
0
  mLastChunk.SetNull(0);
561
0
}
562
563
void
564
VideoTrackEncoder::NotifyEndOfStream()
565
0
{
566
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
567
0
568
0
  if (!mCanceled && !mInitialized) {
569
0
    // If source video track is muted till the end of encoding, initialize the
570
0
    // encoder with default frame width, frame height, and track rate.
571
0
    Init(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT,
572
0
         DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT);
573
0
  }
574
0
575
0
  if (mEndOfStream) {
576
0
    // We have already been notified.
577
0
    return;
578
0
  }
579
0
580
0
  mEndOfStream = true;
581
0
  TRACK_LOG(LogLevel::Info,
582
0
    ("[VideoTrackEncoder %p]: NotifyEndOfStream(), currentTime=%" PRIu64,
583
0
     this, mCurrentTime));
584
0
585
0
  if (!mLastChunk.IsNull() && mLastChunk.mDuration > 0) {
586
0
    RefPtr<layers::Image> lastImage = mLastChunk.mFrame.GetImage();
587
0
    TRACK_LOG(LogLevel::Debug,
588
0
              ("[VideoTrackEncoder]: Appending last video frame %p, "
589
0
               "duration=%.5f", lastImage.get(),
590
0
               FramesToTimeUnit(mLastChunk.mDuration, mTrackRate).ToSeconds()));
591
0
    mOutgoingBuffer.AppendFrame(lastImage.forget(),
592
0
                                mLastChunk.mDuration,
593
0
                                mLastChunk.mFrame.GetIntrinsicSize(),
594
0
                                PRINCIPAL_HANDLE_NONE,
595
0
                                mLastChunk.mFrame.GetForceBlack(),
596
0
                                mLastChunk.mTimeStamp);
597
0
  }
598
0
599
0
  mIncomingBuffer.Clear();
600
0
  mLastChunk.SetNull(0);
601
0
602
0
  if (mInitialized && !mCanceled) {
603
0
    OnDataAvailable();
604
0
  }
605
0
}
606
607
void
608
VideoTrackEncoder::SetStartOffset(StreamTime aStartOffset)
609
0
{
610
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
611
0
  MOZ_ASSERT(mCurrentTime == 0);
612
0
  TRACK_LOG(LogLevel::Info,
613
0
    ("[VideoTrackEncoder %p]: SetStartOffset(), aStartOffset=%" PRIu64,
614
0
     this, aStartOffset));
615
0
  mIncomingBuffer.InsertNullDataAtStart(aStartOffset);
616
0
  mCurrentTime = aStartOffset;
617
0
}
618
619
void
620
VideoTrackEncoder::AdvanceBlockedInput(StreamTime aDuration)
621
0
{
622
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
623
0
  TRACK_LOG(LogLevel::Verbose,
624
0
    ("[VideoTrackEncoder %p]: AdvanceBlockedInput(), aDuration=%" PRIu64,
625
0
     this, aDuration));
626
0
627
0
  // We call Init here so it can account for aDuration towards the Init timeout
628
0
  Init(mOutgoingBuffer, aDuration);
629
0
630
0
  mIncomingBuffer.InsertNullDataAtStart(aDuration);
631
0
  mCurrentTime += aDuration;
632
0
}
633
634
void
635
VideoTrackEncoder::AdvanceCurrentTime(StreamTime aDuration)
636
0
{
637
0
  AUTO_PROFILER_LABEL("VideoTrackEncoder::AdvanceCurrentTime", OTHER);
638
0
639
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
640
0
641
0
  if (mCanceled) {
642
0
    return;
643
0
  }
644
0
645
0
  if (mEndOfStream) {
646
0
    return;
647
0
  }
648
0
649
0
  TRACK_LOG(LogLevel::Verbose,
650
0
    ("[VideoTrackEncoder %p]: AdvanceCurrentTime() %" PRIu64,
651
0
     this, aDuration));
652
0
653
0
  StreamTime currentTime = mCurrentTime + aDuration;
654
0
655
0
  if (mSuspended) {
656
0
    mCurrentTime = currentTime;
657
0
    mIncomingBuffer.ForgetUpTo(mCurrentTime);
658
0
    return;
659
0
  }
660
0
661
0
  VideoSegment tempSegment;
662
0
  if (currentTime <= mIncomingBuffer.GetDuration()) {
663
0
    tempSegment.AppendSlice(mIncomingBuffer, mCurrentTime, currentTime);
664
0
  } else {
665
0
    NS_ASSERTION(false, "VideoTrackEncoder::AdvanceCurrentTime Not enough data");
666
0
    TRACK_LOG(LogLevel::Error,
667
0
      ("[VideoTrackEncoder %p]: AdvanceCurrentTime() Not enough data. "
668
0
       "In incoming=%" PRIu64 ", aDuration=%" PRIu64 ", currentTime=%" PRIu64,
669
0
       this, mIncomingBuffer.GetDuration(), aDuration, currentTime));
670
0
    OnError();
671
0
  }
672
0
673
0
  mCurrentTime = currentTime;
674
0
  mIncomingBuffer.ForgetUpTo(mCurrentTime);
675
0
676
0
  bool chunkAppended = false;
677
0
678
0
  // Convert tempSegment timestamps to durations and add it to mOutgoingBuffer.
679
0
  VideoSegment::ConstChunkIterator iter(tempSegment);
680
0
  for (; !iter.IsEnded(); iter.Next()) {
681
0
    VideoChunk chunk = *iter;
682
0
683
0
    if (mLastChunk.mTimeStamp.IsNull()) {
684
0
      if (chunk.IsNull()) {
685
0
        // The start of this track is frameless. We need to track the time
686
0
        // it takes to get the first frame.
687
0
        mLastChunk.mDuration += chunk.mDuration;
688
0
        continue;
689
0
      }
690
0
691
0
      // This is the first real chunk in the track. Use its timestamp as the
692
0
      // starting point for this track.
693
0
      MOZ_ASSERT(!chunk.mTimeStamp.IsNull());
694
0
      const StreamTime nullDuration = mLastChunk.mDuration;
695
0
      mLastChunk = chunk;
696
0
      chunk.mDuration = 0;
697
0
698
0
      TRACK_LOG(LogLevel::Verbose,
699
0
                ("[VideoTrackEncoder]: Got first video chunk after %" PRId64 " ticks.",
700
0
                 nullDuration));
701
0
      // Adapt to the time before the first frame. This extends the first frame
702
0
      // from [start, end] to [0, end], but it'll do for now.
703
0
      auto diff = FramesToTimeUnit(nullDuration, mTrackRate);
704
0
      if (!diff.IsValid()) {
705
0
        NS_ERROR("null duration overflow");
706
0
        return;
707
0
      }
708
0
709
0
      mLastChunk.mTimeStamp -= diff.ToTimeDuration();
710
0
      mLastChunk.mDuration += nullDuration;
711
0
    }
712
0
713
0
    MOZ_ASSERT(!mLastChunk.IsNull());
714
0
    if (mLastChunk.CanCombineWithFollowing(chunk) || chunk.IsNull()) {
715
0
      TRACK_LOG(LogLevel::Verbose,
716
0
                ("[VideoTrackEncoder]: Got dupe or null chunk."));
717
0
      // This is the same frame as before (or null). We extend the last chunk
718
0
      // with its duration.
719
0
      mLastChunk.mDuration += chunk.mDuration;
720
0
721
0
      if (mLastChunk.mDuration < mTrackRate) {
722
0
        TRACK_LOG(LogLevel::Verbose,
723
0
                  ("[VideoTrackEncoder]: Ignoring dupe/null chunk of duration %" PRId64,
724
0
                   chunk.mDuration));
725
0
        continue;
726
0
      }
727
0
728
0
      TRACK_LOG(LogLevel::Verbose,
729
0
                ("[VideoTrackEncoder]: Chunk >1 second. duration=%" PRId64 ", "
730
0
                 "trackRate=%" PRId32, mLastChunk.mDuration, mTrackRate));
731
0
732
0
      // If we have gotten dupes for over a second, we force send one
733
0
      // to the encoder to make sure there is some output.
734
0
      chunk.mTimeStamp = mLastChunk.mTimeStamp + TimeDuration::FromSeconds(1);
735
0
      chunk.mDuration = mLastChunk.mDuration - mTrackRate;
736
0
      mLastChunk.mDuration = mTrackRate;
737
0
738
0
      if (chunk.IsNull()) {
739
0
        // Ensure that we don't pass null to the encoder by making mLastChunk
740
0
        // null later on.
741
0
        chunk.mFrame = mLastChunk.mFrame;
742
0
      }
743
0
    }
744
0
745
0
    if (mStartTime.IsNull()) {
746
0
      mStartTime = mLastChunk.mTimeStamp;
747
0
    }
748
0
749
0
    TimeDuration relativeTime = chunk.mTimeStamp - mStartTime;
750
0
    RefPtr<layers::Image> lastImage = mLastChunk.mFrame.GetImage();
751
0
    TRACK_LOG(LogLevel::Verbose,
752
0
              ("[VideoTrackEncoder]: Appending video frame %p, at pos %.5fs",
753
0
               lastImage.get(), relativeTime.ToSeconds()));
754
0
    CheckedInt64 duration = UsecsToFrames(relativeTime.ToMicroseconds(),
755
0
                                          mTrackRate)
756
0
                            - mEncodedTicks;
757
0
    if (!duration.isValid()) {
758
0
      NS_ERROR("Duration overflow");
759
0
      return;
760
0
    }
761
0
762
0
    if (duration.value() <= 0) {
763
0
      // The timestamp for mLastChunk is newer than for chunk.
764
0
      // This means the durations reported from MediaStreamGraph for
765
0
      // mLastChunk were larger than the timestamp diff - and durations were
766
0
      // used to trigger the 1-second frame above. This could happen due to
767
0
      // drift or underruns in the graph.
768
0
      TRACK_LOG(LogLevel::Warning,
769
0
                ("[VideoTrackEncoder]: Underrun detected. Diff=%" PRId64,
770
0
                 duration.value()));
771
0
      chunk.mTimeStamp = mLastChunk.mTimeStamp;
772
0
    } else {
773
0
      mEncodedTicks += duration.value();
774
0
      mOutgoingBuffer.AppendFrame(lastImage.forget(),
775
0
                                  duration.value(),
776
0
                                  mLastChunk.mFrame.GetIntrinsicSize(),
777
0
                                  PRINCIPAL_HANDLE_NONE,
778
0
                                  mLastChunk.mFrame.GetForceBlack(),
779
0
                                  mLastChunk.mTimeStamp);
780
0
      chunkAppended = true;
781
0
    }
782
0
783
0
    mLastChunk = chunk;
784
0
  }
785
0
786
0
  if (chunkAppended) {
787
0
    Init(mOutgoingBuffer, aDuration);
788
0
    if (mInitialized) {
789
0
      OnDataAvailable();
790
0
    }
791
0
  }
792
0
}
793
794
size_t
795
VideoTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
796
0
{
797
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
798
0
  return mIncomingBuffer.SizeOfExcludingThis(aMallocSizeOf) +
799
0
         mOutgoingBuffer.SizeOfExcludingThis(aMallocSizeOf);
800
0
}
801
802
void
803
VideoTrackEncoder::SetKeyFrameInterval(int32_t aKeyFrameInterval)
804
0
{
805
0
  MOZ_ASSERT(!mWorkerThread || mWorkerThread->IsCurrentThreadIn());
806
0
  mKeyFrameInterval = std::min(aKeyFrameInterval, DEFAULT_KEYFRAME_INTERVAL_MS);
807
0
}
808
809
} // namespace mozilla