I want to stream a video from server.py to client.py using aiortc and TCPSocketHandling. I seem do be doing everything as per the aiortc examples and documentation, but still getting the following error:-
exception=InvalidStateError('RTCIceTransport is closed')>
Attaching the client.py and server.py code
Server.py
import asyncio
import cv2
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription, RTCIceCandidate
from aiortc.contrib.media import MediaBlackhole, MediaPlayer, MediaRelay
from aiortc.contrib.signaling import TcpSocketSignaling
from av import VideoFrame
class VideoStreamTrack(MediaStreamTrack):
kind = "video"
def __init__(self, video_path):
super().__init__() # Initialize the base class
# self.track = track
self.video_path = video_path
self.cap = cv2.VideoCapture(video_path)
async def recv(self):
# Read frames from the video file and convert them to RTCVideoFrames
ret, img = self.cap.read()
if ret:
# pts, time_base = await self.next_timestamp()
frame = VideoFrame.from_ndarray(img, format="bgr24")
# frame.pts = pts
# frame.time_base = time_base
# await asyncio.sleep(1/30)
return frame
else:
# Video ended, close the connection
self.cap.release()
raise ConnectionError("Video stream ended")
async def serve_video(pc, signaling):
# Create a MediaRelay to relay media tracks between peers
relay = MediaRelay()
# track = MediaStreamTrack()
# Add the video track to the peer connection
video_path = "test.mp4"
video_track = VideoStreamTrack(video_path)
pc.addTrack(relay.subscribe(video_track))
# Create an offer and set it as the local description
offer = await pc.createOffer()
await pc.setLocalDescription(offer)
# Send the offer to the client
await signaling.send(pc.localDescription)
# Wait for the answer from the client
answer = await signaling.receive()
if isinstance(answer, RTCSessionDescription):
await pc.setRemoteDescription(answer)
elif isinstance(answer, RTCIceCandidate):
await pc.addIceCandidate(answer)
# await pc.setRemoteDescription(RTCSessionDescription(answer))
# Start relaying the media between peers
# while True:
# try:
# frame = await video_track.recv()
# await pc.sendVideoFrame(frame)
# except ConnectionError:
# break
# # Cleanup when done
await pc.close()
async def main():
pc = RTCPeerConnection()
# Create a TCP socket signaling instance
signaling = TcpSocketSignaling("127.0.0.1", 8080)
# Connect to the signaling server
await signaling.connect()
# Serve the video
await serve_video(pc, signaling)
# Close the signaling connection
await signaling.close()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Client.py:-
import asyncio
import cv2
from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription, RTCIceCandidate
from aiortc.contrib.media import MediaBlackhole, MediaPlayer, MediaRelay
from aiortc.contrib.signaling import TcpSocketSignaling
class VideoStreamTrack(MediaStreamTrack):
kind = "video"
def __init__(self, track):
super().__init__() # Initialize the base class
self.track = track
async def recv(self):
# Receive RTCVideoFrames and render them
frame = await self.track.recv()
if frame:
img = frame.to_ndarray(format="bgr24")
cv2.imshow("b", frame)
print("a")
cv2.waitKey(1)
else:
# Video ended, close the connection
raise ConnectionError("Video stream ended")
return frame
async def receive_video(pc, signaling):
# Create a MediaRelay to relay media tracks between peers
# relay = MediaRelay()
offer = await signaling.receive()
@pc.on("iceconnectionstatechange")
async def on_iceconnectionstatechange():
print("ICE connection state is %s" % pc.iceConnectionState)
if pc.iceConnectionState == "failed":
print("Error!!!")
await pc.close()
# pcs.discard(pc)
@pc.on("track")
def on_track(track):
video_track = VideoStreamTrack(track)
if isinstance(offer, RTCSessionDescription):
await pc.setRemoteDescription(offer)
elif isinstance(offer, RTCIceCandidate):
await pc.addIceCandidate(offer)
# await pc.setRemoteDescription(RTCSessionDescription(answer))
# Create a renderer to display the received video
# Add the video track to the peer connection
# pc.addTrack(video_track)
# Create an answer and set it as the local description
await pc.setLocalDescription(await pc.createAnswer())
# Send the answer back to the server
await signaling.send(pc.localDescription)
# Start relaying the media between peers
# while True:
# try:
# await video_track.recv()
# except ConnectionError:
# break
# Cleanup when done
await pc.close()
async def main():
pc = RTCPeerConnection()
# Create a TCP socket signaling instance
signaling = TcpSocketSignaling("127.0.0.1", 8080)
# Connect to the signaling server
await signaling.connect()
# Receive the video
await receive_video(pc, signaling)
# Close the signaling connection
await signaling.close()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())