copy from dom/media/SeekTask.cpp
copy to dom/media/AccurateSeekTask.cpp
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/AccurateSeekTask.cpp
@@ -1,84 +1,62 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "SeekTask.h"
+#include "AccurateSeekTask.h"
#include "MediaDecoderReaderWrapper.h"
#include "mozilla/AbstractThread.h"
#include "mozilla/Assertions.h"
#include "nsPrintfCString.h"
namespace mozilla {
extern LazyLogModule gMediaDecoderLog;
extern LazyLogModule gMediaSampleLog;
// avoid redefined macro in unified build
#undef LOG
#undef DECODER_LOG
#undef VERBOSE_LOG
#define LOG(m, l, x, ...) \
- MOZ_LOG(m, l, ("[SeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
+ MOZ_LOG(m, l, ("[AccurateSeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
#define DECODER_LOG(x, ...) \
LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
#define VERBOSE_LOG(x, ...) \
LOG(gMediaDecoderLog, LogLevel::Verbose, x, ##__VA_ARGS__)
#define SAMPLE_LOG(x, ...) \
LOG(gMediaSampleLog, LogLevel::Debug, x, ##__VA_ARGS__)
// Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
// when __VA_ARGS__ expands to nothing. This is a workaround for it.
#define DECODER_WARN_HELPER(a, b) NS_WARNING b
#define DECODER_WARN(x, ...) \
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get()))
-/*static*/ already_AddRefed<SeekTask>
-SeekTask::CreateSeekTask(const void* aDecoderID,
- AbstractThread* aThread,
- MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime)
-{
- RefPtr<SeekTask> task(new SeekTask(aDecoderID, aThread,
- aReader, Move(aSeekJob), aInfo,
- aDuration, aCurrentMediaTime));
-
- return task.forget();
-}
-
-SeekTask::SeekTask(const void* aDecoderID,
- AbstractThread* aThread,
- MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime)
- : mDecoderID(aDecoderID)
- , mOwnerThread(aThread)
- , mReader(aReader)
- , mSeekJob(Move(aSeekJob))
+AccurateSeekTask::AccurateSeekTask(const void* aDecoderID,
+ AbstractThread* aThread,
+ MediaDecoderReaderWrapper* aReader,
+ SeekJob&& aSeekJob,
+ const MediaInfo& aInfo,
+ const media::TimeUnit& aDuration,
+ int64_t aCurrentMediaTime)
+ : SeekTask(aDecoderID, aThread, aReader, Move(aSeekJob))
, mCurrentTimeBeforeSeek(aCurrentMediaTime)
, mAudioRate(aInfo.mAudio.mRate)
, mHasAudio(aInfo.HasAudio())
, mHasVideo(aInfo.HasVideo())
, mDropAudioUntilNextDiscontinuity(false)
, mDropVideoUntilNextDiscontinuity(false)
- , mIsDiscarded(false)
- , mIsAudioQueueFinished(false)
- , mIsVideoQueueFinished(false)
- , mNeedToStopPrerollingAudio(false)
- , mNeedToStopPrerollingVideo(false)
{
+ AssertOwnerThread();
+
// Bound the seek time to be inside the media range.
int64_t end = aDuration.ToMicroseconds();
NS_ASSERTION(end != -1, "Should know end time by now");
int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
seekTime = std::min(seekTime, end);
seekTime = std::max(int64_t(0), seekTime);
NS_ASSERTION(seekTime >= 0 && seekTime <= end,
"Can only seek in range [0,duration]");
@@ -86,229 +64,178 @@ SeekTask::SeekTask(const void* aDecoderI
mDropAudioUntilNextDiscontinuity = HasAudio();
mDropVideoUntilNextDiscontinuity = HasVideo();
// Configure MediaDecoderReaderWrapper.
SetMediaDecoderReaderWrapperCallback();
}
-SeekTask::~SeekTask()
+AccurateSeekTask::~AccurateSeekTask()
{
+ AssertOwnerThread();
MOZ_ASSERT(mIsDiscarded);
}
-void
-SeekTask::Resolve(const char* aCallSite)
-{
- SeekTaskResolveValue val;
- val.mSeekedAudioData = mSeekedAudioData;
- val.mSeekedVideoData = mSeekedVideoData;
- val.mIsAudioQueueFinished = mIsAudioQueueFinished;
- val.mIsVideoQueueFinished = mIsVideoQueueFinished;
- val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
- val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
-
- mSeekTaskPromise.Resolve(val, aCallSite);
-}
-
-void
-SeekTask::RejectIfExist(const char* aCallSite)
+bool
+AccurateSeekTask::HasAudio() const
{
- SeekTaskRejectValue val;
- val.mIsAudioQueueFinished = mIsAudioQueueFinished;
- val.mIsVideoQueueFinished = mIsVideoQueueFinished;
- val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
- val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
-
- mSeekTaskPromise.RejectIfExists(val, aCallSite);
-}
-
-void
-SeekTask::AssertOwnerThread() const
-{
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
-}
-
-bool
-SeekTask::HasAudio() const
-{
+ AssertOwnerThread();
return mHasAudio;
}
bool
-SeekTask::HasVideo() const
+AccurateSeekTask::HasVideo() const
{
+ AssertOwnerThread();
return mHasVideo;
}
-AbstractThread*
-SeekTask::OwnerThread() const
+void
+AccurateSeekTask::Discard()
{
- return mOwnerThread;
-}
+ AssertOwnerThread();
-void
-SeekTask::Discard()
-{
// Disconnect MediaDecoder.
mSeekJob.RejectIfExists(__func__);
// Disconnect MDSM.
RejectIfExist(__func__);
// Disconnect MediaDecoderReaderWrapper.
mSeekRequest.DisconnectIfExists();
CancelMediaDecoderReaderWrapperCallback();
mIsDiscarded = true;
}
bool
-SeekTask::NeedToResetMDSM() const
+AccurateSeekTask::NeedToResetMDSM() const
{
+ AssertOwnerThread();
return true;
}
-SeekJob&
-SeekTask::GetSeekJob()
-{
- return mSeekJob;
-}
-
-bool
-SeekTask::Exists() const
-{
- // mSeekTaskPromise communicates SeekTask and MDSM;
- // mSeekJob communicates MDSM and MediaDecoder;
- // Either one exists means the current seek task has yet finished.
- return !mSeekTaskPromise.IsEmpty() || mSeekJob.Exists();
-}
-
-RefPtr<SeekTask::SeekTaskPromise>
-SeekTask::Seek(const media::TimeUnit& aDuration)
+RefPtr<AccurateSeekTask::SeekTaskPromise>
+AccurateSeekTask::Seek(const media::TimeUnit& aDuration)
{
AssertOwnerThread();
// Do the seek.
mSeekRequest.Begin(mReader->Seek(mSeekJob.mTarget, aDuration)
->Then(OwnerThread(), __func__, this,
- &SeekTask::OnSeekResolved, &SeekTask::OnSeekRejected));
+ &AccurateSeekTask::OnSeekResolved, &AccurateSeekTask::OnSeekRejected));
return mSeekTaskPromise.Ensure(__func__);
}
bool
-SeekTask::IsVideoDecoding() const
+AccurateSeekTask::IsVideoDecoding() const
{
AssertOwnerThread();
return HasVideo() && !mIsVideoQueueFinished;
}
bool
-SeekTask::IsAudioDecoding() const
+AccurateSeekTask::IsAudioDecoding() const
{
AssertOwnerThread();
return HasAudio() && !mIsAudioQueueFinished;
}
nsresult
-SeekTask::EnsureAudioDecodeTaskQueued()
+AccurateSeekTask::EnsureAudioDecodeTaskQueued()
{
AssertOwnerThread();
-
SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
IsAudioDecoding(), AudioRequestStatus());
if (!IsAudioDecoding() ||
mReader->IsRequestingAudioData() ||
mReader->IsWaitingAudioData() ||
mSeekRequest.Exists()) {
return NS_OK;
}
RequestAudioData();
return NS_OK;
}
nsresult
-SeekTask::EnsureVideoDecodeTaskQueued()
+AccurateSeekTask::EnsureVideoDecodeTaskQueued()
{
AssertOwnerThread();
-
SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
IsVideoDecoding(), VideoRequestStatus());
if (!IsVideoDecoding() ||
mReader->IsRequestingVideoData() ||
mReader->IsWaitingVideoData() ||
mSeekRequest.Exists()) {
return NS_OK;
}
RequestVideoData();
return NS_OK;
}
const char*
-SeekTask::AudioRequestStatus()
+AccurateSeekTask::AudioRequestStatus()
{
AssertOwnerThread();
+
if (mReader->IsRequestingAudioData()) {
MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingAudioData());
return "pending";
} else if (mReader->IsWaitingAudioData()) {
return "waiting";
}
return "idle";
}
const char*
-SeekTask::VideoRequestStatus()
+AccurateSeekTask::VideoRequestStatus()
{
AssertOwnerThread();
+
if (mReader->IsRequestingVideoData()) {
MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingVideoData());
return "pending";
} else if (mReader->IsWaitingVideoData()) {
return "waiting";
}
return "idle";
}
void
-SeekTask::RequestAudioData()
+AccurateSeekTask::RequestAudioData()
{
AssertOwnerThread();
-
SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
!!mSeekedAudioData, mReader->SizeOfAudioQueueInFrames());
mReader->RequestAudioData();
}
void
-SeekTask::RequestVideoData()
+AccurateSeekTask::RequestVideoData()
{
AssertOwnerThread();
- //These two variables are not used in the SEEKING state.
- const bool skipToNextKeyFrame = false;
- const media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(0);
+ SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
+ !!mSeekedVideoData, mReader->SizeOfVideoQueueInFrames(), false,
+ media::TimeUnit().ToMicroseconds());
- SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
- !!mSeekedVideoData, mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
- currentTime.ToMicroseconds());
-
- mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
+ mReader->RequestVideoData(false, media::TimeUnit());
}
nsresult
-SeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
+AccurateSeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
{
AssertOwnerThread();
+
RefPtr<AudioData> audio(aSample->As<AudioData>());
MOZ_ASSERT(audio && mSeekJob.Exists() && mSeekJob.mTarget.IsAccurate());
CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
if (!sampleDuration.isValid()) {
return NS_ERROR_FAILURE;
}
@@ -373,19 +300,20 @@ SeekTask::DropAudioUpToSeekTarget(MediaD
audio->mRate));
MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
mSeekedAudioData = data;
return NS_OK;
}
nsresult
-SeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
+AccurateSeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
{
AssertOwnerThread();
+
RefPtr<VideoData> video(aSample->As<VideoData>());
MOZ_ASSERT(video);
DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
video->mTime, video->GetEndTime());
MOZ_ASSERT(mSeekJob.Exists());
const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
// If the frame end time is less than the seek target, we won't want
@@ -410,45 +338,43 @@ SeekTask::DropVideoUpToSeekTarget(MediaD
MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
mSeekedVideoData = video;
}
return NS_OK;
}
bool
-SeekTask::IsAudioSeekComplete()
+AccurateSeekTask::IsAudioSeekComplete()
{
AssertOwnerThread();
-
SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData);
return
!HasAudio() ||
mSeekJob.mTarget.IsVideoOnly() ||
(Exists() && !mDropAudioUntilNextDiscontinuity &&
(mIsAudioQueueFinished || mSeekedAudioData));
}
bool
-SeekTask::IsVideoSeekComplete()
+AccurateSeekTask::IsVideoSeekComplete()
{
AssertOwnerThread();
-
SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
mSeekJob.Exists(), mDropVideoUntilNextDiscontinuity, mIsVideoQueueFinished, !!mSeekedVideoData);
return
!HasVideo() ||
(Exists() && !mDropVideoUntilNextDiscontinuity &&
(mIsVideoQueueFinished || mSeekedVideoData));
}
void
-SeekTask::CheckIfSeekComplete()
+AccurateSeekTask::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");
@@ -469,41 +395,44 @@ SeekTask::CheckIfSeekComplete()
audioSeekComplete, videoSeekComplete);
if (audioSeekComplete && videoSeekComplete) {
Resolve(__func__); // Call to MDSM::SeekCompleted();
}
}
void
-SeekTask::OnSeekResolved(media::TimeUnit)
+AccurateSeekTask::OnSeekResolved(media::TimeUnit)
{
AssertOwnerThread();
+
mSeekRequest.Complete();
// We must decode the first samples of active streams, so we can determine
// the new stream time. So dispatch tasks to do that.
EnsureVideoDecodeTaskQueued();
if (!mSeekJob.mTarget.IsVideoOnly()) {
EnsureAudioDecodeTaskQueued();
}
}
void
-SeekTask::OnSeekRejected(nsresult aResult)
+AccurateSeekTask::OnSeekRejected(nsresult aResult)
{
AssertOwnerThread();
+
mSeekRequest.Complete();
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
RejectIfExist(__func__);
}
void
-SeekTask::OnAudioDecoded(MediaData* aAudioSample)
+AccurateSeekTask::OnAudioDecoded(MediaData* aAudioSample)
{
AssertOwnerThread();
+
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 ? audio->mTime : -1),
@@ -544,17 +473,17 @@ SeekTask::OnAudioDecoded(MediaData* aAud
return;
}
}
}
CheckIfSeekComplete();
}
void
-SeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+AccurateSeekTask::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;
@@ -582,19 +511,20 @@ SeekTask::OnAudioNotDecoded(MediaDecoder
if (aReason == MediaDecoderReader::END_OF_STREAM) {
mIsAudioQueueFinished = true;
mDropAudioUntilNextDiscontinuity = false; // To make IsAudioSeekComplete() return TRUE.
CheckIfSeekComplete();
}
}
void
-SeekTask::OnVideoDecoded(MediaData* aVideoSample)
+AccurateSeekTask::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),
@@ -637,17 +567,17 @@ SeekTask::OnVideoDecoded(MediaData* aVid
return;
}
}
}
CheckIfSeekComplete();
}
void
-SeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+AccurateSeekTask::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;
@@ -685,55 +615,65 @@ SeekTask::OnVideoNotDecoded(MediaDecoder
mIsVideoQueueFinished = true;
mDropVideoUntilNextDiscontinuity = false; // To make IsVideoSeekComplete() return TRUE.
CheckIfSeekComplete();
}
}
void
-SeekTask::SetMediaDecoderReaderWrapperCallback()
+AccurateSeekTask::SetMediaDecoderReaderWrapperCallback()
{
+ AssertOwnerThread();
+
mAudioCallbackID =
- mReader->SetAudioCallback(this, &SeekTask::OnAudioDecoded,
- &SeekTask::OnAudioNotDecoded);
+ mReader->SetAudioCallback(this, &AccurateSeekTask::OnAudioDecoded,
+ &AccurateSeekTask::OnAudioNotDecoded);
mVideoCallbackID =
- mReader->SetVideoCallback(this, &SeekTask::OnVideoDecoded,
- &SeekTask::OnVideoNotDecoded);
+ mReader->SetVideoCallback(this, &AccurateSeekTask::OnVideoDecoded,
+ &AccurateSeekTask::OnVideoNotDecoded);
- RefPtr<SeekTask> self = this;
+ RefPtr<AccurateSeekTask> self = this;
mWaitAudioCallbackID =
mReader->SetWaitAudioCallback(
[self] (MediaData::Type aType) -> void {
+ self->AssertOwnerThread();
self->EnsureAudioDecodeTaskQueued();
},
- [self] (WaitForDataRejectValue aRejection) -> void {});
+ [self] (WaitForDataRejectValue aRejection) -> void {
+ self->AssertOwnerThread();
+ });
mWaitVideoCallbackID =
mReader->SetWaitVideoCallback(
[self] (MediaData::Type aType) -> void {
+ self->AssertOwnerThread();
self->EnsureVideoDecodeTaskQueued();
},
- [self] (WaitForDataRejectValue aRejection) -> void {});
+ [self] (WaitForDataRejectValue aRejection) -> void {
+ self->AssertOwnerThread();
+ });
DECODER_LOG("SeekTask set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
DECODER_LOG("SeekTask set video callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
DECODER_LOG("SeekTask set wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
DECODER_LOG("SeekTask set wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
}
void
-SeekTask::CancelMediaDecoderReaderWrapperCallback()
+AccurateSeekTask::CancelMediaDecoderReaderWrapperCallback()
{
- DECODER_LOG("SeekTask cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
- mReader->CancelAudioCallback(mAudioCallbackID);
+ AssertOwnerThread();
+
+ DECODER_LOG("SeekTask cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
+ mReader->CancelAudioCallback(mAudioCallbackID);
- DECODER_LOG("SeekTask cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
- mReader->CancelVideoCallback(mVideoCallbackID);
+ DECODER_LOG("SeekTask cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
+ mReader->CancelVideoCallback(mVideoCallbackID);
- DECODER_LOG("SeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
- mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
+ DECODER_LOG("SeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
+ mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
- DECODER_LOG("SeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
- mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
+ DECODER_LOG("SeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
+ mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
}
} // namespace mozilla
copy from dom/media/SeekTask.h
copy to dom/media/AccurateSeekTask.h
--- a/dom/media/SeekTask.h
+++ b/dom/media/AccurateSeekTask.h
@@ -1,100 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef SEEK_TASK_H
-#define SEEK_TASK_H
+#ifndef ACCURATE_SEEK_TASK_H
+#define ACCURATE_SEEK_TASK_H
-#include "mozilla/MozPromise.h"
+#include "SeekTask.h"
#include "MediaCallbackID.h"
#include "MediaDecoderReader.h"
#include "SeekJob.h"
namespace mozilla {
-class AbstractThread;
-class MediaData;
-class MediaDecoderReaderWrapper;
-
-struct SeekTaskResolveValue
-{
- RefPtr<MediaData> mSeekedAudioData;
- RefPtr<MediaData> mSeekedVideoData;
- bool mIsAudioQueueFinished;
- bool mIsVideoQueueFinished;
- bool mNeedToStopPrerollingAudio;
- bool mNeedToStopPrerollingVideo;
-};
-
-struct SeekTaskRejectValue
-{
- bool mIsAudioQueueFinished;
- bool mIsVideoQueueFinished;
- bool mNeedToStopPrerollingAudio;
- bool mNeedToStopPrerollingVideo;
-};
-
-class SeekTask {
-
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
-
+class AccurateSeekTask final : public SeekTask {
public:
- static const bool IsExclusive = true;
-
- using SeekTaskPromise =
- MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
+ AccurateSeekTask(const void* aDecoderID,
+ AbstractThread* aThread,
+ MediaDecoderReaderWrapper* aReader,
+ SeekJob&& aSeekJob,
+ const MediaInfo& aInfo,
+ const media::TimeUnit& aDuration,
+ int64_t aCurrentMediaTime);
- static already_AddRefed<SeekTask>
- CreateSeekTask(const void* aDecoderID,
- AbstractThread* aThread,
- MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime);
+ void Discard() override;
- virtual void Discard();
-
- virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration);
-
- virtual bool NeedToResetMDSM() const;
-
- SeekJob& GetSeekJob();
+ RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
- bool Exists() const;
+ bool NeedToResetMDSM() const override;
-protected:
- SeekTask(const void* aDecoderID,
- AbstractThread* aThread,
- MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime);
-
- virtual ~SeekTask();
-
- void Resolve(const char* aCallSite);
-
- void RejectIfExist(const char* aCallSite);
-
- void AssertOwnerThread() const;
+private:
+ ~AccurateSeekTask();
bool HasAudio() const;
bool HasVideo() const;
- TaskQueue* DecodeTaskQueue() const;
-
- AbstractThread* OwnerThread() const;
-
bool IsVideoDecoding() const;
bool IsAudioDecoding() const;
nsresult EnsureVideoDecodeTaskQueued();
nsresult EnsureAudioDecodeTaskQueued();
@@ -111,73 +58,53 @@ protected:
nsresult DropVideoUpToSeekTarget(MediaData* aSample);
bool IsAudioSeekComplete();
bool IsVideoSeekComplete();
void CheckIfSeekComplete();
- virtual void OnSeekResolved(media::TimeUnit);
+ void OnSeekResolved(media::TimeUnit);
- virtual void OnSeekRejected(nsresult aResult);
+ void OnSeekRejected(nsresult aResult);
- virtual void OnAudioDecoded(MediaData* aAudioSample);
+ void OnAudioDecoded(MediaData* aAudioSample);
- virtual void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+ void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
- virtual void OnVideoDecoded(MediaData* aVideoSample);
+ void OnVideoDecoded(MediaData* aVideoSample);
- virtual void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+ void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
void SetMediaDecoderReaderWrapperCallback();
void CancelMediaDecoderReaderWrapperCallback();
/*
- * Data shared with MDSM.
- */
- const void* mDecoderID; // For logging.
- const RefPtr<AbstractThread> mOwnerThread;
- const RefPtr<MediaDecoderReaderWrapper> mReader;
-
- /*
* Internal state.
*/
- SeekJob mSeekJob;
- MozPromiseHolder<SeekTaskPromise> mSeekTaskPromise;
- int64_t mCurrentTimeBeforeSeek;
+ const int64_t mCurrentTimeBeforeSeek;
const uint32_t mAudioRate; // Audio sample rate.
const bool mHasAudio;
const bool mHasVideo;
bool mDropAudioUntilNextDiscontinuity;
bool mDropVideoUntilNextDiscontinuity;
- bool mIsDiscarded;
// This temporarily stores the first frame we decode after we seek.
// This is so that if we hit end of stream while we're decoding to reach
// the seek target, we will still have a frame that we can display as the
// last frame in the media.
RefPtr<MediaData> mFirstVideoFrameAfterSeek;
/*
* Track the current seek promise made by the reader.
*/
MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
CallbackID mAudioCallbackID;
CallbackID mVideoCallbackID;
CallbackID mWaitAudioCallbackID;
CallbackID mWaitVideoCallbackID;
-
- /*
- * Information which are going to be returned to MDSM.
- */
- RefPtr<MediaData> mSeekedAudioData;
- RefPtr<MediaData> mSeekedVideoData;
- bool mIsAudioQueueFinished;
- bool mIsVideoQueueFinished;
- bool mNeedToStopPrerollingAudio;
- bool mNeedToStopPrerollingVideo;
};
} // namespace mozilla
-#endif /* SEEK_TASK_H */
+#endif /* ACCURATE_SEEK_TASK_H */
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -32,16 +32,17 @@
#include "nsContentUtils.h"
#include "nsIEventTarget.h"
#include "nsITimer.h"
#include "nsPrintfCString.h"
#include "nsTArray.h"
#include "nsDeque.h"
#include "prenv.h"
+#include "AccurateSeekTask.h"
#include "AudioSegment.h"
#include "DOMMediaStream.h"
#include "ImageContainer.h"
#include "MediaDecoder.h"
#include "MediaDecoderReader.h"
#include "MediaDecoderReaderWrapper.h"
#include "MediaDecoderStateMachine.h"
#include "MediaShutdownManager.h"
@@ -1389,19 +1390,19 @@ void MediaDecoderStateMachine::InitiateV
DiscardSeekTaskIfExist();
mSeekTaskRequest.DisconnectIfExists();
// SeekTask will register its callbacks to MediaDecoderReaderWrapper.
CancelMediaDecoderReaderWrapperCallback();
// Create a new SeekTask instance for the incoming seek task.
- mSeekTask = SeekTask::CreateSeekTask(mDecoderID, OwnerThread(),
- mReader.get(), Move(seekJob),
- mInfo, Duration(), GetMediaTime());
+ mSeekTask = new AccurateSeekTask(mDecoderID, OwnerThread(),
+ mReader.get(), Move(seekJob),
+ mInfo, Duration(), GetMediaTime());
mOnSeekingStart.Notify(MediaDecoderEventVisibility::Suppressed);
// Reset our state machine and decoding pipeline before seeking.
if (mSeekTask->NeedToResetMDSM()) {
Reset(MediaDecoderReader::VIDEO_ONLY);
}
@@ -1574,19 +1575,19 @@ MediaDecoderStateMachine::InitiateSeek(S
DiscardSeekTaskIfExist();
mSeekTaskRequest.DisconnectIfExists();
// SeekTask will register its callbacks to MediaDecoderReaderWrapper.
CancelMediaDecoderReaderWrapperCallback();
// Create a new SeekTask instance for the incoming seek task.
- mSeekTask = SeekTask::CreateSeekTask(mDecoderID, OwnerThread(),
- mReader.get(), Move(aSeekJob),
- mInfo, Duration(), GetMediaTime());
+ mSeekTask = new AccurateSeekTask(mDecoderID, OwnerThread(),
+ mReader.get(), Move(aSeekJob),
+ mInfo, Duration(), GetMediaTime());
// Stop playback now to ensure that while we're outside the monitor
// dispatching SeekingStarted, playback doesn't advance and mess with
// mCurrentPosition that we've setting to seekTime here.
StopPlayback();
UpdatePlaybackPositionInternal(mSeekTask->GetSeekJob().mTarget.GetTime().ToMicroseconds());
mOnSeekingStart.Notify(mSeekTask->GetSeekJob().mTarget.mEventVisibility);
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/SeekTask.cpp
@@ -2,738 +2,96 @@
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SeekTask.h"
#include "MediaDecoderReaderWrapper.h"
#include "mozilla/AbstractThread.h"
-#include "mozilla/Assertions.h"
-#include "nsPrintfCString.h"
namespace mozilla {
-extern LazyLogModule gMediaDecoderLog;
-extern LazyLogModule gMediaSampleLog;
-
-// avoid redefined macro in unified build
-#undef LOG
-#undef DECODER_LOG
-#undef VERBOSE_LOG
-
-#define LOG(m, l, x, ...) \
- MOZ_LOG(m, l, ("[SeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
-#define DECODER_LOG(x, ...) \
- LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
-#define VERBOSE_LOG(x, ...) \
- LOG(gMediaDecoderLog, LogLevel::Verbose, x, ##__VA_ARGS__)
-#define SAMPLE_LOG(x, ...) \
- LOG(gMediaSampleLog, LogLevel::Debug, x, ##__VA_ARGS__)
-
-// Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
-// when __VA_ARGS__ expands to nothing. This is a workaround for it.
-#define DECODER_WARN_HELPER(a, b) NS_WARNING b
-#define DECODER_WARN(x, ...) \
- DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get()))
-
-/*static*/ already_AddRefed<SeekTask>
-SeekTask::CreateSeekTask(const void* aDecoderID,
- AbstractThread* aThread,
- MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime)
-{
- RefPtr<SeekTask> task(new SeekTask(aDecoderID, aThread,
- aReader, Move(aSeekJob), aInfo,
- aDuration, aCurrentMediaTime));
-
- return task.forget();
-}
-
SeekTask::SeekTask(const void* aDecoderID,
AbstractThread* aThread,
MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime)
+ SeekJob&& aSeekJob)
: mDecoderID(aDecoderID)
, mOwnerThread(aThread)
, mReader(aReader)
, mSeekJob(Move(aSeekJob))
- , mCurrentTimeBeforeSeek(aCurrentMediaTime)
- , mAudioRate(aInfo.mAudio.mRate)
- , mHasAudio(aInfo.HasAudio())
- , mHasVideo(aInfo.HasVideo())
- , mDropAudioUntilNextDiscontinuity(false)
- , mDropVideoUntilNextDiscontinuity(false)
, mIsDiscarded(false)
, mIsAudioQueueFinished(false)
, mIsVideoQueueFinished(false)
, mNeedToStopPrerollingAudio(false)
, mNeedToStopPrerollingVideo(false)
{
- // Bound the seek time to be inside the media range.
- int64_t end = aDuration.ToMicroseconds();
- NS_ASSERTION(end != -1, "Should know end time by now");
- int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
- seekTime = std::min(seekTime, end);
- seekTime = std::max(int64_t(0), seekTime);
- NS_ASSERTION(seekTime >= 0 && seekTime <= end,
- "Can only seek in range [0,duration]");
- mSeekJob.mTarget.SetTime(media::TimeUnit::FromMicroseconds(seekTime));
-
- mDropAudioUntilNextDiscontinuity = HasAudio();
- mDropVideoUntilNextDiscontinuity = HasVideo();
-
- // Configure MediaDecoderReaderWrapper.
- SetMediaDecoderReaderWrapperCallback();
+ AssertOwnerThread();
}
SeekTask::~SeekTask()
{
+ AssertOwnerThread();
MOZ_ASSERT(mIsDiscarded);
}
void
SeekTask::Resolve(const char* aCallSite)
{
+ AssertOwnerThread();
+
SeekTaskResolveValue val;
val.mSeekedAudioData = mSeekedAudioData;
val.mSeekedVideoData = mSeekedVideoData;
val.mIsAudioQueueFinished = mIsAudioQueueFinished;
val.mIsVideoQueueFinished = mIsVideoQueueFinished;
val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
mSeekTaskPromise.Resolve(val, aCallSite);
}
void
SeekTask::RejectIfExist(const char* aCallSite)
{
+ AssertOwnerThread();
+
SeekTaskRejectValue val;
val.mIsAudioQueueFinished = mIsAudioQueueFinished;
val.mIsVideoQueueFinished = mIsVideoQueueFinished;
val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
mSeekTaskPromise.RejectIfExists(val, aCallSite);
}
void
SeekTask::AssertOwnerThread() const
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
}
-bool
-SeekTask::HasAudio() const
-{
- return mHasAudio;
-}
-
-bool
-SeekTask::HasVideo() const
-{
- return mHasVideo;
-}
-
AbstractThread*
SeekTask::OwnerThread() const
{
+ AssertOwnerThread();
return mOwnerThread;
}
-void
-SeekTask::Discard()
-{
- // Disconnect MediaDecoder.
- mSeekJob.RejectIfExists(__func__);
-
- // Disconnect MDSM.
- RejectIfExist(__func__);
-
- // Disconnect MediaDecoderReaderWrapper.
- mSeekRequest.DisconnectIfExists();
- CancelMediaDecoderReaderWrapperCallback();
-
- mIsDiscarded = true;
-}
-
-bool
-SeekTask::NeedToResetMDSM() const
-{
- return true;
-}
-
SeekJob&
SeekTask::GetSeekJob()
{
+ AssertOwnerThread();
return mSeekJob;
}
bool
SeekTask::Exists() const
{
+ AssertOwnerThread();
+
// mSeekTaskPromise communicates SeekTask and MDSM;
// mSeekJob communicates MDSM and MediaDecoder;
// Either one exists means the current seek task has yet finished.
return !mSeekTaskPromise.IsEmpty() || mSeekJob.Exists();
}
-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__);
-}
-
-bool
-SeekTask::IsVideoDecoding() const
-{
- AssertOwnerThread();
- return HasVideo() && !mIsVideoQueueFinished;
-}
-
-bool
-SeekTask::IsAudioDecoding() const
-{
- AssertOwnerThread();
- return HasAudio() && !mIsAudioQueueFinished;
-}
-
-nsresult
-SeekTask::EnsureAudioDecodeTaskQueued()
-{
- AssertOwnerThread();
-
- SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
- IsAudioDecoding(), AudioRequestStatus());
-
- if (!IsAudioDecoding() ||
- mReader->IsRequestingAudioData() ||
- mReader->IsWaitingAudioData() ||
- mSeekRequest.Exists()) {
- return NS_OK;
- }
-
- RequestAudioData();
- return NS_OK;
-}
-
-nsresult
-SeekTask::EnsureVideoDecodeTaskQueued()
-{
- AssertOwnerThread();
-
- SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
- IsVideoDecoding(), VideoRequestStatus());
-
- if (!IsVideoDecoding() ||
- mReader->IsRequestingVideoData() ||
- mReader->IsWaitingVideoData() ||
- mSeekRequest.Exists()) {
- return NS_OK;
- }
-
- RequestVideoData();
- return NS_OK;
-}
-
-const char*
-SeekTask::AudioRequestStatus()
-{
- AssertOwnerThread();
- if (mReader->IsRequestingAudioData()) {
- MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingAudioData());
- return "pending";
- } else if (mReader->IsWaitingAudioData()) {
- return "waiting";
- }
- return "idle";
-}
-
-const char*
-SeekTask::VideoRequestStatus()
-{
- AssertOwnerThread();
- if (mReader->IsRequestingVideoData()) {
- MOZ_DIAGNOSTIC_ASSERT(!mReader->IsWaitingVideoData());
- return "pending";
- } else if (mReader->IsWaitingVideoData()) {
- return "waiting";
- }
- return "idle";
-}
-
-void
-SeekTask::RequestAudioData()
-{
- AssertOwnerThread();
-
- SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
- !!mSeekedAudioData, mReader->SizeOfAudioQueueInFrames());
-
- mReader->RequestAudioData();
-}
-
-void
-SeekTask::RequestVideoData()
-{
- AssertOwnerThread();
- //These two variables are not used in the SEEKING state.
- const bool skipToNextKeyFrame = false;
- const media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(0);
-
- SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
- !!mSeekedVideoData, mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
- currentTime.ToMicroseconds());
-
- mReader->RequestVideoData(skipToNextKeyFrame, currentTime);
-}
-
-nsresult
-SeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
-{
- AssertOwnerThread();
- RefPtr<AudioData> audio(aSample->As<AudioData>());
- MOZ_ASSERT(audio && mSeekJob.Exists() && mSeekJob.mTarget.IsAccurate());
-
- CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
- if (!sampleDuration.isValid()) {
- return NS_ERROR_FAILURE;
- }
-
- if (audio->mTime + sampleDuration.value() <= mSeekJob.mTarget.GetTime().ToMicroseconds()) {
- // Our seek target lies after the frames in this AudioData. Don't
- // push it onto the audio queue, and keep decoding forwards.
- return NS_OK;
- }
-
- if (audio->mTime > mSeekJob.mTarget.GetTime().ToMicroseconds()) {
- // The seek target doesn't lie in the audio block just after the last
- // audio frames we've seen which were before the seek target. This
- // could have been the first audio data we've seen after seek, i.e. the
- // seek terminated after the seek target in the audio stream. Just
- // abort the audio decode-to-target, the state machine will play
- // silence to cover the gap. Typically this happens in poorly muxed
- // files.
- DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
- mSeekedAudioData = audio;
- return NS_OK;
- }
-
- // The seek target lies somewhere in this AudioData's frames, strip off
- // any frames which lie before the seek target, so we'll begin playback
- // exactly at the seek target.
- NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() >= audio->mTime,
- "Target must at or be after data start.");
- NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
- "Data must end after target.");
-
- CheckedInt64 framesToPrune =
- UsecsToFrames(mSeekJob.mTarget.GetTime().ToMicroseconds() - audio->mTime, mAudioRate);
- if (!framesToPrune.isValid()) {
- return NS_ERROR_FAILURE;
- }
- if (framesToPrune.value() > audio->mFrames) {
- // We've messed up somehow. Don't try to trim frames, the |frames|
- // variable below will overflow.
- DECODER_WARN("Can't prune more frames that we have!");
- return NS_ERROR_FAILURE;
- }
- uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
- uint32_t channels = audio->mChannels;
- AlignedAudioBuffer audioData(frames * channels);
- if (!audioData) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- memcpy(audioData.get(),
- audio->mAudioData.get() + (framesToPrune.value() * channels),
- frames * channels * sizeof(AudioDataValue));
- CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
- if (!duration.isValid()) {
- return NS_ERROR_FAILURE;
- }
- RefPtr<AudioData> data(new AudioData(audio->mOffset,
- mSeekJob.mTarget.GetTime().ToMicroseconds(),
- duration.value(),
- frames,
- Move(audioData),
- channels,
- audio->mRate));
- MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
- mSeekedAudioData = data;
-
- return NS_OK;
-}
-
-nsresult
-SeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
-{
- AssertOwnerThread();
- RefPtr<VideoData> video(aSample->As<VideoData>());
- MOZ_ASSERT(video);
- DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
- video->mTime, video->GetEndTime());
- MOZ_ASSERT(mSeekJob.Exists());
- const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
-
- // If the frame end time is less than the seek target, we won't want
- // to display this frame after the seek, so discard it.
- if (target >= video->GetEndTime()) {
- DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
- video->mTime, video->GetEndTime(), target);
- mFirstVideoFrameAfterSeek = video;
- } else {
- if (target >= video->mTime && video->GetEndTime() >= target) {
- // The seek target lies inside this frame's time slice. Adjust the frame's
- // start time to match the seek target. We do this by replacing the
- // first frame with a shallow copy which has the new timestamp.
- RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
- video = temp;
- }
- mFirstVideoFrameAfterSeek = nullptr;
-
- DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
- video->mTime, video->GetEndTime(), target);
-
- MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
- mSeekedVideoData = video;
- }
-
- return NS_OK;
-}
-
-bool
-SeekTask::IsAudioSeekComplete()
-{
- AssertOwnerThread();
-
- SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
- mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData);
- return
- !HasAudio() ||
- mSeekJob.mTarget.IsVideoOnly() ||
- (Exists() && !mDropAudioUntilNextDiscontinuity &&
- (mIsAudioQueueFinished || mSeekedAudioData));
-}
-
-bool
-SeekTask::IsVideoSeekComplete()
-{
- AssertOwnerThread();
-
- SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
- mSeekJob.Exists(), mDropVideoUntilNextDiscontinuity, mIsVideoQueueFinished, !!mSeekedVideoData);
-
- return
- !HasVideo() ||
- (Exists() && !mDropVideoUntilNextDiscontinuity &&
- (mIsVideoQueueFinished || mSeekedVideoData));
-}
-
-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::OnSeekResolved(media::TimeUnit)
-{
- AssertOwnerThread();
- mSeekRequest.Complete();
- // We must decode the first samples of active streams, so we can determine
- // the new stream time. So dispatch tasks to do that.
- EnsureVideoDecodeTaskQueued();
- if (!mSeekJob.mTarget.IsVideoOnly()) {
- EnsureAudioDecodeTaskQueued();
- }
-}
-
-void
-SeekTask::OnSeekRejected(nsresult aResult)
-{
- AssertOwnerThread();
- mSeekRequest.Complete();
- MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
- RejectIfExist(__func__);
-}
-
-void
-SeekTask::OnAudioDecoded(MediaData* aAudioSample)
-{
- AssertOwnerThread();
- 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 ? audio->mTime : -1),
- (audio ? audio->GetEndTime() : -1),
- (audio ? audio->mDiscontinuity : 0));
-
- if (!Exists()) {
- // We've received a sample from a previous decode. Discard it.
- return;
- }
-
- if (audio->mDiscontinuity) {
- mDropAudioUntilNextDiscontinuity = false;
- }
-
- if (!mDropAudioUntilNextDiscontinuity) {
- // 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 &&
- audio->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.
- mSeekedAudioData = audio;
- } else {
- // We're doing an accurate seek. We must discard
- // MediaData up to the one containing exact seek target.
- if (NS_FAILED(DropAudioUpToSeekTarget(audio.get()))) {
- RejectIfExist(__func__);
- return;
- }
- }
- }
- CheckIfSeekComplete();
-}
-
-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();
- }
-}
-
-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
-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
-SeekTask::SetMediaDecoderReaderWrapperCallback()
-{
- mAudioCallbackID =
- mReader->SetAudioCallback(this, &SeekTask::OnAudioDecoded,
- &SeekTask::OnAudioNotDecoded);
-
- mVideoCallbackID =
- mReader->SetVideoCallback(this, &SeekTask::OnVideoDecoded,
- &SeekTask::OnVideoNotDecoded);
-
- RefPtr<SeekTask> self = this;
- mWaitAudioCallbackID =
- mReader->SetWaitAudioCallback(
- [self] (MediaData::Type aType) -> void {
- self->EnsureAudioDecodeTaskQueued();
- },
- [self] (WaitForDataRejectValue aRejection) -> void {});
-
- mWaitVideoCallbackID =
- mReader->SetWaitVideoCallback(
- [self] (MediaData::Type aType) -> void {
- self->EnsureVideoDecodeTaskQueued();
- },
- [self] (WaitForDataRejectValue aRejection) -> void {});
-
- DECODER_LOG("SeekTask set audio callbacks: mAudioCallbackID = %d\n", (int)mAudioCallbackID);
- DECODER_LOG("SeekTask set video callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
- DECODER_LOG("SeekTask set wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
- DECODER_LOG("SeekTask set wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
-}
-
-void
-SeekTask::CancelMediaDecoderReaderWrapperCallback()
-{
- DECODER_LOG("SeekTask cancel audio callbacks: mVideoCallbackID = %d\n", (int)mAudioCallbackID);
- mReader->CancelAudioCallback(mAudioCallbackID);
-
- DECODER_LOG("SeekTask cancel video callbacks: mVideoCallbackID = %d\n", (int)mVideoCallbackID);
- mReader->CancelVideoCallback(mVideoCallbackID);
-
- DECODER_LOG("SeekTask cancel wait audio callbacks: mWaitAudioCallbackID = %d\n", (int)mWaitAudioCallbackID);
- mReader->CancelWaitAudioCallback(mWaitAudioCallbackID);
-
- DECODER_LOG("SeekTask cancel wait video callbacks: mWaitVideoCallbackID = %d\n", (int)mWaitVideoCallbackID);
- mReader->CancelWaitVideoCallback(mWaitVideoCallbackID);
-}
} // namespace mozilla
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -4,24 +4,28 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef SEEK_TASK_H
#define SEEK_TASK_H
#include "mozilla/MozPromise.h"
#include "MediaCallbackID.h"
-#include "MediaDecoderReader.h"
#include "SeekJob.h"
namespace mozilla {
class AbstractThread;
class MediaData;
class MediaDecoderReaderWrapper;
+class MediaInfo;
+
+namespace media {
+class TimeUnit;
+}
struct SeekTaskResolveValue
{
RefPtr<MediaData> mSeekedAudioData;
RefPtr<MediaData> mSeekedVideoData;
bool mIsAudioQueueFinished;
bool mIsVideoQueueFinished;
bool mNeedToStopPrerollingAudio;
@@ -41,137 +45,56 @@ class SeekTask {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
public:
static const bool IsExclusive = true;
using SeekTaskPromise =
MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
- static already_AddRefed<SeekTask>
- CreateSeekTask(const void* aDecoderID,
- AbstractThread* aThread,
- MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime);
+ virtual void Discard() = 0;
- virtual void Discard();
+ virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) = 0;
- virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration);
-
- virtual bool NeedToResetMDSM() const;
+ virtual bool NeedToResetMDSM() const = 0;
SeekJob& GetSeekJob();
bool Exists() const;
protected:
SeekTask(const void* aDecoderID,
AbstractThread* aThread,
MediaDecoderReaderWrapper* aReader,
- SeekJob&& aSeekJob,
- const MediaInfo& aInfo,
- const media::TimeUnit& aDuration,
- int64_t aCurrentMediaTime);
+ SeekJob&& aSeekJob);
virtual ~SeekTask();
void Resolve(const char* aCallSite);
void RejectIfExist(const char* aCallSite);
void AssertOwnerThread() const;
- bool HasAudio() const;
-
- bool HasVideo() const;
-
- TaskQueue* DecodeTaskQueue() const;
-
AbstractThread* OwnerThread() const;
- bool IsVideoDecoding() const;
-
- bool IsAudioDecoding() const;
-
- nsresult EnsureVideoDecodeTaskQueued();
-
- nsresult EnsureAudioDecodeTaskQueued();
-
- const char* AudioRequestStatus();
-
- const char* VideoRequestStatus();
-
- void RequestVideoData();
-
- void RequestAudioData();
-
- nsresult DropAudioUpToSeekTarget(MediaData* aSample);
-
- nsresult DropVideoUpToSeekTarget(MediaData* aSample);
-
- bool IsAudioSeekComplete();
-
- bool IsVideoSeekComplete();
-
- void CheckIfSeekComplete();
-
- virtual void OnSeekResolved(media::TimeUnit);
-
- virtual void OnSeekRejected(nsresult aResult);
-
- virtual void OnAudioDecoded(MediaData* aAudioSample);
-
- virtual void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
-
- virtual void OnVideoDecoded(MediaData* aVideoSample);
-
- virtual void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
-
- void SetMediaDecoderReaderWrapperCallback();
-
- void CancelMediaDecoderReaderWrapperCallback();
-
/*
* Data shared with MDSM.
*/
const void* mDecoderID; // For logging.
const RefPtr<AbstractThread> mOwnerThread;
const RefPtr<MediaDecoderReaderWrapper> mReader;
/*
* Internal state.
*/
SeekJob mSeekJob;
MozPromiseHolder<SeekTaskPromise> mSeekTaskPromise;
- int64_t mCurrentTimeBeforeSeek;
- const uint32_t mAudioRate; // Audio sample rate.
- const bool mHasAudio;
- const bool mHasVideo;
- bool mDropAudioUntilNextDiscontinuity;
- bool mDropVideoUntilNextDiscontinuity;
bool mIsDiscarded;
- // This temporarily stores the first frame we decode after we seek.
- // This is so that if we hit end of stream while we're decoding to reach
- // the seek target, we will still have a frame that we can display as the
- // last frame in the media.
- RefPtr<MediaData> mFirstVideoFrameAfterSeek;
-
- /*
- * Track the current seek promise made by the reader.
- */
- MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
- CallbackID mAudioCallbackID;
- CallbackID mVideoCallbackID;
- CallbackID mWaitAudioCallbackID;
- CallbackID mWaitVideoCallbackID;
-
/*
* Information which are going to be returned to MDSM.
*/
RefPtr<MediaData> mSeekedAudioData;
RefPtr<MediaData> mSeekedVideoData;
bool mIsAudioQueueFinished;
bool mIsVideoQueueFinished;
bool mNeedToStopPrerollingAudio;
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -79,16 +79,17 @@ XPIDL_SOURCES += [
'nsIDOMNavigatorUserMedia.idl',
'nsIMediaManager.idl',
]
XPIDL_MODULE = 'dom_media'
EXPORTS += [
'AbstractMediaDecoder.h',
+ 'AccurateSeekTask.h',
'ADTSDecoder.h',
'ADTSDemuxer.h',
'AudioBufferUtils.h',
'AudioChannelFormat.h',
'AudioCompactor.h',
'AudioConverter.h',
'AudioMixer.h',
'AudioPacketizer.h',
@@ -192,16 +193,17 @@ EXPORTS.mozilla.dom += [
'TextTrackRegion.h',
'VideoPlaybackQuality.h',
'VideoStreamTrack.h',
'VideoTrack.h',
'VideoTrackList.h',
]
UNIFIED_SOURCES += [
+ 'AccurateSeekTask.cpp',
'ADTSDecoder.cpp',
'ADTSDemuxer.cpp',
'AudioCaptureStream.cpp',
'AudioChannelFormat.cpp',
'AudioCompactor.cpp',
'AudioConverter.cpp',
'AudioSegment.cpp',
'AudioStream.cpp',