Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/mp4/MP4Demuxer.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 <algorithm>
8
#include <limits>
9
#include <stdint.h>
10
11
#include "MP4Demuxer.h"
12
13
#include "mozilla/StaticPrefs.h"
14
// Used for telemetry
15
#include "mozilla/Telemetry.h"
16
#include "AnnexB.h"
17
#include "H264.h"
18
#include "MoofParser.h"
19
#include "MP4Metadata.h"
20
#include "ResourceStream.h"
21
#include "BufferStream.h"
22
#include "Index.h"
23
#include "nsAutoPtr.h"
24
#include "nsPrintfCString.h"
25
26
extern mozilla::LazyLogModule gMediaDemuxerLog;
27
mozilla::LogModule* GetDemuxerLog()
28
0
{
29
0
  return gMediaDemuxerLog;
30
0
}
31
32
#define LOG(arg, ...)                                                          \
33
  DDMOZ_LOG(gMediaDemuxerLog,                                                  \
34
            mozilla::LogLevel::Debug,                                          \
35
            "::%s: " arg,                                                      \
36
            __func__,                                                          \
37
            ##__VA_ARGS__)
38
39
namespace mozilla {
40
41
DDLoggedTypeDeclNameAndBase(MP4TrackDemuxer, MediaTrackDemuxer);
42
43
class MP4TrackDemuxer
44
  : public MediaTrackDemuxer
45
  , public DecoderDoctorLifeLogger<MP4TrackDemuxer>
46
{
47
public:
48
  MP4TrackDemuxer(MP4Demuxer* aParent,
49
                  UniquePtr<TrackInfo>&& aInfo,
50
                  const IndiceWrapper& aIndices);
51
52
  UniquePtr<TrackInfo> GetInfo() const override;
53
54
  RefPtr<SeekPromise> Seek(const media::TimeUnit& aTime) override;
55
56
  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
57
58
  void Reset() override;
59
60
  nsresult GetNextRandomAccessPoint(media::TimeUnit* aTime) override;
61
62
  RefPtr<SkipAccessPointPromise>
63
  SkipToNextRandomAccessPoint(const media::TimeUnit& aTimeThreshold) override;
64
65
  media::TimeIntervals GetBuffered() override;
66
67
  void BreakCycles() override;
68
69
  void NotifyDataRemoved();
70
71
private:
72
  friend class MP4Demuxer;
73
  void NotifyDataArrived();
74
  already_AddRefed<MediaRawData> GetNextSample();
75
  void EnsureUpToDateIndex();
76
  void SetNextKeyFrameTime();
77
  RefPtr<MP4Demuxer> mParent;
78
  RefPtr<ResourceStream> mStream;
79
  UniquePtr<TrackInfo> mInfo;
80
  RefPtr<Index> mIndex;
81
  UniquePtr<SampleIterator> mIterator;
82
  Maybe<media::TimeUnit> mNextKeyframeTime;
83
  // Queued samples extracted by the demuxer, but not yet returned.
84
  RefPtr<MediaRawData> mQueuedSample;
85
  bool mNeedReIndex;
86
  bool mNeedSPSForTelemetry;
87
  bool mIsH264 = false;
88
};
89
90
91
// Returns true if no SPS was found and search for it should continue.
92
bool
93
AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata)
94
0
{
95
0
  SPSData spsdata;
96
0
  if (H264::DecodeSPSFromExtraData(aExtradata, spsdata)) {
97
0
    uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0)
98
0
                          | (spsdata.constraint_set1_flag ? (1 << 1) : 0)
99
0
                          | (spsdata.constraint_set2_flag ? (1 << 2) : 0)
100
0
                          | (spsdata.constraint_set3_flag ? (1 << 3) : 0)
101
0
                          | (spsdata.constraint_set4_flag ? (1 << 4) : 0)
102
0
                          | (spsdata.constraint_set5_flag ? (1 << 5) : 0);
103
0
    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_CONSTRAINT_SET_FLAG,
104
0
                          constraints);
105
0
106
0
    // Collect profile_idc values up to 244, otherwise 0 for unknown.
107
0
    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_PROFILE,
108
0
                          spsdata.profile_idc <= 244 ? spsdata.profile_idc : 0);
