#javascript #node.js #ffmpeg #vlc #http-live-streaming
#javascript #node.js #ffmpeg #vlc #http-прямая трансляция
Вопрос:
Я использую MediaRecorder для записи фрагментов моего видео в реальном времени в формате webm из MediaStream и преобразования этих фрагментов в файлы .ts на сервере с помощью ffmpeg, а затем обновляю свой файл playlist.m3u8 с помощью этого кода:
function generateM3u8Playlist(fileDataArr, playlistFp, isLive, cb) {
var durations = fileDataArr.map(function(fd) {
return fd.duration;
});
var maxT = maxOfArr(durations);
var meta = [
'#EXTM3U',
'#EXT-X-VERSION:3',
'#EXT-X-MEDIA-SEQUENCE:0',
'#EXT-X-ALLOW-CACHE:YES',
'#EXT-X-TARGETDURATION:' Math.ceil(maxT),
];
fileDataArr.forEach(function(fd) {
meta.push('#EXTINF:' fd.duration.toFixed(2) ',');
meta.push(fd.fileName2);
});
if (!isLive) {
meta.push('#EXT-X-ENDLIST');
}
meta.push('');
meta = meta.join('n');
fs.writeFile(playlistFp, meta, cb);
}
Здесь fileDataArr
содержится информация для всех созданных фрагментов.
После этого я использую этот код для создания сервера hls :
var runStreamServer = (function(streamFolder) {
var executed = false;
return function(streamFolder) {
if (!executed) {
executed = true;
var HLSServer = require('hls-server')
var http = require('http')
var server = http.createServer()
var hls = new HLSServer(server, {
path: '/stream', // Base URI to output HLS streams
dir: 'C:\Users\Work\Desktop\live-stream\webcam2hls\videos\' streamFolder // Directory that input files are stored
})
console.log("We are going to stream from folder:" streamFolder);
server.listen(8000);
console.log('Server Listening on Port 8000');
}
};
})();
Проблема в том, что если я перестану создавать новые фрагменты, а затем использую ссылку на сервер hls:
http://localhost:8000/stream/playlist.m3u8
затем видео воспроизводится в VLC, но если я пытаюсь воспроизвести во время записи, он продолжает загружать файл, но не воспроизводится. Я хочу, чтобы он воспроизводился во время создания новых фрагментов и обновления playlist.m3u8. Особенность generateM3u8Playlist
функции заключается в том, что она добавляется '#EXT-X-ENDLIST'
в файл списка воспроизведения после того, как я остановил запись.
Программное обеспечение все еще находится в производстве, поэтому его код немного запутан. Спасибо за любые ответы.
Клиентская сторона, которая генерирует большие двоичные объекты, выглядит следующим образом:
var mediaConstraints = {
video: true,
audio:true
};
navigator.getUserMedia(mediaConstraints, onMediaSuccess, onMediaError);
function onMediaSuccess(stream) {
console.log('will start capturing and sending ' (DT / 1000) 's videos when you press start');
var mediaRecorder = new MediaStreamRecorder(stream);
mediaRecorder.mimeType = 'video/webm';
mediaRecorder.ondataavailable = function(blob) {
var count2 = zeroPad(count, 5);
// here count2 just creates a blob number
console.log('sending chunk ' name ' #' count2 '...');
send('/chunk/' name '/' count2 (stopped ? '/finish' : ''), blob);
count;
};
}
// Here we have the send function which sends our blob to server:
function send(url, blob) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'text/plain';
xhr.setRequestHeader('Content-Type', 'video/webm');
//xhr.setRequestHeader("Content-Length", blob.length);
xhr.onload = function(e) {
if (this.status === 200) {
console.log(this.response);
}
};
xhr.send(blob);
}
Код, который получает запрос XHR, выглядит следующим образом:
var parts = u.split('/');
var prefix = parts[2];
var num = parts[3];
var isFirst = false;
var isLast = !!parts[4];
if ((/^0 $/).test(num)) {
var path = require('path');
shell.mkdir(path.join(__dirname, 'videos', prefix));
isFirst = true;
}
var fp = 'videos/' prefix '/' num '.webm';
var msg = 'got ' fp;
console.log(msg);
console.log('isFirst:%s, isLast:%s', isFirst, isLast);
var stream = fs.createWriteStream(fp, { encoding: 'binary' });
/*stream.on('end', function() {
respond(res, ['text/plain', msg]);
});*/
//req.setEncoding('binary');
req.pipe(stream);
req.on('end', function() {
respond(res, ['text/plain', msg]);
if (!LIVE) { return; }
var duration = 20;
var fd = {
fileName: num '.webm',
filePath: fp,
duration: duration
};
var fileDataArr;
if (isFirst) {
fileDataArr = [];
fileDataArrs[prefix] = fileDataArr;
} else {
var fileDataArr = fileDataArrs[prefix];
}
try {
fileDataArr.push(fd);
} catch (err) {
fileDataArr = [];
console.log(err.message);
}
videoUtils.computeStartTimes(fileDataArr);
videoUtils.webm2Mpegts(fd, function(err, mpegtsFp) {
if (err) { return console.error(err); }
console.log('created %s', mpegtsFp);
var playlistFp = 'videos/' prefix '/playlist.m3u8';
var fileDataArr2 = (isLast ? fileDataArr : lastN(fileDataArr, PREV_ITEMS_IN_LIVE));
var action = (isFirst ? 'created' : (isLast ? 'finished' : 'updated'));
videoUtils.generateM3u8Playlist(fileDataArr2, playlistFp, !isLast, function(err) {
console.log('playlist %s %s', playlistFp, (err ? err.toString() : action));
});
});
runStreamServer(prefix);
}
Комментарии:
1. Код для создания списка воспроизведения выглядит нормально. Фрагменты только добавляются
fileDataArr
или также удаляются (т. Е. Это Скользящее живое окно или растущее)?
Ответ №1:
Вы не показываете нам, как вы используете MediaRecorder
для генерации своих «фрагментов» данных. Используете ли вы его ondataavailable
событие для этой цели?
Если да, пожалуйста, имейте это в виду: вы должны объединить все фрагменты, переданные вам, ondataavailable
чтобы получить действительный .поток данных webm (или .matroska).
Вы не можете просто сохранить произвольный фрагмент данных в медиафайле и ожидать, что он будет воспроизводиться. Даже ffmpeg нуждается в том, чтобы все ваши фрагменты передавались в него для генерации корректного вывода. Это потому, что первые несколько фрагментов содержат обязательные .сегмент инициализации webm, а другие фрагменты — нет.
Комментарии:
1. Привет, Джонс, я добавил вспомогательный код, и вы правы, я использую событие ondataavailable для создания больших двоичных объектов / фрагментов. Решение казалось невыполнимым, поэтому я его больше не использую, однако, если есть какой-либо обходной путь, пожалуйста, предоставьте его с кодом.
2. Я преуспел в подобном проекте, открыв websocket и используя его для отправки каждого фрагмента на сервер. Мое приложение настолько отличается от вашего, что у меня нет никакого полезного исходного кода для вас, извините.
3. В настоящее время я использую websockets и kurento и успешно создал поток rtmp, но я все еще ищу решение для создания потока HLS. Любые советы будут оценены.
4. @AdnanAhmed Я пытаюсь решить аналогичную проблему с загрузкой фрагментов с медиаплеера на сервер и потоковой передачей их на веб-клиенте с использованием библиотеки HLS.
5. Какой подход вы использовали в конечном итоге? Можете ли вы поделиться своими идеями и кодом?