Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webm/WebMDemuxer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsError.h"
8
#include "MediaDecoderStateMachine.h"
9
#include "MediaResource.h"
10
#ifdef MOZ_AV1
11
#include "AOMDecoder.h"
12
#endif
13
#include "OpusDecoder.h"
14
#include "VPXDecoder.h"
15
#include "WebMDemuxer.h"
16
#include "WebMBufferedParser.h"
17
#include "gfx2DGlue.h"
18
#include "mozilla/Atomics.h"
19
#include "mozilla/EndianUtils.h"
20
#include "mozilla/SharedThreadPool.h"
21
#include "MediaDataDemuxer.h"
22
#include "nsAutoRef.h"
23
#include "NesteggPacketHolder.h"
24
#include "XiphExtradata.h"
25
#include "prprf.h"           // leaving it for PR_vsnprintf()
26
#include "mozilla/IntegerPrintfMacros.h"
27
#include "mozilla/Sprintf.h"
28
29
#include <algorithm>
30
#include <numeric>
31
#include <stdint.h>
32
33
#define WEBM_DEBUG(arg, ...)                                                   \
34
0
  DDMOZ_LOG(gMediaDemuxerLog,                                                  \
35
0
            mozilla::LogLevel::Debug,                                          \
36
0
            "::%s: " arg,                                                      \
37
0
            __func__,                                                          \
38
0
            ##__VA_ARGS__)
39
extern mozilla::LazyLogModule gMediaDemuxerLog;
40
41
namespace mozilla {
42
43
using namespace gfx;
44
using media::TimeUnit;
45
46
LazyLogModule gNesteggLog("Nestegg");
47
48
// How far ahead will we look when searching future keyframe. In microseconds.
49
// This value is based on what appears to be a reasonable value as most webm
50
// files encountered appear to have keyframes located < 4s.
51
0
#define MAX_LOOK_AHEAD 10000000
52
53
static Atomic<uint32_t> sStreamSourceID(0u);
54
55
// Functions for reading and seeking using WebMDemuxer required for
56
// nestegg_io. The 'user data' passed to these functions is the
57
// demuxer.
58
static int webmdemux_read(void* aBuffer, size_t aLength, void* aUserData)
59
0
{
60
0
  MOZ_ASSERT(aUserData);
61
0
  MOZ_ASSERT(aLength < UINT32_MAX);
62
0
  WebMDemuxer::NestEggContext* context =
63
0
    reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
64
0
  uint32_t count = aLength;
65
0
  if (context->IsMediaSource()) {
66
0
    int64_t length = context->GetEndDataOffset();
67
0
    int64_t position = context->GetResource()->Tell();
68
0
    MOZ_ASSERT(position <= context->GetResource()->GetLength());
69
0
    MOZ_ASSERT(position <= length);
70
0
    if (length >= 0 && count + position > length) {
71
0
      count = length - position;
72
0
    }
73
0
    MOZ_ASSERT(count <= aLength);
74
0
  }
75
0
  uint32_t bytes = 0;
76
0
  nsresult rv =
77
0
    context->GetResource()->Read(static_cast<char*>(aBuffer), count, &bytes);
78
0
  bool eof = bytes < aLength;
79
0
  return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
80
0
}
81
82
static int webmdemux_seek(int64_t aOffset, int aWhence, void* aUserData)
83
0
{
84
0
  MOZ_ASSERT(aUserData);
85
0
  WebMDemuxer::NestEggContext* context =
86
0
    reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
87
0
  nsresult rv = context->GetResource()->Seek(aWhence, aOffset);
88
0
  return NS_SUCCEEDED(rv) ? 0 : -1;
89
0
}
90
91
static int64_t webmdemux_tell(void* aUserData)
92
0
{
93
0
  MOZ_ASSERT(aUserData);
94
0
  WebMDemuxer::NestEggContext* context =
95
0
    reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
96
0
  return context->GetResource()->Tell();
97
0
}
98
99
static void webmdemux_log(nestegg* aContext,
100
                          unsigned int aSeverity,
101
                          char const* aFormat, ...)
102
0
{
103
0
  if (!MOZ_LOG_TEST(gNesteggLog, LogLevel::Debug)) {
104
0
    return;
105
0
  }
106
0
107
0
  va_list args;
108
0
  char msg[256];
109
0
  const char* sevStr;
110
0
111
0
  switch(aSeverity) {
112
0
    case NESTEGG_LOG_DEBUG:
113
0
      sevStr = "DBG";
114
0
      break;
115
0
    case NESTEGG_LOG_INFO:
116
0
      sevStr = "INF";
117
0
      break;
118
0
    case NESTEGG_LOG_WARNING:
119
0
      sevStr = "WRN";
120
0
      break;
121
0
    case NESTEGG_LOG_ERROR:
122
0
      sevStr = "ERR";
123
0
      break;
124
0
    case NESTEGG_LOG_CRITICAL:
125
0
      sevStr = "CRT";
126
0
      break;
127
0
    default:
128
0
      sevStr = "UNK";
129
0
      break;
130
0
  }
131
0
132
0
  va_start(args, aFormat);
133
0
134
0
  SprintfLiteral(msg, "%p [Nestegg-%s] ", aContext, sevStr);
135
0
  PR_vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), aFormat, args);
136
0
  MOZ_LOG(gNesteggLog, LogLevel::Debug, ("%s", msg));
137
0
138
0
  va_end(args);
139
0
}
140
141
WebMDemuxer::NestEggContext::~NestEggContext()
142
0
{
143
0
  if (mContext) {
144
0
    nestegg_destroy(mContext);
145
0
  }
146
0
}
147
148
int
149
WebMDemuxer::NestEggContext::Init()
150
0
{
151
0
  nestegg_io io;
152
0
  io.read = webmdemux_read;
153
0
  io.seek = webmdemux_seek;
154
0
  io.tell = webmdemux_tell;
155
0
  io.userdata = this;
156
0
157
0
  // While reading the metadata, we do not really care about which nestegg
158
0
  // context is being used so long that they are both initialised.
159
0
  // For reading the metadata however, we will use mVideoContext.
160
0
  return nestegg_init(&mContext, io, &webmdemux_log,
161
0
                      mParent->IsMediaSource() ? mResource.GetLength() : -1);
162
0
}
163
164
WebMDemuxer::WebMDemuxer(MediaResource* aResource)
165
  : WebMDemuxer(aResource, false)
166
0
{
167
0
}
168
169
WebMDemuxer::WebMDemuxer(MediaResource* aResource, bool aIsMediaSource)
170
  : mVideoContext(this, aResource)
171
  , mAudioContext(this, aResource)
172
  , mBufferedState(nullptr)
173
  , mInitData(nullptr)
174
  , mVideoTrack(0)
175
  , mAudioTrack(0)
176
  , mSeekPreroll(0)
177
  , mAudioCodec(-1)
178
  , mVideoCodec(-1)
179
  , mHasVideo(false)
180
  , mHasAudio(false)
181
  , mNeedReIndex(true)
182
  , mLastWebMBlockOffset(-1)
183
  , mIsMediaSource(aIsMediaSource)
