Exampleスケジュールの発行を扱うためにBMediaEventLooperクラスを使用してnodeの実装を行うのは、非常に簡単な作業です。事実、Be社はBMediaEventLooperをバイパスしないことを推奨しています。この節では、制御ポート及びキューの処理を扱うために、BMediaEventLooperを使用するnodeを生成する例を提示します。BMediaEventLooperを使用するnodeを実装する際には、複数のクラスからnodeを派生させなければなりません。BMediaEventLooperはもちろんですが、ユーザがそのnodeを設定できるようにしたいなら、BControllableからも派生させる必要があります。それから、バッファを送受信を行うか否かによってBBufferConsumerとBBufferProducerのいずれか或いは両方から派生する必要もあります。sourceを提供できるなら、BTimeSourceから派生する必要もあります。BMediaEventLooperを使用しても、あなたのnodeが派生する全てのクラスの純粋仮想関数を実装する責任がなくなるわけではありません。同様に、あなたは他のいくつか或いは全ての仮想関数を実装しなければならないでしょう。あなたが得るBMediaEventは、受け取ったメッセージを自動的にキューに入れ、制御ポートを自動的に処理します。これは沢山の退屈で繰り返しばかりの作業を減らし、あなたの作業をとても楽にします。例として挙げられたコードを見てみましょう。このnodeはBBufferConsumer、BControllable及びBMediaEventLooperから派生し、ディスクファイルにメディアイベントのログを書込みます。他のparameterの間にあるこのnodeのlatencyはユーザによって設定可能であるため、あなたはこのnodeをシステムに異った負荷をかけるシミュレーションに使用することができます。この節は、サンプルコードの鍵となる部分のみ示します。ダウンロードすることができます。Destructorそれでは、本当の最初から(始めるのにとても良いところから)始めましょう。}BMediaNode、BBufferConsumer、BControllable及びBMediaEventLooperのコンストラクタは、それぞれのサブクラスを標準的な方法で初期化するために、ここで全て呼び出されます。加えて、一組の局所変数も初期化されます。最後に、ログファイルへの書き込みを実際に扱うスレッドが開始されます。これは、LogWriterクラスのコンストラクタによって行われます。このクラスは、ログファイルにアクセスするために使用されます。LogWriterクラスが動作する方法の詳細について、これ以上は立ち入りません。LogWriterはファイルにログを書込むメッセージに影響するいくつかのpublicな変数と、ログファイルに新しいentryを実際に書込むLog()関数を持っている、とだけ言っておきます。}上記のデストラクタは、BMediaEventLooperの制御ループが停止することによって開始されます。これはBMediaEventLooper::Quit()の呼び出しによって行われます。webを私たち自身で削除する代りに、それにNULLをセットします。これによってnodeがクリーンアップを扱えるようになるからです—この場合、nodeがwebを削除します。全てが終了した後、ログを取るためのスレッドを削除します。ログへの書き込みを起こすようなイベントが到着した場合に起こりうる競合状態を避けるために、これは最後に行われます。BMediaEventLooperが終了されるまで待つことで、この潜在的な問題を回避できます。Functions私達は、BMediaNodeクラスからのいくつかの関数を実装する必要もあります。BMediaNode::AddOn()以外に、BMediaEventLooperもこれらの関数のデフォルト実装を提供します。多くの場合、デフォルトの実装が使用できます。ひとつのカスタム実装を見れば、どう実装するか理解することができます。}BMediaNode::AddOn()関数の仕事は、nodeのインスタンスを生成するBMediaAddOnオブジェクトへのポインタを返すことです。この場合、私達はアプリケーションに内包されているため、NULLを返します。add-onによって生成されるなら、そのオブジェクトへのポインタをここで返します。}BMediaNode::SetRunMode()関数は、nodeの実行モードの変更を扱います。BMediaEventLooperクラスはこれを自動的に扱いますが、必要があればこの実装を強化することができます。実装した他のBMediaNode関数の全てに於ても同じですが、この場合、私達は単純に呼び出しをログに落とし、その後BMediaEventLooperの実装に従います。現在の時刻を得るためにTimeSource()->Now()を呼び出します。これは、発生したイベントのタイプの記述とともにログに挿入されます。同様に動作するよう実装した他のBMediaNode関数は、要求をログに書込んだ後、BMediaEventLooperの実装に従います。Functions私達の実装するBControllable関数は、ユーザによるnodeの設定を可能にします。これらの関数は、誰かがparameterのうち一つの現在の値を知る必要がある時や、parameterの値が変更を必要とする時に呼び出されます。}BControllable::GetParameterValue()関数は、nodeの設定可能なparameterのうち一つの現在の値を得るために呼び出されます。このparameterは、引数idのID番号によって指定されます。valueは、値が保存されているメモリバッファのポインタを示し、ioSizeはバッファの大きさを示します。私達の仕事は、parameterが最後に変更された時間をlast_changeに、新しい値をvalueポインタによって示されるバッファに、そして返された値の実際の大きさをioSizeに保存することです。ログを取るためのnodeは、ログファイルに要求を書込むことから始まります。その後、関数を呼び出した者によって提供される空間が結果に対して十分大きいことを確認するためにチェックすることで、現実の実装が始まります。このnodeでは全ての値が4バイトであるためチェックが簡単ですが、あなたがnodeを書く場合はparameter毎のチェックをしなければならないでしょう。それから、idに基づいて、(webが値が変化したことを示す適切なメッセージを受け取った時に設定される)parameterの値の最後の変化をキャッシュした値が、結果に格納されます。私達は、BParameterWebの機能を間もなく見ることになります。}BControllable::SetParameterValue()は、parameterの値を変更する要求が生成されるときに呼び出されます。通常、まず要求をログに取ります。この関数の要点は、switch構文です。これは、要求に対応するmedia_timed_eventをエンキュー(enqueue)します。time及びparameterの値が変更される時に必要とされる他の全てのparameterによって、新しいmedia_timed_eventのインスタンスを生成します。それから、BTimedEventQueue::AddEvent()を呼び出すことでBMediaEventLooperのキューにこれを挿入します。このキューは、BMediaEventLooper::EventQueue()によって返されます。performance_timeによって指定された時間が来たら、これは自動的にキューから削除され、BMediaEventLooper::HandleEvent()に従って送られます。このイベントは、BTimedEventQueue::B_PARAMETERのタイプを与えられます。このタイプは、parameterの変更イベントに使用されます。Functions次に、私達が実装しなければならないBBufferConsumerに来ました。これらの関数は実際に、バッファの到着と、producerとの交渉を扱います。}HandleMessage()は、もしBMediaEventLooperを使用している場合は決して呼び出してはいけません(もし呼び出したら、この継承レベルでは扱えないメッセージを受け取ります)。したがって、私達はそれをログに書き込み、エラーを返します。}AcceptFormat()は、与えられたdestinationにとって指定されたフォーマットが受け入れられるものかどうかを確認するために呼び出されます。あなたは、フォーマットを見てこれを判断するよう実装する必要があります。そのフォーマットに対応していなければ、エラーを報告します。この実装例では、呼び出しをログに書込んだ後、実際にそのdestinationが有効かどうかを確認するためにチェックを行います。このnodeの例では、ただ一つの入力だけが許されているため、これは簡単にチェックできます。入力の配列やリンクされたリストを持つなら、それらを全てチェックする必要があるでしょう。destinationが認識されなければ、B_MEDIA_BAD_DESTINATIONを返して下さい。フォーマットが受け入れられるなら、B_OKを返して下さい。この場合、全てのフォーマットが受け入れ可能です。}GetNextInput()関数は、あなたのnodeが提供する全ての入力を繰り返し処理するために使用されます。out_inputに、要求された入力のmedia_input構造体のコピーを渡して下さい。cookieは、返される入力を指定するために使用されます。呼び出し側は、最初にGetNextInput()を呼び出した際に、0へのポインタを指定します。この値を、入力のリストのどこをスキャンしているか追跡するために適切に設定することができます。このケースでは使用できる入力が一つだけのため、もし*cookieが(リストの終端に到達したことを示す)0ではない値であればB_BAD_INDEXを返します。なければ、out_inputにデータが格納され、cookieが最初の入力がスキャンされたことを示す1に変更されます。}もしcookieが単純な整数の値でないが、しかし実際のポインタであれば、DisposeInputCookie()の実装の中でそれを破棄して下さい。このケースではcookieが整数であるため、なにもすることはありません。}BufferReceived()は、アクティブな接続にバッファが到着する際に呼び出されます。最初の仕事は、適切な時間にそれらが処理されるようキューに入れることです。到着時刻と到着までの時間に関する情報を含め、バッファの到着をログに取ることから始めます。timeを持っています)。そのため、私達はバッファがB_MEDIA_PARAMETERSバッファではないかを調べるために、バッファのヘッダをチェックします。バッファがB_MEDIA_PARAMETERSであれば、parameter変化を利用するためにApplyParameterData()を呼び出します。これによって、バッファは直ちにリサイクルされ、再使用可能となります。他のタイプのバッファであれば、新しいバッファを参照する新しいmedia_timed_eventが生成されます。イベントのタイプはBTimedEventQueue::B_HANDLE_BUFFER、クリーンアップモードはBTimedEventQueue::B_RECYCLE_BUFFERとなるので、バッファはイベントが処理された後に自動的にリサイクルされます。その後、新しいイベントがキューに追加されます。バッファをキューに入れようとした際にエラーが生じたら、バッファはキューにありませんので、BBuffer::Recycle()を呼び出して私達自身がそれをリサイクルする必要があります。これを行わなければ、バッファはリークします。メモリのリークは治療法の知られていない厄介な問題ですので、適切な予防を行って下さい。}ProducerDataStatus()は、上流のproducerがバッファの送出を開始・停止するといった変化を生じた際に呼び出されます。これによってあなたのnodeは、バッファが予期されるか否かに基づいて、その動作効率を最適化することができます。呼び出しをログに書込んだ後、この要求はキューに入れられます。指定されたdestinationであるfor_whomがnodeの実際の入力に一致する場合のみ、要求がキューに追加されるということに留意して下さい。一致しなければ、要求は無視されます。イベントはBTimedEventQueue:B_DATA_STATUSタイプを与えられ、クリーンアップはしないよう要求されます。}GetLatencyFor()の仕事は、特定のdestinationについてのlatencyを報告することです。入力がそのdestinationを持つかどうかを見ることで全ての入力をチェックし、destinationが有効であるということを確認して下さい。一致しなければ、B_MEDIA_BAD_DESTINATIONを返して下さい。sourceを保存して下さい。このnodeの例では、latencyはmLatencyであり、これはユーザが設定できるparameterです。このケースでは、このnodeには(producerではないため)下流のlatencyがないので、カウントされるlatencyは内部のlatency、つまりmLatencyだけです。これは、このnodeに於けるユーザが設定可能なオプションです。}BBufferConsumer::Connected()は、指定されたproducerと、nodeのdestinationであるwhereとの間の接続が確立される際に呼び出されます。この接続は、ログファイルに書込まれます。destinationが有効でなければ、B_MEDIA_BAD_DESTINATIONが返されます。なければ、接続のlatencyが計算され、latencyをBMediaEventLooperに伝えるためにBMediaEventLooper::SetEventLatency()が呼び出されます。これは重要なステップです。BMediaEventLooperクラスがスケジュールを扱いますが、それにはlatencyを知る必要があるからです!このケースでは、latencyの計算に関して大したことはしません。現実世界のnodeには既知のユーザ設定可能な値であるmLatencyが存在し、あなたは意味のある範囲で可能な限り正確に、これを計算する必要があります。最後に、接続についての重要な情報が(例えばmedia_source、producerといった)入力に記録されます。入力へのポインタがout_inputに詰め込まれます。}BBufferConsumer::Disconnected()は、sourceであるproducerと、destinationであるwhereとの間の接続が終了される際に呼び出されます。この呼び出しはログファイルに書込まれ、その後、nodeの接続を切断するために必要なことを全て行います。このケースでは、接続によって使用されるmedia_inputレコードを消去します。実装によっては、nodeは他の作業を行う必要があるかも知れません。}BBufferConsumer::FormatChanged()は、与えられたproducerとconsumerとの間の接続によって伝達されるバッファに対して使用されるmedia_formatが変化した際に、必ず呼び出されます。これは、nodeがBBufferConsumer::RequestFormatChange()を呼び出したことに対応して呼び出されます。要求された変化が生じると、これはそのことをあなたに知らせるために呼び出されるのです。change_tagは、RequestFormatChange()によって返される指定された変更タグに一致します。formatは接続に対して交渉された新しいmedia_formatを示します。あなたのFormatChanged()関数が返ると、これ以後のバッファは新しいフォーマットになり、この時点であなたはこの変更に対して準備をすべきです。このケースでは、実際には受け取ったバッファの内容について心配しなくてよいため、私達は生じた変更をログに書き込んでB_OKを返す以外、なにもすることはありません。Serverから呼び出されます。あなたのnodeは、受け取ったバッファに組み込まれたこれらのタグをキャッシュする必要がありますので、この関数が呼び出された際にはこれらのタグを返すことができます。この例ではseekタグをサポートしないため、要求はログに書込まれてB_OKが返されます。詳しい情報は、BBufferConsumer::SeekTagRequested()をご覧下さい。Functionsnodeの核となるのは、BMediaEventLooperの多様な仮想関数を実装することです。}BMediaRoster::RegisterNode()が呼び出される際に呼び出されるBMediaEventLooper::NodeRegistered()の仕事は、新たに登録されたnodeのセットアップを行うことです。この例では、呼び出しをログに書込むことから始め、BMediaEventLooper::SetPriority()の呼び出しによってスレッドの優先度を設定し、その後BMediaEventLooper::Run()を呼び出すことで制御ポートを動作させます。このスレッドはイベントを処理し、適切な時間にそれらをBMediaEventLooper::HandleEvent()に渡します。これが終了すると、私達は入力を初期化できます。このケースでは入力は一つだけですが、あなたのnodeは複数の入力を持つかも知れません。入力のmedia_destinationのポートは、BMediaEventLooper::ControlPort()に設定されます。Kitに対して、入力と更新するためにメッセージを送るポートがどれかを伝えます。destinationのIDは、nodeによって指定された値—このケースでは0に設定されますが、あなたのnodeはサポートする入力それぞれに対して異なった値を使用して下さい。入力のnodeにはNode()の結果が設定されます。私達はまた、入力の名前を設定します。この名前は、各々の入力ごとにユニークであるべきです。webがbuild_parameter_web()の呼び出しによってコンストラクトされ、私達はwebを確立するためにBControllable::SetParameterWeb()を呼び出します。TimeWarp()これらの関数は、対応するイベントが発生した際に呼び出されます。イベントを扱う時間が来た際に、HandleEvent()は適切なメッセージを受け取るよう実装されているため、普通はこれらを実装する必要はありません。事実上、これらの関数を実装するのは、これらのイベントのうちの一つがいつキューに挿入されたかを知りたい場合のみです。これらの実装を敢えて選択するなら、基底関数を呼び出して下さい。}このコードは結果をログに書き込み、その後にBMediaEventLooper::Start()を呼び出します。これは要求をキューに入れます。これがHandleEvent()でどのように扱われるか、下記に見ることができます。HandleEvent()これはnodeの核心部分です。BMediaEventLooperがキューに入れられたイベントが処理される時刻になったことを検知すると、すぐにHandleEvent()が呼び出されます。あなたはキューから削除されるようにこれらのイベントを扱うよう、この関数を実装するべきです。あなたの実装の特性は、あなたのnodeが何をするかに依存します。logMsg);最初に、私達の例としてはお決まりの処理として、呼び出しがログに書込まれます。次に、下記に見られるように、私達のnodeがサポートする多様なイベントタイプをチェックして扱うための長いswitch命令がやってきます。break;最初のcaseは、BTimedEventQueue::B_HANDLE_BUFFERイベントです。このイベントは、入ってきたバッファを扱う時間になる際に受け取られます。バッファは、イベントのポインタフィールドをBBufferのポインタにキャストすることで得られます。それがNULLであれば、なにも行われません。なければ、バッファのヘッダが掴まれてhdrにしまい込まれます。destinationが不正であれば、なにもしません(これはなにも生じませんが、用心に越したことはありません)。destinationが適正であれば、バッファを処理し始める時となります。timeを引くことで、バッファがいつ到着するかを計算します。それから、バッファの動作がログに書込まれます(このステップは、もちろんこの特定のnode例に限定されたものです)。バッファが遅れ(how_earlyが負の値であり)、B_OFFLINEモードでもB_RECORDINGモードでもなければ、私達はバッファを無視し、producerにバッファが遅れることをBBufferConsumer::NotifyLateProducer()呼び出しによって通知します。これによってproducerは、以後のバッファの遅れを避けるためにその動作を調整することができます。B_OFFLINE及びB_RECORDINGモードに於ける遅れは容認されます。これらのケースでは、遅れは問題にならないからです。バッファが予定通りであれば、私達はバッファを処理します。この例では、おおよそlatencyに対応する処理時間を無駄にします。実際のnodeでは、このコードをバッファを扱うコード(スクリーンへの表示、スピーカーでの演奏、或いは適切なこと全て)に置き換えて下さい。statusが(producerが我々に送るバッファが更に存在することを意味する)B_DATA_AVAILABLEであれば、私達はproducerに対して更にバッファを扱う準備ができていることを伝えるために、BBufferConsumer::RequestAdditionalBuffer()を呼び出さなければなりません。この関数の呼び出しに失敗すれば、演奏は非常にひどいものになるでしょう。RequestAdditionalBuffer()が失敗した場合、エラーがログに記録されます。一度バッファの処理が終了したら、また将来使用できるようにBBuffer::Recycle()を呼び出します。次に生じうるイベントは、BTimedEventQueue::B_PARAMETERです。これはparameterの値が変更されたことを示します。break;もしlatencyのparameterが変更になったら、私達は新しいlatencyをmLatencyに、変更が生じた時間をmLastLatencyChangeに記録し、制御スレッドにlatencyの変更を知らせるためにBMediaEventLooper::SetEventLatency()を呼び出します。加えて、新しいlatencyはBBufferConsumer::SendLatencyChange()の呼び出しによってproducerに送られます。これによって、producerは処理が荒れ狂うこと(或いは新しいlatencyのせいで脱落すること)を避けるために、自らの動作を適切に変更することができます。変更を監視しているもの全てとparameterの値の変化を共有するために、BControllable::BroadcastNewParameterValue()が呼び出されます。最後に、変化をログとしてディスクに書込みます。parameterが変更されたら、私達は新しい値と変更の生じた時間を記録し、それからその変更を世界に広く伝え、変更をログに取ります。これはBMediaEventLooperが関心を持つことに全く対応していないため、追加すべき処理はありません。break;優先度のparameterが変更された際は、制御スレッドに新しい優先度を伝えるためにBMediaEventLooper::SetPriority()を呼び出します。スレッドの優先度を直接変更してはいけません。優先度はnodeの機能に影響しますし、この変更に基づいて行われなければならない他の変更があるからです。新しい値は変更時間とともに保存され、その値は広く伝達され、そしてログのentryはディスクに記録されます。break;ここで、存在しないparameterが変更された場合に対処しましょう。このエラー状態は、ディスクにログとして記録されます。それが決して起こらないとしても、あなたはこの可能性を丁重に扱って下さい。扱われる必要のある次のイベントタイプは、BTimedEventQueue::B_STARTBTimedEventQueue::B_STARTです。これはバッファの処理を開始する時間になると受け取られます。break;この例のnodeでは、開始の要求を単にログとして記録します。あなたのnodeでは、バッファを処理するための準備に必要なコードを挿入して下さい。nodeがproducerであれば、この時点でバッファの送信を始めて下さい。consumerにバッファを送信していることを知らせるために、かならずBBufferProducer::SendDataStatus()を呼び出して下さい。次のイベントタイプはBTimedEventQueue::B_STOPBTimedEventQueue::B_STOPです。break;nodeを停止することは、現在までに受け取ったがまだ処理されていない全てのバッファが無視されることを意味します。このため、イベントキューから全てのBTimedEventQueue::B_HANDLE_BUFFERイベントが消去されます。nodeが停止する際に行う必要のある他の処理があれば、これはそれらを扱う場となります。nodeがproducerなら、もう到着するバッファがないということをconsumerに知らせるために、ここでBBufferProducer::SendDataStatus()を呼び出すべきだということに留意して下さい。BTimedEventQueue::B_SEEK要求は、nodeのメディア上でseek処理が行われる際に受け取られます。break;ここでseek処理を扱って下さい。このnodeでは、seek要求をログに取ります。warp処理を行う際に受け取られます。break;この例では、要求をログに取ります。BTimedEventQueue::B_DATA_STATUSイベントは、producerのBBufferProducer::SendDataStatus()関数が呼び出される際に受け取られます。当然ながら、このイベントを扱う必要があるのはnodeがconsumerの時だけです。mProducerDataStatusに格納されている)を記録します。この情報は、バッファを待つかどうかを決定するために使用されます。あなたのnodeは、もしproducerがバッファの送信を停止したら、自分の優先度を変更するか他の最適化を行いたいでしょうし、送信が再開されたら優先度を上げたいと願うでしょう。私達はこの変更もログに取ります。}最後に、私達は受け取った理解できないメッセージ全てをログに取ります。一般的に、nodeが扱うよう準備されていないメッセージは無視することができます。Kitが利害関係を持つユーザ設定可能な多種のオプションを記述し、またそれらがどうやってユーザインターフェイスに現れるかを記述します。themeによって使用されます。BParameterWebの生成は単純です。parameterに関連する論理的なグループであるBParameterGroupを生成することから始め、その後に生成可能なparameterの様々なタイプのために適切なBParameterGroupの関数を呼び出してそれぞれのグループにparameterを挿入します。BParameterGroup::MakeNullParameter()は、インターフェイスに現れるラベルやその他の設定できないcontrolを生成するために使用することができます。値はスライダのcontrolを使用するためのセットです。BParameterGroup::MakeDiscreteParameter()は、不連続な値を取りうるセットとともにparameterを生成します。いくつかのthemeはラジオボタンやリストを使用しますが、通常これらはポップアップメニューとして表示されます。}優先度のparameterに不連続な値を追加するためにBDiscreteParameter::AddItem()関数を使用することについては、注意して下さい。それぞれのアイテムは、ユーザインターフェイスで表示される値とラベルを持ちます。この例では、これらは多様なスレッドの優先度と名前に対応します。このnodeによって提供されるユーザインターフェイスによって、スレッドの優先度、バッファが"processed"の間のCPUの占有率、そしてバッファの処理のlatencyを含むnodeの振る舞いをユーザが設定できます。当然ながら、あなたのnodeは異ったparameterを持つでしょう(または持たないかも知れません。このケースでは、nodeはBControllableから派生することさえなく、BParameterWebは全く必要ありません)。この関数は、上記のBMediaEventLooper::NodeRegistered()の実装から呼び出されます。NodeRegistered()は、その後にwebを使用するStartControlPanel()またはその他の関数によって使用されるwebを確立するためにSetParameterWeb()を呼び出します。IssuesnodeがBBufferProducerから派生するなら、実装する必要のある仮想関数が明らかにいくつか残っています。Connect()BBufferProducer::Connect()の実装では、latencyの合計を確立するためにBMediaEventLooper::SetEventLatency()を呼び出して下さい。内部的なlatencyに下流のlatencyを加えたこの値は、処理されるイベントをいつキューからポップするか決定するためにBMediaEventLooperによって使用されます。この値を最新の状態に保つことによって、動作効率を改善することができます。Story...この例では、LogWriterクラスの実装は重要ではありませんし、スペースを節約するという名目で上手に無視されています。あなたが好奇心旺盛なら、LoggingApplicationの完全なサンプルコードを、ftp://ftp.be.com/pub/samples/media_kit/LoggingConsumer.zipに参照することができます。reserved..。