ip camera의 rtsp를 node.js 환경에서 ffmpeg를 통하여 트랜스코딩 하여 소켓으로 실시간 영상 송출 해주는 작업을 하였다.
ffmpeg 사용법 참조글 https://www.minzkn.com/moniwiki/wiki.php/HowToFFmpeg
스트림을 생성하여 잘 재생되는 것을 확인하였지만,
그러나 그 다음날 와보면 영상이 꺼져 있는 경우가 매우 빈번히 발생하였다.
로깅을 더듬어가며 확인 해보니
특정시간(자정 즈음) 인풋 에러가 발생하는 것이었다. 이 것은 인터넷 오류로 발생한 것 같다.
문제는 ffmpeg에서 오류가 발생한 경우 그대로 스트림이 죽어버린다는 것이었다.
지속적으로 계속 영상을 보여줘야 하므로 이런 에러가 발생할 경우 스트림을 재시작 할 수 있도록 처리할 필요가 있었다.
현재 차일드 프로세스를 사용하여 ffmpeg를 spawn하여 트랜스코딩 해주고 있다.
ffmpeg에서는 stdout에는 아웃풋(말그대로 영상 data) stderr에는 현재 영상에 대한 에러와 로깅에 관한 정보가 담겨 있다.
우선 차일드 프로세스를 생성하는 부분이
// mpeg1muxer.js
function newStream(this) {
this.stream = child_process.spawn(this.ffmpegPath, this.spawnOptions, {
detached: false,
});
this.inputStreamStarted = true;
this.stream.stdout.on('data', (data) => {
return this.emit('mpeg1data', data);
});
this.stream.stderr.on('data', (data) => {
return this.emit('ffmpegStderr', data);
});
this.stream.on('exit', (code, signal) => {
setTimeout(() => {
this.stream.kill();
// newStream(_this);
}, 2000);
return this.emit('exitWithError');
});
}
util.inherits(Mpeg1Muxer, events.EventEmitter);
이렇게 되며 stdout으로 받은 output data를 mpeg1data 라는 이벤트로 보내주고
stderr로 받은 로그 및 에러 메세지들을 ffmpegStderr로 보내준다.
그리고 새로운 함수로 restartStream을 만들어 간단하게
프로세스를 죽이고 다시 시작하도록 해주었다.
VideoStream.prototype.restartStream = function () {
this.stream.kill();
console.log(`cameraID : ${this.cameraId}, 스트림 재시작`);
return this.startMpeg1Stream();
};
https://trac.ffmpeg.org/wiki/Errors
ffmpeg 위키에 있는 error 메세지 종류를 확인하여
에러 상황이 발생하였을 때 재시작 되도록 처리해 보았다.
if (data.includes('muxing overhead')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Could not write header for output file')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Invalid input file')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('matches no streams')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Output file is empty')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Unknown encoder')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Trailing options were found')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('No pixel format specified')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Could not open file')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Could not get frame')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Could not find tag for codec foo in stream')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Streamcopy requested for')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes("WASAPI can't initialize")) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
if (data.includes('Non-monotonous DTS')) {
console.log('Logging Time : ', getDateAndTime());
console.log(data);
this.restartStream();
}
일단은 이렇게 하니 오류가 잡혔다.
이렇게 처리하면 다른 문제들이 생길 수 있는데
정리해나가야 할 것 같다.
안녕하십니까 선생님 RTSP 스트리밍중 문제가 생겨서 유입이 되었는데요.
수정된 mpeg1muxer.js 파일을 받아볼 수 있을지 여쭙고자 댓글작성합니다.
ghkdtmdqh@naver.com 인데 답변 주시면 감사하겠습니다. ( _ _ )