Skip to content

Commit

Permalink
Implement cross-thread MediaSource attachments
Browse files Browse the repository at this point in the history
Port relevant Chromium code to the Cobalt 25 codebase to allow for
`MediaSource` objects on a Dedicated Worker to render frames on a
`HTMLMediaElement` owned by the main browser thread.

Usage of this feature is gated behind the `MediaSource.EnableInWorkers`
H5VCC flag. In addition, the
`MediaElement.EnableUsingMediaSourceBufferedRange` and
`MediaElement.EnableUsingMediaSourceAttachmentMethods` flags are also
required as pre-requisite features.

This commit does introduce mutex locks to the `SourceBuffer` and `MediaSource`
classes that cannot be fully gated behind H5VCC flags. However, current
usage of both classes is contained to single-threaded usecases. As such,
the mutex lock code should be an effective no-op.

This is based off of the following Chromium commits:

* https://chromium-review.googlesource.com/c/chromium/src/+/2459431
* https://chromium-review.googlesource.com/c/chromium/src/+/2617351
* https://chromium-review.googlesource.com/c/chromium/src/+/2909389

b/338425382
  • Loading branch information
at-ninja committed Oct 31, 2024
1 parent 7e04f7f commit 5fc5cd4
Show file tree
Hide file tree
Showing 10 changed files with 883 additions and 140 deletions.
1 change: 1 addition & 0 deletions cobalt/dom/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ static_library("dom") {
"media_source.cc",
"media_source.h",
"media_source_attachment.h",
"media_source_attachment_supplement.cc",
"media_source_attachment_supplement.h",
"memory_info.cc",
"memory_info.h",
Expand Down
224 changes: 189 additions & 35 deletions cobalt/dom/cross_thread_media_source_attachment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

#include "cobalt/dom/cross_thread_media_source_attachment.h"

#include <utility>

#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "cobalt/dom/media_source.h"
#include "cobalt/dom/media_source_attachment.h"
Expand All @@ -34,98 +37,249 @@ namespace dom {
CrossThreadMediaSourceAttachment::CrossThreadMediaSourceAttachment(
scoped_refptr<MediaSource> media_source)
: media_source_(media_source),
task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
worker_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
recent_element_time_(0.0),
element_has_error_(false) {}
element_has_error_(false),
element_has_max_video_capabilities_(false),
have_ever_attached_(false),
have_ever_started_closing_(false) {}

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

tracer->Trace(attached_element_);
tracer->Trace(media_source_);
base::AutoLock auto_lock(attachment_state_lock_);
if (worker_runner_->RunsTasksInCurrentSequence()) {
tracer->Trace(media_source_);
} else {
tracer->Trace(attached_element_);
}
}

bool CrossThreadMediaSourceAttachment::StartAttachingToMediaElement(
HTMLMediaElement* media_element) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return false;
base::AutoLock auto_lock(attachment_state_lock_);
// Called from main thread. At this point, we can only check if
// this is not being called from the worker thread.
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

if (have_ever_attached_) {
return false;
}

DCHECK(!have_ever_started_closing_);
DCHECK(!attached_element_);

// Attach on the main thread.
bool success = media_source_->StartAttachingToMediaElement(this);
if (!success) {
return false;
}

attached_element_ = media_element;
main_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
DCHECK(main_runner_->RunsTasksInCurrentSequence());

// Grab initial state from the HTMLMediaElement while on the main thread.
recent_element_time_ = media_element->current_time(NULL);
element_has_error_ = static_cast<bool>(media_element->error());
element_has_max_video_capabilities_ =
media_element->HasMaxVideoCapabilities();

DCHECK(!element_has_error_);

have_ever_attached_ = true;
return true;
}

void CrossThreadMediaSourceAttachment::CompleteAttachingToMediaElement(
::media::ChunkDemuxer* chunk_demuxer) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(have_ever_attached_);
DCHECK(main_runner_->RunsTasksInCurrentSequence());
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

worker_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CrossThreadMediaSourceAttachment::
CompleteAttachingToMediaElementOnWorkerThread,
this, chunk_demuxer));
}

void CrossThreadMediaSourceAttachment::
CompleteAttachingToMediaElementOnWorkerThread(
::media::ChunkDemuxer* chunk_demuxer) {
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(worker_runner_->RunsTasksInCurrentSequence());
DCHECK(!main_runner_->RunsTasksInCurrentSequence());

if (have_ever_started_closing_) {
return;
}

media_source_->CompleteAttachingToMediaElement(chunk_demuxer);
}

void CrossThreadMediaSourceAttachment::Close() {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(have_ever_attached_);
DCHECK(!have_ever_started_closing_);
DCHECK(main_runner_->RunsTasksInCurrentSequence());
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

have_ever_started_closing_ = true;

worker_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CrossThreadMediaSourceAttachment::CloseOnWorkerThread,
this));
}

