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