1 /*****************************************************************************
2 * netsync.c: synchronization between several network clients.
3 *****************************************************************************
4 * Copyright (C) 2004-2009 the VideoLAN team
6 * Authors: Gildas Bazin <gbazin@videolan.org>
7 * Jean-Paul Saman <jpsaman@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_input.h>
37 #include <vlc_playlist_legacy.h>
39 #include <sys/types.h>
45 #include <vlc_network.h>
47 #define NETSYNC_PORT 9875
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Open (vlc_object_t
*);
53 static void Close(vlc_object_t
*);
55 #define NETSYNC_TEXT N_("Network master clock")
56 #define NETSYNC_LONGTEXT N_("When set, " \
57 "this VLC instance will act as the master clock for synchronization " \
58 "for clients listening")
60 #define MIP_TEXT N_("Master server IP address")
61 #define MIP_LONGTEXT N_("The IP address of " \
62 "the network master clock to use for clock synchronization.")
64 #define NETSYNC_TIMEOUT_TEXT N_("UDP timeout (in ms)")
65 #define NETSYNC_TIMEOUT_LONGTEXT N_("Length of time (in ms) " \
66 "until aborting data reception.")
69 set_shortname(N_("Network Sync"))
70 set_description(N_("Network synchronization"))
71 set_category(CAT_ADVANCED
)
72 set_subcategory(SUBCAT_ADVANCED_MISC
)
74 add_bool("netsync-master", false,
75 NETSYNC_TEXT
, NETSYNC_LONGTEXT
, true)
76 add_string("netsync-master-ip", NULL
, MIP_TEXT
, MIP_LONGTEXT
,
78 add_integer("netsync-timeout", 500,
79 NETSYNC_TIMEOUT_TEXT
, NETSYNC_TIMEOUT_LONGTEXT
, true)
81 set_capability("interface", 0)
82 set_callbacks(Open
, Close
)
85 /*****************************************************************************
87 *****************************************************************************/
95 input_thread_t
*input
;
99 static int PlaylistEvent(vlc_object_t
*, char const *cmd
,
100 vlc_value_t oldval
, vlc_value_t newval
, void *data
);
102 /*****************************************************************************
103 * Activate: initialize and create stuff
104 *****************************************************************************/
105 static int Open(vlc_object_t
*object
)
107 intf_thread_t
*intf
= (intf_thread_t
*)object
;
111 if (!var_InheritBool(intf
, "netsync-master")) {
112 char *psz_master
= var_InheritString(intf
, "netsync-master-ip");
113 if (psz_master
== NULL
) {
114 msg_Err(intf
, "master address not specified");
117 fd
= net_ConnectUDP(VLC_OBJECT(intf
), psz_master
, NETSYNC_PORT
, -1);
120 fd
= net_ListenUDP1(VLC_OBJECT(intf
), NULL
, NETSYNC_PORT
);
124 msg_Err(intf
, "Netsync socket failure");
128 intf
->p_sys
= sys
= malloc(sizeof(*sys
));
135 sys
->is_master
= var_InheritBool(intf
, "netsync-master");
136 sys
->timeout
= var_InheritInteger(intf
, "netsync-timeout");
137 if (sys
->timeout
< 500)
139 sys
->playlist
= pl_Get(intf
);
142 var_AddCallback(sys
->playlist
, "input-current", PlaylistEvent
, intf
);
146 /*****************************************************************************
147 * Close: destroy interface
148 *****************************************************************************/
149 void Close(vlc_object_t
*object
)
151 intf_thread_t
*intf
= (intf_thread_t
*)object
;
152 intf_sys_t
*sys
= intf
->p_sys
;
154 var_DelCallback(sys
->playlist
, "input-current", PlaylistEvent
, intf
);
156 if (sys
->input
!= NULL
) {
157 vlc_cancel(sys
->thread
);
158 vlc_join(sys
->thread
, NULL
);
165 static vlc_tick_t
GetPcrSystem(input_thread_t
*input
)
167 int canc
= vlc_savecancel();
168 /* TODO use the delay */
170 if (input_GetPcrSystem(input
, &system
, NULL
))
172 vlc_restorecancel(canc
);
177 static void *Master(void *handle
)
179 intf_thread_t
*intf
= handle
;
180 intf_sys_t
*sys
= intf
->p_sys
;
182 struct pollfd ufd
= { .fd
= sys
->fd
, .events
= POLLIN
, };
185 if (poll(&ufd
, 1, -1) < 0)
188 /* We received something */
189 struct sockaddr_storage from
;
190 socklen_t fromlen
= sizeof (from
);
192 if (recvfrom(sys
->fd
, data
, 8, 0,
193 (struct sockaddr
*)&from
, &fromlen
) < 8)
196 vlc_tick_t master_system
= GetPcrSystem(sys
->input
);
197 if (master_system
< 0)
200 data
[0] = hton64(vlc_tick_now());
201 data
[1] = hton64(master_system
);
203 /* Reply to the sender */
204 sendto(sys
->fd
, data
, 16, 0,
205 (struct sockaddr
*)&from
, fromlen
);
207 /* not sure we need the client information to sync,
208 since we are the master anyway */
209 vlc_tick_t client_system
= ntoh64(data
[0]);
210 msg_Dbg(intf
, "Master clockref: %"PRId64
" -> %"PRId64
", from %s "
211 "(date: %"PRId64
")", client_system
, master_system
,
212 (from
.ss_family
== AF_INET
) ? inet_ntoa(((struct sockaddr_in
*)&from
)->sin_addr
)
213 : "non-IPv4", /*date*/ 0);
219 static void *Slave(void *handle
)
221 intf_thread_t
*intf
= handle
;
222 intf_sys_t
*sys
= intf
->p_sys
;
225 struct pollfd ufd
= { .fd
= sys
->fd
, .events
= POLLIN
, };
228 vlc_tick_t system
= GetPcrSystem(sys
->input
);
232 /* Send clock request to the master */
233 const vlc_tick_t send_date
= vlc_tick_now();
235 data
[0] = hton64(system
);
236 send(sys
->fd
, data
, 8, 0);
239 if (poll(&ufd
, 1, sys
->timeout
) <= 0)
242 const vlc_tick_t receive_date
= vlc_tick_now();
243 if (recv(sys
->fd
, data
, 16, 0) < 16)
246 const vlc_tick_t master_date
= ntoh64(data
[0]);
247 const vlc_tick_t master_system
= ntoh64(data
[1]);
248 const vlc_tick_t diff_date
= receive_date
-
249 ((receive_date
- send_date
) / 2 + master_date
);
251 if (master_system
> 0) {
252 int canc
= vlc_savecancel();
254 vlc_tick_t client_system
;
255 if (!input_GetPcrSystem(sys
->input
, &client_system
, NULL
)) {
256 const vlc_tick_t diff_system
= client_system
- master_system
- diff_date
;
257 if (diff_system
!= 0) {
258 input_ModifyPcrSystem(sys
->input
, true, master_system
- diff_date
);
260 msg_Dbg(intf
, "Slave clockref: %"PRId64
" -> %"PRId64
" -> %"PRId64
","
261 " clock diff: %"PRId64
", diff: %"PRId64
"",
262 system
, master_system
, client_system
,
263 diff_system
, diff_date
);
267 vlc_restorecancel(canc
);
270 vlc_tick_sleep(INTF_IDLE_SLEEP
);
275 static int PlaylistEvent(vlc_object_t
*object
, char const *cmd
,
276 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
278 VLC_UNUSED(cmd
); VLC_UNUSED(object
);
279 intf_thread_t
*intf
= data
;
280 intf_sys_t
*sys
= intf
->p_sys
;
281 input_thread_t
*input
= newval
.p_address
;
283 if (sys
->input
!= NULL
) {
284 msg_Err(intf
, "InputEvent DEAD");
285 assert(oldval
.p_address
== sys
->input
);
287 vlc_cancel(sys
->thread
);
288 vlc_join(sys
->thread
, NULL
);
294 if (vlc_clone(&sys
->thread
, sys
->is_master
? Master
: Slave
, intf
,
295 VLC_THREAD_PRIORITY_INPUT
))