gmyth-stream/server/0.3/plugins/transcoders/mencoder.py
author morphbr
Tue Jun 05 12:32:43 2007 +0100 (2007-06-05)
branchtrunk
changeset 744 ef4fb30176dd
parent 742 fe8ddffd7f5c
child 748 bf9dac4bdc5d
permissions -rw-r--r--
[svn r750] * GMyth-Streamer
- Download's status capability
morphbr@718
     1
#!/usr/bin/env python
morphbr@718
     2
morphbr@718
     3
__author__ = "Artur Duque de Souza"
morphbr@718
     4
__author_email__ = "artur.souza@indt.org.br"
morphbr@718
     5
__license__ = "GPL"
morphbr@740
     6
__version__ = "0.3"
morphbr@718
     7
morphbr@718
     8
import os
morphbr@740
     9
import time
morphbr@740
    10
import fcntl
morphbr@718
    11
import shlex
morphbr@740
    12
import socket
morphbr@740
    13
import struct
morphbr@718
    14
import signal
morphbr@718
    15
import subprocess
morphbr@718
    16
morphbr@718
    17
import lib.utils as utils
morphbr@718
    18
import lib.server as server
morphbr@718
    19
import plugins.transcoders.mencoder_lib.mythtv as mythtv
morphbr@718
    20
morphbr@718
    21
from select import select
morphbr@718
    22
import lib.transcoder as transcoder
morphbr@718
    23
morphbr@718
    24
__all__ = ("TranscoderMencoder",)
morphbr@718
    25
morphbr@718
    26
class TranscoderMencoder(transcoder.Transcoder):
morphbr@718
    27
    """Transcoder class that implements a transcoder using Mencoder"""
morphbr@718
    28
    mencoder_path = utils.which("mencoder")
morphbr@718
    29
    name = "mencoder"
morphbr@718
    30
    priority = -1
morphbr@718
    31
    args = {}
morphbr@718
    32
    proc = None
morphbr@718
    33
    gmyth = None
morphbr@718
    34
morphbr@718
    35
    # only works with avi container
morphbr@718
    36
    status = 0
morphbr@718
    37
morphbr@718
    38
    def _setup_params(self):
morphbr@718
    39
        params_first = self.params_first
morphbr@718
    40
morphbr@718
    41
        # general_opts
morphbr@718
    42
        self.args["local"]    = params_first("local", False)
morphbr@718
    43
        self.args["language"] = params_first("language", False)
morphbr@718
    44
        self.args["subtitle"] = params_first("subtitle", False)
morphbr@718
    45
        self.args["format"]   = params_first("format", "mpeg1")
morphbr@718
    46
        self.args["outfile"]  = params_first("outfile", "-")
morphbr@718
    47
morphbr@718
    48
        # input_opt
morphbr@718
    49
        self.args["type"]     = params_first("type", "file")
morphbr@718
    50
        self.args["input"]    = params_first("uri", "-")
morphbr@718
    51
morphbr@718
    52
        # audio_opts
morphbr@718
    53
        self.args["acodec"]   = params_first("acodec", "mp2")
morphbr@718
    54
        self.args["abitrate"] = params_first("abitrate", 192)
morphbr@718
    55
        self.args["volume"]   = params_first("volume", 5)
morphbr@718
    56
morphbr@718
    57
        # video_opts
morphbr@718
    58
        self.args["mux"]      = params_first("mux", "mpeg")
morphbr@718
    59
        self.args["fps"]      = params_first("fps", 25)
morphbr@718
    60
        self.args["vcodec"]   = params_first("vcodec", "mpeg1video")
morphbr@718
    61
        self.args["vbitrate"] = params_first("vbitrate", 400)
morphbr@718
    62
        self.args["width"]    = params_first("width", 320)
morphbr@718
    63
        self.args["height"]   = params_first("height", 240)
morphbr@718
    64
    # _setup_params()
morphbr@718
    65
