MP4Demuxer.cpp (24994B)
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 "MP4Demuxer.h" 8 9 #include <stdint.h> 10 11 #include <algorithm> 12 13 #include "AnnexB.h" 14 #include "BufferStream.h" 15 #include "H264.h" 16 #include "H265.h" 17 #include "MP4Decoder.h" 18 #include "MP4Metadata.h" 19 #include "MoofParser.h" 20 #include "ResourceStream.h" 21 #include "SampleIterator.h" 22 #include "TimeUnits.h" 23 #include "VPXDecoder.h" 24 #include "mozilla/Span.h" 25 #include "mozilla/StaticPrefs_media.h" 26 #include "nsPrintfCString.h" 27 28 #define LOG(arg, ...) \ 29 DDMOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, "::%s: " arg, \ 30 __func__, ##__VA_ARGS__) 31 32 namespace mozilla { 33 34 using TimeUnit = media::TimeUnit; 35 using TimeInterval = media::TimeInterval; 36 using TimeIntervals = media::TimeIntervals; 37 38 DDLoggedTypeDeclNameAndBase(MP4TrackDemuxer, MediaTrackDemuxer); 39 40 class MP4TrackDemuxer : public MediaTrackDemuxer, 41 public DecoderDoctorLifeLogger<MP4TrackDemuxer> { 42 public: 43 MP4TrackDemuxer(MediaResource* aResource, UniquePtr<TrackInfo>&& aInfo, 44 const IndiceWrapper& aIndices, uint32_t aTimeScale); 45 46 UniquePtr<TrackInfo> GetInfo() const override; 47 48 RefPtr<SeekPromise> Seek(const TimeUnit& aTime) override; 49 50 RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override; 51 52 void Reset() override; 53 54 nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override; 55 56 RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint( 57 const TimeUnit& aTimeThreshold) override; 58 59 TimeIntervals GetBuffered() override; 60 61 void NotifyDataRemoved(); 62 void NotifyDataArrived(); 63 64 private: 65 Result<already_AddRefed<MediaRawData>, MediaResult> GetNextSample(); 66 void EnsureUpToDateIndex(); 67 void SetNextKeyFrameTime(); 68 RefPtr<MediaResource> mResource; 69 RefPtr<ResourceStream> mStream; 70 UniquePtr<TrackInfo> mInfo; 71 RefPtr<MP4SampleIndex> mIndex; 72 UniquePtr<SampleIterator> mIterator; 73 Maybe<TimeUnit> mNextKeyframeTime; 74 // Queued samples extracted by the demuxer, but not yet returned. 75 RefPtr<MediaRawData> mQueuedSample; 76 bool mNeedReIndex; 77 enum CodecType { kH264, kVP9, kAAC, kHEVC, kOther } mType = kOther; 78 }; 79 80 MP4Demuxer::MP4Demuxer(MediaResource* aResource) 81 : mResource(aResource), 82 mStream(new ResourceStream(aResource)), 83 mIsSeekable(false) { 84 DDLINKCHILD("resource", aResource); 85 DDLINKCHILD("stream", mStream.get()); 86 } 87 88 RefPtr<MP4Demuxer::InitPromise> MP4Demuxer::Init() { 89 AutoPinned<ResourceStream> stream(mStream); 90 91 // 'result' will capture the first warning, if any. 92 MediaResult result{NS_OK}; 93 94 MP4Metadata::ResultAndByteBuffer initData = MP4Metadata::Metadata(stream); 95 if (!initData.Ref()) { 96 return InitPromise::CreateAndReject( 97 NS_FAILED(initData.Result()) 98 ? std::move(initData.Result()) 99 : MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 100 RESULT_DETAIL("Invalid MP4 metadata or OOM")), 101 __func__); 102 } else if (NS_FAILED(initData.Result()) && result == NS_OK) { 103 result = std::move(initData.Result()); 104 } 105 106 RefPtr<BufferStream> bufferstream = new BufferStream(initData.Ref()); 107 108 MP4Metadata metadata{bufferstream}; 109 DDLINKCHILD("metadata", &metadata); 110 nsresult rv = metadata.Parse(); 111 if (NS_FAILED(rv)) { 112 return InitPromise::CreateAndReject( 113 MediaResult(rv, RESULT_DETAIL("Parse MP4 metadata failed")), __func__); 114 } 115 116 auto audioTrackCount = metadata.GetNumberTracks(TrackInfo::kAudioTrack); 117 if (audioTrackCount.Ref() == MP4Metadata::NumberTracksError()) { 118 if (StaticPrefs::media_playback_warnings_as_errors()) { 119 return InitPromise::CreateAndReject( 120 MediaResult( 121 NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 122 RESULT_DETAIL("Invalid audio track (%s)", 123 audioTrackCount.Result().Description().get())), 124 __func__); 125 } 126 audioTrackCount.Ref() = 0; 127 } 128 129 auto videoTrackCount = metadata.GetNumberTracks(TrackInfo::kVideoTrack); 130 if (videoTrackCount.Ref() == MP4Metadata::NumberTracksError()) { 131 if (StaticPrefs::media_playback_warnings_as_errors()) { 132 return InitPromise::CreateAndReject( 133 MediaResult( 134 NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 135 RESULT_DETAIL("Invalid video track (%s)", 136 videoTrackCount.Result().Description().get())), 137 __func__); 138 } 139 videoTrackCount.Ref() = 0; 140 } 141 142 if (audioTrackCount.Ref() == 0 && videoTrackCount.Ref() == 0) { 143 return InitPromise::CreateAndReject( 144 MediaResult( 145 NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 146 RESULT_DETAIL("No MP4 audio (%s) or video (%s) tracks", 147 audioTrackCount.Result().Description().get(), 148 videoTrackCount.Result().Description().get())), 149 __func__); 150 } 151 152 if (NS_FAILED(audioTrackCount.Result()) && result == NS_OK) { 153 result = std::move(audioTrackCount.Result()); 154 } 155 if (NS_FAILED(videoTrackCount.Result()) && result == NS_OK) { 156 result = std::move(videoTrackCount.Result()); 157 } 158 159 if (audioTrackCount.Ref() != 0) { 160 for (size_t i = 0; i < audioTrackCount.Ref(); i++) { 161 MP4Metadata::ResultAndTrackInfo info = 162 metadata.GetTrackInfo(TrackInfo::kAudioTrack, i); 163 if (!info.Ref()) { 164 if (StaticPrefs::media_playback_warnings_as_errors()) { 165 return InitPromise::CreateAndReject( 166 MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 167 RESULT_DETAIL("Invalid MP4 audio track (%s)", 168 info.Result().Description().get())), 169 __func__); 170 } 171 if (result == NS_OK) { 172 result = 173 MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 174 RESULT_DETAIL("Invalid MP4 audio track (%s)", 175 info.Result().Description().get())); 176 } 177 continue; 178 } else if (NS_FAILED(info.Result()) && result == NS_OK) { 179 result = std::move(info.Result()); 180 } 181 MP4Metadata::ResultAndIndice indices = 182 metadata.GetTrackIndice(info.Ref()->mTrackId); 183 if (!indices.Ref()) { 184 if (NS_FAILED(info.Result()) && result == NS_OK) { 185 result = std::move(indices.Result()); 186 } 187 continue; 188 } 189 LOG("Created audio track demuxer for info (%s)", 190 info.Ref()->ToString().get()); 191 RefPtr<MP4TrackDemuxer> demuxer = 192 new MP4TrackDemuxer(mResource, std::move(info.Ref()), 193 *indices.Ref().get(), info.Ref()->mTimeScale); 194 DDLINKCHILD("audio demuxer", demuxer.get()); 195 mAudioDemuxers.AppendElement(std::move(demuxer)); 196 } 197 } 198 199 if (videoTrackCount.Ref() != 0) { 200 for (size_t i = 0; i < videoTrackCount.Ref(); i++) { 201 MP4Metadata::ResultAndTrackInfo info = 202 metadata.GetTrackInfo(TrackInfo::kVideoTrack, i); 203 if (!info.Ref()) { 204 if (StaticPrefs::media_playback_warnings_as_errors()) { 205 return InitPromise::CreateAndReject( 206 MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 207 RESULT_DETAIL("Invalid MP4 video track (%s)", 208 info.Result().Description().get())), 209 __func__); 210 } 211 if (result == NS_OK) { 212 result = 213 MediaResult(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 214 RESULT_DETAIL("Invalid MP4 video track (%s)", 215 info.Result().Description().get())); 216 } 217 continue; 218 } else if (NS_FAILED(info.Result()) && result == NS_OK) { 219 result = std::move(info.Result()); 220 } 221 MP4Metadata::ResultAndIndice indices = 222 metadata.GetTrackIndice(info.Ref()->mTrackId); 223 if (!indices.Ref()) { 224 if (NS_FAILED(info.Result()) && result == NS_OK) { 225 result = std::move(indices.Result()); 226 } 227 continue; 228 } 229 LOG("Created video track demuxer for info (%s)", 230 info.Ref()->ToString().get()); 231 RefPtr<MP4TrackDemuxer> demuxer = 232 new MP4TrackDemuxer(mResource, std::move(info.Ref()), 233 *indices.Ref().get(), info.Ref()->mTimeScale); 234 DDLINKCHILD("video demuxer", demuxer.get()); 235 mVideoDemuxers.AppendElement(std::move(demuxer)); 236 } 237 } 238 239 MP4Metadata::ResultAndCryptoFile cryptoFile = metadata.Crypto(); 240 if (NS_FAILED(cryptoFile.Result()) && result == NS_OK) { 241 result = std::move(cryptoFile.Result()); 242 } 243 MOZ_ASSERT(cryptoFile.Ref()); 244 if (cryptoFile.Ref()->valid) { 245 const nsTArray<PsshInfo>& psshs = cryptoFile.Ref()->pssh; 246 for (uint32_t i = 0; i < psshs.Length(); i++) { 247 mCryptoInitData.AppendElements(psshs[i].data); 248 } 249 } 250 251 mIsSeekable = metadata.CanSeek(); 252 253 return InitPromise::CreateAndResolve(result, __func__); 254 } 255 256 uint32_t MP4Demuxer::GetNumberTracks(TrackInfo::TrackType aType) const { 257 switch (aType) { 258 case TrackInfo::kAudioTrack: 259 return uint32_t(mAudioDemuxers.Length()); 260 case TrackInfo::kVideoTrack: 261 return uint32_t(mVideoDemuxers.Length()); 262 default: 263 return 0; 264 } 265 } 266 267 already_AddRefed<MediaTrackDemuxer> MP4Demuxer::GetTrackDemuxer( 268 TrackInfo::TrackType aType, uint32_t aTrackNumber) { 269 switch (aType) { 270 case TrackInfo::kAudioTrack: 271 if (aTrackNumber >= uint32_t(mAudioDemuxers.Length())) { 272 return nullptr; 273 } 274 return RefPtr<MediaTrackDemuxer>(mAudioDemuxers[aTrackNumber]).forget(); 275 case TrackInfo::kVideoTrack: 276 if (aTrackNumber >= uint32_t(mVideoDemuxers.Length())) { 277 return nullptr; 278 } 279 return RefPtr<MediaTrackDemuxer>(mVideoDemuxers[aTrackNumber]).forget(); 280 default: 281 return nullptr; 282 } 283 } 284 285 bool MP4Demuxer::IsSeekable() const { return mIsSeekable; } 286 287 void MP4Demuxer::NotifyDataArrived() { 288 for (auto& dmx : mAudioDemuxers) { 289 dmx->NotifyDataArrived(); 290 } 291 for (auto& dmx : mVideoDemuxers) { 292 dmx->NotifyDataArrived(); 293 } 294 } 295 296 void MP4Demuxer::NotifyDataRemoved() { 297 for (auto& dmx : mAudioDemuxers) { 298 dmx->NotifyDataRemoved(); 299 } 300 for (auto& dmx : mVideoDemuxers) { 301 dmx->NotifyDataRemoved(); 302 } 303 } 304 305 UniquePtr<EncryptionInfo> MP4Demuxer::GetCrypto() { 306 UniquePtr<EncryptionInfo> crypto; 307 if (!mCryptoInitData.IsEmpty()) { 308 crypto.reset(new EncryptionInfo{}); 309 crypto->AddInitData(u"cenc"_ns, mCryptoInitData); 310 } 311 return crypto; 312 } 313 314 MP4TrackDemuxer::MP4TrackDemuxer(MediaResource* aResource, 315 UniquePtr<TrackInfo>&& aInfo, 316 const IndiceWrapper& aIndices, 317 uint32_t aTimeScale) 318 : mResource(aResource), 319 mStream(new ResourceStream(aResource)), 320 mInfo(std::move(aInfo)), 321 mIndex(new MP4SampleIndex(aIndices, mStream, mInfo->mTrackId, 322 mInfo->IsAudio(), aTimeScale)), 323 mIterator(MakeUnique<SampleIterator>(mIndex)), 324 mNeedReIndex(true) { 325 EnsureUpToDateIndex(); // Force update of index 326 327 VideoInfo* videoInfo = mInfo->GetAsVideoInfo(); 328 AudioInfo* audioInfo = mInfo->GetAsAudioInfo(); 329 if (videoInfo && MP4Decoder::IsH264(mInfo->mMimeType)) { 330 mType = kH264; 331 RefPtr<MediaByteBuffer> extraData = videoInfo->mExtraData; 332 SPSData spsdata; 333 if (H264::DecodeSPSFromExtraData(extraData, spsdata) && 334 spsdata.pic_width > 0 && spsdata.pic_height > 0 && 335 H264::EnsureSPSIsSane(spsdata)) { 336 videoInfo->mImage.width = spsdata.pic_width; 337 videoInfo->mImage.height = spsdata.pic_height; 338 videoInfo->mDisplay.width = spsdata.display_width; 339 videoInfo->mDisplay.height = spsdata.display_height; 340 } 341 } else if (videoInfo && VPXDecoder::IsVP9(mInfo->mMimeType)) { 342 mType = kVP9; 343 } else if (audioInfo && MP4Decoder::IsAAC(mInfo->mMimeType)) { 344 mType = kAAC; 345 } else if (videoInfo && MP4Decoder::IsHEVC(mInfo->mMimeType)) { 346 mType = kHEVC; 347 if (auto rv = H265::DecodeSPSFromHVCCExtraData(videoInfo->mExtraData); 348 rv.isOk()) { 349 const auto sps = rv.unwrap(); 350 videoInfo->mImage.width = sps.GetImageSize().Width(); 351 videoInfo->mImage.height = sps.GetImageSize().Height(); 352 videoInfo->mDisplay.width = sps.GetDisplaySize().Width(); 353 videoInfo->mDisplay.height = sps.GetDisplaySize().Height(); 354 } 355 } 356 } 357 358 UniquePtr<TrackInfo> MP4TrackDemuxer::GetInfo() const { return mInfo->Clone(); } 359 360 void MP4TrackDemuxer::EnsureUpToDateIndex() { 361 if (!mNeedReIndex) { 362 return; 363 } 364 AutoPinned<MediaResource> resource(mResource); 365 MediaByteRangeSet byteRanges; 366 nsresult rv = resource->GetCachedRanges(byteRanges); 367 if (NS_FAILED(rv)) { 368 return; 369 } 370 mIndex->UpdateMoofIndex(byteRanges); 371 mNeedReIndex = false; 372 } 373 374 RefPtr<MP4TrackDemuxer::SeekPromise> MP4TrackDemuxer::Seek( 375 const TimeUnit& aTime) { 376 auto seekTime = aTime; 377 mQueuedSample = nullptr; 378 379 mIterator->Seek(seekTime); 380 381 #ifdef MOZ_APPLEMEDIA 382 bool hasSeenValidSamples = false, seekingFromFirstSyncSample = false; 383 #endif 384 // Check what time we actually seeked to. 385 do { 386 auto next = GetNextSample(); 387 if (next.isErr()) { 388 auto error = next.unwrapErr(); 389 #ifdef MOZ_APPLEMEDIA 390 // On macOS, only IDR frames are marked as key frames. This means some 391 // sync samples (e.g., Recovery SEI or I-slices) are not treated as key 392 // frames, as they may not be supported by Apple’s VideoToolbox H.264 393 // decoder. If we have seen samples but found no key frame, it indicates 394 // we need to look further back for an earlier sample where an IDR frame 395 // should exist per spec. In that case, we retry from the first sync 396 // sample in the GOP, which is typically an IDR frame. This situation 397 // suggests a poorly muxed file that wastes seek time, but unfortunately 398 // such cases do occur in real-world content. 399 if (mType == kH264 && error == NS_ERROR_DOM_MEDIA_END_OF_STREAM && 400 hasSeenValidSamples && !seekingFromFirstSyncSample) { 401 LOG("Can not find a key frame from the closet sync sample, try again " 402 "from the first sync sample"); 403 seekingFromFirstSyncSample = true; 404 mIterator->Seek(seekTime, SampleIterator::SyncSampleMode::First); 405 continue; 406 } 407 #endif 408 return SeekPromise::CreateAndReject(error, __func__); 409 } 410 RefPtr<MediaRawData> sample = next.unwrap(); 411 if (!sample->Size()) { 412 // This sample can't be decoded, continue searching. 413 continue; 414 } 415 if (sample->mKeyframe) { 416 MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime()); 417 mQueuedSample = sample; 418 seekTime = mQueuedSample->mTime; 419 } 420 #ifdef MOZ_APPLEMEDIA 421 hasSeenValidSamples = true; 422 #endif 423 } while (!mQueuedSample); 424 425 SetNextKeyFrameTime(); 426 427 return SeekPromise::CreateAndResolve(seekTime, __func__); 428 } 429 430 Result<already_AddRefed<MediaRawData>, MediaResult> 431 MP4TrackDemuxer::GetNextSample() { 432 auto next = mIterator->GetNext(); 433 if (next.isErr()) { 434 return next; 435 } 436 RefPtr<MediaRawData> sample = next.unwrap(); 437 438 if (mInfo->GetAsVideoInfo()) { 439 sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData; 440 if (mType == kH264 && !sample->mCrypto.IsEncrypted()) { 441 H264::FrameType type = H264::GetFrameType(sample); 442 switch (type) { 443 case H264::FrameType::I_FRAME_IDR: 444 case H264::FrameType::I_FRAME_OTHER: 445 case H264::FrameType::OTHER: { 446 bool keyframe = type == H264::FrameType::I_FRAME_IDR; 447 #ifndef MOZ_APPLEMEDIA 448 // The Apple VideoToolbox H.264 decoder could return a bad data error 449 // if a non-IDR I-frame is provided as the first sample to the decoder 450 // after seeking. Therefore, only IDR frames should be marked as key 451 // frames. 452 keyframe = keyframe || type == H264::FrameType::I_FRAME_OTHER; 453 #endif 454 if (sample->mKeyframe != keyframe) { 455 NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe " 456 "@ pts:%" PRId64 " dur:%" PRId64 457 " dts:%" PRId64, 458 keyframe ? "" : "non-", 459 sample->mTime.ToMicroseconds(), 460 sample->mDuration.ToMicroseconds(), 461 sample->mTimecode.ToMicroseconds()) 462 .get()); 463 sample->mKeyframe = keyframe; 464 } 465 break; 466 } 467 case H264::FrameType::INVALID: 468 NS_WARNING(nsPrintfCString("Invalid H264 frame @ pts:%" PRId64 469 " dur:%" PRId64 " dts:%" PRId64, 470 sample->mTime.ToMicroseconds(), 471 sample->mDuration.ToMicroseconds(), 472 sample->mTimecode.ToMicroseconds()) 473 .get()); 474 // We could reject the sample now, however demuxer errors are fatal. 475 // So we keep the invalid frame, relying on the H264 decoder to 476 // handle the error later. 477 // TODO: make demuxer errors non-fatal. 478 break; 479 } 480 } else if (mType == kVP9 && !sample->mCrypto.IsEncrypted()) { 481 bool keyframe = VPXDecoder::IsKeyframe( 482 Span<const uint8_t>(sample->Data(), sample->Size()), 483 VPXDecoder::Codec::VP9); 484 if (sample->mKeyframe != keyframe) { 485 NS_WARNING(nsPrintfCString( 486 "Frame incorrectly marked as %skeyframe " 487 "@ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64, 488 keyframe ? "" : "non-", sample->mTime.ToMicroseconds(), 489 sample->mDuration.ToMicroseconds(), 490 sample->mTimecode.ToMicroseconds()) 491 .get()); 492 sample->mKeyframe = keyframe; 493 } 494 } 495 } 496 497 // Adjust trimming information if needed. 498 if (mInfo->GetAsAudioInfo()) { 499 AudioInfo* info = mInfo->GetAsAudioInfo(); 500 TimeUnit originalPts = sample->mTime; 501 TimeUnit originalEnd = sample->GetEndTime(); 502 if (sample->mTime.IsNegative()) { 503 sample->mTime = TimeUnit::Zero(originalPts); 504 sample->mDuration = std::max(TimeUnit::Zero(sample->mTime), 505 originalPts + sample->mDuration); 506 sample->mOriginalPresentationWindow = 507 Some(TimeInterval{originalPts, originalEnd}); 508 } 509 // The demuxer only knows the presentation time of the packet, not the 510 // actual number of samples that will be decoded from this packet. 511 // However we need to trim the last packet to the correct duration. 512 // Find the actual size of the decoded packet to know how many samples to 513 // trim. This only works because the packet size are constant. 514 TimeUnit totalMediaDurationIncludingTrimming = 515 info->mDuration - info->mMediaTime; 516 if (mType == kAAC && 517 sample->GetEndTime() >= totalMediaDurationIncludingTrimming && 518 totalMediaDurationIncludingTrimming.IsPositive()) { 519 // Seek backward a bit. 520 mIterator->Seek(sample->mTime - sample->mDuration); 521 RefPtr<MediaRawData> previousSample = 522 mIterator->GetNext().unwrapOr(nullptr); 523 if (previousSample) { 524 TimeInterval fullPacketDuration{previousSample->mTime, 525 previousSample->GetEndTime()}; 526 sample->mOriginalPresentationWindow = Some(TimeInterval{ 527 originalPts, originalPts + fullPacketDuration.Length()}); 528 } 529 // Seek back so we're back at the original location -- there's no packet 530 // left anyway. 531 mIterator->Seek(sample->mTime); 532 RefPtr<MediaRawData> dummy = mIterator->GetNext().unwrapOr(nullptr); 533 } 534 } 535 536 if (MOZ_LOG_TEST(gMediaDemuxerLog, LogLevel::Verbose)) { 537 bool isAudio = mInfo->GetAsAudioInfo(); 538 TimeUnit originalStart = TimeUnit::Invalid(); 539 TimeUnit originalEnd = TimeUnit::Invalid(); 540 if (sample->mOriginalPresentationWindow) { 541 originalStart = sample->mOriginalPresentationWindow->mStart; 542 originalEnd = sample->mOriginalPresentationWindow->mEnd; 543 } 544 LOG("%s packet demuxed (track id: %d): [%s,%s], duration: %s (original " 545 "time: [%s,%s])", 546 isAudio ? "Audio" : "Video", mInfo->mTrackId, 547 sample->mTime.ToString().get(), sample->GetEndTime().ToString().get(), 548 sample->mDuration.ToString().get(), originalStart.ToString().get(), 549 originalEnd.ToString().get()); 550 } 551 552 return sample.forget(); 553 } 554 555 RefPtr<MP4TrackDemuxer::SamplesPromise> MP4TrackDemuxer::GetSamples( 556 int32_t aNumSamples) { 557 EnsureUpToDateIndex(); 558 RefPtr<SamplesHolder> samples = new SamplesHolder; 559 if (!aNumSamples) { 560 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 561 __func__); 562 } 563 564 if (mQueuedSample) { 565 NS_ASSERTION(mQueuedSample->mKeyframe, "mQueuedSample must be a keyframe"); 566 samples->AppendSample(std::move(mQueuedSample)); 567 MOZ_ASSERT(!mQueuedSample); 568 aNumSamples--; 569 } 570 while (aNumSamples) { 571 auto next = GetNextSample(); 572 if (next.isErr()) { 573 nsresult rv = next.inspectErr().Code(); 574 if ((rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM && 575 rv != NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) || 576 samples->GetSamples().IsEmpty()) { 577 return SamplesPromise::CreateAndReject(next.unwrapErr(), __func__); 578 } 579 break; 580 } 581 RefPtr<MediaRawData> sample = next.unwrap(); 582 if (!sample->Size()) { 583 continue; 584 } 585 MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime()); 586 samples->AppendSample(std::move(sample)); 587 aNumSamples--; 588 } 589 590 if (mNextKeyframeTime.isNothing() || 591 samples->GetSamples().LastElement()->mTime >= mNextKeyframeTime.value()) { 592 SetNextKeyFrameTime(); 593 } 594 return SamplesPromise::CreateAndResolve(samples, __func__); 595 } 596 597 void MP4TrackDemuxer::SetNextKeyFrameTime() { 598 mNextKeyframeTime.reset(); 599 TimeUnit frameTime = mIterator->GetNextKeyframeTime(); 600 if (frameTime.IsValid()) { 601 mNextKeyframeTime.emplace(frameTime); 602 } 603 } 604 605 void MP4TrackDemuxer::Reset() { 606 mQueuedSample = nullptr; 607 // TODO: verify this 608 mIterator->Seek(TimeUnit::FromNegativeInfinity()); 609 SetNextKeyFrameTime(); 610 } 611 612 nsresult MP4TrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime) { 613 if (mNextKeyframeTime.isNothing()) { 614 // There's no next key frame. 615 *aTime = TimeUnit::FromInfinity(); 616 } else { 617 *aTime = mNextKeyframeTime.value(); 618 } 619 return NS_OK; 620 } 621 622 RefPtr<MP4TrackDemuxer::SkipAccessPointPromise> 623 MP4TrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) { 624 mQueuedSample = nullptr; 625 // Loop until we reach the next keyframe after the threshold. 626 uint32_t parsed = 0; 627 Maybe<SkipFailureHolder> failure; 628 while (true) { 629 auto next = GetNextSample(); 630 if (next.isErr()) { 631 failure.emplace(next.unwrapErr(), parsed); 632 break; 633 } 634 RefPtr<MediaRawData> sample = next.unwrap(); 635 parsed++; 636 MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime()); 637 if (sample->mKeyframe && sample->mTime >= aTimeThreshold) { 638 mQueuedSample = sample; 639 break; 640 } 641 } 642 SetNextKeyFrameTime(); 643 if (failure.isSome()) { 644 return SkipAccessPointPromise::CreateAndReject(failure.extract(), __func__); 645 } 646 return SkipAccessPointPromise::CreateAndResolve(parsed, __func__); 647 } 648 649 TimeIntervals MP4TrackDemuxer::GetBuffered() { 650 EnsureUpToDateIndex(); 651 AutoPinned<MediaResource> resource(mResource); 652 MediaByteRangeSet byteRanges; 653 nsresult rv = resource->GetCachedRanges(byteRanges); 654 655 if (NS_FAILED(rv)) { 656 return TimeIntervals(); 657 } 658 659 TimeIntervals timeRanges = mIndex->ConvertByteRangesToTimeRanges(byteRanges); 660 if (AudioInfo* info = mInfo->GetAsAudioInfo(); info) { 661 // Trim as in GetNextSample(). 662 TimeUnit totalMediaDurationIncludingTrimming = 663 info->mDuration - info->mMediaTime; 664 auto end = TimeUnit::FromInfinity(); 665 if (mType == kAAC && totalMediaDurationIncludingTrimming.IsPositive()) { 666 end = info->mDuration; 667 } 668 if (timeRanges.GetStart().IsNegative() || timeRanges.GetEnd() > end) { 669 TimeInterval trimming(TimeUnit::Zero(timeRanges.GetStart()), end); 670 timeRanges = timeRanges.Intersection(trimming); 671 } 672 } 673 674 return timeRanges; 675 } 676 677 void MP4TrackDemuxer::NotifyDataArrived() { mNeedReIndex = true; } 678 679 void MP4TrackDemuxer::NotifyDataRemoved() { 680 AutoPinned<MediaResource> resource(mResource); 681 MediaByteRangeSet byteRanges; 682 nsresult rv = resource->GetCachedRanges(byteRanges); 683 if (NS_FAILED(rv)) { 684 return; 685 } 686 mIndex->UpdateMoofIndex(byteRanges, true /* can evict */); 687 mNeedReIndex = false; 688 } 689 690 } // namespace mozilla 691 692 #undef LOG