Трансляция видео с Raspberry Pi по 3G тремя способами

    Как хобби я выбрал 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
      А для чего собираетесь использовать данный проект? Или просто «была идея-сделал»?
        0
        Это все зависит от денег и времени. Идея была давно, а вот так закончено только сейчас. Получается медленней чем хотелось. Может мои опыт помогут кому-то двигаться быстрей.

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

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

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

        Так у меня их несколько я подумываю сделать свой видео регистратор. Даже хотел вначале потролить ребят, что у меня тоже получилось отечественное производство, конечно не Super FullHD, а просто FullHD, то тоже не плохо, так еще и дешевле )))
          0
          Как мне показалось качество картинки не дотягивает до хорошего регистратора? Да и для домашнего видеонаблюдения соотношение качество цена относительно готовых ip камер тоже не очень получается?
            0
            BCM2835, что в B+ это достаточно старый процессор, новый видео регистраторы вероятно снимают лучше.

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

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

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

            Вот видео на 2:03 коптер на земле. Его не трясет, когда трясет сложно, что то сказать, стабилизация камеры отдельная наука. На разных видео стабилизация реализована по разному.
            0
            Большинство USB камер сжимают каждый кадр в jpeg и шлют этот jpeg по USB. Драйвер камеры разжимает jpeg

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

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

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

              В плане цены, веса и размера камера у RPi точно побеждает. Всё зависит от целей.
                0
                да mjpeg он самый, но распи жмет h264. h264 жмет сильней и качественней и для 3G это хорошо.

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

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

                    и тут же
                    после перевода картинки в YUV пространство, что то теряет в сочности цвета

                    Ээээ… А в каком пространстве, по-вашему, h264 работает? И что представляют из себя h264 keyframes?
                    0
                    В цитате на строчку выше были слова " качество фото и видео через CSI камеру заметно лучше", и мой комментарий именно про это.
                  +1
                  А для чего нужны 7 аккумуляторов 18650 и USB банк на один элемент, которые указаны в списке на компоненты?
                    0
                    Нужен 1. Я купил 6 китайских за $10. Написал за сколько я купил, и что он работает.
                    китайский на 6000-8000mah реально 1000mah — но его хватает, чтобы запустить Pi с 3G трансляцией
                    panasonic nrc18650b 3200mah реально 3200mah — на таких коптеры ставят рекорды по самого долгому полету.

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

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

                      Но RPi я бы питал от постоянного источника если трансляция нужна постоянная. Но если нужна портативность, то есть блоки питания на 2-4 элемента, что увеличивает время работы соотвественно.
                        0
                        и токотдача выше, если несколько батареек, работать однозначно лучше будет. Когда батарейка садилась 3G модем начинал хуже ловить и медленней передавать.
                          0
                          Там ток не так критичен, так как RPi с камерой потребляет порядка 300-400mA. Не думаю, что 3G модем много добавит.
                            0
                            Изначально, я все сделал на батарейке от iPod Touch, но ее для 3G не хватало.

                            Когда батарейка 18650 начинает садится, то скорость интернета падает, и коннектится не с первого раза.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое