How do you stream WebCam feed to multiple clients on the same wifi network, where client IP's can be random due to DHCP? - streaming

I have a scenario where I want to stream a webcam feed to multiple display devices over a single wifi network. I've tried using UDP multicast, but it seems the wifi is flooded with packets and the video is extremely choppy or simply doesn't work. It's like I can see the first frame of the video and then maybe a few later frames, then it just stops altogether.
Currently I have three Raspberry Pi's (RPi) configured like this:
All 3 RPi's are version 4 with Raspberry Pi OS (buster), and none of them are connected to the LAN via ethernet (eth0).
The primary RPi has a webcam and also an USB wifi adapter.
◦ The wifi adapter is a Panda PAU06. [Wifi adapter link]
◦ The webcam is connected to the PRi's camera port.
[camera link]
◦ The primary RPi does not have a display, only the webcam.
◦ The internal nic (wlan0) is connected to my home router, but should not be needed in this scenario. I simply use it to connect to the RPi network, like a jump spot, for configuration purposes.
◦ The Panda nic (wlan1) uses a static IP of 192.168.44.1, and is configured as an Access Point using hostapd and WPA security.
◦ The primary RPi has DHCP configured to hand out addresses in the range 192.168.44.[10-50] on wlan1.
◦ Routing is not setup to allow the wlan1 to access the internet.
The secondary RPi's just have the internal wifi (wlan0) connecting to the primary RPi's SSID.
◦ The secondary RPI's have display's attached to them, with native resolution of 800x480. [display link]
I can ssh just fine between the three RPi's, but obviously I must connect to the primary before connecting to the other two.
This is what I've been trying to setup, but doesn't quite work right. I don't know if I'm doing something wrong, or if there's a bug in gstreamer's multicast protocol.
‣ Host with camera using multicast:
$ sudo route add 224.1.1.1 wlan1
$ gst-launch-1.0 -v v4l2src device=/dev/video0 \
! 'video/x-raw,width=800,height=480,framerate=24/1' \
! clockoverlay 'time-format=%D %H:%M:%S' \
! jpegenc ! rtpjpegpay \
! udpsink host=224.1.1.1 port=5000 auto-multicast=true
‣ Client(s) with display using multicast:
$ gst-launch-1.0 udpsrc address=224.1.1.1 port=5000 \
auto-multicast=true \
! application/x-rtp, 'encoding-name=JPEG' \
! rtpjpegdepay ! jpegdec ! autovideosink
Any comments or suggestions are greatly appreciated!
Note: Using unicast, works fine, but I can only have 1 remote connected and I have to know it's IP address in advance, which I don't because its controlled by DHCP.
‣ Host with camera using unicast:
$ gst-launch-1.0 -v v4l2src device=/dev/video0 \
! 'video/x-raw,width=800,height=480,' framerate=24/1 \
! clockoverlay 'time-format=%D %H:%M:%S' \
! jpegenc ! rtpjpegpay \
! udpsink host=192.168.44.11 port=5000
‣ Client with display using unicast:
$ gst-launch-1.0 udpsrc port=5000 \
! application/x-rtp, 'encoding-name=JPEG' \
! rtpjpegdepay ! jpegdec ! autovideosink

Wifi is weird for Multicast. It can hog the full bandwidth at 2.5GHZ and 20% of bandwith for 5GHz. So using unicast would be better.
You would just use tee for replicating the stream and send to each of your targets (assuming their IP addresses ending with 11,12,13):
gst-launch-1.0 -v v4l2src device=/dev/video0 \
! 'video/x-raw,width=800,height=480,' framerate=24/1 \
! clockoverlay 'time-format=%D %H:%M:%S' \
! jpegenc ! rtpjpegpay \
! tee name=stream \
stream. ! queue ! udpsink auto-multicast=0 host=192.168.44.11 port=5000 \
stream. ! queue ! udpsink auto-multicast=0 host=192.168.44.12 port=5000 \
stream. ! queue ! udpsink auto-multicast=0 host=192.168.44.13 port=5000
I'd suggest adding rtpjitterbuffer on receiver side:
gst-launch-1.0 udpsrc port=5002 auto-multicast=0 ! application/x-rtp,encoding-name=JPEG ! rtpjitterbuffer latency=500 ! rtpjpegdepay ! jpegdec ! autovideosink

Related

main demux error: socket bind error: Cannot assign requested address when trying to connect gstreamer and vlc

I am trying to connect with vlc to an embedded board where a gstreamer cmd is executed. However vlc gives me the error:
[00007f9734001160] main demux error: socket bind error: Cannot assign requested address
The following gstreamer cmd is used:
"appsrc ! videoconvert ! video/x-raw,format=YUY2,width=640,height=480,framerate=30/1 ! jpegenc ! multipartmux ! tcpserversink name=out-sink host=0.0.0.0 port=7001"
And I try to use the following command on the host machine:
vlc rtp://192.168.0.10:7001
Sadly it does not work, however I can ping from the one machine to the other and vice-versa.
Furthermore, it is a precompiled binary, therefore I am unable to change the gstreamer command and can only adapt the command and utilities used at the host machine.
Any ideas what could be the problem?
I tested in my local system with videotestsrc instead of your appsink.
gst-launch-1.0 videotestsrc ! videoconvert ! video/x-raw,format=YUY2,width=640,height=480,framerate=30/1 ! jpegenc ! multipartmux ! tcpserversink name=out-sink host=0.0.0.0 port=7001
Please note that you are transmitting over TCP and without RTP payloading so instead of rtp://.. I listened with tcp:// and vlc was able to open.
vlc tcp://127.0.0.1:7001
If you still want to use rtp I would recommend that you use rtpjpegpay, and some sdp file like
v=0
m=video 7001 RTP/AVP 26
c=IN IP4 127.0.0.1
a=rtpmap:26 JPEG/90000

ChromeCast without an HDMI-CEC TV

I have a TV which doesn't support HDMI-CEC and I've been trying to build a bridge between a Raspberry Pi and my ChromeCast so that the Raspberry Pi can pause or stop play via receiving IR from a remote control.
Using the cec-client on the Pi I can see:
pi#raspberrypi:~/libcec/build $ echo "scan" | cec-client -s -d 1
opening a connection to the CEC adapter...
requesting CEC bus information ...
ERROR: [ 12174] failed to request the physical address
CEC bus information
===================
device #1: Recorder 1
address: 2.0.0.0
active source: no
vendor: Pulse Eight
osd string: CECTester
CEC version: 1.4
power status: on
language: eng
device #4: Playback 1
address: f.f.f.f
active source: no
vendor: Unknown
osd string: Chromecast
CEC version: 1.4
power status: on
language: ???
The failure to request the physical address message is because of the ChromeCast.
So far I've made it the active source, made the Pi the active source, which all do nothing of course since the TV doesn't care, but while playing with those, I've also sent tx commands which are supposed to pause, play or stop. None of which the ChromeCast responds to.
These are some of the commands I have sent:
echo "tx 14 44 46" | cec-client -s -d 1
echo "tx 14 44 61" | cec-client -s -d 1
echo "tx 14 44 45" | cec-client -s -d 1
echo "tx 14 41 25" | cec-client -s -d 1
# etc
None of which have had any response.
Does the ChromeCast require a TV in order to respond? Is there something specific I need to do or send to get the media receiver to respond?
Edit: further research shows the Chromecast sends:
TRAFFIC: [ 47738] >> 4e:83
TRAFFIC: [ 47811] >> 4e:9f
on power up, which is a request for a physical address and the CEC version.
I'd really like to throw a $CAN10 Pi Zero and a couple of bucks of IR receiver and bridge HDMI-CEC around my TV.
The spec is available online and describes the address method. It ends up it doesn't matter as I had the message format for cec-client wrong.
echo "tx 14:44:44" | cec-client -s -d 1
With colons, and voila my raspberry pi can control my chromecast.

Unable to send traffic over RDMA

I have softROCE setup on SUSE Linux SP11. I am trying to validate the traffic over RXE device. I am able to send and receive traffic using ibv_*_pingpong. But for rping it is giving and error RDMA_CM_EVENT_UNREACHABLE, error -110.
#rxe_cfg -l
Name Link Driver Speed NMTU IPv4_addr RDEV RMTU
p4p1 yes r8169 1500 10.213.64.106 rxe0 1024 (3)

