1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/gmyth-stream/server/0.1/plugins/media/mencoder.py Thu May 17 14:07:27 2007 +0100
1.3 @@ -0,0 +1,411 @@
1.4 +from __future__ import division
1.5 +
1.6 +import os
1.7 +import sys
1.8 +import lib
1.9 +import time
1.10 +import shlex
1.11 +import signal
1.12 +import socket
1.13 +import ConfigParser
1.14 +import logging as log
1.15 +
1.16 +from select import *
1.17 +from subprocess import *
1.18 +
1.19 +class Media(object):
1.20 +
1.21 + def __init__(self, config):
1.22 +
1.23 + self.config = config
1.24 + self.do_cleanup()
1.25 +
1.26 + # __init__()
1.27 +
1.28 +
1.29 + def do_cleanup(self):
1.30 + self.path = ""
1.31 + self.args = []
1.32 + self.language = None
1.33 + self.subtitle = None
1.34 + self.mpegopts = None
1.35 + self.socket = None
1.36 + self.child_pid = None
1.37 + self.mplayer = None
1.38 + self.mencoder_pid = None
1.39 + self.mplayer_pid = None
1.40 + self.audio_opts = None
1.41 + self.video_opts = None
1.42 + self.gst_pipe = None
1.43 + self.gst_pid = None
1.44 + self.transcode_local = None
1.45 +
1.46 + # do_cleanup()
1.47 +
1.48 +
1.49 + def setup_opts(self, options):
1.50 +
1.51 + for opt in options:
1.52 +
1.53 + if opt == "local":
1.54 + self.mplayer = lib.which("mplayer")
1.55 +
1.56 + elif opt.find("language=") >= 0:
1.57 + try:
1.58 + lan = opt.split("=")[1]
1.59 + if len(lan) < 2:
1.60 + self.language = lan
1.61 + except Exception, e:
1.62 + log.error("Bad language option: %s" % opt)
1.63 +
1.64 + elif opt.find("subtitle=") >= 0:
1.65 + try:
1.66 + sub = opt.split("=")[1]
1.67 + if len(sub) < 2:
1.68 + self.language = sub
1.69 + except Exception, e:
1.70 + log.error("Bad subtitle option: %s" % opt)
1.71 +
1.72 + elif opt.find("format=") >= 0:
1.73 + try:
1.74 + self.mpegopts = opt.split("=")[1]
1.75 + except Exception, e:
1.76 + log.error("Bad format option: %s" % opt)
1.77 +
1.78 + elif opt.find("outfile=") >= 0:
1.79 + try:
1.80 + self.transcode_local = opt.split("=")[1]
1.81 + except Exception, e:
1.82 + log.error("Bad outfile option: %s" % opt)
1.83 +
1.84 + # setup_opts()
1.85 +
1.86 +
1.87 + def run_mplayer(self):
1.88 + msg = self.filename
1.89 +
1.90 + if self.kind == "dvd":
1.91 + msg = "dvd://" + msg
1.92 +
1.93 + self.mplayer_pid = Popen([self.mplayer, self.filename, "1> %s" % os.devnull,\
1.94 + "2> %s" % os.devnull], stdout=PIPE, close_fds=True)
1.95 +
1.96 + # run_mplayer()
1.97 +
1.98 +
1.99 + def setup_mencoder(self):
1.100 + self.path = self.config.get("Mencoder", "path")
1.101 + mp = Popen([self.path], stdout=PIPE, close_fds=True)
1.102 +
1.103 + version = mp.stdout.read().split("MEncoder ")[1].split(" (C)")[0].split("-")[-1]
1.104 +
1.105 + if version > "4.1.1": self.mencoder_old = False
1.106 + else: self.mencoder_old = True
1.107 +
1.108 + os.kill(mp.pid, signal.SIGKILL)
1.109 + log.info("Mencoder version: %s" % version)
1.110 +
1.111 + if self.mencoder_old:
1.112 + try:
1.113 + self.fifo = self.config.get("Mencoder", "fifo_path")
1.114 + os.mkfifo(self.fifo)
1.115 + except Exception, e:
1.116 + log.info("Fifo: %s" % e)
1.117 + else:
1.118 + self.fifo = "-"
1.119 +
1.120 + # setup_mencoder()
1.121 +
1.122 +
1.123 + def setup_audio(self):
1.124 +
1.125 + if self.acodec == "mp3lame":
1.126 + return "-oac mp3lame -lameopts cbr:br=%s vol=5" % self.abitrate
1.127 + else:
1.128 + return "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (\
1.129 + self.acodec, self.abitrate)
1.130 +
1.131 + # setup_audio()
1.132 +
1.133 +
1.134 + def setup_video(self):
1.135 +
1.136 + video = ""
1.137 +
1.138 + video += " -of %s" % self.mux
1.139 + video += " -ofps %s" % self.fps
1.140 +
1.141 + if self.vcodec == "nuv" or self.vcodec == "xvid"\
1.142 + or self.vcodec == "qtvideo" or self.vcodec == "copy":
1.143 + video += " -ovc %s" % self.vcodec
1.144 + else:
1.145 + video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
1.146 + self.vcodec, self.vbitrate)
1.147 +
1.148 + if self.mux == "mpeg" and self.mpegopts is not None:
1.149 + video += " -mpegopts format=%s" % self.mpegopts
1.150 +
1.151 + video += " -vf scale=%s:%s" % (self.width, self.height)
1.152 +
1.153 + return video
1.154 +
1.155 + # setup_video()
1.156 +
1.157 +
1.158 + def arg_append(self, args, options):
1.159 + l = shlex.split(options)
1.160 + for i in l:
1.161 + args.append(i)
1.162 +
1.163 + # arg_append()
1.164 +
1.165 +
1.166 + def setup_args(self, args):
1.167 +
1.168 + args.append(self.path)
1.169 +
1.170 + #args.append(self.filename)
1.171 + args.append("-")
1.172 +
1.173 + if self.language != None:
1.174 + self.arg_append(args, "-alang %s" % self.language)
1.175 +
1.176 + if self.subtitle != None:
1.177 + self.arg_append(args, "-slang %s" % self.subtitle)
1.178 + self.arg_append(args, "-subfps %s" % self.fps)
1.179 +
1.180 + self.arg_append(args, "-idx")
1.181 + self.arg_append(args, self.audio_opts)
1.182 + self.arg_append(args, self.video_opts)
1.183 +
1.184 + self.arg_append(args, "-really-quiet")
1.185 + self.arg_append(args, "-o %s" % self.fifo)
1.186 + self.arg_append(args, "2> %s" % os.devnull)
1.187 +
1.188 + # setup_args()
1.189 +
1.190 +
1.191 + def setup_filename(self, filename):
1.192 + try:
1.193 + self.kind, self.filename = filename.split("://")
1.194 + except:
1.195 + return (False, "Wrong filename protocol")
1.196 +
1.197 + if self.kind == "file":
1.198 + if not os.path.exists(self.filename):
1.199 + msg = "File requested does not exist. SETUP failed."
1.200 + log.error(msg)
1.201 + return (False, msg)
1.202 +
1.203 + elif self.kind == "dvd":
1.204 + self.filename = "dvd://" + filename
1.205 +
1.206 + elif self.kind == "myth":
1.207 + self.filename = filename
1.208 + self.gst_pipe = os.pipe()
1.209 + print self.gst_pipe[0]
1.210 + print self.gst_pipe[1]
1.211 +
1.212 + return (True, "")
1.213 +
1.214 + # setup_filename()
1.215 +
1.216 +
1.217 + def setup_socket(self):
1.218 + if self.socket != None:
1.219 + self.socket = None
1.220 +
1.221 + self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
1.222 + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1.223 +
1.224 + try:
1.225 + self.socket.bind( ('', self.port) )
1.226 + self.socket.listen(1)
1.227 + except Exception, e:
1.228 + log.error("Could not create socket: %s" % e)
1.229 + return (False, e)
1.230 +
1.231 + return (True, "")
1.232 +
1.233 + # setup_socket()
1.234 +
1.235 +
1.236 + '''
1.237 + MENCODER SETUP DESCRIPTION
1.238 + ===========================
1.239 +
1.240 + -> mux, vcodecs and acodecs
1.241 + |-> mencoder (-of | -ovc | -oac) help
1.242 +
1.243 + -> if used mpeg as mux:
1.244 + |-> to setup format: format=%s as an option at the end
1.245 +
1.246 + '''
1.247 +
1.248 +
1.249 + # good one: /tmp/dvb.mpg avi mpeg4 400 25 mp3lame 192 320 240
1.250 + # file:///tmp/dvb.mpg mpeg mpeg1video 400 25 mp2 192 320 240 format=mpeg1
1.251 + # dvd://4 mpeg mpeg1video 400 25 mp3lame 192 400 240 language=en local
1.252 + # file:///tmp/mpg/bad_day.mpg avi mpeg4 400 25 mp3 192 320 240
1.253 +
1.254 + def setup(self, filename, mux, vcodec, vbitrate,\
1.255 + fps, acodec, abitrate, width, height, port, options):
1.256 +
1.257 + if self.args != []:
1.258 + self.do_cleanup()
1.259 +
1.260 + self.mux = mux
1.261 + self.vcodec = vcodec
1.262 + self.vbitrate = vbitrate
1.263 + self.fps = fps
1.264 + self.acodec = acodec
1.265 + self.abitrate = abitrate
1.266 + self.width = width
1.267 + self.height = height
1.268 + self.port = int(port)
1.269 +
1.270 + self.setup_mencoder()
1.271 +
1.272 + ret_val = self.setup_filename(filename)
1.273 +
1.274 + if not ret_val[0]:
1.275 + return ret_val
1.276 +
1.277 + self.setup_opts(options)
1.278 + self.audio_opts = self.setup_audio()
1.279 + self.video_opts = self.setup_video()
1.280 + self.setup_args(self.args)
1.281 +
1.282 + ret_val = self.setup_socket()
1.283 + return ret_val
1.284 +
1.285 + # setup()
1.286 +
1.287 + def play_loop(self, conn):
1.288 + data = self.pout.read(4096)
1.289 +
1.290 + conn.settimeout(5)
1.291 + retry = 0
1.292 +
1.293 + if not self.transcode_local:
1.294 + while data != "" and retry < 5:
1.295 + try:
1.296 + conn.send(data)
1.297 + r, w, x = select([conn], [], [], 0)
1.298 + if conn in r:
1.299 + back = conn.recv(1024)
1.300 + if back == "OK" and self.mplayer and not self.mplayer_pid:
1.301 + self.run_mplayer()
1.302 +
1.303 + except socket.error, e:
1.304 + log.error("Socket error: %s" % e)
1.305 + retry += 1
1.306 +
1.307 + data = self.pout.read(4096)
1.308 +
1.309 + else:
1.310 + local = open(self.transcode_local, "w")
1.311 + total = os.path.getsize(self.filename)
1.312 + partial = 4096
1.313 +
1.314 + while data != "":
1.315 + try:
1.316 + local.write(data)
1.317 + except Exception, e:
1.318 + log.error("Write error: %s" % e)
1.319 +
1.320 + data = self.pout.read(4096)
1.321 + partial += len(data)
1.322 + conn.send("%.2f\n" % (partial * 100 / total) )
1.323 +
1.324 + local.close()
1.325 + conn.send("DONE\n")
1.326 +
1.327 + return retry
1.328 +
1.329 + # play_loop()
1.330 +
1.331 +
1.332 + def play(self):
1.333 +
1.334 + if self.gst_pipe:
1.335 + try:
1.336 + gst = [ lib.which("gst-launch-0.10"), "--gst-debug-level=0" ]
1.337 + self.arg_append(gst, "mythtvsrc location=%s" % self.filename)
1.338 + self.arg_append(gst, "! fdsink fd=2")
1.339 + self.gst_pid = Popen(gst, close_fds=True)
1.340 + log.info("Running Gstreamer: %s" % gst);
1.341 + except Exception, e:
1.342 + msg = "Could not init Gstreamer: %s" % e
1.343 + log.error(msg)
1.344 + return (False, msg)
1.345 +
1.346 +
1.347 + log.info("Starting Mencoder: %s" % self.args )
1.348 + try:
1.349 + if not self.gst_pipe:
1.350 + self.stdin = open(self.filename)
1.351 + else:
1.352 + self.stdin = self.gst_pid.stdout
1.353 +
1.354 + self.mencoder_pid = Popen(self.args, stdin=self.stdin, stdout=PIPE, close_fds=True)
1.355 + except Exception, e:
1.356 + msg = "Could not init Mencoder: %s" % e
1.357 + log.error(msg)
1.358 + return (False, msg)
1.359 +
1.360 + if self.mencoder_old: self.pout = open(self.fifo)
1.361 + else: self.pout = self.mencoder_pid.stdout
1.362 +
1.363 + self.child_pid = os.fork()
1.364 +
1.365 + if self.child_pid == 0:
1.366 + conn, addr = self.socket.accept()
1.367 +
1.368 + log.info("Sending Data to client: %s" % addr[0])
1.369 + retry = self.play_loop(conn)
1.370 +
1.371 + if retry < 5:
1.372 + log.info("Finished sending Data to client: %s" % addr[0])
1.373 + else:
1.374 + log.error("Client timed out, retried more than %s times" % retry)
1.375 +
1.376 + os.kill(self.mencoder_pid.pid, signal.SIGKILL)
1.377 + sys.exit(0)
1.378 +
1.379 + return (True, "")
1.380 +
1.381 + # play()
1.382 +
1.383 +
1.384 + def stop(self):
1.385 + try:
1.386 +
1.387 + if self.mencoder_pid:
1.388 + os.kill(self.mencoder_pid.pid, signal.SIGTERM)
1.389 + self.mencoder_pid = None
1.390 +
1.391 + if self.mplayer_pid:
1.392 + os.kill(self.mplayer_pid.pid, signal.SIGTERM)
1.393 + self.mplayer_pid = None
1.394 +
1.395 + if self.socket:
1.396 + self.socket.close()
1.397 + self.socket = None
1.398 +
1.399 + if self.child_pid:
1.400 + os.kill(self.child_pid, signal.SIGTERM)
1.401 + self.child_pid = None
1.402 +
1.403 + if self.gst_pid:
1.404 + os.kill(self.gst_pid.pid, signal.SIGTERM)
1.405 + self.gst_pid = None
1.406 +
1.407 + self.do_cleanup()
1.408 +
1.409 + os.wait()
1.410 +
1.411 + except Exception, e:
1.412 + log.error("Stop error: %s" % e)
1.413 +
1.414 + # stop()