WebRTC aiortc - Sending video from server.py to client.py giving RTCIceTransport error

2.1k Views Asked by At

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())
0

There are 0 best solutions below