QTKit Captureを使ってみる - その7 [プログラミング - QTKit Capture]
QTKit Captureのお勉強の続き。さっさとやらないと忘れてしまう。
い、いかん、ああ、もう忘れそう。
ボケとらんとQTCaptureDecompressedVideoOutputを使って、プレビューの表示品質を上げる、具体的なコードを書いてみる。
それからデリゲートメソッドを実装する。
い、いかん、ああ、もう忘れそう。
ボケとらんとQTCaptureDecompressedVideoOutputを使って、プレビューの表示品質を上げる、具体的なコードを書いてみる。
具体的なコード
QTCaptureSessionを作るところまでは一緒。QTCaptureViewのかわりにQTCaptureDecompressedVideoOutputのインスタンスを作って繋ぐ。
decomOutput = [[QTCaptureDecompressedVideoOutput alloc] init]; [decomOutput setDelegate:self]; [captureSession addOutput:decomOutput error:error];例のごとくエラー処理は省く。
それからデリゲートメソッドを実装する。
- (void)captureOutput:(QTCaptureOutput *)captureOutput didOutputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection { @synchronized (self) { CVBufferRetain(videoFrame); CIImage *image = [CIImage imageWithCVImageBuffer:videoFrame]; [previewer setCIImage:image]; CVBufferRelease(videoFrame); } }Appleのガイドにはこのデリゲートメソッドはmain threadで呼ばれるとは限らないので@synchronizedの中でやれ、と書いてある。でもどれがthread safeでどれがthread safeでないかよくわかってないので全部を入れてしまった。こんなに@synchronaizedの中である必要は無いはず。videoFrameのretain - releaseのタイミングもこんなに外側である必要はないはずだけど、考え出すと悩むのでとりあえず動くことを優先する。
ここでのpreviewerはNSViewのサブクラスで、QTCaptureViewのかわりにウィンドウに貼ってあり、
- (void)setCIImage:(CIImage *)image;というメソッドを持っているとする。このメソッドは引数のCIImageをインスタンス変数として保持するだけ。
ダメなコード
そのあと、previewerは自分を描画するdrawRect:の中でごく普通にNSImageを描画するのと同じように- (void)drawRect:(NSRect)rect { if (image) { [image drawInRect:[self frame] fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0f]; [image release]; } }などとすればいいということになる。ところがこのコードはなぜか動作しない。CIImageのdrawInRect:云々のメソッドはNSImageの同名のメソッドとは動作が違うみたいで、NSImageのReferenceにはfromRectの後の引数にNSZeroRectを渡すと全領域が描画されるとあるけど、CIImageにはその記述がない。どうやらそれが問題らしい。
動くコードその1
これを
- (void)drawRect:(NSRect)rect { if (image) { NSCIImageRep *imageRep = [NSCIImageRep imageRepWithCIImage:image]; NSImage *im = [[NSImage alloc] initWithSize:[imageRep size]]; [im addRepresentation:imageRep]; [im drawInRect:[self frame] fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0f]; [im release]; [image release]; } }こうすれば動いた。つまりCIImageからNSCIImageRep(要するにビットマップ)を作ってそれをRepresentationにするNSImageを描画する。Still Motionはこのやり方を使っている。このNSImageをムービーに継ぎ足して行くことでスティルモーション(ストップモーション)を作っている。実質的なオーバーヘッドはそれほど大きくないけど(CIImageやNSImageは単なる入れ物のはずなので)、無駄が多いように見える。
動くコードその2
またNSZeroRectではなく、ソースイメージとしてのCIImageのCGRectをちゃんと指定して、
- (void)drawRect:(NSRect)rect { if (currentImage) { NSRect extent = NSRectFromCGRect([image extent]); [image drawInRect:[self frame] fromRect:extent operation:NSCompositeCopy fraction:1.0f]; [image release]; image = nil; } }とする。これでも動いた(NSRectFromCGRect()と言う関数を見つけて使っているけど、NSRectとCGRectって全く同じ構造体なんだよな。なんか無駄)。ひとつ前のNSImageを作るコードよりはちょっとオーバーヘッドは少ないはず(といっても多分ビットマップは使い回されているので大勢に影響はない)。やはりCIImageのdrawInRect:云々のメソッドはNSZeroRectを受けなくて(というより、その通り解釈する)全画面であっても大きさを指定する必要がある、ということらしい。
このコードを実際に僕のiMacで実行してみると、QTCaptureViewにくらべると
- ブロックノイズは減る
- 色がしゃっきりする
ところがDVストリームの場合、
- 大きな表示サイズでは1秒に2~3回ぐらいフレーム落ちする
- インターレースの横ギザギザがめちゃめちゃ目立つ
IIDCはいいとしてDVのこのふたつの問題を改善する方法はあるのか?
もうちょっとだけ突っ込んでみよう...
2008-09-01 23:43
nice!(0)
コメント(0)
トラックバック(0)
コメント 0