184
0
{
185
0
  DDLINKCHILD("resource", aResource);
186
0
  // Audio/video contexts hold a MediaResourceIndex.
187
0
  DDLINKCHILD("video context", mVideoContext.GetResource());
188
0
  DDLINKCHILD("audio context", mAudioContext.GetResource());
189
0
}
190
191
WebMDemuxer::~WebMDemuxer()
192
0
{
193
0
  Reset(TrackInfo::kVideoTrack);
194
0
  Reset(TrackInfo::kAudioTrack);
195
0
}
196
197
RefPtr<WebMDemuxer::InitPromise>
198
WebMDemuxer::Init()
199
0
{
200
0
  InitBufferedState();
201
0
202
0
  if (NS_FAILED(ReadMetadata())) {
203
0
    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
204
0
                                        __func__);
205
0
  }
206
0
207
0
  if (!GetNumberTracks(TrackInfo::kAudioTrack) &&
208
0
      !GetNumberTracks(TrackInfo::kVideoTrack)) {
209
0
    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
210
0
                                        __func__);
211
0
  }
212
0
213
0
  return InitPromise::CreateAndResolve(NS_OK, __func__);
214
0
}
215
216
void
217
WebMDemuxer::InitBufferedState()
218
0
{
219
0
  MOZ_ASSERT(!mBufferedState);
220
0
  mBufferedState = new WebMBufferedState;
221
0
}
222
223
uint32_t
224
WebMDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
225
0
{
226
0
  switch(aType) {
227
0
    case TrackInfo::kAudioTrack:
228
0
      return mHasAudio ? 1 : 0;
229
0
    case TrackInfo::kVideoTrack:
230
0
      return mHasVideo ? 1 : 0;
231
0
    default:
232
0
      return 0;
233
0
  }
234
0
}
235
236
UniquePtr<TrackInfo>
237
WebMDemuxer::GetTrackInfo(TrackInfo::TrackType aType,
238
                          size_t aTrackNumber) const
239
{
240
  switch(aType) {
241
    case TrackInfo::kAudioTrack:
242
      return mInfo.mAudio.Clone();
243
    case TrackInfo::kVideoTrack:
244
      return mInfo.mVideo.Clone();
245
    default:
246
      return nullptr;
247
  }
248
}
249
250
already_AddRefed<MediaTrackDemuxer>
251
WebMDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
252
0
{
253
0
  if (GetNumberTracks(aType) <= aTrackNumber) {
254
0
    return nullptr;
255
0
  }
256
0
  RefPtr<WebMTrackDemuxer> e =
257
0
    new WebMTrackDemuxer(this, aType, aTrackNumber);
258
0
  DDLINKCHILD("track demuxer", e.get());
259
0
  mDemuxers.AppendElement(e);
260
0
261
0
  return e.forget();
262
0
}
263
264
nsresult
265
WebMDemuxer::Reset(TrackInfo::TrackType aType)
266
0
{
267
0
  if (aType == TrackInfo::kVideoTrack) {
268
0
    mVideoPackets.Reset();
269
0
  } else {
270
0
    mAudioPackets.Reset();
271
0
  }
272
0
  return NS_OK;
273
0
}
274
275
nsresult
276
WebMDemuxer::ReadMetadata()
277
0
{
278
0
  int r = mVideoContext.Init();
279
0
  if (r == -1) {
280
0
    return NS_ERROR_FAILURE;
281
0
  }
282
0
  if (mAudioContext.Init() == -1) {
283
0
    return NS_ERROR_FAILURE;
284
0
  }
285
0
286
0
  // For reading the metadata we can only use the video resource/context.
287
0
  MediaResourceIndex& resource = Resource(TrackInfo::kVideoTrack);
288
0
  nestegg* context = Context(TrackInfo::kVideoTrack);
289
0
290
0
  {
291
0
    // Check how much data nestegg read and force feed it to BufferedState.
292
0
    RefPtr<MediaByteBuffer> buffer = resource.MediaReadAt(0, resource.Tell());
293
0
    if (!buffer) {
294
0
      return NS_ERROR_FAILURE;
295
0
    }
296
0
    mBufferedState->NotifyDataArrived(buffer->Elements(), buffer->Length(), 0);
297
0
    if (mBufferedState->GetInitEndOffset() < 0) {
298
0
      return NS_ERROR_FAILURE;
299
0
    }
300
0
    MOZ_ASSERT(mBufferedState->GetInitEndOffset() <= resource.Tell());
301
0
  }
302
0
  mInitData = resource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
303
0
  if (!mInitData ||
304
0
      mInitData->Length() != size_t(mBufferedState->GetInitEndOffset())) {
305
0
    return NS_ERROR_FAILURE;
306
0
  }
307
0
308
0
  unsigned int ntracks = 0;
309
0
  r = nestegg_track_count(context, &ntracks);
310
0
  if (r == -1) {
311
0
    return NS_ERROR_FAILURE;
312
0
  }
313
0
314
0
  for (unsigned int track = 0; track < ntracks; ++track) {
315
0
    int id = nestegg_track_codec_id(context, track);
316
0
    if (id == -1) {
317
0
      return NS_ERROR_FAILURE;
318
0
    }
319
0
    int type = nestegg_track_type(context, track);
320
0
    if (type == NESTEGG_TRACK_VIDEO && !mHasVideo) {
321
0
      nestegg_video_params params;
322
0
      r = nestegg_track_video_params(context, track, &params);
323
0
      if (r == -1) {
324
0
        return NS_ERROR_FAILURE;
325
0
      }
326
0
      mVideoCodec = nestegg_track_codec_id(context, track);
327
0
      switch(mVideoCodec) {
328
0
        case NESTEGG_CODEC_VP8:
329
0
          mInfo.mVideo.mMimeType = "video/vp8";
330
0
          break;
331
0
        case NESTEGG_CODEC_VP9:
332
0
          mInfo.mVideo.mMimeType = "video/vp9";
333
0
          break;
334
0
        case NESTEGG_CODEC_AV1:
335
0
          mInfo.mVideo.mMimeType = "video/av1";
336
0
          break;
337
0
        default:
338
0
          NS_WARNING("Unknown WebM video codec");
339
0
          return NS_ERROR_FAILURE;
340
0
      }
341
0
      // Picture region, taking into account cropping, before scaling
342
0
      // to the display size.
343
0
      unsigned int cropH = params.crop_right + params.crop_left;
344
0
      unsigned int cropV = params.crop_bottom + params.crop_top;
345
0
      gfx::IntRect pictureRect(params.crop_left,
346
0
                               params.crop_top,
347
0
                               params.width - cropH,
348
0
                               params.height - cropV);
349
0
350
0
      // If the cropping data appears invalid then use the frame data
351
0
      if (pictureRect.width <= 0 ||
352
0
          pictureRect.height <= 0 ||
353
0
          pictureRect.x < 0 ||
354
0
          pictureRect.y < 0) {
355
0
        pictureRect.x = 0;
356
0
        pictureRect.y = 0;
357
0
        pictureRect.width = params.width;
358
0
        pictureRect.height = params.height;
359
0
      }
360
0
361
0
      // Validate the container-reported frame and pictureRect sizes. This
362
0
      // ensures that our video frame creation code doesn't overflow.
363
0
      gfx::IntSize displaySize(params.display_width, params.display_height);
364
0
      gfx::IntSize frameSize(params.width, params.height);
365
0
      if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
366
0
        // Video track's frame sizes will overflow. Ignore the video track.
367
0
        continue;
368
0
      }
369
0
370
0
      mVideoTrack = track;
371
0
      mHasVideo = true;
372
0
373
0
      mInfo.mVideo.mDisplay = displaySize;
374
0
      mInfo.mVideo.mImage = frameSize;
375
0
      mInfo.mVideo.SetImageRect(pictureRect);
376
0
      mInfo.mVideo.SetAlpha(params.alpha_mode);
377
0
378
0
      switch (params.stereo_mode) {
379
0
        case NESTEGG_VIDEO_MONO:
380
0
          mInfo.mVideo.mStereoMode = StereoMode::MONO;
381
0
          break;
382
0
        case NESTEGG_VIDEO_STEREO_LEFT_RIGHT:
383
0
          mInfo.mVideo.mStereoMode = StereoMode::LEFT_RIGHT;
384
0
          break;
385
0
        case NESTEGG_VIDEO_STEREO_BOTTOM_TOP:
386
0
          mInfo.mVideo.mStereoMode = StereoMode::BOTTOM_TOP;
387
0
          break;
388
0
        case NESTEGG_VIDEO_STEREO_TOP_BOTTOM:
389
0
          mInfo.mVideo.mStereoMode = StereoMode::TOP_BOTTOM;
390
0
          break;
391
0
        case NESTEGG_VIDEO_STEREO_RIGHT_LEFT:
392
0
          mInfo.mVideo.mStereoMode = StereoMode::RIGHT_LEFT;
393
0
          break;
394
0
      }
395
0
      uint64_t duration = 0;
396
0
      r = nestegg_duration(context, &duration);
397
0
      if (!r) {
398
0
        mInfo.mVideo.mDuration = TimeUnit::FromNanoseconds(duration);
399
0
      }
400
0
      mInfo.mVideo.mCrypto = GetTrackCrypto(TrackInfo::kVideoTrack, track);
401
0
      if (mInfo.mVideo.mCrypto.mValid) {
402
0
        mCrypto.AddInitData(NS_LITERAL_STRING("webm"),
403
0
                            mInfo.mVideo.mCrypto.mKeyId);
404
0
      }
405
0
    } else if (type == NESTEGG_TRACK_AUDIO && !mHasAudio) {
406
0
      nestegg_audio_params params;
407
0
      r = nestegg_track_audio_params(context, track, &params);
408
0
      if (r == -1) {
409
0
        return NS_ERROR_FAILURE;
410
0
      }
411
0
412
0
      mAudioTrack = track;
413
0
      mHasAudio = true;
414
0
      mAudioCodec = nestegg_track_codec_id(context, track);
415
0
      if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
416
0
        mInfo.mAudio.mMimeType = "audio/vorbis";
417
0
      } else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
418
0
        mInfo.mAudio.mMimeType = "audio/opus";
419
0
        OpusDataDecoder::AppendCodecDelay(
420
0
          mInfo.mAudio.mCodecSpecificConfig,
421
0
          TimeUnit::FromNanoseconds(params.codec_delay).ToMicroseconds());
422
0
      }
