1 /*
2 * This file is part of Wireless Display Software for Linux OS
3 *
4 * Copyright (C) 2014 Intel Corporation.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 */
21
22 #include <glib-unix.h>
23 #include <algorithm>
24
25 #include "mirac-broker.hpp"
26 #include "mirac-glib-logging.hpp"
27
28 struct TimerCallbackData {
TimerCallbackDataTimerCallbackData29 TimerCallbackData(MiracBroker* delegate)
30 : delegate_(delegate), timer_id_(0) {}
31 MiracBroker* delegate_;
32 uint timer_id_;
33 };
34
35 /* static C callback wrapper */
send_cb(gint fd,GIOCondition condition,gpointer data_ptr)36 gboolean MiracBroker::send_cb (gint fd, GIOCondition condition, gpointer data_ptr)
37 {
38 auto broker = static_cast<MiracBroker**> (data_ptr);
39 return (*broker)->send_cb(fd, condition);
40 }
41
42 /* static C callback wrapper */
receive_cb(gint fd,GIOCondition condition,gpointer data_ptr)43 gboolean MiracBroker::receive_cb (gint fd, GIOCondition condition, gpointer data_ptr)
44 {
45 auto broker = static_cast<MiracBroker**> (data_ptr);
46 return (*broker)->receive_cb(fd, condition);
47 }
48
49 /* static C callback wrapper */
listen_cb(gint fd,GIOCondition condition,gpointer data_ptr)50 gboolean MiracBroker::listen_cb (gint fd, GIOCondition condition, gpointer data_ptr)
51 {
52 auto broker = static_cast<MiracBroker**> (data_ptr);
53 return (*broker)->listen_cb(fd, condition);
54 }
55
56 /* static C callback wrapper */
connect_cb(gint fd,GIOCondition condition,gpointer data_ptr)57 gboolean MiracBroker::connect_cb (gint fd, GIOCondition condition, gpointer data_ptr)
58 {
59 auto broker = static_cast<MiracBroker**> (data_ptr);
60 return (*broker)->connect_cb(fd, condition);
61 }
62
63 /* static C callback wrapper */
try_connect(gpointer data_ptr)64 gboolean MiracBroker::try_connect (gpointer data_ptr)
65 {
66 auto broker = static_cast<MiracBroker*> (data_ptr);
67 broker->try_connect();
68 return false;
69 }
70
send_cb(gint fd,GIOCondition condition)71 gboolean MiracBroker::send_cb (gint fd, GIOCondition condition)
72 {
73 try {
74 if (!connection_->Send())
75 return G_SOURCE_CONTINUE;
76 } catch (const MiracConnectionLostException &exception) {
77 on_connection_failure(CONNECTION_LOST);
78 } catch (const std::exception &x) {
79 WDS_WARNING("exception: %s", x.what());
80 }
81 return G_SOURCE_REMOVE;
82 }
83
84
receive_cb(gint fd,GIOCondition condition)85 gboolean MiracBroker::receive_cb (gint fd, GIOCondition condition)
86 {
87 std::string msg;
88 try {
89 if (connection_->Receive(msg)) {
90 WDS_VLOG("Received RTSP message:\n%s", msg.c_str());
91 got_message (msg);
92 }
93 } catch (const MiracConnectionLostException &exception) {
94 on_connection_failure(CONNECTION_LOST);
95 return G_SOURCE_REMOVE;
96 }
97 return G_SOURCE_CONTINUE;
98 }
99
listen_cb(gint fd,GIOCondition condition)100 gboolean MiracBroker::listen_cb (gint fd, GIOCondition condition)
101 {
102 try {
103 connection(network_->Accept());
104 WDS_LOG("connection from: %s", connection_->GetPeerAddress().c_str());
105 on_connected();
106 } catch (const std::exception &x) {
107 WDS_WARNING("exception: %s", x.what());
108 }
109
110 return G_SOURCE_CONTINUE;
111 }
112
connect_cb(gint fd,GIOCondition condition)113 gboolean MiracBroker::connect_cb (gint fd, GIOCondition condition)
114 {
115 try {
116 if (!network_->Connect(NULL, NULL))
117 return G_SOURCE_CONTINUE;
118 WDS_LOG("connection success to: %s", network_->GetPeerAddress().c_str());
119 connection(network_.release());
120
121 /* make sure any network event sources are removed */
122 network(NULL);
123
124 on_connected();
125 } catch (const std::exception &x) {
126 gdouble elapsed = 1000 * g_timer_elapsed(connect_timer_, NULL);
127 if (elapsed + connect_wait_ > connect_timeout_) {
128 on_connection_failure(CONNECTION_TIMEOUT);
129 } else {
130 connect_wait_id_ = g_timeout_add (connect_wait_, try_connect, this);
131 }
132 }
133 return G_SOURCE_REMOVE;
134 }
135
network(MiracNetwork * connection)136 void MiracBroker::network(MiracNetwork *connection)
137 {
138 while (g_source_remove_by_user_data(&network_source_ptr_))
139 ;
140 network_.reset(connection);
141 }
142
connection(MiracNetwork * connection)143 void MiracBroker::connection(MiracNetwork *connection)
144 {
145 while (g_source_remove_by_user_data(&connection_source_ptr_))
146 ;
147 connection_.reset(connection);
148
149 if (connection_)
150 g_unix_fd_add(connection_->GetHandle(), G_IO_IN,
151 receive_cb, &connection_source_ptr_);
152 }
153
try_connect()154 void MiracBroker::try_connect()
155 {
156 WDS_LOG("Trying to connect...");
157
158 connect_wait_id_ = 0;
159 network(new MiracNetwork());
160
161 if (network_->Connect(peer_address_.c_str(), peer_port_.c_str())) {
162 g_unix_fd_add(network_->GetHandle(), G_IO_OUT,
163 MiracBroker::send_cb, &network_source_ptr_);
164 } else {
165 g_unix_fd_add(network_->GetHandle(), G_IO_OUT,
166 MiracBroker::connect_cb, &network_source_ptr_);
167 }
168 }
169
get_host_port() const170 unsigned short MiracBroker::get_host_port() const
171 {
172 return network_->GetHostPort();
173 }
174
get_peer_address() const175 std::string MiracBroker::get_peer_address() const
176 {
177 return connection_->GetPeerAddress();
178 }
179
MiracBroker(const std::string & listen_port)180 MiracBroker::MiracBroker (const std::string& listen_port):
181 connect_timer_(NULL)
182 {
183 network_source_ptr_ = this;
184 connection_source_ptr_ = this;
185
186 network(new MiracNetwork());
187
188 network_->Bind(NULL, listen_port.c_str());
189 g_unix_fd_add(network_->GetHandle(), G_IO_IN,
190 MiracBroker::listen_cb, &network_source_ptr_);
191 }
192
MiracBroker(const std::string & peer_address,const std::string & peer_port,uint timeout)193 MiracBroker::MiracBroker(const std::string& peer_address, const std::string& peer_port, uint timeout):
194 peer_address_(peer_address),
195 peer_port_(peer_port),
196 connect_timeout_(timeout)
197 {
198 network_source_ptr_ = this;
199 connection_source_ptr_ = this;
200 connect_timer_ = g_timer_new();
201 try_connect();
202 }
203
~MiracBroker()204 MiracBroker::~MiracBroker ()
205 {
206 network(NULL);
207 connection(NULL);
208
209 if (connect_timer_) {
210 g_timer_destroy(connect_timer_);
211 connect_timer_ = NULL;
212 }
213
214 if (connect_wait_id_ > 0) {
215 g_source_remove(connect_wait_id_);
216 connect_wait_id_ = 0;
217 }
218 while (!timers_.empty())
219 g_source_remove(timers_.front());
220 }
221
SendRTSPData(const std::string & data)222 void MiracBroker::SendRTSPData(const std::string& data) {
223 WDS_VLOG("Sending RTSP message:\n%s", data.c_str());
224
225 if (connection_ && !connection_->Send(data))
226 g_unix_fd_add(connection_->GetHandle(), G_IO_OUT,
227 send_cb, &connection_source_ptr_);
228 }
229
GetLocalIPAddress() const230 std::string MiracBroker::GetLocalIPAddress() const {
231 return "127.0.0.1"; // FIXME : return the actual local IP address.
232 }
233
on_timeout_remove(gpointer user_data)234 void MiracBroker::on_timeout_remove(gpointer user_data) {
235 TimerCallbackData* data = static_cast<TimerCallbackData*>(user_data);
236 auto& timers = data->delegate_->timers_;
237 auto it = std::find(timers.begin(), timers.end(), data->timer_id_);
238 if (it != timers.end())
239 timers.erase(it);
240 delete data;
241 }
242
on_timeout(gpointer user_data)243 static gboolean on_timeout(gpointer user_data) {
244 TimerCallbackData* data = static_cast<TimerCallbackData*>(user_data);
245 data->delegate_->OnTimeout(data->timer_id_);
246 return FALSE;
247 }
248
OnTimeout(uint timer_id)249 void MiracBroker::OnTimeout(uint timer_id) {
250 Peer()->OnTimerEvent(timer_id);
251 }
252
CreateTimer(int seconds)253 uint MiracBroker::CreateTimer(int seconds) {
254 TimerCallbackData* data = new TimerCallbackData(this);
255 uint timer_id = g_timeout_add_seconds_full(
256 G_PRIORITY_DEFAULT,
257 seconds,
258 on_timeout,
259 data,
260 on_timeout_remove);
261 if (timer_id > 0) {
262 data->timer_id_ = timer_id;
263 timers_.push_back(timer_id);
264 } else {
265 delete data;
266 }
267
268 return timer_id;
269 }
270
ReleaseTimer(uint timer_id)271 void MiracBroker::ReleaseTimer(uint timer_id) {
272 if (timer_id > 0) {
273 auto it = std::find(timers_.begin(), timers_.end(), timer_id);
274 if (it != timers_.end() )
275 g_source_remove(*it);
276 }
277 }
278
GetNextCSeq(int * initial_peer_cseq) const279 int MiracBroker::GetNextCSeq(int* initial_peer_cseq) const {
280 static int send_cseq_;
281 ++send_cseq_;
282 if (initial_peer_cseq && send_cseq_ == *initial_peer_cseq)
283 send_cseq_ *= 2;
284
285 return send_cseq_;
286 }
287
288