你如何使用Node.jsstream式传输MP4文件与ffmpeg?

我一直在努力解决这个问题好几天了,真的很感谢这个话题。

我能够使用fluent-ffmpeg成功传输存储在Node.js服务器上的mp4audio文件,方法是将文件的位置作为string传递,并将其转码为mp3。 如果我从同一个文件创build一个文件stream,并将其传递给fluent-ffmpeg,它可以用于mp3input文件,而不是mp4文件。 在mp4文件的情况下,没有错误抛出,并声称stream成功完成,但没有什么在浏览器中播放。 我猜这是与存储在mp4文件末尾的元数据有关,但我不知道如何编码。 这是完全相同的文件,当它的位置被传递给ffmpeg,而不是stream。 当我尝试传递一个stream到S3上的mp4文件时,再次没有错误,但没有stream到浏览器。 这并不奇怪,因为ffmpeg不能在本地作为stream来处理文件,所以期待它从s3处理stream是一厢情愿的想法。

我怎样才能streams3 mp4文件,而不是作为一个文件本地存储在本地? 如何获得ffmpeg来做到这一点,而不是转码文件呢? 以下是我目前无法使用的代码。 请注意,它试图将s3文件作为stream传递给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文件,我在这里工作的代码能够将文件stream式传输到浏览器,而无需存储本地副本。 所以我面对的stream媒体问题与mp4 / aac容器/编码器格式有关。

我仍然对将m4a文件从s3全部下载到Node.js服务器的方式感兴趣,然后将它传递给ffmpeg进行stream式处理,而不实际上将文件存储在本地文件系统中。

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

再次更新

我设法让服务器将文件(如mp4)直接传输到浏览器。 这一半回答我原来的问题。 我现在唯一的问题是,我必须首先下载文件到本地商店,然后才能stream式传输。 我仍然想find一种方法来从s3stream,而不需要临时文件。

 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服务器上,以便将文件中的元数据从文件末尾移到最前面。 为了在没有存储临时文件的情况下进行stream式处理,首先需要在S3上实际修改文件,并使这个元数据发生变化。 如果您在S3上以这种方式更改了文件,那么您可以修改“更新再次更新”标题下的代码,以便将S3的结果直接传送到ffmpeg构造函数中,而不是传递到Node.js服务器上的文件stream中,然后将该文件位置提供给ffmepg,如现在的代码所示。 您可以将最终的“pipe”命令更改为“save(location)”,以获取本地mp4文件的版本,并将元数据移到前面。 然后,您可以将该文件的新版本上传到S3并尝试端到端stream式传输。 就我个人而言,我现在要创build一个以这种方式修改文件的任务,因为它们首先上传到s3。 这使我可以在mp4中进行录制和stream式传输,而无需在Node.js服务器上进行转码或存储临时文件。

Blockquote如何从s3stream式传输mp4文件,而不是先将其作为文件存储在本地? 如何获得ffmpeg来做到这一点,而不是转码文件呢? 以下是我目前无法使用的代码。 请注意,它试图将s3文件作为stream传递给ffmpeg,并将其转码为一个mp3,我不想这样做。

AFAIK – 如果moovprimefaces在媒体文件的正确位置,对于S3托pipe的mp4,没有什么特别需要stream式传输,因为您可以依赖于http。 如果客户端请求“分块”编码,它将得到一个分块的stream,由下面显示的“END-OF” 标记终止。

 0\r\n \r\n 

通过包含分块头,客户端说“我想要一个stream”。 在封面之下,S3只是nginx或apache不是吗? 他们都尊重标题。

用curl CLItesting它作为你的客户端…

 > User-Agent: curl/7.28.1-DEV > Host: S3.domain > Accept: */* > Transfer-Encoding: chunked > Content-Type: video/mp4 > Expect: 100-continue 

可能要尝试将编解码器添加到“Content-Type:”标题。 我不知道,但不认为这将是需要这种types的stream(primefacesparsing的东西)

这里的一个主要问题是你不能在pipe道上寻找。 所以你必须先存储文件。 但是,如果你想从一开始就stream,你可以使用稍微不同的结构和pipe道。 这是一个最直接的方法。

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

我有一个问题,从S3文件对象缓冲文件stream。 s3文件stream没有正确的头文件,看起来没有正确实现pipe道。

我认为更好的解决scheme是使用这个名为s3-streams的nodejs模块。 它设置正确的标题并缓冲输出,以便可以正确地将stream传送到输出响应套接字。 它可以节省您在重新定义文件stream之前先在本地保存文件stream。