1 /*****************************************************************************
2 * netsync.c: synchronization between several network clients.
3 *****************************************************************************
4 * Copyright (C) 2004-2009 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
8 * Jean-Paul Saman <jpsaman@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_input.h>
37 #include <vlc_playlist.h>
42 #include <sys/types.h>
47 #include <vlc_network.h>
49 #define NETSYNC_PORT 9875
51 /*****************************************************************************
53 *****************************************************************************/
54 static int Open (vlc_object_t
*);
55 static void Close(vlc_object_t
*);
57 #define NETSYNC_TEXT N_("Network master clock")
58 #define NETSYNC_LONGTEXT N_("When set then " \
59 "This VLC instance shall dictate its clock for synchronization " \
60 "over clients listening on the masters network ip address")
62 #define MIP_TEXT N_("Master server ip address")
63 #define MIP_LONGTEXT N_("The IP address of " \
64 "The network master clock to use for clock synchronization.")
66 #define NETSYNC_TIMEOUT_TEXT N_("UDP timeout (in ms)")
67 #define NETSYNC_TIMEOUT_LONGTEXT N_("Length of time (in ms) " \
68 "until aborting data reception.")
71 set_shortname(N_("Network Sync"))
72 set_description(N_("Network synchronization"))
73 set_category(CAT_ADVANCED
)
74 set_subcategory(SUBCAT_ADVANCED_MISC
)
76 add_bool("netsync-master", false, NULL
,
77 NETSYNC_TEXT
, NETSYNC_LONGTEXT
, true)
78 add_string("netsync-master-ip", NULL
, MIP_TEXT
, MIP_LONGTEXT
,
80 add_integer("netsync-timeout", 500, NULL
,
81 NETSYNC_TIMEOUT_TEXT
, NETSYNC_TIMEOUT_LONGTEXT
, true)
83 set_capability("interface", 0)
84 set_callbacks(Open
, Close
)
87 /*****************************************************************************
89 *****************************************************************************/
97 input_thread_t
*input
;
101 static int PlaylistEvent(vlc_object_t
*, char const *cmd
,
102 vlc_value_t oldval
, vlc_value_t newval
, void *data
);
104 /*****************************************************************************
105 * Activate: initialize and create stuff
106 *****************************************************************************/
107 static int Open(vlc_object_t
*object
)
109 intf_thread_t
*intf
= (intf_thread_t
*)object
;
113 if (!var_InheritBool(intf
, "netsync-master")) {
114 char *psz_master
= var_InheritString(intf
, "netsync-master-ip");
115 if (psz_master
== NULL
) {
116 msg_Err(intf
, "master address not specified");
119 fd
= net_ConnectUDP(VLC_OBJECT(intf
), psz_master
, NETSYNC_PORT
, -1);
122 fd
= net_ListenUDP1(VLC_OBJECT(intf
), NULL
, NETSYNC_PORT
);
126 msg_Err(intf
, "Netsync socket failure");
131 intf
->p_sys
= sys
= malloc(sizeof(*sys
));
138 sys
->is_master
= var_InheritBool(intf
, "netsync-master");
139 sys
->timeout
= var_InheritInteger(intf
, "netsync-timeout");
140 if (sys
->timeout
< 500)
142 sys
->playlist
= pl_Get(intf
);
145 var_AddCallback(sys
->playlist
, "input-current", PlaylistEvent
, intf
);
149 /*****************************************************************************
150 * Close: destroy interface
151 *****************************************************************************/
152 void Close(vlc_object_t
*object
)
154 intf_thread_t
*intf
= (intf_thread_t
*)object
;
155 intf_sys_t
*sys
= intf
->p_sys
;
157 assert(sys
->input
== NULL
);
158 var_DelCallback(sys
->playlist
, "input-current", PlaylistEvent
, intf
);
163 static mtime_t
GetPcrSystem(input_thread_t
*input
)
165 int canc
= vlc_savecancel();
166 /* TODO use the delay */
168 if (input_GetPcrSystem(input
, &system
, NULL
))
170 vlc_restorecancel(canc
);
175 static void *Master(void *handle
)
177 intf_thread_t
*intf
= handle
;
178 intf_sys_t
*sys
= intf
->p_sys
;
180 struct pollfd ufd
= { .fd
= sys
->fd
, .events
= POLLIN
, };
183 if (poll(&ufd
, 1, -1) <= 0)
186 /* We received something */
187 struct sockaddr_storage from
;
188 unsigned struct_size
= sizeof(from
);
189 recvfrom(sys
->fd
, data
, sizeof(data
), 0,
190 (struct sockaddr
*)&from
, &struct_size
);
192 mtime_t master_system
= GetPcrSystem(sys
->input
);
193 if (master_system
< 0)
196 data
[0] = hton64(mdate());
197 data
[1] = hton64(master_system
);
199 /* Reply to the sender */
200 sendto(sys
->fd
, data
, sizeof(data
), 0,
201 (struct sockaddr
*)&from
, struct_size
);
203 /* not sure we need the client information to sync,
204 since we are the master anyway */
205 mtime_t client_system
= ntoh64(data
[0]);
206 msg_Dbg(intf
, "Master clockref: %"PRId64
" -> %"PRId64
", from %s "
207 "(date: %"PRId64
")", client_system
, master_system
,
208 (from
.ss_family
== AF_INET
) ? inet_ntoa(((struct sockaddr_in
*)&from
)->sin_addr
)
209 : "non-IPv4", /*date*/ 0);
214 static void *Slave(void *handle
)
216 intf_thread_t
*intf
= handle
;
217 intf_sys_t
*sys
= intf
->p_sys
;
220 struct pollfd ufd
= { .fd
= sys
->fd
, .events
= POLLIN
, };
223 mtime_t system
= GetPcrSystem(sys
->input
);
227 /* Send clock request to the master */
228 data
[0] = hton64(system
);
230 const mtime_t send_date
= mdate();
231 if (send(sys
->fd
, data
, sizeof(data
[0]), 0) <= 0)
235 int ret
= poll(&ufd
, 1, sys
->timeout
);
241 const mtime_t receive_date
= mdate();
242 if (recv(sys
->fd
, data
, sizeof(data
), 0) <= 0)
245 const mtime_t master_date
= ntoh64(data
[0]);
246 const mtime_t master_system
= ntoh64(data
[1]);
247 const mtime_t diff_date
= receive_date
-
248 ((receive_date
- send_date
) / 2 + master_date
);
250 if (master_system
> 0) {
251 int canc
= vlc_savecancel();
253 mtime_t client_system
;
254 if (!input_GetPcrSystem(sys
->input
, &client_system
, NULL
)) {
255 const mtime_t diff_system
= client_system
- master_system
- diff_date
;
256 if (diff_system
!= 0) {
257 input_ModifyPcrSystem(sys
->input
, true, master_system
- diff_date
);
259 msg_Dbg(intf
, "Slave clockref: %"PRId64
" -> %"PRId64
" -> %"PRId64
","
260 " clock diff: %"PRId64
", diff: %"PRId64
"",
261 system
, master_system
, client_system
,
262 diff_system
, diff_date
);
266 vlc_restorecancel(canc
);
269 msleep(INTF_IDLE_SLEEP
);
273 static int InputEvent(vlc_object_t
*object
, char const *cmd
,
274 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
276 VLC_UNUSED(cmd
); VLC_UNUSED(oldval
); VLC_UNUSED(object
);
277 intf_thread_t
*intf
= data
;
278 intf_sys_t
*sys
= intf
->p_sys
;
280 if (newval
.i_int
== INPUT_EVENT_DEAD
&& sys
->input
) {
281 msg_Err(intf
, "InputEvent DEAD");
282 vlc_cancel(sys
->thread
);
283 vlc_join(sys
->thread
, NULL
);
284 vlc_object_release(sys
->input
);
290 static int PlaylistEvent(vlc_object_t
*object
, char const *cmd
,
291 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
293 VLC_UNUSED(cmd
); VLC_UNUSED(oldval
); VLC_UNUSED(object
);
294 intf_thread_t
*intf
= data
;
295 intf_sys_t
*sys
= intf
->p_sys
;
297 input_thread_t
*input
= newval
.p_address
;
298 assert(sys
->input
== NULL
);
299 sys
->input
= vlc_object_hold(input
);
300 if (vlc_clone(&sys
->thread
, sys
->is_master
? Master
: Slave
, intf
,
301 VLC_THREAD_PRIORITY_INPUT
)) {
302 vlc_object_release(input
);
305 var_AddCallback(input
, "intf-event", InputEvent
, intf
);