FFmpeg との間でデータをロスレスでパイプするにはどうすればよいですか?

FFmpeg との間でデータをロスレスでパイプするにはどうすればよいですか?

FFmpeg の 1 つのインスタンスから別のインスタンスにストリームをパイプしましたが、中間ストリームで圧縮が使用されたため、最終結果が醜いものになりました。これを防ぐにはロスレス パイプが必要で、オーディオとビデオの両方を含める必要があります。

この問題には複数の答えがあると思われるので、解決策の包括的なリスト (パイプ可能な互換性のあるコンテナーとコーデック) を提供してくれた人にはボーナス ポイントが与えられます。字幕などの他のデータを考慮した人にもボーナス ポイントが与えられます。

編集: 適切なコーデックとコンテナの組み合わせを探しています。パイプはすでに使用していて、ロスレスにする必要があると言ったのに、なぜ人々がそれを理解するのに苦労しているのかわかりません。

うぬぼれのないように説明する方法がわかりませんが、これは FAQ サイトです。非常に具体的な回答を必要とする質問をしても、検索エンジンに自分の問題を入力してこのサイトにたどり着く何百万ものユーザーの役には立ちません。私の質問は、私が何をしていたのか、なぜうまくいかなかったのか、なぜこれが唯一の選択肢なのかを説明する長い物語とコードで皆の気を散らすことなく、FFmpeg インスタンス間でデータをロスレスにパイプする必要がある他のユーザーを助けるために設計されました。

答え1

ffmpegからビデオとオーディオをロスレスでパイプする方法ffmpeg

質問者による要件:

  • 1つのインスタンスからffmpeg別のインスタンスにロスレスでパイプする
  • 行き来は関係ない/dev/null

例:

ffmpeg -s 1280x720 -f rawvideo -i /dev/zero -ar 48000 -ac 2 -f s16le -i \
/dev/zero -c copy -f nut pipe:1 | ffmpeg -y -i pipe:0 -c copy -f nut /dev/null

誰かがこんなことをする理由は見当たりません。また、ほとんどの場合、1 つのプロセスを使用して必要な操作を実行できる場合、 fromffmpegにパイプする理由はほとんどありません。ffmpegffmpeg

オプションの機能:

  • -s 1280x720 -f rawvideo– 入力内容を説明するオプション/dev/zero一般的な入力形式ではないため、これらの追加オプションが必要になります。

  • -i /dev/zero– ビデオ入力。この例では、何もないところからビデオ ストリームを生成するために使用されています。質問者が、使用されている入力に関する情報を提供することを拒否したため、この例でこれが使用されました。

  • -ar 48000 -ac 2 -f s16le– 入力内容を説明するオプション/dev/zero一般的なオーディオ形式ではありません。

  • -i /dev/zero– オーディオ入力。この例では、「何もない」状態からオーディオ ストリームを生成するために使用されています。

  • -c copyストリームコピー、または再多重化して、入力を出力にします。再エンコードは実行されないため、プロセスはロスレスです。質問者がストリームのコピーを受け入れられるかどうかは不明です。代わりに再エンコードが望ましいのでしょうか?

  • -f nutffmpeg–パイプに使用する形式を指定する必要があります。Nut はコンテナー形式です。ffmpeg -formats完全なリストについては を参照してください。もう 1 つの柔軟な形式は です-f matroskaが、質問者からの詳細情報がなければ、使用する適切なまたは特定の出力コンテナー形式を提案することは不可能です。

  • pipe:1- 使用pipeプロトコルstdout に出力します。あるいは、数字を省略 ( のみpipe:) することもできます。その場合、デフォルトでは、書き込みには stdout ファイル記述子が使用され、読み取りには stdin が使用されます。

答え2

私がこれを実行する方法を学んだのは (以前の回答の一部から)、rawvideoビデオのコーデック、pcm_s16leオーディオ コーデック、および FFmpeg のnutラッパーを使用してストリームをエンコードすることです。nutは FFmpeg 以外の主要なプログラムではサポートされていませんが、プロセス間でデータを効率的にパイプするために必要な非圧縮形式をサポートできるコンテナーとして現在私が知っているのはこれだけです。

このエンコーディングの引数は次のようになります。

... -c:v rawvideo -c:a pcm_16le -f nut - ...

一部のオーディオは 24 ビット以上のサンプルで保存されており、これらの場合は またはpcm_24le別の形式を使用する必要があります。 を実行すると、非圧縮オーディオ形式の完全なリストが表示されますffmpeg -codecs(リストを検索する必要があります)。オーディオのサンプル サイズがわからない場合は、 を使用してもpcm_16le品質に目立った低下は発生しません。

パイプの受信側で入力を標準入力に設定すると、ffmpeg が形式を検出し、ストリームをデコードします。

... ffmpeg -i - ...

この回答の省略記号 (...) はコードの一部ではありません。ここにコードが入ります。ハイフン (-) は、出現場所に応じて、FFmpeg に標準入力または標準出力のいずれかを使用するように指示します。

アップデート:

これを改善するために簡単な実験をしてみましたが、他のプログラムが理解できるため (少なくとも VLC は理解できます)、AVI のほうが適切なコンテナーであるようです。

... -c:v rawvideo -c:a pcm_16le -f avi - ...

これは、互換性という追加のボーナスとともに、古いバージョンとまったく同じように動作します。

振り返ってみると、質問は誰にとっても役立つものであるべきだと主張していたにもかかわらず、多くの状況で役に立たない質問を投稿したことを後悔しています。これにより、回答がより役立つものになります。

答え3

他の回答の問題点は、s16le ではなく pcm_s16le であることです。また、冗長なパラメータが多数含まれています。

パイプでは flac ではなく pcm を使用します。処理にかかる時間がはるかに短いためです (PCM は生のオーディオであり、FLAC はエンコードに多くの時間がかかります)。

とにかく、これが私がやる方法です。

ffmpeg -i <input video file/stream> -vcodec rawvideo -acodec pcm_s16le pipe:1 | ffmpeg -f rawvideo -i - -vcodec <video output codec> -acodec <audio output codec> -vb <video bitrate if applicable> -ab <audio bitrate if applicable> <final-output-filename>

これは前回試したときにはうまくいきましたが、私の目標は ffmpeg を ffplay にパイプすることだったので、少し異なるプロセスです。

例:

これは、ffmpeg からのビデオを、raw ビデオ出力と 16 ビットのリトルエンディアン PCM (24 ビット PCM がある場合は に置き換えて、どちらもロスレスpcm_s24le) として別のインスタンスにパイプします。次に、Android プロジェクトの fraunhoefer AAC ライブラリを使用して、2 番目のインスタンスでそれらを h.264 に変換します ( は、libfaacffmpeg ビルドに一般的に含まれています。代わりに に置き換えることができます。)

ffmpeg -i montypythonsflyingcircus-s1e1.avi -vcodec rawvideo -acodec pcm_s16le pipe:1 | ffmpeg -i - -vcodec libx264 -acodec libfdk_aac -vb 1200k -ab 96k mpfc-s1e01-output.mkv

これで字幕がパイプされない場合は、字幕を SRT にリッピングして後で再び多重化したり、上記のパイプに簡単に追加したりできます。

関連情報