109
0
110
0
    // Make sure level_idc represents a value between levels 1 and 5.2,
111
0
    // otherwise collect 0 for unknown level.
112
0
    Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_LEVEL,
113
0
                          (spsdata.level_idc >= 10 && spsdata.level_idc <= 52)
114
0
                          ? spsdata.level_idc
115
0
                          : 0);
116
0
117
0
    // max_num_ref_frames should be between 0 and 16, anything larger will
118
0
    // be treated as invalid.
119
0
    Telemetry::Accumulate(Telemetry::VIDEO_H264_SPS_MAX_NUM_REF_FRAMES,
120
0
                          std::min(spsdata.max_num_ref_frames, 17u));
121
0
122
0
    return false;
123
0
  }
124
0
125
0
  return true;
126
0
}
127
128
MP4Demuxer::MP4Demuxer(MediaResource* aResource)
129
  : mResource(aResource)
130
  , mStream(new ResourceStream(aResource))
131
  , mIsSeekable(false)
132
0
{
133
0
  DDLINKCHILD("resource", aResource);
134
0
  DDLINKCHILD("stream", mStream.get());
135
0
}
136
137
RefPtr<MP4Demuxer::InitPromise>
138
MP4Demuxer::Init()
139
0
{
140
0
  AutoPinned<ResourceStream> stream(mStream);
141
0
142
0
  // 'result' will capture the first warning, if any.
143
0
  MediaResult result{NS_OK};
144
0
145
0
  MP4Metadata::ResultAndByteBuffer initData =
146
0
    MP4Metadata::Metadata(stream);
147
0
  if (!initData.Ref()) {
148
0
    return InitPromise::CreateAndReject(
149
0
      NS_FAILED(initData.Result())
150
0
      ? std::move(initData.Result())
151
0
      : MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
152
0
                    RESULT_DETAIL("Invalid MP4 metadata or OOM")),
153
0
      __func__);
154
0
  } else if (NS_FAILED(initData.Result()) && result == NS_OK) {
155
0
    result = std::move(initData.Result());
156
0
  }
157
0
158
0
  RefPtr<BufferStream> bufferstream =
159
0
    new BufferStream(initData.Ref());
160
0
161
0
  MP4Metadata metadata{bufferstream};
162
0
  DDLINKCHILD("metadata", &metadata);
163
0
  nsresult rv = metadata.Parse();
164
0
  if (NS_FAILED(rv)) {
165
0
    return InitPromise::CreateAndReject(
166
0
      MediaResult(rv, RESULT_DETAIL("Parse MP4 metadata failed")), __func__);
167
0
  }
168
0
169
0
  auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack);
170
0
  if (audioTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
171
0
    if (StaticPrefs::MediaPlaybackWarningsAsErrors()) {
172
0
      return InitPromise::CreateAndReject(
173
0
        MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
174
0
                    RESULT_DETAIL("Invalid audio track (%s)",
175
0
                                  audioTrackCount.Result().Description().get())),
176
0
        __func__);
177
0
    }
178
0
    audioTrackCount.Ref() = 0;
179
0
  }
180
0
181
0
  auto videoTrackCount = metadata.GetNumberTracks(TrackInfo::kVideoTrack);
182
0
  if (videoTrackCount.Ref() == MP4Metadata::NumberTracksError()) {
183
0
    if (StaticPrefs::MediaPlaybackWarningsAsErrors()) {
184
0
      return InitPromise::CreateAndReject(
185
0
        MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
186
0
                    RESULT_DETAIL("Invalid video track (%s)",
187
0
                                  videoTrackCount.Result().Description().get())),
188
0
        __func__);
189
0
    }
190
0
    videoTrackCount.Ref() = 0;
191
0
  }
