如何使用Node.js使用ffmpeg流式传输MP4文件?

时间:2015-11-15 22:32:14

标签: node.js amazon-s3 ffmpeg streaming fluent-ffmpeg

我已经试图解决这个问题好几天了,非常感谢你对这个问题的任何帮助。

我能够使用fluent-ffmpeg成功传输存储在Node.js服务器上的mp4音频文件,方法是将文件的位置作为字符串传递并将其转码为mp3。如果我从同一个文件创建一个文件流并将其传递给fluent-ffmpeg,则它适用于mp3输入文件,但不适用于mp4文件。在mp4文件的情况下,没有抛出错误,它声称流已成功完成,但浏览器中没有播放任何内容。我猜测这与存储在mp4文件末尾的元数据有关,但我不知道如何编写代码。当它的位置传递给ffmpeg而不是流时,这是完全相同的文件。当我尝试将流传递给s3上的mp4文件时,再次没有引发错误,但没有任何内容流到浏览器。这并不令人惊讶,因为ffmpeg不会在本地使用文件作为流,因此期望它处理来自s3的流是一厢情愿的想法。

如何从s3流式传输mp4文件,而不先将其作为文件存储在本地?如何在不转码文件的情况下让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});
    });
});

这是在调用aws.s3时使用knox库...我还尝试使用Node.js的标准aws sdk编写它,如下所示,但我得到与上面相同的结果。

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});
    });
});

在接收方,我在空的html页面中只有以下javascript:

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()

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

答案

标题&#39;上面的代码再次更新&#39;将来自s3的mp4文件通过Node.js服务器流式传输到浏览器而不使用flash。它确实要求将文件临时存储在Node.js服务器上,以便将文件中的元数据从文件末尾移动到前端。为了在不存储临时文件的情况下进行流式传输,您需要首先在S3上实际修改文件并更改此元数据。如果您在S3上以这种方式更改了文件,则可以修改标题“再次更新”下的代码。因此,S3的结果直接传送到ffmpeg构造函数,而不是传递到Node.js服务器上的文件流,然后将该文件位置提供给ffmepg,就像代码现在一样。你可以改变最后的管道&#39;命令保存(位置)&#39;在本地获取mp4文件的版本,并将元数据移到前面。然后,您可以将该新文件版本上传到S3并尝试端到端流式传输。就个人而言,我现在要创建一个以这种方式修改文件的任务,因为它们首先被上传到s3。这允许我在mp4中记录和流式传输,而无需在Node.js服务器上转码或存储临时文件。

3 个答案:

答案 0 :(得分:6)

这里的一个主要问题是你不能在管道流上寻找。所以你必须先存储文件。但是,如果您只想从头开始流式传输,则可以使用略有不同的构造和管道。这是一个最直接的方法的例子。

// 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});

答案 1 :(得分:3)

  

块引用   如何从s3流式传输mp4文件,而不将其作为文件首先存储在本地?如何在不转码文件的情况下让ffmpeg执行此操作?以下是我目前没有工作的代码。请注意,它尝试将s3文件作为流传递给ffmpeg,并且它还将其转码为mp3,我不想这样做。

AFAIK - 如果moov原子位于媒体文件中的正确位置,对于S3托管的mp4,流媒体没有什么特别需要,因为你可以依赖于它。如果客户请求&#34; chunked&#34;对它进行编码就会得到这样的结果,一个被&#34; END-OF&#34;终止的分块流。 marker如下所示。

0\r\n
\r\n 

通过包含分块标题,客户端说&#34;我想要一个流&#34; 。在幕后,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

可能想尝试将编解码器添加到&#34; Content-Type:&#34;头。我不知道,但不认为这种类型的流媒体需要它(原子解析那些东西)

答案 2 :(得分:0)

我在从S3文件对象缓冲文件流时出现问题。 s3文件流没有设置正确的标头,似乎它没有正确实现管道。

我认为更好的解决方案是使用名为s3-streams的nodejs模块。它设置正确的标头并缓冲输出,以便可以将流正确地传送到输出响应套接字。它可以使您免于在本地保存文件流,然后再进行休息。