HTML5 비디오 클라이언트에 대한 실시간 http 스트리밍에 대한 최상의 접근 방식
저는 node.js를 사용하여 HTML5 클라이언트에 실시간 출력을 스트리밍하는 가장 좋은 방법을 이해하려고 노력하고 있습니다. 왜냐하면 많은 변수가 실행되고 있고 여러 가지 조합을 시도해 본 경험이 없기 때문입니다.
사용 사례:
IP 비디오 카메라 RTSP H.264 스트림은 FFMPEG에 의해 픽업되고 STDOUT로 출력되는 노드의 다음 FFMPEG 설정을 사용하여 mp4 컨테이너로 리믹스됩니다.초기 클라이언트 연결에서만 실행되므로 부분 콘텐츠 요청이 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});
저는 노드 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
});
고객은 HTML5 비디오 태그를 사용해야 합니다.
저는 위의 FFMPEG 명령줄로 이전에 녹화된 비디오 파일을 HTML5 클라이언트에 스트리밍(fs.createReadStream과 206개의 HTTP 부분 콘텐츠 사용)하는 데 문제가 없으므로(그러나 STDOUT 대신 파일에 저장됨) FFMPEG 스트림이 정확하다는 것을 알고 있습니다.그리고 HTTP 노드 서버에 연결할 때 VLC에서 비디오 라이브 스트리밍을 올바르게 볼 수 있습니다.
그러나 노드 HTTP를 통해 FFMPEG에서 라이브 스트리밍을 시도하는 것은 클라이언트가 하나의 프레임을 표시한 후 중지하기 때문에 훨씬 더 어려워 보입니다.HTML5 비디오 클라이언트와 호환되도록 HTTP 연결을 설정하지 않은 것이 문제인 것 같습니다.저는 HTTP 206(부분 콘텐츠)과 200개의 응답을 사용하여 데이터를 버퍼에 넣은 다음 스트리밍을 실패하는 등 다양한 방법을 시도했습니다. 따라서 올바른 방법으로 설정하려면 첫 번째 원칙으로 돌아가야 합니다.
다음은 이 방법에 대한 제 이해입니다. 제가 틀렸다면 수정해 주십시오.
출력을 단편화하고 빈 무브(FFMPEG frag_keyframe 및 empty_moove flags)를 사용하도록 FFMPEG를 설정해야 합니다.즉, 클라이언트는 스트리밍 시 일반적으로 파일 끝에 있는 무브 원자를 사용하지 않습니다(파일 끝 없음). 하지만 제 사용 사례에 적합한 탐색이 불가능하다는 의미입니다.
제가 MP4 fragment와 빈 MOOV를 사용하더라도 HTML5 플레이어는 재생하기 전에 전체 스트림이 다운로드될 때까지 기다릴 것이기 때문에 HTTP 부분 콘텐츠를 사용해야 합니다. 라이브 스트림은 결코 끝나지 않기 때문에 작동할 수 없습니다.
라이브 스트리밍 시 STDOUT 스트림을 HTTP 응답에 연결하는 것이 왜 아직 작동하지 않는지 이해할 수 없습니다. 파일에 저장하면 유사한 코드를 사용하여 HTML5 클라이언트에 이 파일을 쉽게 스트리밍할 수 있습니다.FFMPEG 생성이 시작되고 IP 카메라에 연결되어 청크를 노드로 전송하는 데 1초가 걸리고 노드 데이터 이벤트도 불규칙하기 때문에 타이밍 문제일 수 있습니다.그러나 바이트 스트림은 파일에 저장하는 것과 정확히 동일해야 하며 HTTP는 지연을 충족할 수 있어야 합니다.
FFMPEG가 만든 MP4 파일을 카메라에서 스트리밍할 때 HTTP 클라이언트에서 네트워크 로그를 확인하면 클라이언트 요청이 3개인 것을 알 수 있습니다.HTTP 서버가 약 40Kb를 반환하는 비디오에 대한 일반 GET 요청, 파일의 마지막 10K에 대한 바이트 범위를 가진 부분 콘텐츠 요청, 그리고 중간에 있는 비트에 대한 최종 요청이 로드되지 않습니다.HTML5 클라이언트가 첫 번째 응답을 받으면 MP4 MOV 원자를 로드할 파일의 마지막 부분을 요청하는 것이 아닐까요?이 경우 MOOV 파일이 없고 파일 끝이 없기 때문에 스트리밍에 사용할 수 없습니다.
라이브 스트리밍을 시도할 때 네트워크 로그를 확인하면 약 200바이트만 수신된 상태에서 초기 요청이 중단되고, 다시 200바이트와 2K 길이의 세 번째 요청으로 다시 요청이 중단됩니다.기록된 파일에서 스트리밍할 때 바이스트림을 성공적으로 사용할 수 있는 것과 정확히 동일한데 HTML5 클라이언트가 요청을 중단하는 이유를 이해할 수 없습니다.또한 노드가 FFMPEG 스트림의 나머지 부분을 클라이언트로 보내지 않는 것처럼 보이지만 FFMPEG 노드 HTTP 서버에 도달하는 .on 이벤트 루틴에서 FFMPEG 데이터를 볼 수 있습니다.
STDOUT 스트림을 HTTP 응답 버퍼에 파이핑하는 것이 작동해야 한다고 생각하지만, HTTP 부분 콘텐츠 클라이언트 요청이 (성공적으로) 파일을 읽을 때처럼 제대로 작동할 수 있도록 중간 버퍼와 스트림을 구축해야 합니까?이것이 제 문제의 주요 원인이라고 생각합니다만, 노드에서 어떻게 설정하는 것이 가장 좋은지 정확히 모르겠습니다.그리고 파일 끝이 없어서 파일 끝에 있는 데이터에 대한 클라이언트 요청을 어떻게 처리해야 할지 모르겠습니다.
제가 206개의 부분적인 콘텐츠 요청을 처리하려고 하는 것이 잘못된 방향으로 가고 있으며, 이것이 일반적인 200개의 HTTP 응답에서 작동해야 합니까?HTTP 200 응답은 VLC에서 잘 작동하는데 HTML5 비디오 클라이언트는 부분적인 콘텐츠 요청에서만 작동할 것으로 예상됩니다.
저는 아직 이 문제를 배우고 있기 때문에 이 문제의 다양한 계층(FFMPEG, 노드, 스트리밍, HTTP, HTML5 비디오)을 통해 작업하는 것이 어렵기 때문에 어떤 포인터든 매우 감사할 것입니다.저는 이 사이트와 인터넷을 조사하는 데 몇 시간이 걸렸지만, 노드에서 실시간 스트리밍을 할 수 있는 사람을 만난 적은 없지만, 제가 처음이 될 수는 없고, 이것이 작동할 수 있어야 한다고 생각합니다.
EDIT 3: IOS 10부터 HLS는 단편화된 mp4 파일을 지원할 것입니다.이제 답은 DASH와 HLS 매니페스트로 분할된 mp4 자산을 만드는 것입니다.> 플래시, iOS9 이하, IE10 이하는 존재하지 않는다고 가정합니다.
이 라인 아래의 모든 것은 구식입니다.후손들을 위해 여기에 보관하는 것.
편집 2: 댓글에 있는 사람들이 지적하는 것처럼, 상황은 바뀝니다.거의 모든 브라우저가 AVC/AAC 코덱을 지원합니다. iOS는 여전히 HLS가 필요합니다.그러나 hls.js와 같은 어댑터를 통해 MSE에서 HLS를 재생할 수 있습니다.iOS가 필요한 경우 HLS+hls.js로, 필요하지 않은 경우 단편화된 MP4(즉, DASH)만 사용할 수 있습니다.
동영상, 특히 라이브 동영상이 매우 어려운 이유는 여러 가지가 있습니다. (원래 질문에는 HTML5 동영상이 필수라고 명시되어 있지만 질문자는 댓글에 플래시가 가능하다고 명시되어 있습니다.)따라서 즉시 이 질문은 오해의 소지가 있습니다.)
먼저 다시 말씀드리겠습니다. HTML5를 통한 라이브 스트리밍은 공식적으로 지원되지 않습니다. 해킹이 있지만 마일리지가 다를 수 있습니다.
편집: 제가 이 답변을 쓴 이후로 미디어 소스 확장은 성숙해졌으며 이제 실행 가능한 옵션이 될 것입니다.대부분의 주요 브라우저에서 지원됩니다.IOS는 계속해서 보류 상태입니다.
다음으로 주문형 비디오(VOD)와 라이브 비디오는 매우 다르다는 것을 이해해야 합니다.네, 둘 다 영상이지만, 문제가 달라서 포맷이 다릅니다.예를 들어 컴퓨터의 시계가 필요한 속도보다 1% 더 빨리 실행되는 경우 VOD에서 인식되지 않습니다.라이브 비디오를 사용하면 비디오를 재생하기 전에 비디오를 재생하려고 합니다.진행 중인 라이브 비디오 스트림에 참여하려면 디코더를 초기화하는 데 필요한 데이터가 필요하므로 스트림에서 반복하거나 대역 외로 전송해야 합니다.VOD를 사용하면 원하는 지점까지 파일의 시작 부분을 읽을 수 있습니다.
이제 조금 더 파고 들어보겠습니다.
플랫폼:
- iOS
- PC
- 맥
- 안드로이드
코덱:
- vp8/9
- h.264
- 토라(vp3)
브라우저에서 라이브 비디오에 대한 일반적인 전송 방법:
- 대시(HTTP)
- HLS(HTTP)
- 플래시(RTMP)
- 플래시(HDS)
브라우저에서 VOD에 대한 일반적인 전송 방법:
- DASH(HTTP 스트리밍)
- HLS(HTTP 스트리밍)
- 플래시(RTMP)
- 플래시(HTTP 스트리밍)
- MP4(HTTP 유사 스트리밍)
- 저는 MKV와 OOG에 대해 잘 모르기 때문에 그들에 대해 이야기하지 않을 것입니다.
html5 비디오 태그:
- MP4
- 거미줄
- 달걀
어떤 브라우저가 어떤 형식을 지원하는지 살펴보겠습니다.
사파리:
- HLS(iOS 및 Mac에만 해당)
- h.264
- MP4
파이어폭스
- DASH(MSE 경유, h.264 없음)
- h.264는 플래시를 통해서만 가능합니다!
- VP9
- MP4
- OGG
- 웹m
IE
- 플래시
- DASH(MSEIE 11+ 전용)
- h.264
- MP4
크롬
- 플래시
- DASH(MSE를 통해)
- h.264
- VP9
- MP4
- 거미줄
- 달걀
MP4는 라이브 비디오에 사용할 수 없습니다(참고: DASH는 MP4의 상위 집합이므로 혼동하지 마십시오).MP4는 두 조각으로 나뉩니다: moove와 mdat. mdat에는 원시 오디오 비디오 데이터가 들어 있습니다.하지만 그것은 색인화되지 않았기 때문에 무브가 없으면 쓸모가 없습니다.무브에는 mdat에 있는 모든 데이터의 인덱스가 포함됩니다.그러나 형식 때문에 모든 프레임의 타임스탬프와 크기를 알 때까지 '플랫'할 수 없습니다.프레임 크기를 '파이버'하는 무브를 구성할 수도 있지만 대역폭이 매우 낭비됩니다.
그래서 당신이 모든 곳에 전달하고 싶다면, 우리는 최소 공통분모를 찾아야 합니다.플래시 예제를 사용하지 않으면 LCD가 없다는 것을 알 수 있습니다.
- iOS는 h.264 비디오만 지원합니다.HLS만 라이브로 지원합니다.
- 플래시를 사용하지 않는 한 Firefox는 h.264를 전혀 지원하지 않습니다.
- iOS에서 플래시가 작동하지 않음
LCD에 가장 가까운 것은 HLS를 사용하여 iOS 사용자를 얻고 다른 모든 사용자를 위해 플래시를 사용하는 것입니다.개인적으로 좋아하는 것은 HLS를 인코딩한 다음 플래시를 사용하여 다른 모든 사람을 위해 HLS를 재생하는 것입니다.JW player 6을 통해 플래시에서 HLS를 재생할 수 있습니다(또는 저처럼 AS3에서 FLV에 HLS를 직접 작성합니다).
곧, 이를 위한 가장 일반적인 방법은 iOS/Mac의 HLS와 다른 모든 곳의 MSE를 통한 DASH가 될 것입니다(넷플릭스는 곧 그렇게 할 것입니다).하지만 우리는 여전히 모든 사람들이 브라우저를 업그레이드하기를 기다리고 있습니다.파이어폭스를 위한 별도의 DASH/VP9도 필요할 것 같습니다(오픈264에 대해 알고 있습니다. 형편없네요.메인 또는 하이 프로파일로 비디오를 재생할 수 없습니다.그래서 그것은 현재 쓸모가 없습니다).
모두에게 특히 감사합니다. 이 질문은 복잡한 질문이고 많은 레이어가 있습니다. 라이브 비디오를 스트리밍하려면 모든 것이 작동해야 합니다.저의 원래 질문과 HTML5 비디오 사용 대 플래시 사용을 명확히 하기 위해, 제 사용 사례는 일반적이고 클라이언트와 미래에 구현하기 쉽기 때문에 HTML5에 대한 선호도가 높습니다.플래시는 먼 차선책이므로 이 질문에 대해 HTML5를 고수하겠습니다.
저는 이 연습을 통해 많은 것을 배웠고 라이브 스트리밍이 VOD(HTML5 비디오와 잘 작동함)보다 훨씬 어렵다는 것에 동의합니다.하지만 저는 이것이 제 사용 사례에 만족스럽게 작용할 수 있게 되었고 MSE, 플래시, 정교한 버퍼링 체계와 같은 더 복잡한 옵션을 Node에서 추적한 후 솔루션이 매우 간단해졌습니다.문제는 FFMPEG가 조각난 MP4를 손상시키고 있다는 것이었고, 저는 FFMPEG 매개 변수를 조정해야 했고, 제가 원래 사용했던 http를 통한 표준 노드 스트림 파이프 리디렉션이 전부였습니다.
MP4에는 자체 인덱스가 있는 훨씬 더 작은 조각으로 mp4를 분해하고 mp4 라이브 스트리밍 옵션을 실행할 수 있도록 하는 '조각화' 옵션이 있습니다.그러나 스트림을 다시 검색할 수 없습니다(사용 사례는 OK). 이후 버전의 FFMPEG는 단편화를 지원합니다.
참고 타이밍이 문제가 될 수 있으며, 제 솔루션에서는 재UX의 조합으로 인해 2~6초의 지연이 발생합니다(실제로 FFMPEG는 라이브 스트림을 수신한 다음 HTTP를 통해 서비스하기 위해 노드로 전송해야 함).이에 대해 할 수 있는 일은 많지 않지만 Chrome에서는 비디오가 가능한 한 많이 따라잡으려고 하므로 비디오가 IE11(제가 선호하는 클라이언트)보다 약간 더 최신 버전입니다.
이 게시물에서 코드가 작동하는 방식을 설명하는 대신 GIST에서 코멘트를 확인하십시오(클라이언트 코드는 포함되어 있지 않으며, 노드 http 서버 주소가 있는 표준 HTML5 비디오 태그입니다).GIST는 여기에 있습니다: https://gist.github.com/deandob/9240090
저는 이 사용 사례의 유사한 예를 찾을 수 없었기 때문에 위의 설명과 코드가 다른 사람들에게 도움이 되기를 바랍니다. 특히 제가 이 사이트에서 많은 것을 배웠고 여전히 저 자신을 초보자라고 생각하기 때문입니다!
이것이 저의 구체적인 질문에 대한 답변이지만, 저는 가장 포괄적인 질문이기 때문에 새터메리의 답변을 수락된 답변으로 선택했습니다.
JSMPEG 프로젝트를 살펴보십시오.자바스크립트를 사용하여 브라우저에서 MPEG를 디코딩하는 좋은 아이디어가 구현되었습니다.인코더(예: FFMPEG)의 바이트는 예를 들어 WebSockets 또는 Flash를 사용하여 브라우저로 전송할 수 있습니다.커뮤니티가 따라잡을 수 있다면 현재로서는 최고의 HTML5 라이브 비디오 스트리밍 솔루션이 될 것이라고 생각합니다.
RTSP 기반 웹캠을 HTML5 클라이언트로 라이브 스트리밍하는 한 가지 방법(재인코딩을 포함하므로 품질 손실을 예상하고 CPU 성능이 필요함):
- 아이스캐스트 서버 설정(웹 서버가 있는 동일한 컴퓨터 또는 캠에서 RTSP 스트림을 수신하는 컴퓨터에 있을 수 있음)
카메라에서 스트림을 수신하는 기계에서는 FFMPEG butter를 사용하지 마십시오.RTSP 스트림을 수신 및 디코딩하고 다시 인코딩하여 아이스캐스트 서버로 스트리밍할 수 있습니다.파이프라인 예(비디오만, 오디오 없음):
gst-launch-1.0 rtspsrc location=rtsp://192.168.1.234:554 user-id=admin user-pw=123456 ! rtph264depay ! avdec_h264 ! vp8enc threads=2 deadline=10000 ! webmmux streamable=true ! shout2send password=pass ip=<IP_OF_ICECAST_SERVER> port=12000 mount=cam.webm
=> 아이스캐스트-스트림의 URL(http://127.0.0.1:12000/cam.webm)과 함께 <video> 태그를 사용하면 webm을 지원하는 모든 브라우저와 기기에서 작동합니다.
저는 모든 브라우저(데스크탑, iOS, ...)에서 h264 비디오를 라이브(지연 없이) 재생할 수 있는 HTML5 비디오 플레이어를 브로드웨이 h264 코덱(emscripten) 주변에 작성했습니다.
비디오 스트림이 웹 소켓을 통해 클라이언트로 전송되고 프레임당 디코딩된 프레임이 캔바에 표시됩니다(가속을 위해 webgl 사용).
github에서 https://github.com/131/h264-live-player 을 확인하세요.
이것은 매우 흔한 오해입니다.라이브 HTML5 비디오 지원은 없습니다(iOS 및 Mac Safari의 HLS 제외).당신은 웹 컨테이너를 사용하여 그것을 '해킹'할 수 있을지 모르지만, 나는 그것이 보편적으로 지원될 것이라고 기대하지 않습니다.찾고 있는 것은 한 번에 하나씩 브라우저에 조각을 공급할 수 있는 미디어 소스 확장에 포함되어 있습니다.하지만 당신은 클라이언트 쪽 자바스크립트를 작성해야 할 것입니다.
이 솔루션을 살펴보십시오.플래시폰은 순수 HTML5 페이지에서 라이브 오디오+비디오 스트림을 재생할 수 있는 것으로 알고 있습니다.
그들은 재생을 위해 MPEG1과 G.711 코덱을 사용합니다.이 해킹은 디코딩된 비디오를 HTML5 캔버스 요소로 렌더링하고 HTML5 오디오 컨텍스트를 통해 디코딩된 오디오를 재생합니다.
JPEG 솔루션을 사용하여 서버가 JPEG를 하나씩 브라우저에 배포한 다음 캔버스 요소를 사용하여 이러한 JPEG를 그리는 것은 어떻습니까?http://thejackalofjavascript.com/rpi-live-streaming/
언급URL : https://stackoverflow.com/questions/21921790/best-approach-to-real-time-http-streaming-to-html5-video-client
'programing' 카테고리의 다른 글
VBA를 사용하여 프로그래밍 방식으로 참조를 추가하는 방법 (0) | 2023.05.12 |
---|---|
데이터 바인딩 드롭다운 목록 - 초기 값 (0) | 2023.05.12 |
EscapeUriString과 EscapeDataString의 차이점은 무엇입니까? (0) | 2023.05.12 |
대부분의 포트에서 수신 중인 경우 Node.js EACCES 오류 (0) | 2023.05.12 |
Azure SQL Server 인스턴스에서 데이터베이스 사용자에게 부여된 역할 및 사용 권한을 보려면 어떻게 해야 합니까? (0) | 2023.05.12 |