MediaCapture を使った基本的な写真、ビデオ、およびオーディオのキャプチャ

この記事では、MediaCapture クラスを使用して写真やビデオをキャプチャする最も簡単な方法を示します。 MediaCapture クラスは、キャプチャ パイプラインに対する低レベルの制御を提供し、高度なキャプチャ シナリオを実現する、堅牢な一連の API を公開しますが、この記事では基本的なメディア キャプチャをアプリにすばやく簡単に追加できるようにすることを目的としています。 MediaCapture が提供する機能について詳しくは、「カメラ」をご覧ください。

写真やビデオをキャプチャするだけで、他のメディア キャプチャ機能を追加しない場合や、独自のカメラ UI を作成する必要がない場合は、CameraCaptureUI クラスを使用することができます。このクラスによって、単に Windows の組み込みカメラ アプリを起動し、キャプチャされた写真やビデオのファイルを受信することができます。 詳しくは、「Windows の組み込みカメラ UI を使った写真とビデオのキャプチャ」をご覧ください。

この記事のコードは、カメラのスターター キットのサンプルを基にしています。 このサンプルをダウンロードし、該当するコンテキストで使用されているコードを確認することも、サンプルを独自のアプリの開始点として使用することもできます。

アプリ マニフェストに機能宣言を追加する

アプリからデバイスのカメラにアクセスするには、アプリでデバイス機能 (webcammicrophone) の使用を宣言する必要があります。 キャプチャした写真とビデオをユーザーの画像ライブラリまたはビデオ ライブラリに保存する場合は、picturesLibrary 機能と videosLibrary 機能も宣言する必要があります。

アプリ マニフェストに機能を追加するには

  1. Microsoft Visual Studio のソリューション エクスプローラーで、package.appxmanifest 項目をダブルクリックしてアプリケーション マニフェストのデザイナーを開きます。
  2. [機能] タブを選択します。
  3. [Web カメラ] のボックスと [マイク] のボックスをオンにします。
  4. 画像ライブラリとビデオ ライブラリにアクセスするには、[画像ライブラリ] のボックスと[ビデオ ライブラリ] のボックスをオンにします。

MediaCapture オブジェクトを初期化する

この記事で説明されているすべてのキャプチャ メソッドでは、最初の手順として、コンストラクターを呼び出した後、InitializeAsync を呼び出すことによって、MediaCapture オブジェクトを初期化する必要があります。 MediaCapture オブジェクトはアプリで複数の場所からアクセスされるため、このオブジェクトを保持するためのクラス変数を宣言します。 キャプチャ操作に失敗した場合に通知されるように、MediaCapture オブジェクトの Failed イベントのハンドラーを実装します。

MediaCapture mediaCapture;
bool isPreviewing;
mediaCapture = new MediaCapture();
await mediaCapture.InitializeAsync();
mediaCapture.Failed += MediaCapture_Failed;

カメラ プレビューの設定

MediaCapture を使用して写真、ビデオ、オーディオをキャプチャする場合、カメラのプレビューを表示しないこともできますが、通常は、ユーザーがキャプチャされる内容を確認できるようにプレビュー ストリームを表示する必要があります。 また、一部の MediaCapture 機能では、オートフォーカス、自動露出、自動ホワイト バランスなど、有効にする前にプレビュー ストリームを実行する必要があります。 カメラ プレビューを設定する方法については、「カメラ プレビューの表示」をご覧ください。

SoftwareBitmap への写真のキャプチャ

SoftwareBitmap クラスは、複数の機能間で共通の画像表現を提供するために、Windows 10 で導入されました。 写真をキャプチャした後、ファイルにキャプチャするのではなく、XAML で表示するなど、キャプチャしたイメージをアプリですぐに使用する場合は、SoftwareBitmap にキャプチャする必要があります。 後で画像をディスクに保存するオプションもあります。

MediaCapture オブジェクトを初期化した後、LowLagPhotoCapture クラスを使用して、写真を SoftwareBitmap にキャプチャできます。 このクラスのインスタンスを取得するには、PrepareLowLagPhotoCaptureAsync を呼び出して、必要な画像形式を指定する ImageEncodingPropertiesオブジェクトを渡します。 CreateUncompressed は、指定されたピクセル形式で非圧縮エンコードを作成します。 写真をキャプチャするには、CaptureAsync を呼び出します。これは、CapturedPhoto オブジェクトを返します。 SoftwareBitmap を取得するには、Frame プロパティにアクセスし、次に SoftwareBitmap プロパティにアクセスします。

