gmyth-stream/server/plugins/media/mencoder.py
author rosfran
Mon Apr 16 16:02:22 2007 +0100 (2007-04-16)
branchtrunk
changeset 553 424babd6b788
parent 542 0c3e3011a942
child 560 4a18d3961be9
permissions -rw-r--r--
[svn r558] Sets the mythtv source "location" property, with the new pathname.
     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                 print data
   306 
   307         else:
   308             local = open(self.transcode_local, "w")
   309             total = os.path.getsize(self.filename)
   310             partial = 4096
   311 
   312             while data != "":
   313                 try:
   314                     local.write(data)
   315                 except Exception, e:
   316                     log.error("Write error: %s" % e)
   317 
   318                 data = self.pout.read(4096)
   319                 partial += len(data)
   320                 conn.send("%.2f\n" % (partial * 100 / total) )
   321 
   322             local.close()
   323             conn.send("DONE\n")
   324 
   325         return retry
   326 
   327     # play_loop()
   328 
   329 
   330     def play(self):
   331 
   332         log.info("Starting Mencoder: %s" % self.args )
   333 
   334         if self.gst_pipe:
   335             try:
   336                 gst = [ lib.which("gst-launch-0.10") ]
   337                 self.arg_append(gst, "gnomevfssrc location=%s" % self.filename)
   338                 self.arg_append(gst, "! fdsink fd=%d" % self.gst_pipe[1])
   339                 self.gst_pid = Popen(gst, stdout=self.gst_pipe[1], close_fds=True)
   340                 log.info("Running Gstreamer: %s" % gst);
   341             except Exception, e:
   342                 msg = "Could not init Gstreamer: %s" % e
   343                 log.error(msg)
   344                 return (False, msg)
   345 
   346         try:
   347             if not self.gst_pipe:
   348                 self.stdin = open(self.filename)
   349             else:
   350                 self.stdin = self.gst_pipe[0]
   351 
   352             self.mencoder_pid = Popen(self.args, stdin=self.stdin, stdout=PIPE, close_fds=True)
   353         except Exception, e:
   354             msg = "Could not init Mencoder: %s" % e
   355             log.error(msg)
   356             return (False, msg)
   357 
   358         if self.mencoder_old: self.pout = open(self.fifo)
   359         else: self.pout = self.mencoder_pid.stdout
   360 
   361         self.child_pid = os.fork()
   362 
   363         if self.child_pid == 0:
   364             conn, addr = self.socket.accept()
   365 
   366             log.info("Sending Data to client: %s" % addr[0])
   367             retry = self.play_loop(conn)
   368 
   369             if retry < 5:
   370                 log.info("Finished sending Data to client: %s" % addr[0])
   371             else:
   372                 log.error("Client timed out, retried more than %s times" % retry)
   373 
   374             os.kill(self.mencoder_pid.pid, signal.SIGKILL)
   375             sys.exit(0)
   376 
   377         return (True, "")
   378 
   379     # play()
   380 
   381 
   382     def stop(self):
   383         try:
   384 
   385             if self.mencoder_pid:
   386                 os.kill(self.mencoder_pid.pid, signal.SIGTERM)
   387                 self.mencoder_pid = None
   388 
   389             if self.mplayer_pid:
   390                 os.kill(self.mplayer_pid.pid, signal.SIGTERM)
   391                 self.mplayer_pid = None
   392 
   393             if self.socket:
   394                 self.socket.close()
   395                 self.socket = None
   396 
   397             if self.child_pid:
   398                 os.kill(self.child_pid, signal.SIGTERM)
   399                 self.child_pid = None
   400 
   401             if self.gst_pid:
   402                 os.kill(self.gst_pid.pid, signal.SIGTERM)
   403                 self.gst_pid = None
   404 
   405             self.do_cleanup()
   406 
   407             os.wait()
   408 
   409         except Exception, e:
   410             log.error("Stop error: %s" % e)
   411 
   412     # stop()