Как хобби я выбрал Raspberry Pi, теперь решил поделится тем, что уже получилось. Если коротко, то я реализовал трансляцию видео тремя способами: HLS для iPhone — задержка 20-40 секунд, через nc (net cat) — задержка меньше секунды, но долго буферизирует, и через gstreamer: все хорошо, но нужно смотреть через gstreamer на клиенте.



У 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

Комментарии (20)


  1. MAXXL
    20.05.2015 17:01
    +1

    А для чего собираетесь использовать данный проект? Или просто «была идея-сделал»?


    1. pyra Автор
      20.05.2015 18:00

      Это все зависит от денег и времени. Идея была давно, а вот так закончено только сейчас. Получается медленней чем хотелось. Может мои опыт помогут кому-то двигаться быстрей.

      Второе udev правило это FTDI-USB-Serial и через него у меня получилось ретранслировать телеметрию MultiWii. Я понял, что опасно подключать напрямую. В USB хорошая защита от дурака. Поэтому конечно хотелось, что бы оно поехало, поплыло или полетело.

      Еще мне интересны опыты с высокой выдержкой в raw (без jpeg), opencv. Я понял, что любая USB камера, что у меня есть проигрывает Pi, а та что и лучше в линуксе может все равно жать jpeg, так как дравйвер вероятно самый простой.

      Jetson TK1 я не находит CSI камер, хотя там есть два разьема и там рекомендуются USB3 или FireWire. Ее старший брат используется в автоиндустрии, наверное, для автопилотов.

      Так у меня их несколько я подумываю сделать свой видео регистратор. Даже хотел вначале потролить ребят, что у меня тоже получилось отечественное производство, конечно не Super FullHD, а просто FullHD, то тоже не плохо, так еще и дешевле )))


      1. MAXXL
        20.05.2015 18:07

        Как мне показалось качество картинки не дотягивает до хорошего регистратора? Да и для домашнего видеонаблюдения соотношение качество цена относительно готовых ip камер тоже не очень получается?


        1. pyra Автор
          20.05.2015 22:30

          BCM2835, что в B+ это достаточно старый процессор, новый видео регистраторы вероятно снимают лучше.

          Это возможность, сделать аналог Pi с GPU и камерой. Сейчас наступает эра IoT

          На хабре стоит добавить хаб IoT


      1. DaemonI
        20.05.2015 18:42

        На Jetson есть CSI камеры от e-con. В любом случае, RPi — это тоже не совсем нужный инструмент. Для видеорегистраторов используются чипы от Ambarella и других производителей, которые делают SoC'и для спортивных камер.


        1. pyra Автор
          20.05.2015 22:22

          Видеорегистратор — это я хотел пошутить, а так мне не понятен тег ирония. (какая это ирония если она в теге???), то написал статью как написал.

          Рассказал какие у меня планы и идеи. Может моя идея кому то понравится.


  1. Kisa_ua
    20.05.2015 17:41

    А насколько качественную/резкую картинку дает у Вас Pi-камера? У меня допустим, сфотографировать саму плату (вторую), что б можно было прочитать надписи не получилось. Может конечно еще дает недостаточное освещение в моих экспериментах.


    1. pyra Автор
      20.05.2015 22:13

      Мне лично нравится качество, когда на солнце. Конечно есть лучше. Наверное iPhone, GoPro и тд снимают лучше.

      Вот видео на 2:03 коптер на земле. Его не трясет, когда трясет сложно, что то сказать, стабилизация камеры отдельная наука. На разных видео стабилизация реализована по разному.


  1. dzhe
    20.05.2015 18:25

    Большинство USB камер сжимают каждый кадр в jpeg и шлют этот jpeg по USB. Драйвер камеры разжимает jpeg

    Мне кажется, что это не совсем верное утверждение. Есть стримеры (например, mjpg-streamer), которые пускают видео с камеры в сеть as is. Разжимать нужно в том случае, если с изображением что-то дополнительное делается (например, таймстамп накладывается), или видео переконвертируется в другой формат. И в любом случае, вряд ли вы захотите передавать по сети несжатое видео, а тогда какая разница (для качества), кто именно там его сжимает?

    Поэтому качество фото и видео через CSI камеру заметно лучше, чем через USB.

    Тоже сомнительно, что камера от Малинки лучше, чем, например, Logitech C920.


    1. nochkin
      20.05.2015 20:07

      > Тоже сомнительно, что камера от Малинки лучше, чем, например, Logitech C920.

      В плане цены, веса и размера камера у RPi точно побеждает. Всё зависит от целей.


      1. pyra Автор
        20.05.2015 22:05

        да mjpeg он самый, но распи жмет h264. h264 жмет сильней и качественней и для 3G это хорошо.

        Я точно не знаю, но у меня подозрение, что Logitech C920 умеет жать h264 в USB, который скайп транслирует прямиком. mjpg после перевода картинки в YUV пространство, что то теряет в сочности цвета.

        Может ли распи без бубна работать с Logitech C920 я не знаю.


        1. nochkin
          21.05.2015 05:36
          +1

          C920 имеет хардварный кодек h264. Я как-то игрался давно и вроде проблем завести не было, но подробности уже не вспомню.
          Но USB камер с хардварным кодеком уровня h264 не так много, да и стоят они значительно дороже камеры RPi (~$15).


  1. nochkin
    20.05.2015 20:01
    +1

    А для чего нужны 7 аккумуляторов 18650 и USB банк на один элемент, которые указаны в списке на компоненты?


    1. pyra Автор
      20.05.2015 21:55

      Нужен 1. Я купил 6 китайских за $10. Написал за сколько я купил, и что он работает.
      китайский на 6000-8000mah реально 1000mah — но его хватает, чтобы запустить Pi с 3G трансляцией
      panasonic nrc18650b 3200mah реально 3200mah — на таких коптеры ставят рекорды по самого долгому полету.

      в начале я использовал батарею от iPod, но ее мне не хватала для поключения 3G

      А по деньгам самая выгодная энергия это в китайских, но если надо дольше, и легче то тогда панасоник.


      1. nochkin
        21.05.2015 05:42

        Я больше про то, что эти компоненты не задействованы в статье никак.
        А так я обычно 18650 дёргаю из старых аккумулятором от ноутов. Там обычно порядка 2600mAh и практически бесплатно.

        Но RPi я бы питал от постоянного источника если трансляция нужна постоянная. Но если нужна портативность, то есть блоки питания на 2-4 элемента, что увеличивает время работы соотвественно.