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