SIPP with Proxy Media

I am using SIPP to load test a proxy media server. I am not able to set the IP and port of the proxy media server on the SDP generated by SIPP,
I get the below error,
./sipp -sn uac -d 10000 -l 1000 -i 192.12.24.32 -p 50970 -mi 65.67.8.99 -mp 48321 10.12.24.32:5060 -sf uac_pcap.xml
Unable to bind audio RTP socket (IP=65.67.8.99, port=48322), errno = 99 (Cannot assign requested address).
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
t=0 0
c=IN IP[local_ip_type] [media_ip]
m=audio [auto_media_port] RTP/AVP 8
a=rtpmap:8 PCMA/8000
Please help !!!
I'm not an expert in either sdp (sdp probably should be one of your tags, btw) or sipp but the sipp documentation says:
-mi : Set the local media IP address (default: local primary host IP address)
-mp : Set the local RTP echo port number. Default is 6000.
This means that sipp tries to open the port designed by -mp on the local IP designed by -mi.
From what you said I understand the IP:port combination you give in -mi/-mp to be the IP:port combination on the media server, not on the machine running sipp. This means that sipp is trying to open a port on an IP owned by the destination, hence why it "Cannot assign requested address".
The IP:port of the media server should not be part of the SDP generated by sipp. Sipp should describe its end of the media session, then the reply to the INVITE should contain an SDP coming in from 10.12.24.32:5060 with an SDP describing the other end of the media session, including the IP:port of the media server.
Think about it. In a real call you would know the destination SIP address (or sips or...) but you would have no idea of where their media would be. The exchange of SIP message is what establishes that (through an exchange of SDPs), so your sipp SDP should not contain information about a remote media server that it would not know about in a real call.
Try this*:
./sipp -d 10000 -l 1000 -i 192.12.24.32 -p 50970 -mi 192.12.24.32 -mp 48321 10.12.24.32:5060 -sf uac_pcap.xml
*You use both -sn to specify a built in scenario and -sf to specify a scenario file. In other word you are specifying two scenarios, so I removed the built in scenario from the line. Maybe you have good reasons for it to be here that I know nothing about, in which case put it back in.

tun device: message is not received by server process

I set up two tun devices. The data that is written to each tun device is forwarded over a UDP socket to the other tun device using a simple loop:
// the tuntap device is created using these flags
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
[...]
fd_set fd_list;
FD_ZERO(&fd_list);
FD_SET(fd1, &fd_list); // fd1 is the tun device
FD_SET(fd2, &fd_list); // fd2 is the udp socket
int fds[] = {fd1, fd2};
while(select(max(fd1, fd2)+1, &fd_list, NULL, NULL, NULL) > -1) {
for(i = 0; i < 2; ++i)
if(FD_ISSET(fds[i], &fd_list)) {
nread = read(fds[i], buf, sizeof(buf));
assert(nread > 0);
ret = write(fds[(i+1)%2], buf, nread);
if(ret == -1)
perror("write():");
}
}
After setting up the interfaces using
ifconfig tun0 10.0.0.1
ifconfig tun1 10.0.0.2
I send a ping from one device to the other
ping -I tun1 10.0.0.1
I can see that the IPv4 packet is received by the UDP socket for tun0 and this packet is correctly written to tun0. Also watching the traffic on tun0 using wireshark shows that the packet is received by tun0. However, no ping response packet is created.
I thought that might be a special case for ICMP packets but when I'm using
socat -d -d -d - TCP-LISTEN:2000,so-bindtodevice=tun0 &
sleep 1
echo 2 | socat -d -d -d - TCP:10.0.0.1:2000,so-bindtodevice=tun1
again no connection is established. the connect process (2nd socat call) only continues firing TCP-SYN packets and eventually times out. Again, watching the traffic on tun0 using wireshark shows that the TCP-SYN packet is delivered to the tun0 device.
Why is this packet not forwared to the socat TCP-LISTEN process so it can establish the connection??
Looks like this is a routing error.
When I run the program on two different machines, then the packets are routed through the tun0 device on each machine respectively and http://backreference.org/wp-content/uploads/2010/03/simpletun.tar.bz2 works fine. Running the programm on one machine twice does not work!