你如何使用 Node.js 通过 ffmpeg 流式传输 MP4 文件? [英] How do you use Node.js to stream an MP4 file with ffmpeg?

查看:131
本文介绍了你如何使用 Node.js 通过 ffmpeg 流式传输 MP4 文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经尝试解决这个问题好几天了,非常感谢您对这个问题的任何帮助.

I've been trying to solve this problem for several days now and would really appreciate any help on the subject.

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

I'm able to successfully stream an mp4 audio file stored on a Node.js server using fluent-ffmpeg by passing the location of the file as a string and transcoding it to mp3. If I create a file stream from the same file and pass that to fluent-ffmpeg instead it works for an mp3 input file, but not a mp4 file. In the case of the mp4 file no error is thrown and it claims the stream completed successfully, but nothing is playing in the browser. I'm guessing this has to do with the meta data being stored at the end of an mp4 file, but I don't know how to code around this. This is the exact same file that works correctly when it's location is passed to ffmpeg, rather than the stream. When I try and pass a stream to the mp4 file on s3, again no error is thrown, but nothing streams to the browser. This isn't surprising as ffmpeg won't work with the file locally as stream, so expecting it to handle the stream from s3 is wishful thinking.

如何从 s3 流式传输 mp4 文件,而不先将其作为文件存储在本地?如何在不转码文件的情况下让 ffmpeg 做到这一点?以下是我目前无法使用的代码.请注意,它尝试将 s3 文件作为流传递给 ffmpeg,并且还将其转码为 mp3,我不想这样做.

How can I stream the mp4 file from s3, without storing it locally as a file first? How do I get ffmpeg to do this without transcoding the file too? The following is the code I have at the moment which isn't working. Note that it attempts to pass the s3 file as a stream to ffmpeg and it's also transcoding it into an mp3, which I'd prefer not to do.

.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 编写它,如下所示,但我得到与上面相同的结果.

This is using the knox library when it calls aws.s3... I've also tried writing it using the standard aws sdk for Node.js, as shown below, but I get the same outcome as above.

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 容器/编码器格式有关.

I placed an mp3 file in the same s3 bucket and the code I have here worked and was able to stream the file through to the browser without storing a local copy. So the streaming issues I face have something to do with the mp4/aac container/encoder format.

我仍然对将 m4a 文件从 s3 完整地传输到 Node.js 服务器,然后将其传递给 ffmpeg 进行流式传输而不实际将文件存储在本地文件系统中的方法感兴趣.

I'm still interested in a way to bring the m4a file down from s3 to the Node.js server in it's entirety, then pass it to ffmpeg for streaming without actually storing the file in the local file system.

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

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

再次更新

我已经设法让服务器将文件作为 mp4 直接传输到浏览器.这一半回答了我原来的问题.我现在唯一的问题是我必须先将文件下载到本地存储,然后才能流式传输.我仍然想找到一种无需临时文件即可从 s3 流式传输的方法.

I've managed to get the server streaming the file, as mp4, straight to the browser. This half answers my original question. My only issue now is that I have to download the file to a local store first, before I can stream it. I'd still like to find a way to stream from s3 without needing the temporary file.

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:

On the receiving side I just have the following javascript in an empty html page:

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

The code above under the title 'updated again' will stream an mp4 file, from s3, via a Node.js server to a browser without using flash. It does require that the file be stored temporarily on the Node.js server so that the meta data in the file is moved from the end of the file to the front. In order to stream without storing the temporary file, you need to actual modify the file on S3 first and make this meta data change. If you have changed the file in this way on S3 then you can modify the code under the title 'updated again' so that the result from S3 is piped straight into the ffmpeg constructor, rather than into a file stream on the Node.js server, then providing that file location to ffmepg, as the code does now. You can change the final 'pipe' command to 'save(location)' to get a version of the mp4 file locally with the meta data moved to the front. You can then upload that new version of the file to S3 and try out the end to end streaming. Personally I'm now going to create a task that modifies the files in this way as they are uploaded to s3 in the first place. This allows me to record and stream in mp4 without transcoding or storing a temp file on the Node.js server.

推荐答案

块引用如何从 s3 流式传输 mp4 文件,而不先将其作为文件存储在本地?如何在不转码文件的情况下让 ffmpeg 做到这一点?以下是我目前无法使用的代码.请注意,它尝试将 s3 文件作为流传递给 ffmpeg,并且还将其转码为 mp3,我不想这样做.

Blockquote How can I stream the mp4 file from s3, without storing it locally as a file first? How do I get ffmpeg to do this without transcoding the file too? The following is the code I have at the moment which isn't working. Note that it attempts to pass the s3 file as a stream to ffmpeg and it's also transcoding it into an mp3, which I'd prefer not to do.

AFAIK - 如果 moov atom 在媒体文件中的正确位置,对于 S3 托管的 mp4,流媒体不需要任何特别的东西,因为您可以依赖 http.如果客户端请求分块"编码,它将得到一个分块流,由END-OF"终止 标记如下所示.

AFAIK - if the moov atom is in the right place in media file, for S3 hosted mp4, nothing special is require for streaming because you can rely on http for that. If the client request "chunked" encoding it will get just that, a chunked stream terminated by the "END-OF" marker shown below.

0


 

通过包含分块标头,客户端说我想要一个流".在幕后,S3 只是 nginx 或 apache 不是吗?他们都尊重头球.

By including the chunked header, the client is saying " I want a stream" . Under the covers, S3 is just nginx or apache isn't it? They both honor the headers.

使用 curl CLI 作为客户端进行测试...

test it with curl CLI as your client...

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

可能想尝试将编解码器添加到Content-Type:"标头中.我不知道,但不认为这种类型的流媒体需要它(原子解决了那些东西)

May want to try out adding the codecs to the "Content-Type:" header. I dont know, but dont think it would be required for this type of streaming ( the atom resolves that stuff )

这篇关于你如何使用 Node.js 通过 ffmpeg 流式传输 MP4 文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