Naprawdę utknąłem, próbując zrozumieć najlepszy sposób przesyłania strumieniowego wyniku ffmpeg w czasie rzeczywistym do klienta HTML5 za pomocą node.js, ponieważ istnieje wiele zmiennych i nie mam dużego doświadczenia w tym obszarze, spędziłem wiele godzin próbując różnych kombinacji.
Mój przypadek użycia to:
1) Strumień wideo kamery RTSP H.264 IP jest pobierany przez FFMPEG i ponownie umieszczany w kontenerze mp4 przy użyciu następujących ustawień FFMPEG w węźle, wysyłany do STDOUT. Jest to uruchamiane tylko przy początkowym połączeniu klienta, aby częściowe żądania treści nie próbowały ponownie odrodzić FFMPEG.
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:[email protected]:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
2) Używam węzłowego serwera HTTP do przechwytywania STDOUT i przesyłania strumieniowego z powrotem do klienta na żądanie klienta. Kiedy klient po raz pierwszy się łączy, spawnuję powyższy wiersz poleceń FFMPEG, a następnie przesyłam potokiem strumień STDOUT do odpowiedzi HTTP.
liveFFMPEG.stdout.pipe(resp);
Użyłem również zdarzenia stream do zapisania danych FFMPEG do odpowiedzi HTTP, ale nie robi to różnicy
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
Używam następującego nagłówka HTTP (który jest również używany i działa podczas przesyłania strumieniowego wcześniej nagranych plików)
var total = 999999999 // fake a large file
var partialstart = 0
var partialend = total - 1
if (range !== undefined) {
var parts = range.replace(/bytes=/, "").split("-");
var partialstart = parts[0];
var partialend = parts[1];
}
var start = parseInt(partialstart, 10);
var end = partialend ? parseInt(partialend, 10) : total; // fake a large file if no range reques
var chunksize = (end-start)+1;
resp.writeHead(206, {
'Transfer-Encoding': 'chunked'
, 'Content-Type': 'video/mp4'
, 'Content-Length': chunksize // large size to fake a file
, 'Accept-Ranges': 'bytes ' + start + "-" + end + "/" + total
});
3) Klient musi używać tagów wideo HTML5.
Nie mam problemów z odtwarzaniem strumieniowym (przy użyciu fs.createReadStream z 206 częściową zawartością HTTP) do klienta HTML5 plik wideo wcześniej nagrany przy użyciu powyższego wiersza polecenia FFMPEG (ale zapisany w pliku zamiast STDOUT), więc znam strumień FFMPEG jest poprawny i nawet poprawnie widzę transmisję wideo na żywo w VLC podczas łączenia się z serwerem węzła HTTP.
Jednak próba przesyłania strumieniowego na żywo z FFMPEG przez węzeł HTTP wydaje się dużo trudniejsza, ponieważ klient wyświetli jedną ramkę, a następnie zatrzyma się. Podejrzewam, że problem polega na tym, że nie konfiguruję połączenia HTTP, aby było zgodne z klientem wideo HTML5. Próbowałem różnych rzeczy, takich jak użycie HTTP 206 (częściowa treść) i 200 odpowiedzi, umieszczanie danych w buforze, a następnie przesyłanie strumieniowe bez powodzenia, więc muszę wrócić do pierwszych zasad, aby upewnić się, że skonfigurowałem to poprawnie sposób.
Oto moje rozumienie tego, jak to powinno działać, proszę mnie poprawić, jeśli się mylę:
1) FFMPEG powinien zostać skonfigurowany w celu fragmentacji danych wyjściowych i użycia pustego moov (flagi FFMPEG frag_keyframe i empty_moov mov flagi). Oznacza to, że klient nie używa atomu moov, który zwykle znajduje się na końcu pliku, co nie jest istotne podczas przesyłania strumieniowego (bez końca pliku), ale oznacza, że nie można szukać, co jest dobre w moim przypadku użycia.
2) Mimo że używam fragmentów MP4 i pustego MOOV, nadal muszę korzystać z częściowej zawartości HTTP, ponieważ odtwarzacz HTML5 będzie czekał na pobranie całego strumienia przed rozpoczęciem odtwarzania, co w przypadku transmisji na żywo nigdy się nie kończy, więc jest to niemożliwe.
3) Nie rozumiem, dlaczego przesyłanie strumieniowe strumienia STDOUT do odpowiedzi HTTP nie działa jeszcze podczas przesyłania strumieniowego na żywo, jeśli zapiszę do pliku, mogę łatwo przesyłać strumieniowo ten plik do klientów HTML5 przy użyciu podobnego kodu. Być może jest to kwestia synchronizacji, ponieważ uruchomienie FFMPEG zajmuje sekundę, połączenie z kamerą IP i wysyłanie fragmentów do węzła, a zdarzenia danych węzła również są nieregularne. Jednak bajtowanie powinno być dokładnie takie samo jak zapisywanie w pliku, a HTTP powinien być w stanie zaspokoić opóźnienia.
4) Podczas sprawdzania dziennika sieciowego z klienta HTTP podczas przesyłania strumieniowego pliku MP4 utworzonego przez FFMPEG z kamery widzę, że istnieją 3 żądania klienta: Ogólne żądanie GET dla wideo, które serwer HTTP zwraca około 40 KB, a następnie częściowe żądanie zawartości z zakresem bajtów dla ostatnich 10 KB pliku, a następnie końcowe żądanie dla bitów w środku nie załadowane. Może klient HTML5 po otrzymaniu pierwszej odpowiedzi prosi o załadowanie atomu MP4 MOOV ostatniej części pliku? W takim przypadku nie będzie działać w przypadku przesyłania strumieniowego, ponieważ nie ma pliku MOOV i nie ma końca pliku.
5) Podczas sprawdzania dziennika sieciowego podczas próby przesyłania strumieniowego na żywo otrzymuję przerwane początkowe żądanie z odebranymi około 200 bajtami, a następnie ponowne żądanie ponownie przerwane z 200 bajtami i trzecim żądaniem, które ma tylko 2 KB długości. Nie rozumiem, dlaczego klient HTML5 przerwałby żądanie, ponieważ strumień bajtów jest dokładnie taki sam, jak mogę z powodzeniem używać podczas przesyłania strumieniowego z nagranego pliku. Wygląda również na to, że węzeł nie wysyła pozostałej części strumienia FFMPEG do klienta, ale widzę dane FFMPEG w procedurze zdarzeń .on, więc dociera ono do serwera HTTP węzła FFMPEG.
6) Chociaż myślę, że potokowanie strumienia STDOUT do bufora odpowiedzi HTTP powinno działać, czy muszę zbudować bufor pośredni i strumień, który pozwoli, aby żądania klienta częściowej treści HTTP działały poprawnie tak jak wtedy, gdy (z powodzeniem) odczyta plik ? Myślę, że to jest główny powód moich problemów, ale nie jestem do końca pewien w Node, jak najlepiej to skonfigurować. I nie wiem, jak obsłużyć żądanie klienta dotyczące danych na końcu pliku, ponieważ nie ma końca pliku.
7) Czy jestem na niewłaściwej ścieżce, próbując obsłużyć 206 częściowych żądań treści i czy powinno to działać z normalnymi 200 odpowiedziami HTTP? Odpowiedzi HTTP 200 działają dobrze dla VLC, więc podejrzewam, że klient wideo HTML5 będzie działał tylko z częściowymi żądaniami treści?
Ponieważ wciąż uczę się tego, trudno jest przejść przez różne warstwy tego problemu (FFMPEG, węzeł, streaming, HTTP, HTML5 wideo), więc wszelkie wskazówki będą mile widziane. Spędziłem godziny badając tę stronę i sieć, i nie spotkałem nikogo, kto byłby w stanie transmitować w czasie rzeczywistym w węźle, ale nie mogę być pierwszy i myślę, że to powinno zadziałać (jakoś !).
Content-Type
sobie głowę? Czy używasz kodowania fragmentów? Od tego bym zaczął. Ponadto HTML5 nie musi zapewniać funkcji przesyłania strumieniowego, więcej informacji na ten temat można znaleźć tutaj . Najprawdopodobniej będziesz musiał zaimplementować sposób buforowania i odtwarzania strumienia wideo przy użyciu własnych środków ( patrz tutaj ), ponieważ prawdopodobnie nie jest to dobrze obsługiwane. Również Google w API MediaSource.Odpowiedzi:
Wszystko poniżej tego wiersza jest nieaktualne. Trzymam go tutaj dla potomności.
Istnieje wiele powodów, dla których wideo, a szczególnie wideo na żywo, jest bardzo trudne. (Należy pamiętać, że pierwotne pytanie określało, że wideo HTML5 jest wymagane, ale pytający stwierdził, że Flash jest możliwy w komentarzach. Więc natychmiast to pytanie wprowadza w błąd)
Najpierw powtórzę: NIE MA OFICJALNEGO WSPARCIA DLA TRANSMISJI NA ŻYWO NA HTML5 . Są hacki, ale przebieg może się różnić.
Następnie musisz zrozumieć, że Wideo na żądanie (VOD) i wideo na żywo są bardzo różne. Tak, oba są filmami, ale problemy są różne, stąd formaty są różne. Na przykład, jeśli zegar w twoim komputerze działa o 1% szybciej niż powinien, nie zauważysz na VOD. W przypadku wideo na żywo będziesz próbował odtworzyć wideo, zanim to nastąpi. Jeśli chcesz dołączyć do trwającego strumienia wideo na żywo, potrzebujesz danych niezbędnych do zainicjowania dekodera, więc musisz je powtórzyć w strumieniu lub wysłać poza pasmem. Dzięki VOD możesz odczytać początek pliku, którego szukają, w dowolnym momencie.
Teraz zagłębmy się trochę.
Platformy:
Kodeki:
Typowe metody dostarczania wideo na żywo w przeglądarkach:
Typowe metody dostarczania VOD w przeglądarkach:
Tag wideo HTML5:
Przyjrzyjmy się, które przeglądarki obsługują formaty
Safari:
Firefox
TO ZNACZY
Chrom
MP4 nie może być używany do wideo na żywo (UWAGA: DASH jest nadzbiorem MP4, więc nie myl się z tym). MP4 jest podzielony na dwie części: moov i mdat. mdat zawiera surowe dane audio wideo. Ale nie jest indeksowany, więc bez moov jest bezużyteczny. Moov zawiera indeks wszystkich danych w mdat. Jednak ze względu na swój format nie można go „spłaszczyć”, dopóki nie zostaną znane znaczniki czasu i rozmiar KAŻDEJ ramki. Może być możliwe skonstruowanie moov, który „zgina” rozmiary ramek, ale jest bardzo marnotrawny pod względem przepustowości.
Więc jeśli chcesz dostarczać wszędzie, musimy znaleźć najmniejszy wspólny mianownik. Zobaczysz, że nie ma tutaj LCD bez uciekania się do przykładu flasha:
Najbliżej wyświetlacza LCD jest użycie HLS, aby zdobyć użytkowników iOS i flashować dla wszystkich innych. Moim osobistym ulubionym jest kodowanie HLS, a następnie użycie Flasha, aby grać w HLS dla wszystkich innych. Możesz grać w HLS we flashu za pomocą JW player 6 (lub napisać własny HLS do FLV w AS3, tak jak ja to zrobiłem)
Wkrótce najpopularniejszym sposobem na to będzie HLS na iOS / Mac i DASH przez MSE wszędzie indziej (właśnie to Netflix zrobi wkrótce). Nadal jednak czekamy, aż wszyscy zaktualizują swoje przeglądarki. Prawdopodobnie będziesz potrzebować osobnego DASH / VP9 dla Firefoksa (wiem o open264; jest do bani. Nie może robić wideo w profilu głównym lub wysokim. Więc jest obecnie bezużyteczny).
źródło
Dziękuję wszystkim, zwłaszcza szatmary, ponieważ jest to złożone pytanie i ma wiele warstw, wszystkie muszą działać, zanim będzie można przesyłać strumieniowo wideo na żywo. Aby wyjaśnić moje oryginalne pytanie i użycie wideo HTML5 w porównaniu do flasha - mój przypadek użycia ma silną preferencję dla HTML5, ponieważ jest ogólny, łatwy do wdrożenia na kliencie i przyszłości. Flash jest drugim co do wielkości drugim, więc trzymajmy się HTML5 w przypadku tego pytania.
Wiele się nauczyłem dzięki temu ćwiczeniu i zgadzam się, że transmisja na żywo jest znacznie trudniejsza niż VOD (który działa dobrze z wideo HTML5). Ale sprawiłem, że działało to zadowalająco w moim przypadku użycia, a rozwiązanie okazało się bardzo proste, po ściganiu bardziej złożonych opcji, takich jak MSE, flash, skomplikowane schematy buforowania w węźle. Problem polegał na tym, że FFMPEG uszkadzał pofragmentowane MP4 i musiałem dostroić parametry FFMPEG, a wystarczyło standardowe przekierowanie potoku strumienia węzłów przez http, którego użyłem pierwotnie.
W MP4 dostępna jest opcja „fragmentacji”, która dzieli MP4 na znacznie mniejsze fragmenty, które mają swój własny indeks i sprawia, że opcja MP4 na żywo jest wykonalna. Ale nie można szukać z powrotem w strumieniu (OK w moim przypadku użycia), a nowsze wersje FFMPEG obsługują fragmentację.
Czas może być problemem, a moje rozwiązanie ma opóźnienie od 2 do 6 sekund spowodowane kombinacją remuxingu (w rzeczywistości FFMPEG musi odbierać strumień na żywo, remux, a następnie wysłać go do węzła w celu obsługi przez HTTP) . Niewiele można na to poradzić, jednak w Chrome wideo stara się nadrobić tyle, ile może, co sprawia, że film jest nieco nerwowy, ale bardziej aktualny niż IE11 (mój preferowany klient).
Zamiast wyjaśniać, jak działa kod w tym poście, sprawdź GIST z komentarzami (kod klienta nie jest uwzględniony, jest to standardowy tag wideo HTML5 z adresem serwera węzła http). GIST jest tutaj: https://gist.github.com/deandob/9240090
Nie udało mi się znaleźć podobnych przykładów tego przypadku użycia, więc mam nadzieję, że powyższe wyjaśnienie i kod pomogą innym, zwłaszcza że nauczyłem się wiele z tej strony i nadal uważam się za początkującego!
Chociaż jest to odpowiedź na moje konkretne pytanie, wybrałem odpowiedź szatmary jako zaakceptowaną, ponieważ jest najbardziej wyczerpująca.
źródło
Spójrz na JSMPEG projektu. Zaimplementowano tam świetny pomysł - dekodowanie MPEG w przeglądarce za pomocą JavaScript. Bajty z kodera (na przykład FFMPEG) mogą być przesyłane do przeglądarki za pomocą na przykład WebSockets lub Flash. Jeśli społeczność nadrobi zaległości, myślę, że na razie będzie to najlepsze rozwiązanie do przesyłania strumieniowego wideo HTML5.
źródło
Napisałem odtwarzacz wideo HTML5 wokół kodeka broadway h264 (emscripten), który może odtwarzać na żywo (bez opóźnień) wideo h264 we wszystkich przeglądarkach (komputer, iOS, ...).
Strumień wideo jest wysyłany przez websocket do klienta, dekodowany klatka na klatkę i wyświetlany w kanwie (używając webgl do przyspieszenia)
Sprawdź https://github.com/131/h264-live-player na github.
źródło
Jednym ze sposobów przesyłania strumieniowego na żywo kamery internetowej opartej na RTSP do klienta HTML5 (wymaga ponownego kodowania, więc spodziewaj się utraty jakości i potrzebuje trochę mocy procesora):
Na komputerze odbierającym strumień z kamery nie używaj FFMPEG, ale gstreamer. Jest w stanie odbierać i dekodować strumień RTSP, ponownie go kodować i przesyłać strumieniowo na serwer Icecast. Przykładowy potok (tylko wideo, bez dźwięku):
=> Następnie możesz użyć znacznika <video> z adresem URL strumienia icecast-stream ( http://127.0.0.1:12000/cam.webm ) i będzie on działał w każdej przeglądarce i urządzeniu obsługującym webm
źródło
Spójrz na to rozwiązanie . Jak wiem, Flashphoner pozwala odtwarzać strumień audio + wideo na żywo na czystej stronie HTML5.
Do odtwarzania używają kodeków MPEG1 i G.711 . Włamanie polega na renderowaniu dekodowanego wideo do elementu płótna HTML5 i odtwarzaniu dekodowanego dźwięku w kontekście audio HTML5.
źródło
Co powiesz na użycie rozwiązania JPEG, po prostu pozwól serwerowi dystrybuować JPEG jeden po drugim do przeglądarki, a następnie użyj elementu canvas, aby narysować te JPEG? http://thejackalofjavascript.com/rpi-live-streaming/
źródło
To bardzo powszechne nieporozumienie. Nie ma obsługi wideo na żywo HTML5 (z wyjątkiem HLS na iOS i Mac Safari). Być może będziesz w stanie „zhakować” go za pomocą kontenera webm, ale nie spodziewałbym się, że będzie on powszechnie obsługiwany. To, czego szukasz, znajduje się w rozszerzeniach Media Source, w których możesz podawać fragmenty do przeglądarki pojedynczo. ale będziesz musiał napisać javascript po stronie klienta.
źródło
solutions
ale nie masupport
transmisji na żywo. Odnosi się to bezpośrednio do mojego komentarza przedstawionego powyżej. Webm jest obsługiwany w głównych przeglądarkach, głównie w najnowszej stabilnej wersji.Wypróbuj binaryjs. To jest tak jak socket.io, ale jedyne, co robi dobrze, to strumieniowe przesyłanie audio wideo. Binaryjs google go
źródło