16

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

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

如何从 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 服务器上转码或存储临时文件。

4

3 回答 3

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});
于 2015-11-20T04:19:39.580 回答
3

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

AFAIK - 如果 moov atom 在媒体文件中的正确位置,对于 S3 托管的 mp4,流媒体不需要什么特别的,因为你可以依赖 http。如果客户端请求“分块”编码,它将得到这样的结果,分块流由下面显示的“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:”标头。我不知道,但不认为这种类型的流媒体需要它(原子解决了那些东西)

于 2015-11-22T17:12:56.723 回答
0

我在缓冲来自 S3 文件对象的文件流时遇到问题。s3 文件流没有设置正确的标头,并且似乎没有正确实现管道。

我认为更好的解决方案是使用这个名为 s3-streams 的 nodejs 模块。它设置正确的标头并缓冲输出,以便流可以正确地通过管道传输到输出响应套接字。它使您无需在重新流式传输之前先在本地保存文件流。

于 2016-11-02T03:22:17.340 回答