diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index 6b2410f99d9f..7778437a2ec5 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -172,6 +172,9 @@ void ChunkDemuxerStream::Seek(base::TimeDelta time) { DCHECK(state_ == UNINITIALIZED || state_ == RETURNING_ABORT_FOR_READS) << state_; +#if BUILDFLAG(USE_STARBOARD_MEDIA) + write_head_ = time; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) stream_->Seek(time); } @@ -217,6 +220,13 @@ bool ChunkDemuxerStream::EvictCodedFrames(base::TimeDelta media_time, return stream_->GarbageCollectIfNeeded(media_time, newDataSize); } +#if BUILDFLAG(USE_STARBOARD_MEDIA) +base::TimeDelta ChunkDemuxerStream::GetWriteHead() const { + base::AutoLock auto_lock(lock_); + return write_head_; +} +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + void ChunkDemuxerStream::OnMemoryPressure( base::TimeDelta media_time, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, @@ -490,6 +500,13 @@ void ChunkDemuxerStream::CompletePendingReadIfPossible_Locked() { // Other cases are kOk and just return the buffers. DCHECK(!buffers.empty()); requested_buffer_count_ = 0; +#if BUILDFLAG(USE_STARBOARD_MEDIA) + for (auto&& buffer : buffers) { + if (!buffer->end_of_stream()) { + write_head_ = std::max(write_head_, buffer->timestamp()); + } + } +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) std::move(read_cb_).Run(kOk, std::move(buffers)); } @@ -1051,6 +1068,22 @@ bool ChunkDemuxer::EvictCodedFrames(const std::string& id, return itr->second->EvictCodedFrames(currentMediaTime, newDataSize); } +#if BUILDFLAG(USE_STARBOARD_MEDIA) +base::TimeDelta ChunkDemuxer::GetWriteHead(const std::string& id) const { + base::AutoLock auto_lock(lock_); + DCHECK(IsValidId_Locked(id)); + + auto iter = id_to_streams_map_.find(id); + if (iter == id_to_streams_map_.end() || iter->second.empty()) { + // Handled just in case. + NOTREACHED(); + return base::TimeDelta(); + } + + return iter->second[0]->GetWriteHead(); +} +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + bool ChunkDemuxer::AppendToParseBuffer(const std::string& id, const uint8_t* data, size_t length) { diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 49b7199e0943..fb66b32bfcb0 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -83,6 +83,12 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction bool EvictCodedFrames(base::TimeDelta media_time, size_t newDataSize); +#if BUILDFLAG(USE_STARBOARD_MEDIA) + // Returns the latest presentation timestamp of the buffers queued in the + // stream. + base::TimeDelta GetWriteHead() const; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + void OnMemoryPressure( base::TimeDelta media_time, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, @@ -190,6 +196,7 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { #if BUILDFLAG(USE_STARBOARD_MEDIA) const std::string mime_type_; + base::TimeDelta write_head_ GUARDED_BY(lock_); #endif // BUILDFLAG(USE_STARBOARD_MEDIA) // Specifies the type of the stream. @@ -416,6 +423,12 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { base::TimeDelta currentMediaTime, size_t newDataSize); +#if BUILDFLAG(USE_STARBOARD_MEDIA) + // Returns the latest presentation timestamp of the buffers to be read + // from the DemuxerStream. + [[nodiscard]] base::TimeDelta GetWriteHead(const std::string& id) const; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + void OnMemoryPressure( base::TimeDelta currentMediaTime, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, diff --git a/third_party/blink/public/platform/web_source_buffer.h b/third_party/blink/public/platform/web_source_buffer.h index 7ac41eedb64f..fc3defa085be 100644 --- a/third_party/blink/public/platform/web_source_buffer.h +++ b/third_party/blink/public/platform/web_source_buffer.h @@ -37,6 +37,10 @@ namespace blink { +#if BUILDFLAG(USE_STARBOARD_MEDIA) +class ExceptionState; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + class WebSourceBufferClient; // Interface for actuating the media engine implementation of Media Source @@ -140,6 +144,11 @@ class WebSourceBuffer { // After this method is called, this WebSourceBuffer should never use the // client pointer passed to SetClient(). virtual void RemovedFromMediaSource() = 0; + +#if BUILDFLAG(USE_STARBOARD_MEDIA) + // Return the highest presentation timestamp written to the Renderer. + virtual double GetWriteHead(ExceptionState& exception_state) const = 0; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) }; } // namespace blink diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni index ea769d605683..3445984b8088 100644 --- a/third_party/blink/renderer/bindings/idl_in_modules.gni +++ b/third_party/blink/renderer/bindings/idl_in_modules.gni @@ -4,6 +4,10 @@ import("//third_party/blink/renderer/config.gni") +if (is_cobalt) { + import("//starboard/build/buildflags.gni") +} + # Statically-defined (not build-time-generated) IDL files in 'modules' component # for production. static_idl_files_in_modules = get_path_info( @@ -1204,7 +1208,8 @@ static_idl_files_in_modules = get_path_info( if (is_cobalt) { static_idl_files_in_modules += get_path_info( - [ "//third_party/blink/renderer/modules/cobalt/crash_annotator/crash_annotator.idl", + [ + "//third_party/blink/renderer/modules/cobalt/crash_annotator/crash_annotator.idl", "//third_party/blink/renderer/modules/cobalt/crash_annotator/navigator_crash_annotator.idl", ], "abspath") @@ -1287,6 +1292,15 @@ if (target_os != "android") { "abspath") } +# SourceBufferWriteHead +# An extension to the SourceBuffer interface that allows web apps to check the +# highest presentation timestamp written to the Renderer. +if (is_cobalt && use_starboard_media) { + static_idl_files_in_modules += get_path_info( + [ "//third_party/blink/renderer/modules/mediasource/source_buffer_write_head.idl" ], + "abspath") +} + # Statically-defined (not runtime-generated) IDL files in 'modules' component. # These IDL definitions are used only for testing. static_idl_files_in_modules_for_testing = get_path_info( diff --git a/third_party/blink/renderer/modules/mediasource/BUILD.gn b/third_party/blink/renderer/modules/mediasource/BUILD.gn index a14bb0a20c07..0d8b68a606f5 100644 --- a/third_party/blink/renderer/modules/mediasource/BUILD.gn +++ b/third_party/blink/renderer/modules/mediasource/BUILD.gn @@ -56,8 +56,10 @@ blink_modules_sources("mediasource") { "//third_party/blink/renderer/modules/webcodecs:webcodecs", ] if (is_cobalt && use_starboard_media) { - deps += [ - "//starboard($starboard_toolchain)", + sources += [ + "source_buffer_write_head.cc", + "source_buffer_write_head.h", ] + deps += [ "//starboard($starboard_toolchain)" ] } } diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer.cc b/third_party/blink/renderer/modules/mediasource/source_buffer.cc index 42a23253da5b..5cdd7a0ac68c 100644 --- a/third_party/blink/renderer/modules/mediasource/source_buffer.cc +++ b/third_party/blink/renderer/modules/mediasource/source_buffer.cc @@ -475,6 +475,20 @@ VideoTrackList& SourceBuffer::videoTracks() { return *video_tracks_; } +#if BUILDFLAG(USE_STARBOARD_MEDIA) +double SourceBuffer::GetWriteHead(ExceptionState& exception_state) const { + if (source_ == NULL) { + MediaSource::LogAndThrowDOMException( + exception_state, DOMExceptionCode::kInvalidStateError, + "Cannot obtain SourceBuffer write head with an invalid MediaSource"); + return 0.0; + } + + DCHECK(web_source_buffer_); + return web_source_buffer_->GetWriteHead(exception_state); +} +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + double SourceBuffer::appendWindowStart() const { return append_window_start_; } diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer.h b/third_party/blink/renderer/modules/mediasource/source_buffer.h index 0178bf1a8e66..0ac28dca70f0 100644 --- a/third_party/blink/renderer/modules/mediasource/source_buffer.h +++ b/third_party/blink/renderer/modules/mediasource/source_buffer.h @@ -113,6 +113,12 @@ class SourceBuffer final : public EventTargetWithInlineData, AudioTrackList& audioTracks(); VideoTrackList& videoTracks(); +#if BUILDFLAG(USE_STARBOARD_MEDIA) + // Cobalt-specific method that returns the highest presentation + // timestamp written to the Renderer. + double GetWriteHead(ExceptionState& exception_state) const; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + // "_Locked" requires these be called while in the scope of callback of // |source_|'s attachment's RunExclusively(). Other methods without "_Locked" // may also require the same, since they can be called from within these diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.cc b/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.cc new file mode 100644 index 000000000000..578ba3a42e0c --- /dev/null +++ b/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.cc @@ -0,0 +1,31 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "third_party/blink/renderer/modules/mediasource/source_buffer_write_head.h" + +#include "third_party/blink/renderer/modules/mediasource/source_buffer.h" +#include "third_party/blink/renderer/platform/bindings/exception_state.h" + +#if !BUILDFLAG(USE_STARBOARD_MEDIA) +#error "This file only works with Starboard media" +#endif // !BUILDFLAG(USE_STARBOARD_MEDIA) + +namespace blink { + +// static +double SourceBufferWriteHead::writeHead(SourceBuffer& source_buffer, + ExceptionState& exception_state) { + return source_buffer.GetWriteHead(exception_state); +} +} // namespace blink diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.h b/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.h new file mode 100644 index 000000000000..e5a52110d793 --- /dev/null +++ b/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.h @@ -0,0 +1,39 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_SOURCE_BUFFER_WRITE_HEAD_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_SOURCE_BUFFER_WRITE_HEAD_H_ + +#include "build/build_config.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +#if !BUILDFLAG(USE_STARBOARD_MEDIA) +#error "This file only works with Starboard media" +#endif // !BUILDFLAG(USE_STARBOARD_MEDIA) + +namespace blink { + +class ExceptionState; +class SourceBuffer; + +class SourceBufferWriteHead { + STATIC_ONLY(SourceBufferWriteHead); + + public: + static double writeHead(SourceBuffer&, ExceptionState&); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_SOURCE_BUFFER_WRITE_HEAD_H_ diff --git a/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.idl b/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.idl new file mode 100644 index 000000000000..8ff5c972d000 --- /dev/null +++ b/third_party/blink/renderer/modules/mediasource/source_buffer_write_head.idl @@ -0,0 +1,24 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Non standard interface used for Cobalt only. +// Returns the highest presentation timestamp written to the Renderer; +// raises `InvalidStateError` if the SourceBuffer object has been removed +// from the MediaSource object. + +[ + ImplementedAs=SourceBufferWriteHead +] partial interface SourceBuffer { + [RaisesException] readonly attribute double writeHead; +}; \ No newline at end of file diff --git a/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc b/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc index a040298abd08..a3eb88f63b34 100644 --- a/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc +++ b/third_party/blink/renderer/platform/media/web_source_buffer_impl.cc @@ -245,6 +245,14 @@ void WebSourceBufferImpl::RemovedFromMediaSource() { client_ = nullptr; } +#if BUILDFLAG(USE_STARBOARD_MEDIA) +double WebSourceBufferImpl::GetWriteHead( + ExceptionState& exception_state) const { + DCHECK(demuxer_); + return demuxer_->GetWriteHead(id_).InSecondsF(); +} +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + WebMediaPlayer::TrackType mediaTrackTypeToBlink(media::MediaTrack::Type type) { switch (type) { case media::MediaTrack::Audio: diff --git a/third_party/blink/renderer/platform/media/web_source_buffer_impl.h b/third_party/blink/renderer/platform/media/web_source_buffer_impl.h index 5ecfab637271..25c272f8d1f5 100644 --- a/third_party/blink/renderer/platform/media/web_source_buffer_impl.h +++ b/third_party/blink/renderer/platform/media/web_source_buffer_impl.h @@ -24,6 +24,10 @@ enum class SourceBufferParseWarning; namespace blink { +#if BUILDFLAG(USE_STARBOARD_MEDIA) +class ExceptionState; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + class PLATFORM_EXPORT WebSourceBufferImpl : public WebSourceBuffer { public: WebSourceBufferImpl(const std::string& id, media::ChunkDemuxer* demuxer); @@ -57,6 +61,11 @@ class PLATFORM_EXPORT WebSourceBufferImpl : public WebSourceBuffer { void SetAppendWindowEnd(double end) override; void RemovedFromMediaSource() override; +#if BUILDFLAG(USE_STARBOARD_MEDIA) + // Return the highest presentation timestamp written to the Renderer. + double GetWriteHead(ExceptionState& exception_state) const override; +#endif // BUILDFLAG(USE_STARBOARD_MEDIA) + private: // Demuxer callback handler to process an initialization segment received // during an append() call. diff --git a/third_party/blink/web_tests/external/wpt/media-source/SourceBuffer-writeHead.html b/third_party/blink/web_tests/external/wpt/media-source/SourceBuffer-writeHead.html new file mode 100644 index 000000000000..56b93e84c945 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/media-source/SourceBuffer-writeHead.html @@ -0,0 +1,102 @@ + + + +
+