Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I want to stream a webcam feed using socket programming in Python

This is my code:

server.py:

#The server receives the data

import socket
from PIL import Image
import pygame,sys
import pygame.camera
from pygame.locals import *
import time

host = "localhost"
port = 1890
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen(1)
conn, addr = s.accept()
print "connected by",addr

screen = pygame.display.set_mode((640,480))

while 1:
        data = conn.recv(921637)
        image = pygame.image.fromstring(data,(640,480),"RGB")
        screen.blit(image,(0,0))
        pygame.display.update()
        if not image: 
               break;
        conn.send(data)
conn.close()

client.py

#The client sends the data

import socket
from PIL import Image
import pygame,sys
import pygame.camera
from pygame.locals import *
import time

host = "localhost"
port = 1890
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))


pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0",(640,480))
cam.start()

while 1:
        image = cam.get_image()
        data = pygame.image.tostring(image,"RGB")
        s.sendall(data)     

s.close()
print "recieved", repr(data)

Just to test, I tried the following code and it's working fine, but the above does not...

Working code when implemeted without sockets: camcapture.py

import sys
import time
import pygame
import pygame.camera
from pygame.locals import *

pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0",(640,480))
cam.start()

screen = pygame.display.set_mode((640,480))

while 1:
        image = cam.get_image()
        data = pygame.image.tostring(image,"RGB")
        img = pygame.image.fromstring(data,(640,480),"RGB")
        screen.blit(img,(0,0))
        pygame.display.update()

The error is:

image = pygame.image.fromstring(data,(640,480),"RGB")
ValueError: String length does not equal format and resolution size

Where did I go wrong?

like image 765
ram Avatar asked Feb 21 '13 13:02

ram


Video Answer


1 Answers

The problem is not the camera.

The problem is that you send a very large string over the socket and you incorrectly assume that you can read the entire string at once with conn.recv(921637).

You'll have to call recv multiple times to receive all over your data. Try printing the length of data you send in client.py and print the length of data in server.py before calling pygame.image.fromstring and you'll see.

There are several ways to solve this:

  • make a new connection for each image you send
  • send the size of your data so the server knows how much data to read
  • use some kind of end marker

Here's a simple example:

sender:

import socket
import pygame
import time

host = "localhost"
port = 1890
pygame.init()
image = pygame.surface.Surface((640, 480))
i=0
j=0
while 1:
    image.fill((i,j,0))
    i+=10
    j+=5
    if i >= 255: i = 0
    if j >= 255: j = 0
    data = pygame.image.tostring(image,"RGB")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    s.sendall(data)
    s.close()
    time.sleep(0.5)

receiver:

import socket
import pygame

host="localhost"
port=1890

screen = pygame.display.set_mode((640,480))

while 1:
    s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((host,port))
    s.listen(1)
    conn, addr = s.accept()
    message = []
    while True:
        d = conn.recv(1024*1024)
        if not d: break
        else: message.append(d)
    data = ''.join(message)
    image = pygame.image.fromstring(data,(640,480),"RGB")
    screen.blit(image,(0,0))
    pygame.display.update()
like image 119
sloth Avatar answered Oct 05 '22 22:10

sloth