Skip to content

Commit

Permalink
Create stub for cross-thread MediaSource attachments (youtube#4169)
Browse files Browse the repository at this point in the history
Enables MediaSource, SourceBuffer, and SourceBufferList objects to be
created on Dedicated Workers. Creation of these objects is not gated
behind any H5VCC flags, but they will be non-transferable without usage
of H5VCC flags.

The initial implementation of CrossThreadMediaSourceAttachment is very
bare bones, and does not allow for MediaElements to interact with the
attached MediaSources yet. However, this lays the groundwork for
supporting creating the relevant MediaSource objects on the worker
thread, and the transfer process to get the reference back to the main
thread.

Usage of creating MediaSource URLs is gated through the
`MediaSource.EnableInWorkers` H5VCC flag, in addition to existing flags
`MediaElement.EnableUsingMediaSourceBufferedRange` and
`MediaElement.EnableUsingMediaSourceAttachmentMethods`. If and only if
all three flags are enabled, MediaSources created on Dedicated Workers
and passed to `URL.createObjectURL` will return a non-empty string that
can be sent to the main thread, and used to 'access' the
MediaSourceAttachment. In addition, if and only if all three H5VCC flags
are enabled,

[`MediaSource.canConstructInDedicatedWorker`](https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/canConstructInDedicatedWorker_static)
will return true.

This is based on the following Chromium commit:

• https://chromium-review.googlesource.com/c/chromium/src/+/2407075

b/338425449
  • Loading branch information
at-ninja authored Oct 16, 2024
1 parent d955af2 commit 7e04f7f
Show file tree
Hide file tree
Showing 20 changed files with 571 additions and 26 deletions.
2 changes: 2 additions & 0 deletions cobalt/dom/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ static_library("dom") {
"character_data.h",
"comment.cc",
"comment.h",
"cross_thread_media_source_attachment.cc",
"cross_thread_media_source_attachment.h",
"css_animations_adapter.cc",
"css_animations_adapter.h",
"css_transitions_adapter.cc",
Expand Down
131 changes: 131 additions & 0 deletions cobalt/dom/cross_thread_media_source_attachment.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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.
//
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cobalt/dom/cross_thread_media_source_attachment.h"

#include "base/task/sequenced_task_runner.h"
#include "cobalt/dom/media_source.h"
#include "cobalt/dom/media_source_attachment.h"
#include "cobalt/dom/media_source_ready_state.h"
#include "cobalt/dom/time_ranges.h"
#include "cobalt/script/tracer.h"
#include "cobalt/web/url_registry.h"
#include "media/filters/chunk_demuxer.h"


namespace cobalt {
namespace dom {

CrossThreadMediaSourceAttachment::CrossThreadMediaSourceAttachment(
scoped_refptr<MediaSource> media_source)
: media_source_(media_source),
task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
recent_element_time_(0.0),
element_has_error_(false) {}

void CrossThreadMediaSourceAttachment::TraceMembers(script::Tracer* tracer) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());

tracer->Trace(attached_element_);
tracer->Trace(media_source_);
}

bool CrossThreadMediaSourceAttachment::StartAttachingToMediaElement(
HTMLMediaElement* media_element) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return false;
}

void CrossThreadMediaSourceAttachment::CompleteAttachingToMediaElement(
::media::ChunkDemuxer* chunk_demuxer) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
}

void CrossThreadMediaSourceAttachment::Close() {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
}

scoped_refptr<TimeRanges> CrossThreadMediaSourceAttachment::GetBufferedRange()
const {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return nullptr;
}

MediaSourceReadyState CrossThreadMediaSourceAttachment::GetReadyState() const {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return kMediaSourceReadyStateClosed;
}

void CrossThreadMediaSourceAttachment::NotifyDurationChanged(double duration) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
}

bool CrossThreadMediaSourceAttachment::HasMaxVideoCapabilities() const {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return false;
}

double CrossThreadMediaSourceAttachment::GetRecentMediaTime() {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return 0;
}

bool CrossThreadMediaSourceAttachment::GetElementError() {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return false;
}

scoped_refptr<AudioTrackList>
CrossThreadMediaSourceAttachment::CreateAudioTrackList(
script::EnvironmentSettings* settings) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return nullptr;
}

scoped_refptr<VideoTrackList>
CrossThreadMediaSourceAttachment::CreateVideoTrackList(
script::EnvironmentSettings* settings) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return nullptr;
}

void CrossThreadMediaSourceAttachment::OnElementTimeUpdate(double time) {
NOTIMPLEMENTED();
recent_element_time_ = time;
}

