Skip to content

Commit

Permalink
MSE-in-Workers: srcObject part 2: MS.getHandle()
Browse files Browse the repository at this point in the history
Adds ability for apps to get a MediaSourceHandle from a dedicated worker
MediaSource instance, gated by RuntimeEnabledFeature
"MediaSourceInWorkersUsingHandle". Updates the PassKey required for
creation of a concrete MediaSourceAttachmentSupplement to allow that
only from either URLMediaSource::createObjectURL or the new
MediaSource::getHandle method. This specificity is enabled by a new
AttachmentCreationPassKeyProvider type.
Later changes will enable apps to post the handle to the main thread and
attach it to an HTMLMediaElement via the srcObject attribute; they will
also include test updates.

References:
Full prototype CL:
    https://chromium-review.googlesource.com/c/chromium/src/+/3515334
MSE spec issue:
    w3c/media-source#175
MSE spec feature updates switching from worker MSE attachment via
  object URL to attachment via srcObject MediaSourceHandle:
  * w3c/media-source#305
  * further clarifications in discussion at
    w3c/media-source#306 (comment)

crbug.com/506273 is somewhat related, but not fixed by this change (it
refers to directly setting MediaSource on Window context to the media
element's srcObject attribute.) This change sequence is specific to
enabling MSE-in-Worker attachment via srcObject using a handle object.

BUG=878133,506273

Change-Id: Ic61f4cc4193080bdbc39234b98897d9a789778d6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3688613
Commit-Queue: Matthew Wolenetz <wolenetz@chromium.org>
Reviewed-by: Will Cassella <cassew@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1012129}
NOKEYCHECK=True
GitOrigin-RevId: 09e5fb1a68416f65ef2255c846b81a46f6aa8e52
  • Loading branch information