必要に応じて、CaptureAsync を繰り返し呼び出して、複数の写真をキャプチャできます。 キャプチャが完了したら、FinishAsync を呼び出して LowLagPhotoCapture セッションをシャットダウンし、関連するリソースを解放します。 FinishAsync を呼び出した後、再び写真のキャプチャを開始するには、もう一度 PrepareLowLagPhotoCaptureAsync を呼び出して、キャプチャ セッションを再初期化してから、CaptureAsync を呼び出す必要があります。

// Prepare and capture photo
var lowLagCapture = await mediaCapture.PrepareLowLagPhotoCaptureAsync(ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Bgra8));

var capturedPhoto = await lowLagCapture.CaptureAsync();
var softwareBitmap = capturedPhoto.Frame.SoftwareBitmap;

await lowLagCapture.FinishAsync();

Windows バージョン 1803 以降では、CaptureAsync から返された CapturedFrame クラスの BitmapProperties プロパティにアクセスして、キャプチャした写真に関するメタデータを取得できます。 このデータを BitmapEncoder に渡すと、メタデータをファイルに保存できます。 以前は、圧縮されていない画像形式について、このデータにアクセスできませんでした。 ControlValues プロパティにアクセスすると、キャプチャした写真の露出やホワイト バランスなどのコントロールの値を記述した CapturedFrameControlValues オブジェクトも取得できます。

BitmapEncoder の使い方および SoftwareBitmap オブジェクトの操作 (XAML ページに表示する方法など) について詳しくは、「ビットマップ画像の作成、編集、保存」をご覧ください。

キャプチャ デバイス コントロールの値の設定について詳しくは、「写真とビデオのキャプチャのためのキャプチャ デバイス コントロール」をご覧ください。

Windows 10 バージョン 1803 以降では、MediaCapture によって返された CapturedFrameBitmapProperties プロパティにアクセスして、圧縮されていない形式でキャプチャした写真に関する EXIF 情報などのメタデータを取得できます。 以前のリリースでは、このデータにアクセスできるのは、圧縮ファイル形式にキャプチャされた写真のヘッダーでのみでした。 このデータを BitmapEncoder に渡すと、画像ファイルを手動で書き込むことができます。 ビットマップのエンコードについて詳しくは、「ビットマップ画像の作成、編集、保存」をご覧ください。 ControlValues プロパティにアクセスすると、画像をキャプチャしたときに使用された露出やフラッシュ設定などのフレーム コントロールの値にもアクセスできます。 詳しくは、「写真とビデオのキャプチャのためのキャプチャ デバイス コントロール」をご覧ください。

ファイルへの写真のキャプチャ

一般的な写真アプリでは、キャプチャした写真をディスクやクラウド ストレージに保存し、写真の向きなどのメタデータをファイルに追加する必要があります。 次の例では、ファイルに写真をキャプチャする方法を示します。 後で画像ファイルから SoftwareBitmap を作成するオプションもあります。

この例で示されている方法では、写真をインメモリ ストリームにキャプチャし、写真をストリームからディスク上のファイルにコード変換します。 この例では、GetLibraryAsync を使ってユーザーのピクチャ ライブラリを取得し、次に SaveFolder プロパティを使って既定の保存フォルダーを取得します。 このフォルダーにアクセスするには、必ずピクチャ ライブラリ機能をアプリ マニフェストに追加してください。 CreateFileAsync は、写真の保存先となる新しい StorageFile を作成します。

InMemoryRandomAccessStream を作成し、CapturePhotoToStreamAsync を呼び出して、ストリームと、使用する画像形式を指定する ImageEncodingProperties オブジェクトを渡して、写真をストリームにキャプチャします。 自分でオブジェクトを初期化することによって、カスタム エンコード プロパティを作成することもできますが、このクラスには、ImageEncodingProperties.CreateJpeg など、一般的なエンコード形式用の静的メソッドが用意されています。 次に、OpenAsync を呼び出すことにより、出力ファイルへのファイル ストリームを作成します。 BitmapDecoder を作成して、インメモリ ストリームから画像をデコードします。次に、BitmapEncoder を作成して、CreateForTranscodingAsync を呼び出すことによって画像をファイルにエンコードします。