morphbr@718
    66
morphbr@718
    67
    def _setup_audio(self):
morphbr@718
    68
        if self.args["acodec"] == "mp3lame":
morphbr@718
    69
            audio = "-oac mp3lame -lameopts cbr:br=%s vol=%s" % (
morphbr@718
    70
                self.args["abitrate"], self.args["volume"])
morphbr@718
    71
        else:
morphbr@718
    72
            audio = "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (
morphbr@718
    73
                self.args["acodec"], self.args["abitrate"])
morphbr@718
    74
morphbr@718
    75
        return audio
morphbr@718
    76
    # _setup_audio()
morphbr@718
    77
morphbr@718
    78
morphbr@718
    79
    def _setup_video(self):
morphbr@718
    80
        video = " -of %s" % self.args["mux"]
morphbr@718
    81
        video += " -ofps %s" % self.args["fps"]
morphbr@718
    82
morphbr@718
    83
        vcodec = self.args["vcodec"]
morphbr@718
    84
        if vcodec == "nuv" or vcodec == "xvid"\
morphbr@718
    85
               or vcodec == "qtvideo" or vcodec == "copy":
morphbr@718
    86
            video += " -ovc %s" % vcodec
morphbr@718
    87
        else:
morphbr@718
    88
            video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
morphbr@718
    89
                vcodec, self.args["vbitrate"])
morphbr@718
    90
morphbr@718
    91
        if self.args["mux"] == "mpeg":
morphbr@718
    92
            video += " -mpegopts format=%s" % self.args["format"]
morphbr@736
    93
morphbr@718
    94
        video += " -vf scale=%s:%s" % (self.args["width"], self.args["height"])
morphbr@718
    95
        return video
morphbr@718
    96
    # _setup_video()
morphbr@718
    97
morphbr@718
    98
morphbr@718
    99
    def _arg_append(self, args, options):
morphbr@718
   100
        for arg in shlex.split(options):
morphbr@718
   101
            args.append(arg)
morphbr@718
   102
    # arg_append()
morphbr@718
   103
morphbr@718
   104
    def _setup_mencoder_opts(self, args):
morphbr@718
   105
        args.append(self.mencoder_path)
morphbr@718
   106
morphbr@739
   107
        if self.args["type"] and self.args["type"] == "tv":
morphbr@739
   108
            self._arg_append(args, self.args["tv"])
morphbr@739
   109
        elif self.args["outfile"] == "-" and self.args["type"]:
morphbr@718
   110
            args.append(self.args["input"])
morphbr@718
   111
        else:
morphbr@718
   112
            args.append("-")
morphbr@718
   113
morphbr@718
   114
        if self.args["language"]:
morphbr@718
   115
            self._arg_append(args, "-alang %s" % self.args["language"])
morphbr@718
   116
morphbr@718
   117
        if self.args["subtitle"]:
morphbr@718
   118
            self._arg_append(args, "-slang %s" % self.args["subtitle"])
morphbr@718
   119
            self._arg_append(args, "-subfps %s" % self.args["fps"])
morphbr@718
   120
morphbr@718
   121
        self._arg_append(args, "-idx")
morphbr@718
   122
        self._arg_append(args, "-cache 1024")
morphbr@718
   123
        self._arg_append(args, self._setup_audio())
morphbr@718
   124
        self._arg_append(args, self._setup_video())
morphbr@718
   125
morphbr@718
   126
        self._arg_append(args, "-really-quiet")
morphbr@723
   127
morphbr@723
   128
        if self.args["outfile"] != "-":
morphbr@723
   129
            self.args["outfile"] = ".transcoded/%s" % (
morphbr@723
   130
                                   os.path.basename(self.args["outfile"]))
morphbr@723
   131
morphbr@718
   132
        self._arg_append(args, "-o %s" % self.args["outfile"])
morphbr@718
   133
        self._arg_append(args, "2>%s" % os.devnull)
