几天来,我一直在尝试解决这个问题,并且非常感谢有关该主题的任何帮助。
通过将文件的位置作为字符串传递并将其转码为 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 服务器上转码或存储临时文件。