aboutsummaryrefslogtreecommitdiff
path: root/cast/standalone_receiver
diff options
context:
space:
mode:
authorYuri Wiitala <miu@chromium.org>2020-11-09 12:10:55 -0800
committerCommit Bot <commit-bot@chromium.org>2020-11-09 20:32:07 +0000
commit7b6396aaef5bf0693c6b9aa2e4bdbe139a28e57a (patch)
treebdca6e00b750a3f8c0b9acedaee39729fb29dae6 /cast/standalone_receiver
parent82a5b2d213634a64b952e9135901e0f0e4b81571 (diff)
downloadopenscreen-7b6396aaef5bf0693c6b9aa2e4bdbe139a28e57a.tar.gz
Standalone Receiver: Integration with ApplicationAgent.
Makes the standalone cast receiver a full Cast V2 Receiver with a launchable "Mirroring App." Replaces "CastAgent" with three modules: MirroringApplication (new): A front-end launcher and message port for a ReceiverSession (and a StreamingPlaybackController, which manages the playback UI). ApplicatonAgent (prior patch): An implementation of the Cast V2 Application Control spec, able to launch applications and route messages to/from them. CastService (new): Glues it all together, from a network server socket, through the Cast Channel infrastructure, through the ApplicationAgent and MirroringApplication, to a ReceiverSession and playback GUI. Bug: b/170134354 Change-Id: I9640a3d0c40f174d9f03bc26ee3c2f160736e290 Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2481853 Commit-Queue: Yuri Wiitala <miu@chromium.org> Reviewed-by: Jordan Bayles <jophba@chromium.org>
Diffstat (limited to 'cast/standalone_receiver')
-rw-r--r--cast/standalone_receiver/BUILD.gn22
-rw-r--r--cast/standalone_receiver/cast_agent.cc170
-rw-r--r--cast/standalone_receiver/cast_agent.h111
-rw-r--r--cast/standalone_receiver/cast_agent_integration_tests.cc142
-rw-r--r--cast/standalone_receiver/cast_service.cc109
-rw-r--r--cast/standalone_receiver/cast_service.h77
-rw-r--r--cast/standalone_receiver/main.cc178
-rw-r--r--cast/standalone_receiver/mirroring_application.cc90
-rw-r--r--cast/standalone_receiver/mirroring_application.h69
9 files changed, 402 insertions, 566 deletions
diff --git a/cast/standalone_receiver/BUILD.gn b/cast/standalone_receiver/BUILD.gn
index c36c604b..74d53f65 100644
--- a/cast/standalone_receiver/BUILD.gn
+++ b/cast/standalone_receiver/BUILD.gn
@@ -10,8 +10,10 @@ import("//build_overrides/build.gni")
# application.
if (!build_with_chromium) {
shared_sources = [
- "cast_agent.cc",
- "cast_agent.h",
+ "cast_service.cc",
+ "cast_service.h",
+ "mirroring_application.cc",
+ "mirroring_application.h",
"streaming_playback_controller.cc",
"streaming_playback_controller.h",
]
@@ -58,23 +60,13 @@ if (!build_with_chromium) {
]
}
- source_set("e2e_tests") {
- testonly = true
-
- sources = [ "cast_agent_integration_tests.cc" ]
+ executable("cast_receiver") {
+ sources = [ "main.cc" ]
deps = [
- ":standalone_receiver_dummy",
- "../../third_party/boringssl",
- "../../third_party/googletest:gtest",
+ "../receiver:agent",
"../receiver:channel",
]
- }
-
- executable("cast_receiver") {
- sources = [ "main.cc" ]
-
- deps = [ "../receiver:channel" ]
configs += [ "../common:certificate_config" ]
diff --git a/cast/standalone_receiver/cast_agent.cc b/cast/standalone_receiver/cast_agent.cc
deleted file mode 100644
index 791029a3..00000000
--- a/cast/standalone_receiver/cast_agent.cc
+++ /dev/null
@@ -1,170 +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 "cast/standalone_receiver/cast_agent.h"
-
-#include <fstream>
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/strings/str_cat.h"
-#include "cast/common/channel/cast_socket_message_port.h"
-#include "cast/common/channel/message_util.h"
-#include "cast/streaming/constants.h"
-#include "cast/streaming/offer_messages.h"
-#include "platform/base/tls_credentials.h"
-#include "platform/base/tls_listen_options.h"
-#include "util/json/json_serialization.h"
-#include "util/osp_logging.h"
-#include "util/trace_logging.h"
-
-namespace openscreen {
-namespace cast {
-namespace {
-
-constexpr int kDefaultMaxBacklogSize = 64;
-const TlsListenOptions kDefaultListenOptions{kDefaultMaxBacklogSize};
-
-} // namespace
-
-CastAgent::CastAgent(
- TaskRunner* task_runner,
- const InterfaceInfo& interface,
- DeviceAuthNamespaceHandler::CredentialsProvider* credentials_provider,
- TlsCredentials tls_credentials)
- : task_runner_(task_runner),
- credentials_provider_(credentials_provider),
- tls_credentials_(std::move(tls_credentials)) {
- const IPAddress address = interface.GetIpAddressV4()
- ? interface.GetIpAddressV4()
- : interface.GetIpAddressV6();
- OSP_CHECK(address);
- environment_ = std::make_unique<Environment>(
- &Clock::now, task_runner_,
- IPEndpoint{address, kDefaultCastStreamingPort});
- receive_endpoint_ = IPEndpoint{address, kDefaultCastPort};
-}
-
-CastAgent::~CastAgent() = default;
-
-Error CastAgent::Start() {
- TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
- OSP_CHECK(!current_session_);
-
- task_runner_->PostTask([this] {
- wake_lock_ = ScopedWakeLock::Create(task_runner_);
-
- auth_handler_ = MakeSerialDelete<DeviceAuthNamespaceHandler>(
- task_runner_, credentials_provider_);
- router_ = MakeSerialDelete<VirtualConnectionRouter>(task_runner_,
- &connection_manager_);
- message_port_ =
- MakeSerialDelete<CastSocketMessagePort>(task_runner_, router_.get());
- router_->AddHandlerForLocalId(kPlatformReceiverId, auth_handler_.get());
- socket_factory_ = MakeSerialDelete<ReceiverSocketFactory>(
- task_runner_, this, router_.get());
- connection_factory_ = SerialDeletePtr<TlsConnectionFactory>(
- task_runner_,
- TlsConnectionFactory::CreateFactory(socket_factory_.get(), task_runner_)
- .release());
- connection_factory_->SetListenCredentials(tls_credentials_);
- connection_factory_->Listen(receive_endpoint_, kDefaultListenOptions);
- OSP_LOG_INFO << "Listening for connections at: " << receive_endpoint_;
- });
-
- return Error::None();
-}
-
-Error CastAgent::Stop() {
- task_runner_->PostTask([this] {
- router_.reset();
- connection_factory_.reset();
- controller_.reset();
- current_session_.reset();
- socket_factory_.reset();
- wake_lock_.reset();
- });
- return Error::None();
-}
-
-void CastAgent::OnConnected(ReceiverSocketFactory* factory,
- const IPEndpoint& endpoint,
- std::unique_ptr<CastSocket> socket) {
- TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
- if (current_session_) {
- OSP_LOG_WARN << "Already connected, dropping peer at: " << endpoint;
- return;
- }
-
- OSP_LOG_INFO << "Received connection from peer at: " << endpoint;
- message_port_->SetSocket(socket->GetWeakPtr());
- router_->TakeSocket(this, std::move(socket));
- controller_ =
- std::make_unique<StreamingPlaybackController>(task_runner_, this);
- current_session_ = std::make_unique<ReceiverSession>(
- controller_.get(), environment_.get(), message_port_.get(),
- ReceiverSession::Preferences{});
-}
-
-void CastAgent::OnError(ReceiverSocketFactory* factory, Error error) {
- OSP_LOG_ERROR << "Cast agent received socket factory error: " << error;
- StopCurrentSession();
-}
-
-void CastAgent::OnClose(CastSocket* cast_socket) {
- OSP_VLOG << "Cast agent socket closed.";
- StopCurrentSession();
-}
-
-void CastAgent::OnError(CastSocket* socket, Error error) {
- OSP_LOG_ERROR << "Cast agent received socket error: " << error;
- StopCurrentSession();
-}
-
-// Currently we don't do anything with the receiver output--the session
-// is automatically linked to the playback controller when it is constructed, so
-// we don't actually have to interface with the receivers. If we end up caring
-// about the receiver configurations we will have to handle OnNegotiated here.
-void CastAgent::OnNegotiated(const ReceiverSession* session,
- ReceiverSession::ConfiguredReceivers receivers) {
- OSP_VLOG << "Successfully negotiated with sender.";
-}
-
-void CastAgent::OnReceiversDestroying(const ReceiverSession* session,
- ReceiversDestroyingReason reason) {
- const auto GetReasoning = [&] {
- switch (reason) {
- case kEndOfSession:
- return " at end of session.";
- case kRenegotiated:
- return ", to be replaced with new ones.";
- }
- return "";
- };
- OSP_VLOG << "Receiver instances destroying" << GetReasoning();
-}
-
-// Currently, we just kill the session if an error is encountered.
-void CastAgent::OnError(const ReceiverSession* session, Error error) {
- OSP_LOG_ERROR << "Cast agent received receiver session error: " << error;
- StopCurrentSession();
-}
-
-void CastAgent::OnPlaybackError(StreamingPlaybackController* controller,
- Error error) {
- OSP_LOG_ERROR << "Cast agent received playback error: " << error;
- StopCurrentSession();
-}
-
-void CastAgent::StopCurrentSession() {
- current_session_.reset();
- controller_.reset();
- router_->CloseSocket(message_port_->GetSocketId());
- message_port_->SetSocket(nullptr);
-}
-
-} // namespace cast
-} // namespace openscreen
diff --git a/cast/standalone_receiver/cast_agent.h b/cast/standalone_receiver/cast_agent.h
deleted file mode 100644
index db8cf668..00000000
--- a/cast/standalone_receiver/cast_agent.h
+++ /dev/null
@@ -1,111 +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 CAST_STANDALONE_RECEIVER_CAST_AGENT_H_
-#define CAST_STANDALONE_RECEIVER_CAST_AGENT_H_
-
-#include <openssl/x509.h>
-
-#include <memory>
-#include <vector>
-
-#include "cast/common/channel/cast_socket_message_port.h"
-#include "cast/common/channel/virtual_connection_manager.h"
-#include "cast/common/channel/virtual_connection_router.h"
-#include "cast/common/public/cast_socket.h"
-#include "cast/receiver/channel/device_auth_namespace_handler.h"
-#include "cast/receiver/channel/static_credentials.h"
-#include "cast/receiver/public/receiver_socket_factory.h"
-#include "cast/standalone_receiver/streaming_playback_controller.h"
-#include "cast/streaming/environment.h"
-#include "cast/streaming/receiver_session.h"
-#include "platform/api/scoped_wake_lock.h"
-#include "platform/api/serial_delete_ptr.h"
-#include "platform/base/error.h"
-#include "platform/base/interface_info.h"
-#include "platform/base/tls_credentials.h"
-#include "platform/impl/task_runner.h"
-
-namespace openscreen {
-namespace cast {
-
-// This class manages sender connections, starting with listening over TLS for
-// connection attempts, constructing ReceiverSessions when OFFER messages are
-// received, and linking Receivers to the output decoder and SDL visualizer.
-//
-// Consumers of this class are expected to provide a single threaded task runner
-// implementation, a network interface information struct that will be used
-// both for TLS listening and UDP messaging, and a credentials provider used
-// for TLS listening.
-class CastAgent final : public ReceiverSocketFactory::Client,
- public VirtualConnectionRouter::SocketErrorHandler,
- public ReceiverSession::Client,
- public StreamingPlaybackController::Client {
- public:
- CastAgent(
- TaskRunner* task_runner,
- const InterfaceInfo& interface,
- DeviceAuthNamespaceHandler::CredentialsProvider* credentials_provider,
- TlsCredentials tls_credentials);
- ~CastAgent();
-
- // Initialization occurs as part of construction, however to actually bind
- // for discovery and listening over TLS, the CastAgent must be started.
- Error Start();
- Error Stop();
-
- // ReceiverSocketFactory::Client overrides.
- void OnConnected(ReceiverSocketFactory* factory,
- const IPEndpoint& endpoint,
- std::unique_ptr<CastSocket> socket) override;
- void OnError(ReceiverSocketFactory* factory, Error error) override;
-
- // VirtualConnectionRouter::SocketErrorHandler overrides.
- void OnClose(CastSocket* cast_socket) override;
- void OnError(CastSocket* socket, Error error) override;
-
- // ReceiverSession::Client overrides.
- void OnNegotiated(const ReceiverSession* session,
- ReceiverSession::ConfiguredReceivers receivers) override;
- void OnReceiversDestroying(const ReceiverSession* session,
- ReceiversDestroyingReason reason) override;
- void OnError(const ReceiverSession* session, Error error) override;
-
- // StreamingPlaybackController::Client overrides
- void OnPlaybackError(StreamingPlaybackController* controller,
- Error error) override;
-
- private:
- // Helper for stopping the current session. This is useful for when we don't
- // want to completely stop (e.g. an issue with a specific Sender) but need
- // to terminate the current connection.
- void StopCurrentSession();
-
- // Member variables set as part of construction.
- std::unique_ptr<Environment> environment_;
- TaskRunner* const task_runner_;
- IPEndpoint receive_endpoint_;
- DeviceAuthNamespaceHandler::CredentialsProvider* credentials_provider_;
- TlsCredentials tls_credentials_;
-
- // Member variables set as part of starting up.
- SerialDeletePtr<DeviceAuthNamespaceHandler> auth_handler_;
- SerialDeletePtr<TlsConnectionFactory> connection_factory_;
- VirtualConnectionManager connection_manager_;
- SerialDeletePtr<VirtualConnectionRouter> router_;
- SerialDeletePtr<CastSocketMessagePort> message_port_;
- SerialDeletePtr<ReceiverSocketFactory> socket_factory_;
- SerialDeletePtr<ScopedWakeLock> wake_lock_;
-
- // Member variables set as part of a sender connection.
- // NOTE: currently we only support a single sender connection and a
- // single streaming session.
- std::unique_ptr<ReceiverSession> current_session_;
- std::unique_ptr<StreamingPlaybackController> controller_;
-};
-
-} // namespace cast
-} // namespace openscreen
-
-#endif // CAST_STANDALONE_RECEIVER_CAST_AGENT_H_
diff --git a/cast/standalone_receiver/cast_agent_integration_tests.cc b/cast/standalone_receiver/cast_agent_integration_tests.cc
deleted file mode 100644
index 2cd6b77b..00000000
--- a/cast/standalone_receiver/cast_agent_integration_tests.cc
+++ /dev/null
@@ -1,142 +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 "cast/common/certificate/cast_trust_store.h"
-#include "cast/common/certificate/testing/test_helpers.h"
-#include "cast/common/channel/virtual_connection_manager.h"
-#include "cast/common/channel/virtual_connection_router.h"
-#include "cast/receiver/channel/static_credentials.h"
-#include "cast/sender/public/sender_socket_factory.h"
-#include "cast/standalone_receiver/cast_agent.h"
-#include "gtest/gtest.h"
-#include "platform/api/serial_delete_ptr.h"
-#include "platform/api/time.h"
-#include "platform/impl/network_interface.h"
-#include "platform/impl/platform_client_posix.h"
-#include "platform/impl/task_runner.h"
-
-namespace openscreen {
-namespace cast {
-namespace {
-
-// Based heavily on SenderSocketsClient from cast_socket_e2e_test.cc.
-class MockSender final : public SenderSocketFactory::Client,
- public VirtualConnectionRouter::SocketErrorHandler {
- public:
- explicit MockSender(VirtualConnectionRouter* router) : router_(router) {}
- ~MockSender() = default;
-
- CastSocket* socket() const { return socket_; }
-
- // SenderSocketFactory::Client overrides.
- void OnConnected(SenderSocketFactory* factory,
- const IPEndpoint& endpoint,
- std::unique_ptr<CastSocket> socket) override {
- ASSERT_FALSE(socket_);
- OSP_LOG_INFO << "Sender connected to endpoint: " << endpoint;
- socket_ = socket.get();
- router_->TakeSocket(this, std::move(socket));
- }
-
- void OnError(SenderSocketFactory* factory,
- const IPEndpoint& endpoint,
- Error error) override {
- FAIL() << error;
- }
-
- // VirtualConnectionRouter::SocketErrorHandler overrides.
- void OnClose(CastSocket* socket) override {}
- void OnError(CastSocket* socket, Error error) override { FAIL() << error; }
-
- private:
- VirtualConnectionRouter* const router_;
- std::atomic<CastSocket*> socket_{nullptr};
-};
-
-class CastAgentIntegrationTest : public ::testing::Test {
- public:
- void SetUp() override {
- PlatformClientPosix::Create(std::chrono::milliseconds(50),
- std::chrono::milliseconds(50));
- task_runner_ = reinterpret_cast<TaskRunnerImpl*>(
- PlatformClientPosix::GetInstance()->GetTaskRunner());
-
- sender_router_ = MakeSerialDelete<VirtualConnectionRouter>(
- task_runner_, &sender_vc_manager_);
- sender_client_ = std::make_unique<MockSender>(sender_router_.get());
- sender_factory_ = MakeSerialDelete<SenderSocketFactory>(
- task_runner_, sender_client_.get(), task_runner_);
- sender_tls_factory_ = SerialDeletePtr<TlsConnectionFactory>(
- task_runner_,
- TlsConnectionFactory::CreateFactory(sender_factory_.get(), task_runner_)
- .release());
- sender_factory_->set_factory(sender_tls_factory_.get());
- }
-
- void TearDown() override {
- sender_router_.reset();
- sender_tls_factory_.reset();
- sender_factory_.reset();
- PlatformClientPosix::ShutDown();
- // Must be shut down after platform client, so joined tasks
- // depending on certs are called correctly.
- CastTrustStore::ResetInstance();
- }
-
- void WaitAndAssertSenderSocketConnected() {
- constexpr int kMaxAttempts = 10;
- constexpr std::chrono::milliseconds kSocketWaitDelay(250);
- for (int i = 0; i < kMaxAttempts; ++i) {
- OSP_LOG_INFO << "\tChecking for CastSocket, attempt " << i + 1 << "/"
- << kMaxAttempts;
- if (sender_client_->socket()) {
- break;
- }
- std::this_thread::sleep_for(kSocketWaitDelay);
- }
- ASSERT_TRUE(sender_client_->socket());
- }
-
- void AssertConnect(const IPAddress& address) {
- OSP_LOG_INFO << "Sending connect task";
- task_runner_->PostTask(
- [this, &address, port = (static_cast<uint16_t>(kDefaultCastPort))]() {
- OSP_LOG_INFO << "Calling SenderSocketFactory::Connect";
- sender_factory_->Connect(
- IPEndpoint{address, port},
- SenderSocketFactory::DeviceMediaPolicy::kNone,
- sender_router_.get());
- });
- WaitAndAssertSenderSocketConnected();
- }
-
- TaskRunnerImpl* task_runner_;
- // Cast socket sender components, used in conjuction to mock a Libcast sender.
- VirtualConnectionManager sender_vc_manager_;
- SerialDeletePtr<VirtualConnectionRouter> sender_router_;
- std::unique_ptr<MockSender> sender_client_;
- SerialDeletePtr<SenderSocketFactory> sender_factory_;
- SerialDeletePtr<TlsConnectionFactory> sender_tls_factory_;
-};
-
-TEST_F(CastAgentIntegrationTest, CanConnect) {
- absl::optional<InterfaceInfo> loopback = GetLoopbackInterfaceForTesting();
- ASSERT_TRUE(loopback.has_value());
-
- ErrorOr<GeneratedCredentials> creds =
- GenerateCredentialsForTesting("Test Device Certificate");
- ASSERT_TRUE(creds.is_value());
- CastTrustStore::CreateInstanceForTest(creds.value().root_cert_der);
-
- auto agent = MakeSerialDelete<CastAgent>(
- task_runner_, task_runner_, loopback.value(),
- creds.value().provider.get(), creds.value().tls_credentials);
- EXPECT_TRUE(agent->Start().ok());
- AssertConnect(loopback.value().GetIpAddressV4());
- EXPECT_TRUE(agent->Stop().ok());
-}
-
-} // namespace
-} // namespace cast
-} // namespace openscreen
diff --git a/cast/standalone_receiver/cast_service.cc b/cast/standalone_receiver/cast_service.cc
new file mode 100644
index 00000000..e4a5b531
--- /dev/null
+++ b/cast/standalone_receiver/cast_service.cc
@@ -0,0 +1,109 @@
+// 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 "cast/standalone_receiver/cast_service.h"
+
+#include <utility>
+
+#include "discovery/common/config.h"
+#include "platform/api/tls_connection_factory.h"
+#include "platform/base/interface_info.h"
+#include "platform/base/tls_listen_options.h"
+#include "util/osp_logging.h"
+#include "util/stringprintf.h"
+
+namespace openscreen {
+namespace cast {
+
+namespace {
+
+constexpr uint16_t kDefaultCastServicePort = 8010;
+
+constexpr int kDefaultMaxBacklogSize = 64;
+const TlsListenOptions kDefaultListenOptions{kDefaultMaxBacklogSize};
+
+IPEndpoint DetermineEndpoint(const InterfaceInfo& interface) {
+ const IPAddress address = interface.GetIpAddressV4()
+ ? interface.GetIpAddressV4()
+ : interface.GetIpAddressV6();
+ OSP_CHECK(address);
+ return IPEndpoint{address, kDefaultCastServicePort};
+}
+
+discovery::Config MakeDiscoveryConfig(const InterfaceInfo& interface) {
+ discovery::Config config;
+
+ discovery::Config::NetworkInfo::AddressFamilies supported_address_families =
+ discovery::Config::NetworkInfo::kNoAddressFamily;
+ if (interface.GetIpAddressV4()) {
+ supported_address_families |= discovery::Config::NetworkInfo::kUseIpV4;
+ }
+ if (interface.GetIpAddressV6()) {
+ supported_address_families |= discovery::Config::NetworkInfo::kUseIpV6;
+ }
+ config.network_info.push_back({interface, supported_address_families});
+
+ return config;
+}
+
+} // namespace
+
+CastService::CastService(TaskRunner* task_runner,
+ const InterfaceInfo& interface,
+ GeneratedCredentials credentials,
+ const std::string& friendly_name,
+ const std::string& model_name,
+ bool enable_discovery)
+ : local_endpoint_(DetermineEndpoint(interface)),
+ credentials_(std::move(credentials)),
+ agent_(task_runner, credentials_.provider.get()),
+ mirroring_application_(task_runner, local_endpoint_.address, &agent_),
+ socket_factory_(&agent_, agent_.cast_socket_client()),
+ connection_factory_(
+ TlsConnectionFactory::CreateFactory(&socket_factory_, task_runner)),
+ discovery_service_(enable_discovery ? discovery::CreateDnsSdService(
+ task_runner,
+ this,
+ MakeDiscoveryConfig(interface))
+ : LazyDeletedDiscoveryService()),
+ discovery_publisher_(
+ discovery_service_
+ ? MakeSerialDelete<discovery::DnsSdServicePublisher<ServiceInfo>>(
+ task_runner,
+ discovery_service_.get(),
+ kCastV2ServiceId,
+ ServiceInfoToDnsSdInstance)
+ : LazyDeletedDiscoveryPublisher()) {
+ connection_factory_->SetListenCredentials(credentials_.tls_credentials);
+ connection_factory_->Listen(local_endpoint_, kDefaultListenOptions);
+
+ if (discovery_publisher_) {
+ ServiceInfo info;
+ info.port = local_endpoint_.port;
+ info.unique_id = HexEncode(interface.hardware_address);
+ info.friendly_name = friendly_name;
+ info.model_name = model_name;
+ Error error = discovery_publisher_->Register(info);
+ if (!error.ok()) {
+ OnFatalError(std::move(error));
+ }
+ }
+}
+
+CastService::~CastService() {
+ if (discovery_publisher_) {
+ discovery_publisher_->DeregisterAll();
+ }
+}
+
+void CastService::OnFatalError(Error error) {
+ OSP_LOG_FATAL << "Encountered fatal discovery error: " << error;
+}
+
+void CastService::OnRecoverableError(Error error) {
+ OSP_LOG_ERROR << "Encountered recoverable discovery error: " << error;
+}
+
+} // namespace cast
+} // namespace openscreen
diff --git a/cast/standalone_receiver/cast_service.h b/cast/standalone_receiver/cast_service.h
new file mode 100644
index 00000000..99137de2
--- /dev/null
+++ b/cast/standalone_receiver/cast_service.h
@@ -0,0 +1,77 @@
+// 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 CAST_STANDALONE_RECEIVER_CAST_SERVICE_H_
+#define CAST_STANDALONE_RECEIVER_CAST_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "cast/common/public/service_info.h"
+#include "cast/receiver/application_agent.h"
+#include "cast/receiver/channel/static_credentials.h"
+#include "cast/receiver/public/receiver_socket_factory.h"
+#include "cast/standalone_receiver/mirroring_application.h"
+#include "discovery/common/reporting_client.h"
+#include "discovery/public/dns_sd_service_factory.h"
+#include "discovery/public/dns_sd_service_publisher.h"
+#include "platform/api/serial_delete_ptr.h"
+#include "platform/base/error.h"
+#include "platform/base/ip_address.h"
+
+namespace openscreen {
+
+struct InterfaceInfo;
+class TaskRunner;
+class TlsConnectionFactory;
+
+namespace cast {
+
+// Assembles all the necessary components and manages their lifetimes, to create
+// a full Cast Receiver on the network, with the following overall
+// functionality:
+//
+// * Listens for TCP connections on port 8010.
+// * Establishes TLS tunneling over those connections.
+// * Wraps a CastSocket API around the TLS connections.
+// * Manages available receiver-side applications.
+// * Provides a Cast V2 Mirroring application (media streaming playback in an
+// on-screen window).
+// * Publishes over mDNS to be discoverable to all senders on the same LAN.
+class CastService final : public discovery::ReportingClient {
+ public:
+ CastService(TaskRunner* task_runner,
+ const InterfaceInfo& interface,
+ GeneratedCredentials credentials,
+ const std::string& friendly_name,
+ const std::string& model_name,
+ bool enable_discovery = true);
+
+ ~CastService() final;
+
+ private:
+ using LazyDeletedDiscoveryService = SerialDeletePtr<discovery::DnsSdService>;
+ using LazyDeletedDiscoveryPublisher =
+ SerialDeletePtr<discovery::DnsSdServicePublisher<ServiceInfo>>;
+
+ // discovery::ReportingClient overrides.
+ void OnFatalError(Error error) final;
+ void OnRecoverableError(Error error) final;
+
+ const IPEndpoint local_endpoint_;
+ const GeneratedCredentials credentials_;
+
+ ApplicationAgent agent_;
+ MirroringApplication mirroring_application_;
+ ReceiverSocketFactory socket_factory_;
+ std::unique_ptr<TlsConnectionFactory> connection_factory_;
+
+ LazyDeletedDiscoveryService discovery_service_;
+ LazyDeletedDiscoveryPublisher discovery_publisher_;
+};
+
+} // namespace cast
+} // namespace openscreen
+
+#endif // CAST_STANDALONE_RECEIVER_CAST_SERVICE_H_
diff --git a/cast/standalone_receiver/main.cc b/cast/standalone_receiver/main.cc
index 4e8c321c..6f6b051d 100644
--- a/cast/standalone_receiver/main.cc
+++ b/cast/standalone_receiver/main.cc
@@ -4,21 +4,17 @@
#include <getopt.h>
-#include <array>
-#include <chrono>
+#include <algorithm>
#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
#include "absl/strings/str_cat.h"
-#include "cast/common/public/service_info.h"
#include "cast/receiver/channel/static_credentials.h"
-#include "cast/standalone_receiver/cast_agent.h"
-#include "cast/streaming/ssrc.h"
-#include "discovery/common/config.h"
-#include "discovery/common/reporting_client.h"
-#include "discovery/public/dns_sd_service_factory.h"
-#include "discovery/public/dns_sd_service_publisher.h"
+#include "cast/standalone_receiver/cast_service.h"
#include "platform/api/time.h"
-#include "platform/api/udp_socket.h"
#include "platform/base/error.h"
#include "platform/base/ip_address.h"
#include "platform/impl/logging.h"
@@ -34,87 +30,6 @@ namespace openscreen {
namespace cast {
namespace {
-class DiscoveryReportingClient : public discovery::ReportingClient {
- void OnFatalError(Error error) override {
- OSP_LOG_FATAL << "Encountered fatal discovery error: " << error;
- }
-
- void OnRecoverableError(Error error) override {
- OSP_LOG_ERROR << "Encountered recoverable discovery error: " << error;
- }
-};
-
-struct DiscoveryState {
- SerialDeletePtr<discovery::DnsSdService> service;
- std::unique_ptr<DiscoveryReportingClient> reporting_client;
- std::unique_ptr<discovery::DnsSdServicePublisher<ServiceInfo>> publisher;
-};
-
-ErrorOr<std::unique_ptr<DiscoveryState>> StartDiscovery(
- TaskRunner* task_runner,
- const InterfaceInfo& interface,
- const std::string& friendly_name,
- const std::string& model_name) {
- TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
- discovery::Config config;
-
- discovery::Config::NetworkInfo::AddressFamilies supported_address_families =
- discovery::Config::NetworkInfo::kNoAddressFamily;
- if (interface.GetIpAddressV4()) {
- supported_address_families |= discovery::Config::NetworkInfo::kUseIpV4;
- }
- if (interface.GetIpAddressV6()) {
- supported_address_families |= discovery::Config::NetworkInfo::kUseIpV6;
- }
- OSP_CHECK(supported_address_families !=
- discovery::Config::NetworkInfo::kNoAddressFamily)
- << "No address families supported by the selected interface";
- config.network_info.push_back({interface, supported_address_families});
-
- auto state = std::make_unique<DiscoveryState>();
- state->reporting_client = std::make_unique<DiscoveryReportingClient>();
- state->service = discovery::CreateDnsSdService(
- task_runner, state->reporting_client.get(), config);
-
- ServiceInfo info;
- info.port = kDefaultCastPort;
-
- if (std::all_of(interface.hardware_address.begin(),
- interface.hardware_address.end(),
- [](int e) { return e == 0; })) {
- OSP_LOG_WARN
- << "Hardware address is empty. Either you are on a loopback device "
- "or getting the network interface information failed somehow.";
- }
- info.unique_id = HexEncode(interface.hardware_address);
- info.friendly_name = friendly_name;
- info.model_name = model_name;
-
- state->publisher =
- std::make_unique<discovery::DnsSdServicePublisher<ServiceInfo>>(
- state->service.get(), kCastV2ServiceId, ServiceInfoToDnsSdInstance);
-
- auto error = state->publisher->Register(info);
- if (!error.ok()) {
- return error;
- }
- return state;
-}
-
-std::unique_ptr<CastAgent> StartCastAgent(TaskRunnerImpl* task_runner,
- const InterfaceInfo& interface,
- GeneratedCredentials* creds) {
- TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneReceiver);
- auto agent = std::make_unique<CastAgent>(
- task_runner, interface, creds->provider.get(), creds->tls_credentials);
- const auto error = agent->Start();
- if (!error.ok()) {
- OSP_LOG_ERROR << "Error occurred while starting agent: " << error;
- agent.reset();
- }
- return agent;
-}
-
void LogUsage(const char* argv0) {
constexpr char kTemplate[] = R"(
usage: %s <options> <interface>
@@ -130,7 +45,7 @@ options:
provided, a randomly generated one will be used for this
session.
- -s, --developer-certificate=path-to-cert: Path to PEM file containing a
+ -d, --developer-certificate=path-to-cert: Path to PEM file containing a
developer generated server root TLS certificate.
If a root server certificate is not provided, one
will be generated using a randomly generated
@@ -180,6 +95,33 @@ InterfaceInfo GetInterfaceInfoFromName(const char* name) {
return interface_info;
}
+void RunCastService(TaskRunnerImpl* task_runner,
+ const InterfaceInfo& interface,
+ GeneratedCredentials creds,
+ const std::string& friendly_name,
+ const std::string& model_name,
+ bool discovery_enabled) {
+ std::unique_ptr<CastService> service;
+ task_runner->PostTask([&] {
+ service = std::make_unique<CastService>(task_runner, interface,
+ std::move(creds), friendly_name,
+ model_name, discovery_enabled);
+ });
+
+ OSP_LOG_INFO << "CastService is running. CTRL-C (SIGINT), or send a "
+ "SIGTERM to exit.";
+ task_runner->RunUntilSignaled();
+
+ // Spin the TaskRunner to execute destruction/shutdown tasks.
+ OSP_LOG_INFO << "Shutting down...";
+ task_runner->PostTask([&] {
+ service.reset();
+ task_runner->RequestStopSoon();
+ });
+ task_runner->RunUntilStopped();
+ OSP_LOG_INFO << "Bye!";
+}
+
int RunStandaloneReceiver(int argc, char* argv[]) {
#if !defined(CAST_ALLOW_DEVELOPER_CERTIFICATE)
OSP_LOG_FATAL
@@ -216,7 +158,7 @@ int RunStandaloneReceiver(int argc, char* argv[]) {
std::string friendly_name = "Cast Standalone Receiver";
std::string model_name = "cast_standalone_receiver";
bool should_generate_credentials = false;
- std::unique_ptr<openscreen::TextTraceLoggingPlatform> trace_logger;
+ std::unique_ptr<TextTraceLoggingPlatform> trace_logger;
int ch = -1;
while ((ch = getopt_long(argc, argv, "p:d:f:m:gtvhx", kArgumentOptions,
nullptr)) != -1) {
@@ -237,7 +179,7 @@ int RunStandaloneReceiver(int argc, char* argv[]) {
should_generate_credentials = true;
break;
case 't':
- trace_logger = std::make_unique<openscreen::TextTraceLoggingPlatform>();
+ trace_logger = std::make_unique<TextTraceLoggingPlatform>();
break;
case 'v':
is_verbose = true;
@@ -251,8 +193,7 @@ int RunStandaloneReceiver(int argc, char* argv[]) {
}
}
- SetLogLevel(is_verbose ? openscreen::LogLevel::kVerbose
- : openscreen::LogLevel::kInfo);
+ SetLogLevel(is_verbose ? LogLevel::kVerbose : LogLevel::kInfo);
// Either -g is required, or both -p and -d.
if (should_generate_credentials) {
@@ -266,14 +207,6 @@ int RunStandaloneReceiver(int argc, char* argv[]) {
return 1;
}
- auto* const task_runner = new TaskRunnerImpl(&Clock::now);
- PlatformClientPosix::Create(milliseconds(50), milliseconds(50),
- std::unique_ptr<TaskRunnerImpl>(task_runner));
-
- // Post tasks to kick-off the CastAgent and, if successful, start discovery to
- // make this standalone receiver visible to senders on the network.
- std::unique_ptr<DiscoveryState> discovery_state;
- std::unique_ptr<CastAgent> cast_agent;
const char* interface_name = argv[optind];
OSP_CHECK(interface_name && strlen(interface_name) > 0)
<< "No interface name provided.";
@@ -283,33 +216,22 @@ int RunStandaloneReceiver(int argc, char* argv[]) {
ErrorOr<GeneratedCredentials> creds = GenerateCredentials(
device_id, private_key_path, developer_certificate_path);
OSP_CHECK(creds.is_value()) << creds.error();
- task_runner->PostTask(
- [&, interface = GetInterfaceInfoFromName(interface_name)] {
- cast_agent = StartCastAgent(task_runner, interface, &(creds.value()));
- OSP_CHECK(cast_agent) << "Failed to start CastAgent.";
-
- if (discovery_enabled) {
- auto result =
- StartDiscovery(task_runner, interface, friendly_name, model_name);
- OSP_CHECK(result.is_value()) << "Failed to start discovery.";
- discovery_state = std::move(result.value());
- }
- });
-
- // Run the event loop until an exit is requested (e.g., the video player GUI
- // window is closed, a SIGINT or SIGTERM is received, or whatever other
- // appropriate user indication that shutdown is requested).
- task_runner->RunUntilSignaled();
- // Shutdown the Cast Agent and discovery-related entities. This may cause one
- // or more tasks to be posted, and so the TaskRunner is spun to give them a
- // chance to execute.
- discovery_state.reset();
- cast_agent.reset();
- task_runner->PostTask([task_runner] { task_runner->RequestStopSoon(); });
- task_runner->RunUntilStopped();
+ const InterfaceInfo interface = GetInterfaceInfoFromName(interface_name);
+ OSP_CHECK(interface.GetIpAddressV4() || interface.GetIpAddressV6());
+ OSP_CHECK(std::any_of(interface.hardware_address.begin(),
+ interface.hardware_address.end(),
+ [](int e) { return e > 0; }))
+ << "Hardware address is empty. Either you are on a loopback device "
+ "or getting the network interface information failed somehow.";
+ auto* const task_runner = new TaskRunnerImpl(&Clock::now);
+ PlatformClientPosix::Create(milliseconds(50), milliseconds(50),
+ std::unique_ptr<TaskRunnerImpl>(task_runner));
+ RunCastService(task_runner, interface, std::move(creds.value()),
+ friendly_name, model_name, discovery_enabled);
PlatformClientPosix::ShutDown();
+
return 0;
}
diff --git a/cast/standalone_receiver/mirroring_application.cc b/cast/standalone_receiver/mirroring_application.cc
new file mode 100644
index 00000000..a04c401a
--- /dev/null
+++ b/cast/standalone_receiver/mirroring_application.cc
@@ -0,0 +1,90 @@
+// 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 "cast/standalone_receiver/mirroring_application.h"
+
+#include "cast/common/public/message_port.h"
+#include "cast/streaming/environment.h"
+#include "cast/streaming/message_fields.h"
+#include "cast/streaming/receiver_session.h"
+#include "platform/api/task_runner.h"
+#include "util/osp_logging.h"
+
+namespace openscreen {
+namespace cast {
+
+const char kMirroringAppId[] = "0F5096E8";
+const char kMirroringAudioOnlyAppId[] = "85CDB22F";
+
+const char kMirroringDisplayName[] = "Chrome Mirroring";
+const char kRemotingRpcNamespace[] = "urn:x-cast:com.google.cast.remoting";
+
+MirroringApplication::MirroringApplication(TaskRunner* task_runner,
+ const IPAddress& interface_address,
+ ApplicationAgent* agent)
+ : task_runner_(task_runner),
+ interface_address_(interface_address),
+ app_ids_({kMirroringAppId, kMirroringAudioOnlyAppId}),
+ agent_(agent) {
+ OSP_DCHECK(task_runner_);
+ OSP_DCHECK(agent_);
+ agent_->RegisterApplication(this);
+}
+
+MirroringApplication::~MirroringApplication() {
+ agent_->UnregisterApplication(this); // ApplicationAgent may call Stop().
+ OSP_DCHECK(!current_session_);
+}
+
+const std::vector<std::string>& MirroringApplication::GetAppIds() const {
+ return app_ids_;
+}
+
+bool MirroringApplication::Launch(const std::string& app_id,
+ const Json::Value& app_params,
+ MessagePort* message_port) {
+ if ((app_id != kMirroringAppId && app_id != kMirroringAudioOnlyAppId) ||
+ !message_port || current_session_) {
+ return false;
+ }
+
+ wake_lock_ = ScopedWakeLock::Create(task_runner_);
+ environment_ = std::make_unique<Environment>(
+ &Clock::now, task_runner_,
+ IPEndpoint{interface_address_, kDefaultCastStreamingPort});
+ controller_ =
+ std::make_unique<StreamingPlaybackController>(task_runner_, this);
+ current_session_ = std::make_unique<ReceiverSession>(
+ controller_.get(), environment_.get(), message_port,
+ ReceiverSession::Preferences{});
+ return true;
+}
+
+std::string MirroringApplication::GetSessionId() {
+ return current_session_ ? current_session_->session_id() : std::string();
+}
+
+std::string MirroringApplication::GetDisplayName() {
+ return current_session_ ? kMirroringDisplayName : std::string();
+}
+
+std::vector<std::string> MirroringApplication::GetSupportedNamespaces() {
+ return {kCastWebrtcNamespace, kRemotingRpcNamespace};
+}
+
+void MirroringApplication::Stop() {
+ current_session_.reset();
+ controller_.reset();
+ environment_.reset();
+ wake_lock_.reset();
+}
+
+void MirroringApplication::OnPlaybackError(StreamingPlaybackController*,
+ Error error) {
+ OSP_LOG_ERROR << "[MirroringApplication] " << error;
+ agent_->StopApplicationIfRunning(this); // ApplicationAgent calls Stop().
+}
+
+} // namespace cast
+} // namespace openscreen
diff --git a/cast/standalone_receiver/mirroring_application.h b/cast/standalone_receiver/mirroring_application.h
new file mode 100644
index 00000000..c2e8ccc8
--- /dev/null
+++ b/cast/standalone_receiver/mirroring_application.h
@@ -0,0 +1,69 @@
+// 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 CAST_STANDALONE_RECEIVER_MIRRORING_APPLICATION_H_
+#define CAST_STANDALONE_RECEIVER_MIRRORING_APPLICATION_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cast/receiver/application_agent.h"
+#include "cast/standalone_receiver/streaming_playback_controller.h"
+#include "platform/api/scoped_wake_lock.h"
+#include "platform/api/serial_delete_ptr.h"
+#include "platform/base/error.h"
+#include "platform/base/ip_address.h"
+
+namespace openscreen {
+
+class TaskRunner;
+
+namespace cast {
+
+class MessagePort;
+class ReceiverSession;
+
+// Implements a basic Cast V2 Mirroring Application which, at launch time,
+// bootstraps a ReceiverSession and StreamingPlaybackController, which set-up
+// and manage the media data streaming and play it out in an on-screen window.
+class MirroringApplication final : public ApplicationAgent::Application,
+ public StreamingPlaybackController::Client {
+ public:
+ MirroringApplication(TaskRunner* task_runner,
+ const IPAddress& interface_address,
+ ApplicationAgent* agent);
+
+ ~MirroringApplication() final;
+
+ // ApplicationAgent::Application overrides.
+ const std::vector<std::string>& GetAppIds() const final;
+ bool Launch(const std::string& app_id,
+ const Json::Value& app_params,
+ MessagePort* message_port) final;
+ std::string GetSessionId() final;
+ std::string GetDisplayName() final;
+ std::vector<std::string> GetSupportedNamespaces() final;
+ void Stop() final;
+
+ // StreamingPlaybackController::Client overrides
+ void OnPlaybackError(StreamingPlaybackController* controller,
+ Error error) final;
+
+ private:
+ TaskRunner* const task_runner_;
+ const IPAddress interface_address_;
+ const std::vector<std::string> app_ids_;
+ ApplicationAgent* const agent_;
+
+ SerialDeletePtr<ScopedWakeLock> wake_lock_;
+ std::unique_ptr<Environment> environment_;
+ std::unique_ptr<StreamingPlaybackController> controller_;
+ std::unique_ptr<ReceiverSession> current_session_;
+};
+
+} // namespace cast
+} // namespace openscreen
+
+#endif // CAST_STANDALONE_RECEIVER_MIRRORING_APPLICATION_H_