Tôi thực sự bế tắc khi cố gắng hiểu cách tốt nhất để truyền phát đầu ra thời gian thực của ffmpeg sang máy khách HTML5 bằng cách sử dụng node.js, vì có một số biến khi chơi và tôi không có nhiều kinh nghiệm trong không gian này, đã dành nhiều giờ để thử các kết hợp khác nhau.
Trường hợp sử dụng của tôi là:
1) Luồng máy quay video IP RTSP H.264 được FFMPEG chọn và chuyển sang bộ chứa mp4 bằng các cài đặt FFMPEG sau trong nút, xuất ra STDOUT. Điều này chỉ chạy trên kết nối máy khách ban đầu, do đó, các yêu cầu nội dung một phần không cố gắng sinh ra FFMPEG một lần nữa.
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) Tôi sử dụng máy chủ http của nút để chụp STDOUT và truyền phát lại cho máy khách theo yêu cầu của máy khách. Khi máy khách kết nối lần đầu tiên tôi sinh ra dòng lệnh FFMPEG ở trên, sau đó chuyển luồng STDOUT sang phản hồi HTTP.
liveFFMPEG.stdout.pipe(resp);
Tôi cũng đã sử dụng sự kiện truyền phát để ghi dữ liệu FFMPEG vào phản hồi HTTP nhưng không có sự khác biệt
xliveFFMPEG.stdout.on("data",function(data) {
resp.write(data);
}
Tôi sử dụng tiêu đề HTTP sau (cũng được sử dụng và hoạt động khi truyền phát các tệp được ghi trước)
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) Máy khách phải sử dụng thẻ video HTML5.
Tôi không gặp vấn đề gì với phát lại phát trực tuyến (sử dụng fs.createReadStream với 206 nội dung một phần HTTP) cho máy khách HTML5 một tệp video được ghi lại trước đó bằng dòng lệnh FFMPEG ở trên (nhưng được lưu vào tệp thay vì STDOUT), vì vậy tôi biết luồng FFMPEG là chính xác và tôi thậm chí có thể thấy chính xác video phát trực tiếp trong VLC khi kết nối với máy chủ nút HTTP.
Tuy nhiên, việc cố gắng phát trực tiếp từ FFMPEG qua nút HTTP có vẻ khó hơn rất nhiều vì máy khách sẽ hiển thị một khung rồi dừng lại. Tôi nghi ngờ vấn đề là tôi không thiết lập kết nối HTTP để tương thích với ứng dụng video HTML5. Tôi đã thử nhiều cách như sử dụng HTTP 206 (nội dung một phần) và 200 phản hồi, đưa dữ liệu vào bộ đệm sau đó phát trực tuyến mà không gặp may, vì vậy tôi cần quay lại các nguyên tắc đầu tiên để đảm bảo tôi thiết lập đúng đường.
Dưới đây là sự hiểu biết của tôi về cách thức hoạt động của nó, xin vui lòng sửa cho tôi nếu tôi sai:
1) FFMPEG nên được thiết lập để phân đoạn đầu ra và sử dụng một moov trống (FFMPEG Frag_keyframe và blank_moov Mov flags). Điều này có nghĩa là khách hàng không sử dụng nguyên tử moov thường ở cuối tập tin không liên quan khi phát trực tuyến (không có phần cuối của tập tin), nhưng có nghĩa là không thể tìm kiếm điều gì tốt cho trường hợp sử dụng của tôi.
2) Mặc dù tôi sử dụng các đoạn MP4 và MOOV trống, tôi vẫn phải sử dụng nội dung một phần HTTP, vì trình phát HTML5 sẽ đợi cho đến khi toàn bộ luồng được tải xuống trước khi phát, với luồng trực tiếp không bao giờ kết thúc nên không thể thực hiện được.
3) Tôi không hiểu tại sao đường truyền luồng STDOUT đến phản hồi HTTP không hoạt động khi phát trực tiếp nếu tôi lưu vào tệp Tôi có thể truyền tệp này dễ dàng đến các máy khách HTML5 bằng mã tương tự. Có lẽ đó là một vấn đề thời gian vì phải mất một giây để sinh sản FFMPEG bắt đầu, kết nối với camera IP và gửi các đoạn đến nút và các sự kiện dữ liệu nút cũng không thường xuyên. Tuy nhiên, bytestream phải chính xác giống như lưu vào một tệp và HTTP có thể phục vụ cho sự chậm trễ.
4) Khi kiểm tra nhật ký mạng từ máy khách HTTP khi phát trực tuyến tệp MP4 được tạo bởi FFMPEG từ máy ảnh, tôi thấy có 3 yêu cầu máy khách: Yêu cầu GET chung cho video, máy chủ HTTP trả về khoảng 40Kb, sau đó một phần yêu cầu nội dung với phạm vi byte cho 10K cuối cùng của tệp, sau đó yêu cầu cuối cùng cho các bit ở giữa không được tải. Có lẽ ứng dụng khách HTML5 sau khi nhận được phản hồi đầu tiên đang yêu cầu phần cuối của tệp để tải nguyên tử MP4 MOOV? Nếu đây là trường hợp, nó sẽ không hoạt động để phát trực tuyến vì không có tệp MOOV và không có kết thúc của tệp.
5) Khi kiểm tra nhật ký mạng khi cố gắng phát trực tiếp, tôi nhận được yêu cầu ban đầu bị hủy chỉ với khoảng 200 byte nhận được, sau đó yêu cầu lại lại bị hủy bỏ với 200 byte và yêu cầu thứ ba chỉ dài 2K. Tôi không hiểu tại sao máy khách HTML5 sẽ hủy bỏ yêu cầu vì bytestream hoàn toàn giống với tôi có thể sử dụng thành công khi truyền phát từ một tệp được ghi. Có vẻ như nút không gửi phần còn lại của luồng FFMPEG đến máy khách, tuy nhiên tôi có thể thấy dữ liệu FFMPEG trong thói quen sự kiện .on để nó đến máy chủ HTTP của nút FFMPEG.
6) Mặc dù tôi nghĩ rằng việc truyền luồng STDOUT vào bộ đệm phản hồi HTTP sẽ hoạt động, tôi có phải xây dựng một bộ đệm trung gian và luồng sẽ cho phép các yêu cầu máy khách nội dung một phần HTTP hoạt động đúng như khi nó (thành công) đọc một tệp ? Tôi nghĩ rằng đây là lý do chính cho các vấn đề của tôi tuy nhiên tôi không chắc chắn chính xác trong Node làm thế nào để thiết lập tốt nhất. Và tôi không biết làm thế nào để xử lý yêu cầu của khách hàng đối với dữ liệu ở cuối tệp vì không có kết thúc tệp.
7) Tôi có đang đi sai hướng khi cố gắng xử lý 206 yêu cầu nội dung một phần và điều này có nên hoạt động với 200 phản hồi HTTP bình thường không? Phản hồi HTTP 200 hoạt động tốt cho VLC vì vậy tôi nghi ngờ ứng dụng video HTML5 sẽ chỉ hoạt động với các yêu cầu nội dung một phần?
Vì tôi vẫn đang học những thứ này rất khó để xử lý các lớp khác nhau của vấn đề này (FFMPEG, nút, phát trực tuyến, HTTP, video HTML5) nên bất kỳ con trỏ nào cũng sẽ được đánh giá cao. Tôi đã dành hàng giờ để nghiên cứu trên trang web này và mạng và tôi không bắt gặp bất kỳ ai có thể phát trực tuyến thời gian thực trong nút nhưng tôi không thể là người đầu tiên và tôi nghĩ điều này sẽ có thể hoạt động (bằng cách nào đó !).
Content-Type
trong đầu của bạn? Bạn đang sử dụng mã hóa chunk? Đó là nơi tôi sẽ bắt đầu. Ngoài ra, HTML5 không nhất thiết phải cung cấp chức năng phát trực tuyến, bạn có thể đọc thêm về điều đó tại đây . Rất có thể bạn sẽ cần thực hiện một cách để đệm và phát luồng video bằng phương tiện của riêng bạn ( xem tại đây ), nghĩ rằng điều này có thể không được hỗ trợ tốt. Đồng thời google vào API MediaSource.