size_t MediaSourceReader::SizeOfAudioQueueInFrames() { if (!GetAudioReader()) { MSE_DEBUG("called with no audio reader"); return 0; } return GetAudioReader()->SizeOfAudioQueueInFrames(); }
void MediaSourceReader::DoAudioRequest() { mAudioRequest.Begin(GetAudioReader()->RequestAudioData() ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::OnAudioDecoded, &MediaSourceReader::OnAudioNotDecoded)); }
void MediaSourceReader::DoAudioRequest() { mAudioRequest.Begin(GetAudioReader()->RequestAudioData() ->Then(OwnerThread(), __func__, this, &MediaSourceReader::OnAudioDecoded, &MediaSourceReader::OnAudioNotDecoded)); }
void MediaSourceReader::GetMozDebugReaderData(nsAString& aString) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); nsAutoCString result; result += nsPrintfCString("Dumping data for reader %p:\n", this); if (mAudioTrack) { result += nsPrintfCString("\tDumping Audio Track Decoders: - mLastAudioTime: %f\n", double(mLastAudioTime) / USECS_PER_S); for (int32_t i = mAudioTrack->Decoders().Length() - 1; i >= 0; --i) { nsRefPtr<MediaDecoderReader> newReader = mAudioTrack->Decoders()[i]->GetReader(); nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); mAudioTrack->Decoders()[i]->GetBuffered(ranges); result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n", i, newReader.get(), DumpTimeRanges(ranges).get(), newReader.get() == GetAudioReader() ? "true" : "false", mAudioTrack->Decoders()[i]->GetResource()->GetSize()); } } if (mVideoTrack) { result += nsPrintfCString("\tDumping Video Track Decoders - mLastVideoTime: %f\n", double(mLastVideoTime) / USECS_PER_S); for (int32_t i = mVideoTrack->Decoders().Length() - 1; i >= 0; --i) { nsRefPtr<MediaDecoderReader> newReader = mVideoTrack->Decoders()[i]->GetReader(); nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); mVideoTrack->Decoders()[i]->GetBuffered(ranges); result += nsPrintfCString("\t\tReader %d: %p ranges=%s active=%s size=%lld\n", i, newReader.get(), DumpTimeRanges(ranges).get(), newReader.get() == GetVideoReader() ? "true" : "false", mVideoTrack->Decoders()[i]->GetResource()->GetSize()); } } aString += NS_ConvertUTF8toUTF16(result); }
void MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason) { MOZ_DIAGNOSTIC_ASSERT(!IsSeeking()); mAudioRequest.Complete(); MSE_DEBUG("aReason=%u IsEnded: %d", aReason, IsEnded()); if (aReason == CANCELED) { mAudioPromise.Reject(CANCELED, __func__); return; } // If End of stream. Force switching past this stream to another reader by // switching to the end of the buffered range. int64_t lastAudioTime = mLastAudioTime; if (aReason == END_OF_STREAM && mAudioSourceDecoder) { AdjustEndTime(&mLastAudioTime, mAudioSourceDecoder); } SwitchSourceResult result = SwitchAudioSource(&mLastAudioTime); // See if we can find a different source that can pick up where we left off. if (result == SOURCE_NEW) { GetAudioReader()->ResetDecode(); mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::CompleteAudioSeekAndDoRequest, &MediaSourceReader::CompleteAudioSeekAndRejectPromise)); return; } // If we got a DECODE_ERROR and we have buffered data in the requested range // then it must be a genuine decoding error. // Otherwise we can assume that the data was either evicted or explicitely // removed from the source buffer and we should wait for new data. if (aReason == DECODE_ERROR && result != SOURCE_NONE) { mAudioPromise.Reject(DECODE_ERROR, __func__); return; } CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime); if (mLastAudioTime - lastAudioTime >= EOS_FUZZ_US) { // No decoders are available to switch to. We will re-attempt from the last // failing position. mLastAudioTime = lastAudioTime; } }
nsRefPtr<MediaDecoderReader::AudioDataPromise> MediaSourceReader::RequestAudioData() { MOZ_ASSERT(OnTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking"); MOZ_DIAGNOSTIC_ASSERT(mAudioPromise.IsEmpty(), "No duplicate sample requests"); nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__); MSE_DEBUGV("mLastAudioTime=%lld", mLastAudioTime); if (!mAudioTrack) { MSE_DEBUG("called with no audio track"); mAudioPromise.Reject(DECODE_ERROR, __func__); return p; } if (IsSeeking()) { MSE_DEBUG("called mid-seek. Rejecting."); mAudioPromise.Reject(CANCELED, __func__); return p; } MOZ_DIAGNOSTIC_ASSERT(!mAudioSeekRequest.Exists()); SwitchSourceResult ret = SwitchAudioSource(&mLastAudioTime); switch (ret) { case SOURCE_NEW: GetAudioReader()->ResetDecode(); mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mLastAudioTime), 0) ->Then(OwnerThread(), __func__, this, &MediaSourceReader::CompleteAudioSeekAndDoRequest, &MediaSourceReader::CompleteAudioSeekAndRejectPromise)); break; case SOURCE_NONE: if (!mLastAudioTime) { // This is the first call to RequestAudioData. // Fallback to using decoder with earliest data. mAudioSourceDecoder = FirstDecoder(MediaData::AUDIO_DATA); } if (mLastAudioTime || !mAudioSourceDecoder) { CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime); break; } // Fallback to getting first frame from first decoder. default: DoAudioRequest(); break; } return p; }
MediaSourceReader::SwitchSourceResult MediaSourceReader::SwitchAudioSource(int64_t* aTarget) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); // XXX: Can't handle adding an audio track after ReadMetadata. if (!mAudioTrack) { return SOURCE_NONE; } // We first search without the tolerance and then search with it, so that, in // the case of perfectly-aligned data, we don't prematurely jump to a new // reader and skip the last few samples of the current one. bool usedFuzz = false; nsRefPtr<SourceBufferDecoder> newDecoder = SelectDecoder(*aTarget, /* aTolerance = */ 0, mAudioTrack->Decoders()); if (!newDecoder) { newDecoder = SelectDecoder(*aTarget, EOS_FUZZ_US, mAudioTrack->Decoders()); usedFuzz = true; } if (GetAudioReader() && mAudioSourceDecoder != newDecoder) { GetAudioReader()->SetIdle(); } if (!newDecoder) { mAudioSourceDecoder = nullptr; return SOURCE_NONE; } if (newDecoder == mAudioSourceDecoder) { return SOURCE_EXISTING; } mAudioSourceDecoder = newDecoder; if (usedFuzz) { // A decoder buffered range is continuous. We would have failed the exact // search but succeeded the fuzzy one if our target was shortly before // start time. nsRefPtr<dom::TimeRanges> ranges = new dom::TimeRanges(); newDecoder->GetBuffered(ranges); int64_t startTime = ranges->GetStartTime() * USECS_PER_S; if (*aTarget < startTime) { *aTarget = startTime; } } MSE_DEBUGV("switched decoder to %p (fuzz:%d)", mAudioSourceDecoder.get(), usedFuzz); return SOURCE_NEW; }
void MediaSourceReader::DoAudioSeek() { int64_t seekTime = mPendingSeekTime; if (mSeekToEnd) { seekTime = LastSampleTime(MediaData::AUDIO_DATA); } if (SwitchAudioSource(&seekTime) == SOURCE_NONE) { // Data we need got evicted since the last time we checked for data // availability. Abort current seek attempt. mWaitingForSeekData = true; return; } GetAudioReader()->ResetDecode(); mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(seekTime), 0) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::OnAudioSeekCompleted, &MediaSourceReader::OnAudioSeekFailed)); MSE_DEBUG("reader=%p", GetAudioReader()); }
nsresult MediaSourceReader::ResetDecode() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MSE_DEBUG(""); // Any previous requests we've been waiting on are now unwanted. mAudioRequest.DisconnectIfExists(); mVideoRequest.DisconnectIfExists(); mAudioSeekRequest.DisconnectIfExists(); mVideoSeekRequest.DisconnectIfExists(); // Additionally, reject any outstanding promises _we_ made that we might have // been waiting on the above to fulfill. mAudioPromise.RejectIfExists(CANCELED, __func__); mVideoPromise.RejectIfExists(CANCELED, __func__); mSeekPromise.RejectIfExists(NS_OK, __func__); // Do the same for any data wait promises. mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__); mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::CANCELED), __func__); // Reset miscellaneous seeking state. mWaitingForSeekData = false; mPendingSeekTime = -1; // Reset force video decode ahead. mForceVideoDecodeAhead = false; // Reset all the readers. if (GetAudioReader()) { GetAudioReader()->ResetDecode(); } if (GetVideoReader()) { GetVideoReader()->ResetDecode(); } return MediaDecoderReader::ResetDecode(); }
nsresult MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MSE_DEBUG("tracks=%u/%u audio=%p video=%p", mEssentialTrackBuffers.Length(), mTrackBuffers.Length(), mAudioTrack.get(), mVideoTrack.get()); mEssentialTrackBuffers.Clear(); if (!mAudioTrack && !mVideoTrack) { MSE_DEBUG("missing track: mAudioTrack=%p mVideoTrack=%p", mAudioTrack.get(), mVideoTrack.get()); return NS_ERROR_FAILURE; } if (mAudioTrack == mVideoTrack) { NS_WARNING("Combined audio/video sourcebuffer, this is an unsupported " "configuration, only using video track"); mAudioTrack = nullptr; } if (mAudioTrack) { MOZ_ASSERT(mAudioTrack->IsReady()); mAudioSourceDecoder = mAudioTrack->Decoders()[0]; const MediaInfo& info = GetAudioReader()->GetMediaInfo(); MOZ_ASSERT(info.HasAudio()); mInfo.mAudio = info.mAudio; mInfo.mCrypto.AddInitData(info.mCrypto); MSE_DEBUG("audio reader=%p duration=%lld", mAudioSourceDecoder.get(), mInfo.mMetadataDuration.isSome() ? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1); } if (mVideoTrack) { MOZ_ASSERT(mVideoTrack->IsReady()); mVideoSourceDecoder = mVideoTrack->Decoders()[0]; const MediaInfo& info = GetVideoReader()->GetMediaInfo(); MOZ_ASSERT(info.HasVideo()); mInfo.mVideo = info.mVideo; mInfo.mCrypto.AddInitData(info.mCrypto); MSE_DEBUG("video reader=%p duration=%lld", GetVideoReader(), mInfo.mMetadataDuration.isSome() ? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1); } *aInfo = mInfo; *aTags = nullptr; // TODO: Handle metadata. return NS_OK; }
nsresult MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MSE_DEBUG("tracks=%u/%u audio=%p video=%p", mEssentialTrackBuffers.Length(), mTrackBuffers.Length(), mAudioTrack.get(), mVideoTrack.get()); mEssentialTrackBuffers.Clear(); if (!mAudioTrack && !mVideoTrack) { MSE_DEBUG("missing track: mAudioTrack=%p mVideoTrack=%p", mAudioTrack.get(), mVideoTrack.get()); return NS_ERROR_FAILURE; } if (mAudioTrack) { MOZ_ASSERT(mAudioTrack->IsReady()); mAudioSourceDecoder = mAudioTrack->Decoders()[0]; const MediaInfo& info = GetAudioReader()->GetMediaInfo(); MOZ_ASSERT(info.HasAudio()); mInfo.mAudio = info.mAudio; mInfo.mCrypto.AddInitData(info.mCrypto); MSE_DEBUG("audio reader=%p duration=%lld", mAudioSourceDecoder.get(), mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration()); } if (mVideoTrack) { MOZ_ASSERT(mVideoTrack->IsReady()); mVideoSourceDecoder = mVideoTrack->Decoders()[0]; const MediaInfo& info = GetVideoReader()->GetMediaInfo(); MOZ_ASSERT(info.HasVideo()); mInfo.mVideo = info.mVideo; mInfo.mCrypto.AddInitData(info.mCrypto); MSE_DEBUG("video reader=%p duration=%lld", GetVideoReader(), GetVideoReader()->GetDecoder()->GetMediaDuration()); } *aInfo = mInfo; *aTags = nullptr; // TODO: Handle metadata. return NS_OK; }
void MediaSourceReader::OnAudioDecoded(AudioData* aSample) { MOZ_DIAGNOSTIC_ASSERT(!IsSeeking()); mAudioRequest.Complete(); int64_t ourTime = aSample->mTime + mAudioSourceDecoder->GetTimestampOffset(); if (aSample->mDiscontinuity) { mAudioDiscontinuity = true; } MSE_DEBUGV("[mTime=%lld mDuration=%lld mDiscontinuity=%d]", ourTime, aSample->mDuration, aSample->mDiscontinuity); if (mDropAudioBeforeThreshold) { if (ourTime < mTimeThreshold) { MSE_DEBUG("mTime=%lld < mTimeThreshold=%lld", ourTime, mTimeThreshold); mAudioRequest.Begin(GetAudioReader()->RequestAudioData() ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::OnAudioDecoded, &MediaSourceReader::OnAudioNotDecoded)); return; } mDropAudioBeforeThreshold = false; } // Adjust the sample time into our reference. nsRefPtr<AudioData> newSample = AudioData::TransferAndUpdateTimestampAndDuration(aSample, ourTime, aSample->mDuration); mLastAudioTime = newSample->GetEndTime(); if (mAudioDiscontinuity) { newSample->mDiscontinuity = true; mAudioDiscontinuity = false; } mAudioPromise.Resolve(newSample, __func__); }
void MediaSourceReader::ReadUpdatedMetadata(MediaInfo* aInfo) { if (mAudioTrack) { MOZ_ASSERT(mAudioTrack->IsReady()); mAudioSourceDecoder = mAudioTrack->Decoders()[0]; const MediaInfo& info = GetAudioReader()->GetMediaInfo(); MOZ_ASSERT(info.HasAudio()); mInfo.mAudio = info.mAudio; } if (mVideoTrack) { MOZ_ASSERT(mVideoTrack->IsReady()); mVideoSourceDecoder = mVideoTrack->Decoders()[0]; const MediaInfo& info = GetVideoReader()->GetMediaInfo(); MOZ_ASSERT(info.HasVideo()); mInfo.mVideo = info.mVideo; } *aInfo = mInfo; }
bool MediaSourceReader::IsActiveReader(MediaDecoderReader* aReader) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); return aReader && (aReader == GetVideoReader() || aReader == GetAudioReader()); }