ビデオ キャプチャの効果

このトピックでは、カメラのプレビューおよび録画中のビデオ ストリームに効果を適用する方法と、ビデオ手ブレ補正効果を使用する方法について説明します。

注意

この記事の内容は、写真やビデオの基本的なキャプチャ機能を実装するための手順を紹介した「MediaCapture を使った基本的な写真、ビデオ、およびオーディオのキャプチャ」で取り上げた概念やコードに基づいています。 そちらの記事で基本的なメディア キャプチャのパターンを把握してから、高度なキャプチャ シナリオに進むことをお勧めします。 この記事で紹介しているコードは、MediaCapture のインスタンスが既に作成され、適切に初期化されていることを前提としています。

カメラのビデオ ストリームに対する効果の追加と削除

デバイスのカメラからビデオをプレビューまたはキャプチャするには、「MediaCapture を使った基本的な写真、ビデオ、およびオーディオのキャプチャ」で説明されているように、MediaCapture オブジェクトを使用します。 MediaCapture オブジェクトを初期化した後、プレビュー ストリームやキャプチャ ストリームに 1 つまたは複数のビデオ効果を追加できます。そのためには、AddVideoEffectAsync を呼び出して、追加する効果を表す IVideoEffectDefinition オブジェクトと、カメラのプレビュー ストリームとレコード ストリームのどちらに効果を追加するかを示す MediaStreamType 列挙のメンバーを渡します。

注意

一部のデバイスでは、プレビュー ストリームとキャプチャ ストリームが同じである場合があります。これは、AddVideoEffectAsync を呼び出すときに MediaStreamType.VideoPreview または MediaStreamType.VideoRecord を指定すると、プレビュー ストリームとレコード ストリームの両方に効果が適用されることを意味します。 現在のデバイスでプレビュー ストリームとレコード ストリームが同じであるかどうかを確認するには、MediaCapture オブジェクトの MediaCaptureSettingsVideoDeviceCharacteristic プロパティを調べます。 このプロパティの値が VideoDeviceCharacteristic.AllStreamsIdentical または VideoDeviceCharacteristic.PreviewRecordStreamsIdentical である場合、ストリームは同じであり、一方に対して適用したすべての効果はもう一方にも影響します。

次の例では、カメラのプレビュー ストリームとレコード ストリームの両方に効果を追加します。 この例では、レコード ストリームとプレビュー ストリームが同じかどうかを確認する方法を示します。

if (mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
    mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
{
    // This effect will modify both the preview and the record streams, because they are the same stream.
    myRecordEffect = await mediaCapture.AddVideoEffectAsync(myEffectDefinition, MediaStreamType.VideoRecord);
}
else
{
    myRecordEffect = await mediaCapture.AddVideoEffectAsync(myEffectDefinition, MediaStreamType.VideoRecord);
    myPreviewEffect = await mediaCapture.AddVideoEffectAsync(myEffectDefinition, MediaStreamType.VideoPreview);
}

AddVideoEffectAsync は、追加されたビデオ効果を表す IMediaExtension を実装するオブジェクトを返します。 一部の効果では、PropertySetSetProperties メソッドに渡すことによって、効果の設定を変更できます。

Windows 10 バージョン 1607 では、AddVideoEffectAsync によって返されるオブジェクトを RemoveEffectAsync に渡すことによって、ビデオ パイプラインから効果を削除することもできます。 RemoveEffectAsync は、効果オブジェクトのパラメーターがプレビュー ストリームとレコード ストリームのどちらに追加されたかを自動的に特定するため、呼び出しの際にストリームの種類を指定する必要はありません。

if (myRecordEffect != null)
{
    await mediaCapture.RemoveEffectAsync(myRecordEffect);
}
if(myPreviewEffect != null)
{
    await mediaCapture.RemoveEffectAsync(myPreviewEffect);
}

また、ClearEffectsAsync を呼び出し、すべての効果を削除するストリームを指定して、プレビュー ストリームやキャプチャ ストリームからすべての効果を削除することもできます。

await mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);
await mediaCapture.ClearEffectsAsync(MediaStreamType.VideoRecord);

ビデオ手ブレ補正効果