void CrossThreadMediaSourceAttachment::OnElementError() {
DCHECK(!element_has_error_)
<< "At most one transition to element error per attachment is expected";
NOTIMPLEMENTED();
element_has_error_ = true;
}

} // namespace dom
} // namespace cobalt
98 changes: 98 additions & 0 deletions cobalt/dom/cross_thread_media_source_attachment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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.
//
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COBALT_DOM_CROSS_THREAD_MEDIA_SOURCE_ATTACHMENT_H_
#define COBALT_DOM_CROSS_THREAD_MEDIA_SOURCE_ATTACHMENT_H_

#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "cobalt/dom/audio_track_list.h"
#include "cobalt/dom/media_source.h"
#include "cobalt/dom/media_source_attachment_supplement.h"
#include "cobalt/dom/media_source_ready_state.h"
#include "cobalt/dom/time_ranges.h"
#include "cobalt/dom/video_track_list.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/tracer.h"
#include "cobalt/web/url_registry.h"
#include "media/filters/chunk_demuxer.h"

namespace cobalt {
namespace dom {

// MediaSourceAttachment that supports operations between a HTMLMediaElement on
// the main browser thread and a MediaSource on a DedicatedWorker.
class CrossThreadMediaSourceAttachment
: public MediaSourceAttachmentSupplement {
public:
explicit CrossThreadMediaSourceAttachment(
scoped_refptr<MediaSource> media_source);

// Traceable
void TraceMembers(script::Tracer* tracer) override;

// MediaSourceAttachment
bool StartAttachingToMediaElement(HTMLMediaElement* media_element) override;
void CompleteAttachingToMediaElement(
::media::ChunkDemuxer* chunk_demuxer) override;
void Close() override;
scoped_refptr<TimeRanges> 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;
double GetRecentMediaTime() override;
bool GetElementError() override;
scoped_refptr<AudioTrackList> CreateAudioTrackList(
script::EnvironmentSettings* settings) override;
scoped_refptr<VideoTrackList> CreateVideoTrackList(
script::EnvironmentSettings* settings) override;

// TODO(338425449): Remove methods after feature rollout.
scoped_refptr<MediaSource> media_source() const override { return nullptr; }
base::WeakPtr<HTMLMediaElement> media_element() const override {
return nullptr;
}

private:
~CrossThreadMediaSourceAttachment() = default;

// Reference to the registered MediaSource.
scoped_refptr<MediaSource> media_source_;

// Reference to the HTMLMediaElement the associated MediaSource is attached
// to. Only set after StartAttachingToMediaElement is called.
base::WeakPtr<HTMLMediaElement> attached_element_ = nullptr;

// 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(CrossThreadMediaSourceAttachment);
};

} // namespace dom
} // namespace cobalt