423
0
      mSeekPreroll = params.seek_preroll;
424
0
      mInfo.mAudio.mRate = params.rate;
425
0
      mInfo.mAudio.mChannels = params.channels;
426
0
427
0
      unsigned int nheaders = 0;
428
0
      r = nestegg_track_codec_data_count(context, track, &nheaders);
429
0
      if (r == -1) {
430
0
        return NS_ERROR_FAILURE;
431
0
      }
432
0
433
0
      AutoTArray<const unsigned char*,4> headers;
434
0
      AutoTArray<size_t,4> headerLens;
435
0
      for (uint32_t header = 0; header < nheaders; ++header) {
436
0
        unsigned char* data = 0;
437
0
        size_t length = 0;
438
0
        r = nestegg_track_codec_data(context, track, header, &data, &length);
439
0
        if (r == -1) {
440
0
          return NS_ERROR_FAILURE;
441
0
        }
442
0
        headers.AppendElement(data);
443
0
        headerLens.AppendElement(length);
444
0
      }
445
0
446
0
      // Vorbis has 3 headers, convert to Xiph extradata format to send them to
447
0
      // the demuxer.
448
0
      // TODO: This is already the format WebM stores them in. Would be nice
449
0
      // to avoid having libnestegg split them only for us to pack them again,
450
0
      // but libnestegg does not give us an API to access this data directly.
451
0
      if (nheaders > 1) {
452
0
        if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
453
0
                                    headers, headerLens)) {
454
0
          return NS_ERROR_FAILURE;
455
0
        }
456
0
      }
457
0
      else {
458
0
        mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
459
0
                                                          headerLens[0]);
460
0
      }
461
0
      uint64_t duration = 0;
462
0
      r = nestegg_duration(context, &duration);
463
0
      if (!r) {
464
0
        mInfo.mAudio.mDuration = TimeUnit::FromNanoseconds(duration);
465
0
      }
466
0
      mInfo.mAudio.mCrypto = GetTrackCrypto(TrackInfo::kAudioTrack, track);
467
0
      if (mInfo.mAudio.mCrypto.mValid) {
468
0
        mCrypto.AddInitData(NS_LITERAL_STRING("webm"),
469
0
                            mInfo.mAudio.mCrypto.mKeyId);
470
0
      }
471
0
    }
472
0
  }
473
0
  return NS_OK;
474
0
}
475
476
bool
477
WebMDemuxer::IsSeekable() const
478
0
{
479
0
  return Context(TrackInfo::kVideoTrack) &&
480
0
         nestegg_has_cues(Context(TrackInfo::kVideoTrack));
481
0
}
482
483
bool
484
WebMDemuxer::IsSeekableOnlyInBufferedRanges() const
485
0
{
486
0
  return Context(TrackInfo::kVideoTrack) &&
487
0
         !nestegg_has_cues(Context(TrackInfo::kVideoTrack));
488
0
}
489
490
void
491
WebMDemuxer::EnsureUpToDateIndex()
492
0
{
493
0
  if (!mNeedReIndex || !mInitData) {
494
0
    return;
495
0
  }
496
0
  AutoPinned<MediaResource> resource(
497
0
    Resource(TrackInfo::kVideoTrack).GetResource());
498
0
  MediaByteRangeSet byteRanges;
499
0
  nsresult rv = resource->GetCachedRanges(byteRanges);
500
0
  if (NS_FAILED(rv) || !byteRanges.Length()) {
501
0
    return;
502
0
  }
503
0
  mBufferedState->UpdateIndex(byteRanges, resource);
504
0
505
0
  mNeedReIndex = false;
506
0
507
0
  if (!mIsMediaSource) {
508
0
    return;
509
0
  }
510
0
  mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
511
0
  MOZ_ASSERT(mLastWebMBlockOffset <= resource->GetLength());
512
0
}
513
514
void
515
WebMDemuxer::NotifyDataArrived()
516
0
{
517
0
  WEBM_DEBUG("");
518
0
  mNeedReIndex = true;
519
0
}
520
521
void
522
WebMDemuxer::NotifyDataRemoved()
523
0
{
524
0
  mBufferedState->Reset();
525
0
  if (mInitData) {
526
0
    mBufferedState->NotifyDataArrived(mInitData->Elements(),
527
0
                                      mInitData->Length(), 0);
528
0
  }
529
0
  mNeedReIndex = true;
530
0
}
531
532
UniquePtr<EncryptionInfo>
533
WebMDemuxer::GetCrypto()
534
0
{
535
0
  return mCrypto.IsEncrypted() ? MakeUnique<EncryptionInfo>(mCrypto) : nullptr;
536
0
}
537
538
CryptoTrack
539
WebMDemuxer::GetTrackCrypto(TrackInfo::TrackType aType, size_t aTrackNumber)
540
0
{
541
0
  const int WEBM_IV_SIZE = 16;
542
0
  const unsigned char * contentEncKeyId;
543
0
  size_t contentEncKeyIdLength;
544
0
  CryptoTrack crypto;
545
0
  nestegg* context = Context(aType);
546
0
547
0
  int r = nestegg_track_content_enc_key_id(
548
0
    context, aTrackNumber, &contentEncKeyId, &contentEncKeyIdLength);
549
0
550
0
  if (r == -1) {
551
0
    WEBM_DEBUG("nestegg_track_content_enc_key_id failed r=%d", r);
552
0
    return crypto;
553
0
  }
554
0
555
0
  uint32_t i;
556
0
  nsTArray<uint8_t> initData;
557
0
  for (i = 0; i < contentEncKeyIdLength; i++) {
558
0
    initData.AppendElement(contentEncKeyId[i]);
559
0
  }
560
0
561
0
  if (!initData.IsEmpty()) {
562
0
    crypto.mValid = true;
563
0
    // crypto.mMode is not used for WebMs
564
0
    crypto.mIVSize = WEBM_IV_SIZE;
565
0
    crypto.mKeyId = std::move(initData);
566
0
  }
567
0
568
0
  return crypto;
569
0
}
570
571
nsresult
572
WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType,
573
                           MediaRawDataQueue *aSamples)
574
0
{
575
0
  if (mIsMediaSource) {
576
0
    // To ensure mLastWebMBlockOffset is properly up to date.
577
0
    EnsureUpToDateIndex();
578
0
  }
579
0
580
0
  RefPtr<NesteggPacketHolder> holder;
581
0
  nsresult rv = NextPacket(aType, holder);
582
0
583
0
  if (NS_FAILED(rv)) {
584
0
    return rv;
585
0
  }
586
0
587
0
  int r = 0;
588
0
  unsigned int count = 0;
589
0
  r = nestegg_packet_count(holder->Packet(), &count);
590
0
  if (r == -1) {
591
0
    return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
592
0
  }
593
0
  int64_t tstamp = holder->Timestamp();
594
0
  int64_t duration = holder->Duration();
595
0
596
0
  // The end time of this frame is the start time of the next frame. Fetch
597
0
  // the timestamp of the next packet for this track.  If we've reached the
598
0
  // end of the resource, use the file's duration as the end time of this
599
0
  // video frame.
600
0
  RefPtr<NesteggPacketHolder> next_holder;
601
0
  rv = NextPacket(aType, next_holder);
602
0
  if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
603
0
    return rv;
604
0
  }
605
0
606
0
  int64_t next_tstamp = INT64_MIN;
607
0
  auto calculateNextTimestamp =
608
0
    [&] (auto&& pushPacket, auto&& lastFrameTime, auto&& trackEndTime) {
609
0
      if (next_holder) {
610
0
        next_tstamp = next_holder->Timestamp();
611
0
        (this->*pushPacket)(next_holder);
612
0
      } else if (duration >= 0) {
613
0
        next_tstamp = tstamp + duration;
614
0
      } else if (lastFrameTime.isSome()) {
615
0
        next_tstamp = tstamp + (tstamp - lastFrameTime.ref());
616
0
      } else if (mIsMediaSource) {
617
0
        (this->*pushPacket)(holder);
618
0
      } else {
619
0
        // If we can't get frame's duration, it means either we need to wait for
620
0
        // more data for MSE case or this is the last frame for file resource case.
621
0
        MOZ_ASSERT(trackEndTime >= tstamp);
622
0
        next_tstamp = trackEndTime;
623
0
      }
624
0
      lastFrameTime = Some(tstamp);
625
0
  };
626
0
627
0
  if (aType == TrackInfo::kAudioTrack)  {
628
0
    calculateNextTimestamp(&WebMDemuxer::PushAudioPacket,
629
0
                           mLastAudioFrameTime,
630
0
                           mInfo.mAudio.mDuration.ToMicroseconds());
631
0
  } else {
632
0
    calculateNextTimestamp(&WebMDemuxer::PushVideoPacket,
633
0
                           mLastVideoFrameTime,
634
0
                           mInfo.mVideo.mDuration.ToMicroseconds());
635
0
  }
636
0
637
0
  if (mIsMediaSource && next_tstamp == INT64_MIN) {
638
0
    return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
639
0
  }
640
0
641
0
  int64_t discardPadding = 0;
642
0
  if (aType == TrackInfo::kAudioTrack) {
643
0
    (void) nestegg_packet_discard_padding(holder->Packet(), &discardPadding);
644
0
  }
645
0
646
0
  int packetEncryption = nestegg_packet_encryption(holder->Packet());
647
0
648
0
  for (uint32_t i = 0; i < count; ++i) {
649
0
    unsigned char* data = nullptr;
650
0
    size_t length;
651
0
    r = nestegg_packet_data(holder->Packet(), i, &data, &length);
652
0
    if (r == -1) {
653
0
      WEBM_DEBUG("nestegg_packet_data failed r=%d", r);
654
0
      return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
655
0
    }
656
0
    unsigned char* alphaData = nullptr;
657
0
    size_t alphaLength = 0;
658
0
    // Check packets for alpha information if file has declared alpha frames
659
0
    // may be present.
660
0
    if (mInfo.mVideo.HasAlpha()) {
661
0
      r = nestegg_packet_additional_data(holder->Packet(),
662
0
                                         1,
663
0
                                         &alphaData,
664
0
                                         &alphaLength);
665
0
      if (r == -1) {
666
0
        WEBM_DEBUG(
667
0
          "nestegg_packet_additional_data failed to retrieve alpha data r=%d",
668
0
          r);
669
0
      }
670
0
    }
671
0
    bool isKeyframe = false;
672
0
    if (aType == TrackInfo::kAudioTrack) {
673
0
      isKeyframe = true;
674
0
    } else if (aType == TrackInfo::kVideoTrack) {
675
0
      if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED ||
676
0
          packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
677
0
        // Packet is encrypted, can't peek, use packet info
678
0
        isKeyframe = nestegg_packet_has_keyframe(holder->Packet())
679
0
                     == NESTEGG_PACKET_HAS_KEYFRAME_TRUE;
680
0
      } else {
681
0
        MOZ_ASSERT(packetEncryption ==
682
0
                       NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED ||
683
0
                     packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE,
684
0
                   "Unencrypted packet expected");
685
0
        auto sample = MakeSpan(data, length);
686
0
        auto alphaSample = MakeSpan(alphaData, alphaLength);
687
0
688
0
        switch (mVideoCodec) {
689
0
        case NESTEGG_CODEC_VP8:
690
0
          isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP8);
691
0
          if (isKeyframe && alphaLength) {
692
0
            isKeyframe =
693
0
              VPXDecoder::IsKeyframe(alphaSample, VPXDecoder::Codec::VP8);
694
0
          }
695
0
          break;
696
0
        case NESTEGG_CODEC_VP9:
697
0
          isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP9);
698
0
          if (isKeyframe && alphaLength) {
699
0
            isKeyframe =
700
0
              VPXDecoder::IsKeyframe(alphaSample, VPXDecoder::Codec::VP9);
701
0
          }
702
0
          break;
703
0
#ifdef MOZ_AV1
704
0
        case NESTEGG_CODEC_AV1:
705
0
          isKeyframe = AOMDecoder::IsKeyframe(sample);
706
0
          if (isKeyframe && alphaLength) {
707
0
            isKeyframe = AOMDecoder::IsKeyframe(alphaSample);
708
0
          }
709
0
          break;
710
0
#endif
711
0
        default:
712
0
          NS_WARNING("Cannot detect keyframes in unknown WebM video codec");
713
0
          return NS_ERROR_FAILURE;
714
0
        }