morphbr@718
   134
    # _setup_args()
morphbr@718
   135
morphbr@718
   136
    def _setup_filename(self):
morphbr@718
   137
        """This function setups the file to encode parsing the uri.
morphbr@718
   138
        So, type can be:
morphbr@718
   139
        * file
morphbr@718
   140
        * dvd
morphbr@718
   141
        * myth
morphbr@718
   142
morphbr@718
   143
        If the last one is detected we have to parse the uri to find args.
morphbr@718
   144
        Then we store all the args inside a dictionary: self.args['gmyth-cat']
morphbr@718
   145
        """
morphbr@718
   146
        _type = self.args["type"]
morphbr@718
   147
morphbr@718
   148
        if _type == "file":
morphbr@718
   149
            if not os.path.exists(self.args["input"]):
morphbr@718
   150
                raise IOError,\
morphbr@718
   151
                      "File requested does not exist: %s." % self.args["input"]
morphbr@718
   152
            else:
morphbr@718
   153
                self.args["input"] = "file://%s" % self.args["input"]
morphbr@718
   154
morphbr@718
   155
        elif _type == "dvd":
morphbr@736
   156
            self.args["input"] = "dvd://%s" % self.args["input"]
morphbr@718
   157
morphbr@718
   158
        elif _type == "myth":
morphbr@718
   159
            self.args["gmyth-cat"] = mythtv._setup_mythfilename(self)
morphbr@736
   160
morphbr@736
   161
        elif _type == "tv":
morphbr@736
   162
            driver = self.params_first("driver", "v4l2")
morphbr@736
   163
            norm = self.params_first("norm", "pal-m")
morphbr@736
   164
            channel = self.params_first("channel", "13")
morphbr@736
   165
            chanlist = self.params_first("chanlist", "us-bcast")
morphbr@736
   166
            outfmt = self.params_first("outfmt", "yuy2")
morphbr@736
   167
            vdev = self.params_first("vdev", "/dev/video0")
morphbr@736
   168
            adev = self.params_first("adev", "/dev/dsp")
morphbr@739
   169
            self.args["tv"] = "tv:// -v -tv driver=%s:norm=%s:channel=%s:" \
morphbr@739
   170
                              "chanlist=%s:width=%s:height=%s:outfmt=%s:" \
morphbr@739
   171
                              "device=%s:adevice=%s" % (driver, norm,
morphbr@739
   172
                                                        channel, chanlist,
morphbr@739
   173
                                                        self.args["width"],
morphbr@739
   174
                                                        self.args["height"],
morphbr@739
   175
                                                        outfmt, vdev, adev)
morphbr@718
   176
    # _setup_filename()
morphbr@718
   177
morphbr@718
   178
morphbr@718
   179
    def __init__(self, params):
morphbr@718
   180
        transcoder.Transcoder.__init__(self, params)
morphbr@718
   181
        self.mencoder_opts = []
morphbr@718
   182
morphbr@718
   183
        try:
morphbr@718
   184
            self._setup_params()
morphbr@718
   185
            self._setup_filename()
morphbr@718
   186
            self._setup_mencoder_opts(self.mencoder_opts)
morphbr@718
   187
        except Exception, e:
morphbr@742
   188
            if self.log:
morphbr@742
   189
                self.log.error(self.tid, "Error: %s" % e)
morphbr@742
   190
            else:
morphbr@742
   191
                raise
morphbr@718
   192
    # __init__()
morphbr@718
   193
morphbr@718
   194
morphbr@718
   195
    def _check_opened_file(self, stdw, _stdin):
morphbr@718
   196
        loop = True
morphbr@718
   197
        while loop:
morphbr@718
   198
            try:
morphbr@718
   199
                return open(self.args["outfile"])
morphbr@718
   200
            except:
morphbr@718
   201
                os.write(stdw, _stdin.read(1024))
morphbr@718
   202
    # _check_opened_file
