already_AddRefed<SourceBufferDecoder> MediaSourceReader::CreateSubDecoder(const nsACString& aType) { if (IsShutdown()) { return nullptr; } MOZ_ASSERT(GetTaskQueue()); nsRefPtr<SourceBufferDecoder> decoder = new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder); nsRefPtr<MediaDecoderReader> reader(CreateReaderForType(aType, decoder)); if (!reader) { return nullptr; } // Set a callback on the subreader that forwards calls to this reader. // This reader will then forward them onto the state machine via this // reader's callback. RefPtr<MediaDataDecodedListener<MediaSourceReader>> callback = new MediaDataDecodedListener<MediaSourceReader>(this, GetTaskQueue()); reader->SetCallback(callback); reader->SetTaskQueue(GetTaskQueue()); reader->Init(nullptr); MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p", this, decoder.get(), reader.get()); decoder->SetReader(reader); return decoder.forget(); }
void MediaSourceReader::AttemptSeek() { // Make sure we don't hold the monitor while calling into the reader // Seek methods since it can deadlock. { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (!mWaitingForSeekData || !TrackBuffersContainTime(mPendingSeekTime)) { return; } } ResetDecode(); for (uint32_t i = 0; i < mTrackBuffers.Length(); ++i) { mTrackBuffers[i]->ResetDecode(); } // Decoding discontinuity upon seek, reset last times to seek target. mLastAudioTime = mPendingSeekTime; mLastVideoTime = mPendingSeekTime; if (mAudioTrack) { mAudioIsSeeking = true; SwitchAudioReader(mPendingSeekTime); mAudioReader->Seek(mPendingSeekTime, mPendingStartTime, mPendingEndTime, mPendingCurrentTime) ->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::OnSeekCompleted, &MediaSourceReader::OnSeekFailed); MSE_DEBUG("MediaSourceReader(%p)::Seek audio reader=%p", this, mAudioReader.get()); } if (mVideoTrack) { mVideoIsSeeking = true; SwitchVideoReader(mPendingSeekTime); mVideoReader->Seek(mPendingSeekTime, mPendingStartTime, mPendingEndTime, mPendingCurrentTime) ->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::OnSeekCompleted, &MediaSourceReader::OnSeekFailed); MSE_DEBUG("MediaSourceReader(%p)::Seek video reader=%p", this, mVideoReader.get()); } { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mWaitingForSeekData = false; } }
void MediaSourceReader::OnVideoDecoded(VideoData* aSample) { MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]", this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity); if (mDropVideoBeforeThreshold) { if (aSample->mTime < mTimeThreshold) { MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld", this, aSample->mTime, mTimeThreshold); mVideoReader->RequestVideoData(false, 0)->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded); return; } mDropVideoBeforeThreshold = false; } // Any OnVideoDecoded callbacks received while mVideoIsSeeking must be not // update our last used timestamp, as these are emitted by the reader we're // switching away from. if (!mVideoIsSeeking) { mLastVideoTime = aSample->mTime + aSample->mDuration; } mVideoPromise.Resolve(aSample, __func__); }
nsRefPtr<MediaDecoderReader::SeekPromise> MP4Reader::Seek(int64_t aTime, int64_t aEndTime) { LOG("aTime=(%lld)", aTime); MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); MonitorAutoLock mon(mDemuxerMonitor); if (!mDecoder->GetResource()->IsTransportSeekable() || !mDemuxer->CanSeek()) { VLOG("Seek() END (Unseekable)"); return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } int64_t seekTime = aTime; mQueuedVideoSample = nullptr; if (mDemuxer->HasValidVideo()) { mVideo.mTrackDemuxer->Seek(seekTime); mQueuedVideoSample = PopSampleLocked(kVideo); if (mQueuedVideoSample) { seekTime = mQueuedVideoSample->mMp4Sample->composition_timestamp; } } if (mDemuxer->HasValidAudio()) { mAudio.mTrackDemuxer->Seek(seekTime); } LOG("aTime=%lld exit", aTime); return SeekPromise::CreateAndResolve(seekTime, __func__); }
bool MP4Reader::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed) { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); MOZ_ASSERT(mVideo.mDecoder); Flush(TrackInfo::kVideoTrack); // Loop until we reach the next keyframe after the threshold. while (true) { nsRefPtr<MediaRawData> compressed(PopSample(TrackInfo::kVideoTrack)); if (!compressed) { // EOS, or error. This code assumes EOS, which may or may not be right. MonitorAutoLock mon(mVideo.mMonitor); mVideo.mDemuxEOS = true; return false; } parsed++; if (!compressed->mKeyframe || compressed->mTime < aTimeThreshold) { continue; } mQueuedVideoSample = compressed; break; } return true; }
void MediaSourceReader::DoAudioRequest() { mAudioRequest.Begin(GetAudioReader()->RequestAudioData() ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::OnAudioDecoded, &MediaSourceReader::OnAudioNotDecoded)); }
bool MP4Reader::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed) { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); MOZ_ASSERT(mVideo.mDecoder); Flush(kVideo); // Loop until we reach the next keyframe after the threshold. while (true) { nsAutoPtr<MediaSample> compressed(PopSample(kVideo)); if (!compressed) { // EOS, or error. This code assumes EOS, which may or may not be right. MonitorAutoLock mon(mVideo.mMonitor); mVideo.mDemuxEOS = true; return false; } parsed++; if (!compressed->mMp4Sample->is_sync_point || compressed->mMp4Sample->composition_timestamp < aTimeThreshold) { continue; } mQueuedVideoSample = compressed; break; } return true; }
void MediaSourceReader::ContinueShutdown() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (mTrackBuffers.Length()) { mTrackBuffers[0]->Shutdown()->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::ContinueShutdown, &MediaSourceReader::ContinueShutdown); mShutdownTrackBuffers.AppendElement(mTrackBuffers[0]); mTrackBuffers.RemoveElementAt(0); return; } mAudioTrack = nullptr; mAudioSourceDecoder = nullptr; mVideoTrack = nullptr; mVideoSourceDecoder = nullptr; #ifdef MOZ_FMP4 if (mSharedDecoderManager) { mSharedDecoderManager->Shutdown(); mSharedDecoderManager = nullptr; } #endif MOZ_ASSERT(mAudioPromise.IsEmpty()); MOZ_ASSERT(mVideoPromise.IsEmpty()); mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__); mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__); MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__); }
void MediaSourceReader::DoVideoRequest() { mVideoRequest.Begin(GetVideoReader()->RequestVideoData(mDropVideoBeforeThreshold, GetReaderVideoTime(mTimeThreshold)) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded)); }
already_AddRefed<SourceBufferDecoder> MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset) { if (IsShutdown()) { return nullptr; } MOZ_ASSERT(GetTaskQueue()); nsRefPtr<SourceBufferDecoder> decoder = new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder, aTimestampOffset); nsRefPtr<MediaDecoderReader> reader(CreateReaderForType(aType, decoder)); if (!reader) { return nullptr; } // MSE uses a start time of 0 everywhere. Set that immediately on the // subreader to make sure that it's always in a state where we can invoke // GetBuffered on it. { ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor()); reader->SetStartTime(0); } // This part is icky. It would be nicer to just give each subreader its own // task queue. Unfortunately though, Request{Audio,Video}Data implementations // currently assert that they're on "the decode thread", and so having // separate task queues makes MediaSource stuff unnecessarily cumbersome. We // should remove the need for these assertions (which probably involves making // all Request*Data implementations fully async), and then get rid of the // borrowing. reader->SetBorrowedTaskQueue(GetTaskQueue()); #ifdef MOZ_FMP4 reader->SetSharedDecoderManager(mSharedDecoderManager); #endif reader->Init(nullptr); MSE_DEBUG("subdecoder %p subreader %p", decoder.get(), reader.get()); decoder->SetReader(reader); #ifdef MOZ_EME decoder->SetCDMProxy(mCDMProxy); #endif return decoder.forget(); }
void MediaSourceReader::NotifyTimeRangesChanged() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (mWaitingForSeekData) { //post a task to the state machine thread to call seek. RefPtr<nsIRunnable> task(NS_NewRunnableMethod( this, &MediaSourceReader::AttemptSeek)); GetTaskQueue()->Dispatch(task.forget()); } }
void MediaSourceReader::Ended(bool aEnded) { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); mEnded = aEnded; if (aEnded) { // post a task to the decode queue to try to complete any pending // seek or wait RefPtr<nsIRunnable> task(NS_NewRunnableMethod( this, &MediaSourceReader::NotifyTimeRangesChanged)); GetTaskQueue()->Dispatch(task.forget()); } }
already_AddRefed<SourceBufferDecoder> MediaSourceReader::CreateSubDecoder(const nsACString& aType) { if (IsShutdown()) { return nullptr; } MOZ_ASSERT(GetTaskQueue()); nsRefPtr<SourceBufferDecoder> decoder = new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder); nsRefPtr<MediaDecoderReader> reader(CreateReaderForType(aType, decoder)); if (!reader) { return nullptr; } // MSE uses a start time of 0 everywhere. Set that immediately on the // subreader to make sure that it's always in a state where we can invoke // GetBuffered on it. { ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor()); reader->SetStartTime(0); } // Set a callback on the subreader that forwards calls to this reader. // This reader will then forward them onto the state machine via this // reader's callback. RefPtr<MediaDataDecodedListener<MediaSourceReader>> callback = new MediaDataDecodedListener<MediaSourceReader>(this, GetTaskQueue()); reader->SetCallback(callback); reader->SetTaskQueue(GetTaskQueue()); reader->Init(nullptr); MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p", this, decoder.get(), reader.get()); decoder->SetReader(reader); #ifdef MOZ_EME decoder->SetCDMProxy(mCDMProxy); #endif return decoder.forget(); }
void MediaSourceReader::NotifyTimeRangesChanged() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (mWaitingForSeekData) { //post a task to the decode queue to try to complete the pending seek. RefPtr<nsIRunnable> task(NS_NewRunnableMethod( this, &MediaSourceReader::AttemptSeek)); GetTaskQueue()->Dispatch(task.forget()); } else { MaybeNotifyHaveData(); } }
void MP4Reader::ScheduleUpdate(TrackType aTrack) { auto& decoder = GetDecoderData(aTrack); decoder.mMonitor.AssertCurrentThreadOwns(); if (decoder.mUpdateScheduled) { return; } VLOG("SchedulingUpdate(%s)", TrackTypeToStr(aTrack)); decoder.mUpdateScheduled = true; RefPtr<nsIRunnable> task( NS_NewRunnableMethodWithArg<TrackType>(this, &MP4Reader::Update, aTrack)); GetTaskQueue()->Dispatch(task.forget()); }
nsRefPtr<MediaDecoderReader::VideoDataPromise> MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) { MOZ_ASSERT(OnTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking"); MOZ_DIAGNOSTIC_ASSERT(mVideoPromise.IsEmpty(), "No duplicate sample requests"); nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__); MSE_DEBUGV("RequestVideoData(%d, %lld), mLastVideoTime=%lld", aSkipToNextKeyframe, aTimeThreshold, mLastVideoTime); if (!mVideoTrack) { MSE_DEBUG("called with no video track"); mVideoPromise.Reject(DECODE_ERROR, __func__); return p; } if (aSkipToNextKeyframe) { mTimeThreshold = aTimeThreshold; mDropAudioBeforeThreshold = true; mDropVideoBeforeThreshold = true; } if (IsSeeking()) { MSE_DEBUG("called mid-seek. Rejecting."); mVideoPromise.Reject(CANCELED, __func__); return p; } MOZ_DIAGNOSTIC_ASSERT(!mVideoSeekRequest.Exists()); SwitchSourceResult ret = SwitchVideoSource(&mLastVideoTime); switch (ret) { case SOURCE_NEW: GetVideoReader()->ResetDecode(); mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::CompleteVideoSeekAndDoRequest, &MediaSourceReader::CompleteVideoSeekAndRejectPromise)); break; case SOURCE_NONE: if (mLastVideoTime) { CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime); break; } // Fallback to using first reader. mVideoSourceDecoder = mVideoTrack->Decoders()[0]; default: DoVideoRequest(); break; } return p; }
void MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason) { MOZ_DIAGNOSTIC_ASSERT(!IsSeeking()); mVideoRequest.Complete(); MSE_DEBUG("aReason=%u IsEnded: %d", aReason, IsEnded()); if (aReason == CANCELED) { mVideoPromise.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 lastVideoTime = mLastVideoTime; if (aReason == END_OF_STREAM && mVideoSourceDecoder) { AdjustEndTime(&mLastVideoTime, mVideoSourceDecoder); } // See if we can find a different reader that can pick up where we left off. SwitchSourceResult result = SwitchVideoSource(&mLastVideoTime); if (result == SOURCE_NEW) { GetVideoReader()->ResetDecode(); mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::CompleteVideoSeekAndDoRequest, &MediaSourceReader::CompleteVideoSeekAndRejectPromise)); 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) { mVideoPromise.Reject(DECODE_ERROR, __func__); return; } CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime); if (mLastVideoTime - lastVideoTime >= EOS_FUZZ_US) { // No decoders are available to switch to. We will re-attempt from the last // failing position. mLastVideoTime = lastVideoTime; } }
nsRefPtr<MediaDecoderReader::AudioDataPromise> MediaSourceReader::RequestAudioData() { nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__); MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this); if (!mAudioReader) { MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this); mAudioPromise.Reject(DECODE_ERROR, __func__); return p; } mAudioIsSeeking = false; SwitchAudioReader(mLastAudioTime); mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::OnAudioDecoded, &MediaSourceReader::OnAudioNotDecoded); return p; }
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) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::CompleteAudioSeekAndDoRequest, &MediaSourceReader::CompleteAudioSeekAndRejectPromise)); break; case SOURCE_NONE: if (mLastAudioTime) { CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime); break; } // Fallback to using first reader mAudioSourceDecoder = mAudioTrack->Decoders()[0]; default: DoAudioRequest(); break; } return p; }
nsresult MP4Reader::ResetDecode() { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); Flush(kVideo); { MonitorAutoLock mon(mDemuxerMonitor); if (mVideo.mTrackDemuxer) { mVideo.mTrackDemuxer->Seek(0); } } Flush(kAudio); { MonitorAutoLock mon(mDemuxerMonitor); if (mAudio.mTrackDemuxer) { mAudio.mTrackDemuxer->Seek(0); } } return MediaDecoderReader::ResetDecode(); }
void MediaSourceReader::DoVideoSeek() { int64_t seekTime = mPendingSeekTime; if (mSeekToEnd) { seekTime = LastSampleTime(MediaData::VIDEO_DATA); } if (SwitchVideoSource(&seekTime) == SOURCE_NONE) { // Data we need got evicted since the last time we checked for data // availability. Abort current seek attempt. mWaitingForSeekData = true; return; } GetVideoReader()->ResetDecode(); mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(seekTime), 0) ->RefableThen(GetTaskQueue(), __func__, this, &MediaSourceReader::OnVideoSeekCompleted, &MediaSourceReader::OnVideoSeekFailed)); MSE_DEBUG("reader=%p", GetVideoReader()); }
nsRefPtr<MediaDecoderReader::VideoDataPromise> MP4Reader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); VLOG("skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold); if (!EnsureDecodersSetup()) { NS_WARNING("Error constructing MP4 decoders"); return VideoDataPromise::CreateAndReject(DECODE_ERROR, __func__); } if (mShutdown) { NS_WARNING("RequestVideoData on shutdown MP4Reader!"); return VideoDataPromise::CreateAndReject(CANCELED, __func__); } MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder); bool eos = false; if (ShouldSkip(aSkipToNextKeyframe, aTimeThreshold)) { uint32_t parsed = 0; eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed); if (!eos && NS_FAILED(mVideo.mDecoder->Flush())) { NS_WARNING("Failed to skip/flush video when skipping-to-next-keyframe."); } mDecoder->NotifyDecodedFrames(parsed, 0, parsed); } MonitorAutoLock lock(mVideo.mMonitor); nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__); if (mVideo.mError) { mVideo.mPromise.Reject(DECODE_ERROR, __func__); } else if (eos) { mVideo.mPromise.Reject(END_OF_STREAM, __func__); } else { ScheduleUpdate(kVideo); } return p; }
nsRefPtr<MediaDecoderReader::AudioDataPromise> MP4Reader::RequestAudioData() { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); VLOG(""); if (!EnsureDecodersSetup()) { NS_WARNING("Error constructing MP4 decoders"); return AudioDataPromise::CreateAndReject(DECODE_ERROR, __func__); } if (mShutdown) { NS_WARNING("RequestAudioData on shutdown MP4Reader!"); return AudioDataPromise::CreateAndReject(CANCELED, __func__); } MonitorAutoLock lock(mAudio.mMonitor); nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__); ScheduleUpdate(kAudio); return p; }
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::OnVideoNotDecoded(NotDecodedReason aReason) { MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded()); if (aReason == DECODE_ERROR || aReason == CANCELED) { mVideoPromise.Reject(aReason, __func__); return; } // End of stream. Force switching past this stream to another reader by // switching to the end of the buffered range. MOZ_ASSERT(aReason == END_OF_STREAM); if (mVideoReader) { AdjustEndTime(&mLastVideoTime, mVideoReader); } // See if we can find a different reader that can pick up where we left off. We use the // EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug // 1065207. if (SwitchVideoReader(mLastVideoTime + EOS_FUZZ_US)) { mVideoReader->RequestVideoData(false, 0) ->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded); return; } // If the entire MediaSource is done, generate an EndOfStream. if (IsEnded()) { mVideoPromise.Reject(END_OF_STREAM, __func__); return; } // We don't have the data the caller wants. Tell that we're waiting for JS to // give us more data. mVideoPromise.Reject(WAITING_FOR_DATA, __func__); }
nsRefPtr<ShutdownPromise> MP4Reader::Shutdown() { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); if (mAudio.mDecoder) { Flush(kAudio); mAudio.mDecoder->Shutdown(); mAudio.mDecoder = nullptr; } if (mAudio.mTaskQueue) { mAudio.mTaskQueue->BeginShutdown(); mAudio.mTaskQueue->AwaitShutdownAndIdle(); mAudio.mTaskQueue = nullptr; } mAudio.mPromise.SetMonitor(nullptr); MOZ_ASSERT(mAudio.mPromise.IsEmpty()); if (mVideo.mDecoder) { Flush(kVideo); mVideo.mDecoder->Shutdown(); mVideo.mDecoder = nullptr; } if (mVideo.mTaskQueue) { mVideo.mTaskQueue->BeginShutdown(); mVideo.mTaskQueue->AwaitShutdownAndIdle(); mVideo.mTaskQueue = nullptr; } mVideo.mPromise.SetMonitor(nullptr); MOZ_ASSERT(mVideo.mPromise.IsEmpty()); // Dispose of the queued sample before shutting down the demuxer mQueuedVideoSample = nullptr; mPlatform = nullptr; return MediaDecoderReader::Shutdown(); }
void MP4Reader::Flush(TrackType aTrack) { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); VLOG("Flush(%s) BEGIN", TrackTypeToStr(aTrack)); DecoderData& data = GetDecoderData(aTrack); if (!data.mDecoder) { return; } // Purge the current decoder's state. // Set a flag so that we ignore all output while we call // MediaDataDecoder::Flush(). { MonitorAutoLock mon(data.mMonitor); data.mIsFlushing = true; data.mDemuxEOS = false; data.mDrainComplete = false; } data.mDecoder->Flush(); { MonitorAutoLock mon(data.mMonitor); data.mIsFlushing = false; data.mOutput.Clear(); data.mNumSamplesInput = 0; data.mNumSamplesOutput = 0; data.mInputExhausted = false; if (data.HasPromise()) { data.RejectPromise(CANCELED, __func__); } data.mDiscontinuity = true; data.mUpdateScheduled = false; } if (aTrack == kVideo) { mQueuedVideoSample = nullptr; } VLOG("Flush(%s) END", TrackTypeToStr(aTrack)); }
nsRefPtr<MediaDecoderReader::VideoDataPromise> MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) { nsRefPtr<VideoDataPromise> p = mVideoPromise.Ensure(__func__); MSE_DEBUGV("MediaSourceReader(%p)::RequestVideoData(%d, %lld)", this, aSkipToNextKeyframe, aTimeThreshold); if (!mVideoReader) { MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called with no video reader", this); mVideoPromise.Reject(DECODE_ERROR, __func__); return p; } if (aSkipToNextKeyframe) { mTimeThreshold = aTimeThreshold; mDropAudioBeforeThreshold = true; mDropVideoBeforeThreshold = true; } mVideoIsSeeking = false; SwitchVideoReader(mLastVideoTime); mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold) ->Then(GetTaskQueue(), __func__, this, &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded); return p; }
bool DBThreadPool::ScheduleTaks(DBTask* pTask, DBATaskLevel taskLevel /*= eDTL_1*/) { assert( pTask ); assert( taskLevel < eDBATL_MAX && taskLevel >= eDBATL_0); if(!m_bRun || !pTask) return false; if(pTask->GetAvatarDID() != 0) { uint32 nThreadID = uint32( uint64(pTask->GetAvatarDID()) % m_vThreads.size() ); assert( nThreadID <= m_vThreads.size() ); ODBCThread* pThread = m_vThreads[nThreadID]; assert( pThread ); pThread->GetTaskQueue().PushTask( pTask, taskLevel); } else { GetTaskQueue().PushTask( pTask, taskLevel); } return true; }
void MP4Reader::Update(TrackType aTrack) { MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn()); if (mShutdown) { return; } // Record number of frames decoded and parsed. Automatically update the // stats counters using the AutoNotifyDecoded stack-based class. AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); bool needInput = false; bool needOutput = false; auto& decoder = GetDecoderData(aTrack); { MonitorAutoLock lock(decoder.mMonitor); decoder.mUpdateScheduled = false; if (NeedInput(decoder)) { needInput = true; decoder.mInputExhausted = false; decoder.mNumSamplesInput++; } if (aTrack == kVideo) { uint64_t delta = decoder.mNumSamplesOutput - mLastReportedNumDecodedFrames; a.mDecoded = static_cast<uint32_t>(delta); mLastReportedNumDecodedFrames = decoder.mNumSamplesOutput; } if (decoder.HasPromise()) { needOutput = true; if (!decoder.mOutput.IsEmpty()) { nsRefPtr<MediaData> output = decoder.mOutput[0]; decoder.mOutput.RemoveElementAt(0); ReturnOutput(output, aTrack); } else if (decoder.mDrainComplete) { decoder.RejectPromise(END_OF_STREAM, __func__); } } } VLOG("Update(%s) ni=%d no=%d iex=%d fl=%d", TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted, decoder.mIsFlushing); if (needInput) { nsAutoPtr<MediaSample> sample(PopSample(aTrack)); // Collect telemetry from h264 Annex B SPS. if (!mFoundSPSForTelemetry && sample && AnnexB::HasSPS(sample->mMp4Sample)) { nsRefPtr<ByteBuffer> extradata = AnnexB::ExtractExtraData(sample->mMp4Sample); mFoundSPSForTelemetry = AccumulateSPSTelemetry(extradata); } if (sample) { decoder.mDecoder->Input(sample->mMp4Sample.forget()); if (aTrack == kVideo) { a.mParsed++; } } else { { MonitorAutoLock lock(decoder.mMonitor); MOZ_ASSERT(!decoder.mDemuxEOS); decoder.mDemuxEOS = true; } // DrainComplete takes care of reporting EOS upwards decoder.mDecoder->Drain(); } } }