715
0
        if (isKeyframe) {
716
0
          // For both VP8 and VP9, we only look for resolution changes
717
0
          // on keyframes. Other resolution changes are invalid.
718
0
          auto dimensions = gfx::IntSize(0, 0);
719
0
          switch (mVideoCodec) {
720
0
          case NESTEGG_CODEC_VP8:
721
0
            dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP8);
722
0
            break;
723
0
          case NESTEGG_CODEC_VP9:
724
0
            dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP9);
725
0
            break;
726
0
#ifdef MOZ_AV1
727
0
          case NESTEGG_CODEC_AV1:
728
0
            dimensions = AOMDecoder::GetFrameSize(sample);
729
0
            break;
730
0
#endif
731
0
          }
732
0
          if (mLastSeenFrameSize.isSome() &&
733
0
              (dimensions != mLastSeenFrameSize.value())) {
734
0
            mInfo.mVideo.mDisplay = dimensions;
735
0
            mSharedVideoTrackInfo =
736
0
              new TrackInfoSharedPtr(mInfo.mVideo, ++sStreamSourceID);
737
0
          }
738
0
          mLastSeenFrameSize = Some(dimensions);
739
0
        }
740
0
      }
741
0
    }
742
0
743
0
    WEBM_DEBUG("push sample tstamp: %" PRId64 " next_tstamp: %" PRId64 " length: %zu kf: %d",
744
0
               tstamp, next_tstamp, length, isKeyframe);
745
0
    RefPtr<MediaRawData> sample;
746
0
    if (mInfo.mVideo.HasAlpha() && alphaLength != 0) {
747
0
      sample = new MediaRawData(data, length, alphaData, alphaLength);
748
0
      if ((length && !sample->Data()) || (alphaLength && !sample->AlphaData())) {
749
0
        // OOM.
750
0
        return NS_ERROR_OUT_OF_MEMORY;
751
0
      }
752
0
    } else {
753
0
      sample = new MediaRawData(data, length);
754
0
      if (length && !sample->Data()) {
755
0
        // OOM.
756
0
        return NS_ERROR_OUT_OF_MEMORY;
757
0
      }
758
0
    }
759
0
    sample->mTimecode = TimeUnit::FromMicroseconds(tstamp);
760
0
    sample->mTime = TimeUnit::FromMicroseconds(tstamp);
761
0
    sample->mDuration = TimeUnit::FromMicroseconds(next_tstamp - tstamp);
762
0
    sample->mOffset = holder->Offset();
763
0
    sample->mKeyframe = isKeyframe;
764
0
    if (discardPadding && i == count - 1) {
765
0
      CheckedInt64 discardFrames;
766
0
      if (discardPadding < 0) {
767
0
        // This is an invalid value as discard padding should never be negative.
768
0
        // Set to maximum value so that the decoder will reject it as it's
769
0
        // greater than the number of frames available.
770
0
        discardFrames = INT32_MAX;
771
0
        WEBM_DEBUG("Invalid negative discard padding");
772
0
      } else {
773
0
        discardFrames = TimeUnitToFrames(
774
0
          TimeUnit::FromNanoseconds(discardPadding), mInfo.mAudio.mRate);
775
0
      }
776
0
      if (discardFrames.isValid()) {
777
0
        sample->mDiscardPadding = discardFrames.value();
778
0
      }
779
0
    }
780
0
781
0
    if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED ||
782
0
        packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
783
0
      UniquePtr<MediaRawDataWriter> writer(sample->CreateWriter());
784
0
      unsigned char const* iv;
785
0
      size_t ivLength;
786
0
      nestegg_packet_iv(holder->Packet(), &iv, &ivLength);
787
0
      writer->mCrypto.mValid = true;
788
0
      writer->mCrypto.mIVSize = ivLength;
789
0
      if (ivLength == 0) {
790
0
        // Frame is not encrypted. This shouldn't happen as it means the
791
0
        // encryption bit is set on a frame with no IV, but we gracefully
792
0
        // handle incase.
793
0
        MOZ_ASSERT_UNREACHABLE(
794
0
          "Unencrypted packets should not have the encryption bit set!");
795
0
        WEBM_DEBUG("Unencrypted packet with encryption bit set");
796
0
        writer->mCrypto.mPlainSizes.AppendElement(length);
797
0
        writer->mCrypto.mEncryptedSizes.AppendElement(0);
798
0
      } else {
799
0
        // Frame is encrypted
800
0
        writer->mCrypto.mIV.AppendElements(iv, 8);
801
0
        // Iv from a sample is 64 bits, must be padded with 64 bits more 0s
802
0
        // in compliance with spec
803
0
        for (uint32_t i = 0; i < 8; i++) {
804
0
          writer->mCrypto.mIV.AppendElement(0);
805
0
        }
806
0
807
0
        if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
808
0
          writer->mCrypto.mPlainSizes.AppendElement(0);
809
0
          writer->mCrypto.mEncryptedSizes.AppendElement(length);
810
0
        } else if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
811
0
          uint8_t numPartitions = 0;
812
0
          const uint32_t* partitions = NULL;
813
0
          nestegg_packet_offsets(holder->Packet(), &partitions, &numPartitions);
814
0
815
0
          // WebM stores a list of 'partitions' in the data, which alternate
816
0
          // clear, encrypted. The data in the first partition is always clear.
817
0
          // So, and sample might look as follows:
818
0
          // 00|XXXX|000|XX, where | represents a partition, 0 a clear byte and
819
0
          // X an encrypted byte. If the first bytes in sample are unencrypted,
820
0
          // the first partition will be at zero |XXXX|000|XX.
821
0
          //
822
0
          // As GMP expects the lengths of the clear and encrypted chunks of
823
0
          // data, we calculate these from the difference between the last two
824
0
          // partitions.
825
0
          uint32_t lastOffset = 0;
826
0
          bool encrypted = false;
827
0
828
0
          for (uint8_t i = 0; i < numPartitions; i++) {
829
0
            uint32_t partition = partitions[i];
830
0
            uint32_t currentLength = partition - lastOffset;
831
0
832
0
            if (encrypted) {
833
0
              writer->mCrypto.mEncryptedSizes.AppendElement(currentLength);
834
0
            } else {
835
0
              writer->mCrypto.mPlainSizes.AppendElement(currentLength);
836
0
            }
837
0
838
0
            encrypted = !encrypted;
839
0
            lastOffset = partition;
840
0
841
0
            assert(lastOffset <= length);
842
0
          }
843
0
844
0
          // Add the data between the last offset and the end of the data.
845
0
          // 000|XXX|000
846
0
          //        ^---^
847
0
          if (encrypted) {
848
0
            writer->mCrypto.mEncryptedSizes.AppendElement(length - lastOffset);
849
0
          } else {
850
0
            writer->mCrypto.mPlainSizes.AppendElement(length - lastOffset);
851
0
          }
