nsRefPtr<MP4TrackDemuxer::SamplesPromise> MP4TrackDemuxer::GetSamples(int32_t aNumSamples) { EnsureUpToDateIndex(); nsRefPtr<SamplesHolder> samples = new SamplesHolder; if (!aNumSamples) { return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__); } if (mQueuedSample) { samples->mSamples.AppendElement(mQueuedSample); mQueuedSample = nullptr; aNumSamples--; } MonitorAutoLock mon(mMonitor); nsRefPtr<MediaRawData> sample; while (aNumSamples && (sample = mIterator->GetNext())) { samples->mSamples.AppendElement(sample); aNumSamples--; } if (samples->mSamples.IsEmpty()) { return SamplesPromise::CreateAndReject(DemuxerFailureReason::END_OF_STREAM, __func__); } else { UpdateSamples(samples->mSamples); return SamplesPromise::CreateAndResolve(samples, __func__); } }
already_AddRefed<MediaRawData> MP4TrackDemuxer::GetNextSample() { RefPtr<MediaRawData> sample = mIterator->GetNext(); if (!sample) { return nullptr; } if (mInfo->GetAsVideoInfo()) { sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData; if (mIsH264) { mp4_demuxer::H264::FrameType type = mp4_demuxer::H264::GetFrameType(sample); switch (type) { case mp4_demuxer::H264::FrameType::I_FRAME: MOZ_FALLTHROUGH; case mp4_demuxer::H264::FrameType::OTHER: { bool keyframe = type == mp4_demuxer::H264::FrameType::I_FRAME; if (sample->mKeyframe != keyframe) { NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe " "@ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64, keyframe ? "" : "non-", sample->mTime.ToMicroseconds(), sample->mDuration.ToMicroseconds(), sample->mTimecode.ToMicroseconds()) .get()); sample->mKeyframe = keyframe; } break; } case mp4_demuxer::H264::FrameType::INVALID: NS_WARNING( nsPrintfCString("Invalid H264 frame @ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64, sample->mTime.ToMicroseconds(), sample->mDuration.ToMicroseconds(), sample->mTimecode.ToMicroseconds()) .get()); // We could reject the sample now, however demuxer errors are fatal. // So we keep the invalid frame, relying on the H264 decoder to // handle the error later. // TODO: make demuxer errors non-fatal. break; } } } if (sample->mCrypto.mValid) { nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); writer->mCrypto.mMode = mInfo->mCrypto.mMode; // Only use the default key parsed from the moov if we haven't already got // one from the sample group description. if (writer->mCrypto.mKeyId.Length() == 0) { writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize; writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId); } } return sample.forget(); }
RefPtr<MP4TrackDemuxer::SeekPromise> MP4TrackDemuxer::Seek(media::TimeUnit aTime) { int64_t seekTime = aTime.ToMicroseconds(); mQueuedSample = nullptr; mIterator->Seek(seekTime); // Check what time we actually seeked to. mQueuedSample = mIterator->GetNext(); if (mQueuedSample) { seekTime = mQueuedSample->mTime; } SetNextKeyFrameTime(); return SeekPromise::CreateAndResolve(media::TimeUnit::FromMicroseconds(seekTime), __func__); }
RefPtr<MP4TrackDemuxer::SkipAccessPointPromise> MP4TrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold) { mQueuedSample = nullptr; // Loop until we reach the next keyframe after the threshold. uint32_t parsed = 0; bool found = false; RefPtr<MediaRawData> sample; while (!found && (sample = mIterator->GetNext())) { parsed++; if (sample->mKeyframe && sample->mTime >= aTimeThreshold.ToMicroseconds()) { found = true; mQueuedSample = sample; } } SetNextKeyFrameTime(); if (found) { return SkipAccessPointPromise::CreateAndResolve(parsed, __func__); } else { SkipFailureHolder failure(DemuxerFailureReason::END_OF_STREAM, parsed); return SkipAccessPointPromise::CreateAndReject(Move(failure), __func__); } }