gmyth-stream/server/0.2/plugins/transcoders/mencoder.py
author rosfran
Tue May 15 15:45:34 2007 +0100 (2007-05-15)
branchtrunk
changeset 652 c98ddd4a9513
parent 646 8bd1033f8bf6
child 653 3433df0d6ae3
permissions -rw-r--r--
[svn r658] Added g_main_context_iteration to while loop on reading from FileTransfer.
morphbr@577
     1
import os
morphbr@577
     2
import shlex
morphbr@577
     3
import signal
morphbr@577
     4
import subprocess
morphbr@595
     5
import time
morphbr@651
     6
import fcntl
morphbr@577
     7
morphbr@565
     8
import lib.utils as utils
morphbr@565
     9
import lib.server as server
morphbr@565
    10
morphbr@595
    11
from select import select
morphbr@595
    12
morphbr@565
    13
__all__ = ("TranscoderMencoder",)
morphbr@565
    14
morphbr@565
    15
class TranscoderMencoder(server.Transcoder):
morphbr@585
    16
    """Transcoder class that implements a transcoder using Mencoder"""
morphbr@565
    17
    mencoder_path = utils.which("mencoder")
morphbr@565
    18
    name = "mencoder"
morphbr@565
    19
    priority = -1
morphbr@577
    20
    args = {}
morphbr@577
    21
    proc = None
morphbr@605
    22
    gmyth = None
morphbr@565
    23
morphbr@595
    24
    # only works with avi container
morphbr@595
    25
    status = 0
morphbr@595
    26
morphbr@569
    27
    def _setup_params(self):
morphbr@569
    28
        params_first = self.params_first
morphbr@569
    29
morphbr@569
    30
        # general_opts
morphbr@569
    31
        self.args["local"]    = params_first("local", False)
morphbr@569
    32
        self.args["language"] = params_first("language", False)
morphbr@569
    33
        self.args["subtitle"] = params_first("subtitle", False)
morphbr@628
    34
        self.args["format"]   = params_first("format", "mpeg1")
morphbr@569
    35
        self.args["outfile"]  = params_first("outfile", "-")
morphbr@595
    36
        self.args["sendback"] = params_first("sendback", True)
morphbr@595
    37
morphbr@595
    38
        # handle sendback variable
morphbr@595
    39
        if self.args["sendback"] == "False":
morphbr@595
    40
            self.args["sendback"] = False
morphbr@569
    41
morphbr@569
    42
        # input_opt
morphbr@605
    43
        uri = params_first("uri", "file:-").split(":", 1)
morphbr@577
    44
        self.args["type"]     = uri[0]
morphbr@577
    45
        self.args["input"]    = uri[1]
morphbr@569
    46
morphbr@569
    47
        # audio_opts
morphbr@569
    48
        self.args["acodec"]   = params_first("acodec", "mp2")
morphbr@569
    49
        self.args["abitrate"] = params_first("abitrate", 192)
morphbr@569
    50
        self.args["volume"]   = params_first("volume", 5)
morphbr@569
    51
morphbr@569
    52
        # video_opts
morphbr@569
    53
        self.args["mux"]      = params_first("mux", "mpeg")
morphbr@569
    54
        self.args["fps"]      = params_first("fps", 25)
morphbr@569
    55
        self.args["vcodec"]   = params_first("vcodec", "mpeg1video")
morphbr@569
    56
        self.args["vbitrate"] = params_first("vbitrate", 400)
morphbr@569
    57
        self.args["width"]    = params_first("width", 320)
morphbr@569
    58
        self.args["height"]   = params_first("height", 240)
morphbr@569
    59
    # _setup_params()
morphbr@569
    60
morphbr@569
    61
morphbr@569
    62
    def _setup_audio(self):
morphbr@569
    63
        if self.args["acodec"] == "mp3lame":
morphbr@569
    64
            audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % (
morphbr@569
    65
                self.args["abitrate"], self.args["volume"])
morphbr@569
    66
        else:
morphbr@569
    67
            audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (
morphbr@569
    68
                self.args["acodec"], self.args["abitrate"])
morphbr@569
    69
morphbr@569
    70
        return audio
morphbr@569
    71
    # _setup_audio()
morphbr@569
    72
morphbr@569
    73
morphbr@569
    74
    def _setup_video(self):
morphbr@577
    75
        video = " -of %s" % self.args["mux"]
morphbr@577
    76
        video += " -ofps %s" % self.args["fps"]
morphbr@569
    77
morphbr@569
    78
        vcodec = self.args["vcodec"]
morphbr@569
    79
        if vcodec == "nuv" or vcodec == "xvid"\
morphbr@569
    80
               or vcodec == "qtvideo" or vcodec == "copy":
morphbr@569
    81
            video += " -ovc %s" % vcodec