852
0
853
0
          // Make sure we have an equal number of encrypted and plain sizes (GMP
854
0
          // expects this). This simple check is sufficient as there are two
855
0
          // possible cases at this point:
856
0
          // 1. The number of samples are even (so we don't need to do anything)
857
0
          // 2. There is one more clear sample than encrypted samples, so add a
858
0
          // zero length encrypted chunk.
859
0
          // There can never be more encrypted partitions than clear partitions
860
0
          // due to the alternating structure of the WebM samples and the
861
0
          // restriction that the first chunk is always clear.
862
0
          if (numPartitions % 2 == 0) {
863
0
            writer->mCrypto.mEncryptedSizes.AppendElement(0);
864
0
          }
865
0
866
0
          // Assert that the lengths of the encrypted and plain samples add to
867
0
          // the length of the data.
868
0
          assert(((size_t)(std::accumulate(writer->mCrypto.mPlainSizes.begin(), writer->mCrypto.mPlainSizes.end(), 0) \
869
0
                 + std::accumulate(writer->mCrypto.mEncryptedSizes.begin(), writer->mCrypto.mEncryptedSizes.end(), 0)) \
870
0
                 == length));
871
0
        }
872
0
      }
873
0
    }
874
0
    if (aType == TrackInfo::kVideoTrack) {
875
0
      sample->mTrackInfo = mSharedVideoTrackInfo;
876
0
    }
877
0
    aSamples->Push(sample);
878
0
  }
879
0
  return NS_OK;
880
0
}
881
882
nsresult
883
WebMDemuxer::NextPacket(TrackInfo::TrackType aType,
884
                        RefPtr<NesteggPacketHolder>& aPacket)
885
0
{
886
0
  bool isVideo = aType == TrackInfo::kVideoTrack;
887
0
888
0
  // Flag to indicate that we do need to playback these types of
889
0
  // packets.
890
0
  bool hasType = isVideo ? mHasVideo : mHasAudio;
891
0
892
0
  if (!hasType) {
893
0
    return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
894
0
  }
895
0
896
0
  // The packet queue for the type that we are interested in.
897
0
  WebMPacketQueue &packets = isVideo ? mVideoPackets : mAudioPackets;
898
0
899
0
  if (packets.GetSize() > 0) {
900
0
    aPacket = packets.PopFront();
901
0
    return NS_OK;
902
0
  }
903
0
904
0
  // Track we are interested in
905
0
  uint32_t ourTrack = isVideo ? mVideoTrack : mAudioTrack;
906
0
907
0
  do {
908
0
    RefPtr<NesteggPacketHolder> holder;
909
0
    nsresult rv = DemuxPacket(aType, holder);
910
0
    if (NS_FAILED(rv)) {
911
0
      return rv;
912
0
    }
913
0
    if (!holder) {
914
0
      return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
915
0
    }
916
0
917
0
    if (ourTrack == holder->Track()) {
918
0
      aPacket = holder;
919
0
      return NS_OK;
920
0
    }
921
0
  } while (true);
922
0
}
923
924
nsresult
925
WebMDemuxer::DemuxPacket(TrackInfo::TrackType aType,
926
                         RefPtr<NesteggPacketHolder>& aPacket)
927
0
{
928
0
  nestegg_packet* packet;
929
0
  int r = nestegg_read_packet(Context(aType), &packet);
930
0
  if (r == 0) {
931
0
    nestegg_read_reset(Context(aType));
932
0
    return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
933
0
  } else if (r < 0) {
934
0
    return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
935
0
  }
936
0
937
0
  unsigned int track = 0;
938
0
  r = nestegg_packet_track(packet, &track);
939
0
  if (r == -1) {
940
0
    return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
941
0
  }
942
0
943
0
  int64_t offset = Resource(aType).Tell();
944
0
  RefPtr<NesteggPacketHolder> holder = new NesteggPacketHolder();
945
0
  if (!holder->Init(packet, offset, track, false)) {
946
0
    return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
947
0
  }
948
0
949
0
  aPacket = holder;
950
0
  return NS_OK;
951
0
}
952
953
void
954
WebMDemuxer::PushAudioPacket(NesteggPacketHolder* aItem)
955
0
{
956
0
  mAudioPackets.PushFront(aItem);
957
0
}
958
959
void
960
WebMDemuxer::PushVideoPacket(NesteggPacketHolder* aItem)
961
0
{
962
0
  mVideoPackets.PushFront(aItem);
963
0
}
964
965
nsresult
966
WebMDemuxer::SeekInternal(TrackInfo::TrackType aType,
967
                          const TimeUnit& aTarget)
968
0
{
969
0
  EnsureUpToDateIndex();
970
0
  uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
971
0
  uint64_t target = aTarget.ToNanoseconds();
972
0
973
0
  if (NS_FAILED(Reset(aType))) {
974
0
    return NS_ERROR_FAILURE;
975
0
  }
976
0
977
0
  if (mSeekPreroll) {
978
0
    uint64_t startTime = 0;
979
0
    if (!mBufferedState->GetStartTime(&startTime)) {
980
0
      startTime = 0;
981
0
    }
982
0
    WEBM_DEBUG("Seek Target: %f",
983
0
               TimeUnit::FromNanoseconds(target).ToSeconds());
984
0
    if (target < mSeekPreroll || target - mSeekPreroll < startTime) {
985
0
      target = startTime;
986
0
    } else {
987
0
      target -= mSeekPreroll;
988
0
    }
989
0
    WEBM_DEBUG("SeekPreroll: %f StartTime: %f Adjusted Target: %f",
990
0
               TimeUnit::FromNanoseconds(mSeekPreroll).ToSeconds(),
991
0
               TimeUnit::FromNanoseconds(startTime).ToSeconds(),
992
0
               TimeUnit::FromNanoseconds(target).ToSeconds());
993
0
  }
994
0
  int r = nestegg_track_seek(Context(aType), trackToSeek, target);
995
0
  if (r == -1) {
996
0
    WEBM_DEBUG("track_seek for track %u to %f failed, r=%d", trackToSeek,
997
0
               TimeUnit::FromNanoseconds(target).ToSeconds(), r);
998
0
    // Try seeking directly based on cluster information in memory.
999
0
    int64_t offset = 0;
1000
0
    bool rv = mBufferedState->GetOffsetForTime(target, &offset);
1001
0
    if (!rv) {
1002
0
      WEBM_DEBUG("mBufferedState->GetOffsetForTime failed too");
1003
0
      return NS_ERROR_FAILURE;
1004
0
    }
1005
0
1006
0
    r = nestegg_offset_seek(Context(aType), offset);
1007
0
    if (r == -1) {
1008
0
      WEBM_DEBUG("and nestegg_offset_seek to %" PRIu64 " failed", offset);
1009
0
      return NS_ERROR_FAILURE;
1010
0
    }
1011
0
    WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset);
1012
0
  }
1013
0
1014
0
  if (aType == TrackInfo::kAudioTrack) {
1015
0
    mLastAudioFrameTime.reset();
1016
0
  } else {
1017
0
    mLastVideoFrameTime.reset();
1018
0
  }
1019
0
1020
0
  return NS_OK;
