Я дійсно застряг у спробі зрозуміти найкращий спосіб передавати вихідний файл ffmpeg в реальному часі до клієнта HTML5 за допомогою node.js, оскільки в грі є безліч змінних, і я не маю багато досвіду в цьому просторі, витративши багато годин на випробування різних комбінацій.
Мій випадок використання:
1) IP-відеокамера RTSP H.264 потік підбирається FFMPEG і повторно передається в контейнер mp4, використовуючи наступні параметри FFMPEG у вузлі, виводячи в STDOUT. Це запускається лише на початковому з'єднанні з клієнтом, так що часткові запити щодо вмісту більше не намагаються породжувати FFMPEG.
liveFFMPEG = child_process.spawn("ffmpeg", [
"-i", "rtsp://admin:12345@192.168.1.234:554" , "-vcodec", "copy", "-f",
"mp4", "-reset_timestamps", "1", "-movflags", "frag_keyframe+empty_moov",
"-" // output to stdout
], {detached: false});
2) Я використовую сервер http вузла для зйомки STDOUT та передачі потоку назад до клієнта за запитом клієнта. Коли клієнт вперше підключається, я створюю вищевказаний командний рядок FFMPEG, а потім передаю потік STDOUT у відповідь HTTP.
liveFFMPEG.stdout.pipe(resp);
Я також використовував подію потоку для запису даних FFMPEG у відповідь HTTP, але це не має ніякої різниці
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
Я використовую наступний заголовок HTTP (який також використовується і працює під час передавання поточно записаних файлів)
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) Клієнт повинен використовувати відеотеги HTML5.
У мене немає проблем із потоковим відтворенням (використовуючи fs.createReadStream з частковим вмістом 206 HTTP) для клієнта HTML5 відеофайл, записаний раніше за вказаним вище командним рядком FFMPEG (але збережений у файл замість STDOUT), тому я знаю потік FFMPEG є правильним, і я навіть можу правильно бачити потокове відео в VLC під час підключення до сервера вузлів HTTP.
Однак намагатися пряму трансляцію з FFMPEG через вузол HTTP виявляється набагато складніше, оскільки клієнт відображатиме один кадр, а потім зупиняється. Я підозрюю, що проблема полягає в тому, що я не встановлюю HTTP-з'єднання сумісним із відео-клієнтом HTML5. Я спробував різноманітні речі, як-от використання HTTP 206 (частковий вміст) та 200 відповідей, введення даних у буфер, потім потокове безрезультатно, тому мені потрібно повернутися до перших принципів, щоб переконатися, що я налаштував це правильно шлях.
Ось моє розуміння того, як це має працювати, будь ласка, виправте мене, якщо я помиляюся:
1) FFMPEG має бути налаштований для фрагментації виводу та використання порожнього moov (FFMPEG frag_keyframe та prav_moov mov прапори). Це означає, що клієнт не використовує атом moov, який, як правило, знаходиться в кінці файлу, що не є релевантним при потоковому передачі (немає кінця файлу), але означає, що не потрібно шукати можливих, що добре для мого випадку використання.
2) Незважаючи на те, що я використовую фрагменти MP4 та порожній MOOV, мені все одно доводиться використовувати частковий контент HTTP, оскільки програвач HTML5 буде чекати, поки весь потік не буде завантажений перед відтворенням, який із прямим потоком ніколи не закінчується, тому він не є працездатним.
3) Я не розумію, чому підключення потоку STDOUT до відповіді HTTP не працює при потоковому трансляції, якщо я зберігаю у файл, я можу легко передати цей файл клієнтам HTML5 за допомогою подібного коду. Можливо, це проблема з тимчасовим терміном, оскільки для початку нересту FFMPEG потрібно зайняти секунду, підключитися до IP-камери та відправити шматки до вузла, а події даних вузла також нерегулярні. Однак bytestream має бути точно таким же, як збереження у файлі, а HTTP повинен мати можливість задовольнити затримки.
4) Під час перевірки мережевого журналу від HTTP-клієнта під час передачі з камери файлу MP4, створеного FFMPEG, я бачу, що є 3 запити клієнта: загальний GET-запит на відео, якому HTTP-сервер повертає приблизно 40Kb, потім частковий запит на вміст з байтовим діапазоном за останні 10 К файлу, потім остаточний запит для бітів посередині не завантажується. Може бути, клієнт HTML5, коли він отримує першу відповідь, запитує останню частину файлу, щоб завантажити атом MP4 MOOV? У такому випадку він не працюватиме для потокової передачі, оскільки немає MOOV-файлу та немає кінця.
5) Перевіряючи мережевий журнал при спробі трансляції в прямому ефірі, я отримую перерваний початковий запит, отриманий лише близько 200 байтів, потім повторний запит перервано на 200 байт і третій запит, довжиною якого є лише 2 К. Я не розумію, чому клієнт HTML5 скасовує запит, оскільки bytestream точно такий же, як я можу успішно використовувати під час трансляції із записаного файлу. Також здається, що вузол не надсилає решту потоку FFMPEG клієнту, але я можу бачити дані FFMPEG у процедурі події .on, тому він потрапляє на HTTP-сервер вузла FFMPEG.
6) Хоча я думаю, що передача потоку STDOUT до буфера відповідей HTTP повинна працювати, чи потрібно будувати проміжний буфер і потік, який дозволить клієнтові часткового запиту вмісту HTTP правильно працювати, як це відбувається, коли він (успішно) читає файл ? Я думаю, що це головна причина моїх проблем, однак я не точно впевнений у Node, як найкраще це налаштувати. І я не знаю, як обробити клієнтський запит на дані в кінці файлу, оскільки немає кінця файлу.
7) Чи я не так, намагаючись обробити 206 часткових запитів на вміст, і чи потрібно це працювати з нормальними 200 HTTP-відповідями? Відповіді HTTP 200 добре працюють для VLC, тому я підозрюю, що відео-клієнт HTML5 працюватиме лише з частковими запитами на вміст?
Оскільки я все ще вивчаю цей матеріал, йому важко працювати через різні шари цієї проблеми (FFMPEG, вузол, потокове передавання, HTTP, HTML5 відео), тому будь-які вказівники будуть дуже вдячні. Я витратив години на дослідження на цьому веб-сайті та в мережі, і я не натрапив на когось, хто зміг би здійснювати потокове передавання в реальному часі у вузлі, але я не можу бути першим, і я думаю, що це має бути в змозі працювати (якось !).
Content-Type
в голову? Використовуєте кодування фрагментів? Ось з чого я б почав. Крім того, HTML5 не обов'язково надає функціональність для передачі потоку, ви можете прочитати більше про це тут . Вам, швидше за все, потрібно буде реалізувати спосіб буферизації та відтворення відеопотоку власними засобами ( див. Тут ), вважаючи, що це, ймовірно, недостатньо підтримується. Також перейдіть в Google MediaSource API.