netsync.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:10k
源码类别:

多媒体

开发平台:

MultiPlatform

  1. /*****************************************************************************
  2.  * netsync.c: synchronisation between several network clients.
  3.  *****************************************************************************
  4.  * Copyright (C) 2004 VideoLAN
  5.  * $Id: netsync.c 9087 2004-10-30 15:32:56Z gbazin $
  6.  *
  7.  * Authors: Gildas Bazin <gbazin@videolan.org>
  8.  *
  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.
  13.  *
  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.
  18.  *
  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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  22.  *****************************************************************************/
  23. /*****************************************************************************
  24.  * Preamble
  25.  *****************************************************************************/
  26. #include <stdlib.h>
  27. #include <vlc/vlc.h>
  28. #include <vlc/intf.h>
  29. #include <vlc/input.h>
  30. #ifdef HAVE_UNISTD_H
  31. #    include <unistd.h>
  32. #endif
  33. #ifdef HAVE_SYS_TIME_H
  34. #    include <sys/time.h>
  35. #endif
  36. #include <sys/types.h>
  37. #ifdef WIN32
  38. #   include <winsock2.h>
  39. #   include <ws2tcpip.h>
  40. #   ifndef IN_MULTICAST
  41. #       define IN_MULTICAST(a) IN_CLASSD(a)
  42. #   endif
  43. #else
  44. #   include <sys/socket.h>
  45. #   include <netinet/in.h>
  46. #   if HAVE_ARPA_INET_H
  47. #      include <arpa/inet.h>
  48. #   elif defined( SYS_BEOS )
  49. #      include <net/netdb.h>
  50. #   endif
  51. #endif
  52. #ifdef UNDER_CE
  53. #   define close(a) CloseHandle(a);
  54. #elif defined( WIN32 )
  55. #   define close(a) closesocket(a);
  56. #endif
  57. #include "network.h"
  58. #define NETSYNC_PORT_MASTER 9875
  59. #define NETSYNC_PORT_SLAVE  9876
  60. /* Needed for Solaris */
  61. #ifndef INADDR_NONE
  62. #define INADDR_NONE 0xffffffff
  63. #endif
  64. /*****************************************************************************
  65.  * Module descriptor
  66.  *****************************************************************************/
  67. static int  Activate( vlc_object_t * );
  68. static void Close   ( vlc_object_t * );
  69. static mtime_t GetClockRef( intf_thread_t *, mtime_t );
  70. #define NETSYNC_TEXT N_( "Act as master for network synchronisation" )
  71. #define NETSYNC_LONGTEXT N_( "Allows you to specify if this client should " 
  72.   "act as the master client for the network synchronisation." )
  73. #define MIP_TEXT N_( "Master client ip address" )
  74. #define MIP_LONGTEXT N_( "Allows you to specify the ip address of " 
  75.   "the master client used for the network synchronisation." )
  76. vlc_module_begin();
  77.     set_description( _("Network synchronisation") );
  78.     add_bool( "netsync-master", 0, NULL,
  79.               NETSYNC_TEXT, NETSYNC_LONGTEXT, VLC_TRUE );
  80.     add_string( "netsync-master-ip", NULL, NULL, MIP_TEXT, MIP_LONGTEXT,
  81.                 VLC_TRUE );
  82.     set_capability( "interface", 0 );
  83.     set_callbacks( Activate, Close );
  84. vlc_module_end();
  85. struct intf_sys_t
  86. {
  87.     input_thread_t *p_input;
  88. };
  89. /*****************************************************************************
  90.  * Local prototypes
  91.  *****************************************************************************/
  92. static void Run( intf_thread_t *p_intf );
  93. /*****************************************************************************
  94.  * Activate: initialize and create stuff
  95.  *****************************************************************************/
  96. static int Activate( vlc_object_t *p_this )
  97. {
  98.     intf_thread_t *p_intf = (intf_thread_t*)p_this;
  99.     msg_Info( p_intf, "Using the netsync interface module..." );
  100.     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
  101.     if( !p_intf->p_sys )
  102.     {
  103.         msg_Err( p_intf, "no memory" );
  104.         return VLC_ENOMEM;
  105.     }
  106.     p_intf->p_sys->p_input = NULL;
  107.     p_intf->pf_run = Run;
  108.     return VLC_SUCCESS;
  109. }
  110. /*****************************************************************************
  111.  * Close: destroy interface
  112.  *****************************************************************************/
  113. void Close( vlc_object_t *p_this )
  114. {
  115.     intf_thread_t *p_intf = (intf_thread_t*)p_this;
  116.     free( p_intf->p_sys );
  117. }
  118. /*****************************************************************************
  119.  * Run: interface thread
  120.  *****************************************************************************/
  121. static void Run( intf_thread_t *p_intf )
  122. {
  123. #define MAX_MSG_LENGTH (2 * sizeof(int64_t))
  124.     vlc_bool_t b_master = config_GetInt( p_intf, "netsync-master" );
  125.     char *psz_master = NULL;
  126.     char p_data[MAX_MSG_LENGTH];
  127.     int i_socket;
  128.     if( !b_master )
  129.     {
  130.         psz_master = config_GetPsz( p_intf, "netsync-master-ip" );
  131.         if( psz_master == NULL )
  132.         {
  133.             msg_Err( p_intf, "master address not specified" );
  134.             return;
  135.         }
  136.     }
  137.     i_socket = net_OpenUDP( p_intf, NULL,
  138.                    b_master ? NETSYNC_PORT_MASTER : NETSYNC_PORT_SLAVE,
  139.                    b_master ? NULL : psz_master,
  140.                    b_master ? 0 : NETSYNC_PORT_MASTER );
  141.     if( psz_master ) free( psz_master );
  142.     if( i_socket < 0 )
  143.     {
  144.         msg_Err( p_intf, "failed opening UDP socket." );
  145.         return;
  146.     }
  147.     /* High priority thread */
  148.     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_INPUT );
  149.     while( !p_intf->b_die )
  150.     {
  151.         struct timeval timeout;
  152.         fd_set fds_r;
  153.         /* Update the input */
  154.         if( p_intf->p_sys->p_input == NULL )
  155.         {
  156.             p_intf->p_sys->p_input =
  157.                 (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
  158.                                                    FIND_ANYWHERE );
  159.         }
  160.         else if( p_intf->p_sys->p_input->b_dead )
  161.         {
  162.             vlc_object_release( p_intf->p_sys->p_input );
  163.             p_intf->p_sys->p_input = NULL;
  164.         }
  165.         if( p_intf->p_sys->p_input == NULL )
  166.         {
  167.             /* Wait a bit */
  168.             msleep( INTF_IDLE_SLEEP );
  169.             continue;
  170.         }
  171.         /*
  172.          * We now have an input
  173.          */
  174.         /* Initialize file descriptor set and timeout (0.5s) */
  175.         FD_ZERO( &fds_r );
  176.         FD_SET( i_socket, &fds_r );
  177.         timeout.tv_sec = 0;
  178.         timeout.tv_usec = 500000;
  179.         if( b_master )
  180.         {
  181.             struct sockaddr_storage from;
  182.             mtime_t i_date, i_clockref, i_master_clockref;
  183.             int i_struct_size, i_read, i_ret;
  184.             /* Don't block */
  185.             i_ret = select( i_socket + 1, &fds_r, 0, 0, &timeout );
  186.             if( i_ret == 0 ) continue;
  187.             if( i_ret < 0 )
  188.             {
  189.                 /* Wait a bit */
  190.                 msleep( INTF_IDLE_SLEEP );
  191.                 continue;
  192.             }
  193.             /* We received something */
  194.             i_struct_size = sizeof( from );
  195.             i_read = recvfrom( i_socket, p_data, MAX_MSG_LENGTH, 0,
  196.                                (struct sockaddr*)&from, &i_struct_size );
  197.             i_clockref = ntoh64(*(int64_t *)p_data);
  198.             i_date = mdate();
  199.             *(int64_t *)p_data = hton64( i_date );
  200.             i_master_clockref = GetClockRef( p_intf, i_clockref );
  201.             *(((int64_t *)p_data)+1) = hton64( i_master_clockref );
  202.             /* Reply to the sender */
  203.             sendto( i_socket, p_data, 2 * sizeof(int64_t), 0,
  204.                     (struct sockaddr *)&from, i_struct_size );
  205. #if 0
  206.             msg_Dbg( p_intf, "Master clockref: "I64Fd" -> "I64Fd", from %s "
  207.                      "(date: "I64Fd")", i_clockref, i_master_clockref,
  208.                      from.ss_family == AF_INET
  209.                      ? inet_ntoa(((struct sockaddr_in *)&from)->sin_addr)
  210.                      : "non-IPv4", i_date );
  211. #endif
  212.         }
  213.         else
  214.         {
  215.             mtime_t i_send_date, i_receive_date, i_master_date, i_diff_date;
  216.             mtime_t i_master_clockref, i_client_clockref, i_drift;
  217.             mtime_t i_clockref = 0;
  218.             int i_sent, i_read, i_ret;
  219.             /* Send clock request to the master */
  220.             *(int64_t *)p_data = hton64( i_clockref );
  221.             i_send_date = mdate();
  222.             i_sent = send( i_socket, p_data, sizeof(int64_t), 0 );
  223.             if( i_sent <= 0 )
  224.             {
  225.                 /* Wait a bit */
  226.                 msleep( INTF_IDLE_SLEEP );
  227.                 continue;
  228.             }
  229.             /* Don't block */
  230.             i_ret = select(i_socket + 1, &fds_r, 0, 0, &timeout);
  231.             if( i_ret == 0 ) continue;
  232.             if( i_ret < 0 )
  233.             {
  234.                 /* Wait a bit */
  235.                 msleep( INTF_IDLE_SLEEP );
  236.                 continue;
  237.             }
  238.             i_receive_date = mdate();
  239.             i_read = recv( i_socket, p_data, MAX_MSG_LENGTH, 0 );
  240.             if( i_read <= 0 )
  241.             {
  242.                 /* Wait a bit */
  243.                 msleep( INTF_IDLE_SLEEP );
  244.                 continue;
  245.             }
  246.             i_master_date = ntoh64(*(int64_t *)p_data);
  247.             i_master_clockref = ntoh64(*(((int64_t *)p_data)+1));
  248.             i_diff_date = i_receive_date -
  249.                           ((i_receive_date - i_send_date) / 2 + i_master_date);
  250.             i_client_clockref = i_drift = 0;
  251.             if( p_intf->p_sys->p_input && i_master_clockref )
  252.             {
  253.                 i_client_clockref = GetClockRef( p_intf, i_clockref );
  254.                 i_drift = i_client_clockref - i_master_clockref - i_diff_date;
  255.                 /* Update our clock to match the master's one */
  256.                 if( i_client_clockref )
  257.                     p_intf->p_sys->p_input->i_pts_delay -= i_drift;
  258.             }
  259. #if 0
  260.             msg_Dbg( p_intf, "Slave clockref: "I64Fd" -> "I64Fd" -> "I64Fd", "
  261.                      "clock diff: "I64Fd" drift: "I64Fd,
  262.                      i_clockref, i_master_clockref, 
  263.                      i_client_clockref, i_diff_date, i_drift );
  264. #endif
  265.             /* Wait a bit */
  266.             msleep( INTF_IDLE_SLEEP );
  267.         }
  268.     }
  269.     if( p_intf->p_sys->p_input ) vlc_object_release( p_intf->p_sys->p_input );
  270.     net_Close( i_socket );
  271. }
  272. static mtime_t GetClockRef( intf_thread_t *p_intf, mtime_t i_pts )
  273. {
  274.     input_thread_t *p_input = p_intf->p_sys->p_input;
  275.     mtime_t i_ts;
  276.     if( !p_input || !p_input->p_es_out ) return 0;
  277.     if( es_out_Control( p_input->p_es_out, ES_OUT_GET_TS, i_pts, &i_ts ) ==
  278.         VLC_SUCCESS )
  279.     {
  280.         return i_ts;
  281.     }
  282.     return 0;
  283. }