1021
0
}
1022
1023
media::TimeIntervals
1024
WebMDemuxer::GetBuffered()
1025
0
{
1026
0
  EnsureUpToDateIndex();
1027
0
  AutoPinned<MediaResource> resource(
1028
0
    Resource(TrackInfo::kVideoTrack).GetResource());
1029
0
1030
0
  media::TimeIntervals buffered;
1031
0
1032
0
  MediaByteRangeSet ranges;
1033
0
  nsresult rv = resource->GetCachedRanges(ranges);
1034
0
  if (NS_FAILED(rv)) {
1035
0
    return media::TimeIntervals();
1036
0
  }
1037
0
  uint64_t duration = 0;
1038
0
  uint64_t startOffset = 0;
1039
0
  if (!nestegg_duration(Context(TrackInfo::kVideoTrack), &duration)) {
1040
0
    if(mBufferedState->GetStartTime(&startOffset)) {
1041
0
      duration += startOffset;
1042
0
    }
1043
0
    WEBM_DEBUG("Duration: %f StartTime: %f",
1044
0
               TimeUnit::FromNanoseconds(duration).ToSeconds(),
1045
0
               TimeUnit::FromNanoseconds(startOffset).ToSeconds());
1046
0
  }
1047
0
  for (uint32_t index = 0; index < ranges.Length(); index++) {
1048
0
    uint64_t start, end;
1049
0
    bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
1050
0
                                                        ranges[index].mEnd,
1051
0
                                                        &start, &end);
1052
0
    if (rv) {
1053
0
      NS_ASSERTION(startOffset <= start,
1054
0
          "startOffset negative or larger than start time");
1055
0
1056
0
      if (duration && end > duration) {
1057
0
        WEBM_DEBUG("limit range to duration, end: %f duration: %f",
1058
0
                   TimeUnit::FromNanoseconds(end).ToSeconds(),
1059
0
                   TimeUnit::FromNanoseconds(duration).ToSeconds());
1060
0
        end = duration;
1061
0
      }
1062
0
      auto startTime = TimeUnit::FromNanoseconds(start);
1063
0
      auto endTime = TimeUnit::FromNanoseconds(end);
1064
0
      WEBM_DEBUG("add range %f-%f", startTime.ToSeconds(), endTime.ToSeconds());
1065
0
      buffered += media::TimeInterval(startTime, endTime);
1066
0
    }
1067
0
  }
1068
0
  return buffered;
1069
0
}
1070
1071
bool WebMDemuxer::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
1072
0
{
1073
0
  EnsureUpToDateIndex();
1074
0
  return mBufferedState && mBufferedState->GetOffsetForTime(aTime, aOffset);
1075
0
}
1076
1077
1078
//WebMTrackDemuxer
1079
WebMTrackDemuxer::WebMTrackDemuxer(WebMDemuxer* aParent,
1080
                                   TrackInfo::TrackType aType,
1081
                                   uint32_t aTrackNumber)
1082
  : mParent(aParent)
1083
  , mType(aType)
1084
  , mNeedKeyframe(true)
