【发布时间】:2016-02-16 23:47:42
【问题描述】:
我已经尝试解决这个问题好几天了,非常感谢任何关于这个问题的帮助。
通过将文件的位置作为字符串传递并将其转码为 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});
======================================
更新
我在同一个 s3 存储桶中放置了一个 mp3 文件,我在这里的代码可以工作,并且能够将文件流式传输到浏览器,而无需存储本地副本。所以我面临的流媒体问题与 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 服务器上转码或存储临时文件。
【问题讨论】:
-
文件的编解码器是什么?此外,我还读到 html 5 不支持流式传输某些 m4a 文件,如果那是接收端的内容。另外,您是否尝试过不指定格式和编解码器?示例here 如果您删除了这些,它只会让客户端确定类型是什么,因为您只是将文件流传递给响应。
-
编解码器是: 音频:aac (LC) (mp4a / 0x6134706D), 8000 Hz, mono, fltp, 16 kb/s (default) 接收端是一个webkit音频上下文。我将在一秒钟内更新我的问题以显示代码。我几乎已经回答了我自己的问题。我现在无需转码即可将 mp4 流式传输到客户端。我只是想找出一种方法来做到这一点,而无需将其临时存储在 Node.js 服务器上。
-
google 'moov atom' 并查看 ffmpeg 文档以将索引放在文件的前面。 “-movflags”
-
感谢罗伯特,我已将其包含在我的帖子的最后更新中(代码示例接近结尾)。这允许从 Node.js 流式传输 MP4,但我仍然必须先将文件从 s3 下载到节点服务器。它无法一直通过管道。
标签: node.js amazon-s3 ffmpeg streaming fluent-ffmpeg