node.js를 사용하여 ffmpeg의 실시간 출력을 HTML5 클라이언트로 스트리밍하는 가장 좋은 방법을 이해하려고 노력하고 있습니다. 여기에 많은 변수가 있으며이 공간에 대한 경험이 많지 않기 때문에, 다른 조합을 시도하는 데 많은 시간을 보냈습니다.
내 유스 케이스는 다음과 같습니다.
1) IP 비디오 카메라 RTSP H.264 스트림은 FFMPEG에 의해 픽업되고 노드의 다음 FFMPEG 설정을 사용하여 mp4 컨테이너로 리 뮤직되어 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 비디오 태그를 사용해야합니다.
위의 FFMPEG 명령 줄로 이전에 기록 된 비디오 파일 (STDOUT 대신 파일에 저장 됨)을 HTML5 클라이언트에 스트리밍 재생 (206 HTTP 부분 컨텐츠로 fs.createReadStream 사용)에 문제가 없으므로 FFMPEG 스트림을 알고 있습니다. HTTP 노드 서버에 연결할 때 VLC에서 비디오 라이브 스트리밍을 올바르게 볼 수도 있습니다.
그러나 노드 HTTP를 통해 FFMPEG에서 실시간 스트리밍을 시도하는 것은 클라이언트가 한 프레임을 표시 한 다음 중지하기 때문에 훨씬 어려워 보입니다. 문제는 HTML5 비디오 클라이언트와 호환되도록 HTTP 연결을 설정하지 않았다는 것입니다. HTTP 206 (부분 내용)과 200 응답을 사용하여 데이터를 버퍼에 넣은 다음 운없이 스트리밍하는 것과 같은 다양한 작업을 시도 했으므로 첫 번째 원칙으로 돌아가서 올바르게 설정해야합니다. 방법.
이것이 어떻게 작동하는지에 대한 나의 이해는 다음과 같습니다. 틀린 경우 수정하십시오.
1) 출력을 조각화하고 빈 moov (FFMPEG frag_keyframe 및 empty_moov mov 플래그)를 사용하도록 FFMPEG를 설정해야합니다. 이것은 클라이언트가 일반적으로 파일의 끝 부분에있는 moov atom을 사용하지 않으며 스트리밍 할 때 관련이 없지만 (파일 끝 없음) 내 유스 케이스에 적합한 탐색을 의미하지 않습니다.
2) MP4 조각과 빈 MOOV를 사용하더라도 HTML5 플레이어는 전체 스트림이 다운로드 될 때까지 기다릴 것이므로 라이브 스트림으로 끝나지 않으므로 작동 할 수 없으므로 HTTP 부분 내용을 사용해야합니다.
3) 라이브 스트리밍 할 때 STDOUT 스트림을 HTTP 응답으로 파이핑하는 것이 왜 작동하지 않는지 아직 알 수 없습니다. 파일로 저장하면 비슷한 코드를 사용 하여이 파일을 HTML5 클라이언트로 쉽게 스트리밍 할 수 있습니다. FFMPEG 스폰이 시작되고 IP 카메라에 연결되어 청크를 노드에 보내는데 노드 데이터 이벤트도 불규칙하므로 시간 문제 일 수 있습니다. 그러나 바이트 스트림은 파일에 저장하는 것과 정확히 동일해야하며 HTTP는 지연을 수용 할 수 있어야합니다.
4) 카메라에서 FFMPEG로 생성 된 MP4 파일을 스트리밍 할 때 HTTP 클라이언트에서 네트워크 로그를 확인할 때 3 가지 클라이언트 요청이 있습니다. 비디오에 대한 일반적인 GET 요청, HTTP 서버는 약 40Kb를 반환 한 다음 부분적으로 파일의 마지막 10K에 대한 바이트 범위를 가진 콘텐츠 요청, 중간에 비트에 대한 최종 요청이로드되지 않았습니다. 어쩌면 HTML5 클라이언트가 첫 번째 응답을 받으면 파일의 마지막 부분에서 MP4 MOOV 원자를로드하도록 요구하고 있습니까? 이 경우 MOOV 파일이없고 파일 끝이 없으므로 스트리밍에 사용할 수 없습니다.
5) 실시간 스트리밍을 시도 할 때 네트워크 로그를 확인할 때 약 200 바이트 만 수신 된 초기 요청이 중단 된 다음 200 바이트로 다시 요청이 중단되고 세 번째 요청은 2K입니다. 바이트 스트림이 기록 된 파일에서 스트리밍 할 때 성공적으로 사용할 수있는 것과 정확히 동일하므로 HTML5 클라이언트가 요청을 중단하는 이유를 이해하지 못합니다. 또한 노드가 나머지 FFMPEG 스트림을 클라이언트로 보내지 않는 것 같지만 .on 이벤트 루틴에서 FFMPEG 데이터를 볼 수 있으므로 FFMPEG 노드 HTTP 서버에 도착합니다.
6) STDOUT 스트림을 HTTP 응답 버퍼로 파이프하는 것이 작동해야한다고 생각하지만 HTTP 부분 컨텐츠 클라이언트 요청이 파일을 읽을 때와 같이 올바르게 작동하도록 중간 버퍼 및 스트림을 빌드해야합니다 ? 나는 이것이 내 문제의 주된 이유라고 생각하지만 노드에서 그 설정을 가장 잘하는 방법을 정확히 모르겠습니다. 그리고 파일 끝이 없기 때문에 파일 끝에서 데이터에 대한 클라이언트 요청을 처리하는 방법을 모르겠습니다.
7) 206 개의 부분 컨텐츠 요청을 처리하려고하는데 잘못된 트랙에 있고 정상적인 200 HTTP 응답으로 작동해야합니까? HTTP 200 응답은 VLC에서 잘 작동하므로 HTML5 비디오 클라이언트가 부분 콘텐츠 요청에서만 작동한다고 생각합니까?
여전히이 내용을 배우 면서이 문제의 다양한 계층 (FFMPEG, 노드, 스트리밍, HTTP, HTML5 비디오)을 통해 작업하기가 어려우므로 모든 포인터를 높이 평가할 것입니다. 나는이 사이트와 인터넷에서 조사하는 데 몇 시간을 보냈으며 노드에서 실시간 스트리밍을 할 수있는 사람을 만나지 못했지만 처음은 될 수 없으며 이것이 어떻게 작동해야한다고 생각합니다 !).
Content-Type
은 당신의 머리에 설정 했습니까 ? 청크 인코딩을 사용하고 있습니까? 내가 시작한 곳입니다. 또한 HTML5는 스트리밍 기능을 반드시 제공하지는 않습니다 . 자세한 내용은 여기 를 참조하십시오 . 자신의 수단을 사용하여 비디오 스트림을 버퍼링하고 재생하는 방법을 구현해야 할 것입니다 ( 여기 참조 ). 또한 MediaSource API로 Google을 검색하십시오.