Define variable allow_playlist_parsing globally.
[mplayer.git] / udp_sync.c
index 8e61b99..1b338ba 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
+#define _BSD_SOURCE
+
 #include "config.h"
 
 #if !HAVE_WINSOCK2_H
 #include <errno.h>
+#include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -43,6 +46,7 @@
 #include "mp_msg.h"
 #include "help_mp.h"
 #include "udp_sync.h"
+#include "osdep/timer.h"
 
 
 // config options for UDP sync
@@ -53,17 +57,26 @@ const char *udp_ip = "127.0.0.1"; // where the master sends datagrams
                                   // (can be a broadcast address)
 float udp_seek_threshold = 1.0;   // how far off before we seek
 
-// remember where the master is in the file
-static float udp_master_position = -1.0;
-
 // how far off is still considered equal
 #define UDP_TIMING_TOLERANCE 0.02
 
+static void startup(void)
+{
+#if HAVE_WINSOCK2_H
+    static int wsa_started;
+    if (!wsa_started) {
+        WSADATA wd;
+        WSAStartup(0x0202, &wd);
+        wsa_started = 1;
+    }
+#endif
+}
+
 static void set_blocking(int fd, int blocking)
 {
     long sock_flags;
 #if HAVE_WINSOCK2_H
-    sock_flags = blocking;
+    sock_flags = !blocking;
     ioctlsocket(fd, FIONBIO, &sock_flags);
 #else
     sock_flags = fcntl(fd, F_GETFL, 0);
@@ -74,30 +87,37 @@ static void set_blocking(int fd, int blocking)
 
 // gets a datagram from the master with or without blocking.  updates
 // master_position if successful.  if the master has exited, returns 1.
+// returns -1 on error or if no message received.
 // otherwise, returns 0.
-int get_udp(int blocking, float *master_position)
+static int get_udp(int blocking, double *master_position)
 {
-    struct sockaddr_in cliaddr;
     char mesg[100];
-    socklen_t len;
 
     int chars_received = -1;
     int n;
 
-    static int done_init_yet = 0;
-    static int sockfd;
-    if (!done_init_yet) {
+    static int sockfd = -1;
+    if (sockfd == -1) {
+#if HAVE_WINSOCK2_H
+        DWORD tv = 30000;
+#else
         struct timeval tv = { .tv_sec = 30 };
+#endif
         struct sockaddr_in servaddr = { 0 };
 
-        done_init_yet = 1;
-
+        startup();
         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+        if (sockfd == -1)
+            return -1;
 
         servaddr.sin_family      = AF_INET;
         servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
         servaddr.sin_port        = htons(udp_port);
-        bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
+        if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
+            closesocket(sockfd);
+            sockfd = -1;
+            return -1;
+        }
 
         setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
 
@@ -105,10 +125,9 @@ int get_udp(int blocking, float *master_position)
 
     set_blocking(sockfd, blocking);
 
-    len = sizeof(cliaddr);
-
     while (-1 != (n = recvfrom(sockfd, mesg, sizeof(mesg)-1, 0,
-                               (struct sockaddr *)&cliaddr, &len))) {
+                               NULL, NULL))) {
+        char *end;
         // flush out any further messages so we don't get behind
         if (chars_received == -1)
             set_blocking(sockfd, 0);
@@ -117,39 +136,38 @@ int get_udp(int blocking, float *master_position)
         mesg[chars_received] = 0;
         if (strcmp(mesg, "bye") == 0)
             return 1;
+        *master_position = strtod(mesg, &end);
+        if (*end) {
+            mp_msg(MSGT_CPLAYER, MSGL_WARN, "Could not parse udp string!\n");
+            return -1;
+        }
     }
-
-    // UDP wait error, probably a timeout.  Safe to ignore.
     if (chars_received == -1)
-        return 0;
-
-    // make sure we at least do not crash in case a
-    // failed recvfrom has corrupted the buffer
-    mesg[chars_received] = 0;
+        return -1;
 
-    sscanf(mesg, "%f", master_position);
     return 0;
 }
 
 void send_udp(const char *send_to_ip, int port, char *mesg)
 {
-    static int done_init_yet = 0;
-    static int sockfd;
+    static int sockfd = -1;
     static struct sockaddr_in socketinfo;
 
-    if (!done_init_yet) {
+    if (sockfd == -1) {
         static const int one = 1;
         int ip_valid = 0;
 
-        done_init_yet = 1;
-
+        startup();
         sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+        if (sockfd == -1)
+            exit_player(EXIT_ERROR);
 
         // Enable broadcast
         setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
 
 #if HAVE_WINSOCK2_H
-        ip_valid = (inet_addr(send_to_ip) != INADDR_NONE);
+        socketinfo.sin_addr.s_addr = inet_addr(send_to_ip);
+        ip_valid = socketinfo.sin_addr.s_addr != INADDR_NONE;
 #else
         ip_valid = inet_aton(send_to_ip, &socketinfo.sin_addr);
 #endif
@@ -168,14 +186,30 @@ void send_udp(const char *send_to_ip, int port, char *mesg)
 }
 
 // this function makes sure we stay as close as possible to the master's
-// position.  returns 1 if the master tells us to exit, 0 otherwise.
+// position.  returns 1 if the master tells us to exit,
+// -1 on error and normal timing should be used again, 0 otherwise.
 int udp_slave_sync(MPContext *mpctx)
 {
+    // remember where the master is in the file
+    static double udp_master_position;
+    // whether we timed out before waiting for a master message
+    static int timed_out = -1;
+    // last time we received a valid master message
+    static unsigned last_success;
+    int master_exited;
+
+    if (timed_out < 0) {
+        // initialize
+        udp_master_position = mpctx->sh_video->pts - udp_seek_threshold / 2;
+        timed_out = 0;
+        last_success = GetTimerMS();
+    }
+
     // grab any waiting datagrams without blocking
-    int master_exited = get_udp(0, &udp_master_position);
+    master_exited = get_udp(0, &udp_master_position);
 
-    while (!master_exited) {
-        float my_position = mpctx->sh_video->pts;
+    while (!master_exited || (!timed_out && master_exited < 0)) {
+        double my_position = mpctx->sh_video->pts;
 
         // if we're way off, seek to catch up
         if (FFABS(my_position - udp_master_position) > udp_seek_threshold) {
@@ -200,7 +234,17 @@ int udp_slave_sync(MPContext *mpctx)
         // arrived.  call get_udp again, but this time block until we receive
         // a datagram.
         master_exited = get_udp(1, &udp_master_position);
+        if (master_exited < 0)
+            timed_out = 1;
+    }
+
+    if (master_exited >= 0) {
+        last_success = GetTimerMS();
+        timed_out = 0;
+    } else {
+        master_exited = 0;
+        timed_out |= GetTimerMS() - last_success > 30000;
     }
 
-    return master_exited;
+    return timed_out ? -1 : master_exited;
 }