[MPlayer-cvslog] r32066 - in trunk: AUTHORS DOCS/man/en/mplayer.1 DOCS/xml/en/usage.xml Makefile cfg-mplayer.h help/help_mp-en.h mplayer.c udp_sync.c udp_sync.h

reimar subversion at mplayerhq.hu
Wed Sep 8 07:29:05 CEST 2010


Author: reimar
Date: Wed Sep  8 07:29:05 2010
New Revision: 32066

Log:
Add synchronization of multiple MPlayer instances over UDP.
Patch by Jason Holt [jholt google com]

Added:
   trunk/udp_sync.c
   trunk/udp_sync.h
Modified:
   trunk/AUTHORS
   trunk/Makefile
   trunk/cfg-mplayer.h
   trunk/mplayer.c

Changes in other areas also in this revision:
Modified:
   trunk/DOCS/man/en/mplayer.1
   trunk/DOCS/xml/en/usage.xml
   trunk/help/help_mp-en.h

Modified: trunk/AUTHORS
==============================================================================
--- trunk/AUTHORS	Wed Sep  8 01:46:14 2010	(r32065)
+++ trunk/AUTHORS	Wed Sep  8 07:29:05 2010	(r32066)
@@ -368,6 +368,9 @@ Hoffman, Marc <mmh at pleasantst.com>
 Holm, David (dholm, mswitch) <dholm at telia.com>
     * DXR3 support
 
+Holt, Jason <jholt [at] google.com>
+    * UDP network synchronization
+
 Horst, Bohdan (Nexus) <nexus at irc.pl>
     * FreeBSD support
 

Modified: trunk/Makefile
==============================================================================
--- trunk/Makefile	Wed Sep  8 01:46:14 2010	(r32065)
+++ trunk/Makefile	Wed Sep  8 07:29:05 2010	(r32066)
@@ -614,6 +614,7 @@ SRCS_MPLAYER-$(LIRC)          += input/l
 SRCS_MPLAYER-$(MD5SUM)        += libvo/vo_md5sum.c
 SRCS_MPLAYER-$(MGA)           += libvo/vo_mga.c
 SRCS_MPLAYER-$(NAS)           += libao2/ao_nas.c
+SRCS_MPLAYER-$(NETWORKING)    += udp_sync.c
 SRCS_MPLAYER-$(OPENAL)        += libao2/ao_openal.c
 SRCS_MPLAYER-$(OSS)           += libao2/ao_oss.c
 SRCS_MPLAYER-$(PNM)           += libvo/vo_pnm.c

