gmyth-stream/server/0.3/plugins/transcoders/mencoder.py
author morphbr
Mon Jun 04 12:30:07 2007 +0100 (2007-06-04)
branchtrunk
changeset 742 fe8ddffd7f5c
parent 740 c73e1eb09a6f
child 744 ef4fb30176dd
permissions -rw-r--r--
[svn r748] * GMyth-Streamer
- Mencoder multicast and unicast bug fix;
- Fixed bug when trying to just stream (not transcode)
a file that does not exist.
     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(int(total_read),
   240                                                          int(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         if not self.params_first("multicast", False):
   264             try:
   265                 while self.proc and self.proc.poll() == None:
   266                     d = self.proc.stdout.read(1024)
   267                     outfd.write(d)
   268             except Exception, e:
   269                 self.log.error(self.tid, "Error: %s" % e)
   270                 return False
   271         else:
   272             try:
   273                 outfd.write("OK   ")
   274                 multicast_ip = self.params_first("multicast",
   275                                                  "225.100.100.100")
   276                 multicast_port = int(self.params_first("multicastport",
   277                                                        "12345"))
   278                 sock = socket.socket(socket.AF_INET,
   279                                      socket.SOCK_DGRAM, socket.IPPROTO_UDP)
   280                 sock.setsockopt(socket.IPPROTO_IP,
   281                                 socket.IP_MULTICAST_TTL, 2)
   282 
   283                 while self.proc and self.proc.poll() == None:
   284                     d = self.proc.stdout.read(1024)
   285                     sock.sendto(d, (multicast_ip, multicast_port))
   286             except Exception, e:
   287                 self.log.error(self.tid, "Error: %s" % e)
   288                 return False
   289 
   290         self.log.info(self.tid, "OK: Done")
   291         return True
   292     # _start()
   293 
   294     def _run_mencoder(self, input=None, output=None):
   295         try:
   296             self.proc = subprocess.Popen(self.mencoder_opts, stdin=input,
   297                                          stdout=output, close_fds=True)
   298         except Exception, e:
   299             self.log.error(self.tid, "Error: Mencoder: %s" % e)
   300             return False
   301 
   302         return True
   303     # _run_mencoder()
   304 
   305     def start(self, outfd):
   306         print "mencoder_opts: %s" % self.mencoder_opts
   307         cmd = " ".join(self.mencoder_opts)
   308         self.log.debug(self.tid, "Plugin's tid: %s" % self.tid)
   309         self.log.debug(self.tid, "Mencoder: %s" % cmd)
   310 
   311         ret = False
   312 
   313         if self.args["outfile"] == "-" and \
   314                self.args["type"] in ["file", "dvd", "tv"]:
   315             ret = self._start(outfd)
   316 
   317         elif self.args["type"] == "myth":
   318             ret = mythtv.start_myth(self, outfd)
   319 
   320         else:
   321             ret = self._start_outfile(outfd)
   322 
   323         self.stop()
   324 
   325         if not ret:
   326             self.log.error(self.tid, "Error: Problems while "\
   327                            "starting streaming.")
   328 
   329         return ret
   330     # start()
   331 
   332     def _aux_stop(self, obj, next=False):
   333         if obj:
   334             try:
   335                 os.kill(obj.pid, signal.SIGKILL)
   336                 if next:
   337                     os.kill(obj.pid+1, signal.SIGKILL)
   338             except OSError, e:
   339                 pass
   340 
   341             try:
   342                 obj.wait()
   343             except Exception, e:
   344                 pass
   345 
   346             obj = None
   347     # _aux_stop
   348 
   349     def stop(self):
   350         self._aux_stop(self.proc, True)
   351         self._aux_stop(self.gmyth)
   352     # stop()
   353 
   354 # TranscoderMencoder