VLCソースコード分析



Vlc Source Code Analysis

1。概要

VLCは、VideoLANオープンソースプロジェクト組織の完全にオープンソースのストリーミングメディアサーバーとマルチメディアプレーヤーに属しています。ストリーミングメディアサーバーとして、VLCはマルチメディアプレーヤーとしてプラットフォーム全体で複数のオペレーティングシステムとコンピューターアーキテクチャをサポートし、VLCは複数の形式でメディアファイルを再生できます。主に含まれるもの:WMV、ASF、MPG、MP、AVI、H.264およびその他の一般的なメディア形式。



VLCは完全にモジュール化された構造を採用しています。システム内で、必要なモジュールが動的にロードされ、統合管理のためにmodule_bank構造体に配置されます。 VLCメインモジュールでさえ、プラグインメソッドを介して動的にロードされます(module_InitBank関数を介して(module_bankの初期確立時)。プラグインの動的ロードをサポートしないシステム環境の場合、VLCは組み込みのメソッドでは、VLCの起動時に必要なプラグインを静的にロードし、それらをmodule_bankに配置して統合管理を行います。

VLCモジュールは、access、access_filter、access_output、audio_filter、audio_mixer、audio_output、codec、control、demux、gui、misc、mux、packetizer、stream_output、video_filter、video_output、interface、input、playlistなどの多くのカテゴリに分類されます。太字はコアモジュールです)。 VLCがストリーミングメディアサーバーとして使用されているかマルチメディアプレーヤーとして使用されているかにかかわらず、その本質的なアイデアは「プレーヤー」です。この画像の説明の理由は、(コアは、入力(ファイル、ネットワークストリーム)から出力(オーディオまたはビデオ、画面またはネットワーク上)まで、さまざまなマルチプレクサ、デマルチプレクサ、デコーダを介してメディア処理を行うためのフレームワークを提供するためですインターフェースもLibVLCのプラグインです。ロードするモジュールを選択するのは開発者次第です。公式ウェブサイトの説明)基本的に、ES、PES、PS、TS、その他のストリームの変換、送信、表示を扱います。 。ストリーミングメディアサーバーの場合、ファイルを入力として使用する場合:マルチメディアプレーヤーの場合はPS-> DEMUX-> ES-> MUX-> TS、UDP送信を使用する場合:TS-> DEMUX-> ES。



2.プラグイン管理フレームワーク

VLCの各タイプのモジュールには抽象化レイヤー/構造があります。いくつかの操作の関数ポインタは、抽象層または構造で定義されています。これらの関数ポインタを介して、モジュールの動的ロードと関連関数の割り当てを実現できます。ポインタの関数アドレスは、関数ポインタを呼び出すことにより、最終的に実際のモジュールの操作を呼び出すことができます。

すべてのVLCモジュールについて、エクスポートされる関数はvlc_entry __(MODULE_NAME)の1つだけです。 (その中で、MODULE_NAMEはマクロ定義です。メインモジュールの場合、 include modules_inner.hでmainとして定義されます)モジュールを動的にロードするプロセスは、module_Need関数を使用して、各プラグインの機能などの関連属性。要件を満たし、アクティブ化されたモジュール。いわゆるアクティベーションとは、プラグインの初期化関数が正常に呼び出されることを意味します。各プラグインの初期化関数とデストラクタの場合、関連する関数アドレスはvlc_entry __(MODULE_NAME)関数で指定されます。したがって、各プラグイン(ダイナミックライブラリ)をロードするプロセスは、ダイナミックライブラリファイルを解析し、vlc_entry__関数のアドレスを見つけて、実行することになります。このように、各モジュールの活性化関数は、後の関数によって動的に呼び出される各操作の関数アドレスを割り当てます。



具体的な関数呼び出しプロセスは次のとおりです。

lメインモジュールのロードプロセス:

int main(int i_argc、char * ppsz_argv [])(src vlc.c)-> i_ret = VLC_Init(0、i_argc、ppsz_argv)-> module_InitBank(p_vlc)(src libvlc.c void __module_InitBank(vlc_object_t)- module_LoadMain(p_this)(src misc modules.c)-> AllocateBuiltinModule(p_this、vlc_entry__main)-> pf_entry(p_module)実際の関数はエクスポートされた関数vlc_entry__mainであり、エクスポートされた他のすべてのモジュールはvlc_entry__0_8_6)

