gmyth-stream/server/0.1/plugins/media/mencoder.py
author renatofilho
Thu Jul 05 13:43:24 2007 +0100 (2007-07-05)
branchtrunk
changeset 786 a4529d0f8ede
permissions -rw-r--r--
[svn r792] fixed bug on live
     1 from __future__ import division
     2 
     3 import os
     4 import sys
     5 import lib
     6 import time
     7 import shlex
     8 import signal
     9 import socket
    10 import ConfigParser
    11 import logging as log
    12 
    13 from select import *
    14 from subprocess import *
    15 
    16 class Media(object):
    17 
    18     def __init__(self, config):
    19 
    20         self.config = config
    21         self.do_cleanup()
    22 
    23     # __init__()
    24 
    25 
    26     def do_cleanup(self):
    27         self.path = ""
    28         self.args = []
    29         self.language = None
    30         self.subtitle = None
    31         self.mpegopts = None
    32         self.socket = None
    33         self.child_pid = None
    34         self.mplayer = None
    35         self.mencoder_pid = None
    36         self.mplayer_pid = None
    37         self.audio_opts = None
    38         self.video_opts = None
    39         self.gst_pipe = None
    40         self.gst_pid = None
    41         self.transcode_local = None
    42 
    43     # do_cleanup()
    44 
    45 
    46     def setup_opts(self, options):
    47 
    48         for opt in options:
    49 
    50             if opt == "local":
    51                 self.mplayer = lib.which("mplayer")
    52 
    53             elif opt.find("language=") >= 0:
    54                 try:
    55                     lan = opt.split("=")[1]
    56                     if len(lan) < 2:
    57                         self.language = lan
    58                 except Exception, e:
    59                     log.error("Bad language option: %s" % opt)
    60 
    61             elif opt.find("subtitle=") >= 0:
    62                 try:
    63                     sub = opt.split("=")[1]
    64                     if len(sub) < 2:
    65                         self.language = sub
    66                 except Exception, e:
    67                     log.error("Bad subtitle option: %s" % opt)
    68 
    69             elif opt.find("format=") >= 0:
    70                 try:
    71                     self.mpegopts = opt.split("=")[1]
    72                 except Exception, e:
    73                     log.error("Bad format option: %s" % opt)
    74 
    75             elif opt.find("outfile=") >= 0:
    76                 try:
    77                     self.transcode_local = opt.split("=")[1]
    78                 except Exception, e:
    79                     log.error("Bad outfile option: %s" % opt)
    80 
    81     # setup_opts()
    82 
    83 
    84     def run_mplayer(self):
    85         msg = self.filename
    86 
    87         if self.kind == "dvd":
    88             msg = "dvd://" + msg
    89 
    90         self.mplayer_pid = Popen([self.mplayer, self.filename, "1> %s" % os.devnull,\
    91                                   "2> %s" % os.devnull], stdout=PIPE, close_fds=True)
    92 
    93     # run_mplayer()
    94 
    95 
    96     def setup_mencoder(self):
    97         self.path = self.config.get("Mencoder", "path")
    98         mp = Popen([self.path], stdout=PIPE, close_fds=True)
    99 
   100         version = mp.stdout.read().split("MEncoder ")[1].split(" (C)")[0].split("-")[-1]
   101 
   102         if version > "4.1.1": self.mencoder_old = False
   103         else: self.mencoder_old = True
   104 
   105         os.kill(mp.pid, signal.SIGKILL)
   106         log.info("Mencoder version: %s" % version)
   107 
   108         if self.mencoder_old:
   109             try:
   110                 self.fifo = self.config.get("Mencoder", "fifo_path")
   111                 os.mkfifo(self.fifo)
   112             except Exception, e:
   113                 log.info("Fifo: %s" % e)
   114         else:
   115             self.fifo = "-"
   116 
   117     # setup_mencoder()
   118 
   119 
   120     def setup_audio(self):
   121 
   122         if self.acodec == "mp3lame":
   123             return "-oac mp3lame -lameopts cbr:br=%s vol=5" % self.abitrate
   124         else:
   125             return "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (\
   126                 self.acodec, self.abitrate)
   127 
   128     # setup_audio()
   129 
   130 
   131     def setup_video(self):
   132 
   133         video = ""
   134 
   135         video += " -of %s" % self.mux
   136         video += " -ofps %s" % self.fps
   137 
   138         if self.vcodec == "nuv" or self.vcodec == "xvid"\
   139                or self.vcodec == "qtvideo" or self.vcodec == "copy":
   140             video += " -ovc %s" % self.vcodec
   141         else:
   142             video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
   143                 self.vcodec, self.vbitrate)
   144 
   145         if self.mux == "mpeg" and self.mpegopts is not None:
   146             video += " -mpegopts format=%s" % self.mpegopts
   147 
   148         video += " -vf scale=%s:%s" % (self.width, self.height)
   149 
   150         return video
   151 
   152     # setup_video()
   153 
   154 
   155     def arg_append(self, args, options):
   156         l = shlex.split(options)
   157         for i in l:
   158             args.append(i)
   159 
   160     # arg_append()
   161 
   162 
   163     def setup_args(self, args):
   164 
   165         args.append(self.path)
   166 
   167         #args.append(self.filename)
   168         args.append("-")
   169 
   170         if self.language != None:
   171             self.arg_append(args, "-alang %s" % self.language)
   172 
   173         if self.subtitle != None:
   174             self.arg_append(args, "-slang %s" % self.subtitle)
   175             self.arg_append(args, "-subfps %s" % self.fps)
   176 
   177         self.arg_append(args, "-idx")
   178         self.arg_append(args, self.audio_opts)
   179         self.arg_append(args, self.video_opts)
   180 
   181         self.arg_append(args, "-really-quiet")
   182         self.arg_append(args, "-o %s" % self.fifo)
   183         self.arg_append(args, "2> %s" % os.devnull)
   184 
   185     # setup_args()
   186 
   187 
   188     def setup_filename(self, filename):
   189         try:
   190             self.kind, self.filename = filename.split("://")
   191         except:
   192             return (False, "Wrong filename protocol")
   193 
   194         if self.kind == "file":
   195             if not os.path.exists(self.filename):
   196                 msg = "File requested does not exist. SETUP failed."
   197                 log.error(msg)
   198                 return (False, msg)
   199 
   200         elif self.kind == "dvd":
   201             self.filename = "dvd://" + filename
   202 
   203         elif self.kind == "myth":
   204             self.filename = filename
   205             self.gst_pipe = os.pipe()
   206             print self.gst_pipe[0]
   207             print self.gst_pipe[1]
   208 
   209         return (True, "")
   210 
   211     # setup_filename()
   212 
   213 
   214     def setup_socket(self):
   215         if self.socket != None:
   216             self.socket = None
   217 
   218         self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
   219         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
   220 
   221         try:
   222             self.socket.bind( ('', self.port) )
   223             self.socket.listen(1)
   224         except Exception, e:
   225             log.error("Could not create socket: %s" % e)
   226             return (False, e)
   227 
   228         return (True, "")
   229 
   230     # setup_socket()
   231 
   232 
   233     '''
   234     MENCODER SETUP DESCRIPTION
   235     ===========================
   236 
   237     -> mux, vcodecs and acodecs
   238      |-> mencoder (-of | -ovc | -oac) help
   239 
   240     -> if used mpeg as mux:
   241      |-> to setup format: format=%s as an option at the end
   242 
   243     '''
   244 
   245 
   246     # good one: /tmp/dvb.mpg avi mpeg4 400 25 mp3lame 192 320 240
   247     # file:///tmp/dvb.mpg mpeg mpeg1video 400 25 mp2 192 320 240 format=mpeg1
   248     # dvd://4 mpeg mpeg1video 400 25 mp3lame 192 400 240 language=en local
   249     # file:///tmp/mpg/bad_day.mpg avi mpeg4 400 25 mp3 192 320 240
   250 
   251     def setup(self, filename, mux, vcodec, vbitrate,\
   252               fps, acodec, abitrate, width, height, port, options):
   253 
   254         if self.args != []:
   255             self.do_cleanup()
   256 
   257         self.mux = mux
   258         self.vcodec = vcodec
   259         self.vbitrate = vbitrate
   260         self.fps = fps
   261         self.acodec = acodec
   262         self.abitrate = abitrate
   263         self.width = width
   264         self.height = height
   265         self.port = int(port)
   266 
   267         self.setup_mencoder()
   268 
   269         ret_val = self.setup_filename(filename)
   270 
   271         if not ret_val[0]:
   272             return ret_val
   273 
   274         self.setup_opts(options)
   275         self.audio_opts = self.setup_audio()
   276         self.video_opts = self.setup_video()
   277         self.setup_args(self.args)
   278 
   279         ret_val = self.setup_socket()
   280         return ret_val
   281 
   282     # setup()
   283 
   284     def play_loop(self, conn):
   285         data = self.pout.read(4096)
   286 
   287         conn.settimeout(5)
   288         retry = 0
   289 
   290         if not self.transcode_local:
   291             while data != "" and retry < 5:
   292                 try:
   293                     conn.send(data)
   294                     r, w, x = select([conn], [], [], 0)
   295                     if conn in r:
   296                         back = conn.recv(1024)
   297                         if back == "OK" and self.mplayer and not self.mplayer_pid:
   298                             self.run_mplayer()
   299 
   300                 except socket.error, e:
   301                     log.error("Socket error: %s" % e)
   302                     retry += 1
   303 
   304                 data = self.pout.read(4096)
   305 
   306         else:
   307             local = open(self.transcode_local, "w")
   308             total = os.path.getsize(self.filename)
   309             partial = 4096
   310 
   311             while data != "":
   312                 try:
   313                     local.write(data)
   314                 except Exception, e:
   315                     log.error("Write error: %s" % e)
   316 
   317                 data = self.pout.read(4096)
   318                 partial += len(data)
   319                 conn.send("%.2f\n" % (partial * 100 / total) )
   320 
   321             local.close()
   322             conn.send("DONE\n")
   323 
   324         return retry
   325 
   326     # play_loop()
   327 
   328 
   329     def play(self):
   330 
   331         if self.gst_pipe:
   332             try:
   333                 gst = [ lib.which("gst-launch-0.10"), "--gst-debug-level=0" ]
   334                 self.arg_append(gst, "mythtvsrc location=%s" % self.filename)
   335                 self.arg_append(gst, "! fdsink fd=2")
   336                 self.gst_pid = Popen(gst, close_fds=True)
   337                 log.info("Running Gstreamer: %s" % gst);
   338             except Exception, e:
   339                 msg = "Could not init Gstreamer: %s" % e
   340                 log.error(msg)
   341                 return (False, msg)
   342 
   343 
   344         log.info("Starting Mencoder: %s" % self.args )
   345         try:
   346             if not self.gst_pipe:
   347                 self.stdin = open(self.filename)
   348             else:
   349                 self.stdin = self.gst_pid.stdout
   350 
   351             self.mencoder_pid = Popen(self.args, stdin=self.stdin, stdout=PIPE, close_fds=True)
   352         except Exception, e:
   353             msg = "Could not init Mencoder: %s" % e
   354             log.error(msg)
   355             return (False, msg)
   356 
   357         if self.mencoder_old: self.pout = open(self.fifo)
   358         else: self.pout = self.mencoder_pid.stdout
   359 
   360         self.child_pid = os.fork()
   361 
   362         if self.child_pid == 0:
   363             conn, addr = self.socket.accept()
   364 
   365             log.info("Sending Data to client: %s" % addr[0])
   366             retry = self.play_loop(conn)
   367 
   368             if retry < 5:
   369                 log.info("Finished sending Data to client: %s" % addr[0])
   370             else:
   371                 log.error("Client timed out, retried more than %s times" % retry)
   372 
   373             os.kill(self.mencoder_pid.pid, signal.SIGKILL)
   374             sys.exit(0)
   375 
   376         return (True, "")
   377 
   378     # play()
   379 
   380 
   381     def stop(self):
   382         try:
   383 
   384             if self.mencoder_pid:
   385                 os.kill(self.mencoder_pid.pid, signal.SIGTERM)
   386                 self.mencoder_pid = None
   387 
   388             if self.mplayer_pid:
   389                 os.kill(self.mplayer_pid.pid, signal.SIGTERM)
   390                 self.mplayer_pid = None
   391 
   392             if self.socket:
   393                 self.socket.close()
   394                 self.socket = None
   395 
   396             if self.child_pid:
   397                 os.kill(self.child_pid, signal.SIGTERM)
   398                 self.child_pid = None
   399 
   400             if self.gst_pid:
   401                 os.kill(self.gst_pid.pid, signal.SIGTERM)
   402                 self.gst_pid = None
   403 
   404             self.do_cleanup()
   405 
   406             os.wait()
   407 
   408         except Exception, e:
   409             log.error("Stop error: %s" % e)
   410 
   411     # stop()