1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "cast/standalone_sender/looping_file_cast_agent.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "cast/standalone_sender/looping_file_sender.h"
12 #include "cast/streaming/capture_recommendations.h"
13 #include "cast/streaming/constants.h"
14 #include "cast/streaming/offer_messages.h"
15 #include "util/trace_logging.h"
16
17 namespace openscreen {
18 namespace cast {
19 namespace {
20
21 using DeviceMediaPolicy = SenderSocketFactory::DeviceMediaPolicy;
22
23 } // namespace
24
LoopingFileCastAgent(TaskRunner * task_runner)25 LoopingFileCastAgent::LoopingFileCastAgent(TaskRunner* task_runner)
26 : task_runner_(task_runner) {
27 router_ = MakeSerialDelete<VirtualConnectionRouter>(task_runner_,
28 &connection_manager_);
29 message_port_ =
30 MakeSerialDelete<CastSocketMessagePort>(task_runner_, router_.get());
31 socket_factory_ =
32 MakeSerialDelete<SenderSocketFactory>(task_runner_, this, task_runner_);
33 connection_factory_ = SerialDeletePtr<TlsConnectionFactory>(
34 task_runner_,
35 TlsConnectionFactory::CreateFactory(socket_factory_.get(), task_runner_)
36 .release());
37 socket_factory_->set_factory(connection_factory_.get());
38 }
39
40 LoopingFileCastAgent::~LoopingFileCastAgent() = default;
41
Connect(ConnectionSettings settings)42 void LoopingFileCastAgent::Connect(ConnectionSettings settings) {
43 TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
44 connection_settings_ = std::move(settings);
45 const auto policy = connection_settings_->should_include_video
46 ? DeviceMediaPolicy::kIncludesVideo
47 : DeviceMediaPolicy::kAudioOnly;
48
49 task_runner_->PostTask([this, policy] {
50 wake_lock_ = ScopedWakeLock::Create(task_runner_);
51 socket_factory_->Connect(connection_settings_->receiver_endpoint, policy,
52 router_.get());
53 });
54 }
55
Stop()56 void LoopingFileCastAgent::Stop() {
57 task_runner_->PostTask([this] {
58 StopCurrentSession();
59
60 connection_factory_.reset();
61 connection_settings_.reset();
62 socket_factory_.reset();
63 wake_lock_.reset();
64 });
65 }
66
OnConnected(SenderSocketFactory * factory,const IPEndpoint & endpoint,std::unique_ptr<CastSocket> socket)67 void LoopingFileCastAgent::OnConnected(SenderSocketFactory* factory,
68 const IPEndpoint& endpoint,
69 std::unique_ptr<CastSocket> socket) {
70 TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
71 if (current_session_) {
72 OSP_LOG_WARN << "Already connected, dropping peer at: " << endpoint;
73 return;
74 }
75
76 OSP_LOG_INFO << "Received connection from peer at: " << endpoint;
77 message_port_->SetSocket(socket->GetWeakPtr());
78 router_->TakeSocket(this, std::move(socket));
79 CreateAndStartSession();
80 }
81
OnError(SenderSocketFactory * factory,const IPEndpoint & endpoint,Error error)82 void LoopingFileCastAgent::OnError(SenderSocketFactory* factory,
83 const IPEndpoint& endpoint,
84 Error error) {
85 OSP_LOG_ERROR << "Cast agent received socket factory error: " << error;
86 StopCurrentSession();
87 }
88
OnClose(CastSocket * cast_socket)89 void LoopingFileCastAgent::OnClose(CastSocket* cast_socket) {
90 OSP_VLOG << "Cast agent socket closed.";
91 StopCurrentSession();
92 }
93
OnError(CastSocket * socket,Error error)94 void LoopingFileCastAgent::OnError(CastSocket* socket, Error error) {
95 OSP_LOG_ERROR << "Cast agent received socket error: " << error;
96 StopCurrentSession();
97 }
98
OnNegotiated(const SenderSession * session,SenderSession::ConfiguredSenders senders,capture_recommendations::Recommendations capture_recommendations)99 void LoopingFileCastAgent::OnNegotiated(
100 const SenderSession* session,
101 SenderSession::ConfiguredSenders senders,
102 capture_recommendations::Recommendations capture_recommendations) {
103 if (senders.audio_sender == nullptr || senders.video_sender == nullptr) {
104 OSP_LOG_ERROR << "Missing either audio or video, so exiting...";
105 return;
106 }
107
108 OSP_VLOG << "Successfully negotiated with sender.";
109
110 file_sender_ = std::make_unique<LoopingFileSender>(
111 task_runner_, connection_settings_->path_to_file.c_str(),
112 connection_settings_->receiver_endpoint, senders,
113 connection_settings_->max_bitrate);
114 }
115
116 // Currently, we just kill the session if an error is encountered.
OnError(const SenderSession * session,Error error)117 void LoopingFileCastAgent::OnError(const SenderSession* session, Error error) {
118 OSP_LOG_ERROR << "Cast agent received sender session error: " << error;
119 StopCurrentSession();
120 }
121
CreateAndStartSession()122 void LoopingFileCastAgent::CreateAndStartSession() {
123 TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender);
124 environment_ =
125 std::make_unique<Environment>(&Clock::now, task_runner_, IPEndpoint{});
126 current_session_ = std::make_unique<SenderSession>(
127 connection_settings_->receiver_endpoint.address, this, environment_.get(),
128 message_port_.get());
129
130 AudioCaptureConfig audio_config;
131 VideoCaptureConfig video_config;
132 // Use default display resolution of 1080P.
133 video_config.resolutions.emplace_back(DisplayResolution{});
134
135 OSP_VLOG << "Starting session negotiation.";
136 const Error negotiation_error =
137 current_session_->Negotiate({audio_config}, {video_config});
138 if (!negotiation_error.ok()) {
139 OSP_LOG_ERROR << "Failed to negotiate a session: " << negotiation_error;
140 }
141 }
142
StopCurrentSession()143 void LoopingFileCastAgent::StopCurrentSession() {
144 current_session_.reset();
145 environment_.reset();
146 file_sender_.reset();
147 router_->CloseSocket(message_port_->GetSocketId());
148 message_port_->SetSocket(nullptr);
149 }
150
151 } // namespace cast
152 } // namespace openscreen
153