[MonoTouch]ScreenCaptureができるようになるまでの過程、困った時の解決事例

はじめに

MonoTouchでAugmentedRealityのサンプルコードを作る過程でビデオキャプチャーができずに困りましたが、フォーラムなどでのサポートなどで7日間で解決できました。ここではその過程で行った"困った時の調査"事例を紹介します。
このエントリはC#の基本知識がありMonotouchでiPhoneアプリを開発する方を対象とし、開発過程でしばしば遭遇するドキュメントに詳細記載がないAPIの利用方法および呼び出したAPIの予想外の振る舞いの調査事例を紹介します。

アプリケーションへのカメラ動画の取り込み方法

やりたい事は、iPhoneのカメラ動画をリアルタイムでアプリケーションに取り込むことです。その方法は、Appleのドキュメントに記載されていないCoreGraphicsフレームキットに含まれるUIGetScreenImage()を連続して呼び出すことです。このメソッドはスクリーンイメージをCGImageRef型の画像として返します。
MonoTouchはこのUIGetScreenImage()を直接提供せず、代わりにMonoTouch.CoreGraphics.CGImage クラスの ScreenImage プロパティとして提供します。このプロパティからスクリーンキャプチャがCGImageクラスのインスタンスが取得できます。

遭遇した状況

実際にMonoTouch 1.4.5でCGImage.ScreenImageを連続取得すると、メモリ不足警告が出た後にアプリケーションが異常終了しました。原因を明らかにするためにタイマーを使い一定周期で下記コードを呼び出すテストコードを実行しました。

CGImage screenImage = CGImage.ScreenImage;
screenImage.Dispose();

テストコードは、メモリ不足をデバッグコンソールに出力するために下記のコードのようにUIApplicationDelegateのReceiveMemoryWarning()をオーバライドしていました。

public partial class AppDelegate : UIApplicationDelegate {
  public override void ReceiveMemoryWarning(UIApplication application) {
            System.Diagnostics.Debug.WriteLine("ReceivememoryWarning()");
            System.GC.Collect();
}}

テストコードを実行すると 上記のメモリ不足警告が2度3度表示された後にアプリケーションが終了します。メモリリークが原因だと分かりました。
このときちょっとおかしなことがありました。XCodeのパフォーマンスツールを使えばiPhoneアプリのメモリ消費状況を実行時に確認できます。そこでMonoTouchからXCodeにプロジェクトをエクスポートして確認したのですが、アプリケーションのメモリ消費量はキャプチャに従い単調増加しているわけではありませんでした。またメモリ消費量 18Mバイト あたりで異常終了していることが分かりました。Objective-Cでのメモリリークは、Retainしたメモリ領域を使い終わったときにReleaseしていないために生じます。ですからメモリ消費量は単調増加していくものと予測していたのですが、そうは振舞っていませんでした。

問題の切り分け(Objective-C/MonoTouch)

原因をメモリリークと推測したので、次はObjective-CかMonoTouchいずれの方に原因があるのかを切り分けました。これはMonoTouchテストコード同等のものをObjective-Cで作成し実行すれば分かります。今回のテストコードは既にObjective-Cで動作報告があるものを使用しましたから、原因はMonoTouch側にあると切り分けられました。

MonoTouchの実行詳細を見ていく

これらからMonoTouchの内部処理に踏み込むことが必要でしたが、それは /Developer/MonoTouch/user/lib/..../monotouch.dll を逆アセンブルすれば簡単に知ることができました。逆アセンブラは、例えばWindowsなら "c:\Program Files\Microsoft SDKs/Windows/v6.0A/bin/ildasm.exe" を使うことができます。
テキストエディタでScreenImageプロパティのgetアクセッサを見ていくと、UIGetScreenImage() が返すCGImageRefがリリースされていないと分かりました。

不具合原因の確認

次に推定原因を取り除いたサンプルコードを作成して動作確認するには、UIGetScreenImage()メソッドを自分で直接呼び出す必要があります。MonoTouchからObjective-Cのライブラを呼び出す方法は、Selectorクラスを使うかもしくはXCodeで作成したライブラリをリンクするか、の2方法があります。私はSelectorでフレームキットのスタティックなメソッドを呼び出し方法が分からず、後者の方法で確認しました。これまでで原因切り分けと確認が終了しました。

開発サポート
フォーラムのサポート

これまでの原因切り分けと並行して、参考情報のリンクにあるように、Monotouchのフォーラムで質問をしていました。開発のコアメンバーからの詳しいリプライをもらいつつ、上記の原因切り分けができたわけです。また報告した修正内容も数日で修正反映されていました。素早くかつ的確な対応は、助かりました。

参考情報