📜 ⬆️ ⬇️

Broadcast video from Raspberry Pi over 3G in three ways

As a hobby, I chose Raspberry Pi, now decided to share what has already happened. In short, I implemented video streaming in three ways: HLS for iPhone - delay 20-40 seconds, through nc (net cat) - delay less than second, but buffers for a long time, and through gstreamer: everything is fine, but you need to look through gstreamer on the client .



Pi has the following advantages:

- a large community ( 5 million sold, and there are clones);
- cheap (half a year ago I bought B + for $ 35, and B for $ 42 you can connect to it via AV telly);
- GPU (VideoCore IV - 24Gflops, Intel Edison GPU is blocked);
- voltage on pins 3.3v, which is better than 1.8v for an amateur like me;
- Native MIPI CSI OV5647 camera.
')
I did the following - I connected a Raspberry Pi, 3G modem, a camera and an infrared sensor. Through the infrared remote control, I can send commands: turn on / off the Internet, broadcast live video in three different ways, send an email link to a video broadcast. How to use it, I leave behind the video frame broadcast.

The Raspberry Pi is still able to work only with the OMP5647 5MP camera (fairly old and weak), although a similar Broadcom chip in some kind of Nokia works with the 42MP camera, even after the VideoCore IV GPU documentation became available, no hint of diversity appeared . On alternative boards, the situation is not at all better and mostly USB cameras are used there.

Most USB cameras compress each frame into jpeg and send this jpeg via USB. The camera driver decomposes jpeg ... Therefore, the quality of photos and video through a CSI camera is much better than via USB. It is the camera in Pi that is a killer feature, although if someone (ay Intel) implements a motherboard with camera support at the GoPro level, then of course it will be very interesting.

On Pi, I donned a DVK512 board. It has several pins, 4 ice and 4 buttons.



I also hooked an infrared sensor and a 3G modem. I programmed all this on python and bash under the root. To blink ice and go online with 3G, you have to be a root.

The lirc package is responsible for working with the infrared remote control. It includes the irrecord command, which in theory can learn to work with any remote control, or you can download the config for your remote control from the Internet.
apt-get install lirc 


Most 3G modems will be recognized by doing so.
 sudo apt-get install usb-modeswitch 
,

But in order to work properly with USB, I added the udev rule. By default, when you plug something into USB, the device name can get unpredictable, and one 3G modem can be immediately reflected by four ttyUSB0, ttyUSB1, ttyUSB2, ttyUSB3 devices. This problem can be solved by writing the udev rule, and the system will give a well-defined name for the device.
 /dev/modems/nova_091095493721000_1-1.5 


This name means: 091095493721000 - a unique series of my modem, 1.5 - the first and only USB hub, 5th USB port. (the first port seems to be connected to ezernet)

If os.path.exists ('/ dev / modems / nova_091095493721000_1-1.5'), then the modem is inserted and it is necessary to blink ice.

Here is the udev rule itself

 cat /etc/udev/rules.d/52-ftdi.rules SUBSYSTEMS=="usb", KERNEL=="ttyUSB[0-9]*", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="sensors/ftdi_%s{serial}_%b" SUBSYSTEMS=="usb", KERNEL=="ttyUSB[0-9]*", ATTRS{idVendor}=="1410", ATTRS{idProduct}=="6000", SYMLINK+="modems/nova_%s{serial}_%b" 


The essence of the program is a cycle, which, if the button is pressed, starts or stops the process, if the process is started, the corresponding ice is ignited.

 import lirc import time import subprocess import os import signal import sys import RPi.GPIO as GPIO class Proc(object): def __init__(self, args): self.args = args self.proc = None def start(self): if self.proc != None: if self.proc.poll() == None: return self.proc = subprocess.Popen(self.args, shell=False, preexec_fn=os.setsid) #preexec_fn=os.setsid` def stop(self): if self.proc == None: return #self.proc.kill() try: os.killpg(os.getpgid(self.proc.pid), 15) except OSError as e: print e.strerror try: self.proc.terminate() except OSError as e: print e.strerror self.proc = None def is_live(self): if self.proc != None: if self.proc.poll() == None: return True self.stop() return False GPIO.setmode(GPIO.BCM) GPIO.setup(26, GPIO.OUT) GPIO.setup(12, GPIO.OUT) GPIO.setup(16, GPIO.OUT) GPIO.setup(20, GPIO.OUT) GPIO.output(26,False) GPIO.output(12,False) GPIO.output(16,False) GPIO.output(20,False) ppp = Proc(['./dial.sh']) video = Proc(['./video1.sh']) video2 = Proc(['./video2.sh']) video3 = Proc(['./video3.sh']) ws = Proc(['./webserver.sh']) ws.start() def terminate(): GPIO.cleanup() ppp.stop() video.stop() video2.stop() video3.stop() ws.stop() def signal_term_handler(signal, frame): print 'got SIGTERM' terminate() sys.exit(0) signal.signal(signal.SIGTERM, signal_term_handler) sockid = lirc.init("ctrl1", blocking = False) tac = False try: while True: codeIRs = lirc.nextcode() # print codeIR nocode = True for codeIR in codeIRs: nocode = False print codeIR, len(codeIR) if codeIR == "star": print "STAR DETECTED" if codeIR == "1": ppp.start() if codeIR == "2": ppp.stop() if codeIR == "3": subprocess.call("./notify.py") if codeIR == "4": video.start() if codeIR == "5": video.stop() if codeIR == "7": video2.start() if codeIR == "8": video2.stop() if codeIR == "6": video3.start() if codeIR == "9": video3.stop() tac = not tac GPIO.output(26,ppp.is_live() or (os.path.exists("/dev/modems/nova_091095493721000_1-1.5") and tac)) GPIO.output(12,video.is_live()) GPIO.output(16,video2.is_live() or (video3.is_live() and tac)) GPIO.output(20,ws.is_live()) if nocode: time.sleep(0.1) except KeyboardInterrupt: terminate() print "Good bye" 


