Webカメラで撮影した画像に音声コメントを付けて保存・再生するには2カ月で160本作った還暦開発者が送る10のアプリ開発ノウハウ(6)(3/4 ページ)

» 2013年11月20日 18時00分 公開
[薬師寺国安,PROJECT KySS]

メイン画面のロジックコード(MainWindow.xaml.vb)

 次に、[ソリューション・エクスプローラー]内のMainWindow.xamlを展開して表示される、「MainWindow.xaml.vb」のコードを記述する。

 ここではコードが長くなるため、名前空間の読み込み、メンバー変数の宣言は一部省略している。全てのコードは、サンプルをダウンロードして「MainWindow.xaml.vb」ファイルで確認してほしい。

MP4の音声ファイルを作成し、実装されているカメラのデバイスを取得してComboBoxに表示する処理(DataShowタスク処理)

    Private Async Function DataShow() As Task
        AppBar1.Visibility = Xaml.Visibility.Visible
        Try
            myRecordMediaCapture = New MediaCapture
            myCaptureInitSetting = New MediaCaptureInitializationSettings
            myCaptureInitSetting.AudioDeviceId = String.Empty
            myCaptureInitSetting.StreamingCaptureMode = StreamingCaptureMode.Audio
            Await myRecordMediaCapture.InitializeAsync(myCaptureInitSetting)
            myProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto)
            Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
            Dim mySubFolder = Await myStorageFolder.CreateFolderAsync("ImageVoiceRecord", CreationCollisionOption.OpenIfExists)
            Dim myFile = Await mySubFolder.GetFilesAsync()
            If myFile.Count > 0 Then
                ichiranButton.IsEnabled = True
            Else
                PhotoArea.Children.Clear()
                ichiranButton.IsEnabled = False
            End If
            For Each result In myFile
                If result.Path.Contains("Thumbs.db") Then
                    Await result.DeleteAsync
                End If
            Next
            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
            If cameraNo < 0 Then
                cameraComboBox.SelectedIndex = 0
                shutterButton.IsEnabled = True
            Else
                cameraComboBox.SelectedIndex = cameraNo
                shutterButton.IsEnabled = True
            End If
        Catch
            ErrorShow()
        End Try
    End Function

 以降、上記コードの中身を詳細に見ていこう。

 まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。

 次に、新しいMediaCaptureのインスタンスを作成する。

 MediaCaptureオブジェクトの初期化設定を含む、新しいMediaCaptureInitializationSettingsクラスのインスタンスを作成し、myCaptureInitSettingメンバー変数で参照する。

 マイクのDeviceInformation.Idを取得する、AudioDeviceIdをString.Emptyで初期化しておく。

 ストリーミングモードを設定する、StreamingCaptureModeプロパティに、オーディオのみのキャプチャを指定する。

 MediaCaptureオブジェクトを初期化する、InitializeAsyncメソッドで、オーディオのみのキャプチャを初期化する。

 CreateMp4メソッドで、エンコーディングプロファイルを作成する。エンコーディング形式をAutoに指定する。

 ピクチャライブラリーのImageVoiceRecordサブフォルダーにアクセスする。このサブフォルダー内のファイルをGetFilesAsyncメソッドで取得する。ファイルが存在すれば、「一覧」アイコンの使用を可能にし、そうでない場合は、使用不可とする。

 ImageVoiceRecordサブフォルダー内にThumbs.dbが作成されていたら、これを削除する。このファイルは自動的に作成されるファイルだ。

 cameraComboBox内を一度クリアする。

 FindAllAsyncメソッドで全てのビデオキャプチャデバイスを列挙して、DeviceInfomationオブジェクトのコレクションである、myCameraコレクション変数で参照する。

 Webカメラが実装されている場合は、コレクション変数myCameraが格納しているデバイスの個数分、繰り返し変数iで反復処理を行う。DeviceInfomationの列挙体である、変数cameraInfoで、コレクション変数myCameraが格納しているデバイスを参照させる。

 cameraComboBoxにAddメソッドで取得したデバイス名を追加する。フロントカメラやリアカメラを実装しているタブレットPCでは、2つのデバイス名が追加表示されるので、cameraComboBoxのSelectedIndexが0より小さい場合、つまりデバイスが選択されていない場合は、最初のデバイスを選択する。

 「写真を撮る」アイコンの使用を可能にする。選択されている場合は、それを選択状態にする。

 エラーが発生した場合は、ErrorShowメソッドを実行する。

「写真を撮る」アイコンがタップされた時の処理(shutterButton_Clickメソッド処理)

 このアプリの肝となる処理。取った画像に音声を関連付ける。

    Private Async Sub shutterButton_Click(sender As Object, e As RoutedEventArgs) Handles shutterButton.Click
        PhotoArea.Children.Clear()
        MediaElement1.Play()
        Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
        Dim mySubFolder = Await myFolder.CreateFolderAsync("ImageVoiceRecord", CreationCollisionOption.OpenIfExists)
        Dim myFile As StorageFile = Await mySubFolder.CreateFileAsync(DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".png")
        saveImageFileName = DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒") & ".png"
        Dim myImageEncodingProperty As New ImageEncodingProperties
        myImageEncodingProperty.Subtype = "png"
        myImageEncodingProperty.Width = 640
        myImageEncodingProperty.Height = 480
      ‘ このアプリの肝、Webカメラからの画像を保存する
        Await myMediaCapture.CapturePhotoToStorageFileAsync(myImageEncodingProperty, myFile)
        myPictureFiles = Await mySubFolder.GetFilesAsync()
        Index = myPictureFiles.Count
        saveBmp = New BitmapImage
        saveBmp.SetSource(Await myPictureFiles(Index - 1).OpenReadAsync)
        Dim myImage As New Image
        With myImage
            .Width = 640
            .Height = 480
            .Source = saveBmp
        End With
        Dim myStackPanel As New StackPanel
       
        Dim recordButton As New Button
        With recordButton
            .Content = "録音開始"
            .Tag = no.ToString
        End With
        myStackPanel.Children.Add(myImage)
        myStackPanel.Children.Add(recordButton)
        PhotoArea.Children.Add(myStackPanel)
        AddHandler recordButton.Click, Async Sub(delSender As Object, delArgs As RoutedEventArgs)
                   Dim wavFolder = KnownFolders.PicturesLibrary
                   Dim myWavSubFolder = Await wavFolder.CreateFolderAsync("ImageRecordSoundFile", CreationCollisionOption.OpenIfExists)
                   Dim delDbFile = Await myWavSubFolder.GetFilesAsync
                      For Each result In delDbFile
                           If result.Path.Contains("Thumbs.db") Then
                                 Await result.DeleteAsync
                                 End If
                              Next
                                Select Case recordButton.Content.ToString
                                    Case "録音開始"
                                       recordTextBlock.Visibility = Xaml.Visibility.Visible
                                       Dim recordFile = Await myWavSubFolder.CreateFileAsync(Path.GetFileNameWithoutExtension(saveImageFileName) & ".mp4", CreationCollisionOption.OpenIfExists)
                             ‘ このアプリの肝、音声ファイルを保存する。
                                        Dim saveRecordeFile As StorageFile = recordFile
                                        Await myRecordMediaCapture.StartRecordToStorageFileAsync(myProfile, saveRecordeFile)
                                        recordButton.Content = "録音終了"
                                        ProgressRing1.IsActive = True
                                        ProgressRing1.IsEnabled = True
                                        Exit Select
                                  Case "録音終了"
                                        Await myRecordMediaCapture.StopRecordAsync
                                        recordTextBlock.Visibility = Xaml.Visibility.Collapsed
                                        ProgressRing1.IsActive = False
                                        ProgressRing1.IsEnabled = False
                                        PhotoArea.Children.Clear()
                                        Exit Select
                              End Select
                          End Sub
        no += 1
        ichiranButton.IsEnabled = True
    End Sub

 以降、上記コードの中身を詳細に見ていこう。

 まず、非同期処理で行われるためメソッドの先頭にAsyncを追加している。

 次に、Canvas内を一度クリアして、MediaElement1を再生する。つまり、シャッター音が鳴る動作だ。

 ピクチャライブラリーにアクセスし、CreateFolderAsyncメソッドで、ピクチャフォルダー内に「ImageVoiceRecord」というサブフォルダーを作成する。その際、CreationCollisionOption.OpenIfExistsと指定しておくと、同名フォルダーやファイルがある場合は、そのフォルダーやファイル名を返し、ない場合は新規に作成してくれる。

 CreateFileAsyncメソッドで現在の「年月日時間分秒.png」ファイルを作成し、ファイルを表すメンバー変数「myFile」で参照しておく。メンバー変数「saveImageFileName」に、このpngファイル名を格納しておく。

 イメージストリームの書式を表す新しい、ImageEncodingPropertiesクラスのインスタンス、myImageEncodingPropertyオブジェクトを作成する。書式のサブタイプを表すSubtypeプロパティに「png」を指定し、Widthに「640」、Heightに「480」と指定する。

 CapturePhotoToStorageFileAsyncメソッドで、ストレージファイルにフォトをキャプチャする。書式は以下の通りだ。