ビデオ手ブレ補正効果は、キャプチャ デバイスを手で支えることによって生じる動きを極力目立たなくするために、ビデオ ストリームのフレームを操作します。 この手法を適用するとピクセルが上下左右に移動されますが、ビデオ フレームのすぐ外側に何があるのかは、手ブレ補正効果のロジックにはわからないため、手ブレ補正後は元のビデオがわずかにトリミングされた状態となります。 ここで紹介しているユーティリティ関数を使いビデオ エンコードの設定を調整することで、手ブレ補正効果によるトリミングをベストな状態に制御することができます。

デバイス側でサポートされていれば、Optical Image Stabilization (OIS) がキャプチャ デバイスを機械的に操作することでビデオのブレが抑えられるため、ビデオ フレームの端をトリミングする必要はありません。 詳しくは、「ビデオ キャプチャのためのキャプチャ デバイス コントロール」をご覧ください。

ビデオの手ブレ補正を行うようにアプリを設定する

ビデオ手ブレ補正効果を使うためには、基本的なメディア キャプチャに必要な名前空間に加え、次の名前空間が必要となります。

using Windows.Media.Core;
using Windows.Media.MediaProperties;
using Windows.Media.Effects;
using Windows.Media;

VideoStabilizationEffect オブジェクトを格納するためのメンバー変数を宣言します。 効果の実装の一部として、キャプチャしたビデオをエンコードするために使うエンコードのプロパティを変更します。 後で効果が無効にされたときに入出力のエンコード プロパティを復元できるよう、初期状態のバックアップ コピーを格納するための 2 つの変数を宣言します。 最後に、MediaEncodingProfile 型のメンバー変数を宣言します。メンバー変数として宣言しているのは、コードのいたるところからこのオブジェクトにアクセスすることになるためです。

private VideoStabilizationEffect _videoStabilizationEffect;
private VideoEncodingProperties _inputPropertiesBackup;
private VideoEncodingProperties _outputPropertiesBackup;
private MediaEncodingProfile _encodingProfile;

このシナリオでは、後からアクセスできるようメディア エンコード プロファイル オブジェクトをメンバー変数に代入する必要があります。

_encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);

ビデオ手ブレ補正効果を初期化する

MediaCapture オブジェクトの初期化後、VideoStabilizationEffectDefinition オブジェクトの新しいインスタンスを作成します。 効果をビデオ パイプラインに追加し、VideoStabilizationEffect クラスのインスタンスを取得するには、MediaCapture.AddVideoEffectAsync を呼び出します。 MediaStreamType.VideoRecord を指定すると、ビデオ レコード ストリームに効果を適用する、という意味になります。

EnabledChanged イベントのハンドラーを登録し、ヘルパー メソッド SetUpVideoStabilizationRecommendationAsync を呼び出します。詳細については後で説明します。 最後に、Enabled プロパティを true に設定して効果を有効にします。

// Create the effect definition
VideoStabilizationEffectDefinition stabilizerDefinition = new VideoStabilizationEffectDefinition();

// Add the video stabilization effect to media capture
_videoStabilizationEffect =
    (VideoStabilizationEffect)await mediaCapture.AddVideoEffectAsync(stabilizerDefinition, MediaStreamType.VideoRecord);

_videoStabilizationEffect.EnabledChanged += VideoStabilizationEffect_EnabledChanged;

await SetUpVideoStabilizationRecommendationAsync();

_videoStabilizationEffect.Enabled = true;

先ほども触れましたが、ビデオ手ブレ補正効果に使われている手法では必然的に、補正後、わずかですが元のビデオがトリミングされます。 これは手ブレ補正効果の制限です。ビデオ エンコード プロパティを調整して、できるだけ適切に対処するために、以下のヘルパー関数をコードの中で定義してください。 このステップはビデオ手ブレ補正効果を使ううえで必須ではありませんが、これを省略した場合、最終的に得られるビデオが若干アップスケーリングされるために、ビジュアルの再現性がわずかに低下します。

VideoStabilizationEffect のインスタンスの GetRecommendedStreamConfiguration を呼び出します。このメソッドは、ビデオ補正効果に対して現在の入力ストリームのエンコード プロパティを伝える VideoDeviceController オブジェクトと、現在の出力エンコード プロパティを伝える MediaEncodingProfile とを引数として受け取ります。 このメソッドの戻り値である VideoStreamConfiguration オブジェクトには、入力ストリームと出力ストリームの最新の推奨エンコード プロパティが格納されます。

