Closed csjiyw closed 1 week ago
the python code is bug free while i tested it with the python recv, code as follow: import cv2 from aiortc.rtcrtpreceiver import RemoteStreamTrack
cv2.namedWindow("win", cv2.WINDOW_NORMAL)
import asyncio
import numpy as np from aiortc import RTCPeerConnection, RTCSessionDescription, MediaStreamTrack from aiortc.contrib.signaling import TcpSocketSignaling from av import VideoFrame from datetime import datetime, timedelta
class VideoReceiver: def init(self): self.track = None self.frame = None
async def handle_track(self, track : RemoteStreamTrack):
print("Inside handle track")
self.track = track
frame_count = 0
while True:
try:
print("Waiting for frame...")
frame = await asyncio.wait_for(track.recv(), timeout=5.0)
frame_count += 1
print(f"Received frame {frame_count}")
if isinstance(frame, VideoFrame):
print(f"Frame type: VideoFrame, pts: {frame.pts}, time_base: {frame.time_base}")
frame = frame.to_ndarray(format="bgr24")
elif isinstance(frame, np.ndarray):
print(f"Frame type: numpy array")
else:
print(f"Unexpected frame type: {type(frame)}")
continue
# Add timestamp to the frame
current_time = datetime.now()
new_time = current_time - timedelta(seconds=55)
timestamp = new_time.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
cv2.putText(frame, timestamp, (10, frame.shape[0] - 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2,
cv2.LINE_AA)
self.frame = frame.copy()
cv2.imshow('win', self.frame)
cv2.waitKey(1)
# cv2.imwrite(f"imgs/received_frame_{frame_count}.jpg", frame)
# print(f"Saved frame {frame_count} to file")
# cv2.imshow("Frame", frame)
#
# # Exit on 'q' key press
# if cv2.waitKey(1) & 0xFF == ord('q'):
# break
except asyncio.TimeoutError:
print("Timeout waiting for frame, continuing...")
except Exception as e:
print(f"Error in handle_track: {str(e)}")
if "Connection" in str(e):
break
print("Exiting handle_track")
def show_frame(self):
if self.frame is not None:
cv2.imshow('win', self.frame)
cv2.waitKey(1)
else:
print("frame is None")
async def run(pc, signaling): await signaling.connect()
@pc.on("track")
def on_track(track):
if isinstance(track, MediaStreamTrack):
print(f"Receiving {track.kind} track")
asyncio.ensure_future(video_receiver.handle_track(track))
@pc.on("datachannel")
def on_datachannel(channel):
print(f"Data channel established: {channel.label}")
@pc.on("connectionstatechange")
async def on_connectionstatechange():
print(f"Connection state is {pc.connectionState}")
if pc.connectionState == "connected":
print("WebRTC connection established successfully")
print("Waiting for offer from sender...")
offer = await signaling.receive()
print("Offer received")
await pc.setRemoteDescription(offer)
print("Remote description set")
answer = await pc.createAnswer()
print("Answer created")
await pc.setLocalDescription(answer)
print("Local description set")
await signaling.send(pc.localDescription)
print("Answer sent to sender")
print("Waiting for connection to be established...")
while pc.connectionState != "connected":
await asyncio.sleep(0.1)
print("Connection established, waiting for frames...")
await asyncio.sleep(1000000) # Wait for 35 seconds to receive frames
# while True:
# video_receiver.show_frame()
print("Closing connection")
import threading
async def main(): signaling = TcpSocketSignaling("127.0.0.1", 9999) pc = RTCPeerConnection()
global video_receiver
video_receiver = VideoReceiver()
# t1 = threading.Thread(target=video_receiver.show_frame)
# t1.start()
try:
await run(pc, signaling)
except Exception as e:
print(f"Error in main: {str(e)}")
finally:
print("Closing peer connection")
await pc.close()
if name == "main": asyncio.run(main())
finally i am success to recv video from aiortc example webcam.py by modify aiortcConnector.cs as follows:
using System.Collections; using System.Linq; using System.Text; using Unity.WebRTC; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI;
public class aiortcConnector : MonoBehaviour { [SerializeField] private string aiortcServerURL; [SerializeField] private RawImage receiveVideo;
private enum Side
{
Local,
Remote
}
private class SignalingMsg
{
public string type;
public string sdp;
public string video_transform;
public RTCSessionDescription ToDesc()
{
return new RTCSessionDescription
{
type = type == "offer" ? RTCSdpType.Offer : RTCSdpType.Answer,
sdp = sdp
};
}
}
private RTCPeerConnection pc;
private MediaStream receiveVideoStream;
void Start()
{
WebRTC.Initialize();
StartCoroutine(WebRTC.Update());
Connect();
}
public void Connect()
{
pc = new RTCPeerConnection();
var transceiver = pc.AddTransceiver(TrackKind.Video, new RTCRtpTransceiverInit
{
direction = RTCRtpTransceiverDirection.RecvOnly
});
pc.OnIceGatheringStateChange = state =>
{
Debug.Log($"OnIceGatheringStateChange > state: {state}");
};
pc.OnConnectionStateChange = state =>
{
Debug.Log($"OnConnectionStateChange > state: {state}");
};
receiveVideoStream = new MediaStream();
receiveVideoStream.OnAddTrack = e => {
if (e.Track is VideoStreamTrack track)
{
// You can access received texture using `track.Texture` property.
track.OnVideoReceived+= tex =>
{
receiveVideo.texture = tex;
};
}
};
pc.OnTrack = e =>
{
Debug.Log($"OnTrack");
if (e.Track is VideoStreamTrack video)
{
Debug.Log($"OnTrackVideo");
receiveVideoStream.AddTrack(e.Track);
}
};
StartCoroutine(CreateDesc(RTCSdpType.Offer));
}
private IEnumerator CreateDesc(RTCSdpType type)
{
Debug.Log("CreateDesc"+type);
var op = type == RTCSdpType.Offer ? pc.CreateOffer() : pc.CreateAnswer();
yield return op;
if (op.IsError)
{
Debug.LogError($"Create {type} Error: {op.Error.message}");
yield break;
}
StartCoroutine(SetDesc(Side.Local, op.Desc));
}
private IEnumerator SetDesc(Side side, RTCSessionDescription desc)
{
Debug.Log("SetDesc"+side+desc);
var op = side == Side.Local ? pc.SetLocalDescription(ref desc) : pc.SetRemoteDescription(ref desc);
yield return op;
if (op.IsError)
{
Debug.Log($"Set {desc.type} Error: {op.Error.message}");
yield break;
}
if (side == Side.Local)
{
// aiortc not support Tricle ICE.
var msg = new SignalingMsg
{
type = pc.LocalDescription.type.ToString().ToLower(),
sdp = pc.LocalDescription.sdp
};
Debug.Log("sdp:"+pc.LocalDescription.sdp);
yield return aiortcSignaling(msg);
}
else if (desc.type == RTCSdpType.Offer)
{
yield return StartCoroutine(CreateDesc(RTCSdpType.Answer));
}
}
private IEnumerator aiortcSignaling(SignalingMsg msg)
{
Debug.Log("aiortcSignaling");
var jsonStr = JsonUtility.ToJson(msg);
using var req = new UnityWebRequest($"{aiortcServerURL}/{msg.type}", "POST");
var bodyRaw = Encoding.UTF8.GetBytes(jsonStr);
req.uploadHandler = new UploadHandlerRaw(bodyRaw);
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
yield return req.SendWebRequest();
Debug.Log(req.downloadHandler.text);
var resMsg = JsonUtility.FromJson<SignalingMsg>(req.downloadHandler.text);
yield return StartCoroutine(SetDesc(Side.Remote, resMsg.ToDesc()));
}
void Update()
{
}
}
problem
i want to use python script to stream video to unity using webrtc, i wrote a python tcp webrtc side to stream video and unity code to recv. The signaling part is success connect, however the video stream is not suppose to transfer.
here is python sender
import asyncio import cv2 from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack from aiortc.contrib.signaling import TcpSocketSignaling from av import VideoFrame import fractions from datetime import datetime
class CustomVideoStreamTrack(VideoStreamTrack): def init(self, camera_id): super().init() self.cap = cv2.VideoCapture(camera_id) self.frame_count = 0
async def setup_webrtc_and_run(ip_address, port, camera_id): signaling = TcpSocketSignaling(ip_address, port) pc = RTCPeerConnection() video_sender = CustomVideoStreamTrack(camera_id) pc.addTrack(video_sender)
async def main(): ip_address = "127.0.0.1" # Ip Address of Remote Server/Machine port = 9999 camera_id = 1 # Change this to the appropriate camera ID await setup_webrtc_and_run(ip_address, port, camera_id)
if name == "main": asyncio.run(main())
here is my unity recv
using System; using System.Threading.Tasks; using UnityEngine; using Unity.WebRTC; using System.Collections; using System.Collections.Generic; using System.Linq;
public class WebRTCSignaling : MonoBehaviour {
}
here is the unity tcp signaling code
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using UnityEngine;
public class TcpSocketSignaling { // private TcpListener _listener; private TcpClient _client;
}