必要に応じて、BitmapPropertySet オブジェクトを作成し、イメージ エンコーダーで SetPropertiesAsync を呼び出して、画像ファイルに写真に関するメタデータを含めることができます。 エンコード プロパティについて詳しくは、「画像のメタデータ」をご覧ください。 デバイスの向きを正しく処理することは、ほとんどの写真アプリに不可欠です。 詳しくは、「MediaCapture を使ってデバイスの向きを処理する」をご覧ください。

最後に、エンコーダー オブジェクトで FlushAsync を呼び出して、写真をインメモリ ストリームからファイルにコード変換します。

var myPictures = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Pictures);
StorageFile file = await myPictures.SaveFolder.CreateFileAsync("photo.jpg", CreationCollisionOption.GenerateUniqueName);

using (var captureStream = new InMemoryRandomAccessStream())
{
    await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream);

    using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
    {
        var decoder = await BitmapDecoder.CreateAsync(captureStream);
        var encoder = await BitmapEncoder.CreateForTranscodingAsync(fileStream, decoder);

        var properties = new BitmapPropertySet {
            { "System.Photo.Orientation", new BitmapTypedValue(PhotoOrientation.Normal, PropertyType.UInt16) }
        };
        await encoder.BitmapProperties.SetPropertiesAsync(properties);

        await encoder.FlushAsync();
    }
}

ファイルとフォルダーの操作について詳しくは、「ファイル、フォルダー、およびライブラリ」をご覧ください。

ビデオをキャプチャする

アプリにビデオ キャプチャ機能をすばやく追加するには、LowLagMediaRecording クラスを使用します。 最初に、オブジェクト用のクラス変数を宣言します。

LowLagMediaRecording _mediaRecording;

次に、ビデオの保存先となる StorageFile オブジェクトを作成します。 この例に示すように、ユーザーのビデオ ライブラリに保存するには、ビデオ ライブラリ 機能をアプリ マニフェストに追加する必要があることに注意してください。 PrepareLowLagRecordToStorageFileAsync を呼び出して、ストレージ ファイルと、ビデオのエンコードを指定する MediaEncodingProfile オブジェクトを渡すことにより、メディアの記録を初期化します。 このクラスには、CreateMp4 など、一般的なエンコード プロファイルを作成するための静的メソッドが用意されています。

最後に、StartAsync を呼び出してビデオのキャプチャを開始します。

var myVideos = await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Videos);
StorageFile file = await myVideos.SaveFolder.CreateFileAsync("video.mp4", CreationCollisionOption.GenerateUniqueName);
_mediaRecording = await mediaCapture.PrepareLowLagRecordToStorageFileAsync(
        MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto), file);
await _mediaRecording.StartAsync();

ビデオの録画を停止するには、StopAsync を呼び出します。

await _mediaRecording.StopAsync();

StartAsyncStopAsync の呼び出しを続行して、他のビデオをキャプチャすることもできます。 ビデオのキャプチャが完了したら、FinishAsync を呼び出して、キャプチャ セッションを破棄し、関連付けられているリソースをクリーンアップします。 この呼び出しの後は、再び PrepareLowLagRecordToStorageFileAsync を呼び出してキャプチャ セッションを再初期化してから、StartAsync を呼び出す必要があります。

await _mediaRecording.FinishAsync();

ビデオをキャプチャするときに、MediaCapture オブジェクトの RecordLimitationExceeded イベントのハンドラーを登録する必要があります。このイベントは、1 つの録画の上限 (現在は 3 時間) を超える場合に、オペレーティング システムによって生成されます。 このイベントのハンドラーで、StopAsync を呼び出して録画を完了する必要があります。

mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;
private async void MediaCapture_RecordLimitationExceeded(MediaCapture sender)
{
    await _mediaRecording.StopAsync();
    System.Diagnostics.Debug.WriteLine("Record limitation exceeded.");
}

キャプチャしたビデオ ファイルの再生と編集

ビデオをファイルにキャプチャした後は、アプリの UI の中でファイルを読み込んで再生できます。 この操作には、MediaPlayerElement XAML コントロールと、それに関連付けられている MediaPlayer を使用します。 XAML ページでメディアを再生する方法について詳しくは、「MediaPlayer を使ったオーディオとビデオの再生」をご覧ください。

