morphbr@577: import os
morphbr@577: import shlex
morphbr@577: import signal
morphbr@577: import subprocess
morphbr@595: import time
morphbr@577: 
morphbr@565: import lib.utils as utils
morphbr@565: import lib.server as server
morphbr@565: 
morphbr@595: from select import select
morphbr@595: 
morphbr@565: __all__ = ("TranscoderMencoder",)
morphbr@565: 
morphbr@565: class TranscoderMencoder(server.Transcoder):
morphbr@585:     """Transcoder class that implements a transcoder using Mencoder"""
morphbr@565:     mencoder_path = utils.which("mencoder")
morphbr@565:     name = "mencoder"
morphbr@565:     priority = -1
morphbr@577:     args = {}
morphbr@577:     proc = None
morphbr@565: 
morphbr@595:     # only works with avi container
morphbr@595:     status = 0
morphbr@595: 
morphbr@569:     def _setup_params(self):
morphbr@569:         params_first = self.params_first
morphbr@569: 
morphbr@569:         # general_opts
morphbr@569:         self.args["local"]    = params_first("local", False)
morphbr@569:         self.args["language"] = params_first("language", False)
morphbr@569:         self.args["subtitle"] = params_first("subtitle", False)
morphbr@569:         self.args["format"]   = params_first("format", "")
morphbr@569:         self.args["outfile"]  = params_first("outfile", "-")
morphbr@595:         self.args["sendback"] = params_first("sendback", True)
morphbr@595: 
morphbr@595:         # handle sendback variable
morphbr@595:         if self.args["sendback"] == "False":
morphbr@595:             self.args["sendback"] = False
morphbr@569: 
morphbr@569:         # input_opt
morphbr@577:         uri = params_first("uri", "file://-").split("://")
morphbr@577:         self.args["type"]     = uri[0]
morphbr@577:         self.args["input"]    = uri[1]
morphbr@569: 
morphbr@569:         # audio_opts
morphbr@569:         self.args["acodec"]   = params_first("acodec", "mp2")
morphbr@569:         self.args["abitrate"] = params_first("abitrate", 192)
morphbr@569:         self.args["volume"]   = params_first("volume", 5)
morphbr@569: 
morphbr@569:         # video_opts
morphbr@569:         self.args["mux"]      = params_first("mux", "mpeg")
morphbr@569:         self.args["fps"]      = params_first("fps", 25)
morphbr@569:         self.args["vcodec"]   = params_first("vcodec", "mpeg1video")
morphbr@569:         self.args["vbitrate"] = params_first("vbitrate", 400)
morphbr@569:         self.args["width"]    = params_first("width", 320)
morphbr@569:         self.args["height"]   = params_first("height", 240)
morphbr@569:     # _setup_params()
morphbr@569: 
morphbr@569: 
morphbr@569:     def _setup_audio(self):
morphbr@569:         if self.args["acodec"] == "mp3lame":
morphbr@569:             audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % (
morphbr@569:                 self.args["abitrate"], self.args["volume"])
morphbr@569:         else:
morphbr@569:             audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (
morphbr@569:                 self.args["acodec"], self.args["abitrate"])
morphbr@569: 
morphbr@569:         return audio
morphbr@569:     # _setup_audio()
morphbr@569: 
morphbr@569: 
morphbr@569:     def _setup_video(self):
morphbr@577:         video = " -of %s" % self.args["mux"]
morphbr@577:         video += " -ofps %s" % self.args["fps"]
morphbr@569: 
morphbr@569:         vcodec = self.args["vcodec"]
morphbr@569:         if vcodec == "nuv" or vcodec == "xvid"\
morphbr@569:                or vcodec == "qtvideo" or vcodec == "copy":
morphbr@569:             video += " -ovc %s" % vcodec
morphbr@569:         else:
morphbr@569:             video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
morphbr@569:                 vcodec, self.args["vbitrate"])
morphbr@569: 
morphbr@569:         video += " %s" % self.args["format"]
morphbr@569:         video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"])
morphbr@569: 
morphbr@569:         return video
morphbr@569:     # _setup_video()
morphbr@569: 
morphbr@569: 
morphbr@569:     def _arg_append(self, args, options):
morphbr@577:         for arg in shlex.split(options):
morphbr@569:             args.append(arg)
morphbr@569:     # arg_append()
morphbr@569: 
morphbr@569:     def _setup_mencoder_opts(self, args):
morphbr@569:         args.append(self.mencoder_path)
morphbr@595: 
morphbr@595:         if self.args["outfile"] == "-" and self.args["type"]:
morphbr@595:             args.append(self.args["input"])
morphbr@595:         else:
morphbr@595:             args.append("-")
morphbr@569: 
morphbr@569:         if self.args["language"]:
morphbr@569:             self._arg_append(args, "-alang %s" % self.args["language"])
morphbr@569: 
morphbr@569:         if self.args["subtitle"]:
morphbr@569:             self._arg_append(args, "-slang %s" % self.args["subtitle"])
morphbr@569:             self._arg_append(args, "-subfps %s" % self.args["fps"])
morphbr@569: 
morphbr@569:         self._arg_append(args, "-idx")
morphbr@577:         self._arg_append(args, self._setup_audio())
morphbr@577:         self._arg_append(args, self._setup_video())
morphbr@569: 
morphbr@569:         self._arg_append(args, "-really-quiet")
morphbr@569:         self._arg_append(args, "-o %s" % self.args["outfile"])
morphbr@595:         self._arg_append(args, "2>%s" % os.devnull)
morphbr@569:     # _setup_args()
morphbr@569: 
morphbr@569: 
morphbr@569:     def _setup_filename(self):
morphbr@569:         _type = self.args["type"]
morphbr@569: 
morphbr@569:         if _type == "file":
morphbr@569:             if not os.path.exists(self.args["input"]):
morphbr@577:                 raise IOError,\
morphbr@577:                       "File requested does not exist: %s." % self.args["input"]
morphbr@569: 
morphbr@569:         elif _type == "dvd":
morphbr@569:             self.args["input"] = "dvd://".join(self.args["input"])
morphbr@595: 
morphbr@595:         elif _type == "myth":
morphbr@595:             # gmyth-cat -h 192.168.1.124 -p 6543 -c 111
morphbr@595:             # gmyth-cat -h 192.168.1.124 -p 6543 -f file.nuv
morphbr@595:             # myth://IP:PORT:type:file
morphbr@595:             self.args["gmyth-cat"] = self.args["input"].split(":")
morphbr@569:     # _setup_filename()
morphbr@569: 
morphbr@569: 
morphbr@572:     def __init__(self, params):
morphbr@565:         server.Transcoder.__init__(self, params)
morphbr@577:         self.mencoder_opts = []
morphbr@565: 
morphbr@569:         try:
morphbr@569:             self._setup_params()
morphbr@569:             self._setup_filename()
morphbr@569:             self._setup_mencoder_opts(self.mencoder_opts)
morphbr@569:         except Exception, e:
morphbr@569:             self.log.error(e)
morphbr@565:     # __init__()
morphbr@565: 
morphbr@565: 
morphbr@595:     def _check_opened_file(self, stdw, _stdin):
morphbr@595:         loop = True
morphbr@595:         while loop:
morphbr@595:             try:
morphbr@595:                 return open(self.args["outfile"])
morphbr@595:             except:
morphbr@595:                 os.write(stdw, _stdin.read(1024))
morphbr@595:     # _check_opened_file
morphbr@595: 
morphbr@595: 
morphbr@595:     def _start_outfile(self, outfd):
morphbr@595:         finished = False
morphbr@595: 
morphbr@595:         # fix this (not necessary)
morphbr@595:         outfd.write("OK")
morphbr@595: 
morphbr@595:         # Configuring stdin
morphbr@595:         _stdin = open(self.args["input"])
morphbr@595:         size = int(os.path.getsize(self.args["input"]))
morphbr@595:         self.status = 0
morphbr@595:         total_read = 0
morphbr@595: 
morphbr@595:         # Configuring pipes
morphbr@595:         stdr, stdw = os.pipe()
morphbr@569: 
morphbr@565:         try:
morphbr@595:             self.proc = subprocess.Popen(self.mencoder_opts, stdin=stdr, close_fds=True)
morphbr@565:         except Exception, e:
morphbr@577:             self.log.error("Error executing mencoder: %s" % e)
morphbr@565:             return False
morphbr@565: 
morphbr@595:         stdout = self._check_opened_file(stdw, _stdin)
morphbr@595: 
morphbr@595:         try:
morphbr@595:             while self.proc and self.proc.poll() == None:
morphbr@595:                 if not finished:
morphbr@595:                     data_in = _stdin.read(4096)
morphbr@595:                     if data_in != "":
morphbr@595:                         os.write(stdw, data_in)
morphbr@595:                         total_read += 4096
morphbr@595:                         d = stdout.read(4096)
morphbr@595:                         if self.args["sendback"]:
morphbr@595:                             outfd.write(d)
morphbr@595:                         self.status = total_read * 100 / size
morphbr@595:                     else:
morphbr@595:                         finished = True
morphbr@595:                         os.close(stdw)
morphbr@595: 
morphbr@595:                 else:
morphbr@595:                     d = stdout.read(4096)
morphbr@595:                     if self.args["sendback"] and d != "":
morphbr@595:                         outfd.write(d)
morphbr@595: 
morphbr@595:         except Exception, e:
morphbr@595:             self.log.error("Problems handling data: %s" % e)
morphbr@595:             return False
morphbr@595: 
morphbr@595:         self.log.info("%s: Finished sending data to client" % repr(self))
morphbr@595:         if not self.args["sendback"]:
morphbr@595:             outfd.write("DONE")
morphbr@595: 
morphbr@595:         return True
morphbr@595:     # _start_outfile()
morphbr@595: 
morphbr@595:     def _start(self, outfd):
morphbr@595:         try:
morphbr@595:             self.proc = subprocess.Popen(self.mencoder_opts,
morphbr@595:                                          stdout=subprocess.PIPE, close_fds=True)
morphbr@595:         except Exception, e:
morphbr@595:             self.log.error("Error executing mencoder: %s" % e)
morphbr@595:             return False
morphbr@595: 
morphbr@595: 
morphbr@565:         try:
morphbr@565:             while self.proc and self.proc.poll() == None:
morphbr@577:                 d = self.proc.stdout.read(1024)
morphbr@565:                 outfd.write(d)
morphbr@565:         except Exception, e:
morphbr@565:             self.log.error("Problems handling data: %s" % e)
morphbr@565:             return False
morphbr@565: 
morphbr@595:         self.log.info("%s: Finished sending data to client" % repr(self))
morphbr@565:         return True
morphbr@595:     # _start()
morphbr@595: 
morphbr@595: 
morphbr@595:     def start(self, outfd):
morphbr@595:         cmd = " ".join(self.mencoder_opts)
morphbr@595:         self.log.debug("Mencoder: %s" % cmd)
morphbr@595: 
morphbr@595:         if self.args["outfile"] == "-":
morphbr@595:             return self._start(outfd)
morphbr@595:         else:
morphbr@595:             return self._start_outfile(outfd)
morphbr@565:     # start()
morphbr@565: 
morphbr@565: 
morphbr@565:     def stop(self):
morphbr@565:         if self.proc:
morphbr@565:             try:
morphbr@565:                 os.kill(self.proc.pid, signal.SIGTERM)
morphbr@565:             except OSError, e:
morphbr@565:                 pass
morphbr@565: 
morphbr@565:             try:
morphbr@565:                 self.proc.wait()
morphbr@565:             except Exception, e:
morphbr@565:                 pass
morphbr@565: 
morphbr@565:             self.proc = None
morphbr@569:     # stop()
morphbr@565: 
morphbr@565: # TranscoderMencoder