renatofilho@484: import os
renatofilho@484: import sys
renatofilho@484: import lib
renatofilho@484: import time
morphbr@516: import shlex
renatofilho@484: import signal
renatofilho@484: import socket
renatofilho@484: import ConfigParser
morphbr@516: import logging as log
renatofilho@484: 
renatofilho@484: from select import *
renatofilho@484: from subprocess import *
renatofilho@484: 
morphbr@497: class Media(object):
renatofilho@484: 
renatofilho@484:     def __init__(self, config):
renatofilho@484: 
renatofilho@484:         self.config = config
morphbr@516:         self.path = ""
morphbr@516:         self.args = []
morphbr@516:         self.language = None
morphbr@516:         self.subtitle = None
morphbr@516:         self.mpegopts = None
renatofilho@484:         self.socket = None
renatofilho@484:         self.child_pid = None
renatofilho@484:         self.mplayer = None
renatofilho@484:         self.mencoder_pid = None
renatofilho@484:         self.mplayer_pid = None
renatofilho@484: 
morphbr@514:     # __init__
morphbr@514: 
morphbr@516:     def setup_opts(self, options):
renatofilho@484: 
renatofilho@484:         for opt in options:
renatofilho@484: 
morphbr@504:             if opt == "local":
morphbr@511:                 self.mplayer = lib.which("mplayer")
renatofilho@484: 
morphbr@497:             elif opt.find("language=") >= 0:
renatofilho@484:                 try:
morphbr@516:                     lan = opt.split("=")[1]
morphbr@516:                     if len(lan) < 2:
morphbr@516:                         self.language = lan
morphbr@516:                 except Exception, e:
morphbr@516:                     log.error("Bad language option: %s" % e)
morphbr@516: 
morphbr@516:             elif opt.find("subtitle=") >= 0:
morphbr@516:                 try:
morphbr@516:                     sub = opt.split("=")[1]
morphbr@516:                     if len(sub) < 2:
morphbr@516:                         self.language = sub
morphbr@516:                 except Exception, e:
morphbr@516:                     log.error("Bad subtitle option: %s" % e)
renatofilho@484: 
morphbr@497:             elif opt.find("format=") >= 0:
morphbr@497:                 try:
morphbr@516:                     self.mpegopts = opt.split("=")[1]
morphbr@516:                 except Exception, e:
morphbr@516:                     log.error("Bad format option: %s" % e)
morphbr@497: 
morphbr@516:     # setup_opts
renatofilho@484: 
renatofilho@484:     def run_mplayer(self):
morphbr@514:         msg = self.filename
morphbr@497: 
morphbr@498:         if self.kind == "dvd":
renatofilho@484:             msg = "dvd://" + msg
renatofilho@484: 
morphbr@514:         self.mplayer_pid = Popen([self.mplayer, self.filename, "1> %s" % os.devnull,\
morphbr@514:                                   "2> %s" % os.devnull], stdout=PIPE, close_fds=True)
morphbr@514: 
morphbr@514:     # run_mplayer
morphbr@514: 
morphbr@504:     def setup_mencoder(self):
morphbr@499:         self.path = self.config.get("Mencoder", "path")
morphbr@514:         mp = Popen([self.path], stdout=PIPE, close_fds=True)
morphbr@514: 
morphbr@514:         version = mp.stdout.read().split("MEncoder ")[1].split(" (C)")[0].split("-")[-1]
morphbr@499: 
morphbr@499:         if version > "4.1.1": self.mencoder_old = False
morphbr@499:         else: self.mencoder_old = True
morphbr@499: 
morphbr@514:         os.kill(mp.pid, signal.SIGKILL)
morphbr@514:         log.info("Mencoder version: %s" % version)
morphbr@499: 
morphbr@499:         if self.mencoder_old:
morphbr@516:             try:
morphbr@516:                 self.fifo = self.config.get("Mencoder", "fifo_path")
morphbr@516:                 os.mkfifo(self.fifo)
morphbr@516:             except Exception, e:
morphbr@516:                 log.info("Fifo: %s" % e)
morphbr@499:         else:
morphbr@499:             self.fifo = "-"
renatofilho@484: 
morphbr@514:     # setup_mencoder
morphbr@514: 
morphbr@516:     def setup_audio(self):
morphbr@514: 
morphbr@516:         if self.acodec == "mp3lame":
morphbr@514:             return "-oac mp3lame -lameopts cbr:br=%s vol=5" % self.abitrate
morphbr@514:         else:
morphbr@516:             return "-oac lavc -lavcopts acodec=%s:abitrate=%s" % (\
morphbr@514:                 self.acodec, self.abitrate)
morphbr@514: 
morphbr@516:     # setup_audio
morphbr@516: 
morphbr@516: 
morphbr@516:     def setup_video(self):
morphbr@516: 
morphbr@516:         video = ""
morphbr@516: 
morphbr@516:         video += " -of %s" % self.mux
morphbr@516:         video += " -ofps %s" % self.fps
morphbr@516: 
morphbr@516:         if self.vcodec == "nuv" or self.vcodec == "xvid"\
morphbr@516:                or self.vcodec == "qtvideo" or self.vcodec == "copy":
morphbr@516:             video += " -ovc %s" % self.vcodec
morphbr@516:         else:
morphbr@516:             video += " -ovc lavc -lavcopts vcodec=%s:vbitrate=%s" % (
morphbr@516:                 self.vcodec, self.vbitrate)
morphbr@516: 
morphbr@516:         if self.mux == "mpeg" and self.mpegopts is not None:
morphbr@516:             video += " -mpegopts format=%s" % self.mpegopts
morphbr@516: 
morphbr@516:         video += " -vf scale=%s:%s" % (self.width, self.height)
morphbr@516: 
morphbr@516:         return video
morphbr@516: 
morphbr@516:     # setup_video
morphbr@516: 
morphbr@516: 
morphbr@516:     def arg_append(self, args, options):
morphbr@516:         l = shlex.split(options)
morphbr@516:         for i in l:
morphbr@516:             args.append(i)
morphbr@516: 
morphbr@516:     # arg_append
morphbr@516: 
morphbr@516:     def setup_args(self, args):
morphbr@516: 
morphbr@516:         args.append(self.path)
morphbr@516:         args.append(self.filename)
morphbr@516: 
morphbr@516:         if self.language != None:
morphbr@516:             self.arg_append(args, "-alang %s" % self.language)
morphbr@516: 
morphbr@516:         if self.subtitle != None:
morphbr@516:             self.arg_append(args, "-slang %s" % self.subtitle)
morphbr@516:             self.arg_append(args, "-subfps %s" % self.fps)
morphbr@516: 
morphbr@516:         self.arg_append(args, "-idx")
morphbr@516:         self.arg_append(args, self.audio_opts)
morphbr@516:         self.arg_append(args, self.video_opts)
morphbr@516: 
morphbr@516:         self.arg_append(args, "-really-quiet")
morphbr@516:         self.arg_append(args, "-o %s" % self.fifo)
morphbr@516:         self.arg_append(args, "2> %s" % os.devnull)
morphbr@516: 
morphbr@516:     # setup_args
morphbr@516: 
morphbr@504:     def setup_filename(self, filename):
morphbr@504:         try:
morphbr@504:             self.kind, self.filename = filename.split("://")
morphbr@504:         except:
morphbr@504:             return (False, "Wrong filename protocol")
morphbr@504: 
morphbr@504:         if self.kind == "file":
morphbr@504:             if not os.path.exists(self.filename):
morphbr@504:                 msg = "File requested does not exist. SETUP failed."
morphbr@514:                 log.error(msg)
morphbr@504:                 return (False, msg)
morphbr@504: 
morphbr@504:         elif self.kind == "dvd":
morphbr@516:             self.filename = "dvd://" + filename
morphbr@504: 
morphbr@504:         return (True, "")
morphbr@504: 
morphbr@514:     # setup_filename
morphbr@514: 
morphbr@516:     def setup_socket(self):
morphbr@516:         if self.socket != None:
morphbr@516:             del(self.socket)
morphbr@514: 
morphbr@516:         self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
morphbr@516:         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
morphbr@514: 
morphbr@514:         try:
morphbr@516:             self.socket.bind( ('', self.port) )
morphbr@516:             self.socket.listen(1)
morphbr@514:         except Exception, e:
morphbr@514:             log.error("Could not create socket: %s" % e)
morphbr@514:             return (False, e)
morphbr@514: 
morphbr@514:         return (True, "")
morphbr@514: 
morphbr@516:     # setup_socket
morphbr@516: 
morphbr@504:     '''
morphbr@504:     MENCODER SETUP DESCRIPTION
morphbr@504:     ===========================
morphbr@504: 
morphbr@504:     -> mux, vcodecs and acodecs
morphbr@504:      |-> mencoder (-of | -ovc | -oac) help
morphbr@504: 
morphbr@504:     -> if used mpeg as mux:
morphbr@504:      |-> to setup format: format=%s as an option at the end
morphbr@504: 
morphbr@504:     '''
morphbr@504: 
morphbr@516: 
morphbr@516:     # good one: /tmp/dvb.mpg avi mpeg4 400 25 mp3lame 192 320 240
morphbr@516:     # file:///tmp/dvb.mpg mpeg mpeg1video 400 25 mp2 192 320 240 format=mpeg1
morphbr@516:     # dvd://4 mpeg mpeg1video 400 25 mp3lame 192 400 240 language=en local
morphbr@516:     # file:///tmp/mpg/bad_day.mpg avi mpeg4 400 25 mp3 192 320 240
morphbr@516: 
morphbr@504:     def setup(self, filename, mux, vcodec, vbitrate,\
morphbr@504:               fps, acodec, abitrate, width, height, port, options):
morphbr@504: 
morphbr@504:         self.mux = mux
morphbr@504:         self.vcodec = vcodec
morphbr@504:         self.vbitrate = vbitrate
morphbr@504:         self.fps = fps
morphbr@504:         self.acodec = acodec
morphbr@504:         self.abitrate = abitrate
morphbr@504:         self.width = width
morphbr@504:         self.height = height
morphbr@504:         self.port = int(port)
morphbr@504: 
morphbr@504:         self.setup_mencoder()
morphbr@516: 
morphbr@504:         ret_val = self.setup_filename(filename)
morphbr@504:         if not ret_val[0]:
morphbr@504:             return ret_val[1]
morphbr@504: 
morphbr@516:         self.setup_opts(options)
morphbr@516:         self.audio_opts = self.setup_audio()
morphbr@516:         self.video_opts = self.setup_video()
morphbr@516:         self.setup_args(self.args)
renatofilho@484: 
morphbr@516:         ret_val = self.setup_socket()
morphbr@514:         if not ret_val[0]:
morphbr@514:             return ret_val[1]
renatofilho@484: 
morphbr@514:         return True
renatofilho@484: 
morphbr@514:     # setup
renatofilho@484: 
renatofilho@484:     def play(self):
renatofilho@484: 
morphbr@516:         log.info("Starting Mencoder: %s" % self.args )
morphbr@499: 
morphbr@516:         try:
morphbr@516:             self.mencoder_pid = Popen(self.args, stdout=PIPE, close_fds=True)
morphbr@516:         except Exception, e:
morphbr@516:             msg = "Could not init Mencoder: %s" % e
morphbr@516:             log.error(msg)
morphbr@516:             return msg
renatofilho@484: 
morphbr@516:         if self.mencoder_old: self.pout = open(self.fifo)
morphbr@516:         else: self.pout = self.mencoder_pid.stdout
renatofilho@484: 
renatofilho@484:         self.child_pid = os.fork()
renatofilho@484: 
morphbr@498:         if self.child_pid == 0:
morphbr@516:             conn, addr = self.socket.accept()
morphbr@516:             log.info("Sending Data to client: %s" % addr[0])
renatofilho@484: 
morphbr@516:             data = self.pout.read(4096)
morphbr@499: 
renatofilho@484:             conn.settimeout(5)
renatofilho@484:             retry = 0
renatofilho@484: 
morphbr@498:             while data != "" and retry < 5:
renatofilho@484:                 try:
renatofilho@484:                     conn.send(data)
renatofilho@484:                     r, w, x = select([conn], [], [], 0)
renatofilho@484:                     if conn in r:
renatofilho@484:                         back = conn.recv(1024)
morphbr@498:                         if back == "OK" and self.mplayer and not self.mplayer_pid:
renatofilho@484:                             self.run_mplayer()
renatofilho@484: 
morphbr@516:                 except socket.error, e:
morphbr@516:                     log.error("Socket error: %s" % e)
renatofilho@484:                     retry += 1
renatofilho@484: 
morphbr@516:                 data = self.pout.read(4096)
renatofilho@484: 
morphbr@497:             if retry < 5:
morphbr@516:                 log.info("Finished sending Data to client: %s" % addr[0])
renatofilho@484:             else:
morphbr@516:                 log.error("Client timed out, retried more than %s times" % retry)
renatofilho@484: 
morphbr@516:             os.kill(self.mencoder_pid.pid, signal.SIGKILL)
renatofilho@484:             sys.exit(0)
renatofilho@484: 
morphbr@516:         return True
morphbr@516: 
morphbr@516:         # play
morphbr@516: 
renatofilho@484: 
renatofilho@484:     def stop(self):
renatofilho@484:         try:
renatofilho@484: 
morphbr@516:             if self.mencoder_pid:
morphbr@516:                 os.kill(self.mencoder_pid.pid, signal.SIGKILL)
morphbr@516:                 self.mencoder_pid = None
renatofilho@484: 
morphbr@516:             if self.mplayer_pid:
morphbr@516:                 os.kill(self.mplayer_pid.pid, signal.SIGKILL)
morphbr@516:                 self.mplayer_pid = None
morphbr@516: 
morphbr@516:             if self.socket != None:
morphbr@516:                 self.socket.close()
morphbr@516: 
morphbr@498:             if self.child_pid != None:
morphbr@516:                 os.kill(self.child_pid, signal.SIGKILL)
morphbr@516: 
morphbr@516:         except Exception, e:
morphbr@516:             log.error("Stop error: %s" % e)
morphbr@516: 
morphbr@516:     # stop