wolenetz authored and copybara-github committed Jun 8, 2022
1 parent a99a568 commit f31cbc6
Show file tree
Hide file tree
Showing 14 changed files with 174 additions and 23 deletions.
1 change: 1 addition & 0 deletions blink/public/mojom/use_counter/metrics/web_feature.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -3579,6 +3579,7 @@ enum WebFeature {
kAnyPopupAttribute = 4258,
kDeferredShapingWorked = 4259,
kDeferredShapingReshapedByForceLayout = 4260,
kMediaSourceGetHandle = 4261,

// Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots.
Expand Down
1 change: 1 addition & 0 deletions blink/renderer/modules/mediasource/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import("//third_party/blink/renderer/modules/modules.gni")

blink_modules_sources("mediasource") {
sources = [
"attachment_creation_pass_key_provider.h",
"cross_thread_media_source_attachment.cc",
"cross_thread_media_source_attachment.h",
"html_video_element_media_source.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_ATTACHMENT_CREATION_PASS_KEY_PROVIDER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_ATTACHMENT_CREATION_PASS_KEY_PROVIDER_H_

#include "base/types/pass_key.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/modules/mediasource/url_media_source.h"

namespace blink {

class ExceptionState;
class MediaSource;
class MediaSourceHandleImpl;
class ScriptState;

class AttachmentCreationPassKeyProvider {
STATIC_ONLY(AttachmentCreationPassKeyProvider);

public:
using PassKey = base::PassKey<AttachmentCreationPassKeyProvider>;

private:
// These specific friend methods are allowed to use GetPassKey so they may
// create a MediaSourceAttachmentSupplement.
static PassKey GetPassKey() { return PassKey(); }
friend String URLMediaSource::createObjectURL(ScriptState*, MediaSource*);
friend MediaSourceHandleImpl* MediaSource::getHandle(ExceptionState&);
};

} // namespace blink

#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_ATTACHMENT_CREATION_PASS_KEY_PROVIDER_H_
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

#include "base/feature_list.h"
#include "base/memory/scoped_refptr.h"
#include "base/types/pass_key.h"
#include "third_party/blink/public/common/messaging/message_port_channel.h"
#include "third_party/blink/renderer/core/html/media/html_media_element.h"
#include "third_party/blink/renderer/modules/mediasource/attachment_creation_pass_key_provider.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
Expand All @@ -33,7 +35,7 @@ std::ostream& operator<<(

CrossThreadMediaSourceAttachment::CrossThreadMediaSourceAttachment(
MediaSource* media_source,
base::PassKey<URLMediaSource> /* passkey */)
AttachmentCreationPassKeyProvider::PassKey /* passkey */)
: registered_media_source_(media_source),
// To use scheduling priority that is the same as postMessage, we use
// kPostedMessage here instead of, say, kMediaElementEvent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
#include "third_party/blink/renderer/core/html/track/audio_track_list.h"
#include "third_party/blink/renderer/core/html/track/video_track.h"
#include "third_party/blink/renderer/core/html/track/video_track_list.h"
#include "third_party/blink/renderer/modules/mediasource/attachment_creation_pass_key_provider.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_attachment_supplement.h"
#include "third_party/blink/renderer/modules/mediasource/url_media_source.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier.h"
#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
Expand All @@ -32,12 +32,15 @@ class CrossThreadMediaSourceAttachment final
// AddMainThread{Audio,Video}TrackToMediaElements' internal helpers.
enum class TrackAddRemovalType { kAudio, kVideo };

// The only intended caller of this constructor is
// URLMediaSource::createObjectUrl (as shown by using the PassKey), executing
// in the worker thread context. The raw pointer is then adopted into a
// scoped_refptr in MediaSourceRegistryImpl::RegisterURL.
// The only intended callers of this constructor are restricted to those able
// to obtain an AttachmentCreationPasskeyProvider's pass key. This method is
// expected to only be called in a worker thread context. The raw pointer is
// then adopted into a scoped_refptr by the caller (e.g.,
// URLMediaSource::createObjectUrl will lead to
// MediaSourceRegistryImpl::RegisterURL doing this scoped_refptr adoption;
// separately, MediaSource::getHandle() does this adoption immediately.)
CrossThreadMediaSourceAttachment(MediaSource* media_source,
base::PassKey<URLMediaSource>);
AttachmentCreationPassKeyProvider::PassKey);

CrossThreadMediaSourceAttachment(const CrossThreadMediaSourceAttachment&) =
delete;
Expand Down
76 changes: 76 additions & 0 deletions blink/renderer/modules/mediasource/media_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <tuple>

#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "build/chromeos_buildflags.h"
#include "media/base/audio_decoder_config.h"
Expand All @@ -33,13 +34,15 @@
#include "third_party/blink/renderer/core/html/track/audio_track_list.h"
#include "third_party/blink/renderer/core/html/track/video_track_list.h"
#include "third_party/blink/renderer/modules/mediasource/cross_thread_media_source_attachment.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_handle_impl.h"
#include "third_party/blink/renderer/modules/mediasource/same_thread_media_source_attachment.h"
#include "third_party/blink/renderer/modules/mediasource/same_thread_media_source_tracer.h"
#include "third_party/blink/renderer/modules/mediasource/source_buffer_track_base_supplement.h"
#include "third_party/blink/renderer/modules/webcodecs/audio_decoder.h"
#include "third_party/blink/renderer/modules/webcodecs/video_decoder.h"
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/blob/blob_url.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
Expand All @@ -49,6 +52,7 @@
#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"

#if BUILDFLAG(USE_PROPRIETARY_CODECS)
#include "media/filters/h264_to_annex_b_bitstream_converter.h"
Expand Down Expand Up @@ -1195,6 +1199,78 @@ void MediaSource::ClearLiveSeekableRange_Locked(
SendUpdatedInfoToMainThreadCache();
}

MediaSourceHandleImpl* MediaSource::getHandle(ExceptionState& exception_state) {
MutexLocker lock(attachment_link_lock_);

DVLOG(3) << __func__;

DCHECK(RuntimeEnabledFeatures::MediaSourceInWorkersEnabled(
GetExecutionContext()) &&
RuntimeEnabledFeatures::MediaSourceInWorkersUsingHandleEnabled(
GetExecutionContext()));

// Per https://github.com/w3c/media-source/pull/306:
// 1. If the implementation does not support creating a handle for this
// MediaSource, then throw a NotSupportedError exception and abort these
// steps.
// TODO(crbug.com/506273): Support MediaSource srcObject attachment idiom for
// main-thread-owned MediaSource objects.
if (IsMainThread() ||
!GetExecutionContext()->IsDedicatedWorkerGlobalScope()) {
LogAndThrowDOMException(exception_state,
DOMExceptionCode::kNotSupportedError,
"MediaSourceHandle creation is currently supported "
"only in a dedicated worker.");
return nullptr;
}

// Per https://github.com/w3c/media-source/pull/306:
// 2. If the readyState attribute is not "closed" then throw an
// InvalidStateError exception and abort these steps.
if (!IsClosed()) {
LogAndThrowDOMException(exception_state,
DOMExceptionCode::kInvalidStateError,
"The MediaSource's readyState is not 'closed'.");
return nullptr;
}

// Per https://github.com/w3c/media-source/pull/306:
// 3. If [[handle already retrieved]] is true, then throw an InvalidStateError
// exception and abort these steps.
if (handle_already_retrieved_) {
LogAndThrowDOMException(
exception_state, DOMExceptionCode::kInvalidStateError,
"The MediaSourceHandle has already been retrieved.");
return nullptr;
}

// Per https://github.com/w3c/media-source/pull/306:
// 4. Create a new MediaSourceHandle object and associated resources, and link
// it internally to this MediaSource.
// 5. Set [[handle already retrieved]] to be true.
// 6. Return the new object.
// TODO(crbug.com/506273): Support MediaSource srcObject attachment idiom for
// main-thread-owned MediaSource objects.
DCHECK(GetExecutionContext()->IsDedicatedWorkerGlobalScope());

// PassKey provider usage here ensures that we are allowed to call the
// attachment constructor.
scoped_refptr<CrossThreadMediaSourceAttachment> attachment =
base::MakeRefCounted<CrossThreadMediaSourceAttachment>(
this, AttachmentCreationPassKeyProvider::GetPassKey());
handle_already_retrieved_ = true;

// Create, but don't "register" an internal blob URL with the security origin
// of for the worker's execution context for use later in a window thread
// media element's attachment to the MediaSource leveraging existing URL
// security checks and logging for legacy MSE object URLs.
SecurityOrigin* origin = GetExecutionContext()->GetMutableSecurityOrigin();
String internal_blob_url = BlobURL::CreatePublicURL(origin).GetString();
DCHECK(!internal_blob_url.IsEmpty());
return MakeGarbageCollected<MediaSourceHandleImpl>(
std::move(attachment), std::move(internal_blob_url));
}

bool MediaSource::IsOpen() const {
return ready_state_ == ReadyState::kOpen;
}
Expand Down
17 changes: 13 additions & 4 deletions blink/renderer/modules/mediasource/media_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "third_party/blink/renderer/core/html/time_ranges.h"
#include "third_party/blink/renderer/modules/event_target_modules.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_attachment_supplement.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_handle_impl.h"
#include "third_party/blink/renderer/modules/mediasource/source_buffer.h"
#include "third_party/blink/renderer/modules/mediasource/source_buffer_list.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
Expand Down Expand Up @@ -96,6 +97,9 @@ class MediaSource final : public EventTargetWithInlineData,
void clearLiveSeekableRange(ExceptionState&)
LOCKS_EXCLUDED(attachment_link_lock_);

MediaSourceHandleImpl* getHandle(ExceptionState&)
LOCKS_EXCLUDED(attachment_link_lock_);

static bool isTypeSupported(ExecutionContext* context, const String& type);

// Helper for isTypeSupported, addSourceBuffer and SourceBuffer changeType.
Expand Down Expand Up @@ -277,11 +281,16 @@ class MediaSource final : public EventTargetWithInlineData,
GUARDED_BY(attachment_link_lock_);
bool context_already_destroyed_ GUARDED_BY(attachment_link_lock_);

// This is to ensure that at most one successful call to a MediaSource
// instance's getHandle() is allowed.
bool handle_already_retrieved_ GUARDED_BY(attachment_link_lock_) = false;

// |attachment_link_lock_| protects read/write of |media_source_attachment_|,
// |attachment_tracer_|, and |context_already_destroyed_|. It is only truly
// necessary for CrossThreadAttachment usage of worker MSE, to prevent
// read/write collision on main thread versus worker thread. Note that
// |attachment_link_lock_| must be released before attempting
// |attachment_tracer_|, |context_already_destroyed_|, and
// |handle_already_retrieved_|.
// It is only truly necessary for CrossThreadAttachment usage of worker MSE,
// to prevent read/write collision on main thread versus worker thread. Note
// that |attachment_link_lock_| must be released before attempting
// CrossThreadMediaSourceAttachment RunExclusively() to avoid deadlock. Many
// scenarios initiated by worker thread need to get the attachment to be able
// to invoke operations on it. The attachment then takes internal
Expand Down
10 changes: 10 additions & 0 deletions blink/renderer/modules/mediasource/media_source.idl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ enum EndOfStreamError {
[RaisesException] void setLiveSeekableRange(double start, double end);
[RaisesException] void clearLiveSeekableRange();

// Initially for just MSE-in-Workers (https://crbug.com/878133). See also
// discussion on https://github.com/w3c/media-source/issues/175,
// https://github.com/w3c/media-source/pull/305 and
// https://github.com/w3c/media-source/pull/306.
[
RaisesException,
RuntimeEnabled=MediaSourceInWorkersUsingHandle,
MeasureAs=MediaSourceGetHandle
] MediaSourceHandle getHandle();

// TODO(crbug.com/1144908): Consider adding an overload which is given a
// SourceBufferConfig.
[CallWith=ExecutionContext] static boolean isTypeSupported(DOMString type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#include "third_party/blink/renderer/modules/mediasource/same_thread_media_source_attachment.h"

#include "base/memory/scoped_refptr.h"
#include "base/types/pass_key.h"
#include "third_party/blink/renderer/core/html/media/html_media_element.h"
#include "third_party/blink/renderer/modules/mediasource/attachment_creation_pass_key_provider.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/modules/mediasource/same_thread_media_source_tracer.h"

Expand Down Expand Up @@ -33,7 +35,7 @@ namespace blink {

SameThreadMediaSourceAttachment::SameThreadMediaSourceAttachment(
MediaSource* media_source,
base::PassKey<URLMediaSource> /* passkey */)
AttachmentCreationPassKeyProvider::PassKey /* passkey */)
: registered_media_source_(media_source),
recent_element_time_(0.0),
element_has_error_(false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "third_party/blink/renderer/core/html/track/audio_track_list.h"
#include "third_party/blink/renderer/core/html/track/video_track.h"
#include "third_party/blink/renderer/core/html/track/video_track_list.h"
#include "third_party/blink/renderer/modules/mediasource/attachment_creation_pass_key_provider.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_attachment_supplement.h"
#include "third_party/blink/renderer/modules/mediasource/url_media_source.h"
Expand All @@ -24,12 +25,16 @@ namespace blink {
class SameThreadMediaSourceAttachment final
: public MediaSourceAttachmentSupplement {
public:
// The only intended caller of this constructor is
// URLMediaSource::createObjectUrl, made more clear by using the PassKey. The
// raw pointer is then adopted into a scoped_refptr in
// MediaSourceRegistryImpl::RegisterURL.
// The only intended callers of this constructor are restricted to those able
// to obtain an AttachmentCreationPasskeyProvider's pass key. This method is
// expected to only be called in window/main thread context. The raw pointer
// is then adopted into a scoped_refptr by the caller (e.g.,
// URLMediaSource::createObjectUrl will lead to
// MediaSourceRegistryImpl::RegisterURL doing this scoped_refptr adoption).
// TODO(crbug.com/506273): For main-thread MediaSource's MediaSourceHandle
// usage via srcObject, MediaSource::getHandle() may also call this.
SameThreadMediaSourceAttachment(MediaSource* media_source,
base::PassKey<URLMediaSource>);
AttachmentCreationPassKeyProvider::PassKey);

SameThreadMediaSourceAttachment(const SameThreadMediaSourceAttachment&) =
delete;
Expand Down
15 changes: 11 additions & 4 deletions blink/renderer/modules/mediasource/url_media_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/media/media_source_attachment.h"
#include "third_party/blink/renderer/core/url/dom_url.h"
#include "third_party/blink/renderer/modules/mediasource/attachment_creation_pass_key_provider.h"
#include "third_party/blink/renderer/modules/mediasource/cross_thread_media_source_attachment.h"
#include "third_party/blink/renderer/modules/mediasource/media_source.h"
#include "third_party/blink/renderer/modules/mediasource/media_source_registry_impl.h"
Expand All @@ -58,19 +59,25 @@ String URLMediaSource::createObjectURL(ScriptState* script_state,

MediaSourceAttachment* attachment;
if (execution_context->IsDedicatedWorkerGlobalScope()) {
// TODO(crbug.com/878133): Disallow this code path (CTMSA in object URL),
// instead use CTMSA via MediaSourceHandleImpl.
DCHECK(!IsMainThread());

// PassKey usage here ensures that only we can call the constructor.
attachment = new CrossThreadMediaSourceAttachment(source, PassKey());
// PassKey provider usage here ensures that we are allowed to call the
// attachment constructor.
attachment = new CrossThreadMediaSourceAttachment(
source, AttachmentCreationPassKeyProvider::GetPassKey());
UseCounter::Count(execution_context,
WebFeature::kCreateObjectURLMediaSourceFromWorker);
} else {
// Other contexts outside of main window thread or conditionally a dedicated
// worker thread are not supported (like Shared Worker and Service Worker).
DCHECK(IsMainThread() && execution_context->IsWindow());

// PassKey usage here ensures that only we can call the constructor.
attachment = new SameThreadMediaSourceAttachment(source, PassKey());
// PassKey provider usage here ensures that we are allowed to call the
// attachment constructor.
attachment = new SameThreadMediaSourceAttachment(
source, AttachmentCreationPassKeyProvider::GetPassKey());
}

UseCounter::Count(execution_context, WebFeature::kCreateObjectURLMediaSource);
Expand Down
2 changes: 0 additions & 2 deletions blink/renderer/modules/mediasource/url_media_source.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_URL_MEDIA_SOURCE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASOURCE_URL_MEDIA_SOURCE_H_

#include "base/types/pass_key.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"

Expand All @@ -44,7 +43,6 @@ class URLMediaSource {
STATIC_ONLY(URLMediaSource);

public:
using PassKey = base::PassKey<URLMediaSource>;
static String createObjectURL(ScriptState*, MediaSource*);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] method clearLiveSeekableRange
[Worker] method constructor
[Worker] method endOfStream
[Worker] method getHandle
[Worker] method removeSourceBuffer
[Worker] method setLiveSeekableRange
[Worker] setter duration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5500,6 +5500,7 @@ interface MediaSource : EventTarget
method clearLiveSeekableRange
method constructor
method endOfStream
method getHandle
method removeSourceBuffer
method setLiveSeekableRange
setter duration
Expand Down

0 comments on commit f31cbc6

Please sign in to comment.