CreateFromFileAsync を呼び出すと、ビデオ ファイルから MediaClip オブジェクトを作成することもできます。 MediaComposition は、MediaClip オブジェクトのシーケンスの配置、ビデオの長さのトリミング、レイヤーの作成、BGM の追加、ビデオ効果の適用などの基本的なビデオ編集機能を備えています。 メディア コンポジションの操作について詳しくは、「メディア コンポジションと編集」をご覧ください。

ビデオ録画の一時停止と再開

PauseAsyncResumeAsync を呼び出すことによって、ビデオの録画を一時停止し、別の出力ファイルを作成せずに録画を再開することができます

await _mediaRecording.PauseAsync(Windows.Media.Devices.MediaCapturePauseBehavior.ReleaseHardwareResources);
await _mediaRecording.ResumeAsync();

Windows 10 バージョン 1607 以降では、ビデオの録画を一時停止し、録画を一時停止する前にキャプチャされた最後のフレームを受信できます。 このフレームをカメラ プレビューにオーバーレイすることにより、ユーザーは、録画を再開する前に、一時停止したフレームとカメラの位置合わせをすることができます。 PauseWithResultAsync を呼び出すと、MediaCapturePauseResult オブジェクトが返されます。 LastFrame プロパティは、最後のフレームを表す VideoFrame オブジェクトです。 XAML でこのフレームを表示するには、ビデオ フレームの SoftwareBitmap 表現を取得します。 現時点では、BGRA8 形式、プリマルチプライ済みまたは空のアルファ チャネルの画像のみがサポートされているため、適切な形式を取得するには、必要に応じて、Convert を呼び出します。 新しい SoftwareBitmapSource オブジェクトを作成し、SetBitmapAsync を呼び出して初期化します。 最後に、XAML の Image コントロールの Source プロパティを設定して画像を表示します。 この方法が機能するには、画像が CaptureElement コントロールと整列されており、不透明度の値が 1 未満である必要があります。 UI は UI スレッドでのみ変更できるため、RunAsync 内でこの呼び出しを行ってください。

PauseWithResultAsync は、録画された合計時間を追跡する必要がある場合に、前のセグメントで録画されたビデオの持続時間も返します。

MediaCapturePauseResult result = 
    await _mediaRecording.PauseWithResultAsync(Windows.Media.Devices.MediaCapturePauseBehavior.RetainHardwareResources);

var pausedFrame = result.LastFrame.SoftwareBitmap;
if(pausedFrame.BitmapPixelFormat != BitmapPixelFormat.Bgra8 || pausedFrame.BitmapAlphaMode != BitmapAlphaMode.Ignore)
{
    pausedFrame = SoftwareBitmap.Convert(pausedFrame, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore);
}

var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(pausedFrame);

await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    PauseImage.Source = source;
    PauseImage.Visibility = Visibility.Visible;
});

_totalRecordedTime += result.RecordDuration;

録画を再開するときは、画像のソースを null に設定し、非表示にすることができます。

await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    PauseImage.Source = null;
    PauseImage.Visibility = Visibility.Collapsed;
});

await _mediaRecording.ResumeAsync();

StopWithResultAsync を呼び出すことによってビデオを停止したときに、結果のフレームを取得することもできます。

オーディオのキャプチャ

前に示したビデオのキャプチャと同じ手法を用いて、アプリにオーディオ キャプチャ機能を簡単に追加することができます。 次の例では、アプリケーション データ フォルダーに StorageFile を作成します。 PrepareLowLagRecordToStorageFileAsync を呼び出して、ファイルと、MediaEncodingProfile を渡すことによって、キャプチャ セッションを初期化します。この例では、MediaEncodingProfile は静的メソッド CreateMp3 によって生成されます。 録音を開始するには、StartAsync を呼び出します。

mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;

var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file = await localFolder.CreateFileAsync("audio.mp3", CreationCollisionOption.GenerateUniqueName);
_mediaRecording = await mediaCapture.PrepareLowLagRecordToStorageFileAsync(
        MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High), file);
await _mediaRecording.StartAsync();

StopAsync を呼び出して、オーディオの録音を停止します。

await _mediaRecording.StopAsync();

StartAsyncStopAsync を複数回呼び出して、複数のオーディオ ファイルを録音します。 オーディオのキャプチャが完了したら、FinishAsync を呼び出して、キャプチャ セッションを破棄し、関連付けられているリソースをクリーンアップします。 この呼び出しの後は、再び PrepareLowLagRecordToStorageFileAsync を呼び出してキャプチャ セッションを再初期化してから、StartAsync を呼び出す必要があります。

await _mediaRecording.FinishAsync();

システムによるオーディオ レベルの変更を検出して対応する

Windows 10、バージョン 1803 以降では、アプリのオーディオ キャプチャやオーディオ レンダリング ストリームのオーディオ レベルが、システムによって低下した場合やミュートされた場合に、アプリがそれを検出できます。 たとえば、アプリがバックグラウンドに移動すると、アプリのストリームがシステムによってミュートされることがあります。 AudioStateMonitor クラスを使用すると、オーディオ ストリームの音量がシステムによって変更されたときに、イベントを受け取るように登録できます。 オーディオ キャプチャ ストリーム監視用の AudioStateMonitor のインスタンスを取得するには、CreateForCaptureMonitoring を呼び出します。 オーディオ レンダリング ストリーム監視用のインスタンスを取得するには、CreateForRenderMonitoring を呼び出します。 対応するストリーム カテゴリのオーディオがシステムによって変更されたときに通知を受け取るには、各モニターの SoundLevelChanged イベントのハンドラーを登録します。

// Namespaces for monitoring audio state
using Windows.Media;
using Windows.Media.Audio;
AudioStateMonitor captureAudioStateMonitor;
AudioStateMonitor renderAudioStateMonitor;
captureAudioStateMonitor = AudioStateMonitor.CreateForCaptureMonitoring();
captureAudioStateMonitor.SoundLevelChanged += CaptureAudioStateMonitor_SoundLevelChanged; ;

renderAudioStateMonitor = AudioStateMonitor.CreateForRenderMonitoring();
renderAudioStateMonitor.SoundLevelChanged += RenderAudioStateMonitor_SoundLevelChanged; ;

キャプチャ ストリームの SoundLevelChanged ハンドラーで、AudioStateMonitor センダーの SoundLevelプロパティを確認すると、新しいサウンド レベルを判定できます。 キャプチャー ストリームは、システムによって下げないで (つまり "ダッキング" しないで) ください。 ミュートとフル音量の間の切り替えのみを行うことができます。 オーディオ ストリームがミュートになっている場合は、実行中のキャプチャを停止できます。 オーディオ ストリームがフル音量に戻った場合は、もう一度キャプチャを開始できます。 次の例では、複数のブール型のクラス変数を使用して、オーディオが現在キャプチャ中か、それともオーディオ状態の変更のためにキャプチャが停止しているかを追跡しています。 これらの変数を使用すると、オーディオ キャプチャをプログラムによって停止または開始する適切な時期を判断できます。

bool isCapturingAudio = false;
bool capturingStoppedForAudioState = false;
private void CaptureAudioStateMonitor_SoundLevelChanged(AudioStateMonitor sender, object args)
{
    switch (sender.SoundLevel)
    {
        case SoundLevel.Full:
            if(capturingStoppedForAudioState)
            {
                StartAudioCapture();
                capturingStoppedForAudioState = false;
            }  
            break;
        case SoundLevel.Muted:
            if(isCapturingAudio)
            {
                StopAudioCapture();
                capturingStoppedForAudioState = true;
            }
            break;
        case SoundLevel.Low:
            // This should never happen for capture
            Debug.WriteLine("Unexpected audio state.");
            break;
    }
}

次のコード例では、オーディオ レンダリング用の SoundLevelChanged ハンドラーの実装を示しています。 アプリのシナリオや、再生しているコンテンツの種類に応じて、サウンド レベルがダッキングされている間、オーディオ再生を一時停止します。 メディア再生のサウンド レベルの変更を処理する方法について詳しくは、「MediaPlayer を使ったオーディオとビデオの再生」をご覧ください。

private void RenderAudioStateMonitor_SoundLevelChanged(AudioStateMonitor sender, object args)
{
    if ((sender.SoundLevel == SoundLevel.Full) ||
  (sender.SoundLevel == SoundLevel.Low && !isPodcast))
    {
        mediaPlayer.Play();
    }
    else if ((sender.SoundLevel == SoundLevel.Muted) ||
         (sender.SoundLevel == SoundLevel.Low && isPodcast))
    {
        // Pause playback if we’re muted or if we’re playing a podcast and are ducked
        mediaPlayer.Pause();
    }
}