View Single Post
Posts: 3 | Thanked: 4 times | Joined on Dec 2007
#8
Hey, been a while since I checked this. Quipper, I'll have to disagree with you on always being able to hear the baby. He's in a room upstairs, and if either door is closed it can be hard to hear. I did end up buying a regular audio monitor like you suggested though. After my wireless crapped out a few times I figured I needed something more reliable. Thing is, the audio monitor isn't perfect either. Once a month or so it will be totally blown out by random static (ham radio operator or something). That's when it's nice to just use the webcam. Also, the video feed is nice if you hear something through the audio monitor and just wanna see what's up (usually he's eating his blanket).

I did actually improve the script a lot. I switched to pygame due to all my problems with mplayer. Unfortunately, on the N800 neither pygame or PIL have JPEG support compiled in. (PIL not having it is a big WTF.) So, my script won't work on the N800, I run it on my Aspire One. Also, even with perfectly decoded wave data pygame still has some issue playing the audio for more than a second or so (it's not really set up for streaming the way I'm trying to do it). Here's the latest code though, with misc crap scattered around, for anyone who wants to play with it.

Code:
import urllib2
import socket
import pygame
from pygame.locals import *
import cStringIO
import sys
import wave
import struct

ServerAddr = '192.168.1.13'
UserName = 'admin'
Password = 'admin'

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

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

class ScreenHelper:
    screen        = None
    fullScreen    = True
    windowedRes   = (640, 480)
    fullScreenRes = windowedRes
    curScreenRes  = windowedRes

    def __init__(self):
        pygame.display.set_caption('CamViewer')

        # If the current screen res is a fullscreen mode, set our fullscreen res to that
        info = pygame.display.Info()
        if (info.current_w, info.current_h) in pygame.display.list_modes():
            self.fullScreenRes = (info.current_w, info.current_h)

        self.toggleFullscreen()

    def toggleFullscreen(self):
        if self.fullScreen:
            self.screen = pygame.display.set_mode(self.windowedRes)
            self.curScreenRes = self.windowedRes
            self.fullScreen = False
        else:
            self.screen = pygame.display.set_mode(self.fullScreenRes, pygame.FULLSCREEN)
            self.curScreenRes = self.fullScreenRes
            self.fullScreen = True

    def blit(self, surface):
        offset = (0, 0)

        # If the surface doesn't match our res, scale it, maintaining aspect ratio
        if surface.get_size() != self.curScreenRes:
            widthRatio = float(self.curScreenRes[0]) / float(surface.get_width())
            heightRatio = float(self.curScreenRes[1]) / float(surface.get_height())

            scaledSize = None
            if widthRatio > heightRatio:
                scaledSize = (int(surface.get_width() * heightRatio), int(surface.get_height() * heightRatio))
                offset = ((self.curScreenRes[0] - scaledSize[0]) / 2, 0)
            else:
                scaledSize = (int(surface.get_width() * widthRatio), int(surface.get_height() * widthRatio))
                offset = (0, (self.curScreenRes[1] - scaledSize[1]) / 2)

            surface = pygame.transform.smoothscale(surface, scaledSize)

        # Blit the surface and update the display
        self.screen.blit(surface, offset)
        pygame.display.update()

# Reads a chunk of data from an Airlink camera http stream
class CamDataChunk:
    frame    = 0
    size     = 0
    tag1     = 0
    time     = 0
    unknown1 = 0
    tag2     = 0
    unknown2 = 0
    data     = ''
    
    def read(self, stream):
        try:
            boundaryStr = stream.readline().decode()
            if boundaryStr != '--myboundary\r\n':
                print('Bad boundary: ' + boundaryStr)
                return False

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

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

            emptyStr = stream.readline()

            #print('Good frame, length %d' % length)

            dataLeft = length
            chunkData = ''

            while dataLeft > 0:
                data = stream.read(dataLeft)
                amtRead = len(data)
                dataLeft -= amtRead

                if amtRead == 0:
                    print('Ran out of data')
                    return False

                chunkData += data

            headerSize = 28

            # Header format:
            # int ~synched, +1 per frame
            # int data size? varies jpg, constant audio
            # int J000 for JPG, A000 for wav            
            # int ~synched, +1 per sec?
            # int ???
            # int constant (0x00990201 video, 0x12d20700 audio)
            # int constant (0 both)
            chunkHeader = struct.unpack('iiiiiii', chunkData[:headerSize])
            self.frame    = chunkHeader[0]
            self.size     = chunkHeader[1]
            self.tag1     = chunkHeader[2]
            self.time     = chunkHeader[3]
            self.unknown1 = chunkHeader[4]
            self.tag2     = chunkHeader[5]
            self.unknown2 = chunkHeader[6]
            self.data     = chunkData[headerSize:]

            return True
        except socket.error, (errno, strerror):
            print "Socket error(%s): %s" % (errno, strerror)
            return False
 
def play(screen):
    videoStream = urllib2.urlopen(VideoUrl)
    #audioStream = urllib2.urlopen(AudioUrl)

    videoChunk = CamDataChunk()
    #audioChunk = CamDataChunk()

    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                return

            if event.type == pygame.KEYDOWN:
                if event.key == K_f:
                    screen.toggleFullscreen()
                elif event.key == K_ESCAPE:
                    return

        if videoChunk.read(videoStream) == False:
            print 'Bad video chunk'
            return
        #if audioChunk.read(audioStream) == False:
        #    print 'Bad audio chunk'
        #    return
        
        # Display the current frame
        frameSurface = pygame.image.load(cStringIO.StringIO(videoChunk.data)).convert()
        screen.blit(frameSurface)

        # Play the current audio sample
        #soundObj = pygame.mixer.Sound(waveFile.getvalue())
        #soundObj.play()

pygame.init()

screen = ScreenHelper()

play(screen)

pygame.display.quit()