gmyth-stream/server/0.2/plugins/transcoders/mencoder.py
author morphbr
Tue Aug 28 15:41:35 2007 +0100 (2007-08-28)
branchtrunk
changeset 829 0a4e6b811acc
parent 664 464d715ddb7e
permissions -rw-r--r--
[svn r835] - Increased buffer size on gstmythtvsrc.c
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@653
    10
import plugins.transcoders.mencoder_lib.mythtv as mythtv
morphbr@565
    11
morphbr@595
    12
from select import select
morphbr@595
    13
morphbr@565
    14
__all__ = ("TranscoderMencoder",)
morphbr@565
    15
morphbr@565
    16
class TranscoderMencoder(server.Transcoder):
morphbr@585
    17
    """Transcoder class that implements a transcoder using Mencoder"""
morphbr@565
    18
    mencoder_path = utils.which("mencoder")
morphbr@565
    19
    name = "mencoder"
morphbr@565
    20
    priority = -1
morphbr@577
    21
    args = {}
morphbr@577
    22
    proc = None
morphbr@605
    23
    gmyth = None
morphbr@565
    24
morphbr@595
    25
    # only works with avi container
morphbr@595
    26
    status = 0
morphbr@595
    27
morphbr@569
    28
    def _setup_params(self):
morphbr@569
    29
        params_first = self.params_first
morphbr@569
    30
morphbr@569
    31
        # general_opts
morphbr@569
    32
        self.args["local"]    = params_first("local", False)
morphbr@569
    33
        self.args["language"] = params_first("language", False)
morphbr@569
    34
        self.args["subtitle"] = params_first("subtitle", False)
morphbr@628
    35
        self.args["format"]   = params_first("format", "mpeg1")
morphbr@569
    36
        self.args["outfile"]  = params_first("outfile", "-")
morphbr@569
    37
morphbr@569
    38
        # input_opt
morphbr@664
    39
        self.args["type"]     = params_first("type", "file")
morphbr@664
    40
        self.args["input"]    = params_first("uri", "-")
morphbr@569
    41
morphbr@569
    42
        # audio_opts
morphbr@569
    43
        self.args["acodec"]   = params_first("acodec", "mp2")
morphbr@569
    44
        self.args["abitrate"] = params_first("abitrate", 192)
morphbr@569
    45
        self.args["volume"]   = params_first("volume", 5)
morphbr@569
    46
morphbr@569
    47
        # video_opts
morphbr@569
    48
        self.args["mux"]      = params_first("mux", "mpeg")
morphbr@569
    49
        self.args["fps"]      = params_first("fps", 25)
morphbr@569
    50
        self.args["vcodec"]   = params_first("vcodec", "mpeg1video")
morphbr@569
    51
        self.args["vbitrate"] = params_first("vbitrate", 400)
morphbr@569
    52
        self.args["width"]    = params_first("width", 320)
morphbr@569
    53
        self.args["height"]   = params_first("height", 240)
morphbr@569
    54
    # _setup_params()
morphbr@569
    55
morphbr@569
    56
morphbr@569
    57
    def _setup_audio(self):
morphbr@569
    58
        if self.args["acodec"] == "mp3lame":
morphbr@569
    59
            audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % (
morphbr@569
    60
                self.args["abitrate"], self.args["volume"])
morphbr@569
    61
        else:
morphbr@569
    62
            audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (
morphbr@569
    63
                self.args["acodec"], self.args["abitrate"])
morphbr@569
    64
morphbr@569
    65
        return audio
morphbr@569
    66
    # _setup_audio()
morphbr@569
    67
morphbr@569
    68
morphbr@569
    69
    def _setup_video(self):
morphbr@577
    70
        video = " -of %s" % self.args["mux"]
morphbr@577
    71
        video += " -ofps %s" % self.args["fps"]
morphbr@569
    72
morphbr@569
    73
        vcodec = self.args["vcodec"]
morphbr@569
    74
        if vcodec == "nuv" or vcodec == "xvid"\
morphbr@569
    75
               or vcodec == "qtvideo" or vcodec == "copy":
morphbr@569
    76
            video += " -ovc %s" % vcodec
morphbr@569
    77
        else:
morphbr@569
    78
            video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
morphbr@569
    79
                vcodec, self.args["vbitrate"])
morphbr@569
    80
morphbr@628
    81
        if self.args["mux"] == "mpeg":
morphbr@628
    82
            video += " -mpegopts format=%s" % self.args["format"]
morphbr@569
    83
        video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"])
morphbr@569
    84
morphbr@569
    85
        return video
morphbr@569
    86
    # _setup_video()
morphbr@569
    87
morphbr@569
    88
morphbr@569
    89
    def _arg_append(self, args, options):
morphbr@577
    90
        for arg in shlex.split(options):
morphbr@569
    91
            args.append(arg)
morphbr@569
    92
    # arg_append()
morphbr@569
    93
morphbr@569
    94
    def _setup_mencoder_opts(self, args):
