X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcontrol%2Fnetsync.c;h=2f9e778f13e878c0c1a60747a04ac2625e9be7ca;hb=1ce4f166a9653d8ee369862ee3111cce91af815d;hp=ecf9abc57a4bd83eda96e88a7a65d75ad3611da8;hpb=1c1ae9cbf20a32c185a870adb5d3a8bf226323c8;p=vlc diff --git a/modules/control/netsync.c b/modules/control/netsync.c index ecf9abc57a..2f9e778f13 100644 --- a/modules/control/netsync.c +++ b/modules/control/netsync.c @@ -1,5 +1,5 @@ /***************************************************************************** - * netsync.c: synchronisation between several network clients. + * netsync.c: synchronization between several network clients. ***************************************************************************** * Copyright (C) 2004-2009 the VideoLAN team * $Id$ @@ -28,6 +28,7 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include #include #include @@ -50,252 +51,258 @@ /***************************************************************************** * Module descriptor *****************************************************************************/ -static int Activate( vlc_object_t * ); -static void Close ( vlc_object_t * ); - -#define NETSYNC_TEXT N_( "Network master clock" ) -#define NETSYNC_LONGTEXT N_( "When set then " \ - "this vlc instance shall dictate its clock for synchronisation" \ - "over clients listening on the masters network ip address" ) - -#define MIP_TEXT N_( "Master server ip address" ) -#define MIP_LONGTEXT N_( "The IP address of " \ - "the network master clock to use for clock synchronisation." ) - -#define NETSYNC_TIMEOUT_TEXT N_( "UDP timeout (in ms)" ) -#define NETSYNC_TIMEOUT_LONGTEXT N_("Amount of time (in ms) " \ - "to wait before aborting network reception of data." ) - -vlc_module_begin () - set_shortname( N_("Network Sync")) - set_description( N_("Network synchronisation") ) - set_category( CAT_ADVANCED ) - set_subcategory( SUBCAT_ADVANCED_MISC ) - - add_bool( "netsync-master", false, NULL, - NETSYNC_TEXT, NETSYNC_LONGTEXT, true ) - add_string( "netsync-master-ip", NULL, NULL, MIP_TEXT, MIP_LONGTEXT, - true ) - add_integer( "netsync-timeout", 500, NULL, - NETSYNC_TIMEOUT_TEXT, NETSYNC_TIMEOUT_LONGTEXT, true ) - - set_capability( "interface", 0 ) - set_callbacks( Activate, Close ) -vlc_module_end () +static int Open (vlc_object_t *); +static void Close(vlc_object_t *); + +#define NETSYNC_TEXT N_("Network master clock") +#define NETSYNC_LONGTEXT N_("When set then " \ + "This VLC instance shall dictate its clock for synchronization " \ + "over clients listening on the masters network ip address") + +#define MIP_TEXT N_("Master server ip address") +#define MIP_LONGTEXT N_("The IP address of " \ + "The network master clock to use for clock synchronization.") + +#define NETSYNC_TIMEOUT_TEXT N_("UDP timeout (in ms)") +#define NETSYNC_TIMEOUT_LONGTEXT N_("Length of time (in ms) " \ + "until aborting data reception.") + +vlc_module_begin() + set_shortname(N_("Network Sync")) + set_description(N_("Network synchronization")) + set_category(CAT_ADVANCED) + set_subcategory(SUBCAT_ADVANCED_MISC) + + add_bool("netsync-master", false, NULL, + NETSYNC_TEXT, NETSYNC_LONGTEXT, true) + add_string("netsync-master-ip", NULL, NULL, MIP_TEXT, MIP_LONGTEXT, + true) + add_integer("netsync-timeout", 500, NULL, + NETSYNC_TIMEOUT_TEXT, NETSYNC_TIMEOUT_LONGTEXT, true) + + set_capability("interface", 0) + set_callbacks(Open, Close) +vlc_module_end() /***************************************************************************** * Local prototypes *****************************************************************************/ -static void Run( intf_thread_t *p_intf ); +struct intf_sys_t { + int fd; + int timeout; + bool is_master; + playlist_t *playlist; + + /* */ + input_thread_t *input; + vlc_thread_t thread; +}; + +static int PlaylistEvent(vlc_object_t *, char const *cmd, + vlc_value_t oldval, vlc_value_t newval, void *data); /***************************************************************************** * Activate: initialize and create stuff *****************************************************************************/ -static int Activate( vlc_object_t *p_this ) +static int Open(vlc_object_t *object) { - intf_thread_t *p_intf = (intf_thread_t*)p_this; + intf_thread_t *intf = (intf_thread_t*)object; + intf_sys_t *sys; int fd; - if( !var_InheritBool( p_intf, "netsync-master" ) ) - { - char *psz_master = var_InheritString( p_intf, "netsync-master-ip" ); - if( psz_master == NULL ) - { - msg_Err( p_intf, "master address not specified" ); + if (!var_InheritBool(intf, "netsync-master")) { + char *psz_master = var_InheritString(intf, "netsync-master-ip"); + if (psz_master == NULL) { + msg_Err(intf, "master address not specified"); return VLC_EGENERIC; } - fd = net_ConnectUDP( VLC_OBJECT(p_intf), psz_master, NETSYNC_PORT, -1 ); - free( psz_master ); + fd = net_ConnectUDP(VLC_OBJECT(intf), psz_master, NETSYNC_PORT, -1); + free(psz_master); + } else { + fd = net_ListenUDP1(VLC_OBJECT(intf), NULL, NETSYNC_PORT); } - else - fd = net_ListenUDP1( VLC_OBJECT(p_intf), NULL, NETSYNC_PORT ); - if( fd == -1 ) - { - msg_Err( p_intf, "Netsync socket failure" ); + if (fd == -1) { + msg_Err(intf, "Netsync socket failure"); return VLC_EGENERIC; } - p_intf->p_sys = (void *)(intptr_t)fd; - p_intf->pf_run = Run; + intf->pf_run = NULL; + intf->p_sys = sys = malloc(sizeof(*sys)); + if (!sys) { + net_Close(fd); + return VLC_ENOMEM; + } + + sys->fd = fd; + sys->is_master = var_InheritBool(intf, "netsync-master"); + sys->timeout = var_InheritInteger(intf, "netsync-timeout"); + if (sys->timeout < 500) + sys->timeout = 500; + sys->playlist = pl_Get(intf); + sys->input = NULL; + + var_AddCallback(sys->playlist, "input-current", PlaylistEvent, intf); return VLC_SUCCESS; } /***************************************************************************** * Close: destroy interface *****************************************************************************/ -void Close( vlc_object_t *p_this ) +void Close(vlc_object_t *object) { - intf_thread_t *p_intf = (intf_thread_t*)p_this; + intf_thread_t *intf = (intf_thread_t*)object; + intf_sys_t *sys = intf->p_sys; - net_Close( (intptr_t)p_intf->p_sys ); + assert(sys->input == NULL); + var_DelCallback(sys->playlist, "input-current", PlaylistEvent, intf); + net_Close(sys->fd); + free(sys); } -/***************************************************************************** - * Run: interface thread - *****************************************************************************/ -static void Run( intf_thread_t *p_intf ) +static mtime_t GetPcrSystem(input_thread_t *input) { -#define MAX_MSG_LENGTH (2 * sizeof(int64_t)) int canc = vlc_savecancel(); - input_thread_t *p_input = NULL; - char p_data[MAX_MSG_LENGTH]; - int i_socket; - - playlist_t *p_playlist = pl_Hold( p_intf ); - int i_timeout = __MIN( 500, var_InheritInteger( p_intf, "netsync-timeout" ) ); - bool b_master = var_InheritBool( p_intf, "netsync-master" ); - i_socket = (intptr_t)p_intf->p_sys; - - /* High priority thread */ - vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_INPUT ); - - while( vlc_object_alive( p_intf ) ) - { - /* Update the input */ - if( p_input == NULL ) - { - p_input = playlist_CurrentInput( p_playlist ); - } - else if( p_input->b_dead || !vlc_object_alive( p_input ) ) - { - vlc_object_release( p_input ); - p_input = NULL; - } + /* TODO use the delay */ + mtime_t system; + if (input_GetPcrSystem(input, &system, NULL)) + system = -1; + vlc_restorecancel(canc); - if( p_input == NULL ) - { - /* Wait a bit */ - msleep( INTF_IDLE_SLEEP ); - continue; - } - - /* - * We now have an input - */ - - /* Initialize file descriptor set and timeout (0.5s) */ - /* FIXME: arbitrary tick */ - struct pollfd ufd = { .fd = i_socket, .events = POLLIN, }; - - if( b_master ) - { - struct sockaddr_storage from; - mtime_t i_master_system; - mtime_t i_client_system; - mtime_t i_date; - int i_struct_size, i_read, i_ret; - - /* Don't block */ - i_ret = poll( &ufd, 1, i_timeout ); - if( i_ret <= 0 ) continue; - - /* We received something */ - i_struct_size = sizeof( from ); - i_read = recvfrom( i_socket, p_data, MAX_MSG_LENGTH, 0, - (struct sockaddr*)&from, - (unsigned int *)&i_struct_size ); + return system; +} - /* not sure we need the client information to sync, - since we are the master anyway */ - i_client_system = ntoh64(*(int64_t *)p_data); +static void *Master(void *handle) +{ + intf_thread_t *intf = handle; + intf_sys_t *sys = intf->p_sys; + for (;;) { + struct pollfd ufd = { .fd = sys->fd, .events = POLLIN, }; + uint64_t data[2]; - i_date = mdate(); + if (poll(&ufd, 1, -1) <= 0) + continue; - if( input_GetPcrSystem( p_input, &i_master_system ) ) - continue; + /* We received something */ + struct sockaddr_storage from; + unsigned struct_size = sizeof(from); + recvfrom(sys->fd, data, sizeof(data), 0, + (struct sockaddr*)&from, &struct_size); - *((int64_t *)p_data) = hton64( i_date ); - *(((int64_t *)p_data)+1) = hton64( i_master_system ); + mtime_t master_system = GetPcrSystem(sys->input); + if (master_system < 0) + continue; - /* Reply to the sender */ - sendto( i_socket, p_data, 2 * sizeof(int64_t), 0, - (struct sockaddr *)&from, i_struct_size ); + data[0] = hton64(mdate()); + data[1] = hton64(master_system); + /* Reply to the sender */ + sendto(sys->fd, data, sizeof(data), 0, + (struct sockaddr *)&from, struct_size); #if 0 - msg_Dbg( p_intf, "Master clockref: %"PRId64" -> %"PRId64", from %s " - "(date: %"PRId64")", i_client_system, i_master_system, - (from.ss_family == AF_INET) ? inet_ntoa(((struct sockaddr_in *)&from)->sin_addr) - : "non-IPv4", i_date ); + /* not sure we need the client information to sync, + since we are the master anyway */ + mtime_t client_system = ntoh64(data[0]); + msg_Dbg(intf, "Master clockref: %"PRId64" -> %"PRId64", from %s " + "(date: %"PRId64")", client_system, master_system, + (from.ss_family == AF_INET) ? inet_ntoa(((struct sockaddr_in *)&from)->sin_addr) + : "non-IPv4", /*date*/ 0); #endif - } - else - { - mtime_t i_master_system; - mtime_t i_client_system; - mtime_t i_system = 0; - mtime_t i_send_date, i_receive_date; - mtime_t i_diff_date, i_master_date; - int i_sent, i_read, i_ret; - - if( input_GetPcrSystem( p_input, &i_system ) ) - { - msleep( INTF_IDLE_SLEEP ); - continue; - } - - /* Send clock request to the master */ - i_send_date = mdate(); - *((int64_t *)p_data) = hton64( i_system ); - - i_sent = send( i_socket, p_data, sizeof(int64_t), 0 ); - if( i_sent <= 0 ) - { - msleep( INTF_IDLE_SLEEP ); - continue; - } - - /* Don't block */ - i_ret = poll( &ufd, 1, i_timeout ); - if( i_ret == 0 ) continue; - if( i_ret < 0 ) - { - msleep( INTF_IDLE_SLEEP ); - continue; - } + } +} - i_receive_date = mdate(); - i_read = recv( i_socket, p_data, MAX_MSG_LENGTH, 0 ); - if( i_read <= 0 ) - { - msleep( INTF_IDLE_SLEEP ); - continue; - } +static void *Slave(void *handle) +{ + intf_thread_t *intf = handle; + intf_sys_t *sys = intf->p_sys; - i_master_date = ntoh64(*(int64_t *)p_data); - i_master_system = ntoh64(*(((int64_t *)p_data)+1)); /* system date */ + for (;;) { + struct pollfd ufd = { .fd = sys->fd, .events = POLLIN, }; + uint64_t data[2]; - i_diff_date = i_receive_date - - ((i_receive_date - i_send_date) / 2 + i_master_date); + mtime_t system = GetPcrSystem(sys->input); + if (system < 0) + goto wait; - if( p_input && i_master_system > 0 ) - { - mtime_t i_diff_system; + /* Send clock request to the master */ + data[0] = hton64(system); - if( input_GetPcrSystem( p_input, &i_client_system ) ) - { - msleep( INTF_IDLE_SLEEP ); - continue; - } + const mtime_t send_date = mdate(); + if (send(sys->fd, data, sizeof(data[0]), 0) <= 0) + goto wait; - i_diff_system = i_client_system - i_master_system - i_diff_date; - if( i_diff_system != 0 ) - { - input_ModifyPcrSystem( p_input, true, i_master_system - i_diff_date ); + /* Don't block */ + int ret = poll(&ufd, 1, sys->timeout); + if (ret == 0) + continue; + if (ret < 0) + goto wait; + + const mtime_t receive_date = mdate(); + if (recv(sys->fd, data, sizeof(data), 0) <= 0) + goto wait; + + const mtime_t master_date = ntoh64(data[0]); + const mtime_t master_system = ntoh64(data[1]); + const mtime_t diff_date = receive_date - + ((receive_date - send_date) / 2 + master_date); + + if (master_system > 0) { + int canc = vlc_savecancel(); + + mtime_t client_system; + if (!input_GetPcrSystem(sys->input, &client_system, NULL)) { + const mtime_t diff_system = client_system - master_system - diff_date; + if (diff_system != 0) { + input_ModifyPcrSystem(sys->input, true, master_system - diff_date); #if 0 - msg_Dbg( p_intf, "Slave clockref: %"PRId64" -> %"PRId64" -> %"PRId64"," + msg_Dbg(intf, "Slave clockref: %"PRId64" -> %"PRId64" -> %"PRId64"," " clock diff: %"PRId64", diff: %"PRId64"", - i_system, i_master_system, i_client_system, - i_diff_system, i_diff_date ); + system, master_system, client_system, + diff_system, diff_date); #endif } } - msleep( INTF_IDLE_SLEEP ); + vlc_restorecancel(canc); } + wait: + msleep(INTF_IDLE_SLEEP); + } +} + +static int InputEvent(vlc_object_t *object, char const *cmd, + vlc_value_t oldval, vlc_value_t newval, void *data) +{ + VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(object); + intf_thread_t *intf = data; + intf_sys_t *sys = intf->p_sys; + + if (newval.i_int == INPUT_EVENT_DEAD && sys->input) { + msg_Err(intf, "InputEvent DEAD"); + vlc_cancel(sys->thread); + vlc_join(sys->thread, NULL); + vlc_object_release(sys->input); + sys->input = NULL; } + return VLC_SUCCESS; +} - if( p_input ) vlc_object_release( p_input ); - pl_Release( p_intf ); - vlc_restorecancel( canc ); +static int PlaylistEvent(vlc_object_t *object, char const *cmd, + vlc_value_t oldval, vlc_value_t newval, void *data) +{ + VLC_UNUSED(cmd); VLC_UNUSED(oldval); VLC_UNUSED(object); + intf_thread_t *intf = data; + intf_sys_t *sys = intf->p_sys; + + input_thread_t *input = newval.p_address; + assert(sys->input == NULL); + sys->input = vlc_object_hold(input); + if (vlc_clone(&sys->thread, sys->is_master ? Master : Slave, intf, + VLC_THREAD_PRIORITY_INPUT)) { + vlc_object_release(input); + return VLC_SUCCESS; + } + var_AddCallback(input, "intf-event", InputEvent, intf); + return VLC_SUCCESS; }