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

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

几天来,我一直在尝试解决这个问题,并且非常感谢有关该主题的任何帮助。

通过将文件的位置作为字符串传递并将其转码为 mp3,我能够使用 fluent-ffmpeg 成功地流式传输存储在 Node.js 服务器上的 mp4 音频文件。如果我从同一个文件创建文件流并将其传递给 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()

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

答案

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

最佳答案

这里的主要问题之一是您无法在管道流上搜索。所以你必须先存储文件。但是,如果您只想从头开始流式传输,则可以使用稍微不同的结构和管道。这是一个最直接的方法示例。

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

关于node.js - 如何使用 Node.js 通过 ffmpeg 流式传输 MP4 文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33725893/

相关文章:

node.js - 在aws lambda中获取请求参数

S3 上的 Mysql 数据目录

ffmpeg - 为什么转换后的视频文件大小大于原始文件大小?

PHP ffmpeg flv 流 Passthru

javascript - 变量在 Angular $resource 函数的回调中突然变得未定义

javascript - jsdoc 和 vscode : Documenting a function passed as an argument to another function

javascript - 将参数函数Node.js从一个js传递到另一个

hadoop - 从 s3 到 hadoop 的 distcp - 找不到文件

amazon-web-services - S3 中的 AWS lambda 部署包

python - 找到: missing argument to `-exec' when using subprocess