192
0
193
0
  if (audioTrackCount.Ref() == 0 && videoTrackCount.Ref() == 0) {
194
0
    return InitPromise::CreateAndReject(
195
0
      MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
196
0
                  RESULT_DETAIL("No MP4 audio (%s) or video (%s) tracks",
197
0
                                audioTrackCount.Result().Description().get(),
198
0
                                videoTrackCount.Result().Description().get())),
199
0
      __func__);
200
0
  }
201
0
202
0
  if (NS_FAILED(audioTrackCount.Result()) && result == NS_OK) {
203
0
    result = std::move(audioTrackCount.Result());
204
0
  }
205
0
  if (NS_FAILED(videoTrackCount.Result()) && result == NS_OK) {
206
0
    result = std::move(videoTrackCount.Result());
207
0
  }
208
0
209
0
  if (audioTrackCount.Ref() != 0) {
210
0
    for (size_t i = 0; i < audioTrackCount.Ref(); i++) {
211
0
      MP4Metadata::ResultAndTrackInfo info =
212
0
        metadata.GetTrackInfo(TrackInfo::kAudioTrack, i);
213
0
      if (!info.Ref()) {
214
0
        if (StaticPrefs::MediaPlaybackWarningsAsErrors()) {
215
0
          return InitPromise::CreateAndReject(
216
0
            MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
217
0
                        RESULT_DETAIL("Invalid MP4 audio track (%s)",
218
0
                                      info.Result().Description().get())),
219
0
            __func__);
220
0
        }
221
0
        if (result == NS_OK) {
222
0
          result = MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
223
0
                               RESULT_DETAIL("Invalid MP4 audio track (%s)",
224
0
                                             info.Result().Description().get()));
225
0
        }
226
0
        continue;
227
0
      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
228
0
        result = std::move(info.Result());
229
0
      }
230
0
      MP4Metadata::ResultAndIndice indices =
231
0
        metadata.GetTrackIndice(info.Ref()->mTrackId);
232
0
      if (!indices.Ref()) {
233
0
        if (NS_FAILED(info.Result()) && result == NS_OK) {
234
0
          result = std::move(indices.Result());
235
0
        }
236
0
        continue;
237
0
      }
238
0
      RefPtr<MP4TrackDemuxer> demuxer =
239
0
        new MP4TrackDemuxer(this, std::move(info.Ref()), *indices.Ref().get());
240
0
      DDLINKCHILD("audio demuxer", demuxer.get());
241
0
      mAudioDemuxers.AppendElement(std::move(demuxer));
242
0
    }
243
0
  }
244
0
245
0
  if (videoTrackCount.Ref() != 0) {
246
0
    for (size_t i = 0; i < videoTrackCount.Ref(); i++) {
247
0
      MP4Metadata::ResultAndTrackInfo info =
248
0
        metadata.GetTrackInfo(TrackInfo::kVideoTrack, i);
249
0
      if (!info.Ref()) {
250
0
        if (StaticPrefs::MediaPlaybackWarningsAsErrors()) {
251
0
          return InitPromise::CreateAndReject(
252
0
            MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
253
0
                        RESULT_DETAIL("Invalid MP4 video track (%s)",
254
0
                                      info.Result().Description().get())),
255
0
            __func__);
256
0
        }
257
0
        if (result == NS_OK) {
258
0
          result = MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
259
0
                               RESULT_DETAIL("Invalid MP4 video track (%s)",
260
0
                                             info.Result().Description().get()));
261
0
        }
262
0
        continue;
263
0
      } else if (NS_FAILED(info.Result()) && result == NS_OK) {
264
0
        result = std::move(info.Result());
265
0
      }
266
0
      MP4Metadata::ResultAndIndice indices =
267
0
        metadata.GetTrackIndice(info.Ref()->mTrackId);
268
0
      if (!indices.Ref()) {
269
0
        if (NS_FAILED(info.Result()) && result == NS_OK) {
270
0
          result = std::move(indices.Result());
271
0
        }
272
0
        continue;
273
0
      }
274
0
      RefPtr<MP4TrackDemuxer> demuxer =
275
0
        new MP4TrackDemuxer(this, std::move(info.Ref()), *indices.Ref().get());
276
0
      DDLINKCHILD("video demuxer", demuxer.get());
277
0
      mVideoDemuxers.AppendElement(std::move(demuxer));
278
0
    }
279
0
  }
280
0
281
0
  MP4Metadata::ResultAndCryptoFile cryptoFile =
282
0
    metadata.Crypto();
283
0
  if (NS_FAILED(cryptoFile.Result()) && result == NS_OK) {
284
0
    result = std::move(cryptoFile.Result());
285
0
  }
286
0
  MOZ_ASSERT(cryptoFile.Ref());
287
0
  if (cryptoFile.Ref()->valid) {
288
0
    const nsTArray<PsshInfo>& psshs = cryptoFile.Ref()->pssh;
289
0
    for (uint32_t i = 0; i < psshs.Length(); i++) {
290
0
      mCryptoInitData.AppendElements(psshs[i].data);
291
0
    }
292
0
  }
293
0
294
0
  mIsSeekable = metadata.CanSeek();
295
0
296
0
  return InitPromise::CreateAndResolve(result, __func__);
297
0
}
298
299
uint32_t
300
MP4Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const
301
{
302
  switch (aType) {
303
    case TrackInfo::kAudioTrack: return uint32_t(mAudioDemuxers.Length());
304
    case TrackInfo::kVideoTrack: return uint32_t(mVideoDemuxers.Length());
305
    default: return 0;
306
  }
307
}
308
309
already_AddRefed<MediaTrackDemuxer>
310
MP4Demuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
311
0
{
312
0
  switch (aType) {
313
0
    case TrackInfo::kAudioTrack:
314
0
      if (aTrackNumber >= uint32_t(mAudioDemuxers.Length())) {
315
0
        return nullptr;
316
0
      }
317
0
      return RefPtr<MediaTrackDemuxer>(mAudioDemuxers[aTrackNumber]).forget();
318
0
    case TrackInfo::kVideoTrack:
319
0
      if (aTrackNumber >= uint32_t(mVideoDemuxers.Length())) {
320
0
        return nullptr;
321
0
      }
322
0
      return RefPtr<MediaTrackDemuxer>(mVideoDemuxers[aTrackNumber]).forget();
323
0
    default:
324
0
      return nullptr;
325
0
  }
326
0
}
327
328
bool
329
MP4Demuxer::IsSeekable() const
330
0
{
331
0
  return mIsSeekable;
332
0
}
333
334
void
335
MP4Demuxer::NotifyDataArrived()
336
0
{
337
0
  for (auto& dmx : mAudioDemuxers) {
338
0
    dmx->NotifyDataArrived();
339
0
  }
340
0
  for (auto& dmx : mVideoDemuxers) {
341
0
    dmx->NotifyDataArrived();
342
0
  }
343
0
}
344
345
void
346
MP4Demuxer::NotifyDataRemoved()
347
0
{
348
0
  for (auto& dmx : mAudioDemuxers) {
349
0
    dmx->NotifyDataRemoved();
350
0
  }
351
0
  for (auto& dmx : mVideoDemuxers) {
352
0
    dmx->NotifyDataRemoved();
353
0
  }
354
0
}
355
356
UniquePtr<EncryptionInfo>
357
MP4Demuxer::GetCrypto()
358
0
{
359
0
  UniquePtr<EncryptionInfo> crypto;
360
0
  if (!mCryptoInitData.IsEmpty()) {
361
0
    crypto.reset(new EncryptionInfo{});
362
0
    crypto->AddInitData(NS_LITERAL_STRING("cenc"), mCryptoInitData);
363
0
  }
364
0
  return crypto;
365
0
}
366
367
MP4TrackDemuxer::MP4TrackDemuxer(MP4Demuxer* aParent,
368
                                 UniquePtr<TrackInfo>&& aInfo,
369
                                 const IndiceWrapper& aIndices)
370
  : mParent(aParent)
371
  , mStream(new ResourceStream(mParent->mResource))
372
  , mInfo(std::move(aInfo))
373
  , mIndex(new Index(aIndices,
374
                     mStream,
375
                     mInfo->mTrackId,
376
                     mInfo->IsAudio()))
