gmyth-stream/server/0.3/plugins/transcoders/mencoder.py
author morphbr
Fri Jun 01 17:12:35 2007 +0100 (2007-06-01)
branchtrunk
changeset 740 c73e1eb09a6f
parent 739 517706c7003e
child 742 fe8ddffd7f5c
permissions -rw-r--r--
[svn r746] * GMyth-Streamer:
- Multicast support
     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             self.log.error(self.tid, "Error: %s" % e)
   189     # __init__()
   190 
   191 
   192     def _check_opened_file(self, stdw, _stdin):
   193         loop = True
   194         while loop:
   195             try:
   196                 return open(self.args["outfile"])
   197             except:
   198                 os.write(stdw, _stdin.read(1024))
   199     # _check_opened_file
   200 
   201 
   202     def _start_outfile(self, outfd):
   203         finished = False
   204 
   205         # Configuring stdin
   206         try:
   207             filename = self.args["input"].split("://")[1]
   208             _stdin = open(filename)
   209             size = int(os.path.getsize(filename))
   210         except Exception, e:
   211             self.log.error(self.tid, "Error: Mencoder stdin"\
   212                            " setup error: %s" % e)
   213             outfd.write("Error: Mencoder stdin setup error: %s" %e)
   214             return False
   215 
   216         self.status = 0
   217         total_read = 0
   218 
   219         # Configuring pipes
   220         stdr, stdw = os.pipe()
   221 
   222         if not self._run_mencoder(input=stdr):
   223             return False
   224 
   225         stdout = self._check_opened_file(stdw, _stdin)
   226         outfd.write("OK   ")
   227 
   228         try:
   229             while self.proc and self.proc.poll() == None:
   230                 if not finished:
   231                     data_in = _stdin.read(4096)
   232                     if data_in != "":
   233                         os.write(stdw, data_in)
   234                         total_read += 4096
   235                         d = stdout.read(4096)
   236                         self.status = utils.progress_bar(int(total_read),
   237                                                          int(size), 50)
   238                     else:
   239                         finished = True
   240                         os.close(stdw)
   241 
   242                 else:
   243                     d = stdout.read(4096)
   244 
   245         except Exception, e:
   246             self.log.error(self.tid, "Error: %s" % e)
   247             self.stop()
   248             return False
   249 
   250         self.log.info(self.tid, "OK: Done")
   251         return True
   252     # _start_outfile()
   253 
   254 
   255     def _start(self, outfd):
   256         # Play a file on disk or DVD
   257         if not self._run_mencoder(output=subprocess.PIPE):
   258             return False
   259 
   260         if not self.params_first("multicast", None):
   261             try:
   262                 while self.proc and self.proc.poll() == None:
   263                     d = self.proc.stdout.read(1024)
   264                     outfd.write(d)
   265             except Exception, e:
   266                 self.log.error(self.tid, "Error: %s" % e)
   267                 return False
   268         else:
   269             try:
   270                 outfd.write("OK   ")
   271                 multicast_ip = self.params_first("multicast",
   272                                                  "225.100.100.100")
   273                 multicast_port = int(self.params_first("multicastport",
   274                                                        "12345"))
   275                 sock = socket.socket(socket.AF_INET,
   276                                      socket.SOCK_DGRAM, socket.IPPROTO_UDP)
   277                 sock.setsockopt(socket.IPPROTO_IP,
   278                                 socket.IP_MULTICAST_TTL, 2)
   279 
   280                 while self.proc and self.proc.poll() == None:
   281                     d = self.proc.stdout.read(1024)
   282                     sock.sendto(d, (multicast_ip, multicast_port))
   283             except Exception, e:
   284                 self.log.error(self.tid, "Error: %s" % e)
   285                 return False
   286 
   287         self.log.info(self.tid, "OK: Done")
   288         return True
   289     # _start()
   290 
   291     def _run_mencoder(self, input=None, output=None):
   292         try:
   293             self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
   294                                          stdout=output, close_fds=True)
   295         except Exception, e:
   296             self.log.error(self.tid, "Error: Mencoder: %s" % e)
   297             return False
   298 
   299         return True
   300     # _run_mencoder()
   301 
   302     def start(self, outfd):
   303         print "mencoder_opts: %s" % self.mencoder_opts
   304         cmd = " ".join(self.mencoder_opts)
   305         self.log.debug(self.tid, "Plugin's tid: %s" % self.tid)
   306         self.log.debug(self.tid, "Mencoder: %s" % cmd)
   307 
   308         ret = False
   309 
   310         if self.args["outfile"] == "-" and \
   311                self.args["type"] in ["file", "dvd", "tv"]:
   312             ret = self._start(outfd)
   313 
   314         elif self.args["type"] == "myth":
   315             ret = mythtv.start_myth(self, outfd)
   316 
   317         else:
   318             ret = self._start_outfile(outfd)
   319 
   320         self.stop()
   321 
   322         if not ret:
   323             self.log.error(self.tid, "Error: Problems while "\
   324                            "starting streaming.")
   325 
   326         return ret
   327     # start()
   328 
   329     def _aux_stop(self, obj, next=False):
   330         if obj:
   331             try:
   332                 os.kill(obj.pid, signal.SIGKILL)
   333                 if next:
   334                     os.kill(obj.pid+1, signal.SIGKILL)
   335             except OSError, e:
   336                 pass
   337 
   338             try:
   339                 obj.wait()
   340             except Exception, e:
   341                 pass
   342 
   343             obj = None
   344     # _aux_stop
   345 
   346     def stop(self):
   347         self._aux_stop(self.proc, True)
   348         self._aux_stop(self.gmyth)
   349     # stop()
   350 
   351 # TranscoderMencoder