morphbr@569
    82
        else:
morphbr@569
    83
            video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
morphbr@569
    84
                vcodec, self.args["vbitrate"])
morphbr@569
    85
morphbr@628
    86
        if self.args["mux"] == "mpeg":
morphbr@628
    87
            video += " -mpegopts format=%s" % self.args["format"]
morphbr@569
    88
        video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"])
morphbr@569
    89
morphbr@569
    90
        return video
morphbr@569
    91
    # _setup_video()
morphbr@569
    92
morphbr@569
    93
morphbr@569
    94
    def _arg_append(self, args, options):
morphbr@577
    95
        for arg in shlex.split(options):
morphbr@569
    96
            args.append(arg)
morphbr@569
    97
    # arg_append()
morphbr@569
    98
morphbr@569
    99
    def _setup_mencoder_opts(self, args):
morphbr@569
   100
        args.append(self.mencoder_path)
morphbr@595
   101
morphbr@595
   102
        if self.args["outfile"] == "-" and self.args["type"]:
morphbr@595
   103
            args.append(self.args["input"])
morphbr@595
   104
        else:
morphbr@595
   105
            args.append("-")
morphbr@569
   106
morphbr@569
   107
        if self.args["language"]:
morphbr@569
   108
            self._arg_append(args, "-alang %s" % self.args["language"])
morphbr@569
   109
morphbr@569
   110
        if self.args["subtitle"]:
morphbr@569
   111
            self._arg_append(args, "-slang %s" % self.args["subtitle"])
morphbr@569
   112
            self._arg_append(args, "-subfps %s" % self.args["fps"])
morphbr@569
   113
morphbr@569
   114
        self._arg_append(args, "-idx")
morphbr@628
   115
        self._arg_append(args, "-cache 1024")
morphbr@577
   116
        self._arg_append(args, self._setup_audio())
morphbr@577
   117
        self._arg_append(args, self._setup_video())
morphbr@569
   118
morphbr@569
   119
        self._arg_append(args, "-really-quiet")
morphbr@569
   120
        self._arg_append(args, "-o %s" % self.args["outfile"])
morphbr@595
   121
        self._arg_append(args, "2>%s" % os.devnull)
morphbr@569
   122
    # _setup_args()
morphbr@569
   123
morphbr@569
   124
morphbr@569
   125
    def _setup_filename(self):
morphbr@569
   126
        _type = self.args["type"]
morphbr@569
   127
morphbr@569
   128
        if _type == "file":
morphbr@569
   129
            if not os.path.exists(self.args["input"]):
morphbr@577
   130
                raise IOError,\
morphbr@577
   131
                      "File requested does not exist: %s." % self.args["input"]
morphbr@605
   132
            else:
morphbr@605
   133
                self.args["input"] = "file://%s" % self.args["input"]
morphbr@569
   134
morphbr@569
   135
        elif _type == "dvd":
morphbr@569
   136
            self.args["input"] = "dvd://".join(self.args["input"])
morphbr@595
   137
morphbr@595
   138
        elif _type == "myth":
morphbr@595
   139
            # gmyth-cat -h 192.168.1.124 -p 6543 -c 111
morphbr@595
   140
            # gmyth-cat -h 192.168.1.124 -p 6543 -f file.nuv
morphbr@595
   141
            # myth://IP:PORT:type:file
morphbr@595
   142
            self.args["gmyth-cat"] = self.args["input"].split(":")
morphbr@605
   143
            self.args["input"] = "-"
morphbr@569
   144
    # _setup_filename()
morphbr@569
   145
morphbr@569
   146
morphbr@572
   147
    def __init__(self, params):
morphbr@565
   148
        server.Transcoder.__init__(self, params)
morphbr@577
   149
        self.mencoder_opts = []
morphbr@565
   150
morphbr@569
   151
        try:
morphbr@569
   152
            self._setup_params()
morphbr@569
   153
            self._setup_filename()
morphbr@569
   154
            self._setup_mencoder_opts(self.mencoder_opts)
morphbr@569
   155
        except Exception, e:
morphbr@569
   156
            self.log.error(e)
morphbr@565
   157
    # __init__()
morphbr@565
   158
morphbr@565
   159
morphbr@595
   160
    def _check_opened_file(self, stdw, _stdin):
morphbr@595
   161
        loop = True
morphbr@595
   162
        while loop:
morphbr@595
   163
            try:
morphbr@595
   164
                return open(self.args["outfile"])
morphbr@595
   165
            except:
morphbr@595
   166
                os.write(stdw, _stdin.read(1024))
morphbr@595
   167
    # _check_opened_file
morphbr@595
   168
morphbr@595
   169
morphbr@595
   170
    def _start_outfile(self, outfd):
morphbr@595
   171
        finished = False
