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);