[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