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 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_interface.h>
37 #include <vlc_input.h>
38 #include <vlc_playlist.h>
40 #include <sys/types.h>
46 #include <vlc_network.h>
48 #define NETSYNC_PORT 9875
50 /*****************************************************************************
52 *****************************************************************************/
53 static int Open (vlc_object_t
*);
54 static void Close(vlc_object_t
*);
56 #define NETSYNC_TEXT N_("Network master clock")
57 #define NETSYNC_LONGTEXT N_("When set, " \
58 "this VLC instance will act as the master clock for synchronization " \
59 "for clients listening")
61 #define MIP_TEXT N_("Master server IP address")
62 #define MIP_LONGTEXT N_("The IP address of " \
63 "the network master clock to use for clock synchronization.")
65 #define NETSYNC_TIMEOUT_TEXT N_("UDP timeout (in ms)")
66 #define NETSYNC_TIMEOUT_LONGTEXT N_("Length of time (in ms) " \
67 "until aborting data reception.")
70 set_shortname(N_("Network Sync"))
71 set_description(N_("Network synchronization"))
72 set_category(CAT_ADVANCED
)
73 set_subcategory(SUBCAT_ADVANCED_MISC
)
75 add_bool("netsync-master", false,
76 NETSYNC_TEXT
, NETSYNC_LONGTEXT
, true)
77 add_string("netsync-master-ip", NULL
, MIP_TEXT
, MIP_LONGTEXT
,
79 add_integer("netsync-timeout", 500,
80 NETSYNC_TIMEOUT_TEXT
, NETSYNC_TIMEOUT_LONGTEXT
, true)
82 set_capability("interface", 0)
83 set_callbacks(Open
, Close
)
86 /*****************************************************************************
88 *****************************************************************************/
96 input_thread_t
*input
;
100 static int PlaylistEvent(vlc_object_t
*, char const *cmd
,
101 vlc_value_t oldval
, vlc_value_t newval
, void *data
);
103 /*****************************************************************************
104 * Activate: initialize and create stuff
105 *****************************************************************************/
106 static int Open(vlc_object_t
*object
)
108 intf_thread_t
*intf
= (intf_thread_t
*)object
;
112 if (!var_InheritBool(intf
, "netsync-master")) {
113 char *psz_master
= var_InheritString(intf
, "netsync-master-ip");
114 if (psz_master
== NULL
) {
115 msg_Err(intf
, "master address not specified");
118 fd
= net_ConnectUDP(VLC_OBJECT(intf
), psz_master
, NETSYNC_PORT
, -1);
121 fd
= net_ListenUDP1(VLC_OBJECT(intf
), NULL
, NETSYNC_PORT
);
125 msg_Err(intf
, "Netsync socket failure");
129 intf
->p_sys
= sys
= malloc(sizeof(*sys
));
136 sys
->is_master
= var_InheritBool(intf
, "netsync-master");
137 sys
->timeout
= var_InheritInteger(intf
, "netsync-timeout");
138 if (sys
->timeout
< 500)
140 sys
->playlist
= pl_Get(intf
);
143 var_AddCallback(sys
->playlist
, "input-current", PlaylistEvent
, intf
);
147 /*****************************************************************************
148 * Close: destroy interface
149 *****************************************************************************/
150 void Close(vlc_object_t
*object
)
152 intf_thread_t
*intf
= (intf_thread_t
*)object
;
153 intf_sys_t
*sys
= intf
->p_sys
;
155 var_DelCallback(sys
->playlist
, "input-current", PlaylistEvent
, intf
);
157 if (sys
->input
!= NULL
) {
158 vlc_cancel(sys
->thread
);
159 vlc_join(sys
->thread
, NULL
);
166 static mtime_t
GetPcrSystem(input_thread_t
*input
)
168 int canc
= vlc_savecancel();
169 /* TODO use the delay */
171 if (input_GetPcrSystem(input
, &system
, NULL
))
173 vlc_restorecancel(canc
);
178 static void *Master(void *handle
)
180 intf_thread_t
*intf
= handle
;
181 intf_sys_t
*sys
= intf
->p_sys
;
183 struct pollfd ufd
= { .fd
= sys
->fd
, .events
= POLLIN
, };
186 if (poll(&ufd
, 1, -1) < 0)
189 /* We received something */
190 struct sockaddr_storage from
;
191 socklen_t fromlen
= sizeof (from
);
193 if (recvfrom(sys
->fd
, data
, 8, 0,
194 (struct sockaddr
*)&from
, &fromlen
) < 8)
197 mtime_t master_system
= GetPcrSystem(sys
->input
);
198 if (master_system
< 0)
201 data
[0] = hton64(mdate());
202 data
[1] = hton64(master_system
);
204 /* Reply to the sender */
205 sendto(sys
->fd
, data
, 16, 0,
206 (struct sockaddr
*)&from
, fromlen
);
208 /* not sure we need the client information to sync,
209 since we are the master anyway */
210 mtime_t client_system
= ntoh64(data
[0]);
211 msg_Dbg(intf
, "Master clockref: %"PRId64
" -> %"PRId64
", from %s "
212 "(date: %"PRId64
")", client_system
, master_system
,
213 (from
.ss_family
== AF_INET
) ? inet_ntoa(((struct sockaddr_in
*)&from
)->sin_addr
)
214 : "non-IPv4", /*date*/ 0);
220 static void *Slave(void *handle
)
222 intf_thread_t
*intf
= handle
;
223 intf_sys_t
*sys
= intf
->p_sys
;
226 struct pollfd ufd
= { .fd
= sys
->fd
, .events
= POLLIN
, };
229 mtime_t system
= GetPcrSystem(sys
->input
);
233 /* Send clock request to the master */
234 const mtime_t send_date
= mdate();
236 data
[0] = hton64(system
);
237 send(sys
->fd
, data
, 8, 0);
240 if (poll(&ufd
, 1, sys
->timeout
) <= 0)
243 const mtime_t receive_date
= mdate();
244 if (recv(sys
->fd
, data
, 16, 0) < 16)
247 const mtime_t master_date
= ntoh64(data
[0]);
248 const mtime_t master_system
= ntoh64(data
[1]);
249 const mtime_t diff_date
= receive_date
-
250 ((receive_date
- send_date
) / 2 + master_date
);
252 if (master_system
> 0) {
253 int canc
= vlc_savecancel();
255 mtime_t client_system
;
256 if (!input_GetPcrSystem(sys
->input
, &client_system
, NULL
)) {
257 const mtime_t diff_system
= client_system
- master_system
- diff_date
;
258 if (diff_system
!= 0) {
259 input_ModifyPcrSystem(sys
->input
, true, master_system
- diff_date
);
261 msg_Dbg(intf
, "Slave clockref: %"PRId64
" -> %"PRId64
" -> %"PRId64
","
262 " clock diff: %"PRId64
", diff: %"PRId64
"",
263 system
, master_system
, client_system
,
264 diff_system
, diff_date
);
268 vlc_restorecancel(canc
);
271 msleep(INTF_IDLE_SLEEP
);
276 static int PlaylistEvent(vlc_object_t
*object
, char const *cmd
,
277 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
279 VLC_UNUSED(cmd
); VLC_UNUSED(object
);
280 intf_thread_t
*intf
= data
;
281 intf_sys_t
*sys
= intf
->p_sys
;
282 input_thread_t
*input
= newval
.p_address
;
284 if (sys
->input
!= NULL
) {
285 msg_Err(intf
, "InputEvent DEAD");
286 assert(oldval
.p_address
== sys
->input
);
288 vlc_cancel(sys
->thread
);
289 vlc_join(sys
->thread
, NULL
);
295 if (vlc_clone(&sys
->thread
, sys
->is_master
? Master
: Slave
, intf
,
296 VLC_THREAD_PRIORITY_INPUT
))