Skip to content

Commit

Permalink
OOP-VD: Pass GpuFeatureInfo to utility process using mojo.
Browse files Browse the repository at this point in the history
This CL adds the plumbing needed to pass a gpu::GpuFeatureInfo to the
utility process that hosts out-of-process video decoders. The general
approach is to create a new interface that allows the browser process to
pass a gpu::GpuFeatureInfo immediately after starting the utility
process. This interface is only used by the browser process of
ash-chrome or Chrome-for-Linux and this implies that the GpuFeatureInfo
does not need to be a versioned mojo struct.

Bug: b:195769334
Test: block VP9.2 in gpu_driver_bug_list.json for Ash (but not for LaCrOS) and make sure that LaCrOS w/ OOP-VD fails to use hardware decoding for a VP9.2 video.
Change-Id: I9e634b6ec840e17819139e1246b4c12602426b93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4234494
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Andres Calderon Jaramillo <andrescj@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Reviewed-by: Pilar Molina Lopez <pmolinalopez@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1104182}
  • Loading branch information
andrescj-chromium authored and Chromium LUCI CQ committed Feb 11, 2023
1 parent f1c9765 commit a1abb37
Show file tree
Hide file tree
Showing 13 changed files with 356 additions and 65 deletions.
174 changes: 155 additions & 19 deletions content/browser/media/stable_video_decoder_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,181 @@

#include "content/public/browser/stable_video_decoder_factory.h"

#include "base/containers/queue.h"
#include "build/chromeos_buildflags.h"
#include "components/viz/common/switches.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "content/public/browser/gpu_utils.h"
#include "content/public/browser/service_process_host.h"
#include "media/mojo/mojom/stable/stable_video_decoder.mojom.h"
#include "mojo/public/cpp/bindings/remote_set.h"

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/lacros/lacros_service.h"
#endif

namespace content {

#if BUILDFLAG(ALLOW_HOSTING_OOP_VIDEO_DECODER)

namespace {

// StableVideoDecoderFactoryProcessLauncher is a helper singleton class that
// launches utility processes to host a
// media::stable::mojom::StableVideoDecoderFactory once the gpu::GpuFeatureInfo
// is known.
class StableVideoDecoderFactoryProcessLauncher final
: public GpuDataManagerObserver {
public:
static StableVideoDecoderFactoryProcessLauncher& Instance() {
static base::NoDestructor<StableVideoDecoderFactoryProcessLauncher>
instance;
return *instance;
}

StableVideoDecoderFactoryProcessLauncher(
const StableVideoDecoderFactoryProcessLauncher&) = delete;
StableVideoDecoderFactoryProcessLauncher& operator=(
const StableVideoDecoderFactoryProcessLauncher&) = delete;

void LaunchWhenGpuFeatureInfoIsKnown(
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>
receiver) {
if (gpu_preferences_.disable_accelerated_video_decode) {
return;
}
if (ui_thread_task_runner_->RunsTasksInCurrentSequence()) {
LaunchWhenGpuFeatureInfoIsKnownOnUIThread(std::move(receiver));
return;
}
// base::Unretained(this) is safe because *|this| is never destroyed.
ui_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&StableVideoDecoderFactoryProcessLauncher::
LaunchWhenGpuFeatureInfoIsKnownOnUIThread,
base::Unretained(this), std::move(receiver)));
}

private:
friend class base::NoDestructor<StableVideoDecoderFactoryProcessLauncher>;

StableVideoDecoderFactoryProcessLauncher()
: ui_thread_task_runner_(GetUIThreadTaskRunner({})),
gpu_preferences_(content::GetGpuPreferencesFromCommandLine()) {}
~StableVideoDecoderFactoryProcessLauncher() final = default;

// GpuDataManagerObserver implementation.
void OnGpuInfoUpdate() final {
if (ui_thread_task_runner_->RunsTasksInCurrentSequence()) {
OnGpuInfoUpdateOnUIThread();
return;
}
// base::Unretained(this) is safe because *|this| is never destroyed.
ui_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&StableVideoDecoderFactoryProcessLauncher::
OnGpuInfoUpdateOnUIThread,
base::Unretained(this)));
}

void OnGpuInfoUpdateOnUIThread() {
DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);