1085
0
{
1086
0
  mInfo = mParent->GetTrackInfo(aType, aTrackNumber);
1087
0
  MOZ_ASSERT(mInfo);
1088
0
}
1089
1090
WebMTrackDemuxer::~WebMTrackDemuxer()
1091
0
{
1092
0
  mSamples.Reset();
1093
0
}
1094
1095
UniquePtr<TrackInfo>
1096
WebMTrackDemuxer::GetInfo() const
1097
0
{
1098
0
  return mInfo->Clone();
1099
0
}
1100
1101
RefPtr<WebMTrackDemuxer::SeekPromise>
1102
WebMTrackDemuxer::Seek(const TimeUnit& aTime)
1103
0
{
1104
0
  // Seeks to aTime. Upon success, SeekPromise will be resolved with the
1105
0
  // actual time seeked to. Typically the random access point time
1106
0
1107
0
  auto seekTime = aTime;
1108
0
  bool keyframe = false;
1109
0
1110
0
  mNeedKeyframe = true;
1111
0
1112
0
  do {
1113
0
    mSamples.Reset();
1114
0
    mParent->SeekInternal(mType, seekTime);
1115
0
    nsresult rv = mParent->GetNextPacket(mType, &mSamples);
1116
0
    if (NS_FAILED(rv)) {
1117
0
      if (rv == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
1118
0
        // Ignore the error for now, the next GetSample will be rejected with EOS.
1119
0
        return SeekPromise::CreateAndResolve(TimeUnit::Zero(), __func__);
1120
0
      }
1121
0
      return SeekPromise::CreateAndReject(rv, __func__);
1122
0
    }
1123
0
1124
0
    // Check what time we actually seeked to.
1125
0
    if (mSamples.GetSize() == 0) {
1126
0
      // We can't determine if the seek succeeded at this stage, so break the
1127
0
      // loop.
1128
0
      break;
1129
0
    }
1130
0
1131
0
    for (const auto& sample : mSamples) {
1132
0
      seekTime = sample->mTime;
1133
0
      keyframe = sample->mKeyframe;
1134
0
      if (keyframe) {
1135
0
        break;
1136
0
      }
1137
0
    }
1138
0
    if (mType == TrackInfo::kVideoTrack &&
1139
0
        !mInfo->GetAsVideoInfo()->HasAlpha()) {
1140
0
      // We only perform a search for a keyframe on videos with alpha layer to
1141
0
      // prevent potential regression for normal video (even though invalid)
1142
0
      break;
1143
0
    }
1144
0
    if (!keyframe) {
1145
0
      // We didn't find any keyframe, attempt to seek to the previous cluster.
1146
0
      seekTime = mSamples.First()->mTime - TimeUnit::FromMicroseconds(1);
1147
0
    }
1148
0
  } while (!keyframe && seekTime >= TimeUnit::Zero());
1149
0
1150
0
  SetNextKeyFrameTime();
1151
0
1152
0
  return SeekPromise::CreateAndResolve(seekTime, __func__);
1153
0
}
1154
1155
nsresult
1156
WebMTrackDemuxer::NextSample(RefPtr<MediaRawData>& aData)
1157
0
{
1158
0
  nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
1159
0
  while (mSamples.GetSize() < 1 &&
1160
0
         NS_SUCCEEDED((rv = mParent->GetNextPacket(mType, &mSamples)))) {
1161
0
  }
1162
0
  if (mSamples.GetSize()) {
1163
0
    aData = mSamples.PopFront();
1164
0
    return NS_OK;
1165
0
  }
1166
0
  return rv;
1167
0
}
1168
1169
RefPtr<WebMTrackDemuxer::SamplesPromise>
1170
WebMTrackDemuxer::GetSamples(int32_t aNumSamples)
1171
0
{
1172
0
  RefPtr<SamplesHolder> samples = new SamplesHolder;
1173
0
  MOZ_ASSERT(aNumSamples);
1174
0
1175
0
  nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
1176
0
1177
0
  while (aNumSamples) {
1178
0
    RefPtr<MediaRawData> sample;
1179
0
    rv = NextSample(sample);
1180
0
    if (NS_FAILED(rv)) {
1181
0
      break;
1182
0
    }
1183
0
    if (mNeedKeyframe && !sample->mKeyframe) {
1184
0
      continue;
1185
0
    }
1186
0
    mNeedKeyframe = false;
1187
0
    samples->mSamples.AppendElement(sample);
1188
0
    aNumSamples--;
1189
0
  }
1190
0
1191
0
  if (samples->mSamples.IsEmpty()) {
1192
0
    return SamplesPromise::CreateAndReject(rv, __func__);
1193
0
  } else {
1194
0
    UpdateSamples(samples->mSamples);
1195
0
    return SamplesPromise::CreateAndResolve(samples, __func__);
1196
0
  }
1197
0
}
1198
1199
void
1200
WebMTrackDemuxer::SetNextKeyFrameTime()
1201
0
{
1202
0
  if (mType != TrackInfo::kVideoTrack || mParent->IsMediaSource()) {
1203
0
    return;
1204
0
  }
1205
0
1206
0
  auto frameTime = TimeUnit::Invalid();
1207
0
1208
0
  mNextKeyframeTime.reset();
1209
0
1210
0
  MediaRawDataQueue skipSamplesQueue;
1211
0
  bool foundKeyframe = false;
1212
0
  while (!foundKeyframe && mSamples.GetSize()) {
1213
0
    RefPtr<MediaRawData> sample = mSamples.PopFront();
1214
0
    if (sample->mKeyframe) {
1215
0
      frameTime = sample->mTime;
1216
0
      foundKeyframe = true;
1217
0
    }
1218
0
    skipSamplesQueue.Push(sample.forget());
1219
0
  }
1220
0
  Maybe<int64_t> startTime;
1221
0
  if (skipSamplesQueue.GetSize()) {
1222
0
    const RefPtr<MediaRawData>& sample = skipSamplesQueue.First();
1223
0
    startTime.emplace(sample->mTimecode.ToMicroseconds());
1224
0
  }
1225
0
  // Demux and buffer frames until we find a keyframe.
1226
0
  RefPtr<MediaRawData> sample;
1227
0
  nsresult rv = NS_OK;
1228
0
  while (!foundKeyframe && NS_SUCCEEDED((rv = NextSample(sample)))) {
1229
0
    if (sample->mKeyframe) {
1230
0
      frameTime = sample->mTime;
1231
0
      foundKeyframe = true;
1232
0
    }
1233
0
    int64_t sampleTimecode = sample->mTimecode.ToMicroseconds();
1234
0
    skipSamplesQueue.Push(sample.forget());
1235
0
    if (!startTime) {
1236
0
      startTime.emplace(sampleTimecode);
1237
0
    } else if (!foundKeyframe &&
1238
0
               sampleTimecode > startTime.ref() + MAX_LOOK_AHEAD) {
1239
0
      WEBM_DEBUG("Couldn't find keyframe in a reasonable time, aborting");
1240
0
      break;
1241
0
    }
1242
0
  }
1243
0
  // We may have demuxed more than intended, so ensure that all frames are kept
1244
0
  // in the right order.
1245
0
  mSamples.PushFront(std::move(skipSamplesQueue));
1246
0
1247
0
  if (frameTime.IsValid()) {
1248
0
    mNextKeyframeTime.emplace(frameTime);
1249
0
    WEBM_DEBUG("Next Keyframe %f (%u queued %.02fs)",
1250
0
               mNextKeyframeTime.value().ToSeconds(),
1251
0
               uint32_t(mSamples.GetSize()),
1252
0
               (mSamples.Last()->mTimecode - mSamples.First()->mTimecode).ToSeconds());
1253
0
  } else {
1254
0
    WEBM_DEBUG("Couldn't determine next keyframe time  (%u queued)",
1255
0
               uint32_t(mSamples.GetSize()));
1256
0
  }
1257
0
}
1258
1259
void
1260
WebMTrackDemuxer::Reset()
1261
0
{
1262
0
  mSamples.Reset();
1263
0
  media::TimeIntervals buffered = GetBuffered();
1264
0
  mNeedKeyframe = true;
1265
0
  if (buffered.Length()) {
1266
0
    WEBM_DEBUG("Seek to start point: %f", buffered.Start(0).ToSeconds());
1267
0
    mParent->SeekInternal(mType, buffered.Start(0));
1268
0
    SetNextKeyFrameTime();
1269
0
  } else {
1270
0
    mNextKeyframeTime.reset();
1271
0
  }
1272
0
}
1273
1274
void
1275
WebMTrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples)
1276
0
{
1277
0
  for (const auto& sample : aSamples) {
1278
0
    if (sample->mCrypto.mValid) {
1279
0
      UniquePtr<MediaRawDataWriter> writer(sample->CreateWriter());
1280
0
      writer->mCrypto.mMode = mInfo->mCrypto.mMode;
1281
0
      writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
1282
0
      writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
1283
0
    }
1284
0
  }
1285
0
  if (mNextKeyframeTime.isNothing() ||
1286
0
      aSamples.LastElement()->mTime >= mNextKeyframeTime.value()) {
1287
0
    SetNextKeyFrameTime();
1288
0
  }
1289
0
}
1290
1291
nsresult
1292
WebMTrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime)
1293
0
{
1294
0
  if (mNextKeyframeTime.isNothing()) {
1295
0
    // There's no next key frame.
1296
0
    *aTime = TimeUnit::FromInfinity();
1297
0
  } else {
1298
0
    *aTime = mNextKeyframeTime.ref();
1299
0
  }
1300
0
  return NS_OK;
1301
0
}
1302
1303
RefPtr<WebMTrackDemuxer::SkipAccessPointPromise>
1304
WebMTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
1305
0
{
1306
0
  uint32_t parsed = 0;
1307
0
  bool found = false;
1308
0
  RefPtr<MediaRawData> sample;
1309
0
  nsresult rv = NS_OK;
1310
0
1311
0
  WEBM_DEBUG("TimeThreshold: %f", aTimeThreshold.ToSeconds());
1312
0
  while (!found && NS_SUCCEEDED((rv = NextSample(sample)))) {
1313
0
    parsed++;
1314
0
    if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
1315
0
      WEBM_DEBUG("next sample: %f (parsed: %d)",
1316
0
                 sample->mTime.ToSeconds(), parsed);
1317
0
      found = true;
1318
0
      mSamples.Reset();
1319
0
      mSamples.PushFront(sample.forget());
1320
0
    }
1321
0
  }
1322
0
  if (NS_SUCCEEDED(rv)) {
1323
0
    SetNextKeyFrameTime();
1324
0
  }
1325
0
  if (found) {
1326
0
    return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
1327
0
  } else {
1328
0
    SkipFailureHolder failure(NS_ERROR_DOM_MEDIA_END_OF_STREAM, parsed);
1329
0
    return SkipAccessPointPromise::CreateAndReject(std::move(failure), __func__);
1330
0
  }
1331
0
}
1332
1333
media::TimeIntervals
1334
WebMTrackDemuxer::GetBuffered()
1335
0
{
1336
0
  return mParent->GetBuffered();
1337
0
}
1338
1339
void
1340
WebMTrackDemuxer::BreakCycles()
1341
0
{
1342
0
  mParent = nullptr;
1343
0
}
1344
1345
int64_t
1346
WebMTrackDemuxer::GetEvictionOffset(const TimeUnit& aTime)
1347
0
{
1348
0
  int64_t offset;
1349
0
  if (!mParent->GetOffsetForTime(aTime.ToNanoseconds(), &offset)) {
1350
0
    return 0;
1351
0
  }
1352
0
1353
0
  return offset;
1354
0
}
1355
1356
#undef WEBM_DEBUG
1357
} // namespace mozilla