morphbr@718
   203
morphbr@718
   204
morphbr@718
   205
    def _start_outfile(self, outfd):
morphbr@718
   206
        finished = False
morphbr@718
   207
morphbr@718
   208
        # Configuring stdin
morphbr@718
   209
        try:
morphbr@723
   210
            filename = self.args["input"].split("://")[1]
morphbr@723
   211
            _stdin = open(filename)
morphbr@723
   212
            size = int(os.path.getsize(filename))
morphbr@718
   213
        except Exception, e:
morphbr@723
   214
            self.log.error(self.tid, "Error: Mencoder stdin"\
morphbr@723
   215
                           " setup error: %s" % e)
morphbr@723
   216
            outfd.write("Error: Mencoder stdin setup error: %s" %e)
morphbr@718
   217
            return False
morphbr@718
   218
morphbr@718
   219
        self.status = 0
morphbr@718
   220
        total_read = 0
morphbr@718
   221
morphbr@718
   222
        # Configuring pipes
morphbr@718
   223
        stdr, stdw = os.pipe()
morphbr@718
   224
morphbr@718
   225
        if not self._run_mencoder(input=stdr):
morphbr@718
   226
            return False
morphbr@718
   227
morphbr@718
   228
        stdout = self._check_opened_file(stdw, _stdin)
morphbr@726
   229
        outfd.write("OK   ")
morphbr@726
   230
morphbr@718
   231
        try:
morphbr@718
   232
            while self.proc and self.proc.poll() == None:
morphbr@718
   233
                if not finished:
morphbr@718
   234
                    data_in = _stdin.read(4096)
morphbr@718
   235
                    if data_in != "":
morphbr@718
   236
                        os.write(stdw, data_in)
morphbr@718
   237
                        total_read += 4096
morphbr@718
   238
                        d = stdout.read(4096)
morphbr@744
   239
                        self.status = utils.progress_bar(total_read,
morphbr@744
   240
                                                         size, 50)
morphbr@718
   241
                    else:
morphbr@718
   242
                        finished = True
morphbr@718
   243
                        os.close(stdw)
morphbr@718
   244
morphbr@718
   245
                else:
morphbr@718
   246
                    d = stdout.read(4096)
morphbr@718
   247
morphbr@718
   248
        except Exception, e:
morphbr@723
   249
            self.log.error(self.tid, "Error: %s" % e)
morphbr@718
   250
            self.stop()
morphbr@718
   251
            return False
morphbr@718
   252
morphbr@723
   253
        self.log.info(self.tid, "OK: Done")
morphbr@718
   254
        return True
morphbr@718
   255
    # _start_outfile()
morphbr@718
   256
morphbr@736
   257
morphbr@718
   258
    def _start(self, outfd):
morphbr@718
   259
        # Play a file on disk or DVD
morphbr@718
   260
        if not self._run_mencoder(output=subprocess.PIPE):
morphbr@718
   261
            return False
morphbr@718
   262
morphbr@742
   263
        if not self.params_first("multicast", False):
morphbr@740
   264
            try:
morphbr@740
   265
                while self.proc and self.proc.poll() == None:
morphbr@740
   266
                    d = self.proc.stdout.read(1024)
morphbr@740
   267
                    outfd.write(d)
morphbr@740
   268
            except Exception, e:
morphbr@740
   269
                self.log.error(self.tid, "Error: %s" % e)
morphbr@740
   270
                return False
morphbr@740
   271
        else:
morphbr@740
   272
            try:
morphbr@740
   273
                outfd.write("OK   ")
morphbr@740
   274
                multicast_ip = self.params_first("multicast",
morphbr@740
   275
                                                 "225.100.100.100")
morphbr@740
   276
                multicast_port = int(self.params_first("multicastport",
morphbr@740
   277
                                                       "12345"))
morphbr@740
   278
                sock = socket.socket(socket.AF_INET,
morphbr@740
   279
                                     socket.SOCK_DGRAM, socket.IPPROTO_UDP)