morphbr@569
    95
        args.append(self.mencoder_path)
morphbr@595
    96
morphbr@595
    97
        if self.args["outfile"] == "-" and self.args["type"]:
morphbr@595
    98
            args.append(self.args["input"])
morphbr@595
    99
        else:
morphbr@595
   100
            args.append("-")
morphbr@569
   101
morphbr@569
   102
        if self.args["language"]:
morphbr@569
   103
            self._arg_append(args, "-alang %s" % self.args["language"])
morphbr@569
   104
morphbr@569
   105
        if self.args["subtitle"]:
morphbr@569
   106
            self._arg_append(args, "-slang %s" % self.args["subtitle"])
morphbr@569
   107
            self._arg_append(args, "-subfps %s" % self.args["fps"])
morphbr@569
   108
morphbr@569
   109
        self._arg_append(args, "-idx")
morphbr@628
   110
        self._arg_append(args, "-cache 1024")
morphbr@577
   111
        self._arg_append(args, self._setup_audio())
morphbr@577
   112
        self._arg_append(args, self._setup_video())
morphbr@569
   113
morphbr@569
   114
        self._arg_append(args, "-really-quiet")
morphbr@569
   115
        self._arg_append(args, "-o %s" % self.args["outfile"])
morphbr@595
   116
        self._arg_append(args, "2>%s" % os.devnull)
morphbr@569
   117
    # _setup_args()
morphbr@569
   118
morphbr@664
   119
    def _setup_filename(self):
morphbr@664
   120
        """This function setups the file to encode parsing the uri.
morphbr@664
   121
        So, type can be:
morphbr@664
   122
        * file
morphbr@664
   123
        * dvd
morphbr@664
   124
        * myth
morphbr@569
   125
morphbr@664
   126
        If the last one is detected we have to parse the uri to find args.
morphbr@664
   127
        Then we store all the args inside a dictionary: self.args['gmyth-cat']
morphbr@664
   128
        """
morphbr@569
   129
        _type = self.args["type"]
morphbr@569
   130
morphbr@569
   131
        if _type == "file":
morphbr@569
   132
            if not os.path.exists(self.args["input"]):
morphbr@577
   133
                raise IOError,\
morphbr@577
   134
                      "File requested does not exist: %s." % self.args["input"]
morphbr@605
   135
            else:
morphbr@605
   136
                self.args["input"] = "file://%s" % self.args["input"]
morphbr@569
   137
morphbr@569
   138
        elif _type == "dvd":
morphbr@569
   139
            self.args["input"] = "dvd://".join(self.args["input"])
morphbr@595
   140
morphbr@595
   141
        elif _type == "myth":
morphbr@664
   142
            self.args["gmyth-cat"] = mythtv._setup_mythfilename(self)
morphbr@569
   143
    # _setup_filename()
morphbr@569
   144
morphbr@569
   145
morphbr@572
   146
    def __init__(self, params):
morphbr@565
   147
        server.Transcoder.__init__(self, params)
morphbr@577
   148
        self.mencoder_opts = []
morphbr@565
   149
morphbr@569
   150
        try:
morphbr@569
   151
            self._setup_params()
morphbr@569
   152
            self._setup_filename()
morphbr@569
   153
            self._setup_mencoder_opts(self.mencoder_opts)
morphbr@569
   154
        except Exception, e:
morphbr@569
   155
            self.log.error(e)
morphbr@565
   156
    # __init__()
morphbr@565
   157
morphbr@565
   158
morphbr@595
   159
    def _check_opened_file(self, stdw, _stdin):
morphbr@595
   160
        loop = True
morphbr@595
   161
        while loop:
morphbr@595
   162
            try:
morphbr@595
   163
                return open(self.args["outfile"])
morphbr@595
   164
            except:
morphbr@595
   165
                os.write(stdw, _stdin.read(1024))
morphbr@595
   166
    # _check_opened_file
morphbr@595
   167
morphbr@595
   168
morphbr@595
   169
    def _start_outfile(self, outfd):
morphbr@595
   170
        finished = False
morphbr@595
   171
morphbr@595
   172
        # fix this (not necessary)
morphbr@595
   173
        outfd.write("OK")
morphbr@595
   174
morphbr@595
   175
        # Configuring stdin
morphbr@638
   176
        try:
morphbr@638
   177
            _stdin = open(self.args["input"])
morphbr@638
   178
            size = int(os.path.getsize(self.args["input"]))
morphbr@638
   179
        except Exception, e:
morphbr@638
   180
            self.log.error("Mencoder stdin setup error: %s" % e)
morphbr@638
   181
            return False
morphbr@638
   182
morphbr@595
   183
        self.status = 0
morphbr@595
   184
        total_read = 0
morphbr@595
   185
morphbr@595
   186
        # Configuring pipes
morphbr@595
   187
        stdr, stdw = os.pipe()
morphbr@569
   188
morphbr@605
   189
        if not self._run_mencoder(input=stdr):