auto* manager = GpuDataManagerImpl::GetInstance();
if (!manager->IsGpuFeatureInfoAvailable()) {
return;
}
gpu_feature_info_ = manager->GetGpuFeatureInfo();

while (!pending_factory_receivers_.empty()) {
auto factory_receiver = std::move(pending_factory_receivers_.front());
pending_factory_receivers_.pop();
LaunchOnUIThread(std::move(factory_receiver));
}
}

void LaunchWhenGpuFeatureInfoIsKnownOnUIThread(
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>
receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);

if (gpu_feature_info_) {
LaunchOnUIThread(std::move(receiver));
return;
}
pending_factory_receivers_.emplace(std::move(receiver));
GpuDataManagerImpl::GetInstance()->AddObserver(this);
OnGpuInfoUpdateOnUIThread();
}

void LaunchOnUIThread(
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>
receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);

#if BUILDFLAG(IS_CHROMEOS_ASH)
const bool enable_direct_video_decoder =
gpu_preferences_.enable_chromeos_direct_video_decoder;
#else
const bool enable_direct_video_decoder = true;
#endif

mojo::Remote<media::stable::mojom::StableVideoDecoderFactoryProcess>
process;
ServiceProcessHost::Launch(
process.BindNewPipeAndPassReceiver(),
ServiceProcessHost::Options().WithDisplayName("Video Decoder").Pass());
process->InitializeStableVideoDecoderFactory(
*gpu_feature_info_, enable_direct_video_decoder, std::move(receiver));
processes_.Add(std::move(process));
}

const scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner_;
const gpu::GpuPreferences gpu_preferences_;
SEQUENCE_CHECKER(ui_sequence_checker_);

// Each utility process launched by this class hosts a
// StableVideoDecoderFactoryProcess implementation which is used to broker a
// StableVideoDecoderFactory connection. The process stays alive until either
// a) the StableVideoDecoderFactoryProcess connection is lost, or b) it
// crashes. Case (a) will typically happen when the client that uses the
// StableVideoDecoderFactory connection closes its endpoint (e.g., a renderer
// process dies). In that situation, the utility process should detect that
// the StableVideoDecoderFactory connection got lost and subsequently close
// the StableVideoDecoderFactoryProcess connection which should cause the
// termination of the process. We need to keep the
// StableVideoDecoderFactoryProcess connection endpoint in a RemoteSet to keep
// the process alive until the StableVideoDecoderFactory connection is lost.
mojo::RemoteSet<media::stable::mojom::StableVideoDecoderFactoryProcess>
processes_ GUARDED_BY_CONTEXT(ui_sequence_checker_);

absl::optional<gpu::GpuFeatureInfo> gpu_feature_info_
GUARDED_BY_CONTEXT(ui_sequence_checker_);

// This member holds onto any requests for a StableVideoDecoderFactory until
// the gpu::GpuFeatureInfo is known.
base::queue<
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>>
pending_factory_receivers_ GUARDED_BY_CONTEXT(ui_sequence_checker_);
};

} // namespace

#endif // BUILDFLAG(ALLOW_HOSTING_OOP_VIDEO_DECODER)

void LaunchStableVideoDecoderFactory(
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>
receiver) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#if BUILDFLAG(ALLOW_HOSTING_OOP_VIDEO_DECODER)
StableVideoDecoderFactoryProcessLauncher::Instance()
.LaunchWhenGpuFeatureInfoIsKnown(std::move(receiver));
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
// For LaCrOS, we need to use crosapi to establish a
// StableVideoDecoderFactory connection to ash-chrome.
auto* lacros_service = chromeos::LacrosService::Get();
if (lacros_service && lacros_service->IsStableVideoDecoderFactoryAvailable())
lacros_service->BindStableVideoDecoderFactory(std::move(receiver));
#else
std::vector<std::string> extra_switches;
#if BUILDFLAG(IS_CHROMEOS_ASH)
gpu::GpuPreferences gpu_preferences =
content::GetGpuPreferencesFromCommandLine();
if (!gpu_preferences.enable_chromeos_direct_video_decoder) {
// TODO(b/195769334): consider passing |gpu_preferences|.ToSwitchValue() to
// the utility process instead.
extra_switches.push_back(
::switches::kPlatformDisallowsChromeOSDirectVideoDecoder);
}
#endif
ServiceProcessHost::Launch(
std::move(receiver),
ServiceProcessHost::Options()
.WithDisplayName("Video Decoder")
.WithExtraCommandLineSwitches(std::move(extra_switches))
.Pass());
#endif
}

