void MediaSourceReader::ContinueShutdown() { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (mTrackBuffers.Length()) { mTrackBuffers[0]->Shutdown()->Then(OwnerThread(), __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__); }
int64_t MediaOmxReader::ProcessCachedData(int64_t aOffset) { // Could run on decoder thread or IO thread. nsRefPtr<AbstractMediaDecoder> decoder = SafeGetDecoder(); if (!decoder) { // reader has shut down return -1; } // We read data in chunks of 32 KiB. We can reduce this // value if media, such as sdcards, is too slow. // Because of SD card's slowness, need to keep sReadSize to small size. // See Bug 914870. static const int64_t sReadSize = 32 * 1024; NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread."); MOZ_ASSERT(decoder->GetResource()); int64_t resourceLength = decoder->GetResource()->GetCachedDataEnd(0); NS_ENSURE_TRUE(resourceLength >= 0, -1); if (aOffset >= resourceLength) { return 0; // Cache is empty, nothing to do } int64_t bufferLength = std::min<int64_t>(resourceLength-aOffset, sReadSize); nsRefPtr<NotifyDataArrivedRunnable> runnable( new NotifyDataArrivedRunnable(this, bufferLength, aOffset, resourceLength)); if (OnTaskQueue()) { runnable->Run(); } else { OwnerThread()->Dispatch(runnable.forget()); } return resourceLength - aOffset - bufferLength; }
nsRefPtr<MediaDecoderReader::SeekPromise> MediaOmxReader::Seek(int64_t aTarget, int64_t aEndTime) { MOZ_ASSERT(OnTaskQueue()); EnsureActive(); nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__); if (mHasAudio && mHasVideo) { // The OMXDecoder seeks/demuxes audio and video streams separately. So if // we seek both audio and video to aTarget, the audio stream can typically // seek closer to the seek target, since typically every audio block is // a sync point, whereas for video there are only keyframes once every few // seconds. So if we have both audio and video, we must seek the video // stream to the preceeding keyframe first, get the stream time, and then // seek the audio stream to match the video stream's time. Otherwise, the // audio and video streams won't be in sync after the seek. mVideoSeekTimeUs = aTarget; nsRefPtr<MediaOmxReader> self = this; mSeekRequest.Begin(DecodeToFirstVideoData()->Then(OwnerThread(), __func__, [self] (VideoData* v) { self->mSeekRequest.Complete(); self->mAudioSeekTimeUs = v->mTime; self->mSeekPromise.Resolve(self->mAudioSeekTimeUs, __func__); }, [self, aTarget] () { self->mSeekRequest.Complete(); self->mAudioSeekTimeUs = aTarget; self->mSeekPromise.Resolve(aTarget, __func__); })); } else { mAudioSeekTimeUs = mVideoSeekTimeUs = aTarget; mSeekPromise.Resolve(aTarget, __func__); } return p; }
void MediaSourceReader::DoAudioRequest() { mAudioRequest.Begin(GetAudioReader()->RequestAudioData() ->Then(OwnerThread(), __func__, this, &MediaSourceReader::OnAudioDecoded, &MediaSourceReader::OnAudioNotDecoded)); }
RefPtr<MediaDecoderReader::MediaDataPromise> MediaDecoderReader::DecodeToFirstVideoData() { MOZ_ASSERT(OnTaskQueue()); typedef MediaDecoderReader::MediaDataPromise PromiseType; RefPtr<PromiseType::Private> p = new PromiseType::Private(__func__); RefPtr<MediaDecoderReader> self = this; InvokeUntil([self] () -> bool { MOZ_ASSERT(self->OnTaskQueue()); NS_ENSURE_TRUE(!self->mShutdown, false); bool skip = false; if (!self->DecodeVideoFrame(skip, 0)) { self->VideoQueue().Finish(); return !!self->VideoQueue().PeekFront(); } return true; }, [self] () -> bool { MOZ_ASSERT(self->OnTaskQueue()); return self->VideoQueue().GetSize(); })->Then(OwnerThread(), __func__, [self, p] () { p->Resolve(self->VideoQueue().PeekFront(), __func__); }, [p] () { // We don't have a way to differentiate EOS, error, and shutdown here. :-( p->Reject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); }); return p.forget(); }
void AccurateSeekTask::SetCallbacks() { AssertOwnerThread(); mAudioCallback = mReader->AudioCallback().Connect( OwnerThread(), [this] (AudioCallbackData aData) { if (aData.is<MediaData*>()) { OnAudioDecoded(aData.as<MediaData*>()); } else { OnNotDecoded(MediaData::AUDIO_DATA, aData.as<MediaDecoderReader::NotDecodedReason>()); } }); mVideoCallback = mReader->VideoCallback().Connect( OwnerThread(), [this] (VideoCallbackData aData) { typedef Tuple<MediaData*, TimeStamp> Type; if (aData.is<Type>()) { OnVideoDecoded(Get<0>(aData.as<Type>())); } else { OnNotDecoded(MediaData::VIDEO_DATA, aData.as<MediaDecoderReader::NotDecodedReason>()); } }); mAudioWaitCallback = mReader->AudioWaitCallback().Connect( OwnerThread(), [this] (WaitCallbackData aData) { // Ignore pending requests from video-only seek. if (mTarget.IsVideoOnly()) { return; } if (aData.is<MediaData::Type>()) { RequestAudioData(); } }); mVideoWaitCallback = mReader->VideoWaitCallback().Connect( OwnerThread(), [this] (WaitCallbackData aData) { if (aData.is<MediaData::Type>()) { RequestVideoData(); } }); }
nsRefPtr<MediaDecoderReader::VideoDataPromise> MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold, bool aForceDecodeAhead) { 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()); mForceVideoDecodeAhead = aForceDecodeAhead; SwitchSourceResult ret = SwitchVideoSource(&mLastVideoTime); switch (ret) { case SOURCE_NEW: GetVideoReader()->ResetDecode(); mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mLastVideoTime), 0) ->Then(OwnerThread(), __func__, this, &MediaSourceReader::CompleteVideoSeekAndDoRequest, &MediaSourceReader::CompleteVideoSeekAndRejectPromise)); break; case SOURCE_NONE: if (!mLastVideoTime) { // This is the first call to RequestVideoData. // Fallback to using decoder with earliest data. mVideoSourceDecoder = FirstDecoder(MediaData::VIDEO_DATA); } if (mLastVideoTime || !mVideoSourceDecoder) { CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime); break; } // Fallback to getting first frame from first decoder. default: DoVideoRequest(); break; } return p; }
void MediaSourceReader::DoVideoRequest() { mVideoRequest.Begin(GetVideoReader()->RequestVideoData(mDropVideoBeforeThreshold, GetReaderVideoTime(mTimeThreshold), mForceVideoDecodeAhead) ->Then(OwnerThread(), __func__, this, &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded)); }
RefPtr<SeekTask::SeekTaskPromise> SeekTask::Seek(const media::TimeUnit& aDuration) { AssertOwnerThread(); // Do the seek. mSeekRequest.Begin(mReader->Seek(mSeekJob.mTarget, aDuration) ->Then(OwnerThread(), __func__, this, &SeekTask::OnSeekResolved, &SeekTask::OnSeekRejected)); return mSeekTaskPromise.Ensure(__func__); }
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)); OwnerThread()->Dispatch(task.forget()); } else { MaybeNotifyHaveData(); } }
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)); OwnerThread()->Dispatch(task.forget()); } }
nsresult RtspOmxReader::InitOmxDecoder() { if (!mOmxDecoder.get()) { NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null."); NS_ASSERTION(mDecoder->GetResource(), "RtspOmxReader mDecoder->GetResource() is null."); mExtractor = new RtspExtractor(mRtspResource); mOmxDecoder = new OmxDecoder(mDecoder, OwnerThread()); if (!mOmxDecoder->Init(mExtractor)) { return NS_ERROR_FAILURE; } } return NS_OK; }
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) ->Then(OwnerThread(), __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() { 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; }
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) ->Then(OwnerThread(), __func__, this, &MediaSourceReader::OnVideoSeekCompleted, &MediaSourceReader::OnVideoSeekFailed)); MSE_DEBUG("reader=%p", GetVideoReader()); }
nsresult MediaOmxReader::InitOmxDecoder() { if (!mOmxDecoder.get()) { //register sniffers, if they are not registered in this process. DataSource::RegisterDefaultSniffers(); sp<DataSource> dataSource = new MediaStreamSource(mDecoder->GetResource()); dataSource->initCheck(); mExtractor = MediaExtractor::Create(dataSource); if (!mExtractor.get()) { return NS_ERROR_FAILURE; } mOmxDecoder = new OmxDecoder(mDecoder, OwnerThread()); if (!mOmxDecoder->Init(mExtractor)) { return NS_ERROR_FAILURE; } mStreamSource = static_cast<MediaStreamSource*>(dataSource.get()); } return NS_OK; }
already_AddRefed<SourceBufferDecoder> MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset) { MOZ_ASSERT(NS_IsMainThread()); if (mDecoder->IsShutdown()) { return nullptr; } // The task queue borrowing 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. nsRefPtr<SourceBufferDecoder> decoder = new SourceBufferDecoder(new SourceBufferResource(aType), mDecoder, aTimestampOffset); nsRefPtr<MediaDecoderReader> reader(CreateReaderForType(aType, decoder, OwnerThread())); 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. reader->DispatchSetStartTime(0); #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::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() ->Then(OwnerThread(), __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 MediaDecoderReader::ThrottledNotifyDataArrived(const Interval<int64_t>& aInterval) { MOZ_ASSERT(OnTaskQueue()); NS_ENSURE_TRUE_VOID(!mShutdown); if (mThrottledInterval.isNothing()) { mThrottledInterval.emplace(aInterval); } else if (mThrottledInterval.ref().Contains(aInterval)) { return; } else if (!mThrottledInterval.ref().Contiguous(aInterval)) { DoThrottledNotify(); mThrottledInterval.emplace(aInterval); } else { mThrottledInterval = Some(mThrottledInterval.ref().Span(aInterval)); } // If it's been long enough since our last update, do it. if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) { DoThrottledNotify(); } else if (!mThrottledNotify.Exists()) { // Otherwise, schedule an update if one isn't scheduled already. nsRefPtr<MediaDecoderReader> self = this; mThrottledNotify.Begin( mTimer->WaitUntil(mLastThrottledNotify + mThrottleDuration, __func__) ->Then(OwnerThread(), __func__, [self] () -> void { self->mThrottledNotify.Complete(); NS_ENSURE_TRUE_VOID(!self->mShutdown); self->DoThrottledNotify(); }, [self] () -> void { self->mThrottledNotify.Complete(); NS_WARNING("throttle callback rejected"); }) ); } }
nsRefPtr<MediaDecoderReader::MetadataPromise> MediaOmxReader::AsyncReadMetadata() { MOZ_ASSERT(OnTaskQueue()); EnsureActive(); // Initialize the internal OMX Decoder. nsresult rv = InitOmxDecoder(); if (NS_FAILED(rv)) { return MediaDecoderReader::MetadataPromise::CreateAndReject( ReadMetadataFailureReason::METADATA_ERROR, __func__); } bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3); if (isMP3) { // When read sdcard's file on b2g platform at constructor, // the mDecoder->GetResource()->GetLength() would return -1. // Delay set the total duration on this function. mMP3FrameParser.SetLength(mDecoder->GetResource()->GetLength()); ProcessCachedData(0); } nsRefPtr<MediaDecoderReader::MetadataPromise> p = mMetadataPromise.Ensure(__func__); nsRefPtr<MediaOmxReader> self = this; mMediaResourceRequest.Begin(mOmxDecoder->AllocateMediaResources() ->Then(OwnerThread(), __func__, [self] (bool) -> void { self->mMediaResourceRequest.Complete(); self->HandleResourceAllocated(); }, [self] (bool) -> void { self->mMediaResourceRequest.Complete(); self->mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); })); return p; }