Bug 1293646: [MSE] P2. Only reject a seek request with EOS if it's passed the explicit duration. r?gerald
With MSE, the actual duration is always exact as it is amended when data is added. We do not want to fire ended when we attempt to seek to unbuffered data once endOfStream has been called. Instead we will fire the waiting event.
MozReview-Commit-ID: Cl2uBLk2qRQ
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -56,16 +56,17 @@ public:
virtual MediaResource* GetResource() const = 0;
// Increments the parsed, decoded and dropped frame counters by the passed in
// counts.
// Can be called on any thread.
virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) = 0;
virtual AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() { return nullptr; };
+ virtual AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() { return nullptr; }
// Return an event that will be notified when data arrives in MediaResource.
// MediaDecoderReader will register with this event to receive notifications
// in order to update buffer ranges.
// Return null if this decoder doesn't support the event.
virtual MediaEventSource<void>* DataArrivedEvent()
{
return nullptr;
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -817,31 +817,31 @@ protected:
// True if the media is only seekable within its buffered ranges.
Canonical<bool> mMediaSeekableOnlyInBufferedRanges;
// True if the decoder is visible.
Canonical<bool> mIsVisible;
public:
AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
+ AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() override {
+ return &mExplicitDuration;
+ }
AbstractCanonical<double>* CanonicalVolume() {
return &mVolume;
}
AbstractCanonical<double>* CanonicalPlaybackRate() {
return &mPlaybackRate;
}
AbstractCanonical<bool>* CanonicalPreservesPitch() {
return &mPreservesPitch;
}
AbstractCanonical<media::NullableTimeUnit>* CanonicalEstimatedDuration() {
return &mEstimatedDuration;
}
- AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() {
- return &mExplicitDuration;
- }
AbstractCanonical<PlayState>* CanonicalPlayState() {
return &mPlayState;
}
AbstractCanonical<PlayState>* CanonicalNextPlayState() {
return &mNextState;
}
AbstractCanonical<bool>* CanonicalLogicallySeeking() {
return &mLogicallySeeking;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -72,16 +72,17 @@ MediaFormatReader::MediaFormatReader(Abs
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
, mLayersBackendType(aLayersBackend)
, mInitDone(false)
, mIsEncrypted(false)
, mTrackDemuxersMayBlock(false)
, mDemuxOnly(false)
, mSeekScheduled(false)
, mVideoFrameContainer(aVideoFrameContainer)
+ , mExplicitDuration(mTaskQueue, Maybe<double>(), "MediaFormatReader::mExplicitDuration(Mirror)")
{
MOZ_ASSERT(aDemuxer);
MOZ_COUNT_CTOR(MediaFormatReader);
}
MediaFormatReader::~MediaFormatReader()
{
MOZ_COUNT_DTOR(MediaFormatReader);
@@ -136,16 +137,18 @@ MediaFormatReader::Shutdown()
mVideo.mTaskQueue = nullptr;
}
MOZ_ASSERT(!mVideo.HasPromise());
mDemuxer = nullptr;
mPlatform = nullptr;
mVideoFrameContainer = nullptr;
+ mExplicitDuration.DisconnectIfConnected();
+
return MediaDecoderReader::Shutdown();
}
void
MediaFormatReader::InitLayersBackendType()
{
// Extract the layer manager backend type so that platform decoders
// can determine whether it's worthwhile using hardware accelerated
@@ -248,16 +251,20 @@ MediaFormatReader::AsyncReadMetadata()
if (mInitDone) {
// We are returning from dormant.
RefPtr<MetadataHolder> metadata = new MetadataHolder();
metadata->mInfo = mInfo;
metadata->mTags = nullptr;
return MetadataPromise::CreateAndResolve(metadata, __func__);
}
+ if (mDecoder->CanonicalExplicitDuration()) {
+ mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
+ }
+
RefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__);
mDemuxerInitRequest.Begin(mDemuxer->Init()
->Then(OwnerThread(), __func__, this,
&MediaFormatReader::OnDemuxerInitDone,
&MediaFormatReader::OnDemuxerInitFailed));
return p;
}
@@ -1068,16 +1075,30 @@ MediaFormatReader::InternalSeek(TrackTyp
"Seek promise must be disconnected when timethreshold is reset");
decoder.mTimeThreshold.ref().mHasSeeked = true;
self->SetVideoDecodeThreshold();
self->NotifyDecodingRequested(aTrack);
},
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
+
+ if (aResult == DemuxerFailureReason::END_OF_STREAM) {
+ // We want to enter EOS when performing an
+ // internal seek only if we're attempting to seek past
+ // the explicit duration to avoid unwanted ended
+ // event to be fired.
+ if (self->mExplicitDuration.Ref().isSome() &&
+ decoder.mTimeThreshold.ref().Time() <
+ TimeUnit::FromSeconds(
+ self->mExplicitDuration.Ref().ref())) {
+ aResult = DemuxerFailureReason::WAITING_FOR_DATA;
+ }
+ }
+
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::END_OF_STREAM:
decoder.mTimeThreshold.reset();
self->NotifyEndOfStream(aTrack);
break;
@@ -1717,16 +1738,24 @@ MediaFormatReader::OnSeekFailed(TrackTyp
MOZ_ASSERT(OnTaskQueue());
LOGV("%s failure:%d", TrackTypeToStr(aTrack), aResult);
if (aTrack == TrackType::kVideoTrack) {
mVideo.mSeekRequest.Complete();
} else {
mAudio.mSeekRequest.Complete();
}
+ // We want to enter EOS when performing a seek only if we're attempting to
+ // seek past the explicit duration to avoid unwanted ended
+ // event to be fired.
+ if (mExplicitDuration.Ref().isSome() &&
+ mPendingSeekTime.ref() < TimeUnit::FromSeconds(mExplicitDuration.Ref().ref())) {
+ aResult = DemuxerFailureReason::WAITING_FOR_DATA;
+ }
+
if (aResult == DemuxerFailureReason::WAITING_FOR_DATA) {
if (HasVideo() && aTrack == TrackType::kAudioTrack &&
mFallbackSeekTime.isSome() &&
mPendingSeekTime.ref() != mFallbackSeekTime.ref()) {
// We have failed to seek audio where video seeked to earlier.
// Attempt to seek instead to the closest point that we know we have in
// order to limit A/V sync discrepency.
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -4,18 +4,19 @@
* 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/. */
#if !defined(MediaFormatReader_h_)
#define MediaFormatReader_h_
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/StateMirroring.h"
#include "mozilla/TaskQueue.h"
-#include "mozilla/Monitor.h"
#include "MediaDataDemuxer.h"
#include "MediaDecoderReader.h"
#include "nsAutoPtr.h"
#include "PDMFactory.h"
namespace mozilla {
@@ -574,13 +575,16 @@ private:
layers::ImageContainer* GetImageContainer();
#ifdef MOZ_EME
RefPtr<CDMProxy> mCDMProxy;
#endif
RefPtr<GMPCrashHelper> mCrashHelper;
void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
+
+ // The duration explicitly set by JS, mirrored from the main thread.
+ Mirror<Maybe<double>> mExplicitDuration;
};
} // namespace mozilla
#endif