lモジュールをロードするプロセスを実現するためのModule_Need関数:

module_t * __module_Need(vlc_object_t * p_this、const char * psz_capability、

const char *psz_name, vlc_bool_t b_strict) (srcmiscmodules.c) -> vlc_list_find (queries out all loaded modules) -> then loop, find the first most suitable module according to capability -> AllocatePlugin ( Dynamically load the required plug-ins, this function will traverse all dynamic library files in the directory where the dynamic library is located) ->p_module->pf_activate (call activation function)

l VLC_Init関数フロー:

module_InitBank-> module_LoadBuiltins(静的プラグインをロード)-> module_LoadPlugins(動的プラグインをロード-> VLC_AddIntf(インターフェイスプラグインを追加、VLCはホットキーモジュールを静的にロードします)

VLCのさまざまな処理タスクに応じて、main、memcpy、hotkeysなどのさまざまなモジュールが静的にロードされます。動的にロードされるモジュールは、処理タスクによって大きく異なります。

3.VLCストリーミングメディアサーバーアーキテクチャ

以下では、主にストリーミングメディアサーバーとしてのVLCのアーキテクチャについて説明します。プログラムリストファイルの場合は、実行中のプロセスをデバッグし、最後に要約を示します。

この例のプレイリストは次のとおりです。

新しいbrブロードキャストが有効になりました

br入力/mnt/hgfs/movie/caiyan.mpgを設定します

br出力のセットアップ#standard {mux = ts、access = udp、url = 234.0.1.4、sap、name = ch1}

この例では、VLCによって提供されるAPI:libvlc_new、libvlc_vlm_new、libvlc_vlm_play_media、libvlc_vlm_load_fileなど(一部のAPIは自分で追加されます)は、放送番組brの放送を完了することができます。

内部VLCがどのように機能して、これらのインターフェイスを介してストリーミングメディアのリリースを完了するかを詳しく見てみましょう。

  1. まず、プログラムはlibvlc_new( src control core.c)インターフェイスを呼び出して、プログラムの実行中に一意のVLC実行インスタンスlibvlc_instance_tを作成します。

  2. libvlc_newインターフェースでは、特定の初期化作業を実行するためにVLC_Init関数が呼び出されます。

  3. VLC_Init( src libvlc.c)関数では、最初にsystem_Init関数を使用してシステムへの着信パラメーターの初期化を完了し、次にmodule_bank構造体をmodule_InitBank( src misc modules)によって初期化します。 c)機能し、メインモジュールが作成されます。 、次に、module_LoadBuiltinsを介して静的モジュールをロードし、module_LoadPlugins( src misc modules.c)関数を介して動的モジュールをロードし、module_Need( src misc modules.c)関数を介してmemcpyモジュールをロードしてアクティブ化します。そしてplaylist_Create( src playlist playlist.c)関数は、プレイリスト再生管理用のスレッドを作成し、そのスレッド処理関数はRunThread( src playlist playlist.c)であり、VLC_AddIntf( src libvlc.c)関数とホットキーモジュールをアクティブ化し、最後にシステム設定に従ってマクロHAVE_X11_XLIB_Hを定義するため、スクリーンセーバーモジュールも追加する必要があります。

  4. 概要:この時点でロードされるモジュールは、メイン、ホットキー、スクリーンセーバー、memcpyであり、プレイリストを管理するために追加のスレッドが作成され、スレッドはp_playlist-> b_die状態になるまで無期限にループします。

  5. 次に、プログラムでlibvlc_vlm_newインターフェイスを呼び出して、VLMオブジェクトを作成します(このインターフェイスは自分で追加します)。

  6. このインターフェイスは、vlm_New( src misc vlm.c)関数を呼び出して、VLMオブジェクトの作成を実現します。関数の戻り値はvlm_tへのポインターです。

  7. Vlm_new関数では、vlm管理スレッドが作成され、スレッド処理関数はManage( src misc vlm.c)です。この機能は、現在のさまざまなメディア(vod、broadcast、schedule)の再生インスタンスを周期的に処理し、各再生の詳細を制御します(たとえば、ある入力から次の入力スケジュールサイクルの周期的なスケジューリングへの切り替えなど)。プレイリストスレッドとは異なり、Manageは主に再生インスタンスの操作用であり、RunThreadは主にプレイリストの管理用です。つまり、VLC管理は階層的であり、プレイリストレベルとプレイリスト内のメディア再生インスタンスレベルです。

  8. 次に、プログラムはlibvlc_vlm_load_fileインターフェイスを呼び出してプレイリストをロードします(このインターフェイスも自分で追加され、プレイリストは上記のとおりです)。

  9. このインターフェイスは、vlm_Load( src misc vlm.c)関数を呼び出します。この関数では、stream_UrlNew、stream_Seek、stream_Read、およびLoadの関数が順番に呼び出されます。各機能の機能については、以下で詳しく説明します。

