Backend Technologies

socket_002: Real-Time Camera Streaming with Sockets

codeaddict 2025. 1. 30. 12:23

In this tutorial, we’ll create a server that captures video from a webcam and streams it to a client over a network. The client will receive the video frames and display them in real-time.

 


Prerequisites

  1. Install the required libraries:
pip install opencv-python

2. Ensure you have a webcam connected to your machine.

 — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

How It Works

  1. Server Side:
  • Capture video frames from the webcam using OpenCV.
  • Encode the frames into a byte stream.
  • Send the encoded frames to the client over a socket.

2. Client Side:

  • Receive the encoded frames from the server.
  • Decode the frames and display them using OpenCV.

 — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Step 1: Create the Camera Streaming Server

Save the following code in a file named camera_server.py:

import socket
import cv2
import pickle
import struct

# Create a TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Bind the socket to an IP address and port
server_address = ('localhost', 12345)
server_socket.bind(server_address)

# Listen for incoming connections
server_socket.listen(1)
print("Camera server is listening on port 12345...")

# Accept a connection from a client
client_socket, client_address = server_socket.accept()
print(f"Connection established with {client_address}")

# Set a timeout for the client socket (e.g., 5 seconds)
client_socket.settimeout(5.0)

# Initialize a buffer to store the incoming data
data_buffer = b""
# Size of the payload (4 bytes for the length of the frame)
payload_size = struct.calcsize("L")

try:
    while True:
        try:
            # Receive the frame size from the client
            while len(data_buffer) < payload_size:
                packet = client_socket.recv(4096)
                if not packet:  # Client disconnected
                    print("Client disconnected.")
                    break
                data_buffer += packet

            if not data_buffer:
                break  # Exit if client disconnected

            # Extract the frame size from the buffer
            packed_msg_size = data_buffer[:payload_size]
            data_buffer = data_buffer[payload_size:]
            msg_size = struct.unpack("L", packed_msg_size)[0]

            # Receive the frame data from the client
            while len(data_buffer) < msg_size:
                packet = client_socket.recv(4096)
                if not packet:  # Client disconnected
                    print("Client disconnected.")
                    break
                data_buffer += packet

            if not data_buffer:
                break  # Exit if client disconnected

            # Extract the frame from the buffer
            frame_data = data_buffer[:msg_size]
            data_buffer = data_buffer[msg_size:]

            # Deserialize the frame
            frame = pickle.loads(frame_data)

            # Overlay text on the frame to indicate it's from the server
            cv2.putText(frame, "From Server", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

            # Display the frame in a window
            cv2.imshow("Server Camera", frame)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

        except socket.timeout:
            print("No data received from client. Closing connection.")
            break

except KeyboardInterrupt:
    print("Server stopped.")

finally:
    # Release resources
    cv2.destroyAllWindows()
    client_socket.close()
    server_socket.close()

Step 2: Create the Camera Streaming Client

Save the following code in a file named camera_client.py:

import socket
import cv2
import pickle
import struct

# Create a TCP socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect to the server
server_address = ('localhost', 12345)
client_socket.connect(server_address)

# Open the webcam
cap = cv2.VideoCapture(0)

try:
    while True:
        # Capture a frame from the webcam
        ret, frame = cap.read()
        if not ret:
            break

        # Serialize the frame into a byte stream
        data = pickle.dumps(frame)
        # Pack the length of the data into a 4-byte header
        message_size = struct.pack("L", len(data))

        # Send the frame to the server
        client_socket.sendall(message_size + data)

        # Display the original frame in a window (optional)
        cv2.imshow("Client Camera", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

except KeyboardInterrupt:
    print("Client stopped.")

finally:
    # Release resources
    cap.release()
    client_socket.close()
    cv2.destroyAllWindows()

Step 3: Run the Code

  1. Open two terminal windows:
  • One for the server.
  • One for the client.

2. Run the server:

  • In the first terminal, navigate to the directory where camera_server.py is saved.
  • Run the server using the following command:
python camera_server.py
  • The server will start and display:
Camera server is listening on port 12345…

3. Run the client:

  • In the second terminal, navigate to the directory where camera_client.py is saved.
  • Run the client using the following command:
  • python camera_client.py
python camera_client.py
  • A window will open displaying the live camera stream from the server.

4. Stop the streaming:

  • Press q in the client window to stop the stream and close the window.

How It Works

  1. The server captures video frames from the webcam using OpenCV.
  2. Each frame is serialized into a byte stream using pickle.
  3. The server sends the frame size (as a 4-byte header) followed by the frame data to the client.
  4. The client receives the frame size and data, deserializes the frame, and displays it using OpenCV.

Key Takeaways

  • Sockets can be used to stream real-time data, such as video, between a server and a client.
  • OpenCV is a powerful library for capturing and processing video frames.
  • Serialization (using pickle) is necessary to send complex data (like images) over sockets.