View Single Post
Posts: 3 | Thanked: 4 times | Joined on Dec 2007
#3
I got some time to mess with this again today, and you're right lardman, it did have extra data in there. The video and audio streams are broken up by Content-Length headers. After I figured out the format of the header, I wrote a Python script to try and extract out the unnecessary data. Unfortunately, even after stripping out the header I was still getting popping. The beginning of the data after the header doesn't have anything resembling a WAV header, so I assume it's some custom control data the ActiveX program uses (sync data?). Anyway, as a hack I just stripped out extra bytes until the popping stopped. Not really the best solution, but the audio seems to sound right now.

Once I got that working I tried using the same code to extract the video data, to make things easier on the decoder. Unfortunately, it didn't work. For some reason mplayer thinks it ran out of data before it even gets any. Setting the cache size didn't seem to help. So, in the interest of getting something working I spawned off a process letting mplayer handle the video stream, while I piped the cleaned up audio data to another instance. It worked, but it's pretty rough. I tried running mplayer streaming the video feed overnight last week, and when I woke up in the morning it was desynched an not rendering anymore. That's why I was hoping to process the video data myself too, so I could catch that and restart it.

Anyway, at this point I'm not too confident that I'll get something solid working. I've got a Aspire One on order, and I may end up just using that so I can use the ActiveX crap. It's too bad these camera manufacturers don't settle on some standard format. Flash would be pretty sweet, because then I could play it on my Chumby.

I'm including my very rough code below, in case anyone else ends up trying to do something like this.

Code:
import urllib2
import subprocess

ServerAddr = '192.168.1.100'
UserName = 'admin'
Password = 'admin'

AudioUrl = 'http://' + ServerAddr + '/cgi/audio/audio.cgi'
VideoUrl = 'http://' + ServerAddr + '/cgi/mjpg/mjpeg.cgi'

VideoCmd = 'mplayer -demuxer lavf -'
AudioCmd = 'mplayer -rawaudio channels=1:rate=8000:bitrate=128 -demuxer rawaudio -'

# Passing the username and password with the URL doesn't seem to work
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, ServerName, UserName, Password)
authhandler = urllib2.HTTPBasicAuthHandler(passman)
opener = urllib2.build_opener(authhandler)
urllib2.install_opener(opener)

def playStream(url, cmdLine, strip=0):
    print 'Opening ' + url
    stream = urllib2.urlopen(url)

    goodData = True

    p = subprocess.Popen(cmdLine, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while goodData:
        boundaryStr = stream.readline()
        if boundaryStr != '--myboundary\r\n':
            print 'Bad boundary: ' + boundaryStr
            break

        lengthStr = stream.readline()
        if not lengthStr.startswith('Content-Length: '):
            print 'Bad length: ' + lengthStr
            break

        length = int(lengthStr.strip().split()[1])

        emptyStr = stream.readline()

        print 'Good frame, length %d' % length
        rawData = stream.read(length)
        if len(rawData) == length:
            try:
                p.stdin.write(rawData[strip:])
            except IOError:
                break
        else:
            print 'Bad read length: %d' % len(rawData)
            break

    p.stdin.close()
    print p.stdout.read()
    p.stdout.close()
    p.wait()

videoCmd = 'mplayer -demuxer lavf http://%s:%s@%s/cgi/mjpg/mjpeg.cgi?foo.mjpg' % (UserName, Password, ServerAddr)
p = subprocess.Popen(videoCmd, shell=True)
#playStream(VideoUrl, VideoCmd)
playStream(AudioUrl, AudioCmd, 24)