У Pi есть следующие преимущества:
— большое сообщество (продано 5 миллионов, а еще ведь есть клоны);
— дешево (пол года назад я купил B+ за $35, а B за $42 к ней можно подключить по AV телек);
— GPU (VideoCore IV — 24Gflops, у Intel Edison GPU заблокирован);
— напряжение на пинах 3.3v, что лучше чем 1.8v для любителя как я;
— Родная MIPI CSI камера OV5647.
Я сделал следующее — подключил Raspberry Pi, 3G modem, камеру и инфракрасный датчик. Через инфракрасный пульт я могу отдавать комманды: включить/выключить интернет, транслировать живое видео тремя разными способами, отсылать имейл ссылкой на видео трансляцию. Как это использовать я оставляю за кадром видео трансляции.
Raspberry Pi до сих пор умеет работать только с 5MP камерой OV5647 (достаточно старая и слабая), хотя аналогичный броадкомовский чип в какой-то нокии работает с 42MP камерой, даже после того как документация по GPU VideoCore IV стала доступна никакого намека на разнообразие не появилось. На альтернативных платах ситуация нисколько не лучше и там используются в основном USB камеры.
Большинство USB камер сжимают каждый кадр в jpeg и шлют этот jpeg по USB. Драйвер камеры разжимает jpeg… Поэтому качество фото и видео через CSI камеру заметно лучше, чем через USB. Именно камера в Pi это киллер фича, хотя если кто-то (ау Интел) реализует плату с поддержкой камеры на уровне GoPro, то конечно это будет очень интересно.
На Pi я надел плату DVK512 на ней есть несколько выводов, 4 леда и 4 кнопки.
Еще я прицепил инфракрасный датчик и 3G модем. Программировал я все это на питоне и баше под рутом. Чтобы мигать ледом и выходить в инет по 3G, надо быть рутом.
За работу с инфракрасным пультом отвечает пакет lirc. В него входит команда irrecord, которая в теории может научиться работать с любым пультом, а можно скачать конфиг для вашего пульта из интернета.
apt-get install lirc
Большинство 3G модемов будет опознано, если сделать так
sudo apt-get install usb-modeswitch
,
Но чтобы нормально работать с USB я добавил udev правило. По умолчанию, когда вы втыкаете что-либо в USB, то название устройства может получить непредсказуемо, а один 3G modem может сразу отразиться четырьмя устройствами ttyUSB0, ttyUSB1, ttyUSB2, ttyUSB3. Эту проблему можно решить, написав udev правило, и система будет давать строго определенное имя для устройства.
/dev/modems/nova_091095493721000_1-1.5
Такое имя означает: 091095493721000 — уникальный сериал моего модема, 1.5 — первый и единственный USB хаб, 5й USB порт. (в первый порт вроде подключен езернет)
Если os.path.exists('/dev/modems/nova_091095493721000_1-1.5'), то модем вставлен и надо мигать ледом.
Вот само udev правило
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"
Суть программы — это цикл, который если нажата кнопка, запускает или останавливает процесс, если процесс запущен, то зажигается соответствующий лед.
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"
Класс Proc запускает программы и умеет останавливать их вместе с их подпроцессами. Пакет GPIO используется для мигания ледом, а пакет lirc, для чтения инфракрасного датчика.
Первым делом программа запускает скрипт webserver.sh
#!/bin/bash
cd video
python -m SimpleHTTPServer
это сервер нужен для HLS видео трансляции на iPhone. iPhone очень притязательное устройство и видео принимает строго определенных характеристик.
dial.sh — скрипт для выхода в интернет
#!/bin/bash
route delete default
wvdial
Если интернет пропал, скрипт умрет, если скрипт убить, то интернет пропадет. Работает достаточно предсказуемо.
Трансляция на iPhone
iPhone очень привередливое устройство и оно хочет видео в формате HLS. Нарезанные (сегментированные) по 2 секунды видео файлы, плеер айфона скачивает по HTTP. Каждый файл должен содержать особые фреймы (ключ -ih в raspivid). Если коротко, то родная программа Pi raspivid умеет вроде сегментировать, но как-то она это делает не так. avconv и ffmpeg из Raspbian тоже умеют сегментировать и тоже как-то так, вообщем 5 часом и вы скомпелите свежий ffmpeg скаченный из git. Вот этот ffmpeg будет правильно нарезать поток для трансляции в 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"
Эта трансляция работает, но задержка 40 секунд это норма.
Трансляция с задержкой менее 1 секунды
тут все просто! Надо направить поток в net cat
#!/bin/bash
raspivid -t 0 -h 240 -w 320 -fps 30 -hf -b 100000 -o - | nc -l 5001
А смотреть можно с помощью mplayer
nc 94.248.14.212 5001 | mplayer -fps 120 -cache 1024 -
но так как трансляция должна начаться раньше, то mplayer должен догнать трансляцию. Поэтому надо ставить -fps выше, чем в трансляции. Но в любом случае, когда mplayer догонит, то его будет подергивать.
Самым оптимальмым получился стриминг по gstreamer
$> 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
и соответственно смотреть
gst-launch-1.0 -v tcpclientsrc host=94.248.14.212 port=5000 ! gdpdepay ! rtph264depay ! avdec_h264 ! videoconvert ! autovideosink sync=false
В Google Play есть приложения, которые умеют показывать видео из gstreamer.
PS: цены на компоненты, чтобы никто не переплачивал. (с учетом бесплатной доставки)
6 китайских аккумуляторов 18650 — $11
1 panasonic nrc18650b — $10 (почти как в тесле)
USB банк на один 18650 $1.24
DVK 512 — $15
IR датчик + пульт — $2.17
Сейчас стоит брать Raspberry Pi 2 ~$40
PS2: У Pi все пины имеют два названия. Родные броадкомовские GPIO.setmode(GPIO.BCM) и универсальные для всех Raspberry Pi GPIO.setmode(GPIO.BOARD), но у DVK512 своя третья нумерация.
Led0 — 26 #GPIO.setmode(GPIO.BCM), он же GPIO.setmode(GPIO.BOARD) — 37, он же на DV512 P25
Led1 — 12
Led2 — 16
Led3 — 20
Key0 — 5
Key1 — 6
Key2 — 13
Key3 — 19
Happy Hacking
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий