diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc
index 6aa4a171361..2be5f86f59b 100644
--- a/cobalt/dom/html_media_element.cc
+++ b/cobalt/dom/html_media_element.cc
@@ -431,9 +431,11 @@ void HTMLMediaElement::set_current_time(
LOG(ERROR) << "invalid state error";
web::DOMException::Raise(web::DOMException::kInvalidStateErr,
exception_state);
- return;
+ } else {
+ Seek(time);
}
- Seek(time);
+
+ ReportCurrentTimeToMediaSource();
}
double HTMLMediaElement::duration() const {
@@ -816,7 +818,7 @@ void HTMLMediaElement::PrepareForLoad() {
set_playback_rate(default_playback_rate());
// 6 - Set the error attribute to null and the autoplaying flag to true.
- error_ = NULL;
+ SetError(NULL);
autoplaying_ = true;
// 7 - Invoke the media element's resource selection algorithm.
@@ -1050,8 +1052,8 @@ void HTMLMediaElement::NoneSupported(const std::string& message) {
// 6.1 - Set the error attribute to a new MediaError object whose code
// attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
- error_ = new MediaError(MediaError::kMediaErrSrcNotSupported,
- message.empty() ? "Source not supported." : message);
+ SetError(new MediaError(MediaError::kMediaErrSrcNotSupported,
+ message.empty() ? "Source not supported." : message));
// 6.2 - Forget the media element's media-resource-specific text tracks.
// 6.3 - Set the element's networkState attribute to the kNetworkNoSource
@@ -1129,6 +1131,10 @@ void HTMLMediaElement::OnPlaybackProgressTimer() {
}
ScheduleTimeupdateEvent(true);
+
+ // The playback progress timer is used here to provide a steady clock that
+ // allows the attached MediaSource to have access to a recent media time.
+ ReportCurrentTimeToMediaSource();
}
void HTMLMediaElement::StartPlaybackProgressTimer() {
@@ -1511,6 +1517,8 @@ void HTMLMediaElement::UpdatePlayState() {
AddPlayedRange(last_seek_time_, time);
}
}
+
+ ReportCurrentTimeToMediaSource();
}
bool HTMLMediaElement::PotentiallyPlaying() const {
@@ -1575,6 +1583,32 @@ void HTMLMediaElement::ConfigureMediaControls() {
DLOG_IF(WARNING, controls_) << "media control is not supported";
}
+void HTMLMediaElement::ReportCurrentTimeToMediaSource() {
+ if (!is_using_media_source_attachment_methods_) {
+ return;
+ }
+
+ if (!media_source_attachment_) {
+ return;
+ }
+
+ media_source_attachment_->OnElementTimeUpdate(current_time(NULL));
+}
+
+void HTMLMediaElement::SetError(scoped_refptr error) {
+ error_ = error;
+
+ if (!is_using_media_source_attachment_methods_) {
+ return;
+ }
+
+ if (!error || !media_source_attachment_) {
+ return;
+ }
+
+ media_source_attachment_->OnElementError();
+}
+
void HTMLMediaElement::MediaEngineError(scoped_refptr error) {
if (error->message().empty()) {
LOG(WARNING) << "HTMLMediaElement::MediaEngineError " << error->code();
@@ -1589,7 +1623,7 @@ void HTMLMediaElement::MediaEngineError(scoped_refptr error) {
// 2 - Set the error attribute to a new MediaError object whose code attribute
// is set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
- error_ = error;
+ SetError(error);
// 3 - Queue a task to fire a simple event named error at the media element.
ScheduleOwnEvent(base::Tokens::error());
diff --git a/cobalt/dom/html_media_element.h b/cobalt/dom/html_media_element.h
index 6a855486250..6e93f9e9e0b 100644
--- a/cobalt/dom/html_media_element.h
+++ b/cobalt/dom/html_media_element.h
@@ -229,7 +229,13 @@ class HTMLMediaElement : public HTMLElement,
void ConfigureMediaControls();
+ // Pushes the current media time to the attached MediaSourceAttachment to
+ // avoid having to asynchronously pull media time from a cross-thread
+ // MediaSource object.
+ void ReportCurrentTimeToMediaSource();
+
// Error report
+ void SetError(scoped_refptr error);
void MediaEngineError(scoped_refptr error);
// WebMediaPlayerClient methods
@@ -313,6 +319,7 @@ class HTMLMediaElement : public HTMLElement,
// Time has not changed since sending an "ended" event.
bool sent_end_event_;
+ // See SetError().
scoped_refptr error_;
// Helper object to reduce the image capacity while a video is playing.
diff --git a/cobalt/dom/media_source.cc b/cobalt/dom/media_source.cc
index 2967676882a..16dbea235df 100644
--- a/cobalt/dom/media_source.cc
+++ b/cobalt/dom/media_source.cc
@@ -145,17 +145,6 @@ bool IsMediaElementUsingMediaSourceBufferedRangeEnabled(
.value_or(false);
}
-// If this function returns true, MediaSource will proxy calls to the
-// attached HTMLMediaElement object through the MediaSourceAttachment interface
-// instead of directly calling against the HTMLMediaElement object.
-// The default value is false.
-bool IsMediaElementUsingMediaSourceAttachmentMethodsEnabled(
- web::EnvironmentSettings* settings) {
- return GetMediaSettings(settings)
- .IsMediaElementUsingMediaSourceAttachmentMethodsEnabled()
- .value_or(false);
-}
-
} // namespace
MediaSource::MediaSource(script::EnvironmentSettings* settings)
@@ -170,9 +159,6 @@ MediaSource::MediaSource(script::EnvironmentSettings* settings)
chunk_demuxer_(NULL),
ready_state_(kMediaSourceReadyStateClosed),
ALLOW_THIS_IN_INITIALIZER_LIST(event_queue_(this)),
- is_using_media_source_attachment_methods_(
- IsMediaElementUsingMediaSourceAttachmentMethodsEnabled(
- environment_settings())),
source_buffers_(new SourceBufferList(settings, &event_queue_)),
active_source_buffers_(new SourceBufferList(settings, &event_queue_)),
live_seekable_range_(new TimeRanges) {
@@ -294,7 +280,8 @@ scoped_refptr MediaSource::AddSourceBuffer(
switch (status) {
case ChunkDemuxer::kOk:
source_buffer =
- new SourceBuffer(settings, guid, this, chunk_demuxer_, &event_queue_);
+ new SourceBuffer(settings, guid, this, chunk_demuxer_, &event_queue_,
+ is_using_media_source_attachment_methods_);
break;
case ChunkDemuxer::kNotSupported:
web::DOMException::Raise(web::DOMException::kNotSupportedErr,
@@ -439,6 +426,8 @@ bool MediaSource::IsTypeSupported(script::EnvironmentSettings* settings,
bool MediaSource::StartAttachingToMediaElement(
HTMLMediaElement* media_element) {
+ is_using_media_source_attachment_methods_ = false;
+
if (attached_element_) {
return false;
}
@@ -472,6 +461,8 @@ bool MediaSource::StartAttachingToMediaElement(
bool MediaSource::StartAttachingToMediaElement(
MediaSourceAttachmentSupplement* media_source_attachment) {
+ is_using_media_source_attachment_methods_ = true;
+
if (media_source_attachment_) {
return false;
}
@@ -685,10 +676,12 @@ void MediaSource::SetSourceBufferActive(SourceBuffer* source_buffer,
}
HTMLMediaElement* MediaSource::GetMediaElement() const {
+ DCHECK(!is_using_media_source_attachment_methods_);
return attached_element_;
}
MediaSourceAttachmentSupplement* MediaSource::GetMediaSourceAttachment() const {
+ DCHECK(is_using_media_source_attachment_methods_);
return media_source_attachment_;
}
diff --git a/cobalt/dom/media_source.h b/cobalt/dom/media_source.h
index c30d7d7f053..1cae5b40b17 100644
--- a/cobalt/dom/media_source.h
+++ b/cobalt/dom/media_source.h
@@ -170,7 +170,7 @@ class MediaSource : public web::EventTarget {
MediaSourceReadyState ready_state_;
EventQueue event_queue_;
// TODO(b/338425449): Remove direct references to HTMLMediaElement.
- const bool is_using_media_source_attachment_methods_;
+ bool is_using_media_source_attachment_methods_ = false;
base::WeakPtr attached_element_;
base::WeakPtr media_source_attachment_;
diff --git a/cobalt/dom/media_source_attachment.h b/cobalt/dom/media_source_attachment.h
index 83aa86245aa..870e1e97dca 100644
--- a/cobalt/dom/media_source_attachment.h
+++ b/cobalt/dom/media_source_attachment.h
@@ -71,6 +71,22 @@ class MediaSourceAttachment
// flag MediaElement.EnableUsingMediaSourceAttachmentMethods is disabled.
virtual scoped_refptr media_source() const = 0;
+ // Provide state updates to the MediaSource that are necessary for its
+ // operation. These are pushed rather than pulled to reduce complexity and
+ // latency, especially when the MediaSource is in a Worker context.
+ // OnElementTimeUpdate() gives the MediaSource a notion of the recent media
+ // element currentTime so that it can more effectively prevent evicting
+ // buffered media near to playback and/or seek target time in its heuristic.
+ // Alternatives such as pumping this via the media pipeline are insufficient,
+ // as the media pipeline may not be aware of overrides to the playback start
+ // position.
+ virtual void OnElementTimeUpdate(double time) = 0;
+
+ // Needed as a precondition in the Prepare Append algorithm, OnElementError()
+ // lets the MediaSource know if the attached media element has transitioned to
+ // having an error.
+ virtual void OnElementError() = 0;
+
private:
friend class base::RefCountedThreadSafe;
diff --git a/cobalt/dom/same_thread_media_source_attachment.cc b/cobalt/dom/same_thread_media_source_attachment.cc
index 1bb911596ea..8f85a02d26e 100644
--- a/cobalt/dom/same_thread_media_source_attachment.cc
+++ b/cobalt/dom/same_thread_media_source_attachment.cc
@@ -34,7 +34,9 @@ namespace dom {
SameThreadMediaSourceAttachment::SameThreadMediaSourceAttachment(
scoped_refptr media_source)
: media_source_(media_source),
- task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
+ task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
+ recent_element_time_(0.0),
+ element_has_error_(false) {}
void SameThreadMediaSourceAttachment::TraceMembers(script::Tracer* tracer) {
DCHECK_EQ(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
@@ -108,14 +110,23 @@ double SameThreadMediaSourceAttachment::GetRecentMediaTime() {
DCHECK_EQ(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
DCHECK(attached_element_);
- return attached_element_->current_time(NULL);
+ double result = attached_element_->current_time(NULL);
+
+ DVLOG(2) << __func__ << ": recent time=" << recent_element_time_
+ << ", actual current time=" << result;
+
+ return result;
}
bool SameThreadMediaSourceAttachment::GetElementError() {
DCHECK_EQ(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
DCHECK(attached_element_);
- return static_cast(attached_element_->error());
+ bool result = static_cast(attached_element_->error());
+
+ DCHECK_EQ(result, element_has_error_);
+
+ return result;
}
scoped_refptr
@@ -136,5 +147,16 @@ SameThreadMediaSourceAttachment::CreateVideoTrackList(
return base::MakeRefCounted(settings, attached_element_);
}
+void SameThreadMediaSourceAttachment::OnElementTimeUpdate(double time) {
+ recent_element_time_ = time;
+}
+
+void SameThreadMediaSourceAttachment::OnElementError() {
+ DCHECK(!element_has_error_)
+ << "At most one transition to element error per attachment is expected";
+
+ element_has_error_ = true;
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/cobalt/dom/same_thread_media_source_attachment.h b/cobalt/dom/same_thread_media_source_attachment.h
index 15033c171d6..abaf41c1f13 100644
--- a/cobalt/dom/same_thread_media_source_attachment.h
+++ b/cobalt/dom/same_thread_media_source_attachment.h
@@ -51,6 +51,9 @@ class SameThreadMediaSourceAttachment : public MediaSourceAttachmentSupplement {
scoped_refptr GetBufferedRange() const override;
MediaSourceReadyState GetReadyState() const override;
+ void OnElementTimeUpdate(double time) override;
+ void OnElementError() override;
+
// MediaSourceAttachmentSupplement
void NotifyDurationChanged(double duration) override;
bool HasMaxVideoCapabilities() const override;
@@ -84,6 +87,9 @@ class SameThreadMediaSourceAttachment : public MediaSourceAttachmentSupplement {
// Used to ensure all calls are made on the thread that created this object.
base::SequencedTaskRunner* task_runner_;
+ double recent_element_time_; // See OnElementTimeUpdate().
+ bool element_has_error_; // See OnElementError().
+
DISALLOW_COPY_AND_ASSIGN(SameThreadMediaSourceAttachment);
};
diff --git a/cobalt/dom/source_buffer.cc b/cobalt/dom/source_buffer.cc
index 42679e43ba1..2865b74062e 100644
--- a/cobalt/dom/source_buffer.cc
+++ b/cobalt/dom/source_buffer.cc
@@ -129,17 +129,6 @@ bool IsAvoidCopyingArrayBufferEnabled(web::EnvironmentSettings* settings) {
return media_settings.IsAvoidCopyingArrayBufferEnabled().value_or(false);
}
-// If this function returns true, MediaSource will proxy calls to the
-// attached HTMLMediaElement object through the MediaSourceAttachment interface
-// instead of directly calling against the HTMLMediaElement object.
-// The default value is false.
-bool IsMediaElementUsingMediaSourceAttachmentMethodsEnabled(
- web::EnvironmentSettings* settings) {
- return GetMediaSettings(settings)
- .IsMediaElementUsingMediaSourceAttachmentMethodsEnabled()
- .value_or(false);
-}
-
} // namespace
SourceBuffer::OnInitSegmentReceivedHelper::OnInitSegmentReceivedHelper(
@@ -170,7 +159,8 @@ void SourceBuffer::OnInitSegmentReceivedHelper::TryToRunOnInitSegmentReceived(
SourceBuffer::SourceBuffer(script::EnvironmentSettings* settings,
const std::string& id, MediaSource* media_source,
- ChunkDemuxer* chunk_demuxer, EventQueue* event_queue)
+ ChunkDemuxer* chunk_demuxer, EventQueue* event_queue,
+ const bool is_using_media_source_attachment_methods)
: web::EventTarget(settings),
on_init_segment_received_helper_(new OnInitSegmentReceivedHelper(this)),
id_(id),
@@ -181,8 +171,7 @@ SourceBuffer::SourceBuffer(script::EnvironmentSettings* settings,
chunk_demuxer_(chunk_demuxer),
event_queue_(event_queue),
is_using_media_source_attachment_methods_(
- IsMediaElementUsingMediaSourceAttachmentMethodsEnabled(
- environment_settings())),
+ is_using_media_source_attachment_methods),
audio_tracks_(
is_using_media_source_attachment_methods_
? media_source->GetMediaSourceAttachment()->CreateAudioTrackList(
diff --git a/cobalt/dom/source_buffer.h b/cobalt/dom/source_buffer.h
index 11c42d4cf98..ae9efef8bda 100644
--- a/cobalt/dom/source_buffer.h
+++ b/cobalt/dom/source_buffer.h
@@ -90,9 +90,11 @@ class SourceBuffer : public web::EventTarget {
// Custom, not in any spec.
//
+ // TODO(b/338425449): Remove is_using_media_source_attachment_methods.
SourceBuffer(script::EnvironmentSettings* settings, const std::string& id,
MediaSource* media_source, ChunkDemuxer* chunk_demuxer,
- EventQueue* event_queue);
+ EventQueue* event_queue,
+ const bool is_using_media_source_attachment_methods);
// Web API: SourceBuffer
//