Next: MalibFrame
Up: 各クラスの概要
Previous: 各クラスの概要
Subsections
MAlib が提供する全てのクラスの親クラスとなるルートクラスである(抽象クラス)。
MAlib のオブジェクトが共通に持つべき属性(メンバ変数)と振舞い(メンバ関数)
をこのクラスで定義する。
現在のところ、メンバ変数としてはバーチャル関数テーブルへのポインタ
(klass)、リファレンスカウンタ(refcount)、
リファレンスカウント用テンポラリ変数(refvar)が用意されている。
またバーチャル関数としてはデストラクタ(malib_object_delete())
のみが用意されている。
オブジェクトの消去は、関連するリンクを全て辿って再帰的に行う。
例えば次に示す連結が存在する場合を考える。
ソースA → バッファB → フィルタC → ホルダD(デスティネーション)
この場合、デスティネーションたりうる「ホルダD」を消去するだけで
全て消去されるように、デストラクタを記述することに注意する。
なお次に述べるとおり、オブジェクトの消去はリファレンスカウンタが0になっ
た時点ではじめて消去される。リファレンスカウンタの減算は
バーチャル関数 malib_object_delete() の定義において必ず行なわれる。
したがってオブジェクトの消去を malib_object_delete() を用いて
行なっている限りは、ユーザあるいはサブクラスの実装者は
リファレンスカウンタの取扱いを意識する必要はない。その意味でも、
オブジェクトの消去は必ず malib_object_delete() を呼ぶことにより
実行すべきである。
各オブジェクトはリファレンスカウンタを持つ。オブジェクトのリファレンスカ
ウンタは現在のところ次の場合に利用されている。
- リンクを辿ったオブジェクトの自動的消去
- 画像フレームの更新
- フレーム情報(frame_info)の共有管理
フレーム情報の共有に関しては MalibSource の項
(
ページ)で解説する。
まずオブジェクトの消去に関しては、前述したとおりリンクを辿り自動的に
消去することを基本とする。データ構造が図3.1に示す
とおりであるとき、ユーザから明示的にオブジェクトC、オブジェクトDを
消去した場合には、次の手順でオブジェクトの消去が行なわれる。
- 1.
- ユーザがオブジェクトCを明示的に消去する。
- (a)
- オブジェクトBのリファレンスカウンタが減算される。
- (b)
- オブジェクトCそのものが消去される。
- 2.
- 続いてオブジェクトDを明示的に消去する。
- (a)
- オブジェクトBのリファレンスカウンタが減算され、0となる。
- (b)
- オブジェクトBの消去に伴い、オブジェクトAのリファレンス
カウンタが減算される。その結果オブジェクトAも消去される。
- (c)
- オブジェクトDそのものが消去される。
画像処理における同期の確保
また画像フレームの更新(
ページ)に関して、
各フィルタの処理対象の同期をとるためにリファレンスカウンタを用いている。
複雑なリンク構成をもつデータ構造に対して処理を行なう場合にも、
リファレンスカウンティングにより同一のソースから得られるデータを
対象として処理を行なうことが保証される。
具体的な例は次のとおりである。ソースから各種のフィルタおよびバッファを
経由して、デスティネーションとなるホルダまでの処理の構成パターンを
図3.2に示す。なお図3.2における
矢印は処理の流れを表し、実際のデータ構造としては逆方向の参照が与えられて
いることに注意する(すなわち、バッファBはソースAへのポインタを持つ)。
Figure 3.2:
フィルタ-バッファのリンク構造
|
|
まず簡単な場合(図3.2、上)で説明すると、
画像処理の連鎖はリンク構造の最後尾(ホルダD)に対して
malib_holder_increment_frame()を呼び出すことで
開始される(詳細は後述する)。同関数では、前段へリンクを辿ることで
リンク構造の先頭まで処理が移り、先頭すなわちソースAでは新規に画像を
生成する処理が行なわれる。例えばソースAが MalibBttv のオブジェクトで
あった場合は、新たなフレームが1枚キャプチャされ、新規の画像が生成される。
その後、新しく生成された画像に対し、関数呼び出しのリターンに対応して
リンクを戻りつつ各種のフィルタ処理が加わることで、一連の
画像処理が進められるしくみとなっている。
ところで、データ構造として図3.2(下)に示すような複雑な
リンクが構成されている場合、単純に再帰的な呼び出しのみで処理を実施
しようとすると、次のような呼び出し順序で処理が行なわれることになる。
| ホルダK → フィルタJ |
|
|
|
|
|
| |
→ バッファD |
→ フィルタC |
→ バッファB |
→ ソースA |
|
|
|
| |
→ バッファH |
→ フィルタG |
→ バッファF |
→ フィルタE |
→ バッファB |
→ ソースA |
|
| |
→ バッファI |
→ フィルタE |
→ バッファB |
→ ソースA |
|
|
|
ここで、アンダーラインを付したオブジェクトでは処理が複数回実施されて
いることが問題となる。すなわち、ホルダKに対する一回の
malib_holder_increment_frame()によって、ソースAでの
新規画像生成が3回行なわれてしまうことになり、バッファDに接続されている
リンク、バッファHに接続されているリンク、バッファIに接続されている
リンク全てが異なるソース画像に対しての処理を行なうこととなる。
これでは例えばフィルタJにおいて比較操作をしたくとも、無意味な比較を
することになる恐れがある。
そこで、全ての系列における同期を確保するためにリファレンスカウンタ
refcountおよびrefvarを利用する。
refvarの取扱いに関する規則は以下のとおりである。
- 1.
- refvarの初期値はrefcountに等しい。すなわち通常のリファ
レンスカウンティングを行なう場合は、refcountの増減に合わせ
てrefvarの値の増減も行なう。
- 2.
- malib_holder_increment_frame()からリンク構造を辿りつつ
画像処理を進める際には、refvarとrefcountの値が
等しい場合のみ、前段へ処理を進める。ソースの場合は、
refvarとrefcountの値が等しい場合のみ新規の画像を
生成する。refvarとrefcountの値が等しくない場合は、
キャッシュされている画像を利用し後段へ処理を進める。
- 3.
- 一回の呼び出しにつき、refcountの値をひとつ減算する。
refcountの値が0になった場合には、refcountの値に
初期化する。
以上のルールを適用すると、先ほどの処理の流れは次のようになる。
| ホルダK → フィルタJ |
|
|
|
|
|
| |
→ バッファD |
→ フィルタC |
→ バッファB |
→ ソースA |
|
|
|
| |
→ バッファH |
→ フィルタG |
→ バッファF |
→ フィルタE |
→ (バッファB) |
|
|
| |
→ バッファI |
→ (フィルタE) |
|
|
|
|
|
()で示した箇所は、前段に対する処理の呼び出しを実施せず、
既にあるデータを利用して処理を行なっている箇所である。上記では、
ソースAの新規画像作成処理は一度しか行なわれていない。
またフィルタEでのフィルタ適用画像は既に前回バッファFから
呼び出されたものと同じものに対する処理をバッファIに渡している。
このように、全てに対して同じソースからのデータが順次渡るように
なっていることがわかる。
なおこれらのリファレンスカウンティング機能を確実に実施するために、
データ構造のリンクは専用の関数を利用しなければならない。具体的には
例えばバッファとフィルタの接続にはmalib_filter_set_buffer_with_frame()を用いる必要がある。
またソースとホルダの接続にはmalib_holder_set_source()を
利用しなければならない。
他のリファレンスカウンタ操作も同様、リファレンスカウンタ操作が必要と
なる場合にはポインタ操作を行なうと共に必ずリファレンスカウンタの加減算を
忘れてはならない。
また、本実装で用いられているリファレンスカウンタは単純に参照数を
数えているだけである。したがって図3.3の
ようなリンク構造を為している場合、ホルダDおよびホルダEの両者に対して
malib_holder_increment_frame()の呼び出しを交互に行なわなければ
ならない。例えばホルダEが宙に浮いた状況に置かると、ホルダDに対する
malib_holder_increment_frame()の呼び出しが、実際には
2回に1回しか有効に働かないことになるので注意しなければならない。
Figure 3.3:
フィルタ-バッファのリンク構造(2)
|
|
なおリファレンスカウンタの一般的な注意として、循環構造を為すような
参照構造を作成するとそれらのオブジェクトは消去不可能になるので注意が
必要である。
Next: MalibFrame
Up: 各クラスの概要
Previous: 各クラスの概要
Jun IIO
2001-06-14