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