Windows Phone 7
Windows Phone アプリケーションでカメラを使用する
写真を使えば、言葉だけを交わすよりも、効果的かつ的確にコミュニケーションできます。「百聞は一見にしかず」という格言をお聞きになったことがあるでしょう。Windows Phone アプリケーションからカメラに直接アクセスできれば、解決できる問題の種類は大きく変わります。Windows Phone 7.5 からは、「たくさんの言葉で語られる問題」の解決に、デバイスに搭載されたカメラを利用できるようになります。
今回は、デバイス前面と背面のカメラ、カメラ API、および関連するマニフェスト機能を紹介し、次期 Windows Phone 7.5 アプリケーションでカメラを使用するさまざまな方法をいくつか説明します。説明するテーマは、次の 3 つです。
- 写真を撮る: ごく簡単なフォト アプリケーションを作成します。
- カメラ プレビュー バッファーにアクセスする: 「カメラのグレースケールのサンプル」を紹介します。
- ビデオを録画する: 「ビデオ レコーダーのサンプル」をレビューします。
Windows Phone 7.5 アプリケーションの作成には Windows Phone SDK 7.1 が必要です。この SDK には、上記のシナリオをそれぞれ詳しく例示するコード サンプルが含まれています。詳細については、SDK のコード サンプルのページ (https://msdn.microsoft.com/ja-jp/library/ff431744(VS.92).aspx) で「基本的なカメラのサンプル」、「カメラのグレースケールのサンプル」、および「ビデオ レコーダーのサンプル」を参照してください。
今回の記事では、Windows Phone 7 から利用可能になったカメラ キャプチャ タスクは扱っていません。このタスクは、アプリケーション用に写真を取得する簡単な方法を提供しますが、プログラムで写真を撮ったり、カメラのプレビュー バッファーにアクセスすることはできません。
Windows Phone 7.5 デバイスには、それぞれプライマリ カメラと前向きカメラに指定する、最大 2 台のカメラを搭載できます。プライマリ カメラはデバイスの背面に搭載され、一般に前向きカメラに比べて高解像度で、多くの機能を提供します。Windows Phone 7.5 デバイスではどちらのカメラも必須ではないため、コードではカメラのオブジェクトを作成する前に、カメラが搭載されているかどうかを確認する必要があります。記事の後半で、この目的に IsCameraTypeSupported という静的メソッドを使用する方法を紹介します。
米国で入手できる Windows Phone デバイスの多くは、5MP 以上のセンサー、オートフォーカス、およびフラッシュを備えたプライマリ カメラを搭載しています。前向きカメラは Windows Phone 7.5 の新機能です。
デバイス仕様の詳細については、windowsphone.com の [購入] タブを参照してください。
写真を撮る
プライマリ カメラでも、前向きカメラでもアクセスに使用するクラスは同じです。つまり、選択するカメラの種類を PhotoCamera オブジェクトのコンストラクターの唯一のパラメーターに指定するだけです。しかし、設計上は、前向きカメラの操作を処理する方法は区別して考えます。たとえば、前向きカメラの画像は実際の鏡のようにユーザーに操作させるような考え方です。
Windows Phone 7.5 アプリケーションで写真を撮るときは、主に Microsoft.Devices 名前空間の PhotoCamera クラスを操作します。このクラスでは、カメラの設定や動作を多数制御できます。たとえば、次のことが可能です。
- PhotoCamera.CaptureImage メソッドを使用してカメラのシャッターを有効にする
- PhotoCamera.Focus メソッドを使用してオートフォーカスをトリガする
- PhotoCamera.Resolution プロパティを設定して写真の解像度を指定する
- PhotoCamera.FlashMode プロパティを設定してフラッシュの設定を指定する
- CameraButtons 静的クラスのイベントを使用してハードウェア シャッター ボタンを組み込む
- PhotoCamera.FocusAtPoint メソッドを使用してタッチ フォーカスを実装する
ここでは最初の項目だけを紹介します。他のすべての方法を示す例については、Windows Phone SDK のコード サンプルのページで「基本的なカメラのサンプル」を参照してください。
カメラを使用できる場合でも、これらの API がすべてサポートされているとは限りません。以下の手法により、どの機能が利用できるかを判断できます。
- カメラ: PhotoCamera.IsCameraTypeSupported 静的メソッドを使用する
- オートフォーカス: PhotoCamera.IsFocusSupported メソッドを使用する
- 写真解像度の設定: PhotoCamera.AvailableResolutions コレクションを確認する
- フラッシュの設定: PhotoCamera.IsFlashModeSupported メソッドを使用する
- 特定位置へのオートフォーカス: PhotoCamera.IsFocusAtPointSupported メソッドを使用する
アプリで写真を撮る方法についてのアイデアを紹介するために、ファインダーに触れたときに写真を撮り、撮影した写真を Pictures ハブの Camera Roll フォルダーに保存する簡単なアプリのチュートリアルを示します。
まず、Windows Phone アプリケーション テンプレートを使用して、標準の Windows Phone プロジェクトを作成します。Windows Phone 7.5 のアプリは、C# または Visual Basic で作成できます。今回の例では C# を使用します。
今回は例をシンプルにするために、デバイスを横向きに限定し、プライマリ カメラだけを使用するように制限しています。デバイスの向きと 2 台のカメラを管理して、それぞれが異なる方向に向くことを考えると、すぐに混乱が始まります。このような場合は物理デバイスをテストして、希望する動作が実現されるかどうかを確認することをお勧めします。デバイスの向きについては、後で詳しく説明します。
MainPage.xaml で、PhoneApplicationPage 属性を以下のように更新します。
SupportedOrientations="Landscape" Orientation="LandscapeLeft"
次に、LayoutRoot グリッドの内容を Canvas と TextBlock に置き換えます (図 1 参照)。
図 1 Canvas と TextBlock の追加
<Canvas x:Name="viewfinderCanvas" Width="640" Height="480" Tap="viewfinder_Tapped">
<Canvas.Background>
<VideoBrush x:Name="viewfinderBrush">
<VideoBrush.RelativeTransform>
<CompositeTransform
x:Name="viewfinderTransform"
CenterX="0.5"
CenterY="0.5"/>
</VideoBrush.RelativeTransform>
</VideoBrush>
</Canvas.Background>
</Canvas>
<TextBlock Width="626" Height="40"
HorizontalAlignment="Left"
Margin="8,428,0,0"
Name="txtMessage"
VerticalAlignment="Top"
FontSize="24"
FontWeight="ExtraBold"
Text="Tap the screen to capture a photo."/>
図 1 の XAML では、Canvas で VideoBrush を使用してファインダーを表示し、TextBlock を用意してユーザーとコミュニケーションを取っています。縦横比はカメラのセンサーが 4:3 で、画面が 15:9 です。キャンバスのサイズをセンサーと同じ 4:3 の比率 (640x480) にしないと、画像は横方向に拡大して表示されることになります。
Canvas 要素の Tap 属性は、ユーザーが画面をタップしたときに呼び出すメソッド (viewfinder_Tapped) を指定します。カメラのプレビュー バッファーからの画像ストリームを表示するため、キャンバスの背景に viewfinderBrush というビデオブラシを指定します。ユーザーはこの viewfinderBrush により、一眼レフ (SLR) カメラのファインダーのようにカメラのプレビュー フレームを見ることができます。viewfinderBrush の変換は、基本的にはファインダーの向きが回転されたときに、ファインダーをキャンバスの中心に "固定" します。この XAML の分離コードについては、この後説明します。図 2 に Simple Photo App の UI を示します。
図 2 Simple Photo App の UI
写真を撮り、撮影した写真を Pictures ハブの Camera Roll フォルダーに保存するために、カメラの初期化と解放が必要です。そのため、PhotoCamera クラスと MediaLibrary クラスをそれぞれ用意します。まず、Microsoft.Xna.Framework アセンブリへの参照を追加します。今回の例では XNA のプログラミングについて理解する必要はありません。ただし、メディア ライブラリにアクセスするために、このアセンブリに含まれる型を理解することは必要です。
MainPage.xaml.cs ファイルの先頭で、カメラとメディア ライブラリに関するディレクティブを追加します。
using Microsoft.Devices;
using Microsoft.Xna.Framework.Media;
MainPage クラスには、次のクラスレベルの変数を追加します。
private int photoCounter = 0;
PhotoCamera cam;
MediaLibrary library = new MediaLibrary();
カメラの初期化には数秒かかります。PhotoCamera オブジェクトをクラス レベルで宣言することによって、ページに移動するときにオブジェクトを作成し、ページから離れるときにオブジェクトをメモリから削除できます。ここでは作成と削除の目的に OnNavigatedTo メソッドと OnNavigatingFrom メソッドを使用します。
OnNavigatedTo メソッドでは、カメラ オブジェクトを作成し、使用予定のイベント登録して、カメラ プレビューをファインダーのソース (viewfinderBrush) として設定します。一般的なことですが、Windows Phone 7.5 ではカメラはオプションなので、カメラ オブジェクトを作成する前にカメラが搭載されていることをチェックすることが重要です。プライマリ カメラが搭載されていないときは、このメソッドでユーザーにメッセージを表示します。
図 3 に示すメソッドを MainPage クラスに追加します。
図 3 OnNavigatedTo メソッドと OnNavigatingFrom メソッド
protected override void OnNavigatedTo
(System.Windows.Navigation.NavigationEventArgs e)
{
if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true)
{
cam = new PhotoCamera(CameraType.Primary);
cam.CaptureImageAvailable +=
new EventHandler<Microsoft.Devices.ContentReadyEventArgs>
(cam_CaptureImageAvailable);
viewfinderBrush.SetSource(cam);
}
else
{
txtMessage.Text = "A Camera is not available on this device.";
}
}
protected override void OnNavigatingFrom
(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
if (cam != null)
{
cam.Dispose();
}
}
ページから離れるときに、OnNavigatingFrom メソッドを使用して、カメラ オブジェクトを破棄し、すべてのカメラ イベントの登録を抹消します。これにより、電力消費を最小限に抑え、迅速にシャットダウンを行い、メモリを解放することができます。
XAML に示すように、写真を撮るにはユーザーがファインダーをタップしたときに、viewfinder_Tapped メソッドを呼び出します。このメソッドはカメラの準備が整っている場合に画像のキャプチャを開始します。カメラが初期化されていないか、別の画像のキャプチャが進行中の場合は、例外がスローされます。例外がスローされないようにするには Initialized イベントが発生するまで写真のキャプチャを開始するメカニズムを無効にすることを考えます。今回の例では説明を簡単にするために、この手順を省略しています。
図 4 に、MainPage クラスに追加する必要のあるコードを示します。
図 4 viewfinder_Tapped メソッド
void viewfinder_Tapped(object sender, GestureEventArgs e)
{
if (cam != null)
{
try
{
cam.CaptureImage();
}
catch (Exception ex)
{
this.Dispatcher.BeginInvoke(delegate()
{
txtMessage.Text = ex.Message;
});
}
}
}
写真の撮影と保存は非同期に行います。CaptureImage メソッドが呼び出されると、イベントのチェーンが始まり、制御が UI に戻ります。図 5 のイベント シーケンス図に示すように、各画像のキャプチャには 2 つの段階があります。最初にカメラ センサーが写真を撮り、次にセンサーのデータを基に画像が作成されます。
図 5 PhotoCamera クラスの画像キャプチャ イベントのシーケンス
センサーが写真を撮影後写真を保存する際に、フルサイズの画像と縮小表示画像の 2 つの画像ファイルが並列に作成されます。どちらの画像もまだ使用できません。各画像は、対応するイベントの引数に含まれる e.ImageStream プロパティから JPG 画像のストリームとして利用できます。
メディア ライブラリはデバイスの Pictures ハブに表示する独自の縮小表示画像を自動的に作成するため、今回の例では画像の縮小表示バージョンは必要ありません。ただし、自身のアプリで縮小表示画像を表示する場合は、CaptureThumbnailAvailable イベント ハンドラーで e.ImageStream を適切に選択します。
ストリームが利用可能になれば、そのストリームを使用して複数の場所に画像を保存できます。たとえば、次の場所に保存します。
- Camera Roll フォルダー: MediaLibrary.SavePictureToCameraRoll メソッドを使用します。
- Saved Pictures フォルダー: MediaLibary.SavePicture メソッドを使用します。
- 分離ストレージ: IsolatedStorageFileStream.Write メソッドを使用します。
今回の例では、画像を Camera Roll フォルダーに保存します。画像を分離ストレージに保存する方法については、Windows Phone SDK の「基本的なカメラのサンプル」を参照してください。図 6 のコードを MainPage クラスに追加します。
図 6 Camera Roll フォルダーへの画像の保存
void cam_CaptureImageAvailable(object sender,
Microsoft.Devices.ContentReadyEventArgs e)
{
photoCounter++;
string fileName = photoCounter + ".jpg";
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtMessage.Text = "Captured image available, saving picture.";
});
library.SavePictureToCameraRoll(fileName, e.ImageStream);
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtMessage.Text = "Picture has been saved to camera roll.";
});
}
図 6 のコードでは、画像を Camera Roll フォルダーに保存する前後に UI にメッセージを送信しています。このメッセージは進行状況を把握するためのもので、特に必要なわけではありません。BeginInvoke メソッドはメッセージを UI スレッドに渡すために必要です。BeginInvoke を使用しないとスレッド間の例外がスローされることになります。説明を簡単にするために、このメソッドではエラー処理コードを省略しています。
向きの回転を処理するには、画像をメディア ライブラリに保存するときに、画像の正しい向きをファイルの EXIF 情報に記録する必要があります。アプリで主に考えなければならないことは、カメラのプレビューと UI の向きを合わせる方法です。プレビューが正しい向きで表示されるようにするには、必要に応じてファインダー (VideoBrush) を回転します。回転は、OnOrientationChanged 仮想メソッドをオーバーライドすることで行います。図 7 のコードを MainPage クラスに追加します。
図 7 OnOrientationChanged 仮想メソッドのオーバーライド
void cam_CaptureImageAvailable(object sender,
Microsoft.Devices.ContentReadyEventArgs e)
{
photoCounter++;
string fileName = photoCounter + ".jpg";
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtMessage.Text = "Captured image available, saving picture.";
});
library.SavePictureToCameraRoll(fileName, e.ImageStream);
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
txtMessage.Text = "Picture has been saved to camera roll.";
});
}
protected override void OnOrientationChanged
(OrientationChangedEventArgs e)
{
if (cam != null)
{
Dispatcher.BeginInvoke(() =>
{
double rotation = cam.Orientation;
switch (this.Orientation)
{
case PageOrientation.LandscapeLeft:
rotation = cam.Orientation - 90;
break;
case PageOrientation.LandscapeRight:
rotation = cam.Orientation + 90;
break;
}
viewfinderTransform.Rotation = rotation;
});
}
base.OnOrientationChanged(e);
}
ファインダーの向きを調整しない場合、標準的なプライマリ カメラのファインダーはハードウェアのシャッター ボタンが上を向いているとき (LandscapeLeft) のみ、正しい向きで表示されます。ハードウェアのシャッター ボタンが下を向く (LandscapeRight) ようにデバイスを回転する場合、UI に適切に表示するにはファインダーを 180 度回転する必要があります。プライマリ カメラの物理的な向きが非標準の場合は、PhotoCameraOrientation プロパティを使用します。
アプリの機能を宣言する場合、アプリでカメラを使用する際に、アプリのマニフェスト ファイル (WMAppManifest.xml) で機能を宣言する必要があります。使用するカメラを問わず、ID_CAP_ISV_CAMERA 機能は必要です。オプションで前向きカメラを使用する必要があることを指定するには、ID_HW_FRONTCAMERA を使用します。
<Capability Name="ID_CAP_ISV_CAMERA"/>
<Capability Name="ID_HW_FRONTCAMERA"/>
カメラのアプリは ID_CAP_ISV_CAMERA 機能を宣言しなければ実行されません。これまでこの機能を宣言しなくても問題が発生しなかったのは、Windows Phone プロジェクトを新しく作成するときにこの機能が自動的に追加されていたためです。しかし、アプリをアップグレードする場合は、この宣言を手動で追加する必要があります。ID_HW_FRONTCAMERA は常に手動で追加する必要がありますが、この宣言を行わなくてもアプリは実行されます。
これらの機能はデバイスにカメラが搭載されていないことをユーザーに警告するには有効ですが、そのようなデバイスを使用しているユーザーがアプリをダウンロードまたは購入することを防ぐことはできません。そのため、アプリのトライアル バージョンを利用できるようにすることをお勧めします。そうすれば、ユーザーのデバイスにカメラが搭載されていない場合でも、アプリが想定どおりに機能しないことを学ぶためだけに費用を負担することはなくなります。その結果、そのアプリの評価は上がります。
今回のシンプルなカメラ アプリをデバイスでデバッグするには F5 キーを押します。アプリをエミュレーターでデバッグすることはできますが、エミュレーターには物理カメラが存在しないため、画面を黒い四角形が移動するのを確認できるだけです。デバイスを使ってデバッグするときは、PC からデバイスを切り離すまで新しい画像を Picture ハブに表示できないことに注意してください。
詳細については、Windows Phone SDK の「基本的なカメラのサンプル」を参照してください。このサンプルでは、フラッシュの調整や解像度の設定からタッチ フォーカスやハードウェア シャッター ボタンの組み込みまで、写真を撮影するためのすべての API のデモが行われています。
カメラ プレビュー バッファーにアクセスする
上記の例ではカメラ プレビュー バッファーのフレームがファインダーにストリーミングされました。PhotoCamera クラスではプレビュー バッファーの現在のフレームを公開し、各フレームをピクセル単位に操作することも可能にしています。プレビュー バッファーのフレームを操作し、そのフレームを書き込み可能なビットマップ形式で UI に表示する方法を確認するために、Windows Phone SDK のサンプルを見てみましょう。
PhotoCamera クラスでは、"プレビューを取得する" 以下のメソッドを使用して、プレビュー バッファーの現在のフレームを公開します。
- GetPreviewBufferArgb32: 現在のフレームを ARGB 形式の整数配列で取得します。
- GetPreviewBufferYCbCr: 現在のフレームを YCbCr 形式のバイト配列で取得します。
- GetPreviewBufferY: 同様の形式で輝度のプレーンのみをバイト配列で取得します。
ARGB は、Windows Phone 向けの Silverlight アプリケーションでカラーを記述する際に使用する形式です。YCbCr は画像を効果的に処理できるようにしますが、Silverlight では YCbCr を使用できません。アプリで YCbCr フレームを操作する場合、表示する前にフレームを ARGB に変更する必要があります。これらの形式とカラー変換の詳細については、MSDN ライブラリの「Windows Phone のカメラ カラーの変換 (YCbCr から ARGB)」(https://msdn.microsoft.com/ja-jp/library/hh394035(VS.92).aspx) を参照してください。
Windows Phone SDK の「カメラのグレースケールのサンプル」 (図 8 参照) では、プレビュー バッファーの ARGB 形式のフレームを操作する方法とほぼリアルタイムで書き込み可能なビットマップ画像にそのフレームを書き込む方法を例示しています。今回のサンプルでは、各フレームをカラーからグレースケールに変換します。このサンプルは ARGB 操作の例を示すことを目的にしています。アプリでグレースケールのみが必要な場合、代わりに GetPreviewBufferY メソッドを使用することを検討してください。
図 8 カメラのグレースケールのサンプルの UI
XAML ファイルでは、以下のように Image タグを使用して、対応する書き込み可能なビットマップ (UI の左下隅の白黒の画像) をホストします。
<Image x:Name="MainImage"
Width="320" Height="240"
HorizontalAlignment="Left" VerticalAlignment="Bottom"
Margin="16,0,0,16"
Stretch="Uniform"/>
グレースケール変換を可能にするためにボタンを押すと、変換処理のために新しいスレッドが作成されます。この処理では、プレビュー バッファーと同じサイズの書き込み可能なビットマップを作成し、Image コントロールのソースとして割り当てます。
wb = new WriteableBitmap(
(int)cam.PreviewResolution.Width,
(int)cam.PreviewResolution.Height);
this.MainImage.Source = wb;
スレッドの処理は PumpARGBFrames メソッドで実行します。このメソッドでは、ARGBPx という整数配列を使用して、現在のプレビュー バッファーのスナップショットを保持します。配列内の各整数は、フレームの 1 ピクセルを ARGB 形式で表します。この配列は、同じサイズでプレビュー バッファーとしても作成します。
int[] ARGBPx = new int[
(int)cam.PreviewResolution.Width *
(int)cam.PreviewResolution.Height];
サンプルの "グレースケール" 機能が有効になっている間、スレッドはプレビュー バッファーの現在のフレームを ARGBPx 配列にコピーします。ここで phCam はカメラ オブジェクトです。
phCam.GetPreviewBufferArgb32(ARGBPx);
バッファーが配列にコピーされた後、スレッドは各ピクセルをすべてループして、ピクセルをグレースケールに変換します (この方法の詳細についてはサンプルを参照してください)。
for (int i = 0; i < ARGBPx.Length; i++)
{
ARGBPx[i] = ColorToGray(ARGBPx[i]);
}
最後に、次のフレームを処理する前に、スレッドでは BeginInvoke メソッドを使用して、UI の WriteableBitmap を更新します。CopyTo メソッドは、WriteableBitmap のピクセルに ARGBPx 配列を上書きします。Invalidate メソッドは WriteableBitmap を強制的に再描画します。
Deployment.Current.Dispatcher.BeginInvoke(delegate()
{
// Copy to WriteableBitmap.
ARGBPx.CopyTo(wb.Pixels, 0);
wb.Invalidate();
pauseFramesEvent.Set();
});
WriteableBitmap クラスは、クリエイティブな機能を広範囲にサポートします。これで、カメラのプレビュー バッファーを UI のさまざまな表示機能に組み込むことができるようになります。
ビデオを録画する
PhotoCamera クラスはプレビュー バッファーを UI にストリーミングするために使用できますが、ビデオの録画には使用できません。ビデオの録画には System.Windows.Media 名前空間のクラスがいくつか必要です。今回の記事の最後に、分離ストレージの MP4 ファイルにビデオを録画する方法を確認するため、Windows Phone SDK の「ビデオ レコーダーのサンプル」 (図 9 参照) を見ていきます。このサンプルは SDK コード サンプル ページにあります。
図 9 ビデオ レコーダーのサンプルの UI
ビデオ録画用の主なクラスは以下のとおりです。
- CaptureDeviceConfiguration: ビデオ キャプチャ デバイスを使用できるかどうかの確認に使用します。
- CaptureSource: ビデオの録画またはプレビューの開始および停止に使用します。
- VideoBrush: Silverlight UI のコントロールに CaptureSource オブジェクトまたは PhotoCamera オブジェクトを設定するのに使用します。
- FileSink: CaptureSource オブジェクトの実行中に分離ストレージにビデオを録画するのに使用します。
XAML ファイルの Rectangle コントロールはカメラのファインダーを表示するのに使用します。
<Rectangle
x:Name="viewfinderRectangle"
Width="640"
Height="480"
HorizontalAlignment="Left"
Canvas.Left="80"/>
ただし、ビデオの表示には Rectangle コントロールは必要ありません。最初の例で示したように Canvas コントロールを使用します。Rectangle コントロールは、別の方法でビデオを表示できることを示すためだけに使用しています。
以下の変数をページ レベルで宣言します。
// Viewfinder for capturing video.
private VideoBrush videoRecorderBrush;
// Source and device for capturing video.
private CaptureSource captureSource;
private VideoCaptureDevice videoCaptureDevice;
// File details for storing the recording.
private IsolatedStorageFileStream isoVideoFile;
private FileSink fileSink;
private string isoVideoFileName = "CameraMovie.mp4";
ユーザーがページに移動すると、InitializeVideoRecorder メソッドによりカメラが起動され、カメラのプレビューが Rectangle コントロールに送信されます。captureSource オブジェクトと fileSink オブジェクトを作成後、InitializeVideoRecorder メソッドは静的オブジェクトの CaptureDeviceConfiguration を使用してビデオ デバイスを検索します。カメラを使用できなければ、videoCaptureDevice は Null になります。
videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
Windows Phone 7.5 ではカメラはオプションです。最近のデバイスにカメラが搭載されているのは一般的ですが、コードでカメラの有無を確認するのがベスト プラクティスです。図 10 に示すように、カメラの有無は videoCaptureDevice を使用して確認します。カメラが搭載されている場合は、captureSource に videoRecorderBrush というビデオブラシをソースとして設定します。その後、この videoRecorderBrush を使用して、viewfinderRectangle という Rectangle コントロールを設定します。captureSource の Start メソッドを呼び出すと、カメラは Rctangle コントロールにビデオの送信を開始します。
図 10 ビデオ プレビューの表示
// Initialize the camera if it exists on the device.
if (videoCaptureDevice != null)
{
// Create the VideoBrush for the viewfinder.
videoRecorderBrush = new VideoBrush();
videoRecorderBrush.SetSource(captureSource);
// Display the viewfinder image on the rectangle.
viewfinderRectangle.Fill = videoRecorderBrush;
// Start video capture and display it on the viewfinder.
captureSource.Start();
// Set the button state and the message.
UpdateUI(ButtonState.Initialized, "Tap record to start recording...");
}
else
{
// Disable buttons when the camera is not supported by the device.
UpdateUI(ButtonState.CameraNotSupported, "A camera is not supported on this device.");
}
今回の例では、UpdateUI というヘルパー メソッドを使用してボタンの状態を管理し、ユーザーにメッセージを表示します。詳細については、「ビデオ レコーダーのサンプル」を参照してください。
fileSink オブジェクトを作成しましたが、この時点ではビデオは録画されません。この状態のアプリはビデオを "プレビュー" しているだけです。ビデオを録画するには、fileSink を captureSource と結び付ける必要があります。つまり、ビデオを録画する前に captureSource を停止する必要があります。
ユーザーがビデオ レコーダー サンプルの録画ボタンをタップすると、StartVideoRecorder メソッドによってプレビューから録画への遷移が開始されます。この遷移の最初の手順では、captureSource を停止し、fileSink を再構成します。
// Connect fileSink to captureSource.
if (captureSource.VideoCaptureDevice != null
&& captureSource.State == CaptureState.Started)
{
captureSource.Stop();
// Connect the input and output of fileSink.
fileSink.CaptureSource = captureSource;
fileSink.IsolatedStorageFileName = isoVideoFileName;
}
Silverlight プラグイン用のアプリを開発したことがあれば、CaptureSource クラスと VideoBrush クラスは使い慣れていると思いますが、FileSink クラスはまったく新しいクラスです。FileSink クラスは Windows Phone アプリ専用のクラスで、分離ストレージへの書き込みをすべて担当します。必要なのはファイル名を指定することだけです。
fileSink を再構成したら、StartVideoRecorder メソッドにより captureSource を再開し、UI を更新します。
captureSource.Start();
// Set the button states and the message.
UpdateUI(ButtonState.Ready, "Ready to record.");
ユーザーが録画を停止すると、録画からプレビューに遷移するために、captureSource を停止してから fileSink を再構成する必要があります (図 11 参照)。
図 11 録画からプレビューへの遷移
// Stop recording.
if (captureSource.VideoCaptureDevice != null
&& captureSource.State == CaptureState.Started)
{
captureSource.Stop();
// Disconnect fileSink.
fileSink.CaptureSource = null;
fileSink.IsolatedStorageFileName = null;
// Set the button states and the message.
UpdateUI(ButtonState.NoChange, "Preparing viewfinder...");
StartVideoPreview();
}
StartVideoPreview ロジックを別のメソッドに分けたのは、プレビューからビデオの再生状態 (今回のサンプルでは扱いません) に遷移できるようにするためです。ここでは再生について説明しませんが、Windows Phone で同時に実行できるビデオ ストリームは 1 つだけであることに注意することが重要です。
「ビデオ レコーダーのサンプル」は以下の 2 つの独立したビデオ ストリームを使って機能します。
- captureSource ~ videoRecorderBrush ~ viewfinderRectangle (Rectangle コントロール)
- isoVideoFile ~ VideoPlayer (MediaElement コントロール)
同時に実行できるストリームが 1 つだけなので、このサンプルではストリームごとに "破棄する" メソッドを用意して、他のストリームを実行する前に呼び出すようにしています。DisposeVideoPlayer メソッドと DisposeVideoRecorder メソッドでは、それぞれのオブジェクトの Stop メソッドを呼び出し (MediaElement のソースを Null に設定して) ストリームを停止しています。CaptureSource オブジェクトと MediaElement オブジェクトは実際には IDisposable インターフェイスを実装しません。
ここで「カメラのグレースケールのサンプル」では 2 つのビデオが同時に実行されているように思えるかもしれません。しかし、実際のアプリには PhotoCamera オブジェクトから VideoBrush コントロールへのストリームしかありませんでした。グレースケールの "ビデオ" は、カメラ プレビュー バッファーから個別に操作され、高速に再描画されるビットマップにすぎません。
まとめ
Windows Phone 7.5 で新しくなったカメラ API は、以前のバージョンの OS では不可能であった問題を解決し、新たな方法で楽しむような、新たな種類のアプリの可能性を広げます。今回はこの API のごく少数の側面に触れたにすぎません。完全なリファレンスについては、Windows Phone SDK ドキュメントの「Windows Phone のカメラと写真」(https://msdn.microsoft.com/ja-jp/library/ff402553(v=vs.92).aspx) を参照してください。
Matt Stroshane は、Windows Phone チーム向けに開発者ドキュメントを執筆しています。ほかにも、SQL Server、SQL Azure、Visual Studio など、MSDN ライブラリで特集する製品の記事も執筆しています。執筆活動をしていないときは、シアトルの通りで、次回のマラソン大会に備えてトレーニングしている姿を見かけます。Twitter (twitter.com/mattstroshane、英語) で彼をフォローしてください。
この記事のレビューに協力してくれた技術スタッフの Eric Bennett、Nikhil Deore、Adam Lydick、および Jon Sheller に心より感謝いたします。