Search code examples
pythonsocketscamerapygamepython-3.5

Python3.5 send object over socket (Pygame cam image)


I want to send a PyGame Image over socket with Python 3.5 and I always got a bug.

I get the image, I pickle it, and then I send it.

In the client, I receive it, I unpickle it, and I show it.

But I've got an error:

fenetre.blit(img, (20, 30))
pygame.error: display Surface quit

Here is my code for the server (the one which sends the image):

pygame.init()
pygame.camera.init()
cam = pygame.camera.Camera("/dev/video0", (680, 480))
cam.start()
class Streaming(Thread):


    def __init__(self):
        Thread.__init__(self)
    def run(self):

        s = socket.socket()
        s.bind(('192.168.1.158', 12801))
        s.listen(1)
        while True:
            sc, info = s.accept()
            print("Video client connected : "+str(info))
            try:
                while True:
                    image = cam.get_image()
                    str_img = pickle.dumps(image)
                    sc.send(str_img)
                    print(str_img)
                    print("Sending Image")
                    time.sleep(0.005)
            except Exception as e:
                print(str(e))

And the code for the client:

fenetre = pygame.display.set_mode((900, 900))
class Receiving(Thread):


    def __init__(self):
        Thread.__init__(self)

        self.clock = pygame.time.Clock()

    def run(self):
        global fenetre
        si = socket.socket()
        si.connect(("192.168.1.158", 12801))
        while True:
            img = si.recv(4096)
            img = pickle.loads(img)
            fenetre.blit(img, (20, 30))
            pygame.display.flip()
            self.clock.tick(60)

Thanks in advance!


Solution

  • I made example which sends surface with current time.

    (Pygame can't work with my camera).

    It uses struc.pack() to send image size always as 4 bytes before image is send. So client first receives 4 bytes and have image size. Then it can use this information to receive image.

    Both use loops to send/receive all the time.

    server.py

    #!/usr/bin/env python
    
    import pygame
    from threading import Thread
    import socket
    import struct # to send `int` as  `4 bytes`
    import time   # for test
    
    # --- constants ---
    
    ADDRESS = ("localhost", 12801)
    
    SURFACE_SIZE = (640, 480)
    
    WHITE = (255, 255, 255)
    BLACK = (  0,   0,   0)
    GREEN = (  0, 255,   0)
    
    # --- classes ---
    
    class Streaming(Thread):
    
        def __init__(self):
            Thread.__init__(self)
    
            pygame.init()
    
            #pygame.camera.init()
            #self.cam = pygame.camera.Camera("/dev/video0", SURFACE_SIZE)
            #self.cam.start()
    
            # create surface to imitate camera image
            self.image = pygame.Surface(SURFACE_SIZE)
            self.image_rect = self.image.get_rect()
    
            # create font to display text on surface
            self.font = pygame.font.Font(None, 50)
    
        def get_image(self):
            # emulate cam.get_image()
    
            # get current time as string
            current_time = time.strftime('%H:%M:%S.%s')
    
            # render surface with text (and center it)
            text = self.font.render(current_time, True, BLACK, GREEN)
            text_rect = text.get_rect(center=self.image_rect.center)
    
            # clear image and put new text
            self.image.fill(WHITE)
            self.image.blit(text, text_rect)
    
            return self.image
    
        def run(self):
    
            s = socket.socket()
    
            # solution for: "socket.error: [Errno 98] Address already in use"
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    
            s.bind(ADDRESS)
            s.listen(1)
    
            print("Wait for connection")
    
            try:
                sc, info = s.accept()
                print("Video client connected:", info)
    
                while True:
                    # get image surface
    
                    #image = self.cam.get_image()
                    image = self.get_image()
    
                    # convert surface to string
    
                    img_str = pygame.image.tostring(image, 'RGB')
    
                    print('len:', len(img_str))
    
                    # send string size
    
                    len_str = struct.pack('!i', len(img_str))
                    sc.send(len_str)
    
                    # send string image
    
                    sc.send(img_str)
    
                    # wait
    
                    time.sleep(0.5)
    
            except Exception as e:
                print(e)
            finally:
                # exit
                print("Closing socket and exit")
                sc.close()
                s.close()
                pygame.quit()
    
    # --- main ---
    
    Streaming().run()
    

    client.py

    #!/usr/bin/env python
    
    import pygame
    from threading import Thread
    import socket
    import struct
    
    # --- constants ---
    
    ADDRESS = ("localhost", 12801)
    
    SURFACE_SIZE = (640, 480)
    
    # --- classes ---
    
    class Receiving(Thread):
    
        def __init__(self):
            Thread.__init__(self)
    
            pygame.init()
    
            self.screen = pygame.display.set_mode((800, 600))
            self.screen_rect = self.screen.get_rect()
    
            self.clock = pygame.time.Clock()
    
        def run(self):
            s = socket.socket()
            s.connect(ADDRESS)
    
            try:
                running = True
                while running:
                    # receive size
    
                    len_str = s.recv(4)
                    size = struct.unpack('!i', len_str)[0]
    
                    print('size:', size)
    
                    # receive string
    
                    img_str = b''
    
                    while size > 0:
                        if size >= 4096:
                            data = s.recv(4096)
                        else:
                            data = s.recv(size)
    
                        if not data:
                            break
    
                        size -= len(data)
                        img_str += data
    
                    print('len:', len(img_str))
    
                    # convert string to surface
    
                    image = pygame.image.fromstring(img_str, SURFACE_SIZE, 'RGB')
                    image_rect = image.get_rect(center=self.screen_rect.center)
    
                    # blit
    
                    self.screen.blit(image, image_rect)
                    pygame.display.flip()
    
                    #self.clock.tick(30)
    
                    # wait for ESC or close window
    
                    for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                            running = False
                        elif event.type == pygame.KEYDOWN:
                            if event.key == pygame.K_ESCAPE:
                                running = False
    
            except Exception as e:
                print(e)
            finally:
                # exit
                print("Closing socket and exit")
                s.close()
                pygame.quit()
    
    # --- main ---
    
    Receiving().run()