##
# convert a stream to a low-bandwidth mp3 stream.
# works pretty well with FStream on iPhone via edge
# public domain.  any improvements welcome!
# ian@wienand.org
##

# stream to convert
REMOTE_URL = 'mms://media3.abc.net.au/702Sydney'

# the port to listen on
PORT = 8000

# temporary fifos used for communications
WAV_FIFO = '/tmp/incoming.wav'
MP3_FIFO = '/tmp/output.mp3'

# mplayer options.  This should put pcm data into the fifo,
# which is converted to mp3 by lame and sent out by the server
MPLAYER=['/usr/bin/mplayer', '-really-quiet',
         '-ao', 'pcm:file="%s"' % WAV_FIFO,
         '-vc', 'dummy' '-vo', 'null',
         '-noframedrop', REMOTE_URL]

# lame options.  This is a 24k bit stream, downsampled to mono.  The
# server serves up the output from this
LAME=['/usr/bin/lame', '--quiet', '-ms', '-a', '-b 24', WAV_FIFO, MP3_FIFO]

import string, time

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import socket
import subprocess
import signal
import os
import stat

# The theory is, because this never really returns, it just keeps on
# pumping out data until the remote end terminates the connection.  At
# that point we should get a socket error and clean oursevles up.  If
# anyone else trys to connect in the mean time, it will be blocked in
# the HTTPServer.  Multiple client support is left as an exercise for
# the reader :)
#
# I believe some remote ends require HTTP/1.1 and thus require a
# content-length header.  I think the standard way is to fake some
# really big number.  This doesn't seem necessary for my applications
# which are happy with http/1.0

class MP3Handler(BaseHTTPRequestHandler):

    def do_GET(self):
        try:
            wav_pid = subprocess.Popen(MPLAYER,
                                       stdin=subprocess.PIPE)
            print "mplayer running as %d" % wav_pid.pid
            lame_pid = subprocess.Popen(LAME)
            print "lame running as %d" % lame_pid.pid

            f = open(MP3_FIFO)
            self.send_response(200)
            self.send_header('Content-type', 'audio/mpeg')
            self.end_headers()
            while True:
                # arbitrary, but seems to work OK
                data = f.read(1024)
                self.wfile.write(data)
            return

        except:
            print "connection lost"
            wav_pid.communicate("q")
            os.kill(lame_pid.pid, signal.SIGKILL)
            os.wait()
            f.close()
            print "cleanup complete, ready"

# setup the fifos used in communication between mplayer and lame
def check_fifos():
    try:
        wav_stat = os.stat(WAV_FIFO)
    except:
        try:
            print "Creating WAV fifo %s" % WAV_FIFO
            os.mkfifo(WAV_FIFO)
        except:
            print "Can not create WAV FIFO!"
            exit(1)
    else:
        if not stat.S_ISFIFO(wav_stat.st_mode):
            print "WAV output is not a FIFO"
            exit(1)

    try:
        mp3_stat = os.stat(MP3_FIFO)
    except:
        try:
            print "Creating MP3 fifo %s" % MP3_FIFO
            os.mkfifo(MP3_FIFO)
        except:
            print "Can not create MP3 FIFO!"
            exit(1)
    else:
        if not stat.S_ISFIFO(mp3_stat.st_mode):
            print "MP3 input is not a FIFO"
            exit(1)


def main():

    # 10 seconds seems about to avoid drop-outs, but correctly reset
    # when the remote end doesn't 
    socket.setdefaulttimeout(10)

    check_fifos()

    try:
        server = HTTPServer(('', PORT), MP3Handler)
        print "Serving <%s> on port %d" % (REMOTE_URL, PORT)
        server.serve_forever()
    finally:
        print "Cleaning up"
        server.socket.close()
        os.unlink(WAV_FIFO)
        os.unlink(MP3_FIFO)
        
if __name__ == '__main__':
    main()
        
