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