0

Expose AV Settings outside cast_streaming Component

For use with other embedders aside from Fuchsia, the AV Settings (such
as valid codecs, screen dimensions, etc...) must be exposed and
set-able.

This CL makes the following changes:
- Expose the openscreen::cast::Preferences object as a top-level
  parameter for the embedder to provide.
- Expose conversion utilities to be used by embedders in creating the
  above object
- Add unit tests to the config conversions file.

Bug: 1207721, internal b/182429089
Change-Id: I3af458b0491d2c413b1feee4801542c88e83bdb9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2935034
Reviewed-by: Matthew Wolenetz <wolenetz@chromium.org>
Reviewed-by: Fabrice de Gans <fdegans@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Commit-Queue: Ryan Keane <rwkeane@google.com>
Cr-Commit-Position: refs/heads/master@{#893086}
This commit is contained in:
Ryan Keane
2021-06-16 19:01:33 +00:00
committed by Chromium LUCI CQ
parent f9e2951503
commit 3381995478
17 changed files with 586 additions and 190 deletions

@ -22,16 +22,28 @@ source_set("core") {
sources = [
"cast_message_port_impl.cc",
"cast_message_port_impl.h",
"config_conversions.cc",
"config_conversions.h",
"message_serialization.cc",
"message_serialization.h",
]
}
source_set("receiver_session_public") {
deps = [
"//base",
"//mojo/public/cpp/system",
]
public_deps = [
"//components/cast_streaming/public/mojom",
"//third_party/openscreen/src/cast/streaming:receiver",
]
visibility = [ ":*" ]
sources = [ "public/receiver_session.h" ]
}
source_set("receiver_session") {
deps = [
":core",
":receiver_session_public",
":streaming_session",
"//base",
"//media",
@ -41,10 +53,9 @@ source_set("receiver_session") {
]
public_deps = [
"//components/cast_streaming/public/mojom",
"//third_party/openscreen/src/cast/common:channel",
"//third_party/openscreen/src/cast/streaming:receiver",
]
visibility = [ ":*" ]
public = [ "public/receiver_session.h" ]
sources = [
"receiver_session_impl.cc",
"receiver_session_impl.h",
@ -54,7 +65,9 @@ source_set("receiver_session") {
source_set("streaming_session") {
deps = [
":core",
":receiver_session_public",
"//base",
"//components/cast_streaming/public",
"//components/openscreen_platform",
"//media",
"//media/mojo/common",
@ -84,13 +97,12 @@ source_set("network_context") {
sources = [ "network_context_getter.cc" ]
}
# TODO(crbug.com/1208194): Also move cast_streaming_session_client here from
# //fuchsia.
source_set("browser") {
public_deps = [
":network_context",
":receiver_session",
":receiver_session_public",
]
deps = [ ":receiver_session" ]
}
# TODO(crbug.com/1207715): Move to /tests directory.
@ -98,6 +110,7 @@ source_set("test_sender") {
testonly = true
deps = [
":core",
"//components/cast_streaming/public",
"//media/mojo/common",
"//media/mojo/mojom",
"//mojo/public/cpp/system",
@ -125,6 +138,7 @@ source_set("test_receiver") {
deps = [
":streaming_session",
"//base",
"//components/cast_streaming/public",
"//components/openscreen_platform",
"//media",
"//media/mojo/common",

@ -6,8 +6,8 @@
#include "base/bind.h"
#include "base/time/time.h"
#include "components/cast_streaming/browser/config_conversions.h"
#include "components/cast_streaming/browser/stream_consumer.h"
#include "components/cast_streaming/public/config_conversions.h"
#include "media/base/timestamp_constants.h"
#include "media/mojo/common/mojo_decoder_buffer_converter.h"
#include "mojo/public/cpp/system/data_pipe.h"
@ -38,6 +38,7 @@ namespace cast_streaming {
CastStreamingSession::ReceiverSessionClient::ReceiverSessionClient(
CastStreamingSession::Client* client,
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
std::unique_ptr<cast_api_bindings::MessagePort> message_port,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(task_runner),
@ -51,15 +52,9 @@ CastStreamingSession::ReceiverSessionClient::ReceiverSessionClient(
DCHECK(task_runner);
DCHECK(client_);
// TODO(crbug.com/1087520): Add streaming session Constraints and
// DisplayDescription.
receiver_session_ = std::make_unique<openscreen::cast::ReceiverSession>(
this, &environment_, &cast_message_port_impl_,
openscreen::cast::ReceiverSession::Preferences(
{openscreen::cast::VideoCodec::kH264,
openscreen::cast::VideoCodec::kVp8},
{openscreen::cast::AudioCodec::kAac,
openscreen::cast::AudioCodec::kOpus}));
std::move(*av_constraints));
init_timeout_timer_.Start(
FROM_HERE, kInitTimeout,
@ -102,9 +97,8 @@ CastStreamingSession::ReceiverSessionClient::InitializeAudioConsumer(
base::BindRepeating(&base::OneShotTimer::Reset,
base::Unretained(&data_timeout_timer_)));
return AudioStreamInfo{
AudioCaptureConfigToAudioDecoderConfig(audio_capture_config),
std::move(data_pipe_consumer)};
return AudioStreamInfo{ToAudioDecoderConfig(audio_capture_config),
std::move(data_pipe_consumer)};
}
absl::optional<CastStreamingSession::VideoStreamInfo>
@ -137,9 +131,8 @@ CastStreamingSession::ReceiverSessionClient::InitializeVideoConsumer(
base::BindRepeating(&base::OneShotTimer::Reset,
base::Unretained(&data_timeout_timer_)));
return VideoStreamInfo{
VideoCaptureConfigToVideoDecoderConfig(video_capture_config),
std::move(data_pipe_consumer)};
return VideoStreamInfo{ToVideoDecoderConfig(video_capture_config),
std::move(data_pipe_consumer)};
}
void CastStreamingSession::ReceiverSessionClient::OnNegotiated(
@ -266,13 +259,14 @@ CastStreamingSession::~CastStreamingSession() = default;
void CastStreamingSession::Start(
Client* client,
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
std::unique_ptr<cast_api_bindings::MessagePort> message_port,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
DVLOG(1) << __func__;
DCHECK(client);
DCHECK(!receiver_session_);
receiver_session_ = std::make_unique<ReceiverSessionClient>(
client, std::move(message_port), task_runner);
client, std::move(av_constraints), std::move(message_port), task_runner);
}
void CastStreamingSession::Stop() {

@ -12,6 +12,7 @@
#include "base/timer/timer.h"
#include "components/cast/message_port/message_port.h"
#include "components/cast_streaming/browser/cast_message_port_impl.h"
#include "components/cast_streaming/browser/public/receiver_session.h"
#include "components/openscreen_platform/network_util.h"
#include "components/openscreen_platform/task_runner.h"
#include "media/base/audio_decoder_config.h"
@ -85,7 +86,11 @@ class CastStreamingSession {
// * On failure, OnSessionEnded() will be called.
// * When a new offer is sent by the Cast Streaming Sender,
// OnSessionReinitialization() will be called.
//
// |av_constraints| specifies the supported media codecs and limitations
// surrounding this support.
void Start(Client* client,
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
std::unique_ptr<cast_api_bindings::MessagePort> message_port,
scoped_refptr<base::SequencedTaskRunner> task_runner);
@ -101,6 +106,7 @@ class CastStreamingSession {
public:
ReceiverSessionClient(
CastStreamingSession::Client* client,
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
std::unique_ptr<cast_api_bindings::MessagePort> message_port,
scoped_refptr<base::SequencedTaskRunner> task_runner);
~ReceiverSessionClient() final;

@ -1,112 +0,0 @@
// Copyright 2020 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.
#include "components/cast_streaming/browser/config_conversions.h"
#include "base/notreached.h"
#include "media/base/media_util.h"
namespace cast_streaming {
openscreen::cast::AudioCaptureConfig AudioDecoderConfigToAudioCaptureConfig(
const media::AudioDecoderConfig& audio_config) {
openscreen::cast::AudioCaptureConfig audio_capture_config;
switch (audio_config.codec()) {
case media::AudioCodec::kCodecAAC:
audio_capture_config.codec = openscreen::cast::AudioCodec::kAac;
break;
case media::AudioCodec::kCodecOpus:
audio_capture_config.codec = openscreen::cast::AudioCodec::kOpus;
break;
default:
NOTREACHED();
}
audio_capture_config.channels =
media::ChannelLayoutToChannelCount(audio_config.channel_layout());
audio_capture_config.sample_rate = audio_config.samples_per_second();
return audio_capture_config;
}
openscreen::cast::VideoCaptureConfig VideoDecoderConfigToVideoCaptureConfig(
const media::VideoDecoderConfig& video_config) {
openscreen::cast::VideoCaptureConfig video_capture_config;
switch (video_config.codec()) {
case media::VideoCodec::kCodecH264:
video_capture_config.codec = openscreen::cast::VideoCodec::kH264;
break;
case media::VideoCodec::kCodecVP8:
video_capture_config.codec = openscreen::cast::VideoCodec::kVp8;
break;
default:
NOTREACHED();
}
video_capture_config.resolutions.push_back(
{video_config.visible_rect().width(),
video_config.visible_rect().height()});
return video_capture_config;
}
media::AudioDecoderConfig AudioCaptureConfigToAudioDecoderConfig(
const openscreen::cast::AudioCaptureConfig& audio_capture_config) {
// Gather data for the audio decoder config.
media::AudioCodec media_audio_codec = media::AudioCodec::kUnknownAudioCodec;
switch (audio_capture_config.codec) {
case openscreen::cast::AudioCodec::kAac:
media_audio_codec = media::AudioCodec::kCodecAAC;
break;
case openscreen::cast::AudioCodec::kOpus:
media_audio_codec = media::AudioCodec::kCodecOpus;
break;
default:
NOTREACHED();
break;
}
return media::AudioDecoderConfig(
media_audio_codec, media::SampleFormat::kSampleFormatF32,
media::GuessChannelLayout(audio_capture_config.channels),
audio_capture_config.sample_rate /* samples_per_second */,
media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted);
}
media::VideoDecoderConfig VideoCaptureConfigToVideoDecoderConfig(
const openscreen::cast::VideoCaptureConfig& video_capture_config) {
// Gather data for the video decoder config.
uint32_t video_width = video_capture_config.resolutions[0].width;
uint32_t video_height = video_capture_config.resolutions[0].height;
gfx::Size video_size(video_width, video_height);
gfx::Rect video_rect(video_width, video_height);
media::VideoCodec media_video_codec = media::VideoCodec::kUnknownVideoCodec;
media::VideoCodecProfile video_codec_profile =
media::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN;
switch (video_capture_config.codec) {
case openscreen::cast::VideoCodec::kH264:
media_video_codec = media::VideoCodec::kCodecH264;
video_codec_profile = media::VideoCodecProfile::H264PROFILE_BASELINE;
break;
case openscreen::cast::VideoCodec::kVp8:
media_video_codec = media::VideoCodec::kCodecVP8;
video_codec_profile = media::VideoCodecProfile::VP8PROFILE_MIN;
break;
case openscreen::cast::VideoCodec::kHevc:
case openscreen::cast::VideoCodec::kVp9:
default:
NOTREACHED();
break;
}
return media::VideoDecoderConfig(
media_video_codec, video_codec_profile,
media::VideoDecoderConfig::AlphaMode::kIsOpaque, media::VideoColorSpace(),
media::VideoTransformation(), video_size, video_rect, video_size,
media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted);
}
} // namespace cast_streaming

@ -1,30 +0,0 @@
// Copyright 2020 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 COMPONENTS_CAST_STREAMING_BROWSER_CONFIG_CONVERSIONS_H_
#define COMPONENTS_CAST_STREAMING_BROWSER_CONFIG_CONVERSIONS_H_
#include "media/base/audio_decoder_config.h"
#include "media/base/video_decoder_config.h"
#include "third_party/openscreen/src/cast/streaming/capture_configs.h"
namespace cast_streaming {
// Utility functions to convert between media and Open Screen types.
openscreen::cast::AudioCaptureConfig AudioDecoderConfigToAudioCaptureConfig(
const media::AudioDecoderConfig& audio_config);
openscreen::cast::VideoCaptureConfig VideoDecoderConfigToVideoCaptureConfig(
const media::VideoDecoderConfig& video_config);
media::AudioDecoderConfig AudioCaptureConfigToAudioDecoderConfig(
const openscreen::cast::AudioCaptureConfig& audio_capture_config);
media::VideoDecoderConfig VideoCaptureConfigToVideoDecoderConfig(
const openscreen::cast::VideoCaptureConfig& video_capture_config);
} // namespace cast_streaming
#endif // COMPONENTS_CAST_STREAMING_BROWSER_CONFIG_CONVERSIONS_H_

@ -10,6 +10,7 @@
#include "base/callback.h"
#include "components/cast_streaming/public/mojom/cast_streaming_session.mojom.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/openscreen/src/cast/streaming/receiver_session.h"
namespace cast_api_bindings {
class MessagePort;
@ -21,14 +22,25 @@ namespace cast_streaming {
// |message_port| and with a given |cast_streaming_receiver|. On destruction,
// the Cast Streaming Receiver Session will be terminated if it was ever
// started.
// TODO(1220176): Forward declare ReceiverSession::Preferences instead of
// requiring the import above.
class ReceiverSession {
public:
using MessagePortProvider =
base::OnceCallback<std::unique_ptr<cast_api_bindings::MessagePort>()>;
using AVConstraints = openscreen::cast::ReceiverSession::Preferences;
virtual ~ReceiverSession() = default;
// |av_constraints| specifies the supported media codecs, an ordering to
// signify the receiver's preferences of which codecs should be used, and any
// limitations surrounding this support.
// |message_port_provider| creates a new MessagePort to be used for sending
// and receiving Cast messages.
// TODO(crbug.com/1219079): Add conversion functions to create the
// ReceiverSession::Preferences object from //media types.
static std::unique_ptr<ReceiverSession> Create(
std::unique_ptr<AVConstraints> av_constraints,
MessagePortProvider message_port_provider);
// Sets up the CastStreamingReceiver mojo remote. This will immediately call

@ -13,14 +13,19 @@ namespace cast_streaming {
// static
std::unique_ptr<ReceiverSession> ReceiverSession::Create(
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
ReceiverSession::MessagePortProvider message_port_provider) {
return std::make_unique<ReceiverSessionImpl>(
std::move(message_port_provider));
std::move(av_constraints), std::move(message_port_provider));
}
ReceiverSessionImpl::ReceiverSessionImpl(
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
ReceiverSession::MessagePortProvider message_port_provider)
: message_port_provider_(std::move(message_port_provider)) {
: message_port_provider_(std::move(message_port_provider)),
av_constraints_(std::move(av_constraints)) {
// TODO(crbug.com/1218495): Validate the provided codecs against build flags.
DCHECK(av_constraints_);
DCHECK(message_port_provider_);
}
@ -44,7 +49,8 @@ void ReceiverSessionImpl::SetCastStreamingReceiver(
void ReceiverSessionImpl::OnReceiverEnabled() {
DVLOG(1) << __func__;
DCHECK(message_port_provider_);
cast_streaming_session_.Start(this, std::move(message_port_provider_).Run(),
cast_streaming_session_.Start(this, std::move(av_constraints_),
std::move(message_port_provider_).Run(),
base::SequencedTaskRunnerHandle::Get());
}
@ -128,6 +134,7 @@ void ReceiverSessionImpl::OnMojoDisconnect() {
// Close the underlying connection.
if (message_port_provider_) {
av_constraints_ = std::make_unique<ReceiverSession::AVConstraints>();
std::move(message_port_provider_).Run().reset();
}

@ -19,7 +19,11 @@ namespace cast_streaming {
class ReceiverSessionImpl : public cast_streaming::CastStreamingSession::Client,
public ReceiverSession {
public:
explicit ReceiverSessionImpl(MessagePortProvider message_port_provider);
// |av_constraints| specifies the supported media codecs and limitations
// surrounding this support.
ReceiverSessionImpl(
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints,
MessagePortProvider message_port_provider);
~ReceiverSessionImpl() final;
ReceiverSessionImpl(const ReceiverSessionImpl&) = delete;
@ -55,6 +59,7 @@ class ReceiverSessionImpl : public cast_streaming::CastStreamingSession::Client,
// Populated in the ctor, and empty following a call to either
// OnReceiverEnabled() or OnMojoDisconnect().
MessagePortProvider message_port_provider_;
std::unique_ptr<ReceiverSession::AVConstraints> av_constraints_;
mojo::AssociatedRemote<mojom::CastStreamingReceiver> cast_streaming_receiver_;
cast_streaming::CastStreamingSession cast_streaming_session_;

@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/cast_streaming/public/config_conversions.h"
namespace cast_streaming {
@ -16,7 +17,14 @@ CastStreamingTestReceiver::~CastStreamingTestReceiver() = default;
void CastStreamingTestReceiver::Start(
std::unique_ptr<cast_api_bindings::MessagePort> message_port) {
VLOG(1) << __func__;
receiver_session_.Start(this, std::move(message_port),
auto stream_config =
std::make_unique<cast_streaming::ReceiverSession::AVConstraints>(
ToVideoCaptureConfigCodecs(media::VideoCodec::kCodecH264,
media::VideoCodec::kCodecVP8),
ToAudioCaptureConfigCodecs(media::AudioCodec::kCodecAAC,
media::AudioCodec::kCodecOpus));
receiver_session_.Start(this, std::move(stream_config),
std::move(message_port),
base::SequencedTaskRunnerHandle::Get());
}

@ -10,7 +10,7 @@
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/cast_streaming/browser/config_conversions.h"
#include "components/cast_streaming/public/config_conversions.h"
#include "third_party/openscreen/src/cast/streaming/capture_recommendations.h"
namespace cast_streaming {
@ -88,14 +88,12 @@ bool CastStreamingTestSender::Start(
std::vector<openscreen::cast::AudioCaptureConfig> audio_configs;
if (audio_config) {
audio_configs.push_back(
AudioDecoderConfigToAudioCaptureConfig(audio_config.value()));
audio_configs.push_back(ToAudioCaptureConfig(audio_config.value()));
}
std::vector<openscreen::cast::VideoCaptureConfig> video_configs;
if (video_config) {
video_configs.push_back(
VideoDecoderConfigToVideoCaptureConfig(video_config.value()));
video_configs.push_back(ToVideoCaptureConfig(video_config.value()));
}
openscreen::Error error = sender_session_->Negotiate(
@ -191,14 +189,12 @@ void CastStreamingTestSender::OnNegotiated(
if (senders.audio_sender) {
audio_sender_ = senders.audio_sender;
audio_decoder_config_ =
AudioCaptureConfigToAudioDecoderConfig(senders.audio_config);
audio_decoder_config_ = ToAudioDecoderConfig(senders.audio_config);
}
if (senders.video_sender) {
video_sender_ = senders.video_sender;
video_decoder_config_ =
VideoCaptureConfigToVideoDecoderConfig(senders.video_config);
video_decoder_config_ = ToVideoDecoderConfig(senders.video_config);
}
is_active_ = true;

@ -5,18 +5,33 @@
import("//testing/test.gni")
source_set("public") {
deps = [ "//url" ]
deps = [
"//base",
"//media",
"//ui/gfx/geometry",
"//url",
]
public_deps = [
"//third_party/openscreen/src/cast/streaming:receiver",
"//third_party/openscreen/src/cast/streaming:streaming_configs",
]
sources = [
"cast_streaming_url.cc",
"cast_streaming_url.h",
"config_conversions.cc",
"config_conversions.h",
]
}
# NOTE: This source set is intentionally empty. It is used to force the building
# of the code defined in this directory, as it is production code which must
# be validated as part of the CQ.
source_set("unit_tests") {
testonly = true
deps = [ ":public" ]
sources = []
deps = [
":public",
"//base/test:test_support",
"//media:test_support",
"//media/mojo:test_support",
"//testing/gmock",
"//testing/gtest",
]
sources = [ "config_conversions_unittest.cc" ]
}

@ -0,0 +1,6 @@
include_rules = [
"+media/base",
"+third_party/openscreen/src/cast",
"+ui/gfx/geometry",
"+url",
]

@ -0,0 +1,166 @@
// Copyright 2020 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.
#include "components/cast_streaming/public/config_conversions.h"
#include "base/check.h"
#include "base/notreached.h"
#include "media/base/media_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace cast_streaming {
namespace {
media::VideoCodecProfile ToVideoDecoderConfigCodecProfile(
openscreen::cast::VideoCodec codec) {
switch (codec) {
// TODO(b/186875732): Determine the values for Hevc and Vp9 experimentally.
case openscreen::cast::VideoCodec::kH264:
return media::VideoCodecProfile::H264PROFILE_BASELINE;
case openscreen::cast::VideoCodec::kHevc:
return media::VideoCodecProfile::HEVCPROFILE_MAIN;
case openscreen::cast::VideoCodec::kVp8:
return media::VideoCodecProfile::VP8PROFILE_MIN;
case openscreen::cast::VideoCodec::kVp9:
return media::VideoCodecProfile::VP9PROFILE_PROFILE0;
case openscreen::cast::VideoCodec::kNotSpecified:
break;
}
NOTREACHED();
return media::VideoCodecProfile::VIDEO_CODEC_PROFILE_UNKNOWN;
}
media::AudioCodec ToAudioDecoderConfigCodec(
openscreen::cast::AudioCodec codec) {
switch (codec) {
case openscreen::cast::AudioCodec::kAac:
return media::AudioCodec::kCodecAAC;
case openscreen::cast::AudioCodec::kOpus:
return media::AudioCodec::kCodecOpus;
case openscreen::cast::AudioCodec::kNotSpecified:
break;
}
NOTREACHED();
return media::AudioCodec::kUnknownAudioCodec;
}
media::VideoCodec ToVideoDecoderConfigCodec(
openscreen::cast::VideoCodec codec) {
switch (codec) {
case openscreen::cast::VideoCodec::kH264:
return media::VideoCodec::kCodecH264;
case openscreen::cast::VideoCodec::kVp8:
return media::VideoCodec::kCodecVP8;
case openscreen::cast::VideoCodec::kHevc:
return media::VideoCodec::kCodecHEVC;
case openscreen::cast::VideoCodec::kVp9:
return media::VideoCodec::kCodecVP9;
case openscreen::cast::VideoCodec::kNotSpecified:
break;
}
NOTREACHED();
return media::VideoCodec::kUnknownVideoCodec;
}
} // namespace
openscreen::cast::AudioCodec ToAudioCaptureConfigCodec(
media::AudioCodec codec) {
switch (codec) {
case media::AudioCodec::kCodecAAC:
return openscreen::cast::AudioCodec::kAac;
case media::AudioCodec::kCodecOpus:
return openscreen::cast::AudioCodec::kOpus;
default:
break;
}
NOTREACHED();
return openscreen::cast::AudioCodec::kNotSpecified;
}
openscreen::cast::VideoCodec ToVideoCaptureConfigCodec(
media::VideoCodec codec) {
switch (codec) {
case media::VideoCodec::kCodecH264:
return openscreen::cast::VideoCodec::kH264;
case media::VideoCodec::kCodecVP8:
return openscreen::cast::VideoCodec::kVp8;
case media::VideoCodec::kCodecHEVC:
return openscreen::cast::VideoCodec::kHevc;
case media::VideoCodec::kCodecVP9:
return openscreen::cast::VideoCodec::kVp9;
default:
break;
}
NOTREACHED();
return openscreen::cast::VideoCodec::kNotSpecified;
}
openscreen::cast::AudioCaptureConfig ToAudioCaptureConfig(
const media::AudioDecoderConfig& audio_config) {
DCHECK(!audio_config.is_encrypted());
openscreen::cast::AudioCaptureConfig audio_capture_config;
audio_capture_config.codec = ToAudioCaptureConfigCodec(audio_config.codec());
audio_capture_config.channels =
media::ChannelLayoutToChannelCount(audio_config.channel_layout());
audio_capture_config.sample_rate = audio_config.samples_per_second();
audio_capture_config.bit_rate = 0; // Selected by the sender.
return audio_capture_config;
}
openscreen::cast::VideoCaptureConfig ToVideoCaptureConfig(
const media::VideoDecoderConfig& video_config) {
DCHECK(!video_config.is_encrypted());
openscreen::cast::VideoCaptureConfig video_capture_config;
video_capture_config.codec = ToVideoCaptureConfigCodec(video_config.codec());
video_capture_config.resolutions.push_back(
{video_config.visible_rect().width(),
video_config.visible_rect().height()});
video_capture_config.max_bit_rate = 0; // Selected by the sender.
return video_capture_config;
}
media::AudioDecoderConfig ToAudioDecoderConfig(
const openscreen::cast::AudioCaptureConfig& audio_capture_config) {
media::AudioCodec media_audio_codec =
ToAudioDecoderConfigCodec(audio_capture_config.codec);
return media::AudioDecoderConfig(
media_audio_codec, media::SampleFormat::kSampleFormatF32,
media::GuessChannelLayout(audio_capture_config.channels),
audio_capture_config.sample_rate /* samples_per_second */,
media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted);
}
media::VideoDecoderConfig ToVideoDecoderConfig(
const openscreen::cast::VideoCaptureConfig& video_capture_config) {
// Gather data for the video decoder config.
DCHECK(video_capture_config.resolutions.size());
uint32_t video_width = video_capture_config.resolutions[0].width;
uint32_t video_height = video_capture_config.resolutions[0].height;
gfx::Size video_size(video_width, video_height);
gfx::Rect video_rect(video_width, video_height);
media::VideoCodec media_video_codec =
ToVideoDecoderConfigCodec(video_capture_config.codec);
media::VideoCodecProfile video_codec_profile =
ToVideoDecoderConfigCodecProfile(video_capture_config.codec);
return media::VideoDecoderConfig(
media_video_codec, video_codec_profile,
media::VideoDecoderConfig::AlphaMode::kIsOpaque, media::VideoColorSpace(),
media::VideoTransformation(), video_size, video_rect, video_size,
media::EmptyExtraData(), media::EncryptionScheme::kUnencrypted);
}
} // namespace cast_streaming

@ -0,0 +1,50 @@
// Copyright 2020 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 COMPONENTS_CAST_STREAMING_PUBLIC_CONFIG_CONVERSIONS_H_
#define COMPONENTS_CAST_STREAMING_PUBLIC_CONFIG_CONVERSIONS_H_
#include <vector>
#include "media/base/audio_decoder_config.h"
#include "media/base/video_decoder_config.h"
#include "third_party/openscreen/src/cast/streaming/capture_configs.h"
namespace cast_streaming {
// Utility functions to convert between media and Open Screen types.
openscreen::cast::AudioCaptureConfig ToAudioCaptureConfig(
const media::AudioDecoderConfig& audio_config);
openscreen::cast::VideoCaptureConfig ToVideoCaptureConfig(
const media::VideoDecoderConfig& video_config);
media::AudioDecoderConfig ToAudioDecoderConfig(
const openscreen::cast::AudioCaptureConfig& audio_capture_config);
media::VideoDecoderConfig ToVideoDecoderConfig(
const openscreen::cast::VideoCaptureConfig& video_capture_config);
openscreen::cast::AudioCodec ToAudioCaptureConfigCodec(media::AudioCodec codec);
openscreen::cast::VideoCodec ToVideoCaptureConfigCodec(media::VideoCodec codec);
template <typename... TCodecs>
std::vector<openscreen::cast::AudioCodec> ToAudioCaptureConfigCodecs(
TCodecs... codecs) {
return std::vector<openscreen::cast::AudioCodec>{
ToAudioCaptureConfigCodec(codecs)...};
}
template <typename... TCodecs>
std::vector<openscreen::cast::VideoCodec> ToVideoCaptureConfigCodecs(
TCodecs... codecs) {
return std::vector<openscreen::cast::VideoCodec>{
ToVideoCaptureConfigCodec(codecs)...};
}
} // namespace cast_streaming
#endif // COMPONENTS_CAST_STREAMING_PUBLIC_CONFIG_CONVERSIONS_H_

@ -0,0 +1,241 @@
// Copyright 2021 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.
#include "components/cast_streaming/public/config_conversions.h"
#include "media/base/media_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/openscreen/src/cast/streaming/capture_configs.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace cast_streaming {
namespace {
void ValidateAudioConfig(const media::AudioDecoderConfig& config,
const media::AudioDecoderConfig& expected) {
EXPECT_EQ(config.codec(), expected.codec());
EXPECT_EQ(config.sample_format(), media::SampleFormat::kSampleFormatF32);
EXPECT_EQ(config.channel_layout(), expected.channel_layout());
EXPECT_EQ(config.samples_per_second(), expected.samples_per_second());
EXPECT_EQ(config.extra_data().size(), size_t{0});
EXPECT_FALSE(config.is_encrypted());
}
void ValidateAudioConfig(const openscreen::cast::AudioCaptureConfig& config,
const openscreen::cast::AudioCaptureConfig& expected) {
EXPECT_EQ(config.codec, expected.codec);
EXPECT_EQ(config.channels, expected.channels);
EXPECT_EQ(config.bit_rate, expected.bit_rate);
EXPECT_EQ(config.sample_rate, expected.sample_rate);
EXPECT_EQ(config.target_playout_delay, expected.target_playout_delay);
}
void ValidateVideoConfig(const media::VideoDecoderConfig& config,
const media::VideoDecoderConfig& expected) {
EXPECT_EQ(config.codec(), expected.codec());
EXPECT_EQ(config.profile(), expected.profile());
EXPECT_EQ(config.alpha_mode(),
media::VideoDecoderConfig::AlphaMode::kIsOpaque);
EXPECT_EQ(config.extra_data().size(), size_t{0});
EXPECT_FALSE(config.is_encrypted());
EXPECT_EQ(config.coded_size().width(), expected.coded_size().width());
EXPECT_EQ(config.coded_size().height(), expected.coded_size().height());
EXPECT_EQ(config.visible_rect().width(), expected.visible_rect().width());
EXPECT_EQ(config.visible_rect().height(), expected.visible_rect().height());
EXPECT_EQ(config.natural_size().width(), expected.natural_size().width());
EXPECT_EQ(config.natural_size().height(), expected.natural_size().height());
}
void ValidateVideoConfig(const openscreen::cast::VideoCaptureConfig& config,
const openscreen::cast::VideoCaptureConfig& expected) {
EXPECT_EQ(config.codec, expected.codec);
EXPECT_EQ(config.max_frame_rate, expected.max_frame_rate);
EXPECT_EQ(config.max_bit_rate, expected.max_bit_rate);
EXPECT_EQ(config.target_playout_delay, expected.target_playout_delay);
ASSERT_EQ(config.resolutions.size(), expected.resolutions.size());
for (const auto& resolution : config.resolutions) {
EXPECT_TRUE(std::find(expected.resolutions.begin(),
expected.resolutions.end(),
resolution) != expected.resolutions.end());
}
}
openscreen::cast::AudioCaptureConfig CreateAudioCaptureConfig() {
openscreen::cast::AudioCaptureConfig config;
config.codec = openscreen::cast::AudioCodec::kAac;
config.channels = 2;
config.sample_rate = 42;
return config;
}
media::AudioDecoderConfig CreateAudioDecoderConfig(
media::AudioCodec codec,
media::ChannelLayout channel_layout,
int samples_per_second) {
return media::AudioDecoderConfig(codec, media::SampleFormat::kSampleFormatF32,
channel_layout, samples_per_second,
media::EmptyExtraData(),
media::EncryptionScheme::kUnencrypted);
}
openscreen::cast::VideoCaptureConfig CreateVideoCaptureConfig() {
openscreen::cast::VideoCaptureConfig config;
config.codec = openscreen::cast::VideoCodec::kH264;
config.resolutions.push_back({1080, 720});
return config;
}
media::VideoDecoderConfig CreateVideoDecoderConfig(
media::VideoCodec codec,
media::VideoCodecProfile codec_profile,
int width,
int height) {
gfx::Size video_size(width, height);
gfx::Rect video_rect(width, height);
return media::VideoDecoderConfig(
codec, codec_profile, media::VideoDecoderConfig::AlphaMode::kIsOpaque,
media::VideoColorSpace(), media::VideoTransformation(), video_size,
video_rect, video_size, media::EmptyExtraData(),
media::EncryptionScheme::kUnencrypted);
}
} // namespace
TEST(ConfigConversionsTest, AudioConfigCodecConversion) {
auto capture_config = CreateAudioCaptureConfig();
auto decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecAAC,
media::ChannelLayout::CHANNEL_LAYOUT_STEREO, 42);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
capture_config.codec = openscreen::cast::AudioCodec::kOpus;
decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecOpus,
media::ChannelLayout::CHANNEL_LAYOUT_STEREO, 42);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
}
TEST(ConfigConversionsTest, AudioConfigChannelsConversion) {
auto capture_config = CreateAudioCaptureConfig();
auto decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecAAC,
media::ChannelLayout::CHANNEL_LAYOUT_STEREO, 42);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
capture_config.channels = 1;
decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecAAC,
media::ChannelLayout::CHANNEL_LAYOUT_MONO, 42);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
// Other configurations are not expected in practice.
}
TEST(ConfigConversionsTest, AudioConfigSampleRateConversion) {
auto capture_config = CreateAudioCaptureConfig();
auto decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecAAC,
media::ChannelLayout::CHANNEL_LAYOUT_STEREO, 42);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
capture_config.sample_rate = 1234;
decoder_config = CreateAudioDecoderConfig(
media::AudioCodec::kCodecAAC, media::ChannelLayout::CHANNEL_LAYOUT_STEREO,
1234);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
capture_config.sample_rate = -1;
decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecAAC,
media::ChannelLayout::CHANNEL_LAYOUT_STEREO, -1);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
capture_config.sample_rate = 0;
decoder_config =
CreateAudioDecoderConfig(media::AudioCodec::kCodecAAC,
media::ChannelLayout::CHANNEL_LAYOUT_STEREO, 0);
ValidateAudioConfig(ToAudioDecoderConfig(capture_config), decoder_config);
ValidateAudioConfig(ToAudioCaptureConfig(decoder_config), capture_config);
}
TEST(ConfigConversionsTest, VideoConfigCodecConversion) {
const int width = 1080;
const int height = 720;
auto capture_config = CreateVideoCaptureConfig();
auto decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecH264,
media::VideoCodecProfile::H264PROFILE_BASELINE, width, height);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
capture_config.codec = openscreen::cast::VideoCodec::kVp8;
decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecVP8, media::VideoCodecProfile::VP8PROFILE_MIN,
width, height);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
capture_config.codec = openscreen::cast::VideoCodec::kHevc;
decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecHEVC, media::VideoCodecProfile::HEVCPROFILE_MAIN,
width, height);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
capture_config.codec = openscreen::cast::VideoCodec::kVp9;
decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecVP9,
media::VideoCodecProfile::VP9PROFILE_PROFILE0, width, height);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
}
TEST(ConfigConversionsTest, VideoConfigResolutionConversion) {
auto capture_config = CreateVideoCaptureConfig();
auto decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecH264,
media::VideoCodecProfile::H264PROFILE_BASELINE, 1080, 720);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
ASSERT_EQ(capture_config.resolutions.size(), size_t{1});
capture_config.resolutions[0].width = 42;
capture_config.resolutions[0].height = 16;
decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecH264,
media::VideoCodecProfile::H264PROFILE_BASELINE, 42, 16);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
capture_config.resolutions[0].width = 1;
capture_config.resolutions[0].height = 2;
decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecH264,
media::VideoCodecProfile::H264PROFILE_BASELINE, 1, 2);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
capture_config.resolutions[0].width = 0;
capture_config.resolutions[0].height = 0;
decoder_config = CreateVideoDecoderConfig(
media::VideoCodec::kCodecH264,
media::VideoCodecProfile::H264PROFILE_BASELINE, 0, 0);
ValidateVideoConfig(ToVideoDecoderConfig(capture_config), decoder_config);
ValidateVideoConfig(ToVideoCaptureConfig(decoder_config), capture_config);
}
} // namespace cast_streaming

@ -104,6 +104,7 @@ component("web_engine_core") {
"//base/util/memory_pressure",
"//components/cast/message_port:message_port_fuchsia",
"//components/cast_streaming/browser",
"//components/cast_streaming/public",
"//components/cast_streaming/public/mojom",
"//components/cast_streaming/renderer",
"//components/cdm/renderer",

@ -6,6 +6,9 @@
#include "base/bind.h"
#include "components/cast/message_port/message_port_fuchsia.h"
#include "components/cast_streaming/public/config_conversions.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/video_decoder_config.h"
ReceiverSessionClient::ReceiverSessionClient(
fidl::InterfaceRequest<fuchsia::web::MessagePort> message_port_request)
@ -20,12 +23,26 @@ void ReceiverSessionClient::SetCastStreamingReceiver(
cast_streaming_receiver) {
DCHECK(message_port_request_);
receiver_session_ = cast_streaming::ReceiverSession::Create(base::BindOnce(
[](fidl::InterfaceRequest<fuchsia::web::MessagePort> port)
-> std::unique_ptr<cast_api_bindings::MessagePort> {
return cast_api_bindings::MessagePortFuchsia::Create(std::move(port));
},
std::move(message_port_request_)));
// TODO: Add streaming session Constraints based on system capabilities
// (see crbug.com/1013412) and DisplayDescription (see crbug.com/1087520).
// TODO(crbug.com/1218498): Only populate codecs corresponding to those called
// out by build flags.
auto stream_config =
std::make_unique<cast_streaming::ReceiverSession::AVConstraints>(
cast_streaming::ToVideoCaptureConfigCodecs(
media::VideoCodec::kCodecH264, media::VideoCodec::kCodecVP8),
cast_streaming::ToAudioCaptureConfigCodecs(
media::AudioCodec::kCodecAAC, media::AudioCodec::kCodecOpus));
receiver_session_ = cast_streaming::ReceiverSession::Create(
std::move(stream_config),
base::BindOnce(
[](fidl::InterfaceRequest<fuchsia::web::MessagePort> port)
-> std::unique_ptr<cast_api_bindings::MessagePort> {
return cast_api_bindings::MessagePortFuchsia::Create(
std::move(port));
},
std::move(message_port_request_)));
receiver_session_->SetCastStreamingReceiver(
std::move(cast_streaming_receiver));
}