CapturePhotoToStorageFileAsync(ImageEncodingProperties,IStorageFile)

 コレクション変数myPictureFilesにGetFilesAsyncメソッドで、「ImageVoiceRecord」フォルダー内の画像ファイルを取得して格納する。

 コレクション変数myPictureFilesが格納しているファイルの個数を、Countプロパティで取得して、メンバー変数Indexに格納する。

 新しいBitmapImageクラスのインスタンスsaveBmpを作成する。SetSourceメソッドに、「Await myPictureFiles(Index - 1).OpenReadAsync」と指定して、コレクション変数内のIndexに対応するファイルをOpenReadAsyncメソッドで開き、ソースイメージに指定する。

 Indexを-1しているのは、コレクション変数「myPictureFiles」が格納しているファイルのインデックスが0から始まるためだ。

 新しいImageのインスタンスmyImageオブジェクトを作成し、Widthに「640」、Heightに「480」と指定し、SourceプロパティにSaveBmpオブジェクトを指定する。

 StackPanelの新しいインスタンスmyStackPanelオブジェクトを作成する。

 Buttonの新しいインスタンスrecordButtonを作成する。Contentプロパティに「録音開始」と指定し、Tagプロパティに文字列にキャストした、1ずつ増加するメンバー変数「no」の値を指定する。

 myStackPanelオブジェクトに、myImageとrecordButtonオブジェクトを追加する。「PhotopArea」という名前を持つ、CanvasにAddメソッドでmyStackPanelオブジェクトを追加する。画像に、「録音開始」ボタンが付加して表示される。

 AddHandlerステートメントで、recordButtonがクリックされた時のイベントハンドラを追加する。イベントハンドラ内では、以下の処理を行う。

 ピクチャライブラリー内の「ImageRecordSoundFile」フォルダーにアクセスする。このフォルダー内のファイルをGetFilesAsyncメソッドで取得し、delDbFileで参照する。このフォルダー内に「Thumbs.db」というファイルがあれば削除する。

 Select Case文を使って、recordButtonのContentプロパティの値で条件分岐を行う。

 ボタンの文字が「録音開始」だった場合には、以下の処理を行う。「録音中……」という文字を表示する。CreateFileAsyncメソッドで、メンバー変数「saveImageFileName」の格納しているファイル名から、指定したファイル名のパス文字列を、拡張子を付けずに取得する。

 文字列".mp4"と連結してMP4ファイルを作成し、変数recordFileで参照しておく。ファイルを表す変数「saveRecordFile」をrecordFileで初期化しておく。

 StartRecordToStorageFileAsyncメソッドで、ストレージファイルへ非同期的な記録を開始する。書式は下記の通りだ。

MediaCapture. StartRecordToStorageFileAsync(記録用のエンコーディング プロファイル,イメージを保存するストレージ ファイル)

 recordButtonの表面の文字を「録音終了」に変更し、ProgressRing1を動作させる。録音をしている間はプログレスリングが回る。

 ボタンの文字が「録音終了」で、このボタンをタップした場合は以下の処理を行う。StopRecordAsyncメソッドでは、「録音中……」の文字を消し、ProgressRing1の動作を停止する。

 「PhotoArea」という名前のCanvas内をクリアする。表示されていた画像も消えてしまう。

 メンバー変数「no」の値を1ずつ増加し、「一覧」アイコンの使用を可能にする。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。