morphbr@740
   280
                sock.setsockopt(socket.IPPROTO_IP,
morphbr@740
   281
                                socket.IP_MULTICAST_TTL, 2)
morphbr@740
   282
morphbr@740
   283
                while self.proc and self.proc.poll() == None:
morphbr@740
   284
                    d = self.proc.stdout.read(1024)
morphbr@740
   285
                    sock.sendto(d, (multicast_ip, multicast_port))
morphbr@740
   286
            except Exception, e:
morphbr@740
   287
                self.log.error(self.tid, "Error: %s" % e)
morphbr@740
   288
                return False
morphbr@718
   289
morphbr@723
   290
        self.log.info(self.tid, "OK: Done")
morphbr@718
   291
        return True
morphbr@718
   292
    # _start()
morphbr@718
   293
morphbr@738
   294
    def _run_mencoder(self, input=None, output=None):
morphbr@718
   295
        try:
morphbr@718
   296
            self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
morphbr@738
   297
                                         stdout=output, close_fds=True)
morphbr@718
   298
        except Exception, e:
morphbr@723
   299
            self.log.error(self.tid, "Error: Mencoder: %s" % e)
morphbr@718
   300
            return False
morphbr@718
   301
morphbr@718
   302
        return True
morphbr@718
   303
    # _run_mencoder()
morphbr@718
   304
morphbr@718
   305
    def start(self, outfd):
morphbr@739
   306
        print "mencoder_opts: %s" % self.mencoder_opts
morphbr@718
   307
        cmd = " ".join(self.mencoder_opts)
morphbr@718
   308
        self.log.debug(self.tid, "Plugin's tid: %s" % self.tid)
morphbr@718
   309
        self.log.debug(self.tid, "Mencoder: %s" % cmd)
morphbr@718
   310
morphbr@718
   311
        ret = False
morphbr@718
   312
morphbr@718
   313
        if self.args["outfile"] == "-" and \
morphbr@736
   314
               self.args["type"] in ["file", "dvd", "tv"]:
morphbr@718
   315
            ret = self._start(outfd)
morphbr@718
   316
morphbr@718
   317
        elif self.args["type"] == "myth":
morphbr@718
   318
            ret = mythtv.start_myth(self, outfd)
morphbr@718
   319
morphbr@718
   320
        else:
morphbr@718
   321
            ret = self._start_outfile(outfd)
morphbr@718
   322
morphbr@718
   323
        self.stop()
morphbr@718
   324
morphbr@718
   325
        if not ret:
morphbr@723
   326
            self.log.error(self.tid, "Error: Problems while "\
morphbr@723
   327
                           "starting streaming.")
morphbr@718
   328
morphbr@718
   329
        return ret
morphbr@718
   330
    # start()
morphbr@718
   331
morphbr@718
   332
    def _aux_stop(self, obj, next=False):
morphbr@718
   333
        if obj:
morphbr@718
   334
            try:
morphbr@718
   335
                os.kill(obj.pid, signal.SIGKILL)
morphbr@718
   336
                if next:
morphbr@718
   337
                    os.kill(obj.pid+1, signal.SIGKILL)
morphbr@718
   338
            except OSError, e:
morphbr@718
   339
                pass
morphbr@718
   340
morphbr@718
   341
            try:
morphbr@718
   342
                obj.wait()
morphbr@718
   343
            except Exception, e:
morphbr@718
   344
                pass
morphbr@718
   345
morphbr@718
   346
            obj = None
morphbr@718
   347
    # _aux_stop
morphbr@718
   348
morphbr@718
   349
    def stop(self):
morphbr@718
   350
        self._aux_stop(self.proc, True)
morphbr@718
   351
        self._aux_stop(self.gmyth)
morphbr@718
   352
    # stop()
morphbr@718
   353
morphbr@718
   354
# TranscoderMencoder