a)1つ目は、stream_UrlNew( src input stream.c)関数です。まず、MRLSplit( src input input.c)関数を調整して、アクセス、デマックス、およびパスの分析を完了します。この例の具体的な分析結果は、access = ''、demux = ''、path = 'aa'です。次に、access2_New( src input access.c)関数を呼び出して、access_t構造体を作成し、初期化します。特定の操作中にロードされるモジュールの関連パラメーターは、capability = 'access2'、name = 'access_file'、psz_filename = access /libaccess_file_plugin.soです。最後に、stream_AccessNew( src input stream.c)関数を呼び出して、stream_t構造オブジェクトを作成し、オブジェクト内のすべての関数ポインターを初期化します。

b)次に、stream_Seek( include vlc_stream.h)インライン関数を呼び出して開始位置を設定します

c)次に、stream_Size( include vlc_stream.h)を呼び出してサイズを取得します

d)次に、stream_Read( include vlc_stream.h)を呼び出してバッファーに読み込みます

e)最後に、Load( src misc vlm.c)を呼び出して、プログラムリストの実際のロードを完了します。プレイビルファイルの場合、1行ずつ分析され、ExecuteCommand( src misc vlm.c)が呼び出されて分析が完了します。 Load関数の呼び出しは、入力文字列値の設定、出力文字列値の設定、マルチプレクサ値の設定、再生に関連する有効化およびループ関連のパラメーターなど、関連するパラメーターを設定するだけです。ロード作業は、次のリリースストリームの準備のみです。

  1. プログラムでlibvlc_vlm_play_mediaインターフェイスを呼び出して、プログラムストリームを公開します。 (自分でインターフェースを追加)

  2. libvlc_vlm_play_mediaインターフェースでは、コマンド「control br play」が作成され、vlm_ExecuteCommand( src misc vlm.c)が実行されてコマンドの実行が完了します。コマンドタイプに応じて、vlm_MediaControl( src misc vlm.c)関数処理。

  3. vlm_MediaControl関数では、vlc_input_item_Init( include vlc_input.h)関数が呼び出されて再生インスタンスの初期化が完了し、input_CreateThread2( src input input.c)関数が呼び出されて再生の作成が完了します。糸。このスレッドの処理機能はRun( src input input.c)です。

  4. 実行スレッドは、ストリーミングサーバーとしてのVLC全体の中核です。これは主に次のステップに分けられます:Init、MainLoop、End。 MainLoopは無限ループであり、ストリーミングメディアの公開プロセス全体を完了します。

a)最初にInit( src input input.c)関数を呼び出して、関連する統計パラメータを初期化します

b)次に、input_EsOutNew( src input es_out.c)関数を呼び出して、es_out_t構造体オブジェクトとes_out_sys_t構造体オブジェクトを初期化し、関連する関数ポインターを設定します。

c)次に、InputSourceInit( src input input.c)関数を呼び出して、input_thread_tオブジェクトのinput_source_tオブジェクトを初期化します。これには、主に3つの構造オブジェクトaccess_t、stream_t、demux_tが含まれます。

d)この時点での各モジュールの実際の負荷を要約します。

