blob: f034c7107fc1c0fc898b3801fb342bab2c7e81a4 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/mediastream/media_stream_set.h"
#include "base/functional/bind.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/modules/mediastream/media_stream.h"
#include "third_party/blink/renderer/modules/mediastream/screen_capture_media_stream_track.h"
#include "third_party/blink/renderer/modules/mediastream/user_media_request.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/modules/screen_details/screen_detailed.h"
#include "third_party/blink/renderer/modules/screen_details/screen_details.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
#include "ui/display/types/display_constants.h"
namespace blink {
namespace {
ScreenDetailed* FindScreenDetailedByDisplayId(
ScreenDetails* screen_details,
std::optional<int64_t> display_id) {
if (display_id == display::kInvalidDisplayId) {
return nullptr;
}
auto screen_iterator =
std::ranges::find_if(screen_details->screens(),
[display_id](const ScreenDetailed* screen_detailed) {
return *display_id == screen_detailed->DisplayId();
});
return (screen_iterator != screen_details->screens().end()) ? *screen_iterator
: nullptr;
}
} // namespace
MediaStreamSet* MediaStreamSet::Create(
ExecutionContext* context,
const GCedMediaStreamDescriptorVector& stream_descriptors,
UserMediaRequestType request_type,
MediaStreamSetInitializedCallback callback) {
DCHECK(IsMainThread());
return MakeGarbageCollected<MediaStreamSet>(
context, stream_descriptors, request_type, std::move(callback));
}
MediaStreamSet::MediaStreamSet(
ExecutionContext* context,
const GCedMediaStreamDescriptorVector& stream_descriptors,
UserMediaRequestType request_type,
MediaStreamSetInitializedCallback callback)
: ExecutionContextClient(context),
media_streams_to_initialize_count_(stream_descriptors.size()),
media_streams_initialized_callback_(std::move(callback)) {
DCHECK(IsMainThread());
if (request_type == UserMediaRequestType::kAllScreensMedia) {
InitializeGetAllScreensMediaStreams(context, stream_descriptors);
return;
}
if (stream_descriptors.empty()) {
// No streams -> all streams are initialized, meaning the set
// itself is fully initialized.
context->GetTaskRunner(TaskType::kInternalMedia)
->PostTask(FROM_HERE,
BindOnce(&MediaStreamSet::OnMediaStreamSetInitialized,
WrapPersistent(this)));
return;
}
// The set will be initialized when all of its streams are initialized.
// When the last stream is initialized, its callback will trigger
// a call to OnMediaStreamSetInitialized.
for (wtf_size_t stream_index = 0; stream_index < stream_descriptors.size();
++stream_index) {
MediaStream::Create(context, stream_descriptors[stream_index],
/*track=*/nullptr,
BindOnce(&MediaStreamSet::OnMediaStreamInitialized,
WrapPersistent(this)));
}
}
void MediaStreamSet::Trace(Visitor* visitor) const {
visitor->Trace(initialized_media_streams_);
ExecutionContextClient::Trace(visitor);
}
void MediaStreamSet::InitializeGetAllScreensMediaStreams(
ExecutionContext* context,
const GCedMediaStreamDescriptorVector& stream_descriptors) {
DCHECK(IsMainThread());
LocalDOMWindow* const window = To<LocalDOMWindow>(context);
DCHECK(window);
// TODO(crbug.com/1358949): Move the generation of the |ScreenDetails| object
// next to the generation of the descriptors and store them as members to
// avoid race conditions. Further, match the getAllScreensMedia API and the
// window placement API by unique IDs instead of assuming the same order.
ScreenDetails* const screen_details =
MakeGarbageCollected<ScreenDetails>(window);
const bool screen_details_match_descriptors =
screen_details->screens().size() == stream_descriptors.size();
for (wtf_size_t stream_index = 0; stream_index < stream_descriptors.size();
++stream_index) {
MediaStreamDescriptor* const descriptor = stream_descriptors[stream_index];
DCHECK_EQ(1u, descriptor->NumberOfVideoComponents());
ScreenDetailed* screen = FindScreenDetailedByDisplayId(
screen_details,
descriptor->VideoComponent(0u)->Source()->GetDisplayId());
MediaStreamTrack* video_track =
MakeGarbageCollected<ScreenCaptureMediaStreamTrack>(
context, descriptor->VideoComponent(0u),
screen_details_match_descriptors ? screen_details : nullptr,
screen);
initialized_media_streams_.push_back(
MediaStream::Create(context, descriptor, {}, {video_track}));
}
context->GetTaskRunner(TaskType::kInternalMedia)
->PostTask(FROM_HERE,
BindOnce(&MediaStreamSet::OnMediaStreamSetInitialized,
WrapPersistent(this)));
}
void MediaStreamSet::OnMediaStreamSetInitialized() {
DCHECK(IsMainThread());
std::move(std::move(media_streams_initialized_callback_))
.Run(initialized_media_streams_);
}
// TODO(crbug.com/1300883): Clean up other streams if one stream capture
// results in an error. This is only required for getAllScreensMedia.
// Currently existing functionality generates only one stream which is not
// affected by this change.
void MediaStreamSet::OnMediaStreamInitialized(
MediaStream* initialized_media_stream) {
DCHECK(IsMainThread());
DCHECK_LT(initialized_media_streams_.size(),
media_streams_to_initialize_count_);
initialized_media_streams_.push_back(initialized_media_stream);
if (initialized_media_streams_.size() == media_streams_to_initialize_count_) {
OnMediaStreamSetInitialized();
}
}
} // namespace blink