1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/gmyth-stream/server/0.3/plugins/transcoders/mencoder.py Tue May 29 21:24:48 2007 +0100
1.3 @@ -0,0 +1,305 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +__author__ = "Artur Duque de Souza"
1.7 +__author_email__ = "artur.souza@indt.org.br"
1.8 +__license__ = "GPL"
1.9 +__version__ = "0.1"
1.10 +
1.11 +import os
1.12 +import shlex
1.13 +import signal
1.14 +import subprocess
1.15 +import time
1.16 +import fcntl
1.17 +
1.18 +import lib.utils as utils
1.19 +import lib.server as server
1.20 +import plugins.transcoders.mencoder_lib.mythtv as mythtv
1.21 +
1.22 +from select import select
1.23 +import lib.transcoder as transcoder
1.24 +
1.25 +__all__ = ("TranscoderMencoder",)
1.26 +
1.27 +class TranscoderMencoder(transcoder.Transcoder):
1.28 + """Transcoder class that implements a transcoder using Mencoder"""
1.29 + mencoder_path = utils.which("mencoder")
1.30 + name = "mencoder"
1.31 + priority = -1
1.32 + args = {}
1.33 + proc = None
1.34 + gmyth = None
1.35 +
1.36 + # only works with avi container
1.37 + status = 0
1.38 +
1.39 + def _setup_params(self):
1.40 + params_first = self.params_first
1.41 +
1.42 + # general_opts
1.43 + self.args["local"] = params_first("local", False)
1.44 + self.args["language"] = params_first("language", False)
1.45 + self.args["subtitle"] = params_first("subtitle", False)
1.46 + self.args["format"] = params_first("format", "mpeg1")
1.47 + self.args["outfile"] = params_first("outfile", "-")
1.48 +
1.49 + # input_opt
1.50 + self.args["type"] = params_first("type", "file")
1.51 + self.args["input"] = params_first("uri", "-")
1.52 +
1.53 + # audio_opts
1.54 + self.args["acodec"] = params_first("acodec", "mp2")
1.55 + self.args["abitrate"] = params_first("abitrate", 192)
1.56 + self.args["volume"] = params_first("volume", 5)
1.57 +
1.58 + # video_opts
1.59 + self.args["mux"] = params_first("mux", "mpeg")
1.60 + self.args["fps"] = params_first("fps", 25)
1.61 + self.args["vcodec"] = params_first("vcodec", "mpeg1video")
1.62 + self.args["vbitrate"] = params_first("vbitrate", 400)
1.63 + self.args["width"] = params_first("width", 320)
1.64 + self.args["height"] = params_first("height", 240)
1.65 + # _setup_params()
1.66 +
1.67 +
1.68 + def _setup_audio(self):
1.69 + if self.args["acodec"] == "mp3lame":
1.70 + audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % (
1.71 + self.args["abitrate"], self.args["volume"])
1.72 + else:
1.73 + audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (
1.74 + self.args["acodec"], self.args["abitrate"])
1.75 +
1.76 + return audio
1.77 + # _setup_audio()
1.78 +
1.79 +
1.80 + def _setup_video(self):
1.81 + video = " -of %s" % self.args["mux"]
1.82 + video += " -ofps %s" % self.args["fps"]
1.83 +
1.84 + vcodec = self.args["vcodec"]
1.85 + if vcodec == "nuv" or vcodec == "xvid"\
1.86 + or vcodec == "qtvideo" or vcodec == "copy":
1.87 + video += " -ovc %s" % vcodec
1.88 + else:
1.89 + video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
1.90 + vcodec, self.args["vbitrate"])
1.91 +
1.92 + if self.args["mux"] == "mpeg":
1.93 + video += " -mpegopts format=%s" % self.args["format"]
1.94 + video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"])
1.95 +
1.96 + return video
1.97 + # _setup_video()
1.98 +
1.99 +
1.100 + def _arg_append(self, args, options):
1.101 + for arg in shlex.split(options):
1.102 + args.append(arg)
1.103 + # arg_append()
1.104 +
1.105 + def _setup_mencoder_opts(self, args):
1.106 + args.append(self.mencoder_path)
1.107 +
1.108 + if self.args["outfile"] == "-" and self.args["type"]:
1.109 + args.append(self.args["input"])
1.110 + else:
1.111 + args.append("-")
1.112 +
1.113 + if self.args["language"]:
1.114 + self._arg_append(args, "-alang %s" % self.args["language"])
1.115 +
1.116 + if self.args["subtitle"]:
1.117 + self._arg_append(args, "-slang %s" % self.args["subtitle"])
1.118 + self._arg_append(args, "-subfps %s" % self.args["fps"])
1.119 +
1.120 + self._arg_append(args, "-idx")
1.121 + self._arg_append(args, "-cache 1024")
1.122 + self._arg_append(args, self._setup_audio())
1.123 + self._arg_append(args, self._setup_video())
1.124 +
1.125 + self._arg_append(args, "-really-quiet")
1.126 + self._arg_append(args, "-o %s" % self.args["outfile"])
1.127 + self._arg_append(args, "2>%s" % os.devnull)
1.128 + # _setup_args()
1.129 +
1.130 + def _setup_filename(self):
1.131 + """This function setups the file to encode parsing the uri.
1.132 + So, type can be:
1.133 + * file
1.134 + * dvd
1.135 + * myth
1.136 +
1.137 + If the last one is detected we have to parse the uri to find args.
1.138 + Then we store all the args inside a dictionary: self.args['gmyth-cat']
1.139 + """
1.140 + _type = self.args["type"]
1.141 +
1.142 + if _type == "file":
1.143 + if not os.path.exists(self.args["input"]):
1.144 + raise IOError,\
1.145 + "File requested does not exist: %s." % self.args["input"]
1.146 + else:
1.147 + self.args["input"] = "file://%s" % self.args["input"]
1.148 +
1.149 + elif _type == "dvd":
1.150 + self.args["input"] = "dvd://".join(self.args["input"])
1.151 +
1.152 + elif _type == "myth":
1.153 + self.args["gmyth-cat"] = mythtv._setup_mythfilename(self)
1.154 + # _setup_filename()
1.155 +
1.156 +
1.157 + def __init__(self, params):
1.158 + transcoder.Transcoder.__init__(self, params)
1.159 + self.mencoder_opts = []
1.160 +
1.161 + try:
1.162 + self._setup_params()
1.163 + self._setup_filename()
1.164 + self._setup_mencoder_opts(self.mencoder_opts)
1.165 + except Exception, e:
1.166 + self.log.error(self.tid, e)
1.167 + # __init__()
1.168 +
1.169 +
1.170 + def _check_opened_file(self, stdw, _stdin):
1.171 + loop = True
1.172 + while loop:
1.173 + try:
1.174 + return open(self.args["outfile"])
1.175 + except:
1.176 + os.write(stdw, _stdin.read(1024))
1.177 + # _check_opened_file
1.178 +
1.179 +
1.180 + def _start_outfile(self, outfd):
1.181 + finished = False
1.182 +
1.183 + # fix this (not necessary)
1.184 + outfd.write("OK")
1.185 +
1.186 + # Configuring stdin
1.187 + try:
1.188 + _stdin = open(self.args["input"])
1.189 + size = int(os.path.getsize(self.args["input"]))
1.190 + except Exception, e:
1.191 + self.log.error(self.tid, "Mencoder stdin setup error: %s" % e)
1.192 + return False
1.193 +
1.194 + self.status = 0
1.195 + total_read = 0
1.196 +
1.197 + # Configuring pipes
1.198 + stdr, stdw = os.pipe()
1.199 +
1.200 + if not self._run_mencoder(input=stdr):
1.201 + return False
1.202 +
1.203 + stdout = self._check_opened_file(stdw, _stdin)
1.204 +
1.205 + try:
1.206 + while self.proc and self.proc.poll() == None:
1.207 + if not finished:
1.208 + data_in = _stdin.read(4096)
1.209 + if data_in != "":
1.210 + os.write(stdw, data_in)
1.211 + total_read += 4096
1.212 + d = stdout.read(4096)
1.213 + self.status = utils.progress_bar(self.log,
1.214 + int(total_read),
1.215 + int(size), 50)
1.216 + else:
1.217 + finished = True
1.218 + os.close(stdw)
1.219 +
1.220 + else:
1.221 + d = stdout.read(4096)
1.222 +
1.223 + except Exception, e:
1.224 + self.log.error(self.tid, "Problems handling data: %s" % e)
1.225 + self.stop()
1.226 + return False
1.227 +
1.228 + self.log.info(self.tid, "%s: Finished sending data to client" % repr(self))
1.229 + return True
1.230 + # _start_outfile()
1.231 +
1.232 + def _start(self, outfd):
1.233 + # Play a file on disk or DVD
1.234 + if not self._run_mencoder(output=subprocess.PIPE):
1.235 + return False
1.236 +
1.237 + try:
1.238 + while self.proc and self.proc.poll() == None:
1.239 + d = self.proc.stdout.read(1024)
1.240 + outfd.write(d)
1.241 + except Exception, e:
1.242 + self.log.error(self.tid, "Problems handling data: %s" % e)
1.243 + return False
1.244 +
1.245 + self.log.info(self.tid, "%s: Finished sending data to client" % repr(self))
1.246 + return True
1.247 + # _start()
1.248 +
1.249 + def _run_mencoder(self, input=None, output=None):
1.250 + try:
1.251 + self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
1.252 + stdout=output, close_fds=True)
1.253 + except Exception, e:
1.254 + self.log.error(self.tid, "Error executing mencoder: %s" % e)
1.255 + return False
1.256 +
1.257 + return True
1.258 + # _run_mencoder()
1.259 +
1.260 + def start(self, outfd):
1.261 + cmd = " ".join(self.mencoder_opts)
1.262 + self.log.debug(self.tid, "Plugin's tid: %s" % self.tid)
1.263 + self.log.debug(self.tid, "Mencoder: %s" % cmd)
1.264 + #fixme
1.265 +
1.266 + ret = False
1.267 +
1.268 + if self.args["outfile"] == "-" and \
1.269 + self.args["type"] in ["file", "dvd"]:
1.270 + ret = self._start(outfd)
1.271 +
1.272 + elif self.args["type"] == "myth":
1.273 + ret = mythtv.start_myth(self, outfd)
1.274 +
1.275 + else:
1.276 + ret = self._start_outfile(outfd)
1.277 +
1.278 + self.stop()
1.279 +
1.280 + if not ret:
1.281 + self.log.error(self.tid, "Problems while starting streaming.")
1.282 +
1.283 + return ret
1.284 + # start()
1.285 +
1.286 + def _aux_stop(self, obj, next=False):
1.287 + if obj:
1.288 + try:
1.289 + os.kill(obj.pid, signal.SIGKILL)
1.290 + if next:
1.291 + os.kill(obj.pid+1, signal.SIGKILL)
1.292 + except OSError, e:
1.293 + pass
1.294 +
1.295 + try:
1.296 + obj.wait()
1.297 + except Exception, e:
1.298 + pass
1.299 +
1.300 + obj = None
1.301 + # _aux_stop
1.302 +
1.303 + def stop(self):
1.304 + self._aux_stop(self.proc, True)
1.305 + self._aux_stop(self.gmyth)
1.306 + # stop()
1.307 +
1.308 +# TranscoderMencoder