void SeekTask::CheckIfSeekComplete() { AssertOwnerThread(); const bool videoSeekComplete = IsVideoSeekComplete(); if (HasVideo() && !videoSeekComplete) { // We haven't reached the target. Ensure we have requested another sample. if (NS_FAILED(EnsureVideoDecodeTaskQueued())) { DECODER_WARN("Failed to request video during seek"); RejectIfExist(__func__); } } const bool audioSeekComplete = IsAudioSeekComplete(); if (HasAudio() && !audioSeekComplete) { // We haven't reached the target. Ensure we have requested another sample. if (NS_FAILED(EnsureAudioDecodeTaskQueued())) { DECODER_WARN("Failed to request audio during seek"); RejectIfExist(__func__); } } SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d", audioSeekComplete, videoSeekComplete); if (audioSeekComplete && videoSeekComplete) { Resolve(__func__); // Call to MDSM::SeekCompleted(); } }
void SeekTask::OnSeekRejected(nsresult aResult) { AssertOwnerThread(); mSeekRequest.Complete(); MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest"); RejectIfExist(__func__); }
void SeekTask::OnVideoDecoded(MediaData* aVideoSample) { AssertOwnerThread(); RefPtr<MediaData> video(aVideoSample); MOZ_ASSERT(video); // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is // resolved. SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d", (video ? video->mTime : -1), (video ? video->GetEndTime() : -1), (video ? video->mDiscontinuity : 0)); if (!Exists()) { // We've received a sample from a previous decode. Discard it. return; } if (mDropVideoUntilNextDiscontinuity) { if (video->mDiscontinuity) { mDropVideoUntilNextDiscontinuity = false; } } if (!mDropVideoUntilNextDiscontinuity) { // We must be after the discontinuity; we're receiving samples // at or after the seek target. if (mSeekJob.mTarget.IsFast() && mSeekJob.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek && video->mTime < mCurrentTimeBeforeSeek) { // We are doing a fastSeek, but we ended up *before* the previous // playback position. This is surprising UX, so switch to an accurate // seek and decode to the seek target. This is not conformant to the // spec, fastSeek should always be fast, but until we get the time to // change all Readers to seek to the keyframe after the currentTime // in this case, we'll just decode forward. Bug 1026330. mSeekJob.mTarget.SetType(SeekTarget::Accurate); } if (mSeekJob.mTarget.IsFast()) { // Non-precise seek. We can stop the seek at the first sample. mSeekedVideoData = video; } else { // We're doing an accurate seek. We still need to discard // MediaData up to the one containing exact seek target. if (NS_FAILED(DropVideoUpToSeekTarget(video.get()))) { RejectIfExist(__func__); return; } } } CheckIfSeekComplete(); }
void AccurateSeekTask::OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason) { AssertOwnerThread(); MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished"); SAMPLE_LOG("OnNotDecoded type=%d reason=%u", aType, aReason); // Ignore pending requests from video-only seek. if (aType == MediaData::AUDIO_DATA && mTarget.IsVideoOnly()) { return; } if (aReason == MediaDecoderReader::DECODE_ERROR) { // If this is a decode error, delegate to the generic error path. CancelCallbacks(); RejectIfExist(__func__); return; } // If the decoder is waiting for data, we tell it to call us back when the // data arrives. if (aReason == MediaDecoderReader::WAITING_FOR_DATA) { mReader->WaitForData(aType); return; } if (aReason == MediaDecoderReader::CANCELED) { if (aType == MediaData::AUDIO_DATA) { RequestAudioData(); } else { RequestVideoData(); } return; } if (aReason == MediaDecoderReader::END_OF_STREAM) { if (aType == MediaData::AUDIO_DATA) { mIsAudioQueueFinished = true; mDoneAudioSeeking = true; } else { mIsVideoQueueFinished = true; mDoneVideoSeeking = true; if (mFirstVideoFrameAfterSeek) { // Hit the end of stream. Move mFirstVideoFrameAfterSeek into // mSeekedVideoData so we have something to display after seeking. mSeekedVideoData = mFirstVideoFrameAfterSeek.forget(); } } MaybeFinishSeek(); } }
void AccurateSeekTask::Discard() { AssertOwnerThread(); // Disconnect MDSM. RejectIfExist(__func__); // Disconnect MediaDecoderReaderWrapper. mSeekRequest.DisconnectIfExists(); CancelCallbacks(); mIsDiscarded = true; }
void SeekTask::Discard() { // Disconnect MediaDecoder. mSeekJob.RejectIfExists(__func__); // Disconnect MDSM. RejectIfExist(__func__); // Disconnect MediaDecoderReaderWrapper. mSeekRequest.DisconnectIfExists(); CancelMediaDecoderReaderWrapperCallback(); mIsDiscarded = true; }
void SeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason) { AssertOwnerThread(); SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason); if (aReason == MediaDecoderReader::DECODE_ERROR) { // If this is a decode error, delegate to the generic error path. RejectIfExist(__func__); return; } // If the decoder is waiting for data, we tell it to call us back when the // data arrives. if (aReason == MediaDecoderReader::WAITING_FOR_DATA) { MOZ_ASSERT(mReader->IsWaitForDataSupported(), "Readers that send WAITING_FOR_DATA need to implement WaitForData"); mReader->WaitForData(MediaData::VIDEO_DATA); // We are out of data to decode and will enter buffering mode soon. // We want to play the frames we have already decoded, so we stop pre-rolling // and ensure that loadeddata is fired as required. mNeedToStopPrerollingVideo = true; return; } if (aReason == MediaDecoderReader::CANCELED) { EnsureVideoDecodeTaskQueued(); return; } if (aReason == MediaDecoderReader::END_OF_STREAM) { if (Exists() && mFirstVideoFrameAfterSeek) { // Null sample. Hit end of stream. If we have decoded a frame, // insert it into the queue so that we have something to display. // We make sure to do this before invoking VideoQueue().Finish() // below. mSeekedVideoData = mFirstVideoFrameAfterSeek; mFirstVideoFrameAfterSeek = nullptr; } mIsVideoQueueFinished = true; mDropVideoUntilNextDiscontinuity = false; // To make IsVideoSeekComplete() return TRUE. CheckIfSeekComplete(); } }
void AccurateSeekTask::OnAudioDecoded(MediaData* aAudioSample) { AssertOwnerThread(); MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished"); RefPtr<MediaData> audio(aAudioSample); MOZ_ASSERT(audio); // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is // resolved. SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d", audio->mTime, audio->GetEndTime(), audio->mDiscontinuity); // Video-only seek doesn't reset audio decoder. There might be pending audio // requests when AccurateSeekTask::Seek() begins. We will just store the data // without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget(). if (mTarget.IsVideoOnly()) { mSeekedAudioData = audio.forget(); return; } if (mFirstAudioSample) { mFirstAudioSample = false; MOZ_ASSERT(audio->mDiscontinuity); } AdjustFastSeekIfNeeded(audio); if (mTarget.IsFast()) { // Non-precise seek; we can stop the seek at the first sample. mSeekedAudioData = audio; mDoneAudioSeeking = true; } else if (NS_FAILED(DropAudioUpToSeekTarget(audio))) { CancelCallbacks(); RejectIfExist(__func__); return; } if (!mDoneAudioSeeking) { RequestAudioData(); return; } MaybeFinishSeek(); }
void AccurateSeekTask::OnVideoDecoded(MediaData* aVideoSample) { AssertOwnerThread(); MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished"); RefPtr<MediaData> video(aVideoSample); MOZ_ASSERT(video); // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is // resolved. SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d", video->mTime, video->GetEndTime(), video->mDiscontinuity); if (mFirstVideoSample) { mFirstVideoSample = false; MOZ_ASSERT(video->mDiscontinuity); } AdjustFastSeekIfNeeded(video); if (mTarget.IsFast()) { // Non-precise seek. We can stop the seek at the first sample. mSeekedVideoData = video; mDoneVideoSeeking = true; } else if (NS_FAILED(DropVideoUpToSeekTarget(video.get()))) { CancelCallbacks(); RejectIfExist(__func__); return; } if (!mDoneVideoSeeking) { RequestVideoData(); return; } MaybeFinishSeek(); }
void SeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason) { AssertOwnerThread(); SAMPLE_LOG("OnAduioNotDecoded (aReason=%u)", aReason); if (aReason == MediaDecoderReader::DECODE_ERROR) { // If this is a decode error, delegate to the generic error path. RejectIfExist(__func__); return; } // If the decoder is waiting for data, we tell it to call us back when the // data arrives. if (aReason == MediaDecoderReader::WAITING_FOR_DATA) { MOZ_ASSERT(mReader->IsWaitForDataSupported(), "Readers that send WAITING_FOR_DATA need to implement WaitForData"); mReader->WaitForData(MediaData::AUDIO_DATA); // We are out of data to decode and will enter buffering mode soon. // We want to play the frames we have already decoded, so we stop pre-rolling // and ensure that loadeddata is fired as required. mNeedToStopPrerollingAudio = true; return; } if (aReason == MediaDecoderReader::CANCELED) { EnsureAudioDecodeTaskQueued(); return; } if (aReason == MediaDecoderReader::END_OF_STREAM) { mIsAudioQueueFinished = true; mDropAudioUntilNextDiscontinuity = false; // To make IsAudioSeekComplete() return TRUE. CheckIfSeekComplete(); } }