1) (access_t)type='access',name='access_filter',capability='access2',psz_filename='access/libaccess_file_plugin.so'; 2) (stream_t)type='stream',pf_read='AStreamReadStream',pf_seek='AStreamPeekStream',pf_control='AStreamControl',pf_destory='AStreamDestory'; 3) (demux_t)type='demux',capability='demux2',shortcuts='ps'; 4) (sout_instance_t)type='stream out',psz_capability='sout stream',shortcut='stream_out_standard',psz_filename='/stream_out/libstream_out_standard_plugin.so'; 5) (es_out_t)pf_add='ESOutAdd',pf_send='ESOutSend',pf_del='ESOutDel',pf_control='ESOutControl'

e)次に、MainLoop( src input input.c)関数を呼び出して、読み取り、逆多重化、デコード、多重化、および送信を完了します。

f)MainLoop関数は、input_thread_tオブジェクトにb_die、b_error、およびb_eofが含まれるまで無限ループです。この関数には、次のコード行があります。

i_ret=p_input->input.p_demux->pf_demux(p_input->input.p_demux) It is the starting point of the streaming media server, and all subsequent operations will continue to be derived in this function.

g)Pf_demuxは( modules demux ps.c)のDemux関数を呼び出します。この関数では、主に次の操作が実行されます。

  1. 最初にps_pkt_resynch( modules demux ps.c)関数を呼び出して、PSストリーム内のデータパケットの再同期を完了します(これにはマルチメディア関連の知識が含まれている必要があり、補足する必要があります)

  2. 次に、ps_pkt_read( modules demux ps.c)関数を呼び出し、最後にstream_Block関数を呼び出します。この関数は、実際の状況に応じてstream_tモジュールのpf_readまたはpf_block関数を呼び出し、関数の結果は読み取りバッファーを返します。

  3. データパケットのi_codeの値に応じて、異なる処理を行います。オーディオおよびビデオデータストリームの場合、es_out_Send( include vlc_es_out.h)関数を呼び出して処理します

  4. es_out_Sendは、関数ポインターを使用して実際にEsOutSend( src input es_out.c)関数を呼び出す抽象化レイヤー関数です。

  5. EsOutSend関数は、最終的にinput_DecoderDecode( src input decode.c)関数を呼び出します。

  6. input_DecoderDecode関数は、DecoderDecode( src input decode.c)関数を呼び出して、デコードを完了します。

  7. DecoderDecode関数は、pf_packetize( modules packetizer mpegvideo.c)関数を呼び出して、PESパッケージを実装します。

  8. DecoderDecode関数は、sout_InputSendBuffer( src stream_output stream_output.c)関数を呼び出して送信を実現します

  9. sout_InputSendBuffer関数のpf_sendポインターは、( modules stream_out standard.c)送信関数を参照します。

  10. Send関数は、ストリーム出力(stream_output)の抽象化レイヤー( src stream_output stream_output.c)でsout_MuxSendBuffer関数を呼び出します。最初に、送信されるデータがfifoキューに配置され、次にpf_mux関数ポインターが呼び出されて多重化が完了します。使用する

  11. Pf_mux関数ポインタは( modules mux mpeg ts.c)Mux関数を指します。多重化が完了すると、( modules mux mpeg ts.c)TSSchedule関数が最終的に呼び出され、ディスパッチの準備が整います。

  12. TSDate( modules mux mpeg ts.c)関数は、TSSchedule関数で呼び出されます

  13. TSDate関数では、ストリーミング出力(stream_output)の抽象層( src stream_output stream_output.c)のsout_AccessOutWrite関数が呼び出され、最後にpf_write関数が呼び出されてデータ出力が完了します。

  14. pf_write関数は、( modules access_output udp.c)のWrite関数を指します。これにより、データのUDP送信が完了し、データがTSストリーム出力に変換されます。

  15. 概要:pf_demux関数は、ストリーミングメディアのすべての操作の開始点です。これから派生した他の多くのモジュールの処理を通じて、上記の分析から、システムの本質はPS、ES、PES、およびTSのいくつかのストリーム間の変換であることがわかります。場合によって(主にサーバーまたはクライアントであることを意味します)、変換方法は異なります。