morphbr@565
   190
            return False
morphbr@565
   191
morphbr@595
   192
        stdout = self._check_opened_file(stdw, _stdin)
morphbr@595
   193
morphbr@595
   194
        try:
morphbr@595
   195
            while self.proc and self.proc.poll() == None:
morphbr@595
   196
                if not finished:
morphbr@595
   197
                    data_in = _stdin.read(4096)
morphbr@595
   198
                    if data_in != "":
morphbr@595
   199
                        os.write(stdw, data_in)
morphbr@595
   200
                        total_read += 4096
morphbr@595
   201
                        d = stdout.read(4096)
morphbr@660
   202
                        self.status = utils.progress_bar(self.log,
morphbr@660
   203
                                                         int(total_read),
morphbr@660
   204
                                                         int(size), 50)
morphbr@595
   205
                    else:
morphbr@595
   206
                        finished = True
morphbr@595
   207
                        os.close(stdw)
morphbr@595
   208
morphbr@595
   209
                else:
morphbr@595
   210
                    d = stdout.read(4096)
morphbr@595
   211
morphbr@595
   212
        except Exception, e:
morphbr@595
   213
            self.log.error("Problems handling data: %s" % e)
morphbr@628
   214
            self.stop()
morphbr@595
   215
            return False
morphbr@595
   216
morphbr@595
   217
        self.log.info("%s: Finished sending data to client" % repr(self))
morphbr@595
   218
        return True
morphbr@595
   219
    # _start_outfile()
morphbr@595
   220
morphbr@595
   221
    def _start(self, outfd):
morphbr@651
   222
        # Play a file on disk or DVD
morphbr@605
   223
        if not self._run_mencoder(output=subprocess.PIPE):
morphbr@595
   224
            return False
morphbr@595
   225
morphbr@565
   226
        try:
morphbr@565
   227
            while self.proc and self.proc.poll() == None:
morphbr@577
   228
                d = self.proc.stdout.read(1024)
morphbr@565
   229
                outfd.write(d)
morphbr@565
   230
        except Exception, e:
morphbr@565
   231
            self.log.error("Problems handling data: %s" % e)
morphbr@565
   232
            return False
morphbr@565
   233
morphbr@595
   234
        self.log.info("%s: Finished sending data to client" % repr(self))
morphbr@565
   235
        return True
morphbr@595
   236
    # _start()
morphbr@595
   237
morphbr@605
   238
    def _run_mencoder(self, input=None, output=None):
morphbr@605
   239
        try:
morphbr@605
   240
            self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
morphbr@605
   241
                                         stdout=output, close_fds=True)
morphbr@605
   242
        except Exception, e:
morphbr@605
   243
            self.log.error("Error executing mencoder: %s" % e)
morphbr@605
   244
            return False
morphbr@605
   245
morphbr@605
   246
        return True
morphbr@605
   247
    # _run_mencoder()
morphbr@595
   248
morphbr@595
   249
    def start(self, outfd):
morphbr@595
   250
        cmd = " ".join(self.mencoder_opts)
morphbr@595
   251
        self.log.debug("Mencoder: %s" % cmd)
morphbr@595
   252
morphbr@605
   253
        ret = False
morphbr@605
   254
morphbr@717
   255
        if self.args["outfile"] == "-" and \
morphbr@717
   256
               self.args["type"] in ["file", "dvd"]:
morphbr@605
   257
            ret = self._start(outfd)
morphbr@605
   258
morphbr@605
   259
        elif self.args["type"] == "myth":
morphbr@653
   260
            ret = mythtv.start_myth(self, outfd)
morphbr@605
   261
morphbr@595
   262
        else:
morphbr@605
   263
            ret = self._start_outfile(outfd)
morphbr@605
   264
morphbr@605
   265
        self.stop()
morphbr@717
   266
morphbr@717
   267
        if not ret:
morphbr@717
   268
            self.log.error("Problems while starting streaming.")
morphbr@717
   269
morphbr@605
   270
        return ret
morphbr@565
   271
    # start()
morphbr@565
   272
morphbr@660
   273
    def _aux_stop(self, obj):
morphbr@660
   274
        if obj:
morphbr@565
   275
            try:
morphbr@660
   276
                os.kill(obj.pid, signal.SIGKILL)
morphbr@565
   277
            except OSError, e:
morphbr@565
   278
                pass
morphbr@565
   279
morphbr@565
   280
            try:
morphbr@660
   281
                obj.wait()
morphbr@565
   282
            except Exception, e:
morphbr@565
   283
                pass
morphbr@565
   284
morphbr@660
   285
            obj = None
morphbr@660
   286
    # _aux_stop
morphbr@605
   287
morphbr@660
   288
    def stop(self):
morphbr@660
   289
        self._aux_stop(self.proc)
morphbr@660
   290
        self._aux_stop(self.gmyth)
morphbr@569
   291
    # stop()
morphbr@565
   292
morphbr@565
   293
# TranscoderMencoder