How to Build a Simple Python Video Stream Server with aiortc?
Introduction Setting up a simple video stream server using Python and the aiortc library might seem challenging at first, especially when integrating both Python and HTML/JavaScript components. In this article, we will explore a code example that demonstrates creating a basic Python video streaming server and the corresponding JavaScript code to connect to it. This implementation leverages WebRTC for real-time communication, focusing on a local network setup to stream video from a server to a browser. Understanding the Problem When working with WebRTC, one common issue developers face is related to Session Description Protocol (SDP) errors, specifically ValueError: None is not in list. This typically occurs when the offer or answer SDPs being exchanged do not contain valid track information or the necessary metadata for establishing a connection. In this instance, the browser and server's SDP configurations seem to lack stream track information, leading to the connection failure. Setting Up Your Python Server To create a Python video streaming server using aiortc, follow these steps: 1. Install Dependencies Before diving into the code, ensure you have the necessary packages installed. You can do this using pip: pip install aiortc aiohttp opencv-python av numpy 2. Python Code Example Below is a complete example of a basic Python server that streams video of a bouncing ball: import asyncio from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack from av import VideoFrame import numpy as np from aiohttp import web class BouncingBallTrack(VideoStreamTrack): def __init__(self): super().__init__() self.width = 640 self.height = 480 self.ball_position = [self.width // 2, self.height // 2] self.ball_velocity = [2, 2] async def recv(self): frame = np.zeros((self.height, self.width, 3), dtype=np.uint8) # Update ball position self.ball_position[0] += self.ball_velocity[0] self.ball_position[1] += self.ball_velocity[1] # Bounce off walls if self.ball_position[0] >= self.width or self.ball_position[0] = self.height or self.ball_position[1] { if (event.track.kind === 'video') { video.srcObject = event.streams[0]; } }; // Create an offer to send to the server const offer = await pc.createOffer(); await pc.setLocalDescription(offer); // Send the offer to the server and get the answer const response = await fetch('/offer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sdp: pc.localDescription.sdp, type: pc.localDescription.type }) }); const answer = await response.json(); await pc.setRemoteDescription(new RTCSessionDescription(answer)); Troubleshooting SDP Issues If you encounter the ValueError: None is not in list error when you attempt to set the local description after sending your offer, ensure the following: The SDP received from the client contains valid track information. Both server and client are correctly set up to handle incoming SDP offers and answers. Check your network configurations to ensure there are no issues with the local network preventing the exchange of ICE candidates. Frequently Asked Questions (FAQs) Why am I getting a ValueError on the server? The ValueError can occur if the SDP provided does not contain valid media descriptions. Ensure both client and server are configured correctly to handle media tracks. Do I need ICE servers for local network streaming? No, when you are running both the server and the client on the same local network, ICE servers are not necessary. Conclusion Creating a simple Python server to stream video can be accomplished using the aiortc library and some JavaScript to handle the client side. If you follow the provided code examples closely and ensure that the SDP being exchanged contains valid stream track information, you should be able to set up a functioning video stream without issues. If you run into problems, refer back to the troubleshooting section as your guide to resolving connection errors.

Introduction
Setting up a simple video stream server using Python and the aiortc library might seem challenging at first, especially when integrating both Python and HTML/JavaScript components. In this article, we will explore a code example that demonstrates creating a basic Python video streaming server and the corresponding JavaScript code to connect to it. This implementation leverages WebRTC for real-time communication, focusing on a local network setup to stream video from a server to a browser.
Understanding the Problem
When working with WebRTC, one common issue developers face is related to Session Description Protocol (SDP) errors, specifically ValueError: None is not in list
. This typically occurs when the offer or answer SDPs being exchanged do not contain valid track information or the necessary metadata for establishing a connection. In this instance, the browser and server's SDP configurations seem to lack stream track information, leading to the connection failure.
Setting Up Your Python Server
To create a Python video streaming server using aiortc, follow these steps:
1. Install Dependencies
Before diving into the code, ensure you have the necessary packages installed. You can do this using pip:
pip install aiortc aiohttp opencv-python av numpy
2. Python Code Example
Below is a complete example of a basic Python server that streams video of a bouncing ball:
import asyncio
from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack
from av import VideoFrame
import numpy as np
from aiohttp import web
class BouncingBallTrack(VideoStreamTrack):
def __init__(self):
super().__init__()
self.width = 640
self.height = 480
self.ball_position = [self.width // 2, self.height // 2]
self.ball_velocity = [2, 2]
async def recv(self):
frame = np.zeros((self.height, self.width, 3), dtype=np.uint8)
# Update ball position
self.ball_position[0] += self.ball_velocity[0]
self.ball_position[1] += self.ball_velocity[1]
# Bounce off walls
if self.ball_position[0] >= self.width or self.ball_position[0] <= 0:
self.ball_velocity[0] *= -1
if self.ball_position[1] >= self.height or self.ball_position[1] <= 0:
self.ball_velocity[1] *= -1
# Draw ball on the frame
cv2.circle(frame, tuple(map(int, self.ball_position)), 20, (255, 0, 0), -1)
frame = VideoFrame.from_ndarray(frame, format="bgr")
return frame
async def offer(request):
params = await request.json()
offer = RTCSessionDescription(sdp=params['sdp'], type=params['type'])
pc = RTCPeerConnection()
# Add local media track
pc.addTrack(BouncingBallTrack())
await pc.setRemoteDescription(offer)
answer = await pc.createAnswer()
await pc.setLocalDescription(answer)
return web.json_response({
'sdp': pc.localDescription.sdp,
'type': pc.localDescription.type
})
if __name__ == '__main__':
app = web.Application()
app.router.add_post('/offer', offer)
web.run_app(app, port=8080)
JavaScript Code Example
The JavaScript code to connect to the Python server is below. This code creates an RTCPeerConnection and handles receiving the video stream:
const video = document.getElementById('video');
const pc = new RTCPeerConnection({
iceServers: [] // No ICE servers needed for local network
});
// When remote adds a track, show it in our video element
pc.ontrack = event => {
if (event.track.kind === 'video') {
video.srcObject = event.streams[0];
}
};
// Create an offer to send to the server
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// Send the offer to the server and get the answer
const response = await fetch('/offer', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
sdp: pc.localDescription.sdp,
type: pc.localDescription.type
})
});
const answer = await response.json();
await pc.setRemoteDescription(new RTCSessionDescription(answer));
Troubleshooting SDP Issues
If you encounter the ValueError: None is not in list
error when you attempt to set the local description after sending your offer, ensure the following:
- The SDP received from the client contains valid track information.
- Both server and client are correctly set up to handle incoming SDP offers and answers.
- Check your network configurations to ensure there are no issues with the local network preventing the exchange of ICE candidates.
Frequently Asked Questions (FAQs)
Why am I getting a ValueError on the server?
The ValueError can occur if the SDP provided does not contain valid media descriptions. Ensure both client and server are configured correctly to handle media tracks.
Do I need ICE servers for local network streaming?
No, when you are running both the server and the client on the same local network, ICE servers are not necessary.
Conclusion
Creating a simple Python server to stream video can be accomplished using the aiortc library and some JavaScript to handle the client side. If you follow the provided code examples closely and ensure that the SDP being exchanged contains valid stream track information, you should be able to set up a functioning video stream without issues. If you run into problems, refer back to the troubleshooting section as your guide to resolving connection errors.