The Proc class runs programs and can stop them along with their subprocesses. The GPIO package is used for blinking ice, and the lirc package is used to read an infrared sensor.

First of all, the program runs the webserver.sh script
 #!/bin/bash cd video python -m SimpleHTTPServer 

This server is needed for HLS video broadcast on iPhone. The iPhone is a very demanding device and the video takes on well-defined characteristics.

dial.sh - script to access the Internet
 #!/bin/bash route delete default wvdial 

If the Internet is gone, the script will die, if the script is killed, then the Internet will disappear. It works quite predictably.

Broadcast to iPhone


The iPhone is a very picky device and it wants video in HLS format. The video files are cut (segmented) for 2 seconds, the iPhone player downloads via HTTP. Each file must contain special frames (-ih key in raspivid). In short, the native program Pi raspivid can sort of segment, but somehow it does not. avconv and ffmpeg from Raspbian also know how to segment and also somehow, in general, in 5 hours and you compile the fresh ffmpeg downloaded from git. This ffmpeg will cut the stream correctly for broadcast to the iPhone.

 #!/bin/bash base="/home/pi/my/py/ir/" cd $base rm -fr video/* raspivid -n -ih -t 0 -ISO 800 -ex night -w 320 -h 240 -fps 30 -b 500000 -o - | \ ./ffmpeg -y \ -loglevel panic \ -i - \ -c:v copy \ -map 0 \ -f ssegment \ -segment_time 1 \ -segment_format mpegts \ -segment_list "$base/video/stream.m3u8" \ -segment_list_size 10 \ -segment_wrap 20 \ -segment_list_flags +live \ -segment_list_type m3u8 \ -segment_list_entry_prefix / \ "$base/video/%03d.ts" 


This broadcast works, but a delay of 40 seconds is the norm.

Broadcast with a delay of less than 1 second


everything is simple! It is necessary to direct the flow to the net cat
 #!/bin/bash raspivid -t 0 -h 240 -w 320 -fps 30 -hf -b 100000 -o - | nc -l 5001 

And you can watch using mplayer
 nc 94.248.14.212 5001 | mplayer -fps 120 -cache 1024 - 

but since the broadcast should start earlier, mplayer has to catch up with the broadcast. Therefore, it is necessary to set -fps higher than in the broadcast. But in any case, when mplayer catches up, it will twitch.

Gstreamer streaming is the most optimal

 $> cat video3.sh #!/bin/bash raspivid -t 0 -h 240 -w 320 -fps 25 -hf -b 2000000 -o - | gst-launch-1.0 -v fdsrc ! h264parse ! rtph264pay config-interval=1 pt=96 ! gdppay ! tcpserversink host=0.0.0.0 port=5000 


and watch accordingly

 gst-launch-1.0 -v tcpclientsrc host=94.248.14.212 port=5000 ! gdpdepay ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false 


On Google Play, there are applications that can show videos from gstreamer.

PS: component prices so that no one overpays. (including free shipping)
6 Chinese batteries 18650 - $ 11
1 panasonic nrc18650b - $ 10 (almost as in tesle)
USB bank for one 18650 $ 1.24
DVK 512 - $ 15
IR sensor + remote - $ 2.17
Now it's worth taking a Raspberry Pi 2 ~ $ 40

PS2: In Pi, all pins have two names. Native broadcasted GPIO.setmode (GPIO.BCM) and universal for all Raspberry Pi GPIO.setmode (GPIO.BOARD), but DVK512 has its third numbering.



Led0 - 26 # GPIO.setmode (GPIO.BCM), it’s also GPIO.setmode (GPIO.BOARD) - 37, it’s on DV512 P25
Led1 - 12
Led2 - 16
Led3 - 20
Key0 - 5
Key1 - 6
Key2 - 13
Key3 - 19

Happy hacking

Source: https://habr.com/ru/post/258377/


All Articles