зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1276272 - part 3 - implement promise-based HTMLMediaElement::seekToNextFrame(); r=jwwang
MozReview-Commit-ID: JaUQe5GK6bF --HG-- extra : rebase_source : 63ffb58ca29776757cd869579e149a4330311e08
This commit is contained in:
Родитель
5b76709f3e
Коммит
9ace015fa5
|
@ -1466,19 +1466,19 @@ HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv)
|
|||
{
|
||||
LOG(LogLevel::Debug, ("Reporting telemetry VIDEO_FASTSEEK_USED"));
|
||||
Telemetry::Accumulate(Telemetry::VIDEO_FASTSEEK_USED, 1);
|
||||
Seek(aTime, SeekTarget::PrevSyncPoint, aRv);
|
||||
RefPtr<Promise> tobeDropped = Seek(aTime, SeekTarget::PrevSyncPoint, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::SeekToNextFrame(ErrorResult& aRv)
|
||||
{
|
||||
Seek(CurrentTime(), SeekTarget::NextFrame, aRv);
|
||||
return Seek(CurrentTime(), SeekTarget::NextFrame, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv)
|
||||
{
|
||||
Seek(aCurrentTime, SeekTarget::Accurate, aRv);
|
||||
RefPtr<Promise> tobeDropped = Seek(aCurrentTime, SeekTarget::Accurate, aRv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1519,7 +1519,7 @@ IsInRanges(dom::TimeRanges& aRanges,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::Seek(double aTime,
|
||||
SeekTarget::Type aSeekType,
|
||||
ErrorResult& aRv)
|
||||
|
@ -1527,6 +1527,19 @@ HTMLMediaElement::Seek(double aTime,
|
|||
// aTime should be non-NaN.
|
||||
MOZ_ASSERT(!mozilla::IsNaN(aTime));
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect if user has interacted with element by seeking so that
|
||||
// play will not be blocked when initiated by a script.
|
||||
if (EventStateManager::IsHandlingUserInput() || nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
|
||||
|
@ -1537,7 +1550,8 @@ HTMLMediaElement::Seek(double aTime,
|
|||
|
||||
if (mSrcStream) {
|
||||
// do nothing since media streams have an empty Seekable range.
|
||||
return;
|
||||
promise->MaybeRejectWithUndefined();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
if (mPlayed && mCurrentPlayRangeStart != -1.0) {
|
||||
|
@ -1554,27 +1568,30 @@ HTMLMediaElement::Seek(double aTime,
|
|||
|
||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||
mDefaultPlaybackStartPosition = aTime;
|
||||
return;
|
||||
promise->MaybeRejectWithUndefined();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
if (!mDecoder) {
|
||||
// mDecoder must always be set in order to reach this point.
|
||||
NS_ASSERTION(mDecoder, "SetCurrentTime failed: no decoder");
|
||||
return;
|
||||
promise->MaybeRejectWithUndefined();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Clamp the seek target to inside the seekable ranges.
|
||||
RefPtr<dom::TimeRanges> seekable = new dom::TimeRanges(ToSupports(OwnerDoc()));
|
||||
media::TimeIntervals seekableIntervals = mDecoder->GetSeekable();
|
||||
if (seekableIntervals.IsInvalid()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); // This will reject the promise.
|
||||
return promise.forget();
|
||||
}
|
||||
seekableIntervals.ToTimeRanges(seekable);
|
||||
uint32_t length = 0;
|
||||
seekable->GetLength(&length);
|
||||
if (!length) {
|
||||
return;
|
||||
promise->MaybeRejectWithUndefined();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// If the position we want to seek to is not in a seekable range, we seek
|
||||
|
@ -1585,8 +1602,8 @@ HTMLMediaElement::Seek(double aTime,
|
|||
int32_t range = 0;
|
||||
bool isInRange = false;
|
||||
if (NS_FAILED(IsInRanges(*seekable, aTime, isInRange, range))) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); // This will reject the promise.
|
||||
return promise.forget();
|
||||
}
|
||||
if (!isInRange) {
|
||||
if (range != -1) {
|
||||
|
@ -1596,11 +1613,11 @@ HTMLMediaElement::Seek(double aTime,
|
|||
double leftBound, rightBound;
|
||||
if (NS_FAILED(seekable->End(range, &leftBound))) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
return promise.forget();
|
||||
}
|
||||
if (NS_FAILED(seekable->Start(range + 1, &rightBound))) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
return promise.forget();
|
||||
}
|
||||
double distanceLeft = Abs(leftBound - aTime);
|
||||
double distanceRight = Abs(rightBound - aTime);
|
||||
|
@ -1615,7 +1632,7 @@ HTMLMediaElement::Seek(double aTime,
|
|||
// Clamp the seek target to the end of the last seekable range.
|
||||
if (NS_FAILED(seekable->End(length - 1, &aTime))) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
return promise.forget();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1641,13 +1658,15 @@ HTMLMediaElement::Seek(double aTime,
|
|||
// The media backend is responsible for dispatching the timeupdate
|
||||
// event if it changes the playback position as a result of the seek.
|
||||
LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) starting seek", this, aTime));
|
||||
nsresult rv = mDecoder->Seek(aTime, aSeekType);
|
||||
nsresult rv = mDecoder->Seek(aTime, aSeekType, promise);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
}
|
||||
|
||||
// We changed whether we're seeking so we need to AddRemoveSelfReference.
|
||||
AddRemoveSelfReference();
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)
|
||||
|
|
|
@ -482,7 +482,7 @@ public:
|
|||
|
||||
void FastSeek(double aTime, ErrorResult& aRv);
|
||||
|
||||
void SeekToNextFrame(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> SeekToNextFrame(ErrorResult& aRv);
|
||||
|
||||
double Duration() const;
|
||||
|
||||
|
@ -1125,7 +1125,7 @@ protected:
|
|||
// seek target, or PrevSyncPoint if a quicker but less precise seek is
|
||||
// desired, and we'll seek to the sync point (keyframe and/or start of the
|
||||
// next block of audio samples) preceeding seek target.
|
||||
void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
|
||||
|
||||
// A method to check if we are playing through the AudioChannel.
|
||||
bool IsPlayingThroughTheAudioChannel() const;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "mozilla/dom/AudioTrack.h"
|
||||
#include "mozilla/dom/AudioTrackList.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/VideoTrack.h"
|
||||
#include "mozilla/dom/VideoTrackList.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
@ -814,7 +815,7 @@ MediaDecoder::Play()
|
|||
}
|
||||
|
||||
nsresult
|
||||
MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
||||
MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType, dom::Promise* aPromise /*=nullptr*/)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE(!mShuttingDown, NS_ERROR_FAILURE);
|
||||
|
@ -831,7 +832,7 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
|||
|
||||
mLogicallySeeking = true;
|
||||
SeekTarget target = SeekTarget(timeUsecs, aSeekType);
|
||||
CallSeek(target);
|
||||
CallSeek(target, aPromise);
|
||||
|
||||
if (mPlayState == PLAY_STATE_ENDED) {
|
||||
PinForSeek();
|
||||
|
@ -841,10 +842,48 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
|
|||
}
|
||||
|
||||
void
|
||||
MediaDecoder::CallSeek(const SeekTarget& aTarget)
|
||||
MediaDecoder::AsyncResolveSeekDOMPromiseIfExists()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mSeekDOMPromise) {
|
||||
RefPtr<dom::Promise> promise = mSeekDOMPromise;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
});
|
||||
AbstractThread::MainThread()->Dispatch(r.forget());
|
||||
mSeekDOMPromise = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::AsyncRejectSeekDOMPromiseIfExists()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mSeekDOMPromise) {
|
||||
RefPtr<dom::Promise> promise = mSeekDOMPromise;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
|
||||
promise->MaybeRejectWithUndefined();
|
||||
});
|
||||
AbstractThread::MainThread()->Dispatch(r.forget());
|
||||
mSeekDOMPromise = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::DiscardOngoingSeekIfExists()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
AsyncRejectSeekDOMPromiseIfExists();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DiscardOngoingSeekIfExists();
|
||||
|
||||
mSeekDOMPromise = aPromise;
|
||||
mSeekRequest.Begin(
|
||||
mDecoderStateMachine->InvokeSeek(aTarget)
|
||||
->Then(AbstractThread::MainThread(), __func__, this,
|
||||
|
@ -1256,12 +1295,22 @@ MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
|
|||
|
||||
if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
|
||||
mOwner->SeekCompleted();
|
||||
AsyncResolveSeekDOMPromiseIfExists();
|
||||
if (fireEnded) {
|
||||
mOwner->PlaybackEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::OnSeekRejected()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSeekRequest.Complete();
|
||||
mLogicallySeeking = false;
|
||||
AsyncRejectSeekDOMPromiseIfExists();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoder::SeekingStarted(MediaDecoderEventVisibility aEventVisibility)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,10 @@ class nsIPrincipal;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Promise;
|
||||
}
|
||||
|
||||
class VideoFrameContainer;
|
||||
class MediaDecoderStateMachine;
|
||||
|
||||
|
@ -166,7 +170,8 @@ public:
|
|||
// Seek to the time position in (seconds) from the start of the video.
|
||||
// If aDoFastSeek is true, we'll seek to the sync point/keyframe preceeding
|
||||
// the seek target.
|
||||
virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType);
|
||||
virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType,
|
||||
dom::Promise* aPromise = nullptr);
|
||||
|
||||
// Initialize state machine and schedule it.
|
||||
nsresult InitializeStateMachine();
|
||||
|
@ -391,12 +396,7 @@ private:
|
|||
// Call on the main thread only.
|
||||
void PlaybackEnded();
|
||||
|
||||
void OnSeekRejected()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mSeekRequest.Complete();
|
||||
mLogicallySeeking = false;
|
||||
}
|
||||
void OnSeekRejected();
|
||||
void OnSeekResolved(SeekResolveValue aVal);
|
||||
|
||||
void SeekingChanged()
|
||||
|
@ -620,12 +620,24 @@ private:
|
|||
#endif
|
||||
|
||||
protected:
|
||||
virtual void CallSeek(const SeekTarget& aTarget);
|
||||
// The promise resolving/rejection is queued as a "micro-task" which will be
|
||||
// handled immediately after the current JS task and before any pending JS
|
||||
// tasks.
|
||||
// At the time we are going to resolve/reject a promise, the "seeking" event
|
||||
// task should already be queued but might yet be processed, so we queue one
|
||||
// more task to file the promise resolving/rejection micro-tasks
|
||||
// asynchronously to make sure that the micro-tasks are processed after the
|
||||
// "seeking" event task.
|
||||
void AsyncResolveSeekDOMPromiseIfExists();
|
||||
void AsyncRejectSeekDOMPromiseIfExists();
|
||||
void DiscardOngoingSeekIfExists();
|
||||
virtual void CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise);
|
||||
|
||||
// Returns true if heuristic dormant is supported.
|
||||
bool IsHeuristicDormantSupported() const;
|
||||
|
||||
MozPromiseRequestHolder<SeekPromise> mSeekRequest;
|
||||
RefPtr<dom::Promise> mSeekDOMPromise;
|
||||
|
||||
// True when seeking or otherwise moving the play position around in
|
||||
// such a manner that progress event data is inaccurate. This is set
|
||||
|
|
Загрузка…
Ссылка в новой задаче