]> git.sesse.net Git - vlc/blob - modules/control/netsync.c
No functional changes (K&R + no hungarian notations, netsync).
[vlc] / modules / control / netsync.c
1 /*****************************************************************************
2  * netsync.c: synchronisation between several network clients.
3  *****************************************************************************
4  * Copyright (C) 2004-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *          Jean-Paul Saman <jpsaman@videolan.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_interface.h>
35 #include <vlc_input.h>
36 #include <vlc_playlist.h>
37
38 #ifdef HAVE_UNISTD_H
39 #    include <unistd.h>
40 #endif
41 #include <sys/types.h>
42 #ifdef HAVE_POLL
43 #   include <poll.h>
44 #endif
45
46 #include <vlc_network.h>
47
48 #define NETSYNC_PORT 9875
49
50 /*****************************************************************************
51  * Module descriptor
52  *****************************************************************************/
53 static int  Open (vlc_object_t *);
54 static void Close(vlc_object_t *);
55
56 #define NETSYNC_TEXT N_("Network master clock")
57 #define NETSYNC_LONGTEXT N_("When set then " \
58   "this vlc instance shall dictate its clock for synchronisation" \
59   "over clients listening on the masters network ip address")
60
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 synchronisation.")
64
65 #define NETSYNC_TIMEOUT_TEXT N_("UDP timeout (in ms)")
66 #define NETSYNC_TIMEOUT_LONGTEXT N_("Amount of time (in ms) " \
67   "to wait before aborting network reception of data.")
68
69 vlc_module_begin()
70     set_shortname(N_("Network Sync"))
71     set_description(N_("Network synchronisation"))
72     set_category(CAT_ADVANCED)
73     set_subcategory(SUBCAT_ADVANCED_MISC)
74
75     add_bool("netsync-master", false, NULL,
76               NETSYNC_TEXT, NETSYNC_LONGTEXT, true)
77     add_string("netsync-master-ip", NULL, NULL, MIP_TEXT, MIP_LONGTEXT,
78                 true)
79     add_integer("netsync-timeout", 500, NULL,
80                  NETSYNC_TIMEOUT_TEXT, NETSYNC_TIMEOUT_LONGTEXT, true)
81
82     set_capability("interface", 0)
83     set_callbacks(Open, Close)
84 vlc_module_end()
85
86 /*****************************************************************************
87  * Local prototypes
88  *****************************************************************************/
89 static void Run(intf_thread_t *intf);
90
91 /*****************************************************************************
92  * Activate: initialize and create stuff
93  *****************************************************************************/
94 static int Open(vlc_object_t *object)
95 {
96     intf_thread_t *intf = (intf_thread_t*)object;
97     int fd;
98
99     if (!var_InheritBool(intf, "netsync-master")) {
100         char *psz_master = var_InheritString(intf, "netsync-master-ip");
101         if (psz_master == NULL) {
102             msg_Err(intf, "master address not specified");
103             return VLC_EGENERIC;
104         }
105         fd = net_ConnectUDP(VLC_OBJECT(intf), psz_master, NETSYNC_PORT, -1);
106         free(psz_master);
107     }
108     else
109         fd = net_ListenUDP1(VLC_OBJECT(intf), NULL, NETSYNC_PORT);
110
111     if (fd == -1) {
112         msg_Err(intf, "Netsync socket failure");
113         return VLC_EGENERIC;
114     }
115
116     intf->p_sys = (void *)(intptr_t)fd;
117     intf->pf_run = Run;
118     return VLC_SUCCESS;
119 }
120
121 /*****************************************************************************
122  * Close: destroy interface
123  *****************************************************************************/
124 void Close(vlc_object_t *object)
125 {
126     intf_thread_t *intf = (intf_thread_t*)object;
127
128     net_Close((intptr_t)intf->p_sys);
129 }
130
131 /*****************************************************************************
132  * Run: interface thread
133  *****************************************************************************/
134 static void Run(intf_thread_t *intf)
135 {
136 #define MAX_MSG_LENGTH (2 * sizeof(int64_t))
137     int canc = vlc_savecancel();
138     input_thread_t *input = NULL;
139     char data[MAX_MSG_LENGTH];
140     int fd = (intptr_t)intf->p_sys;
141
142     playlist_t *playlist = pl_Hold(intf);
143     int timeout = var_InheritInteger(intf, "netsync-timeout");
144     if (timeout < 500)
145         timeout = 500;
146     bool is_master = var_InheritBool(intf, "netsync-master");
147
148     /* High priority thread */
149     vlc_thread_set_priority(intf, VLC_THREAD_PRIORITY_INPUT);
150
151     while (vlc_object_alive(intf)) {
152         /* Update the input */
153         if (input == NULL) {
154             input = playlist_CurrentInput(playlist);
155         } else if (input->b_dead || !vlc_object_alive(input)) {
156             vlc_object_release(input);
157             input = NULL;
158         }
159
160         if (input == NULL) {
161             /* Wait a bit */
162             msleep(INTF_IDLE_SLEEP);
163             continue;
164         }
165
166         /*
167          * We now have an input
168          */
169
170         /* Initialize file descriptor set and timeout (0.5s) */
171         /* FIXME: arbitrary tick */
172         struct pollfd ufd = { .fd = fd, .events = POLLIN, };
173
174         if (is_master) {
175             struct sockaddr_storage from;
176             mtime_t master_system;
177             mtime_t client_system;
178             mtime_t date;
179             int struct_size, read_size, ret;
180
181             /* Don't block */
182             ret = poll(&ufd, 1, timeout);
183             if (ret <= 0)
184                 continue;
185
186             /* We received something */
187             struct_size = sizeof(from);
188             read_size = recvfrom(fd, data, MAX_MSG_LENGTH, 0,
189                                  (struct sockaddr*)&from,
190                                  (unsigned int *)&struct_size);
191
192             /* not sure we need the client information to sync,
193                since we are the master anyway */
194             client_system = ntoh64(*(int64_t *)data);
195
196             date = mdate();
197
198             if (input_GetPcrSystem(input, &master_system))
199                 continue;
200
201             *((int64_t *)data) = hton64(date);
202             *(((int64_t *)data)+1) = hton64(master_system);
203
204             /* Reply to the sender */
205             sendto(fd, data, 2 * sizeof(int64_t), 0,
206                     (struct sockaddr *)&from, struct_size);
207
208 #if 0
209             msg_Dbg(intf, "Master clockref: %"PRId64" -> %"PRId64", from %s "
210                      "(date: %"PRId64")", client_system, master_system,
211                      (from.ss_family == AF_INET) ? inet_ntoa(((struct sockaddr_in *)&from)->sin_addr)
212                      : "non-IPv4", date);
213 #endif
214         }
215         else
216         {
217             mtime_t master_system;
218             mtime_t client_system;
219             mtime_t system = 0;
220             mtime_t send_date, receive_date;
221             mtime_t diff_date, master_date;
222             int sent, read_size, ret;
223
224             if (input_GetPcrSystem(input, &system)) {
225                 msleep(INTF_IDLE_SLEEP);
226                 continue;
227             }
228
229             /* Send clock request to the master */
230             send_date = mdate();
231             *((int64_t *)data) = hton64(system);
232
233             sent = send(fd, data, sizeof(int64_t), 0);
234             if (sent <= 0) {
235                 msleep(INTF_IDLE_SLEEP);
236                 continue;
237             }
238
239             /* Don't block */
240             ret = poll(&ufd, 1, timeout);
241             if (ret == 0)
242                 continue;
243             if (ret < 0) {
244                 msleep(INTF_IDLE_SLEEP);
245                 continue;
246             }
247
248             receive_date = mdate();
249             read_size = recv(fd, data, MAX_MSG_LENGTH, 0);
250             if (read_size <= 0) {
251                 msleep(INTF_IDLE_SLEEP);
252                 continue;
253             }
254
255             master_date = ntoh64(*(int64_t *)data);
256             master_system = ntoh64(*(((int64_t *)data)+1)); /* system date */
257
258             diff_date = receive_date -
259                           ((receive_date - send_date) / 2 + master_date);
260
261             if (input && master_system > 0) {
262                 mtime_t diff_system;
263
264                 if (input_GetPcrSystem(input, &client_system)) {
265                     msleep(INTF_IDLE_SLEEP);
266                     continue;
267                 }
268
269                 diff_system = client_system - master_system - diff_date;
270                 if (diff_system != 0) {
271                     input_ModifyPcrSystem(input, true, master_system - diff_date);
272 #if 0
273                     msg_Dbg(intf, "Slave clockref: %"PRId64" -> %"PRId64" -> %"PRId64","
274                              " clock diff: %"PRId64", diff: %"PRId64"",
275                              system, master_system, client_system,
276                              diff_system, diff_date);
277 #endif
278                 }
279             }
280             msleep(INTF_IDLE_SLEEP);
281         }
282     }
283
284     if (input)
285         vlc_object_release(input);
286     pl_Release(intf);
287     vlc_restorecancel(canc);
288 }
289