Webカメラを使ったアプリを作ろう
では、ロジックコードについて解説しましょう。今回のコードは約90行程度のコードです。タイトル通り100行に収まって一安心というところです。
ロジックコード(MainPage.xaml.vb)
名前空間をインポートします(リスト2)。
リスト2 各種名前空間のインポート
写真、オーディオ録音、ビデオなんどのメディを作成したり、連携させたりするためのクラスを提供するWindows.Media.Capture名前空間を読み込みます。 Imports Windows.Media.Capture ファイル、フォルダーおよびアプリケーションの設定を管理するクラスの含まれる、Windows.Storage名前空間をインポートします。 Imports Windows.Storage メディア形式のプロパティが作成される時に必要となるクラスおよび他のプログラミング要素を提供するクラスの含まれる、Windows.Media.MediaProperties名前空間をインポートします。 Imports Windows.Media.MediaProperties コンテキストメニューおよびメッセージダイアログのサポートを提供するクラスの含まれる、Windows.UI.Popups名前空間をインポートします。 Imports Windows.UI.Popups デバイスを列挙するためのクラスを提供するクラスの含まれる、Windows.Devices.Enumeration名前空間をインポートします。 Imports Windows.Devices.Enumeration コアシステム機能へのアクセスおよびUIのランタイム情報をアプリケーションに提供するクラスの含まれるWindows.UI.Core名前空間をインポートします。 Imports Windows.UI.Core コンテキストメニューおよびメッセージダイアログのサポートを提供するWindows.UI.Popups名前空間をインポートします。 Imports Windows.UI.Popups
ここで皆さんは「どのクラスがどの名前空間に含まれているのかいちいち覚えてなんかいられないよ」と思わないでしょうか? そこはご心配無用です。ちゃんとVS2013ではそこまで考えて、プログラマーの負担を軽減してくれています。図3を見てください。MessageDialogと入力したのですが、波線が引かれてエラーになっています。波線の個所に赤の短いアンダーラインが見えるはずです。そこにマウスカーソルを持っていくと[赤丸に×]のアイコンが表示されます。この[赤丸に×]のアイコンの横の[▼]をクリックすると図3のようなメッセージが表示され、このクラスを使うためには●●●●の名前空間が必要なのでインポートしますと表示され、クリックすると、その名前空間が自動的に追加されます。他のクラスに関しても、不足している名前空間をちゃんと表示して取り込んでくれます。
では、次にメンバー変数を定義しておきましょう(リスト3)。
リスト3 メンバー変数の定義
Public NotInheritable Class MainPage Inherits Page インデックスによってアクセスできる要素の読み取り専用コレクションを表すクラスのIReadOnlyList(Of IStorageFile)クラス型のメンバー変数myPictureFilesを宣言します。 Private myPictureFiles As IReadOnlyList(Of IStorageFile) Webカメラなどのキャプチャデバイスからフォト、オーディオ、ビデオをキャプチャする機能を提供するMediaCaptureクラス型のメンバー変数myMediaCaptureを宣言します。 Private myMediaCapture As MediaCapture コレクションメンバー変数myPictureFilesの中のインデックス番号を指定するInteger型のメンバー変数Indexを宣言します。 Private Index As Integer BitmapImageクラス型のメンバー変数saveBmpを宣言します。 Private saveBmp As BitmapImage DeviceInfomationオブジェクトのコレクションを表すDeviceInformationCollectionクラス型のメンバー変数myCameraを宣言します。 Private myCamera As DeviceInformationCollection 保存する画像ファイル名を格納する文字列型のメンバー変数saveImageFileNamewを宣言します。 Private saveImageFileName As String ファイルを表すクラスであるStorageFileクラス型のメンバー変数myFileを宣言します。 Private myFile As StorageFile
メンバー変数の宣言ができたところで、第1回目で解説した方法でリスト4のコードを生成します。ページがアクティブになった時のコードです。
AddHandlerステートメントで、現在アクティブになって入るウインドウのVisibleプロパティの値が変更された時に、Current_Changedイベントハンドラを追加します。実装されているカメラのデバイスを取得してComboBoxに表示するDataShowタスクを実行します。非同期処理で行なわれるためメソッドの先頭にAsyncを付けます。
リスト4 ページがアクティブになった時の処理
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) AddHandler Window.Current.VisibilityChanged, AddressOf Current_Changed Await DataShow() MyBase.OnNavigatedTo(e) End Sub
次は、ページがアクティブになった時に実行されるCurrent_Changedイベントの処理です(リスト5)。
ウインドウが表示されている場合は、CameraRestartタスクを実行します。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
リスト5 現在アクティブになって入るウインドウのVisibleプロパティの値が変更された時の処理
Private Async Sub Current_Changed(sender As Object, e As VisibilityChangedEventArgs) If e.Visible = True Then Await CameraRestart() End If End Sub
リスト5でウインドウが表示されている場合と書いていましたが、そのウインドウが表示されているときの処理がリスト6です。
新しいMediaCaptureのインスタンスを作成します。MediaCaptureオブジェクトの初期化設定を含む、新しいMediaCaptureInitializationSettingsクラスのインスタンスを作成し、settings変数で参照します。カメラのデバイス情報を持っている、myCameraから、非表示となっているコンボボックスのインデックスが0の値のデバイスIDを表す文字列を取得して、VideoDeviceIdプロパティに指定します。この場合は、例えばタブレットPCでリアカメラやフロントカメラが実装されていても、常にフロントカメラを選択して、その画像を表示します。InitializeAsyncメソッドで、MediaCaptureオブジェクトをsettingオブジェクトで初期化します。CaptureElementのSourceプロパティにmyMediaCaptureオブジェクトを指定します。StartPreviewAsyncメソッドでプレビューを開始します。
※注意:この処理を行っていないと、カメラがバックグラウンドに回って復帰した時に、カメラがハングアップしてしまうので、この処理は必ず必要です。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
リスト6 ウインドウが表示されているときの処理
Private Async Function CameraRestart() As Task myMediaCapture = New MediaCapture Try Dim settings = New MediaCaptureInitializationSettings() settings.VideoDeviceId = myCamera(0).Id Await myMediaCapture.InitializeAsync(settings) CaptureElement1.Source = myMediaCapture Await myMediaCapture.StartPreviewAsync Catch Exit Function End Try End Function
次は、非表示となっているComboBoxにPCが実装しているデバイスの一覧を表示する処理です(リスト7)。
ピクチャライブラリにアクセスし、CreateFolderAsyncメソッドで「CameraSample」というサブフォルダーを作成します。CreationCollisionOption.OpenIfExistsを指定することで、既にフォルダーが存在する場合は、そのフォルダー名を返してくれます。フォルダーが存在しない場合は新規に作成してくれます。CreationCollisionOption列挙体については下記URLを参照してください。
http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.creationcollisionoption
FindAllAsyncメソッドで全てのビデオキャプチャデバイスを列挙して、DeviceInfomationオブジェクトのコレクションである、myCameraコレクション変数で参照します。
Webカメラが実装されている場合は、コレクション変数myCameraが格納しているデバイスの個数分、繰り返し変数iで反復処理を行います。DeviceInfomationの列挙体である、変数cameraInfoで、コレクション変数myCameraが格納しているデバイスを参照します。cameraComboBoxにAddメソッドで取得したデバイス名を追加していきます。フロントカメラやリアカメラを実装しているタブレットPCでは、二つのデバイス名が追加されます。最初のデバイスを選択します(リアとフロントカメラがある場合フロントカメラを表示します)。新しいMediaCaptureのインスタンスを作成します。InitializeAsyncメソッドで、MediaCaptureオブジェクトを初期化します。CaptureElementのSourceプロパティにmyMediaCaptureオブジェクトを指定します。StartPreviewAsyncメソッドでプレビューを開始します。これでフロントカメラの画像が表示されます。
エラーが発生した場合はErrorShowタスクを実行します。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
リスト7 実装されているデバイスの一覧を取得して、非表示になっているComboBoxに表示する処理。非表示であるためユーザーからは見えません。
Private Async Function DataShow() As Task Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim SubFolder = Await myFolder.CreateFolderAsync("CameraSample", CreationCollisionOption.OpenIfExists) Dim myPictureFile = Await SubFolder.GetFilesAsync Try cameraComboBox.Items.Clear() myCamera = Await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture) For i As Integer = 0 To myCamera.Count - 1 Dim cameraInfo = myCamera(i) cameraComboBox.Items.Add(cameraInfo.Name) Next cameraComboBox.SelectedIndex = 0 myMediaCapture = New MediaCapture Await myMediaCapture.InitializeAsync() CaptureElement1.Source = myMediaCapture Await myMediaCapture.StartPreviewAsync Catch ErrorShow() End Try End Function
PCにWebカメラ等のデバイスが実装されていない場合はエラーが発生します。その場合のエラー処理です(リスト8)。
リスト8 Webカメラが実装されていない場合のエラー処理
Private Async Sub ErrorShow() Dim message As New MessageDialog("カメラが装備されておりません。") Await message.ShowAsync shutterButton.IsEnabled = False Exit Sub End Sub
次に、[写真を撮る]アイコンがタップされた時の処理です。このアイコンがタップされると同時にWebカメラからの画像も保存されます(リスト9)。保存される場所はピクチャライブラリ内のCameraSampleというサブフォルダーに保存されます。削除機能はつけていませんので、不要になった画像はこのフォルダーに入って手動で削除して下さい。