効果のトリミング適用後に失われる解像度を最小限にするために、入力の推奨エンコード プロパティは、デバイスによってサポートされる範囲で、指定した初期設定よりも解像度が高くなります。

新しいエンコード プロパティを設定するには、VideoDeviceController.SetMediaStreamPropertiesAsync を呼び出します。 効果を無効にしたときに設定を元に戻すことができるよう、新しいプロパティを設定する前にメンバー変数を使って初期エンコード プロパティを保存します。

ビデオ手ブレ補正効果で出力ビデオをトリミングする必要がある場合、出力の推奨エンコード プロパティは、トリミングされたビデオのサイズになります。 つまり出力の解像度は、トリミングされたビデオのサイズと一致します。 推奨される出力プロパティを使わなかった場合、最初の出力サイズに合わせてビデオがアップスケーリングされるため、ビジュアルの再現性が低下します。

MediaEncodingProfile オブジェクトの Video プロパティを設定します。 効果を無効にしたときに設定を元に戻すことができるよう、新しいプロパティを設定する前にメンバー変数を使って初期エンコード プロパティを保存します。

private async Task SetUpVideoStabilizationRecommendationAsync()
{

    // Get the recommendation from the effect based on our current input and output configuration
    var recommendation = _videoStabilizationEffect.GetRecommendedStreamConfiguration(mediaCapture.VideoDeviceController, _encodingProfile.Video);

    // Handle the recommendation for the input into the effect, which can contain a larger resolution than currently configured, so cropping is minimized
    if (recommendation.InputProperties != null)
    {
        // Back up the current input properties from before VS was activated
        _inputPropertiesBackup = mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoRecord) as VideoEncodingProperties;

        // Set the recommendation from the effect (a resolution higher than the current one to allow for cropping) on the input
        await mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, recommendation.InputProperties);
        await mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, recommendation.InputProperties);
    }

    // Handle the recommendations for the output from the effect
    if (recommendation.OutputProperties != null)
    {
        // Back up the current output properties from before VS was activated
        _outputPropertiesBackup = _encodingProfile.Video;

        // Apply the recommended encoding profile for the output
        _encodingProfile.Video = recommendation.OutputProperties;
    }
}

ビデオ手ブレ補正効果の無効化イベントを処理する

ピクセル スループットが高すぎて効果の処理が追い付かない場合や、効果の実行に時間がかかっていることをシステムが検出した場合、ビデオ手ブレ補正効果がシステムによって自動的に無効化されます。 このように状態が変化した場合、EnabledChanged イベントが発生します。 最新の効果の状態 (有効または無効) は、sender パラメーターに格納された VideoStabilizationEffect のインスタンスによって知ることができます。 VideoStabilizationEffectEnabledChangedEventArgs に格納される VideoStabilizationEffectEnabledChangedReason の値は、効果が有効または無効にされた理由を示しています。 このイベントは、プログラムから効果を有効または無効にした場合にも発生します。この場合の理由は Programmatic になります。

通常、このイベントを使ってアプリの UI を調整し、ビデオ手ブレ補正の現在の状態を示します。

private async void VideoStabilizationEffect_EnabledChanged(VideoStabilizationEffect sender, VideoStabilizationEffectEnabledChangedEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Update your UI to reflect the change in status
        ShowMessageToUser("video stabilization status: " + sender.Enabled + ". Reason: " + args.Reason);
    });
}

ビデオ手ブレ補正効果をクリーンアップする

ビデオ手ブレ補正効果をクリーンアップするには、RemoveEffectAsync を呼び出してビデオ パイプラインから効果を削除します。 初期エンコード プロパティを格納しているメンバー変数が null 以外であれば、それらの変数を使ってエンコード プロパティを復元します。 最後に、EnabledChanged イベント ハンドラーを削除し、効果を null に設定します。

// Clear all effects in the pipeline
await mediaCapture.RemoveEffectAsync(_videoStabilizationEffect);

// If backed up settings (stream properties and encoding profile) exist, restore them and clear the backups
if (_inputPropertiesBackup != null)
{
    await mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, _inputPropertiesBackup);
    _inputPropertiesBackup = null;
}

if (_outputPropertiesBackup != null)
{
    _encodingProfile.Video = _outputPropertiesBackup;
    _outputPropertiesBackup = null;
}

_videoStabilizationEffect.EnabledChanged -= VideoStabilizationEffect_EnabledChanged;

_videoStabilizationEffect = null;