poniedziałek, 21 października 2013

Raspberry Pi i strumieniowanie video

Na potrzeby projektu Zabawka dla Sierściucha, którego koncepcję opisałem w jednym z poprzednich wpisów potrzebuję strumieniowania video w czasie rzeczywistym, dodatkowo w postaci którą można w prosty sposób osadzić na stronie WWW i to bez użycia flash'a.

Pytałem Wujka Google co wie na ten temat, ale okazało się że spektrum możliwości niestety nie jest zbyt szerokie. Zależało mi żeby rozwiązanie działało w miarę płynnie, nie miało dużych opóźnień no i oczywiście żeby dało się je uruchomić na Raspberry Pi.

Chyba najsensowniejsze rozwiązanie które znalazłem to MJPG-streamer.
W uproszczeniu można napisać, że pobiera on z kamery statyczne obrazy i kopiuje je do pamięci. Wtyczki wyjściowe potrafią przesłać te obrazy do przeglądarki tworząc animację. Potrafi wykorzystać sprzętową kompresję wbudowaną w kamery. Dzięki temu nie zużywa dużo pamięci ani mocy procesora.

Nie mam dedykowanej kamery do Raspberry Pi, więc wykorzystałem zwykłą najtańszą kamerę USB, przechwytującą video z rozdzielczością do 640x480. Rozwiązanie jest na tyle uniwersalne że później można wymienić kamerę na lepszą :)

Jako że Raspberry Pi oparte jest o architekturę ARM, w większości przypadków programy które nie są zawarte w dystrybucji którą zainstalowaliśmy nie mają dedykowanych pakietów. Pozostaje zatem zbudowanie ich ze źródeł. Na szczęście nie stanowi to większego problemu.

Na RPI zainstalowałem dystrybucję Raspbian, która bazuje na Debianie. W tym przypadku kompilacja będzie wymagała następujących kroków (zakładam że użytkownik zalogowany jest np na koncie "pi" - lub dowolnym innym bez uprawnień administratora):

Na początek musimy zainstalować wymagane zależności. Zrobimy to następującymi poleceniami:

sudo apt-get install subversion 
sudo apt-get install libv4l-dev 
sudo apt-get install libjpeg8-dev 
sudo apt-get install imagemagick

Następnie pobieramy kod źródłowy mjpeg-streamer'a bezpośrednio z repozytorium SVN:

svn checkout svn://svn.code.sf.net/p/mjpg-streamer/code/ mjpg-streamer-code 

Przechodzimy do katalogu w którym znajdują się pobrane źródła:

cd mjpg-streamer-code/mjpg-streamer 

i kompilujemy:

make USE_LIBV4L2=true clean all 

po miejmy nadzieję poprawnej kompilacji, trzeba zainstalować zbudowaną aplikację w systemie:

sudo make DESTDIR=/usr install 

tworzymy katalog dla obrazów:

sudo mkdir /var/www/mjpg_streamer
sudo chmod a+rw /var/www/mjpg_streamer

W tym momencie możemy już przetestować czy strumieniowanie działa.
Odpalamy polecenie:

mjpg_streamer -i "/usr/lib/input_uvc.so -d /dev/video0 -r 320x240 -f 15 -y" -o "/usr/lib/output_http.so -p 8090 -w /var/www/mjpg_streamer" 

które oznacza mniej więcej tyle, że rozpoczynamy strumieniowanie obrazu z urządzenia /dev/video0 (kamera usb) o rozdzielczości 320x240, z framerate 15 FPS używając kodeka YUYV (opcja -y), na porcie 8090 wykorzystując folder /var/www/mjpeg_streamer

W przypadku mojej kamery opcja -y była niezbędna -  kamera nie wspierała dekodowania MJPEG. W przypadku lepszych kamer powinno zadziałać bez tej opcji - co generalnie jest rozwiązaniem lepszym, ponieważ dekodowanie MJPEG jest podobno wydajniejsze od YUYV.

Żeby przetestować działanie strumienia trzeba spreparować sobie plik html w takiej postaci:

<html><body>
<img src="http://IP_RASPBERRY_PI:8090?action=stream">
</body></html>

I otworzyć go w przeglądarce. Okienko powinno zawierać obraz z kamery.

Gdy już mamy działający strumień, możemy przekonfigurować system aby strumieniowanie uruchamiało się automatycznie przy starcie.

W tym celu tworzymy plik stream.sh:

 sudo nano /usr/bin/stream.sh 

i dodajemy do niego polecenie uruchamiające strumień, czyli ponownie:

mjpg_streamer -i "/usr/lib/input_uvc.so -d /dev/video0 -r 320x240 -f 15 -y" -o "/usr/lib/output_http.so -p 8090 -w /var/www/mjpg_streamer" 


i zapisujemy plik. Nadajemy mu uprawnienia wykonywalne:

 sudo chmod a+x /usr/bin/stream.sh

i tworzymy dowiązanie symboliczne w /etc/init.d

 sudo ln -s /usr/bin/stream.sh /etc/init.d/stream.sh

Dodajemy utworzony skrypt do automatycznego startu poleceniem:

 sudo update-rc.d stream.sh defaults 94 6

W tym momencie po restarcie systemu strumień powinien być automatycznie dostępny.
Docelowo w projekcie to Raspberry ma serwować stronę WWW z obrazem wideo. Dlatego musimy zainstalować na malince Apache + PHP.

Wystarczy polecenie:
 sudo apt-get install php5 

Serwer Apache jest w zależnościach PHP więc zainstaluje się automatycznie.

Chcemy aby nasza strona docelowo była generowana dynamicznie, zatem zamieniamy statyczny plik index.html:

sudo rm /var/www/index.html

na plik /var/www/index.php o następującej zawartości:

Odpalenie w przeglądarce adresu:
http://IP_RASPBERRY_PI

powinno wyświetlić serwowane przez Malinkę wideo.

Z wstępnych testów wynika że przy strumieniowaniu przez WIFI obraz w rozdzielczości 320x240 przy 15 FPS działa praktycznie bez żadnych opóźnień. Strumień 640x480 przy 15FPS ma około 2-3 sekundy opóźnienia - co w praktyce może okazać się akceptowalnym wynikiem. Nie badałem jeszcze skąd te opóźnienia. Przyczyn może być kilka - wolne WIFI, słaba kamera bez obsługi MJPEG...
W każdym razie obciążenie CPU w Raspberry Pi jest praktycznie niezauważalne.

Jeśli macie lepsze propozycje przesyłania strumienia, chętnie się o nich dowiem - temat jest ciągle otwarty :)

4 komentarze:

  1. Witam,
    chcę obrócić obraz.Gdzie znajdę plik input_raspi.so wykorzystujący parametr rotation i jak go zainstalować?
    Pozdrawiam

    OdpowiedzUsuń
    Odpowiedzi
    1. Znalazłem wersję eksperymentalną z tą wtyczką: https://github.com/jacksonliam/mjpg-streamer
      Można używać 2 wtyczek na raz?

      Usuń
    2. Odpowiem sobie samemu: Dwie wtyczki na raz nie działają, żeby obrócić obraz użyłem eksperymentalnej wersji z wtyczką input_raspi.so:
      mjpg_streamer -i "/usr/lib/input_raspicam.so -x 640 -y 480 -fps 15 -rot 180" -o "/usr/lib/output_http.so -p 8090 -w /var/www/mjpg_streamer" MJ

      Usuń
  2. Bardzo dobry wpis. Pozdrawiam serdecznie.

    OdpowiedzUsuń