377
  , mIterator(MakeUnique<SampleIterator>(mIndex))
378
  , mNeedReIndex(true)
379
0
{
380
0
  EnsureUpToDateIndex(); // Force update of index
381
0
382
0
  VideoInfo* videoInfo = mInfo->GetAsVideoInfo();
383
0
  // Collect telemetry from h264 AVCC SPS.
384
0
  if (videoInfo && (mInfo->mMimeType.EqualsLiteral("video/mp4") ||
385
0
                    mInfo->mMimeType.EqualsLiteral("video/avc"))) {
386
0
    mIsH264 = true;
387
0
    RefPtr<MediaByteBuffer> extraData = videoInfo->mExtraData;
388
0
    mNeedSPSForTelemetry = AccumulateSPSTelemetry(extraData);
389
0
    SPSData spsdata;
390
0
    if (H264::DecodeSPSFromExtraData(extraData, spsdata) &&
391
0
        spsdata.pic_width > 0 && spsdata.pic_height > 0 &&
392
0
        H264::EnsureSPSIsSane(spsdata)) {
393
0
      videoInfo->mImage.width = spsdata.pic_width;
394
0
      videoInfo->mImage.height = spsdata.pic_height;
395
0
      videoInfo->mDisplay.width = spsdata.display_width;
396
0
      videoInfo->mDisplay.height = spsdata.display_height;
397
0
    }
398
0
  } else {
399
0
    // No SPS to be found.
400
0
    mNeedSPSForTelemetry = false;
401
0
  }
402
0
}
403
404
UniquePtr<TrackInfo>
405
MP4TrackDemuxer::GetInfo() const
406
0
{
407
0
  return mInfo->Clone();
408
0
}
409
410
void
411
MP4TrackDemuxer::EnsureUpToDateIndex()
412
0
{
413
0
  if (!mNeedReIndex) {
414
0
    return;
415
0
  }
416
0
  AutoPinned<MediaResource> resource(mParent->mResource);
417
0
  MediaByteRangeSet byteRanges;
418
0
  nsresult rv = resource->GetCachedRanges(byteRanges);
419
0
  if (NS_FAILED(rv)) {
420
0
    return;
421
0
  }
422
0
  mIndex->UpdateMoofIndex(byteRanges);
423
0
  mNeedReIndex = false;
424
0
}
425
426
RefPtr<MP4TrackDemuxer::SeekPromise>
427
MP4TrackDemuxer::Seek(const media::TimeUnit& aTime)
428
0
{
429
0
  auto seekTime = aTime;
430
0
  mQueuedSample = nullptr;
431
0
432
0
  mIterator->Seek(seekTime.ToMicroseconds());
433
0
434
0
  // Check what time we actually seeked to.
435
0
  do {
436
0
    RefPtr<MediaRawData> sample = GetNextSample();
437
0
    if (!sample) {
438
0
      return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
439
0
                                          __func__);
440
0
    }
441
0
    if (!sample->Size()) {
442
0
      // This sample can't be decoded, continue searching.
443
0
      continue;
444
0
    }
445
0
    if (sample->mKeyframe) {
446
0
      mQueuedSample = sample;
447
0
      seekTime = mQueuedSample->mTime;
448
0
    }
449
0
  } while (!mQueuedSample);
450
0
451
0
  SetNextKeyFrameTime();
452
0
453
0
  return SeekPromise::CreateAndResolve(seekTime, __func__);
454
0
}
455
456
already_AddRefed<MediaRawData>
457
MP4TrackDemuxer::GetNextSample()
458
0
{
459
0
  RefPtr<MediaRawData> sample = mIterator->GetNext();
460
0
  if (!sample) {
461
0
    return nullptr;
462
0
  }
463
0
  if (mInfo->GetAsVideoInfo()) {
464
0
    sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
465
0
    if (mIsH264 && !sample->mCrypto.mValid) {
466
0
      H264::FrameType type = H264::GetFrameType(sample);
467
0
      switch (type) {
468
0
        case H264::FrameType::I_FRAME: MOZ_FALLTHROUGH;
469
0
        case H264::FrameType::OTHER:
470
0
        {
471
0
          bool keyframe = type == H264::FrameType::I_FRAME;
472
0
          if (sample->mKeyframe != keyframe) {
473
0
            NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe "
474
0
                                       "@ pts:%" PRId64 " dur:%" PRId64
475
0
                                       " dts:%" PRId64,
476
0
                                       keyframe ? "" : "non-",
477
0
                                       sample->mTime.ToMicroseconds(),
478
0
                                       sample->mDuration.ToMicroseconds(),
479
0
                                       sample->mTimecode.ToMicroseconds())
480
0
                         .get());
481
0
            sample->mKeyframe = keyframe;
482
0
          }
483
0
          break;
484
0
        }
485
0
        case H264::FrameType::INVALID:
486
0
          NS_WARNING(
487
0
            nsPrintfCString("Invalid H264 frame @ pts:%" PRId64 " dur:%" PRId64
488
0
                            " dts:%" PRId64,
489
0
                            sample->mTime.ToMicroseconds(),
490
0
                            sample->mDuration.ToMicroseconds(),
491
0
                            sample->mTimecode.ToMicroseconds())
492
0
              .get());
493
0
          // We could reject the sample now, however demuxer errors are fatal.
494
0
          // So we keep the invalid frame, relying on the H264 decoder to
495
0
          // handle the error later.
496
0
          // TODO: make demuxer errors non-fatal.
497
0
          break;
498
0
      }
499
0
    }
500
0
  }
501
0
502
0
  if (sample->mCrypto.mValid) {
503
0
    UniquePtr<MediaRawDataWriter> writer(sample->CreateWriter());
504
0
    writer->mCrypto.mMode = mInfo->mCrypto.mMode;
505
0
506
0
    // Only use the default key parsed from the moov if we haven't already got
507
0
    // one from the sample group description.
508
0
    if (writer->mCrypto.mKeyId.Length() == 0) {
509
0
      writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
510
0
      writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
511
0
    }
512
0
  }
513
0
  return sample.forget();
514
0
}
515
516
RefPtr<MP4TrackDemuxer::SamplesPromise>
517
MP4TrackDemuxer::GetSamples(int32_t aNumSamples)
518
0
{
519
0
  EnsureUpToDateIndex();
520
0
  RefPtr<SamplesHolder> samples = new SamplesHolder;
521
0
  if (!aNumSamples) {
522
0
    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR,
523
0
                                           __func__);
524
0
  }
525
0
526
0
  if (mQueuedSample) {
527
0
    NS_ASSERTION(mQueuedSample->mKeyframe,
528
0
                 "mQueuedSample must be a keyframe");
529
0
    samples->mSamples.AppendElement(mQueuedSample);
530
0
    mQueuedSample = nullptr;
531
0
    aNumSamples--;
532
0
  }
533
0
  RefPtr<MediaRawData> sample;
534
0
  while (aNumSamples && (sample = GetNextSample())) {
535
0
    if (!sample->Size()) {
536
0
      continue;
537
0
    }
538
0
    samples->mSamples.AppendElement(sample);
539
0
    aNumSamples--;
540
0
  }
541
0
542
0
  if (samples->mSamples.IsEmpty()) {
543
0
    return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
544
0
                                           __func__);
545
0
  }
546
0
  for (const auto& sample : samples->mSamples) {
547
0
    // Collect telemetry from h264 Annex B SPS.
548
0
    if (mNeedSPSForTelemetry && mIsH264 &&
549
0
        AnnexB::IsAVCC(sample)) {
550
0
      RefPtr<MediaByteBuffer> extradata =
551
0
        H264::ExtractExtraData(sample);
552
0
      if (H264::HasSPS(extradata)) {
553
0
        RefPtr<MediaByteBuffer> extradata =
554
0
          H264::ExtractExtraData(sample);
555
0
        mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata);
556
0
      }
557
0
    }
558
0
  }
