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