Modified: trunk/cfg-mplayer.h
==============================================================================
--- trunk/cfg-mplayer.h	Wed Sep  8 01:46:14 2010	(r32065)
+++ trunk/cfg-mplayer.h	Wed Sep  8 07:29:05 2010	(r32066)
@@ -275,6 +275,14 @@ const m_option_t mplayer_opts[]={
 
     {"benchmark", &benchmark, CONF_TYPE_FLAG, 0, 0, 1, NULL},
 
+#ifdef CONFIG_NETWORKING
+    {"udp-slave", &udp_slave, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+    {"udp-master", &udp_master, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+    {"udp-ip", &udp_ip, CONF_TYPE_STRING, 0, 0, 1, NULL},
+    {"udp-port", &udp_port, CONF_TYPE_INT, 0, 1, 65535, NULL},
+    {"udp-seek-threshold", &udp_seek_threshold, CONF_TYPE_FLOAT, CONF_RANGE, 0.1, 100, NULL},
+#endif /* CONFIG_NETWORKING */
+
     // dump some stream out instead of playing the file
     // this really should be in MEncoder instead of MPlayer... -> TODO
     {"dumpfile", &stream_dump_name, CONF_TYPE_STRING, 0, 0, 0, NULL},

Modified: trunk/mplayer.c
==============================================================================
--- trunk/mplayer.c	Wed Sep  8 01:46:14 2010	(r32065)
+++ trunk/mplayer.c	Wed Sep  8 07:29:05 2010	(r32066)
@@ -125,6 +125,12 @@
 #include "subreader.h"
 #include "vobsub.h"
 #include "eosd.h"
+#include "osdep/getch2.h"
+#include "osdep/timer.h"
+
+#ifdef CONFIG_NETWORKING
+#include "udp_sync.h"
+#endif /* CONFIG_NETWORKING */
 
 #ifdef CONFIG_X11
 #include "libvo/x11_common.h"
@@ -721,6 +727,11 @@ void uninit_player(unsigned int mask){
 void exit_player_with_rc(enum exit_reason how, int rc)
 {
 
+#ifdef CONFIG_NETWORKING
+  if (udp_master)
+    send_udp(udp_ip, udp_port, "bye");
+#endif /* CONFIG_NETWORKING */
+
   if (mpctx->user_muted && !mpctx->edl_muted) mixer_mute(&mpctx->mixer);
   uninit_player(INITIALIZED_ALL);
 #if defined(__MINGW32__) || defined(__CYGWIN__)
@@ -2213,6 +2224,17 @@ static int sleep_until_update(float *tim
     int frame_time_remaining = 0;
     current_module="calc_sleep_time";
 
+#ifdef CONFIG_NETWORKING
+    if (udp_slave) {
+        int udp_master_exited = udp_slave_sync(mpctx);
+        if (udp_master_exited) {
+            mp_msg(MSGT_CPLAYER, MSGL_INFO, MSGTR_MasterQuit);
+            exit_player(EXIT_QUIT);
+        }
+        return 0;
+    }
+#endif /* CONFIG_NETWORKING */
+
     *time_frame -= GetRelativeTime(); // reset timer
 
     if (mpctx->sh_audio && !mpctx->d_audio->eof) {
@@ -2261,6 +2283,15 @@ static int sleep_until_update(float *tim
     // flag 256 means: libvo driver does its timing (dvb card)
     if (*time_frame > 0.001 && !(vo_flags&256))
 	*time_frame = timing_sleep(*time_frame);
+
+#ifdef CONFIG_NETWORKING
+    if (udp_master) {
+      char current_time[256];
+      sprintf(current_time, "%f", mpctx->sh_video->pts);
+      send_udp(udp_ip, udp_port, current_time);
+    }
+#endif /* CONFIG_NETWORKING */
+
     return frame_time_remaining;
 }
 

Added: trunk/udp_sync.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/udp_sync.c	Wed Sep  8 07:29:05 2010	(r32066)
@@ -0,0 +1,218 @@
+/*
+ * Network playback synchronization
+ * Copyright (C) 2009 Google Inc.
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#if !HAVE_WINSOCK2_H
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <netdb.h>
+#include <signal.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif /* HAVE_WINSOCK2_H */
+
+#include "mplayer.h"
+#include "mp_core.h"
+#include "udp_sync.h"
+#include "mp_msg.h"
+#include "help_mp.h"
+
+
+// config options for UDP sync
+int udp_master = 0;
+int udp_slave = 0;
+int udp_port = 23867;
+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
+
+// gets a datagram from the master with or without blocking.  updates
+// master_position if successful.  if the master has exited, returns 1.
+// otherwise, returns 0.
+int get_udp(int blocking, float *master_position)
+{
+    long sock_flags;
+    struct sockaddr_in cliaddr;
+    char mesg[100];
+    socklen_t len;
+
+    int chars_received;
+    int n;
+
+    static int done_init_yet = 0;
+    static int sockfd;
+    if (!done_init_yet) {
+        struct timeval tv;
+        struct sockaddr_in servaddr;
+
+        done_init_yet = 1;
+
+        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+        memset(&servaddr, sizeof(servaddr), 0);
+        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));
+
+        tv.tv_sec = 30;
+        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+
+    }
+
+#if HAVE_WINSOCK2_H
+    sock_flags = blocking;
+    ioctlsocket(sockfd, FIONBIO, &sock_flags);
+#else
+    sock_flags = fcntl(sockfd, F_GETFL, 0);
+    sock_flags = blocking ? sock_flags & ~O_NONBLOCK : sock_flags | O_NONBLOCK;
+    fcntl(sockfd, F_SETFL, sock_flags);
+#endif /* HAVE_WINSOCK2_H */
+
+    len = sizeof(cliaddr);
+
+    chars_received = recvfrom(sockfd, mesg, sizeof(mesg)-1, 0, (struct sockaddr *)&cliaddr, &len);
+
+    if (chars_received == -1) {
+      return 0;
+    }
+
+#if HAVE_WINSOCK2_H
+    sock_flags = 0;
+    ioctlsocket(sockfd, FIONBIO, &sock_flags);
+#else
+    fcntl(sockfd, F_SETFL, sock_flags | O_NONBLOCK);
+#endif
+
+    // flush out any further messages so we don't get behind
+    while (-1 != (n = recvfrom(sockfd, mesg, sizeof(mesg)-1, 0, (struct sockaddr *)&cliaddr, &len))) {
+        chars_received = n;
+        mesg[chars_received] = 0;
+        if (strcmp(mesg, "bye") == 0) {
+          return 1;
+        }
+    }
+
+    if (chars_received > -1) {
+        mesg[chars_received] = 0;
+
+        if (strcmp(mesg, "bye") == 0) {
+            return 1;
+        } else {
+            sscanf(mesg, "%f", master_position);
+            return 0;
+        }
+    } else {
+        // UDP wait error, probably a timeout.  Safe to ignore.
+    }
+
+    return 0;
+}
+
+void send_udp(const char *send_to_ip, int port, char *mesg)
+{
+    static int done_init_yet = 0;
+    static int sockfd;
+    static struct sockaddr_in socketinfo;
+
+    int one = 1;
+
+    if (!done_init_yet) {
+        int ip_valid = 0;
+
+        done_init_yet = 1;
+
+        sockfd=socket(AF_INET, SOCK_DGRAM, 0);
+
+        // Enable broadcast
+        setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
+
+#if HAVE_WINSOCK2_H
+        ip_valid = (inet_addr(send_to_ip) != INADDR_NONE);
+#else
+        ip_valid = inet_aton(send_to_ip, &socketinfo.sin_addr);
+#endif
+
+        if (!ip_valid) {
+            mp_msg(MSGT_CPLAYER, MSGL_FATAL, MSGTR_InvalidIP);
+            exit_player(EXIT_ERROR);
+        }
+
+        socketinfo.sin_family = AF_INET;
+        socketinfo.sin_port = htons(port);
+    }
+
+    sendto(sockfd, mesg, strlen(mesg), 0, (struct sockaddr *) &socketinfo, sizeof(socketinfo));
+}
+
+// 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.
+int udp_slave_sync(MPContext *mpctx)
+{
+    // grab any waiting datagrams without blocking
+    int master_exited = get_udp(0, &udp_master_position);
+
+    while (!master_exited) {
+        float 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) {
+            abs_seek_pos = SEEK_ABSOLUTE;
+            rel_seek_secs = udp_master_position;
+            break;
+        }
+
+        // normally we expect that the master will have just played the
+        // frame we're ready to play.  break out and play it, and we'll be
+        // right in sync.
+        // or, the master might be up to a few seconds ahead of us, in
+        // which case we also want to play the current frame immediately,
+        // without waiting.
+        // UDP_TIMING_TOLERANCE is a small value that lets us consider
+        // the master equal to us even if it's very slightly ahead.
+        if (udp_master_position + UDP_TIMING_TOLERANCE > my_position) {
+          break;
+        }
+
+        // the remaining case is that we're slightly ahead of the master.
+        // usually, it just means we called get_udp() before the datagram
+        // arrived.  call get_udp again, but this time block until we receive
+        // a datagram.
+        master_exited = get_udp(1, &udp_master_position);
+    }
+
+    return master_exited;
+}

Added: trunk/udp_sync.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/udp_sync.h	Wed Sep  8 07:29:05 2010	(r32066)
@@ -0,0 +1,39 @@
+/*
+ * Network playback synchronization
+ * Copyright (C) 2009 Google Inc.
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_UDP_SYNC_H
+#define MPLAYER_UDP_SYNC_H
+
+#include "mp_core.h"
+
+// config options for UDP sync
+extern int udp_master;
+extern int udp_slave;
+extern int udp_port;
+extern const char *udp_ip; // where the master sends datagrams
+                           // (can be a broadcast address)
+extern float udp_seek_threshold; // how far off before we seek
+
+void send_udp(const char *send_to_ip, int port, char *mesg);
+int get_udp(int blocking, float *master_position);
+int udp_slave_sync(MPContext *mpctx);
+
+#endif /* MPLAYER_UDP_SYNC_H */


More information about the MPlayer-cvslog mailing list