#endif // COBALT_DOM_CROSS_THREAD_MEDIA_SOURCE_ATTACHMENT_H_
6 changes: 6 additions & 0 deletions cobalt/dom/media_settings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ bool MediaSettingsImpl::Set(const std::string& name, int value) {
LOG(INFO) << name << ": set to " << value;
return true;
}
} else if (name == "MediaSource.EnableInWorkers") {
if (value == 0 || value == 1) {
is_mse_in_workers_enabled_ = value != 0;
LOG(INFO) << name << ": set to " << value;
return true;
}
} else {
LOG(WARNING) << "Ignore unknown setting with name \"" << name << "\"";
return false;
Expand Down
6 changes: 6 additions & 0 deletions cobalt/dom/media_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class MediaSettings {
IsMediaElementUsingMediaSourceBufferedRangeEnabled() const = 0;
virtual base::Optional<bool>
IsMediaElementUsingMediaSourceAttachmentMethodsEnabled() const = 0;
virtual base::Optional<bool> IsMseInWorkersEnabled() const = 0;

protected:
MediaSettings() = default;
Expand Down Expand Up @@ -107,6 +108,10 @@ class MediaSettingsImpl : public MediaSettings {
base::AutoLock auto_lock(lock_);
return is_media_element_using_media_source_attachment_methods_enabled_;
}
base::Optional<bool> IsMseInWorkersEnabled() const override {
base::AutoLock auto_lock(lock_);
return is_mse_in_workers_enabled_;
}

// Returns true when the setting associated with `name` is set to `value`.
// Returns false when `name` is not associated with any settings, or if
Expand All @@ -128,6 +133,7 @@ class MediaSettingsImpl : public MediaSettings {
is_media_element_using_media_source_buffered_range_enabled_;
base::Optional<bool>
is_media_element_using_media_source_attachment_methods_enabled_;
base::Optional<bool> is_mse_in_workers_enabled_;

base::Optional<bool> is_painting_video_background_to_black_;
};
Expand Down
10 changes: 10 additions & 0 deletions cobalt/dom/media_settings_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ TEST(MediaSettingsImplTest, Empty) {
EXPECT_FALSE(impl.GetMaxSourceBufferAppendSizeInBytes());
EXPECT_FALSE(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds());
EXPECT_FALSE(impl.IsPaintingVideoBackgroundToBlack());
EXPECT_FALSE(impl.IsMseInWorkersEnabled());
}

TEST(MediaSettingsImplTest, SunnyDay) {
Expand All @@ -51,6 +52,7 @@ TEST(MediaSettingsImplTest, SunnyDay) {
ASSERT_TRUE(impl.Set("MediaElement.EnableUsingMediaSourceBufferedRange", 1));
ASSERT_TRUE(
impl.Set("MediaElement.EnableUsingMediaSourceAttachmentMethods", 1));
ASSERT_TRUE(impl.Set("MediaSource.EnableInWorkers", 1));

EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 100);
EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 101);
Expand All @@ -66,6 +68,7 @@ TEST(MediaSettingsImplTest, SunnyDay) {
impl.IsMediaElementUsingMediaSourceBufferedRangeEnabled().value());
EXPECT_TRUE(
impl.IsMediaElementUsingMediaSourceAttachmentMethodsEnabled().value());
EXPECT_TRUE(impl.IsMseInWorkersEnabled().value());
}

TEST(MediaSettingsImplTest, RainyDay) {
Expand All @@ -86,6 +89,7 @@ TEST(MediaSettingsImplTest, RainyDay) {
impl.Set("MediaElement.EnableUsingMediaSourceBufferedRange", -101));
ASSERT_FALSE(
impl.Set("MediaElement.EnableUsingMediaSourceAttachmentMethods", -101));
ASSERT_FALSE(impl.Set("MediaElement.EnableInWorkers", -123));

EXPECT_FALSE(impl.GetSourceBufferEvictExtraInBytes());
EXPECT_FALSE(impl.GetMinimumProcessorCountToOffloadAlgorithm());
Expand All @@ -98,6 +102,7 @@ TEST(MediaSettingsImplTest, RainyDay) {
EXPECT_FALSE(impl.IsPaintingVideoBackgroundToBlack());
EXPECT_FALSE(impl.IsMediaElementUsingMediaSourceBufferedRangeEnabled());
EXPECT_FALSE(impl.IsMediaElementUsingMediaSourceAttachmentMethodsEnabled());
EXPECT_FALSE(impl.IsMseInWorkersEnabled());
}

TEST(MediaSettingsImplTest, ZeroValuesWork) {
Expand All @@ -117,6 +122,7 @@ TEST(MediaSettingsImplTest, ZeroValuesWork) {
ASSERT_TRUE(impl.Set("MediaElement.EnableUsingMediaSourceBufferedRange", 0));
ASSERT_TRUE(
impl.Set("MediaElement.EnableUsingMediaSourceAttachmentMethods", 0));
ASSERT_TRUE(impl.Set("MediaSource.EnableInWorkers", 0));

EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 0);
EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 0);
Expand All @@ -129,6 +135,7 @@ TEST(MediaSettingsImplTest, ZeroValuesWork) {
impl.IsMediaElementUsingMediaSourceBufferedRangeEnabled().value());
EXPECT_FALSE(
impl.IsMediaElementUsingMediaSourceAttachmentMethodsEnabled().value());
EXPECT_FALSE(impl.IsMseInWorkersEnabled().value());
}

TEST(MediaSettingsImplTest, Updatable) {
Expand All @@ -148,6 +155,7 @@ TEST(MediaSettingsImplTest, Updatable) {
ASSERT_TRUE(impl.Set("MediaElement.EnableUsingMediaSourceBufferedRange", 0));
ASSERT_TRUE(
impl.Set("MediaElement.EnableUsingMediaSourceAttachmentMethods", 0));
ASSERT_TRUE(impl.Set("MediaSource.EnableInWorkers", 0));

ASSERT_TRUE(impl.Set("MediaSource.SourceBufferEvictExtraInBytes", 1));
ASSERT_TRUE(
Expand All @@ -163,6 +171,7 @@ TEST(MediaSettingsImplTest, Updatable) {
ASSERT_TRUE(impl.Set("MediaElement.EnableUsingMediaSourceBufferedRange", 1));
ASSERT_TRUE(
impl.Set("MediaElement.EnableUsingMediaSourceAttachmentMethods", 1));
ASSERT_TRUE(impl.Set("MediaSource.EnableInWorkers", 1));

EXPECT_EQ(impl.GetSourceBufferEvictExtraInBytes().value(), 1);
EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 1);
Expand All @@ -178,6 +187,7 @@ TEST(MediaSettingsImplTest, Updatable) {
impl.IsMediaElementUsingMediaSourceBufferedRangeEnabled().value());
EXPECT_TRUE(
impl.IsMediaElementUsingMediaSourceAttachmentMethodsEnabled().value());
EXPECT_TRUE(impl.IsMseInWorkersEnabled().value());
}

TEST(MediaSettingsImplTest, InvalidSettingNames) {
Expand Down
Loading

0 comments on commit 7e04f7f

Please sign in to comment.