gmyth-stream/server/0.1/plugins/media/mencoder.py
branchtrunk
changeset 783 08371b4fff76
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gmyth-stream/server/0.1/plugins/media/mencoder.py	Wed Jul 04 21:49:50 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()