morphbr@595
   172
morphbr@595
   173
        # fix this (not necessary)
morphbr@595
   174
        outfd.write("OK")
morphbr@595
   175
morphbr@595
   176
        # Configuring stdin
morphbr@638
   177
        try:
morphbr@638
   178
            _stdin = open(self.args["input"])
morphbr@638
   179
            size = int(os.path.getsize(self.args["input"]))
morphbr@638
   180
        except Exception, e:
morphbr@638
   181
            self.log.error("Mencoder stdin setup error: %s" % e)
morphbr@638
   182
            return False
morphbr@638
   183
morphbr@595
   184
        self.status = 0
morphbr@595
   185
        total_read = 0
morphbr@595
   186
morphbr@595
   187
        # Configuring pipes
morphbr@595
   188
        stdr, stdw = os.pipe()
morphbr@569
   189
morphbr@605
   190
        if not self._run_mencoder(input=stdr):
morphbr@565
   191
            return False
morphbr@565
   192
morphbr@595
   193
        stdout = self._check_opened_file(stdw, _stdin)
morphbr@595
   194
morphbr@595
   195
        try:
morphbr@595
   196
            while self.proc and self.proc.poll() == None:
morphbr@595
   197
                if not finished:
morphbr@595
   198
                    data_in = _stdin.read(4096)
morphbr@595
   199
                    if data_in != "":
morphbr@595
   200
                        os.write(stdw, data_in)
morphbr@595
   201
                        total_read += 4096
morphbr@595
   202
                        d = stdout.read(4096)
morphbr@595
   203
                        if self.args["sendback"]:
morphbr@595
   204
                            outfd.write(d)
morphbr@595
   205
                        self.status = total_read * 100 / size
morphbr@595
   206
                    else:
morphbr@595
   207
                        finished = True
morphbr@595
   208
                        os.close(stdw)
morphbr@595
   209
morphbr@595
   210
                else:
morphbr@595
   211
                    d = stdout.read(4096)
morphbr@595
   212
                    if self.args["sendback"] and d != "":
morphbr@595
   213
                        outfd.write(d)
morphbr@595
   214
morphbr@595
   215
        except Exception, e:
morphbr@595
   216
            self.log.error("Problems handling data: %s" % e)
morphbr@628
   217
            self.stop()
morphbr@595
   218
            return False
morphbr@595
   219
morphbr@595
   220
        self.log.info("%s: Finished sending data to client" % repr(self))
morphbr@595
   221
        if not self.args["sendback"]:
morphbr@595
   222
            outfd.write("DONE")
morphbr@595
   223
morphbr@595
   224
        return True
morphbr@595
   225
    # _start_outfile()
morphbr@595
   226
morphbr@595
   227
    def _start(self, outfd):
morphbr@651
   228
        # Play a file on disk or DVD
morphbr@605
   229
        if not self._run_mencoder(output=subprocess.PIPE):
morphbr@595
   230
            return False
morphbr@595
   231
morphbr@565
   232
        try:
morphbr@565
   233
            while self.proc and self.proc.poll() == None:
morphbr@577
   234
                d = self.proc.stdout.read(1024)
morphbr@565
   235
                outfd.write(d)
morphbr@565
   236
        except Exception, e:
morphbr@565
   237
            self.log.error("Problems handling data: %s" % e)
morphbr@565
   238
            return False
morphbr@565
   239
morphbr@595
   240
        self.log.info("%s: Finished sending data to client" % repr(self))
morphbr@565
   241
        return True
morphbr@595
   242
    # _start()
morphbr@595
   243
morphbr@605
   244
    def _start_myth(self, outfd):
morphbr@605
   245
        # gmyth-cat -h 192.168.1.124 -p 6543 -c 111
morphbr@605
   246
        # gmyth-cat -h 192.168.1.124 -p 6543 -f file.nuv
morphbr@605
   247
        # myth://IP:PORT:type:file
morphbr@605
   248
        host = self.args["gmyth-cat"][0]
morphbr@605
   249
        port = self.args["gmyth-cat"][1]
morphbr@605
   250
        kind = self.args["gmyth-cat"][2]
morphbr@605
   251
        fchan = self.args["gmyth-cat"][3]
morphbr@605
   252
morphbr@605
   253
        gmyth_cat = utils.which("gmyth-cat")
morphbr@605
   254
        opts = [gmyth_cat, "-h", host, "-p", port, "-" + kind, fchan]
morphbr@605
   255
morphbr@605
   256
        try:
morphbr@646
   257
            self.gmyth = subprocess.Popen(opts, stdout=subprocess.PIPE,
morphbr@651
   258
                                          stderr=subprocess.PIPE,
morphbr@646
   259
                                          close_fds=True)
morphbr@605
   260
        except Exception, e:
