diff options
author | Yuri Wiitala <miu@chromium.org> | 2020-11-09 12:10:55 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-11-09 20:32:07 +0000 |
commit | 7b6396aaef5bf0693c6b9aa2e4bdbe139a28e57a (patch) | |
tree | bdca6e00b750a3f8c0b9acedaee39729fb29dae6 | |
parent | 82a5b2d213634a64b952e9135901e0f0e4b81571 (diff) | |
download | openscreen-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>
-rw-r--r-- | BUILD.gn | 1 | ||||
-rw-r--r-- | cast/receiver/application_agent.cc | 5 | ||||
-rw-r--r-- | cast/receiver/application_agent.h | 7 | ||||
-rw-r--r-- | cast/receiver/application_agent_unittest.cc | 2 | ||||
-rw-r--r-- | cast/standalone_receiver/BUILD.gn | 22 | ||||
-rw-r--r-- | cast/standalone_receiver/cast_agent.cc | 170 | ||||
-rw-r--r-- | cast/standalone_receiver/cast_agent.h | 111 | ||||
-rw-r--r-- | cast/standalone_receiver/cast_agent_integration_tests.cc | 142 | ||||
-rw-r--r-- | cast/standalone_receiver/cast_service.cc | 109 | ||||
-rw-r--r-- | cast/standalone_receiver/cast_service.h | 77 | ||||
-rw-r--r-- | cast/standalone_receiver/main.cc | 178 | ||||
-rw-r--r-- | cast/standalone_receiver/mirroring_application.cc | 90 | ||||
-rw-r--r-- | cast/standalone_receiver/mirroring_application.h | 69 | ||||
-rw-r--r-- | cast/streaming/constants.h | 5 | ||||
-rw-r--r-- | cast/streaming/receiver_session.cc | 4 | ||||
-rw-r--r-- | cast/streaming/receiver_session.h | 3 | ||||
-rw-r--r-- | cast/streaming/sender_session.cc | 3 | ||||
-rw-r--r-- | discovery/dnssd/public/dns_sd_publisher.h | 4 | ||||
-rw-r--r-- | discovery/public/dns_sd_service_publisher.h | 2 |
19 files changed, 428 insertions, 576 deletions
@@ -118,7 +118,6 @@ if (!build_with_chromium && is_posix) { testonly = true public_deps = [ "cast/common:discovery_e2e_test", - "cast/standalone_receiver:e2e_tests", "cast/test:e2e_tests", "cast/test:make_crl_tests($host_toolchain)", ] diff --git a/cast/receiver/application_agent.cc b/cast/receiver/application_agent.cc index 4dc435d6..97f2da22 100644 --- a/cast/receiver/application_agent.cc +++ b/cast/receiver/application_agent.cc @@ -358,6 +358,11 @@ void ApplicationAgent::PopulateReceiverStatus(Json::Value* out) { if (launched_app_) { Json::Value& details = status[kMessageKeyApplications][0]; + // If the Application can send/receive messages, the destination for such + // messages is provided here, in |transportId|. However, the other end must + // first set up the virtual connection by issuing a CONNECT request. + // Otherwise, messages will not get routed to the Application by the + // VirtualConnectionRouter. if (!message_port_.client_sender_id().empty()) { details[kMessageKeyTransportId] = message_port_.client_sender_id(); } diff --git a/cast/receiver/application_agent.h b/cast/receiver/application_agent.h index 4aa551d3..a6a792aa 100644 --- a/cast/receiver/application_agent.h +++ b/cast/receiver/application_agent.h @@ -14,6 +14,7 @@ #include "cast/common/channel/connection_namespace_handler.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/public/receiver_socket_factory.h" #include "platform/api/serial_delete_ptr.h" @@ -87,6 +88,10 @@ class ApplicationAgent final ~ApplicationAgent() final; + // Return the interface by which the CastSocket inbound traffic is delivered + // into this agent and any running Applications. + CastSocket::Client* cast_socket_client() { return &router_; } + // Registers an Application for launching by this agent. |app| must outlive // this ApplicationAgent, or until UnregisterApplication() is called. void RegisterApplication(Application* app, @@ -151,8 +156,8 @@ class ApplicationAgent final TaskRunner* const task_runner_; DeviceAuthNamespaceHandler auth_handler_; - ConnectionNamespaceHandler connection_handler_; VirtualConnectionManager connection_manager_; + ConnectionNamespaceHandler connection_handler_; VirtualConnectionRouter router_; std::map<std::string, Application*> registered_applications_; diff --git a/cast/receiver/application_agent_unittest.cc b/cast/receiver/application_agent_unittest.cc index c89a451f..6240ac02 100644 --- a/cast/receiver/application_agent_unittest.cc +++ b/cast/receiver/application_agent_unittest.cc @@ -78,7 +78,7 @@ CastMessage TestAuthChallengeMessage() { class FakeApplication : public ApplicationAgent::Application, public MessagePort::Client { public: - explicit FakeApplication(const char* app_id, const char* display_name) + FakeApplication(const char* app_id, const char* display_name) : app_ids_({app_id}), display_name_(display_name) { OSP_CHECK(app_ids_.front().size() == 8); } 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_ diff --git a/cast/streaming/constants.h b/cast/streaming/constants.h index 65cb2a1a..1075a817 100644 --- a/cast/streaming/constants.h +++ b/cast/streaming/constants.h @@ -65,11 +65,6 @@ constexpr int kDefaultAudioSampleRate = 48000; // The default audio number of channels is set to stereo. constexpr int kDefaultAudioChannels = 2; -// TODO(jophba): migrate to discovering a randomly generated streaming -// sender id. This will require communicating the ID to the sender so that -// it can send messages appropriately. -constexpr char kDefaultStreamingReceiverSenderId[] = "receiver-12345"; - // Codecs known and understood by cast senders and receivers. Note: receivers // are required to implement the following codecs to be Cast V2 compliant: H264, // VP8, AAC, Opus. Senders have to implement at least one codec for audio and diff --git a/cast/streaming/receiver_session.cc b/cast/streaming/receiver_session.cc index 66cd01d1..a78b7bd8 100644 --- a/cast/streaming/receiver_session.cc +++ b/cast/streaming/receiver_session.cc @@ -11,6 +11,7 @@ #include "absl/strings/match.h" #include "absl/strings/numbers.h" +#include "cast/common/channel/message_util.h" #include "cast/common/public/message_port.h" #include "cast/streaming/environment.h" #include "cast/streaming/message_fields.h" @@ -96,8 +97,9 @@ ReceiverSession::ReceiverSession(Client* const client, : client_(client), environment_(environment), preferences_(std::move(preferences)), + session_id_(MakeUniqueSessionId("streaming_receiver")), messager_(message_port, - kDefaultStreamingReceiverSenderId, + session_id_, [this](Error error) { OSP_DLOG_WARN << "Got a session messager error: " << error; client_->OnError(this, error); diff --git a/cast/streaming/receiver_session.h b/cast/streaming/receiver_session.h index f7bc7f27..fc181960 100644 --- a/cast/streaming/receiver_session.h +++ b/cast/streaming/receiver_session.h @@ -112,6 +112,8 @@ class ReceiverSession final { ReceiverSession& operator=(ReceiverSession&&) = delete; ~ReceiverSession(); + const std::string& session_id() const { return session_id_; } + private: // Specific message type handler methods. void OnOffer(SessionMessager::Message message); @@ -135,6 +137,7 @@ class ReceiverSession final { Client* const client_; Environment* const environment_; const Preferences preferences_; + const std::string session_id_; SessionMessager messager_; bool supports_wifi_status_reporting_ = false; diff --git a/cast/streaming/sender_session.cc b/cast/streaming/sender_session.cc index cd8f62c4..63f291bc 100644 --- a/cast/streaming/sender_session.cc +++ b/cast/streaming/sender_session.cc @@ -198,8 +198,9 @@ Error SenderSession::Negotiate(std::vector<AudioCaptureConfig> audio_configs, // Currently we don't have a way to discover the ID of the receiver we // are connected to, since we have to send the first message. // TODO(jophba): migrate to discovered receiver ID when available. + static constexpr char kPlaceholderReceiverSenderId[] = "receiver-12345"; messager_.SendMessage(SessionMessager::Message{ - kDefaultStreamingReceiverSenderId, kCastWebrtcNamespace, + kPlaceholderReceiverSenderId, kCastWebrtcNamespace, ++current_sequence_number_, std::move(message_body)}); return Error::None(); } diff --git a/discovery/dnssd/public/dns_sd_publisher.h b/discovery/dnssd/public/dns_sd_publisher.h index abbd8b1a..3c139b4e 100644 --- a/discovery/dnssd/public/dns_sd_publisher.h +++ b/discovery/dnssd/public/dns_sd_publisher.h @@ -5,12 +5,16 @@ #ifndef DISCOVERY_DNSSD_PUBLIC_DNS_SD_PUBLISHER_H_ #define DISCOVERY_DNSSD_PUBLIC_DNS_SD_PUBLISHER_H_ +#include <string> + #include "discovery/dnssd/public/dns_sd_instance.h" #include "platform/base/error.h" namespace openscreen { namespace discovery { +class DnsSdInstanceEndpoint; + class DnsSdPublisher { public: class Client { diff --git a/discovery/public/dns_sd_service_publisher.h b/discovery/public/dns_sd_service_publisher.h index 29659a5f..e99d6743 100644 --- a/discovery/public/dns_sd_service_publisher.h +++ b/discovery/public/dns_sd_service_publisher.h @@ -6,8 +6,10 @@ #define DISCOVERY_PUBLIC_DNS_SD_SERVICE_PUBLISHER_H_ #include <string> +#include <utility> #include "discovery/dnssd/public/dns_sd_instance.h" +#include "discovery/dnssd/public/dns_sd_instance_endpoint.h" #include "discovery/dnssd/public/dns_sd_publisher.h" #include "discovery/dnssd/public/dns_sd_service.h" #include "platform/base/error.h" |