Expand Down
5 changes: 3 additions & 2 deletions content/public/browser/stable_video_decoder_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

namespace content {

// Binds a StableVideoDecoderFactory PendingReceiver by either using the
// crosapi (on LaCrOS) or starting a new utility process (on non-LaCrOS).
// Binds a StableVideoDecoderFactory PendingReceiver by either using the crosapi
// (on LaCrOS) or starting a new utility process (on non-LaCrOS). This function
// can be called from any thread.
CONTENT_EXPORT void LaunchStableVideoDecoderFactory(
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>
receiver);
Expand Down
15 changes: 7 additions & 8 deletions content/utility/services.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ extern sandbox::TargetServices* g_utility_target_services;

#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)) && \
(BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
#include "media/mojo/services/stable_video_decoder_factory_service.h" // nogncheck
#include "media/mojo/services/stable_video_decoder_factory_process_service.h" // nogncheck
#endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)) &&
// (BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))

Expand Down Expand Up @@ -321,12 +321,11 @@ auto RunOOPArcVideoAcceleratorFactoryService(

#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)) && \
(BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
auto RunStableVideoDecoderFactoryService(
mojo::PendingReceiver<media::stable::mojom::StableVideoDecoderFactory>
receiver) {
auto factory = std::make_unique<media::StableVideoDecoderFactoryService>();
factory->BindReceiver(std::move(receiver));
return factory;
auto RunStableVideoDecoderFactoryProcessService(
mojo::PendingReceiver<
media::stable::mojom::StableVideoDecoderFactoryProcess> receiver) {
return std::make_unique<media::StableVideoDecoderFactoryProcessService>(
std::move(receiver));
}
#endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)) &&
// (BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
Expand Down Expand Up @@ -391,7 +390,7 @@ void RegisterMainThreadServices(mojo::ServiceFactory& services) {

#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)) && \
(BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))
services.Add(RunStableVideoDecoderFactoryService);
services.Add(RunStableVideoDecoderFactoryProcessService);
#endif // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)) &&
// (BUILDFLAG(USE_VAAPI) || BUILDFLAG(USE_V4L2_CODEC))

Expand Down
1 change: 1 addition & 0 deletions media/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ buildflag_header("media_buildflags") {

flags = [
"ALLOW_OOP_VIDEO_DECODER=$allow_oop_video_decoder",
"ALLOW_HOSTING_OOP_VIDEO_DECODER=$allow_hosting_oop_video_decoder",
"ALTERNATE_CDM_STORAGE_ID_KEY=\"$alternate_cdm_storage_id_key\"",
"CHROME_WIDE_ECHO_CANCELLATION=$chrome_wide_echo_cancellation_supported",
"ENABLE_PLATFORM_AC3_EAC3_AUDIO=$enable_platform_ac3_eac3_audio",
Expand Down
15 changes: 11 additions & 4 deletions media/media_options.gni
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ is_cast_media_device = is_castos || is_cast_android
# which makes the interaction with platform drivers (for the purposes of
# hardware accelerated video decoding) happen on utility processes for stability
# and security purposes. When |allow_oop_video_decoder| is true, code to
# implement this feature is compiled. Note that even if
# |allow_oop_video_decoder| is true, the feature may be disabled by a runtime
# flag.
# use this feature is compiled. Note that even if |allow_oop_video_decoder| is
# true, the feature may be disabled by a runtime flag.
#
# When |allow_hosting_oop_video_decoder| is true, code to host the video decoder
# utility processes is compiled. Note that even if
# |allow_hosting_oop_video_decoder| is true, the hosting of these utility
# processes may be disabled by a runtime flag.
#
# TODO(b/195769334): finish replacing usages of (is_linux || is_chromeos) with
# allow_oop_video_decoder where appropriate.
# allow_oop_video_decoder where appropriate. Also, finish replacing usages of
# (is_linux || is_chromeos_ash) with allow_hosting_oop_video_decoder where
# appropriate.
allow_oop_video_decoder = is_linux || is_chromeos
allow_hosting_oop_video_decoder = is_linux || is_chromeos_ash

declare_args() {
# Allows distributions to link pulseaudio directly (DT_NEEDED) instead of
Expand Down
1 change: 1 addition & 0 deletions media/mojo/mojom/stable/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mojom("stable_video_decoder") {

public_deps = [
":native_pixmap_handle",
"//gpu/ipc/common:interfaces",
"//media/mojo/mojom:encryption_pattern",
"//mojo/public/mojom/base",
"//sandbox/policy/mojom",
Expand Down
37 changes: 31 additions & 6 deletions media/mojo/mojom/stable/stable_video_decoder.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

module media.stable.mojom;

import "gpu/ipc/common/gpu_feature_info.mojom";
import "media/mojo/mojom/stable/stable_video_decoder_types.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
import "sandbox/policy/mojom/sandbox.mojom";
Expand Down Expand Up @@ -157,14 +158,38 @@ const sandbox.mojom.Sandbox kStableVideoDecoderFactoryServiceSandbox
const sandbox.mojom.Sandbox kStableVideoDecoderFactoryServiceSandbox
= sandbox.mojom.Sandbox.kNoSandbox;

// A StableVideoDecoderFactory service is intended to be hosted in a utility
// process in either ash-chrome or Chrome-for-Linux. It allows the browser
// process to bind a StableVideoDecoder on behalf of some client which can be,
// e.g., the lacros-chrome browser process or a renderer process in ash-chrome.
// A StableVideoDecoderFactory allows the browser process to bind a
// StableVideoDecoder on behalf of some client which can be, e.g., the
// lacros-chrome browser process or a renderer process in ash-chrome.
// Next min method ID: 1
[ServiceSandbox=kStableVideoDecoderFactoryServiceSandbox, Stable,
Uuid="d6047fd9-fffb-4e37-ad9b-383a1c9e1d2d"]
[Stable, Uuid="d6047fd9-fffb-4e37-ad9b-383a1c9e1d2d"]
interface StableVideoDecoderFactory {
// Creates a StableVideoDecoder and should be called by the browser process.
CreateStableVideoDecoder@0(pending_receiver<StableVideoDecoder> receiver);
};

// A StableVideoDecoderFactoryProcess is intended to be hosted in a utility
// process in either ash-chrome or Chrome-for-linux. The client is expected to
// be the browser process of ash-chrome or Chrome-for-linux. The intended usage
// is as follows:
//
// 1) The browser process of ash-chrome or Chrome-for-linux receives a request
// to bind a pending_receiver<StableVideoDecoderFactory>.
//
// 2) That browser process starts a utility process to bind a
// pending_receiver<StableVideoDecoderFactoryProcess>. It then uses this
// connection to call InitializeStableVideoDecoderFactory() with the
// pending_receiver<StableVideoDecoderFactory> from (1).
[ServiceSandbox=kStableVideoDecoderFactoryServiceSandbox,
EnableIf=is_linux_or_chromeos_ash]
interface StableVideoDecoderFactoryProcess {
// Initializes a StableVideoDecoderFactory using |gpu_feature_info| to
// restrict the supported video decode configurations. If
// |enable_direct_video_decoder| is true, the StableVideoDecoderFactory shall
// create video decoders backed by the direct video decoder stack. Otherwise,
// it will use the legacy VDA implementations.
InitializeStableVideoDecoderFactory(
gpu.mojom.GpuFeatureInfo gpu_feature_info,
bool enable_direct_video_decoder,
pending_receiver<StableVideoDecoderFactory> receiver);
};
2 changes: 2 additions & 0 deletions media/mojo/services/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ component("services") {

if ((is_chromeos_ash || is_linux) && (use_vaapi || use_v4l2_codec)) {
sources += [
"stable_video_decoder_factory_process_service.cc",
"stable_video_decoder_factory_process_service.h",
"stable_video_decoder_factory_service.cc",
"stable_video_decoder_factory_service.h",
"stable_video_decoder_service.cc",
Expand Down

0 comments on commit a1abb37

Please sign in to comment.