559
0
560
0
  if (mNextKeyframeTime.isNothing() ||
561
0
      samples->mSamples.LastElement()->mTime >= mNextKeyframeTime.value()) {
562
0
    SetNextKeyFrameTime();
563
0
  }
564
0
  return SamplesPromise::CreateAndResolve(samples, __func__);
565
0
}
566
567
void
568
MP4TrackDemuxer::SetNextKeyFrameTime()
569
0
{
570
0
  mNextKeyframeTime.reset();
571
0
  Microseconds frameTime = mIterator->GetNextKeyframeTime();
572
0
  if (frameTime != -1) {
573
0
    mNextKeyframeTime.emplace(
574
0
      media::TimeUnit::FromMicroseconds(frameTime));
575
0
  }
576
0
}
577
578
void
579
MP4TrackDemuxer::Reset()
580
0
{
581
0
  mQueuedSample = nullptr;
582
0
  // TODO, Seek to first frame available, which isn't always 0.
583
0
  mIterator->Seek(0);
584
0
  SetNextKeyFrameTime();
585
0
}
586
587
nsresult
588
MP4TrackDemuxer::GetNextRandomAccessPoint(media::TimeUnit* aTime)
589
0
{
590
0
  if (mNextKeyframeTime.isNothing()) {
591
0
    // There's no next key frame.
592
0
    *aTime = media::TimeUnit::FromInfinity();
593
0
  } else {
594
0
    *aTime = mNextKeyframeTime.value();
595
0
  }
596
0
  return NS_OK;
597
0
}
598
599
RefPtr<MP4TrackDemuxer::SkipAccessPointPromise>
600
MP4TrackDemuxer::SkipToNextRandomAccessPoint(
601
  const media::TimeUnit& aTimeThreshold)
602
0
{
603
0
  mQueuedSample = nullptr;
604
0
  // Loop until we reach the next keyframe after the threshold.
605
0
  uint32_t parsed = 0;
606
0
  bool found = false;
607
0
  RefPtr<MediaRawData> sample;
608
0
  while (!found && (sample = GetNextSample())) {
609
0
    parsed++;
610
0
    if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
611
0
      found = true;
612
0
      mQueuedSample = sample;
613
0
    }
614
0
  }
615
0
  SetNextKeyFrameTime();
616
0
  if (found) {
617
0
    return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
618
0
  }
619
0
  SkipFailureHolder failure(NS_ERROR_DOM_MEDIA_END_OF_STREAM, parsed);
620
0
  return SkipAccessPointPromise::CreateAndReject(std::move(failure), __func__);
621
0
}
622
623
media::TimeIntervals
624
MP4TrackDemuxer::GetBuffered()
625
0
{
626
0
  EnsureUpToDateIndex();
627
0
  AutoPinned<MediaResource> resource(mParent->mResource);
628
0
  MediaByteRangeSet byteRanges;
629
0
  nsresult rv = resource->GetCachedRanges(byteRanges);
630
0
631
0
  if (NS_FAILED(rv)) {
632
0
    return media::TimeIntervals();
633
0
  }
634
0
635
0
  return mIndex->ConvertByteRangesToTimeRanges(byteRanges);
636
0
}
637
638
void
639
MP4TrackDemuxer::NotifyDataArrived()
640
0
{
641
0
  mNeedReIndex = true;
642
0
}
643
644
void
645
MP4TrackDemuxer::NotifyDataRemoved()
646
0
{
647
0
  AutoPinned<MediaResource> resource(mParent->mResource);
648
0
  MediaByteRangeSet byteRanges;
649
0
  nsresult rv = resource->GetCachedRanges(byteRanges);
650
0
  if (NS_FAILED(rv)) {
651
0
    return;
652
0
  }
653
0
  mIndex->UpdateMoofIndex(byteRanges, true /* can evict */);
654
0
  mNeedReIndex = false;
655
0
}
656
657
void
658
MP4TrackDemuxer::BreakCycles()
659
0
{
660
0
  mParent = nullptr;
661
0
}
662
663
} // namespace mozilla
664
665
#undef LOG