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