morphbr@605
   261
            self.log.error("Error executing gmyth-cat: %s" % e)
morphbr@605
   262
            return False
morphbr@605
   263
morphbr@651
   264
        err = self.gmyth.stderr
morphbr@651
   265
morphbr@646
   266
        if not self._run_mencoder(input=self.gmyth.stdout,
morphbr@646
   267
                                  output=subprocess.PIPE):
morphbr@605
   268
            return False
morphbr@605
   269
morphbr@651
   270
        if kind == "f":
morphbr@651
   271
            partial = 0
morphbr@651
   272
            size = err.read(20).split("\n")[0].split("Size:")[1]
morphbr@651
   273
            self.log.debug("Size of file: %s" % size)
morphbr@651
   274
            flags = fcntl.fcntl (err, fcntl.F_GETFL, 0) | os.O_NONBLOCK
morphbr@651
   275
            fcntl.fcntl(err, fcntl.F_SETFL, flags)
morphbr@628
   276
morphbr@605
   277
        try:
morphbr@605
   278
            while self.proc and self.proc.poll() == None:
morphbr@651
   279
                r, w, x = select([err, self.proc.stdout], [], [], 0)
morphbr@605
   280
                if self.proc.stdout in r:
morphbr@605
   281
                    d = self.proc.stdout.read(4096)
morphbr@605
   282
                    outfd.write(d)
morphbr@628
   283
morphbr@651
   284
                if err in r and kind == "f":
morphbr@651
   285
                    partial = err.read(50).split("\n")[-2]
morphbr@651
   286
                    if partial != "":
morphbr@651
   287
                        utils.progress_bar(self.log, int(partial), int(size), 50)
morphbr@628
   288
morphbr@628
   289
morphbr@605
   290
        except Exception, e:
morphbr@605
   291
            self.log.error("Problems handling data: %s" % e)
morphbr@605
   292
            return False
morphbr@605
   293
morphbr@605
   294
        return True
morphbr@605
   295
    # _start_myth()
morphbr@605
   296
morphbr@605
   297
    def _run_mencoder(self, input=None, output=None):
morphbr@605
   298
        try:
morphbr@605
   299
            self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
morphbr@605
   300
                                         stdout=output, close_fds=True)
morphbr@605
   301
        except Exception, e:
morphbr@605
   302
            self.log.error("Error executing mencoder: %s" % e)
morphbr@605
   303
            return False
morphbr@605
   304
morphbr@605
   305
        return True
morphbr@605
   306
    # _run_mencoder()
morphbr@595
   307
morphbr@595
   308
    def start(self, outfd):
morphbr@595
   309
        cmd = " ".join(self.mencoder_opts)
morphbr@595
   310
        self.log.debug("Mencoder: %s" % cmd)
morphbr@595
   311
morphbr@605
   312
        ret = False
morphbr@605
   313
morphbr@605
   314
        if self.args["outfile"] == "-" and self.args["type"] in ["file", "dvd"]:
morphbr@605
   315
            ret = self._start(outfd)
morphbr@605
   316
morphbr@605
   317
        elif self.args["type"] == "myth":
morphbr@605
   318
            ret = self._start_myth(outfd)
morphbr@605
   319
morphbr@595
   320
        else:
morphbr@605
   321
            ret = self._start_outfile(outfd)
morphbr@605
   322
morphbr@605
   323
        self.stop()
morphbr@605
   324
        return ret
morphbr@565
   325
    # start()
morphbr@565
   326
morphbr@565
   327
morphbr@565
   328
    def stop(self):
morphbr@565
   329
        if self.proc:
morphbr@565
   330
            try:
morphbr@605
   331
                os.kill(self.proc.pid, signal.SIGKILL)
morphbr@565
   332
            except OSError, e:
morphbr@565
   333
                pass
morphbr@565
   334
morphbr@565
   335
            try:
morphbr@565
   336
                self.proc.wait()
morphbr@565
   337
            except Exception, e:
morphbr@565
   338
                pass
morphbr@565
   339
morphbr@565
   340
            self.proc = None
morphbr@605
   341
morphbr@605
   342
        if self.gmyth:
morphbr@605
   343
            try:
morphbr@605
   344
                os.kill(self.gmyth.pid, signal.SIGKILL)
morphbr@605
   345
            except OSError, e:
morphbr@605
   346
                pass
morphbr@605
   347
morphbr@605
   348
            try:
morphbr@605
   349
                self.gmyth.wait()
morphbr@605
   350
            except Exception, e:
morphbr@605
   351
                pass
morphbr@605
   352
morphbr@605
   353
            self.gmyth = None
morphbr@605
   354
morphbr@569
   355
    # stop()
morphbr@565
   356
morphbr@565
   357
# TranscoderMencoder