私はこの問題を数日間解決しようとしてきましたが、この件について何か助けていただければ幸いです。
ファイルの場所を文字列として渡し、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()
=====================================
答え
上記の「再度更新された」というタイトルのコードは、フラッシュを使用せずに、mp4ファイルをs3からNode.jsサーバー経由でブラウザーにストリーミングします。ファイル内のメタデータがファイルの末尾から先頭に移動されるように、ファイルをNode.jsサーバーに一時的に保存する必要があります。一時ファイルを保存せずにストリーミングするには、最初にS3でファイルを実際に変更し、このメタデータを変更する必要があります。 S3でこのようにファイルを変更した場合は、「updated again」というタイトルのコードを変更して、S3の結果がNode.jsサーバーのファイルストリームではなく、ffmpegコンストラクターに直接パイプされるようにすることができます。 、次に、コードが現在行っているように、そのファイルの場所をffmepgに提供します。最後の「pipe」コマンドを「save(location)」に変更して、メタデータを前面に移動した状態でmp4ファイルのバージョンをローカルで取得できます。次に、その新しいバージョンのファイルをS3にアップロードして、エンドツーエンドのストリーミングを試すことができます。個人的には、最初にs3にアップロードされるときに、この方法でファイルを変更するタスクを作成します。これにより、Node.jsサーバーに一時ファイルをトランスコードしたり保存したりせずに、mp4で記録およびストリーミングできます。
Blockquote最初にファイルとしてローカルに保存せずにs3からmp4ファイルをストリーミングするにはどうすればよいですか?ファイルをトランスコードせずにffmpegでこれを行うにはどうすればよいですか?以下は、私が現在持っている、機能していないコードです。 s3ファイルをストリームとしてffmpegに渡そうとし、それをmp3にトランスコードしていることに注意してください。これは私がしたくないことです。
AFAIK-moov atomがメディアファイルの適切な場所にある場合、S3でホストされているmp4の場合、httpに依存できるため、ストリーミングに特別なことは何も必要ありません。クライアントの要求が「チャンク」の場合「それをエンコードすると、それだけが得られます。チャンク化されたストリームは、以下に示す「END-OF」 マーカー で終了します。
0\r\n
\r\n
チャンクヘッダーを含めることで、クライアントは「ストリームが欲しい」と言っています。隠れて、S3は単なるnginxですか、それともApacheではありませんか?どちらもヘッダーを尊重します。
クライアントとしてcurlCLIを使用してテストします...
> User-Agent: curl/7.28.1-DEV
> Host: S3.domain
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: video/mp4
> Expect: 100-continue
「Content-Type:」ヘッダーにコーデックを追加してみてください。私は知りませんが、このタイプのストリーミングに必要になるとは思いません(atomはそのようなものを解決します)
ここでの主な問題の1つは、パイプストリームをシークできないことです。したがって、最初にファイルを保存する必要があります。ただし、最初からストリーミングしたい場合は、わずかに異なる構造とパイプを使用できます。これを行う最も簡単な方法の例を次に示します。
// 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ファイルオブジェクトからのファイルストリームのバッファリングで問題が発生しました。 s3ファイルストリームには正しいヘッダーが設定されておらず、パイピングが正しく実装されていないようです。
より良い解決策は、s3-streamsと呼ばれるこのnodejsモジュールを使用することだと思います。ストリームを出力応答ソケットに正しくパイプできるように、正しいヘッダーを設定し、出力をバッファリングします。これにより、ファイルストリームを再ストリーミングする前に、最初にローカルに保存する必要がなくなります。