morphbr@718: #!/usr/bin/env python morphbr@718: morphbr@718: __author__ = "Artur Duque de Souza" morphbr@718: __author_email__ = "artur.souza@indt.org.br" morphbr@718: __license__ = "GPL" morphbr@740: __version__ = "0.3" morphbr@718: morphbr@718: import os morphbr@740: import time morphbr@740: import fcntl morphbr@718: import shlex morphbr@740: import socket morphbr@740: import struct morphbr@718: import signal morphbr@718: import subprocess morphbr@718: morphbr@718: import lib.utils as utils morphbr@718: import lib.server as server morphbr@718: import plugins.transcoders.mencoder_lib.mythtv as mythtv morphbr@718: morphbr@718: from select import select morphbr@718: import lib.transcoder as transcoder morphbr@718: morphbr@718: __all__ = ("TranscoderMencoder",) morphbr@718: morphbr@718: class TranscoderMencoder(transcoder.Transcoder): morphbr@718: """Transcoder class that implements a transcoder using Mencoder""" morphbr@718: mencoder_path = utils.which("mencoder") morphbr@718: name = "mencoder" morphbr@718: priority = -1 morphbr@718: args = {} morphbr@718: proc = None morphbr@718: gmyth = None morphbr@718: morphbr@718: # only works with avi container morphbr@718: status = 0 morphbr@718: morphbr@718: def _setup_params(self): morphbr@718: params_first = self.params_first morphbr@718: morphbr@718: # general_opts morphbr@718: self.args["local"] = params_first("local", False) morphbr@718: self.args["language"] = params_first("language", False) morphbr@718: self.args["subtitle"] = params_first("subtitle", False) morphbr@718: self.args["format"] = params_first("format", "mpeg1") morphbr@718: self.args["outfile"] = params_first("outfile", "-") morphbr@718: morphbr@718: # input_opt morphbr@718: self.args["type"] = params_first("type", "file") morphbr@718: self.args["input"] = params_first("uri", "-") morphbr@718: morphbr@718: # audio_opts morphbr@718: self.args["acodec"] = params_first("acodec", "mp2") morphbr@718: self.args["abitrate"] = params_first("abitrate", 192) morphbr@718: self.args["volume"] = params_first("volume", 5) morphbr@718: morphbr@718: # video_opts morphbr@718: self.args["mux"] = params_first("mux", "mpeg") morphbr@718: self.args["fps"] = params_first("fps", 25) morphbr@718: self.args["vcodec"] = params_first("vcodec", "mpeg1video") morphbr@718: self.args["vbitrate"] = params_first("vbitrate", 400) morphbr@718: self.args["width"] = params_first("width", 320) morphbr@718: self.args["height"] = params_first("height", 240) morphbr@718: # _setup_params() morphbr@718: morphbr@718: morphbr@718: def _setup_audio(self): morphbr@718: if self.args["acodec"] == "mp3lame": morphbr@718: audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % ( morphbr@718: self.args["abitrate"], self.args["volume"]) morphbr@718: else: morphbr@718: audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % ( morphbr@718: self.args["acodec"], self.args["abitrate"]) morphbr@718: morphbr@718: return audio morphbr@718: # _setup_audio() morphbr@718: morphbr@718: morphbr@718: def _setup_video(self): morphbr@718: video = " -of %s" % self.args["mux"] morphbr@718: video += " -ofps %s" % self.args["fps"] morphbr@718: morphbr@718: vcodec = self.args["vcodec"] morphbr@718: if vcodec == "nuv" or vcodec == "xvid"\ morphbr@718: or vcodec == "qtvideo" or vcodec == "copy": morphbr@718: video += " -ovc %s" % vcodec morphbr@718: else: morphbr@718: video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % ( morphbr@718: vcodec, self.args["vbitrate"]) morphbr@718: morphbr@718: if self.args["mux"] == "mpeg": morphbr@718: video += " -mpegopts format=%s" % self.args["format"] morphbr@736: morphbr@718: video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"]) morphbr@718: return video morphbr@718: # _setup_video() morphbr@718: morphbr@718: morphbr@718: def _arg_append(self, args, options): morphbr@718: for arg in shlex.split(options): morphbr@718: args.append(arg) morphbr@718: # arg_append() morphbr@718: morphbr@718: def _setup_mencoder_opts(self, args): morphbr@718: args.append(self.mencoder_path) morphbr@718: morphbr@739: if self.args["type"] and self.args["type"] == "tv": morphbr@739: self._arg_append(args, self.args["tv"]) morphbr@739: elif self.args["outfile"] == "-" and self.args["type"]: morphbr@718: args.append(self.args["input"]) morphbr@718: else: morphbr@718: args.append("-") morphbr@718: morphbr@718: if self.args["language"]: morphbr@718: self._arg_append(args, "-alang %s" % self.args["language"]) morphbr@718: morphbr@718: if self.args["subtitle"]: morphbr@718: self._arg_append(args, "-slang %s" % self.args["subtitle"]) morphbr@718: self._arg_append(args, "-subfps %s" % self.args["fps"]) morphbr@718: morphbr@718: self._arg_append(args, "-idx") morphbr@718: self._arg_append(args, "-cache 1024") morphbr@718: self._arg_append(args, self._setup_audio()) morphbr@718: self._arg_append(args, self._setup_video()) morphbr@718: morphbr@718: self._arg_append(args, "-really-quiet") morphbr@723: morphbr@723: if self.args["outfile"] != "-": morphbr@723: self.args["outfile"] = ".transcoded/%s" % ( morphbr@723: os.path.basename(self.args["outfile"])) morphbr@723: morphbr@718: self._arg_append(args, "-o %s" % self.args["outfile"]) morphbr@718: self._arg_append(args, "2>%s" % os.devnull) morphbr@718: # _setup_args() morphbr@718: morphbr@718: def _setup_filename(self): morphbr@718: """This function setups the file to encode parsing the uri. morphbr@718: So, type can be: morphbr@718: * file morphbr@718: * dvd morphbr@718: * myth morphbr@718: morphbr@718: If the last one is detected we have to parse the uri to find args. morphbr@718: Then we store all the args inside a dictionary: self.args['gmyth-cat'] morphbr@718: """ morphbr@718: _type = self.args["type"] morphbr@718: morphbr@718: if _type == "file": morphbr@718: if not os.path.exists(self.args["input"]): morphbr@718: raise IOError,\ morphbr@718: "File requested does not exist: %s." % self.args["input"] morphbr@718: else: morphbr@718: self.args["input"] = "file://%s" % self.args["input"] morphbr@718: morphbr@718: elif _type == "dvd": morphbr@736: self.args["input"] = "dvd://%s" % self.args["input"] morphbr@718: morphbr@718: elif _type == "myth": morphbr@718: self.args["gmyth-cat"] = mythtv._setup_mythfilename(self) morphbr@736: morphbr@736: elif _type == "tv": morphbr@736: driver = self.params_first("driver", "v4l2") morphbr@736: norm = self.params_first("norm", "pal-m") morphbr@736: channel = self.params_first("channel", "13") morphbr@736: chanlist = self.params_first("chanlist", "us-bcast") morphbr@736: outfmt = self.params_first("outfmt", "yuy2") morphbr@736: vdev = self.params_first("vdev", "/dev/video0") morphbr@736: adev = self.params_first("adev", "/dev/dsp") morphbr@739: self.args["tv"] = "tv:// -v -tv driver=%s:norm=%s:channel=%s:" \ morphbr@739: "chanlist=%s:width=%s:height=%s:outfmt=%s:" \ morphbr@739: "device=%s:adevice=%s" % (driver, norm, morphbr@739: channel, chanlist, morphbr@739: self.args["width"], morphbr@739: self.args["height"], morphbr@739: outfmt, vdev, adev) morphbr@718: # _setup_filename() morphbr@718: morphbr@718: morphbr@718: def __init__(self, params): morphbr@718: transcoder.Transcoder.__init__(self, params) morphbr@718: self.mencoder_opts = [] morphbr@718: morphbr@718: try: morphbr@718: self._setup_params() morphbr@718: self._setup_filename() morphbr@718: self._setup_mencoder_opts(self.mencoder_opts) morphbr@718: except Exception, e: morphbr@742: if self.log: morphbr@742: self.log.error(self.tid, "Error: %s" % e) morphbr@742: else: morphbr@742: raise morphbr@718: # __init__() morphbr@718: morphbr@718: morphbr@718: def _check_opened_file(self, stdw, _stdin): morphbr@718: loop = True morphbr@718: while loop: morphbr@718: try: morphbr@718: return open(self.args["outfile"]) morphbr@718: except: morphbr@718: os.write(stdw, _stdin.read(1024)) morphbr@718: # _check_opened_file morphbr@718: morphbr@718: morphbr@718: def _start_outfile(self, outfd): morphbr@718: finished = False morphbr@718: morphbr@718: # Configuring stdin morphbr@718: try: morphbr@723: filename = self.args["input"].split("://")[1] morphbr@723: _stdin = open(filename) morphbr@723: size = int(os.path.getsize(filename)) morphbr@718: except Exception, e: morphbr@723: self.log.error(self.tid, "Error: Mencoder stdin"\ morphbr@723: " setup error: %s" % e) morphbr@723: outfd.write("Error: Mencoder stdin setup error: %s" %e) morphbr@718: return False morphbr@718: morphbr@718: self.status = 0 morphbr@718: total_read = 0 morphbr@718: morphbr@718: # Configuring pipes morphbr@718: stdr, stdw = os.pipe() morphbr@718: morphbr@718: if not self._run_mencoder(input=stdr): morphbr@718: return False morphbr@718: morphbr@718: stdout = self._check_opened_file(stdw, _stdin) morphbr@726: outfd.write("OK ") morphbr@726: morphbr@718: try: morphbr@718: while self.proc and self.proc.poll() == None: morphbr@718: if not finished: morphbr@718: data_in = _stdin.read(4096) morphbr@718: if data_in != "": morphbr@718: os.write(stdw, data_in) morphbr@718: total_read += 4096 morphbr@718: d = stdout.read(4096) morphbr@744: self.status = utils.progress_bar(total_read, morphbr@744: size, 50) morphbr@718: else: morphbr@718: finished = True morphbr@718: os.close(stdw) morphbr@718: morphbr@718: else: morphbr@718: d = stdout.read(4096) morphbr@718: morphbr@718: except Exception, e: morphbr@723: self.log.error(self.tid, "Error: %s" % e) morphbr@718: self.stop() morphbr@718: return False morphbr@718: morphbr@723: self.log.info(self.tid, "OK: Done") morphbr@718: return True morphbr@718: # _start_outfile() morphbr@718: morphbr@736: morphbr@718: def _start(self, outfd): morphbr@718: # Play a file on disk or DVD morphbr@718: if not self._run_mencoder(output=subprocess.PIPE): morphbr@718: return False morphbr@718: morphbr@742: if not self.params_first("multicast", False): morphbr@740: try: morphbr@740: while self.proc and self.proc.poll() == None: morphbr@740: d = self.proc.stdout.read(1024) morphbr@740: outfd.write(d) morphbr@740: except Exception, e: morphbr@740: self.log.error(self.tid, "Error: %s" % e) morphbr@740: return False morphbr@740: else: morphbr@740: try: morphbr@740: outfd.write("OK ") morphbr@740: multicast_ip = self.params_first("multicast", morphbr@740: "225.100.100.100") morphbr@740: multicast_port = int(self.params_first("multicastport", morphbr@740: "12345")) morphbr@740: sock = socket.socket(socket.AF_INET, morphbr@740: socket.SOCK_DGRAM, socket.IPPROTO_UDP) morphbr@740: sock.setsockopt(socket.IPPROTO_IP, morphbr@740: socket.IP_MULTICAST_TTL, 2) morphbr@740: morphbr@740: while self.proc and self.proc.poll() == None: morphbr@740: d = self.proc.stdout.read(1024) morphbr@740: sock.sendto(d, (multicast_ip, multicast_port)) morphbr@740: except Exception, e: morphbr@740: self.log.error(self.tid, "Error: %s" % e) morphbr@740: return False morphbr@718: morphbr@723: self.log.info(self.tid, "OK: Done") morphbr@718: return True morphbr@718: # _start() morphbr@718: morphbr@738: def _run_mencoder(self, input=None, output=None): morphbr@718: try: morphbr@718: self.proc = subprocess.Popen(self.mencoder_opts, stdin=input, morphbr@738: stdout=output, close_fds=True) morphbr@718: except Exception, e: morphbr@723: self.log.error(self.tid, "Error: Mencoder: %s" % e) morphbr@718: return False morphbr@718: morphbr@718: return True morphbr@718: # _run_mencoder() morphbr@718: morphbr@718: def start(self, outfd): morphbr@718: cmd = " ".join(self.mencoder_opts) morphbr@718: self.log.debug(self.tid, "Plugin's tid: %s" % self.tid) morphbr@718: self.log.debug(self.tid, "Mencoder: %s" % cmd) morphbr@718: morphbr@718: ret = False morphbr@718: morphbr@718: if self.args["outfile"] == "-" and \ morphbr@736: self.args["type"] in ["file", "dvd", "tv"]: morphbr@718: ret = self._start(outfd) morphbr@718: morphbr@718: elif self.args["type"] == "myth": morphbr@718: ret = mythtv.start_myth(self, outfd) morphbr@718: morphbr@718: else: morphbr@718: ret = self._start_outfile(outfd) morphbr@718: morphbr@718: self.stop() morphbr@718: morphbr@718: if not ret: morphbr@723: self.log.error(self.tid, "Error: Problems while "\ morphbr@723: "starting streaming.") morphbr@718: morphbr@718: return ret morphbr@718: # start() morphbr@718: morphbr@718: def _aux_stop(self, obj, next=False): morphbr@718: if obj: morphbr@718: try: morphbr@718: os.kill(obj.pid, signal.SIGKILL) morphbr@718: if next: morphbr@718: os.kill(obj.pid+1, signal.SIGKILL) morphbr@718: except OSError, e: morphbr@718: pass morphbr@718: morphbr@718: try: morphbr@718: obj.wait() morphbr@718: except Exception, e: morphbr@718: pass morphbr@718: morphbr@718: obj = None morphbr@718: # _aux_stop morphbr@718: morphbr@718: def stop(self): morphbr@718: self._aux_stop(self.proc, True) morphbr@718: self._aux_stop(self.gmyth) morphbr@718: # stop() morphbr@718: morphbr@718: # TranscoderMencoder