diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 675537625b31..7ad1d63c2121 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -2803,6 +2803,8 @@ nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal) LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal)); decoder->SetMediaSeekable(aOriginal->IsMediaSeekable()); + decoder->SetMediaSeekableOnlyInBufferedRanges( + aOriginal->IsMediaSeekableOnlyInBufferedRanges()); RefPtr resource = originalResource->CloneData(decoder->GetResourceCallback()); diff --git a/dom/media/MediaDataDemuxer.h b/dom/media/MediaDataDemuxer.h index 57ee35dbb83c..59c72689961e 100644 --- a/dom/media/MediaDataDemuxer.h +++ b/dom/media/MediaDataDemuxer.h @@ -70,6 +70,10 @@ public: // Returns true if the underlying resource allows seeking. virtual bool IsSeekable() const = 0; + // Returns true if the underlying resource can only seek within buffered + // ranges. + virtual bool IsSeekableOnlyInBufferedRanges() const { return false; } + // Returns the media's crypto information, or nullptr if media isn't // encrypted. virtual UniquePtr GetCrypto() diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 7f7e6198bc7f..accdf4c8d50a 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -564,6 +564,8 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner) "MediaDecoder::mDecoderPosition (Canonical)") , mMediaSeekable(AbstractThread::MainThread(), true, "MediaDecoder::mMediaSeekable (Canonical)") + , mMediaSeekableOnlyInBufferedRanges(AbstractThread::MainThread(), false, + "MediaDecoder::mMediaSeekableOnlyInBufferedRanges (Canonical)") , mTelemetryReported(false) { MOZ_COUNT_CTOR(MediaDecoder); @@ -875,6 +877,7 @@ MediaDecoder::MetadataLoaded(nsAutoPtr aInfo, aInfo->HasAudio(), aInfo->HasVideo()); SetMediaSeekable(aInfo->mMediaSeekable); + SetMediaSeekableOnlyInBufferedRanges(aInfo->mMediaSeekableOnlyInBufferedRanges); mInfo = aInfo.forget(); ConstructMediaTracks(); @@ -1377,6 +1380,12 @@ MediaDecoder::SetMediaSeekable(bool aMediaSeekable) { mMediaSeekable = aMediaSeekable; } +void +MediaDecoder::SetMediaSeekableOnlyInBufferedRanges(bool aMediaSeekableOnlyInBufferedRanges){ + MOZ_ASSERT(NS_IsMainThread()); + mMediaSeekableOnlyInBufferedRanges = aMediaSeekableOnlyInBufferedRanges; +} + bool MediaDecoder::IsTransportSeekable() { @@ -1392,14 +1401,23 @@ MediaDecoder::IsMediaSeekable() return mMediaSeekable; } +bool +MediaDecoder::IsMediaSeekableOnlyInBufferedRanges() +{ + MOZ_ASSERT(NS_IsMainThread()); + return mMediaSeekableOnlyInBufferedRanges; +} + media::TimeIntervals MediaDecoder::GetSeekable() { MOZ_ASSERT(NS_IsMainThread()); // We can seek in buffered range if the media is seekable. Also, we can seek // in unbuffered ranges if the transport level is seekable (local file or the - // server supports range requests, etc.) - if (!IsMediaSeekable()) { + // server supports range requests, etc.) or in cue-less WebMs + if (IsMediaSeekableOnlyInBufferedRanges()) { + return GetBuffered(); + } else if (!IsMediaSeekable()) { return media::TimeIntervals(); } else if (!IsTransportSeekable()) { return GetBuffered(); diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 16cf42ef0c4e..e85fb0cf9ec8 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -253,12 +253,17 @@ public: // Called from HTMLMediaElement when owner document activity changes virtual void SetElementVisibility(bool aIsVisible) {} - // Set a flag indicating whether seeking is supported + // Set a flag indicating whether random seeking is supported void SetMediaSeekable(bool aMediaSeekable); + // Set a flag indicating whether seeking is supported only in buffered ranges + void SetMediaSeekableOnlyInBufferedRanges(bool aMediaSeekableOnlyInBufferedRanges); - // Returns true if this media supports seeking. False for example for WebM - // files without an index and chained ogg files. + // Returns true if this media supports random seeking. False for example with + // chained ogg files. bool IsMediaSeekable(); + // Returns true if this media supports seeking only in buffered ranges. True + // for example in WebMs with no cues + bool IsMediaSeekableOnlyInBufferedRanges(); // Returns true if seeking is supported on a transport level (e.g. the server // supports range requests, we are playing a file, etc.). bool IsTransportSeekable(); @@ -792,6 +797,9 @@ protected: // True if the media is seekable (i.e. supports random access). Canonical mMediaSeekable; + // True if the media is only seekable within its buffered ranges. + Canonical mMediaSeekableOnlyInBufferedRanges; + public: AbstractCanonical* CanonicalDurationOrNull() override; AbstractCanonical* CanonicalVolume() { @@ -833,6 +841,9 @@ public: AbstractCanonical* CanonicalMediaSeekable() { return &mMediaSeekable; } + AbstractCanonical* CanonicalMediaSeekableOnlyInBufferedRanges() { + return &mMediaSeekableOnlyInBufferedRanges; + } private: // Notify owner when the audible state changed diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index 7b457cfd69bf..d765321ec7e8 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -268,6 +268,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, "MediaDecoderStateMachine::mDecoderPosition (Mirror)"), mMediaSeekable(mTaskQueue, true, "MediaDecoderStateMachine::mMediaSeekable (Mirror)"), + mMediaSeekableOnlyInBufferedRanges(mTaskQueue, false, + "MediaDecoderStateMachine::mMediaSeekableOnlyInBufferedRanges (Mirror)"), mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mDuration (Canonical"), mIsShutdown(mTaskQueue, false, @@ -350,6 +352,7 @@ MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable()); mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition()); mMediaSeekable.Connect(aDecoder->CanonicalMediaSeekable()); + mMediaSeekableOnlyInBufferedRanges.Connect(aDecoder->CanonicalMediaSeekableOnlyInBufferedRanges()); // Initialize watchers. mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated); @@ -1474,9 +1477,8 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget) return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__); } - // We need to be able to seek both at a transport level and at a media level - // to seek. - if (!mMediaSeekable) { + // We need to be able to seek in some way + if (!mMediaSeekable && !mMediaSeekableOnlyInBufferedRanges) { DECODER_WARN("Seek() function should not be called on a non-seekable state machine"); return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__); } @@ -2192,6 +2194,7 @@ MediaDecoderStateMachine::FinishShutdown() mPlaybackRateReliable.DisconnectIfConnected(); mDecoderPosition.DisconnectIfConnected(); mMediaSeekable.DisconnectIfConnected(); + mMediaSeekableOnlyInBufferedRanges.DisconnectIfConnected(); mDuration.DisconnectAll(); mIsShutdown.DisconnectAll(); diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index 2c13d6a2f68a..04a77e656eb1 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -1239,6 +1239,9 @@ private: // True if the media is seekable (i.e. supports random access). Mirror mMediaSeekable; + // True if the media is seekable only in buffered ranges. + Mirror mMediaSeekableOnlyInBufferedRanges; + // Duration of the media. This is guaranteed to be non-null after we finish // decoding the first frame. Canonical mDuration; diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 2af5cfb64e11..895fe7ffd4b3 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -337,6 +337,8 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) } mInfo.mMediaSeekable = mDemuxer->IsSeekable(); + mInfo.mMediaSeekableOnlyInBufferedRanges = + mDemuxer->IsSeekableOnlyInBufferedRanges(); if (!videoActive && !audioActive) { mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); @@ -1428,7 +1430,7 @@ MediaFormatReader::Seek(SeekTarget aTarget, int64_t aUnused) MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing()); MOZ_DIAGNOSTIC_ASSERT(mAudio.mTimeThreshold.isNothing()); - if (!mInfo.mMediaSeekable) { + if (!mInfo.mMediaSeekable && !mInfo.mMediaSeekableOnlyInBufferedRanges) { LOG("Seek() END (Unseekable)"); return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } diff --git a/dom/media/MediaInfo.h b/dom/media/MediaInfo.h index a1427d6221d8..217e15f81a8e 100644 --- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -417,6 +417,9 @@ public: // True if the media is seekable (i.e. supports random access). bool mMediaSeekable = true; + // True if the media is only seekable within its buffered ranges. + bool mMediaSeekableOnlyInBufferedRanges = false; + EncryptionInfo mCrypto; }; diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index d01496e6f217..20a703879c1d 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -431,7 +431,13 @@ WebMDemuxer::ReadMetadata() bool WebMDemuxer::IsSeekable() const { - return mContext; + return mContext && nestegg_has_cues(mContext); +} + +bool +WebMDemuxer::IsSeekableOnlyInBufferedRanges() const +{ + return mContext && !nestegg_has_cues(mContext); } void diff --git a/dom/media/webm/WebMDemuxer.h b/dom/media/webm/WebMDemuxer.h index d1d9a2c61856..a06415e93319 100644 --- a/dom/media/webm/WebMDemuxer.h +++ b/dom/media/webm/WebMDemuxer.h @@ -98,6 +98,8 @@ public: bool IsSeekable() const override; + bool IsSeekableOnlyInBufferedRanges() const override; + UniquePtr GetCrypto() override; bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);