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