SSブログ

NuDCLその11 [Mac用USBデバイス工作]

今日は仕事が終わってから1時間半もすることがなかったのでUniversalReceiver.cppのソースを眺めていた。自宅でまとめるよりずいぶんはかどる。なんでかいな。ビールを飲まないからか。

3.7  コールバック関数

NuDCLに設定されているコールバック関数は
static void UniversalReceiveDCLCallback_Helper(void* refcon, NuDCLRef dcl)
{
    UniversalReceiver *pReceiver = (UniversalReceiver*) refcon;
        pReceiver->UniversalReceiveDCLCallback();
        return;
}
となっていてただメンバ関数を呼んでいるだけ。これはよくあるC関数からC++やObjective-Cに戻るためのパターン。ほかのコールバックも同じことをする関数になっている。

3.7.1  タイムスタンプ処理

UniversalReceiveDCLCallbackの中身はというと、最初かなりの行数にわたってタイムスタンプをバスサイクルから実時間に変換している。これはよくわからない。なぜか8秒足したりしてるし。タイムスタンプが必要になったらここんところを丸コピーだな。

3.7.2  データの取り出し

コールバックの中身を見る前に、使う側のオブジェクトがUniversalReceiverからどうやってデータをもらうかを見ておく。
コードを読むとどうやら正常にパケットが処理された場合、二通りの受け取り方があるらしい。それはどちらもUniversalReceiverへのコールバック関数としてわたしてやることになる。

まず、パケットが到着したらその時点で呼ばれるコールバックとして
typedef IOReturn (*UniversalDataPushProc)(void *pRefCon,
                                          UInt32 payloadLength,
                                          UInt8 *pPayload, 
                                          UInt32 isochHeader,
                                          UInt32 fireWireTimeStamp);

	// Registered Handler functions
	UniversalDataPushProc packetPush;
となっている。読んで字のごとくの単純なものである。

もうひとつは
typedef FWAVCUniversalReceiveCycleData UniversalReceiveCycleData;

// Function prototype for alternate "structured" data push callback.
typedef IOReturn (*StructuredUniversalDataPushProc)(
                                        UInt32 CycleDataCount,
                                        UniversalReceiveCycleData *pCycleData,
                                        void *pRefCon);

    StructuredUniversalDataPushProc structuredDataPush;
というコールバックでこっちはsegmentごとに一回呼ばれる。データの実体はUniversalReceiveCycleDataという構造体に入ってくる。これは
typedef struct FWAVCUniversalReceiveCycleDataStruct
{
    void *pRefCon; 
    UInt32 payloadLength;
    UInt8 *pPayload; 
    UInt32 isochHeader;
    UInt32 fireWireTimeStamp;
    UInt64 nanoSecondsTimeStamp;
    void *pExpansionData;	// Currently Unused
}FWAVCUniversalReceiveCycleData;
というふうにAVSShared.hで定義されている。これは配列になっていて中身は実質的にパケットごとに呼ばれるのと同じものである。当然こっちのほうが呼び出しのオーバーヘッドは(ちょっとだけど)減ることになる。パケットごとに処理するには重いような場合、こっちを使うということだろう。

3.7.3  パケットの処理

コールバックの最初の方で
    segment = currentSegment;
    startBuf = segment * isochCyclesPerSegment;
となっている。currentSegmentはインスタンス変数でfixupDCLJumpTargets関数で初期化され、コールバックの最後にsegmentがひとつ終わるごとにインクリメントされる。segmentとstartBufは自動変数で、startBufはsegmentの先頭のパケットが全体の何番めになっているか、という数になる。

タイムスタンプ処理が終わったあと
    UInt32 *pCycleBuf = (UInt32*) (pReceiveBuffer + (startBuf * cycleBufLen));
とあって、pCycleBufは受信パケットデータの先頭を指す。その後
    for (i = 0 ; i < isochCyclesPerSegment ; i++) {
        payloadLen = ((*pCycleBuf & 0xFFFF0000) >> 16);
 		
        UInt8* pPayload = (UInt8*)(&pCycleBuf[1]);
となっている。アイソクロナスパケットの構造を図-2に示す。
0715fig2.png
パケットの先頭はヘッダで、DCLコマンド中でupdateが実行されていればヘッダのエンディアンは調整され、CRCフィールドは(デフォルトでは)取り除かれるている。

つまりpaylodaLen変数はdata_lengthフィールドが設定され、pPayloadはデータ領域の先頭を指すことになる。

さらに、
        if (payloadLen > ((noDataCIPMode == true) ? 8 : 0)) 
            segmentHasData = true;

        if (((payloadLen+4) > cycleBufLen) && (segmentBadPacketDetected == false)){
            ....			
            segmentBadPacketDetected = true;
        }
となってデータの長さによってデータを持っているかどうか、バッファが上書きされていないかどうかを判断している。CIPModeというはFireWireでAVデータを送るときのペイロードの先頭にあるヘッダがあるかどうかのフラグらしい。このヘッダによって圧縮がDVなのか、MPEGなのか、パケットデータはフレームのどの位置なのか、などと言った情報がわかるらしくて、このフォーマットではデータがカラでもこのヘッダだけが送られてくることがあるらしいのでそれをチェックしていると思われる。

次は
        if (structuredDataPush != nil) {
            ....  (1)
			// Bump the numbeer of cycleStructs
            numCycleStructs++;
        }
        else if (packetPush != nil)
            packetPush(pPacketPushRefCon,
                       payloadLen,
                       pPayload,
                       pCycleBuf[0],
                       dclTimeStamp);
        ....
        pCycleBuf += (cycleBufLen / 4);
    }
となって(1)の部分では構造体の中身を詰めている。structuredDataPushが優先されて呼ばれるということがわかる。pCycleBufポインタを進めてループを終わっている。どうでもいいけど変わった綴りの間違いを犯していて、よほどビールが飲みたかったのかもしれない。これは綴りが間違っていたので目立つコメントだったけど、これに似たようなpthread_mutex_lock関数を呼ぶところに「ミューテクスロックをロックする」、AllocateReceivePacketStartDCLを呼んでいるところで「パケットを受け取るDCLをアロケートする」、CreateLocalIsochPortを呼んでいるところで「ローカルポートを作成する」といったようなコメントがいっぱいある。こういう役に立つとは思えないコメントがこのソースには多い。そんなことより何のためにやっているかとか、前後との関係とかをコメントして欲しいんだけど。

ところで、上のコードの「....」の部分ではまたタイムスタンプをいじっている。理解できない。さらにこのループを抜けたあと、structuredDataPushを呼ぶコードが続いている。

どちらを使うにしても、DCLのバッファのアドレスを直接渡しているので、次のDCLの読み込みまでにバッファのデータを退避するのは呼ばれた側の責任になる。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立07/15献立07/20 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。