void CrossThreadMediaSourceAttachment::CloseOnWorkerThread() {
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(have_ever_started_closing_);
DCHECK(attached_element_);
DCHECK(worker_runner_->RunsTasksInCurrentSequence());
DCHECK(!main_runner_->RunsTasksInCurrentSequence());

media_source_->Close();
}

scoped_refptr<TimeRanges> CrossThreadMediaSourceAttachment::GetBufferedRange()
const {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return nullptr;
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(attached_element_);
DCHECK(!have_ever_started_closing_);
DCHECK(have_ever_attached_);
DCHECK(main_runner_->RunsTasksInCurrentSequence());
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

return media_source_->GetBufferedRange();
}

MediaSourceReadyState CrossThreadMediaSourceAttachment::GetReadyState() const {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return kMediaSourceReadyStateClosed;
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(attached_element_);
DCHECK(!have_ever_started_closing_);
DCHECK(have_ever_attached_);
DCHECK(main_runner_->RunsTasksInCurrentSequence());
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

return media_source_->ready_state();
}

void CrossThreadMediaSourceAttachment::NotifyDurationChanged(double duration) {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
void CrossThreadMediaSourceAttachment::NotifyDurationChanged(
double /*duration*/) {
attachment_state_lock_.AssertAcquired();
DCHECK(worker_runner_->RunsTasksInCurrentSequence());

// No-op for cross thread MSA.
}

bool CrossThreadMediaSourceAttachment::HasMaxVideoCapabilities() const {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return false;
attachment_state_lock_.AssertAcquired();
DCHECK(worker_runner_->RunsTasksInCurrentSequence());

return element_has_max_video_capabilities_;
}

double CrossThreadMediaSourceAttachment::GetRecentMediaTime() {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return 0;
attachment_state_lock_.AssertAcquired();
DCHECK(worker_runner_->RunsTasksInCurrentSequence());

return recent_element_time_;
}

bool CrossThreadMediaSourceAttachment::GetElementError() {
DCHECK_NE(task_runner_, base::SequencedTaskRunner::GetCurrentDefault());
NOTIMPLEMENTED();
return false;
attachment_state_lock_.AssertAcquired();
DCHECK(worker_runner_->RunsTasksInCurrentSequence());

return element_has_error_;
}

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();
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(main_runner_->RunsTasksInCurrentSequence());
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

worker_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CrossThreadMediaSourceAttachment::UpdateWorkerThreadTimeCache, this,
time));
}

void CrossThreadMediaSourceAttachment::UpdateWorkerThreadTimeCache(
double time) {
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(worker_runner_->RunsTasksInCurrentSequence());
DCHECK(!main_runner_->RunsTasksInCurrentSequence());

recent_element_time_ = time;
}

void CrossThreadMediaSourceAttachment::OnElementError() {
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(main_runner_->RunsTasksInCurrentSequence());
DCHECK(!worker_runner_->RunsTasksInCurrentSequence());

worker_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CrossThreadMediaSourceAttachment::HandleElementErrorOnWorkerThread,
this));
}

void CrossThreadMediaSourceAttachment::HandleElementErrorOnWorkerThread() {
base::AutoLock auto_lock(attachment_state_lock_);
DCHECK(!element_has_error_)
<< "At most one transition to element error per attachment is expected";
NOTIMPLEMENTED();
DCHECK(worker_runner_->RunsTasksInCurrentSequence());
DCHECK(main_runner_->RunsTasksInCurrentSequence());

element_has_error_ = true;
}

bool CrossThreadMediaSourceAttachment::FullyAttachedOrSameThread() const {
attachment_state_lock_.AssertAcquired();
DCHECK(have_ever_attached_);
DCHECK(worker_runner_->RunsTasksInCurrentSequence());
DCHECK(!main_runner_->RunsTasksInCurrentSequence());

return attached_element_ && !have_ever_started_closing_;
}

bool CrossThreadMediaSourceAttachment::RunExclusively(
bool abort_if_not_fully_attached, RunExclusivelyCB cb) {
base::AutoLock auto_lock(attachment_state_lock_);

DCHECK(have_ever_attached_);
DCHECK(worker_runner_->RunsTasksInCurrentSequence());

if (abort_if_not_fully_attached &&
(!attached_element_ || have_ever_started_closing_)) {
return false;
}

std::move(cb).Run();
return true;
}

void CrossThreadMediaSourceAttachment::
AssertCrossThreadMutexIsAcquiredForDebugging() {
attachment_state_lock_.AssertAcquired();
}

} // namespace dom
} // namespace cobalt
Loading

0 comments on commit 5fc5cd4

Please sign in to comment.