[FFmpeg戦闘] FFplayオーディオフィルター解析

元のアドレス: https://juejin.cn/post/7153334309208719368

オーディオ ストリーム フィルターは、configure_audio_filters() 関数によって作成されます。ffplay はコードの多用途性を必要とするため、コマンド ライン パラメーターがフィルターを使用しない場合でも、AVFrame は空のフィルターを通過して次の処理を実行します。

configure_audio_filters() 関数のフローチャートは次のとおりです。

画像

configure_audio_filters() 関数は次のように定義されます。

static int configure_audio_filters(VideoState *is, const char *afilters, int force_output_format){
    
    ....}

この関数のパラメータについては以下で説明します。

VideoState *はffplayプレーヤーのグローバルマネージャーです。

char *afilters は、次のコマンドのようなフィルター文字列です。

ffplay -af "atempo=2.0" -i juren-5s.mp4

文字列「atempo=2.0」がフィルターに割り当てられます。

int Force_output_format は、バッファシンク エクスポート フィルタのオーディオ フレーム サンプリングおよびその他の情報を is->audio_tgt と同じになるように強制するかどうかを表します。

前述したように、is->audio_tgt はオーディオ ハードウェア デバイスによって開かれる情報です。is->audio_tgt は、最終的にSDL に渡されるオーディオ形式です。すべてのサンプリング レート、チャネル数などは、最終的に is->audio_tgt に変換する必要があります。

次のように、configure_audio_filters() 関数のキー コードを分析してみましょう。

画像

この関数は、先頭に 2 つの要素だけを持つ配列を定義します。これは、実際には、ffmpeg プロジェクトがパラメータを渡す方法です。関数に配列を渡すには、主に 2 つの方法があります。

1.配列のサイズを渡します。いったいどれだけの要素があるのか​​。

2.配列の末尾を渡し、最後の要素(-1)を読み込めば終了です。

ffmpeg の関数のほとんどは 2 番目の方法を使用します。

次に avfilter_graph_free() を調整してフィルター コンテナー(FilterGraph) を解放します。なぜリリースが必要なのでしょうか?

is->agraph は確かに最初は NULL ですが、configure_audio_filters() 関数が2 回目に呼び出される可能性があり、is->agraph は 2 回目では NULL ではありません。

次のように、configure_audio_filters()の最初の呼び出しはstream_component_open() 内にあります。

画像

2 番目の呼び出しは、次のように audio_thread() 内にあります。

画像

configure_audio_filters() への 2 回目の呼び出しは、実際にデコードされた AVFrame のサンプリング レート、チャンネルなどが、コンテナーに記録されたものと一致しないためです。以前は、is->audio_filter_src はコンテナーとカプセル化層から直接取得されたデータでした。カプセル化層によって記録されたオーディオ サンプリング レートが間違っている可能性があるため、実際にデコードされた AVFrame が優先される必要があります。

さらに、2 回目では、force_output_format パラメータが 1 に設定され、バッファシンク エクスポート フィルタのサンプリング情報が is->audio_tgt と同じに強制的に設定されることに注意してください。

実際、is->auddec.pkt_serial != last_serial の条件が true でなければならないため、 configure_audio_filters() は間違いなく 2 回目に調整されます。

次のステップは、フィルタで使用されるスレッドの数を設定することです。次のように、0 は自動的に選択されるスレッドの数です。

is->agraph->nb_threads = filter_nbthreads;

3 番目の重要な点は、リサンプリング オプション (aresample_swr_opts) を次のように設定することです。

画像

リサンプリング オプションがどのような種類のコマンド ライン パラメータであるかは、次のように libswresample/options.c にあります。

画像

たとえば、次のようになります。

ffpaly -ich 1 -i juren-5s.mp4

ich 1 は解析され、ffplay.c の swr_opts 変数にコピーされます。

ここでは、新しい関数 av_opt_set() も使用されています。この関数は、フィルターの属性フィールドを設定できるだけでなく、 AVClass がある限り、デコーダー、ラッパーなど、ほとんどのデータ構造の属性フィールドも設定できます。Structure 内のデータについては、 av_opt_set() を使用して属性を設定できます。詳細については、「Opt.h ファイル関数の分析」を参照してください。

次の重要なポイントは、次のように入口フィルターと出口フィルターを設定することです。

画像

また、エクスポート フィルターは、sample_fmts を AV_SAMPLE_FMT_S16 に設定します。これは ffpaly プレーヤー自体の機能であり、MP4 ファイル内のオーディオ形式が何であっても、AV_SAMPLE_FMT_S16 形式に変換され、再生のために SDL にスローされることを意味します。 SDL_OpenAudioDevice を使用してオーディオを開きます。デバイスのインストール時に、ハードコーディングされたS16 形式が使用されます。

Force_output_format のロジックは主に、バッファシンク エクスポート フィルタのサンプリング情報を is->audio_tgt と同じになるように強制することです。audio_tgt は、SDL がオーディオ フレームを受け入れる最終形式です。

初めてconfigure_audio_filters()関数を呼び出すとき、force_output_formatは0であり、このロジックは入力されません。

最後のステップでは、次のように、configure_filtergraph() 関数を調整して入口フィルターと出口フィルターをリンクし、フィルター コンテナー (FilterGraph) を作成します。

画像

上の図で最も重要なことは、入口フィルターと出口フィルターがグローバル マネージャーに割り当てられていることです。後で、AVFrame 出力をデコーダーから入口フィルターにスローし、それを出口フィルターに読み取るだけです。

configure_filtergraph() 関数の内部ロジックは比較的単純です。ご自身で学習してください。さまざまなフィルタ関数に慣れていない場合は、「FFmpeg のスケール フィルタの概要」などのフィルタに関する記事を参照してください

  >>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864 
  >>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群 739729163 领取

おすすめ

転載: blog.csdn.net/weixin_52622200/article/details/131616162