diff -r 000000000000 -r 367d791aeb57 gmyth-stream/server/0.1/plugins/media/mencoder.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gmyth-stream/server/0.1/plugins/media/mencoder.py Thu May 17 14:07:27 2007 +0100 @@ -0,0 +1,411 @@ +from __future__ import division + +import os +import sys +import lib +import time +import shlex +import signal +import socket +import ConfigParser +import logging as log + +from select import * +from subprocess import * + +class Media(object): + + def __init__(self, config): + + self.config = config + self.do_cleanup() + + # __init__() + + + def do_cleanup(self): + self.path = "" + self.args = [] + self.language = None + self.subtitle = None + self.mpegopts = None + self.socket = None + self.child_pid = None + self.mplayer = None + self.mencoder_pid = None + self.mplayer_pid = None + self.audio_opts = None + self.video_opts = None + self.gst_pipe = None + self.gst_pid = None + self.transcode_local = None + + # do_cleanup() + + + def setup_opts(self, options): + + for opt in options: + + if opt == "local": + self.mplayer = lib.which("mplayer") + + elif opt.find("language=") >= 0: + try: + lan = opt.split("=")[1] + if len(lan) < 2: + self.language = lan + except Exception, e: + log.error("Bad language option: %s" % opt) + + elif opt.find("subtitle=") >= 0: + try: + sub = opt.split("=")[1] + if len(sub) < 2: + self.language = sub + except Exception, e: + log.error("Bad subtitle option: %s" % opt) + + elif opt.find("format=") >= 0: + try: + self.mpegopts = opt.split("=")[1] + except Exception, e: + log.error("Bad format option: %s" % opt) + + elif opt.find("outfile=") >= 0: + try: + self.transcode_local = opt.split("=")[1] + except Exception, e: + log.error("Bad outfile option: %s" % opt) + + # setup_opts() + + + def run_mplayer(self): + msg = self.filename + + if self.kind == "dvd": + msg = "dvd://" + msg + + self.mplayer_pid = Popen([self.mplayer, self.filename, "1> %s" % os.devnull,\ + "2> %s" % os.devnull], stdout=PIPE, close_fds=True) + + # run_mplayer() + + + def setup_mencoder(self): + self.path = self.config.get("Mencoder", "path") + mp = Popen([self.path], stdout=PIPE, close_fds=True) + + version = mp.stdout.read().split("MEncoder ")[1].split(" (C)")[0].split("-")[-1] + + if version > "4.1.1": self.mencoder_old = False + else: self.mencoder_old = True + + os.kill(mp.pid, signal.SIGKILL) + log.info("Mencoder version: %s" % version) + + if self.mencoder_old: + try: + self.fifo = self.config.get("Mencoder", "fifo_path") + os.mkfifo(self.fifo) + except Exception, e: + log.info("Fifo: %s" % e) + else: + self.fifo = "-" + + # setup_mencoder() + + + def setup_audio(self): + + if self.acodec == "mp3lame": + return "-oac mp3lame -lameopts cbr:br=%s vol=5" % self.abitrate + else: + return "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (\ + self.acodec, self.abitrate) + + # setup_audio() + + + def setup_video(self): + + video = "" + + video += " -of %s" % self.mux + video += " -ofps %s" % self.fps + + if self.vcodec == "nuv" or self.vcodec == "xvid"\ + or self.vcodec == "qtvideo" or self.vcodec == "copy": + video += " -ovc %s" % self.vcodec + else: + video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % ( + self.vcodec, self.vbitrate) + + if self.mux == "mpeg" and self.mpegopts is not None: + video += " -mpegopts format=%s" % self.mpegopts + + video += " -vf scale=%s:%s" % (self.width, self.height) + + return video + + # setup_video() + + + def arg_append(self, args, options): + l = shlex.split(options) + for i in l: + args.append(i) + + # arg_append() + + + def setup_args(self, args): + + args.append(self.path) + + #args.append(self.filename) + args.append("-") + + if self.language != None: + self.arg_append(args, "-alang %s" % self.language) + + if self.subtitle != None: + self.arg_append(args, "-slang %s" % self.subtitle) + self.arg_append(args, "-subfps %s" % self.fps) + + self.arg_append(args, "-idx") + self.arg_append(args, self.audio_opts) + self.arg_append(args, self.video_opts) + + self.arg_append(args, "-really-quiet") + self.arg_append(args, "-o %s" % self.fifo) + self.arg_append(args, "2> %s" % os.devnull) + + # setup_args() + + + def setup_filename(self, filename): + try: + self.kind, self.filename = filename.split("://") + except: + return (False, "Wrong filename protocol") + + if self.kind == "file": + if not os.path.exists(self.filename): + msg = "File requested does not exist. SETUP failed." + log.error(msg) + return (False, msg) + + elif self.kind == "dvd": + self.filename = "dvd://" + filename + + elif self.kind == "myth": + self.filename = filename + self.gst_pipe = os.pipe() + print self.gst_pipe[0] + print self.gst_pipe[1] + + return (True, "") + + # setup_filename() + + + def setup_socket(self): + if self.socket != None: + self.socket = None + + self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + try: + self.socket.bind( ('', self.port) ) + self.socket.listen(1) + except Exception, e: + log.error("Could not create socket: %s" % e) + return (False, e) + + return (True, "") + + # setup_socket() + + + ''' + MENCODER SETUP DESCRIPTION + =========================== + + -> mux, vcodecs and acodecs + |-> mencoder (-of | -ovc | -oac) help + + -> if used mpeg as mux: + |-> to setup format: format=%s as an option at the end + + ''' + + + # good one: /tmp/dvb.mpg avi mpeg4 400 25 mp3lame 192 320 240 + # file:///tmp/dvb.mpg mpeg mpeg1video 400 25 mp2 192 320 240 format=mpeg1 + # dvd://4 mpeg mpeg1video 400 25 mp3lame 192 400 240 language=en local + # file:///tmp/mpg/bad_day.mpg avi mpeg4 400 25 mp3 192 320 240 + + def setup(self, filename, mux, vcodec, vbitrate,\ + fps, acodec, abitrate, width, height, port, options): + + if self.args != []: + self.do_cleanup() + + self.mux = mux + self.vcodec = vcodec + self.vbitrate = vbitrate + self.fps = fps + self.acodec = acodec + self.abitrate = abitrate + self.width = width + self.height = height + self.port = int(port) + + self.setup_mencoder() + + ret_val = self.setup_filename(filename) + + if not ret_val[0]: + return ret_val + + self.setup_opts(options) + self.audio_opts = self.setup_audio() + self.video_opts = self.setup_video() + self.setup_args(self.args) + + ret_val = self.setup_socket() + return ret_val + + # setup() + + def play_loop(self, conn): + data = self.pout.read(4096) + + conn.settimeout(5) + retry = 0 + + if not self.transcode_local: + while data != "" and retry < 5: + try: + conn.send(data) + r, w, x = select([conn], [], [], 0) + if conn in r: + back = conn.recv(1024) + if back == "OK" and self.mplayer and not self.mplayer_pid: + self.run_mplayer() + + except socket.error, e: + log.error("Socket error: %s" % e) + retry += 1 + + data = self.pout.read(4096) + + else: + local = open(self.transcode_local, "w") + total = os.path.getsize(self.filename) + partial = 4096 + + while data != "": + try: + local.write(data) + except Exception, e: + log.error("Write error: %s" % e) + + data = self.pout.read(4096) + partial += len(data) + conn.send("%.2f\n" % (partial * 100 / total) ) + + local.close() + conn.send("DONE\n") + + return retry + + # play_loop() + + + def play(self): + + if self.gst_pipe: + try: + gst = [ lib.which("gst-launch-0.10"), "--gst-debug-level=0" ] + self.arg_append(gst, "mythtvsrc location=%s" % self.filename) + self.arg_append(gst, "! fdsink fd=2") + self.gst_pid = Popen(gst, close_fds=True) + log.info("Running Gstreamer: %s" % gst); + except Exception, e: + msg = "Could not init Gstreamer: %s" % e + log.error(msg) + return (False, msg) + + + log.info("Starting Mencoder: %s" % self.args ) + try: + if not self.gst_pipe: + self.stdin = open(self.filename) + else: + self.stdin = self.gst_pid.stdout + + self.mencoder_pid = Popen(self.args, stdin=self.stdin, stdout=PIPE, close_fds=True) + except Exception, e: + msg = "Could not init Mencoder: %s" % e + log.error(msg) + return (False, msg) + + if self.mencoder_old: self.pout = open(self.fifo) + else: self.pout = self.mencoder_pid.stdout + + self.child_pid = os.fork() + + if self.child_pid == 0: + conn, addr = self.socket.accept() + + log.info("Sending Data to client: %s" % addr[0]) + retry = self.play_loop(conn) + + if retry < 5: + log.info("Finished sending Data to client: %s" % addr[0]) + else: + log.error("Client timed out, retried more than %s times" % retry) + + os.kill(self.mencoder_pid.pid, signal.SIGKILL) + sys.exit(0) + + return (True, "") + + # play() + + + def stop(self): + try: + + if self.mencoder_pid: + os.kill(self.mencoder_pid.pid, signal.SIGTERM) + self.mencoder_pid = None + + if self.mplayer_pid: + os.kill(self.mplayer_pid.pid, signal.SIGTERM) + self.mplayer_pid = None + + if self.socket: + self.socket.close() + self.socket = None + + if self.child_pid: + os.kill(self.child_pid, signal.SIGTERM) + self.child_pid = None + + if self.gst_pid: + os.kill(self.gst_pid.pid, signal.SIGTERM) + self.gst_pid = None + + self.do_cleanup() + + os.wait() + + except Exception, e: + log.error("Stop error: %s" % e) + + # stop()