#!/usr/bin/env python2 # mplayer syncronize v.0.02 alpha -- # # MPlayer has native support for udp syncronization used to syncronize # video walls etc in local networks. (-udp-ip/-udp-master/-udp-slave) # It sends a datagram for each frame to syncronize but does not solve # the latency issues that occur over the internet. This script is # planned to do exactly that. # # TODO: the server should coordinate a real syncronization. probably the best # way to do this would be to use the local system time of each client. # (assuming it is syncronized by ntp) # BUGS: sometimes the sockets are not properly closed. should rewrite most of # the the threading/socket uninitialization code. # # This program 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 3 of the License, or # (at your option) any later version. # # This program 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 this program. If not, see . import re, sys, time, subprocess, threading, socket if len(sys.argv) < 4: print('Usage: %s [-server] ' % sys.argv[0]) print('\nhost, port -- used for server and client mode (tcp)') print('mediafile -- local mediafile (all clients should use the same)') print('options -- gets passed to the mplayer process example: "-fs -display :0.0"') sys.exit() # settings host = port = mediafile = None server_mode = False options = '' socket_timeout = 3 # parse arguments n = 1 if sys.argv[n] == '-server': server_mode = True; n+=1 host = sys.argv[n] port = int(sys.argv[n+1]) mediafile = sys.argv[n+2] if len(sys.argv)-n == 4: options = sys.argv[n+3] # Client thread (used for client and server) class Client(threading.Thread): def __init__(self, socket, **kwargs): super(Client, self).__init__() self.socket = socket self.socket.settimeout(socket_timeout) self.server = None self.mplayer = None if 'server' in kwargs: self.server = kwargs['server'] if 'mplayer' in kwargs: self.mplayer = kwargs['mplayer'] def run(self): buf = '' self.running = True while self.running: # read line from socket: if buf == '': raw = None try: raw = self.socket.recv(1024) except: pass if not raw: continue buf += raw nl = buf.index('\n') line = buf[0:nl+1].strip() buf = buf[nl+1:] if line == '': continue # process received line: print('[Client: %s] "%s"' % (self.socket.getpeername()[0], line)) # client quit command if line == 'cquit': break # parse lines and execute mplayer commands # or just forward them: if self.server: for client in self.server.clients: client.send(line) # forwarding elif self.mplayer: arg = '' si = len(line) if ' ' in line: si = line.index(' ') arg = line[si+1:] command = line[0:si] if command == 'pause' or command == 'p': self.mplayer.pause() elif command == 'osd': self.mplayer.osd(arg) elif command == 'seek': self.mplayer.seek(arg) elif command == 'raw': self.mplayer.send(arg) self.socket.close() self.running = False print('[Client] disconnected') if self.server: self.server.clients.remove(self) def send(self, cmd): self.socket.send(cmd+'\n') def quit(self): self.send('cquit') self.running = False class Server(threading.Thread): def __init__(self): super(Server, self).__init__() self.running = False def run(self): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.clients = [] self.socket.bind((host, port)) self.socket.listen(1) self.socket.settimeout(socket_timeout) self.running = True print('[Server] Wait for clients') while self.running: try: client_socket, client_addr = self.socket.accept() print('[Server] Accept client: %s' % str(client_addr)) client = Client(client_socket, server=self) client.daemon = True client.start() self.clients.append(client) except: pass self.socket.close() def wait_for_it(self): while not self.running: pass def quit(self): print('Close server') self.socket.close() for client in self.clients: client.quit() self.running = False class MplayerProcess(threading.Thread): cmd = 'mplayer -slave -quiet -input nodefault-bindings:conf=/dev/null %s "%s"' #cmd = 'mplayer -slave -quiet -input nodefault-bindings -noconfig all %s "%s"' def __init__(self, mediafile, args=''): super(MplayerProcess, self).__init__() self.mediafile = mediafile self.args = args self.proc = None def run(self): self.running = True cmd = MplayerProcess.cmd % (self.args, self.mediafile) print('Start mplayer: %s' % cmd) self.proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True, close_fds=True) self.ret_code = self.proc.wait() def wait_for_it(self): while self.is_alive() and not self.proc: pass if not self.is_alive(): return False while not 'Starting playback...' in self.proc.stdout.readline(): pass return True def send(self, cmd): self.proc.stdin.write('%s\n' % cmd) def pause(self): self.send('pause') def quit(self): print('Send quit command to mplayer...') self.running = False self.send('quit') def osd(self, msg): print('Receive osd message: %s' % msg) self.send('pausing_keep osd_show_text %s' % msg) def seek(self, sec): self.send('pausing seek %d 2' % int(sec)) def fs(self): print('switch to fullscreen mode') self.send('pausing_keep vo_fullscreen') mplayer = MplayerProcess(mediafile, options) mplayer.start() if not mplayer.wait_for_it(): print('meh.') sys.exit() mplayer.pause() print('MPlayer is running. The default keybindings of mplayer are') print('deactivated. Please use this interface to control it:') print('\n p, pause -- toggle pause everywhere (remote)') print(' seek [x] -- seek to x seconds (remote)') print(' raw [x] -- execute x via mplayers slave mode protocol:') print(' http://www.mplayerhq.hu/DOCS/tech/slave.txt') print(' fs -- toggle fullscreen mode (local)') print(' q, quit -- clean exit (local)\n') if server_mode: server = Server() server.daemon = True server.start() server.wait_for_it() # TODO: moo.. else: server = None client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: client_socket.connect((host, port)) except: print('Client Socket Error! (Server connection failed?)') mplayer.quit() sys.exit() client = Client(client_socket, mplayer=mplayer) # the client controls the mplayer client.daemon = True client.start() while mplayer.running: cmd = raw_input('> ') if 'fs' in cmd: mplayer.fs() continue if 'quit' in cmd or cmd == 'q': mplayer.quit() if server: server.quit() client.quit() break # other commands should be sent via client to the server client.send(cmd.strip()) if server: server.quit() client.quit()