Я пытаюсь решить эту проблему уже несколько дней и буду очень признателен за любую помощь по этому поводу.
Я могу успешно транслировать аудиофайл mp4, хранящийся на сервере Node.js, с помощью fluent-ffmpeg, передав расположение файла в виде строки и перекодировав его в mp3. Если я создаю файловый поток из того же файла и передаю его в fluent-ffmpeg, вместо этого он работает с входным файлом mp3, но не с файлом mp4. В случае с файлом mp4 ошибка не выдается, и он утверждает, что поток завершен успешно, но в браузере ничего не воспроизводится. Я предполагаю, что это связано с метаданными, хранящимися в конце файла mp4, но я не знаю, как это исправить. Это тот же самый файл, который работает правильно, когда его местоположение передается в ffmpeg, а не в поток. Когда я пытаюсь передать поток в файл mp4 на s3, снова не возникает никаких ошибок, но в браузер ничего не передается. Это неудивительно, поскольку ffmpeg не будет работать с файлом локально как с потоком, поэтому ожидать, что он будет обрабатывать поток от s3, - это принятие желаемого за действительное.
Как я могу транслировать файл mp4 из s3, не сохраняя его локально как файл сначала? Как мне заставить ffmpeg делать это без перекодирования файла? Ниже приведен код, который у меня сейчас не работает. Обратите внимание, что он пытается передать файл s3 в виде потока в ffmpeg, а также перекодирует его в mp3, чего я бы предпочел не делать.
.get(function(req,res) {
aws.s3(s3Bucket).getFile(s3Path, function (err, result) {
if (err) {
return next(err);
}
var proc = new ffmpeg(result)
.withAudioCodec('libmp3lame')
.format('mp3')
.on('error', function (err, stdout, stderr) {
console.log('an error happened: ' + err.message);
console.log('ffmpeg stdout: ' + stdout);
console.log('ffmpeg stderr: ' + stderr);
})
.on('end', function () {
console.log('Processing finished !');
})
.on('progress', function (progress) {
console.log('Processing: ' + progress.percent + '% done');
})
.pipe(res, {end: true});
});
});
Это использует библиотеку knox, когда она вызывает aws.s3 ... Я также пробовал писать ее, используя стандартный aws sdk для Node.js, как показано ниже, но я получаю тот же результат, что и выше.
var AWS = require('aws-sdk');
var s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_KEY,
region: process.env.AWS_REGION_ID
});
var fileStream = s3.getObject({
Bucket: s3Bucket,
Key: s3Key
}).createReadStream();
var proc = new ffmpeg(fileStream)
.withAudioCodec('libmp3lame')
.format('mp3')
.on('error', function (err, stdout, stderr) {
console.log('an error happened: ' + err.message);
console.log('ffmpeg stdout: ' + stdout);
console.log('ffmpeg stderr: ' + stderr);
})
.on('end', function () {
console.log('Processing finished !');
})
.on('progress', function (progress) {
console.log('Processing: ' + progress.percent + '% done');
})
.pipe(res, {end: true});
=====================================
Обновлено
Я поместил mp3-файл в ту же корзину s3 и код, который я здесь использовал, и смог передать файл в потоковом режиме через браузер без сохранения локальной копии. Итак, проблемы с потоковой передачей, с которыми я сталкиваюсь, как-то связаны с форматом контейнера / кодировщика mp4 / aac.
Меня все еще интересует способ передать файл m4a с s3 на сервер Node.js целиком, а затем передать его в ffmpeg для потоковой передачи без фактического сохранения файла в локальной файловой системе.
=====================================
Обновлено снова
Мне удалось заставить сервер передавать файл в формате mp4 прямо в браузер. Эта половина отвечает на мой первоначальный вопрос. Моя единственная проблема сейчас заключается в том, что мне нужно сначала загрузить файл в локальный магазин, прежде чем я смогу его транслировать. Я все еще хотел бы найти способ потоковой передачи с s3 без временного файла.
aws.s3(s3Bucket).getFile(s3Path, function(err, result){
result.pipe(fs.createWriteStream(file_location));
result.on('end', function() {
console.log('File Downloaded!');
var proc = new ffmpeg(file_location)
.outputOptions(['-movflags isml+frag_keyframe'])
.toFormat('mp4')
.withAudioCodec('copy')
.seekInput(offset)
.on('error', function(err,stdout,stderr) {
console.log('an error happened: ' + err.message);
console.log('ffmpeg stdout: ' + stdout);
console.log('ffmpeg stderr: ' + stderr);
})
.on('end', function() {
console.log('Processing finished !');
})
.on('progress', function(progress) {
console.log('Processing: ' + progress.percent + '% done');
})
.pipe(res, {end: true});
});
});
На принимающей стороне у меня просто следующий javascript на пустой странице html:
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
function process(Data) {
source = context.createBufferSource(); // Create Sound Source
context.decodeAudioData(Data, function(buffer){
source.buffer = buffer;
source.connect(context.destination);
source.start(context.currentTime);
});
};
function loadSound() {
var request = new XMLHttpRequest();
request.open("GET", "/stream/<audio_identifier>", true);
request.responseType = "arraybuffer";
request.onload = function() {
var Data = request.response;
process(Data);
};
request.send();
};
loadSound()
=====================================
Ответ
Приведенный выше код под заголовком «обновлено снова» будет передавать файл mp4 из s3 через сервер Node.js в браузер без использования flash. Это действительно требует, чтобы файл был временно сохранен на сервере Node.js, чтобы метаданные в файле перемещались из конца файла в начало. Чтобы осуществлять потоковую передачу без сохранения временного файла, вам необходимо сначала фактически изменить файл на S3 и изменить эти метаданные. Если вы изменили файл таким образом на S3, вы можете изменить код под заголовком «обновлено снова», чтобы результат S3 передавался прямо в конструктор ffmpeg, а не в файловый поток на сервере Node.js. , а затем предоставив это местоположение файла для ffmepg, как сейчас делает код. Вы можете изменить последнюю команду 'pipe' на 'save (location)', чтобы получить версию файла mp4 локально с перемещением метаданных на передний план. Затем вы можете загрузить эту новую версию файла на S3 и опробовать непрерывную потоковую передачу. Лично я сейчас собираюсь создать задачу, которая изменяет файлы таким образом, поскольку они в первую очередь загружаются в s3. Это позволяет мне записывать и транслировать в mp4 без перекодирования или хранения временного файла на сервере Node.js.