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