Как вы используете Node.js для потоковой передачи файла MP4 с ffmpeg?

Я пытаюсь решить эту проблему в течение нескольких дней и был бы очень признателен за любую помощь по этому вопросу.

Я могу успешно передавать аудиофайл mp4, хранящийся на узле.JS-сервер использует fluent-ffmpeg, передавая местоположение файла в виде строки и перекодируя его в mp3. Если я создаю файловый поток из того же файла и передаю его fluent-ffmpeg, он работает для входного файла mp3, но не для файла mp4. В случае файл mp4 не ошибка и он утверждает, что поток завершен успешно, но ничего не играет в браузере. Я предполагаю, что это связано с метаданными, хранящимися в конце файла mp4, но я не знаю, как кодировать это. Это тот же самый файл, который работает правильно, когда его местоположение передается в ffmpeg, а не в поток. Когда я пытаюсь передать поток в файл mp4 на s3, снова не возникает ошибок, но ничего не течет в браузер. Это не удивительно, так как видео не будет работать с файл локально как поток, поэтому ожидать, что он будет обрабатывать поток из s3, - это принятие желаемого за действительное.

как я могу передать файл mp4 из s3, не сохраняя его локально в качестве файла? Как мне сделать видео, сделать это без перекодирования файла? Ниже приведен код у меня на данный момент не работает. Обратите внимание, что он пытается передать файл 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 к узлу.JS-сервер полностью, затем передайте его в ffmpeg для потоковой передачи без фактического хранения файла в локальной файловой системе.

=====================================

Снова Обновляется!--16-->

мне удалось получить сервер потоковой передачи файла, как 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, через узел.JS-сервер для браузера без использования flash. Это требует, чтобы файл был временно сохранен на узле.JS-сервер, чтобы метаданные в файле перемещались из конца файла в начало. Чтобы поток без хранения временного файла, вам нужно сначала изменить файл на S3 и сделать это изменение метаданных. Если вы изменили файл таким образом на S3, вы можете изменить код под заголовком "обновлено снова" , чтобы результат от S3 был передается прямо в конструктор ffmpeg, а не в поток файлов на узле.JS-сервер, а затем предоставление этого местоположения файла ffmepg, как это делает код сейчас. Вы можете изменить окончательную команду "труба" на " сохранить(местоположение)", чтобы получить версию файла mp4 локально с метаданными, перемещенными на фронт. Затем вы можете загрузить эту новую версию файла в S3 и попробовать сквозную потоковую передачу. Лично я теперь собираюсь создать задачу, которая изменяет файлы таким образом, как они загружаются на S3 на первом месте. Это позволяет мне записывать и передавать в mp4 без перекодирования или хранения временного файла на узле.сервер js.

3 ответов


Blockquote Как я могу передать файл mp4 из s3, не сохраняя его локально в качестве файла? Как мне сделать видео, сделать это без перекодирования файла? Ниже приведен код у меня на данный момент не работает. Обратите внимание, что он пытается передать файл s3 как поток в ffmpeg, а также перекодировать его в mp3, чего я бы предпочел не делать.

AFAIK-Если атом moov находится в нужном месте в медиа-файле, для S3, размещенного mp4, ничего особенным требуется для потоковой передачи, потому что вы можете положиться на HTTP для этого. Если клиентский запрос "chunked" кодирует его, он получит именно это, chunked stream, завершенный "END-OF" маркер показано ниже.

0\r\n
\r\n 

включив разделенный заголовок, клиент говорит:" Я хочу поток". Под обложками S3-это просто nginx или apache, не так ли? Они оба чтят заголовки.

проверьте его с curl CLI в качестве клиента...

> User-Agent: curl/7.28.1-DEV
> Host: S3.domain
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: video/mp4
> Expect: 100-continue

может чтобы попробовать добавить кодеки в заголовок" Content-Type:". Я не знаю, но не думаю, что это потребуется для этого типа потоковой передачи ( атом разрешает этот материал )


одна из основных проблем здесь заключается в том, что вы не можете искать в конвейерном потоке. Поэтому сначала нужно сохранить файл. Однако, если вы хотите просто поток с самого начала, вы можете использовать немного другую конструкцию и трубу. Вот пример наиболее прямого способа сделать это.

// can just create an in-memory read stream without saving
var stream = aws.s3(s3Bucket).getObject(s3Path).createReadStream();

// fluent-ffmpeg supports a readstream as an arg in constructor
var proc = new ffmpeg(stream)
    .outputOptions(['-movflags isml+frag_keyframe'])
    .toFormat('mp4')
    .withAudioCodec('copy')
    //.seekInput(offset) this is a problem with piping
    .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});

У меня была проблема с буферизацией потоков файлов из объекта S3 file. S3 filestream не имеет правильного набора заголовков, И кажется, что он неправильно реализует трубопровод.

Я думаю, что лучшим решением является использование этого модуля nodejs под названием s3-streams. Он устанавливает правильные заголовки и буферы вывода, чтобы поток мог быть правильно передан в выходной сокет ответа. Это избавит вас от экономии файлового локально, прежде чем restreaming его.