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