MediaFrameReader を使ったメディア フレームの処理Process media frames with MediaFrameReader

この記事では、MediaCapture と共に MediaFrameReader を使って、色、深度、赤外線カメラ、オーディオ デバイスなどの 1 つ以上の利用可能なソースや、スケルタル トラッキング フレームを生成するようなカスタム フレーム ソースから、メディア フレームを取得する方法を示します。This article shows you how to use a MediaFrameReader with MediaCapture to get media frames from one or more available sources, including color, depth, and infrared cameras, audio devices, or even custom frame sources such as those that produce skeletal tracking frames. この機能は、拡張現実アプリや奥行きを検出するカメラ アプリなど、メディア フレームのリアルタイム処理を実行するアプリで使用するために設計されました。This feature is designed to be used by apps that perform real-time processing of media frames, such as augmented reality and depth-aware camera apps.

一般的な写真アプリなど、ビデオや写真を単にキャプチャすることに興味がある場合は、MediaCapture でサポートされているその他のキャプチャ手法のいずれかを使う方がよいでしょう。If you are interested in simply capturing video or photos, such as a typical photography app, then you probably want to use one of the other capture techniques supported by MediaCapture. 利用可能なメディア キャプチャ手法のリストとそれらの使用方法を示す記事については、「カメラ」をご覧ください。For a list of available media capture techniques and articles showing how to use them, see Camera.

注意

この記事で説明している機能は、Windows 10 バージョン 1607 以降でのみ利用できます。The features discussed in this article are only available starting with Windows 10, version 1607.

注意

MediaFrameReader を使ってカラー カメラ、深度カメラ、赤外線カメラなど、さまざまなフレーム ソースからのフレームを表示する方法を示す、ユニバーサル Windows アプリのサンプルがあります。There is an Universal Windows app sample that demonstrates using MediaFrameReader to display frames from different frame sources, including color, depth, and infrared camreas. 詳しくは、「カメラ フレームのサンプル」をご覧ください。For more information, see Camera frames sample.

注意

Windows 10、バージョン 1803 では、オーディオ データで MediaFrameReader を使用するための新しい API セットが導入されました。A new set of APIs for using MediaFrameReader with audio data were introduced in Windows 10, version 1803. 詳しくは、「MediaFrameReader を使ったオーディオ フレームの処理」をご覧ください。For more information, see Process audio frames with MediaFrameReader.

プロジェクトの設定Setting up your project

MediaCapture を使う他のアプリと同様に、カメラ デバイスにアクセスする前にアプリが webcam 機能を使うことを宣言する必要があります。As with any app that uses MediaCapture, you must declare that your app uses the webcam capability before attempting to access any camera device. アプリがオーディオ デバイスからキャプチャする場合は、microphone デバイス機能も宣言する必要があります。If your app will capture from an audio device, you should also declare the microphone device capability.

アプリマニフェストに機能を追加するAdd capabilities to the app manifest

  1. Microsoft Visual Studio のソリューション エクスプローラーで、package.appxmanifest 項目をダブルクリックしてアプリケーション マニフェストのデザイナーを開きます。In Microsoft Visual Studio, in Solution Explorer, open the designer for the application manifest by double-clicking the package.appxmanifest item.
  2. [機能] タブをクリックします。Select the Capabilities tab.
  3. [Web カメラ] のボックスと [マイク] のボックスをオンにします。Check the box for Webcam and the box for Microphone.
  4. 画像ライブラリとビデオ ライブラリにアクセスするには、 [画像ライブラリ] のボックスと [ビデオ ライブラリ] のボックスをオンにします。For access to the Pictures and Videos library check the boxes for Pictures Library and the box for Videos Library.

この記事のサンプル コードでは、既定のプロジェクト テンプレートに含まれている API に加えて、次の名前空間の API が使っています。The example code in this article uses APIs from the following namespaces, in addition to those included by the default project template.

using Windows.Media.Capture.Frames;
using Windows.Devices.Enumeration;
using Windows.Media.Capture;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Media.MediaProperties;
using Windows.Graphics.Imaging;
using System.Threading;
using Windows.UI.Core;
using System.Threading.Tasks;
using Windows.Media.Core;
using System.Diagnostics;
using Windows.Media;
using Windows.Media.Devices;
using Windows.Media.Audio;

フレーム ソースとフレーム ソース グループを選択するSelect frame sources and frame source groups

メディア フレームを処理する多くのアプリは、デバイスの色、深度カメラなど、複数のソースからフレームを一度に取得する必要があります。Many apps that process media frames need to get frames from multiple sources at once, such as a device's color and depth cameras. Mediaframesourcegroupオブジェクトは、同時に使用できる一連のメディアフレームソースを表します。The MediaFrameSourceGroup object represents a set of media frame sources that can be used simultaneously. 静的メソッド MediaFrameSourceGroup.FindAllAsync を呼び出して、現在のデバイスでサポートされているフレーム ソースのすべてのグループの一覧を取得します。Call the static method MediaFrameSourceGroup.FindAllAsync to get a list of all of the groups of frame sources supported by the current device.

var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();

DeviceWatcher を作成することもできます。これには、デバイス上で使用可能なフレームソースグループがあるときに通知を受信するために、Devicewatcher. Createwatcherと、 Mediaframesourcegroup. getdeviceselectorから返された値を使用します。外部カメラが電源に接続されている場合などの変更。You can also create a DeviceWatcher using DeviceInformation.CreateWatcher and the value returned from MediaFrameSourceGroup.GetDeviceSelector to receive notifications when the available frame source groups on the device changes, such as when an external camera is plugged in. 詳しくは、「デバイスの列挙」をご覧ください。For more information see Enumerate devices.

MediaFrameSourceGroup には、グループに含まれるフレーム ソースを記述する MediaFrameSourceInfo オブジェクトのコレクションがあります。A MediaFrameSourceGroup has a collection of MediaFrameSourceInfo objects that describe the frame sources included in the group. デバイスで利用可能なフレーム ソース グループを取得した後、目的のフレーム ソースを公開するグループを選択できます。After retrieving the frame source groups available on the device, you can select the group that exposes the frame sources you are interested in.

次の例は、フレーム ソース グループを選択する最も簡単な方法を示しています。The following example shows the simplest way to select a frame source group. このコードは、すべての利用可能なグループをループで処理してから、SourceInfos コレクション内の各項目をループで処理します。This code simply loops over all of the available groups and then loops over each item in the SourceInfos collection. MediaFrameSourceInfo について、目的の機能をサポートしているかどうかがチェックされます。Each MediaFrameSourceInfo is checked to see if it supports the features we are seeking. この場合は、MediaStreamType プロパティでデバイスがビデオ プレビュー ストリームを提供するかどうかを示す値 VideoPreview がチェックされ、SourceKind プロパティでソースが色のフレームを提供するかどうかを示す値 Color がチェックされます。In this case, the MediaStreamType property is checked for the value VideoPreview, meaning the device provides a video preview stream, and the SourceKind property is checked for the value Color, indicating that the source provides color frames.

var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();

MediaFrameSourceGroup selectedGroup = null;
MediaFrameSourceInfo colorSourceInfo = null;

foreach (var sourceGroup in frameSourceGroups)
{
    foreach (var sourceInfo in sourceGroup.SourceInfos)
    {
        if (sourceInfo.MediaStreamType == MediaStreamType.VideoPreview
            && sourceInfo.SourceKind == MediaFrameSourceKind.Color)
        {
            colorSourceInfo = sourceInfo;
            break;
        }
    }
    if (colorSourceInfo != null)
    {
        selectedGroup = sourceGroup;
        break;
    }
}

目的のフレーム ソース グループとフレーム ソースを識別するこの方法は、簡単なケースでは機能しますが、より複雑な条件に基づいてフレーム ソースを選択する場合は難しくなります。This method of identifying the desired frame source group and frame sources works for simple cases, but if you want to select frame sources based on more complex criteria, it can quickly become cumbersome. 別の方法として、Linq 構文と匿名オブジェクトを使って選択する方法があります。Another method is to use Linq syntax and anonymous objects to make the selection. 次の例では、Select 拡張メソッドを使って、frameSourceGroups 一覧内の MediaFrameSourceGroup オブジェクトを 2 つのフィールドを持つ匿名オブジェクトに変換します。2 つのフィールドは、グループ自体を表す sourceGroup と、グループ内の色のフレーム ソースを表す colorSourceInfo です。The following example uses the Select extension method to transform the MediaFrameSourceGroup objects in the frameSourceGroups list into an anonymous object with two fields: sourceGroup, representing the group itself, and colorSourceInfo, which represents the color frame source in the group. colorSourceInfo フィールドは、指定された述語が true に解決される最初のオブジェクトを選ぶ FirstOrDefault の結果に設定されます。The colorSourceInfo field is set to the result of FirstOrDefault, which selects the first object for which the provided predicate resolves to true. この場合、述語が true になるのは、ストリーム タイプが VideoPreview、ソースの種類が Color で、カメラがデバイスのフロント パネルにある場合です。In this case, the predicate is true if the stream type is VideoPreview, the source kind is Color, and if the camera is on the front panel of the device.

上記のクエリから返された匿名オブジェクトの一覧から、Where 拡張メソッドを使って、colorSourceInfo フィールドが null でないオブジェクトのみを選択します。From the list of anonymous objects returned from the query described above, the Where extension method is used to select only those objects where the colorSourceInfo field is not null. 最後に、FirstOrDefault が呼び出されて一覧内で最初の項目が選択されます。Finally, FirstOrDefault is called to select the first item in the list.

これで、選択したオブジェクトのフィールドを使って、選択した MediaFrameSourceGroup とカラー カメラを表す MediaFrameSourceInfo オブジェクトへの参照を取得できます。Now you can use the fields of the selected object to get references to the selected MediaFrameSourceGroup and the MediaFrameSourceInfo object representing the color camera. 後でこれらを使って、MediaCapture オブジェクトを初期化し、選択したソースの MediaFrameReader を作成します。These will be used later to initialize the MediaCapture object and create a MediaFrameReader for the selected source. 最後に、ソース グループが null であるかどうかをテストする必要があります。これは、現在のデバイスが要求されたキャプチャ ソースを持っていないことを意味します。Finally, you should test to see if the source group is null, meaning the current device doesn't have your requested capture sources.

var selectedGroupObjects = frameSourceGroups.Select(group =>
   new
   {
       sourceGroup = group,
       colorSourceInfo = group.SourceInfos.FirstOrDefault((sourceInfo) =>
       {
           // On XBox/Kinect, omit the MediaStreamType and EnclosureLocation tests
           return sourceInfo.MediaStreamType == MediaStreamType.VideoPreview
           && sourceInfo.SourceKind == MediaFrameSourceKind.Color
           && sourceInfo.DeviceInformation?.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front;
       })

   }).Where(t => t.colorSourceInfo != null)
   .FirstOrDefault();

MediaFrameSourceGroup selectedGroup = selectedGroupObjects?.sourceGroup;
MediaFrameSourceInfo colorSourceInfo = selectedGroupObjects?.colorSourceInfo;

if (selectedGroup == null)
{
    return;
}

次の例では、上記と同様の手法を使って、カラー カメラ、深度カメラ、赤外線カメラなどのソース グループを選択します。The following example uses a similar technique as described above to select a source group that contains color, depth, and infrared cameras.

var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var eligibleGroups = allGroups.Select(g => new
{
    Group = g,

    // For each source kind, find the source which offers that kind of media frame,
    // or null if there is no such source.
    SourceInfos = new MediaFrameSourceInfo[]
    {
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Color),
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Depth),
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Infrared),
    }
}).Where(g => g.SourceInfos.Any(info => info != null)).ToList();

if (eligibleGroups.Count == 0)
{
    System.Diagnostics.Debug.WriteLine("No source group with color, depth or infrared found.");
    return;
}

var selectedGroupIndex = 0; // Select the first eligible group
MediaFrameSourceGroup selectedGroup = eligibleGroups[selectedGroupIndex].Group;
MediaFrameSourceInfo colorSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[0];
MediaFrameSourceInfo infraredSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[1];
MediaFrameSourceInfo depthSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[2];

注意

Windows 10、バージョン 1803 以降では、MediaCaptureVideoProfile クラスを使用して、目的の機能セットを備えたメディア フレーム ソースを選択できます。Starting with Windows 10, version 1803, you can use the MediaCaptureVideoProfile class to select a media frame source with a set of desired capabilities. 詳しくは、後ほどこの記事の「ビデオ プロファイルを使用してフレーム ソースを選択する」で説明します。For more information, see the section Use video profiles to select a frame source later in this article.

選択したフレーム ソース グループを使うように MediaCapture オブジェクトを初期化するInitialize the MediaCapture object to use the selected frame source group

次に、前の手順で選択したフレーム ソース グループを使うように MediaCapture オブジェクトを初期化します。The next step is to initialize the MediaCapture object to use the frame source group you selected in the previous step.

通常、MediaCapture オブジェクトはアプリ内の複数の場所から使用できるため、それを保持するクラス メンバー変数を宣言する必要があります。The MediaCapture object is typically used from multiple locations within your app, so you should declare a class member variable to hold it.

MediaCapture mediaCapture;

コンス トラクターを呼び出して、MediaCapture オブジェクトのインスタンスを作成します。Create an instance of the MediaCapture object by calling the constructor. 次に、 MediaCaptureオブジェクトを初期化するために使用されるMediaCaptureInitializationSettingsオブジェクトを作成します。Next, create a MediaCaptureInitializationSettings object that will be used to initialize the MediaCapture object. この例では、次の設定を使用しています。In this example, the following settings are used:

  • Sourcegroup -フレームを取得するために使用するソースグループをシステムに指示します。SourceGroup - This tells the system which source group you will be using to get frames. ソース グループは、同時に使用できるメディア フレーム ソースのセットを定義することに注意してください。Remember that the source group defines a set of media frame sources that can be used simultaneously.
  • Sharingmode -キャプチャソースデバイスを排他的に制御する必要があるかどうかをシステムに伝えます。SharingMode - This tells the system whether you need exclusive control over the capture source devices. これを ExclusiveControl に設定すると、生成するフレームの形式など、キャプチャ デバイスの設定を変更することができますが、別のアプリが既に排他的制御を持っている場合、自分のアプリはメディア キャプチャ デバイスを初期化しようとすると失敗します。If you set this to ExclusiveControl, it means that you can change the settings of the capture device, such as the format of the frames it produces, but this means that if another app already has exclusive control, your app will fail when it tries to initialize the media capture device. これを SharedReadOnly に設定すると、別のアプリで使われてもフレーム ソースからフレームを受け取ることができますが、デバイスの設定を変更することはできません。If you set this to SharedReadOnly, you can receive frames from the frame sources even if they are in use by another app, but you can't change the settings for the devices.
  • MemoryPreference - cpuを指定した場合、システムは cpu メモリを使用します。これにより、フレームが到着したときに、これらのオブジェクトをソフトウェアビットマップオブジェクトとして使用できるようになります。MemoryPreference - If you specify CPU, the system will use CPU memory which guarantees that when frames arrive, they will be available as SoftwareBitmap objects. Auto を指定すると、システムはフレームを格納するのに最適なメモリの場所を動的に選択します。If you specify Auto, the system will dynamically choose the optimal memory location to store frames. システムが GPU メモリの使用を選択した場合、メディア フレームは SoftwareBitmap としてではなく、IDirect3DSurface オブジェクトとして到着します。If the system chooses to use GPU memory, the media frames will arrive as an IDirect3DSurface object and not as a SoftwareBitmap.
  • StreamingCaptureMode -オーディオをストリーミングする必要がないことを示すために、これをVideoに設定します。StreamingCaptureMode - Set this to Video to indicate that audio doesn't need to be streamed.

InitializeAsync を呼び出して、目的の設定で MediaCapture を初期化します。Call InitializeAsync to initialize the MediaCapture with your desired settings. 初期化が失敗する場合は、必ず try ブロック内でこれを呼び出してください。Be sure to call this within a try block in case initialization fails.

mediaCapture = new MediaCapture();

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = selectedGroup,
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
    StreamingCaptureMode = StreamingCaptureMode.Video
};
try
{
    await mediaCapture.InitializeAsync(settings);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine("MediaCapture initialization failed: " + ex.Message);
    return;
}

フレーム ソースの優先形式を設定するSet the preferred format for the frame source

フレーム ソースの優先形式を設定するには、ソースを表す MediaFrameSource オブジェクトを取得する必要があります。To set the preferred format for a frame source, you need to get a MediaFrameSource object representing the source. このオブジェクトを取得するには、初期化した MediaCapture オブジェクトの Frames ディクショナリにアクセスし、使用するフレーム ソースの識別子を指定します。You get this object by accessing the Frames dictionary of the initialized MediaCapture object, specifying the identifier of the frame source you want to use. これは、フレーム ソース グループを選択していたときに MediaFrameSourceInfo オブジェクトを保存したからです。This is why we saved the MediaFrameSourceInfo object when we were selecting a frame source group.

MediaFrameSource.SupportedFormats プロパティには、フレーム ソースのサポートされている形式を記述する MediaFrameFormat オブジェクトの一覧が含まれています。The MediaFrameSource.SupportedFormats property contains a list of MediaFrameFormat objects describing the supported formats for the frame source. Where Linq 拡張メソッドを使って、目的のプロパティに基づいて形式を選択します。Use the Where Linq extension method to select a format based on desired properties. この例では、幅が 1080 ピクセルで、32 ビット RGB 形式のフレームを提供できる形式が選択されています。In this example, a format is selected that has a width of 1080 pixels and can supply frames in 32-bit RGB format. FirstOrDefault 拡張メソッドは一覧内で最初のエントリを選択します。The FirstOrDefault extension method selects the first entry in the list. 選択された形式が null の場合、要求された形式はそのフレーム ソースでサポートされません。If the selected format is null, then the requested format is not supported by the frame source. 形式がサポートされている場合は、SetFormatAsync を呼び出すことでソースがこの形式を使うことを要求できます。If the format is supported, you can request that the source use this format by calling SetFormatAsync.

var colorFrameSource = mediaCapture.FrameSources[colorSourceInfo.Id];
var preferredFormat = colorFrameSource.SupportedFormats.Where(format =>
{
    return format.VideoFormat.Width >= 1080
    && format.Subtype == MediaEncodingSubtypes.Argb32;

}).FirstOrDefault();

if (preferredFormat == null)
{
    // Our desired format is not supported
    return;
}

await colorFrameSource.SetFormatAsync(preferredFormat);

フレーム ソースのフレーム リーダーを作成するCreate a frame reader for the frame source

メディア フレーム ソースのフレームを受け取るには、MediaFrameReader を使います。To receive frames for a media frame source, use a MediaFrameReader.

MediaFrameReader mediaFrameReader;

初期化した MediaCapture オブジェクトで CreateFrameReaderAsync を呼び出して、フレーム リーダーをインスタンス化します。Instantiate the frame reader by calling CreateFrameReaderAsync on your initialized MediaCapture object. このメソッドの最初の引数は、フレームを受け取るフレーム ソースです。The first argument to this method is the frame source from which you want to receive frames. 使用するフレーム ソースごとに、別々のフレーム リーダーを作成できます。You can create a separate frame reader for each frame source you want to use. 2 番目の引数で、フレームが到着するときの出力形式をシステムに知らせます。The second argument tells the system the output format in which you want frames to arrive. これによって、フレームが到着したときに独自の変換を行う必要がなくなります。This can save you from having to do your own conversions to frames as they arrive. フレーム ソースでサポートされていない形式を指定すると例外がスローされるため、その値が SupportedFormats コレクションにあることを確認してください。Note that if you specify a format that is not supported by the frame source, an exception will be thrown, so be sure that this value is in the SupportedFormats collection.

フレーム リーダーを作成した後、ソースから新しいフレームが利用可能になったときに発生する FrameArrived イベントのハンドラーを登録します。After creating the frame reader, register a handler for the FrameArrived event which is raised whenever a new frame is available from the source.

StartAsync を呼び出して、ソースからフレームの読み取りを開始するようにシステムに伝えます。Tell the system to start reading frames from the source by calling StartAsync.

mediaFrameReader = await mediaCapture.CreateFrameReaderAsync(colorFrameSource, MediaEncodingSubtypes.Argb32);
mediaFrameReader.FrameArrived += ColorFrameReader_FrameArrived;
await mediaFrameReader.StartAsync();

フレームの到着イベントを処理するHandle the frame arrived event

新しいフレームが利用可能になると、MediaFrameReader.FrameArrived イベントが発生します。The MediaFrameReader.FrameArrived event is raised whenever a new frame is available. 到着したすべてのフレームを処理するか、必要なときのみフレームを使うかを選択できます。You can choose to process every frame that arrives or only use frames when you need them. フレーム リーダーは自身のスレッドでイベントを発生させるため、何らかの同期ロジックを実装して、複数のスレッドからの同じデータにアクセスしていないことを確認する必要があります。Because the frame reader raises the event on its own thread, you may need to implement some synchronization logic to make sure that you aren't attempting to access the same data from multiple threads. このセクションでは、XAML ページのイメージ コントロールへの色フレームの描画を同期する方法を示します。This section shows you how to synchronize drawing color frames to an image control in a XAML page. このシナリオでは、XAML コントロールへのすべての更新を UI スレッドで実行するように要求する追加の同期制約について検討します。This scenario addresses the additional synchronization constraint that requires all updates to XAML controls be performed on the UI thread.

XAML でフレームを表示するときの最初の手順は、イメージ コントロールの作成です。The first step in displaying frames in XAML is to create an Image control.

<Image x:Name="imageElement" Width="320" Height="240" />

コード ビハインド ページで、SoftwareBitmap 型のクラス メンバー変数を宣言します。これは、すべての着信イメージのコピー先のバック バッファーとして使用されます。In your code behind page, declare a class member variable of type SoftwareBitmap which will be used as a back buffer that all incoming images will be copied to. イメージ データ自体はコピーされず、オブジェクト参照だけがコピーされます。Note that the image data itself isn't copied, just the object references. また、UI 操作が現在実行されているかどうかを追跡するブール値を宣言します。Also, declare a boolean to track whether our UI operation is currently running.

private SoftwareBitmap backBuffer;
private bool taskRunning = false;

フレームは SoftwareBitmap オブジェクトとして到着するため、SoftwareBitmap を XAML Control のソースとして使用できるようにする SoftwareBitmapSource オブジェクトを作成する必要があります。Because the frames will arrive as SoftwareBitmap objects, you need to create a SoftwareBitmapSource object which allows you to use a SoftwareBitmap as the source for a XAML Control. フレーム リーダーを開始する前に、コード内のどこかでイメージ ソースを設定する必要があります。You should set the image source somewhere in your code before you start the frame reader.

imageElement.Source = new SoftwareBitmapSource();

ここで、FrameArrived イベント ハンドラーを実装します。Now it's time to implement the FrameArrived event handler. ハンドラーが呼び出されると、sender パラメーターにイベントを発生させた MediaFrameReader オブジェクトへの参照が含まれます。When the handler is called, the sender parameter contains a reference to the MediaFrameReader object which raised the event. このオブジェクトで TryAcquireLatestFrame を呼び出して、最新のフレームの取得を試みます。Call TryAcquireLatestFrame on this object to attempt to get the latest frame. 名前からわかるように、TryAcquireLatestFrame はフレームを返すことに失敗することがあります。As the name implies, TryAcquireLatestFrame may not succeed in returning a frame. そのため、VideoMediaFrame プロパティ、SoftwareBitmap プロパティの順にアクセスするときは、必ず null を検査してください。So, when you access the VideoMediaFrame and then SoftwareBitmap properties, be sure to test for null. この例では、null 条件演算子 ? を使ってIn this example the null condtional operator ? SoftwareBitmap にアクセスした後、取得したオブジェクトで null がチェックされています。is used to access the SoftwareBitmap and then the retrieved object is checked for null.

Image コントロールは、プリマルチプライ処理済みまたはアルファなしの BRGA8 形式でのみイメージを表示できます。The Image control can only display images in BRGA8 format with either pre-multiplied or no alpha. 到着するフレームがその形式でない場合は、静的メソッド Convert を使ってソフトウェア ビットマップを正しい形式に変換します。If the arriving frame is not in that format, the static method Convert is used to convert the software bitmap to the correct format.

次に、Interlocked.Exchange メソッドを使って、到着するビットマップの参照をバックバッファー ビットマップと交換します。Next, the Interlocked.Exchange method is used to swap the reference of to arriving bitmap with the backbuffer bitmap. このメソッドは、スレッド セーフであるアトミック操作でこれらの参照を交換します。This method swaps these references in an atomic operation that is thread-safe. 交換後、softwareBitmap 変数に格納されている古いバックバッファー イメージは、リソースをクリーンアップするために破棄されます。After swapping, the old backbuffer image, now in the softwareBitmap variable is disposed of to clean up its resources.

次に、Image 要素に関連付けられている CoreDispatcher を使って、RunAsync を呼び出して UI スレッドで実行されるタスクを作成します。Next, the CoreDispatcher associated with the Image element is used to create a task that will run on the UI thread by calling RunAsync. 非同期タスクはタスク内で実行されるため、RunAsync に渡されるラムダ式は async キーワードを付けて宣言されます。Because the asynchronous tasks will be performed within the task, the lambda expression passed to RunAsync is declared with the async keyword.

タスク内で、 _taskRunning 変数をチェックして、タスクの 1 つのインスタンスだけが一度に実行されていることを確認します。Within the task, the _taskRunning variable is checked to make sure that only one instance of the task is running at a time. タスクが既に実行されていない場合は、タスクがもう一度実行されることを防ぐために _taskRunning を true に設定します。If the task isn't already running, _taskRunning is set to true to prevent the task from running again. while ループで、バックバッファー イメージが null になるまで、Interlocked.Exchange を呼び出してバックバッファーから一時的な SoftwareBitmap にコピーします。In a while loop, Interlocked.Exchange is called to copy from the backbuffer into a temporary SoftwareBitmap until the backbuffer image is null. 一時的なビットマップが入力されるたびに、ImageSource プロパティを SoftwareBitmapSource にキャストし、SetBitmapAsync を呼び出してイメージのソースを設定します。For each time the temporary bitmap is populated, the Source property of the Image is cast to a SoftwareBitmapSource, and then SetBitmapAsync is called to set the source of the image.

最後に、次回にハンドラーが呼び出されたときにタスクをもう一度実行できるように、 _taskRunning 変数を false に戻します。Finally, the _taskRunning variable is set back to false so that the task can be run again the next time the handler is called.

注意

MediaFrameReferenceVideoMediaFrame プロパティが提供する SoftwareBitmap オブジェクトまたは Direct3DSurface オブジェクトにアクセスすると、これらのオブジェクトへの強参照がシステムによって作成されます。そのため、それらが含まれている MediaFrameReference に対して Dispose を呼び出してもこれらのオブジェクトは破棄されません。If you access the SoftwareBitmap or Direct3DSurface objects provided by the VideoMediaFrame property of a MediaFrameReference, the system creates a strong reference to these objects, which means that they will not be disposed when you call Dispose on the containing MediaFrameReference. それらのオブジェクトを即座に破棄するには、SoftwareBitmap または Direct3DSurfaceDispose メソッドを明示的に直接呼び出す必要があります。You must explicitly call the Dispose method of the SoftwareBitmap or Direct3DSurface directly for the objects to be immediately disposed. そうしない場合、最終的にはガーベジ コレクターによってこれらのオブジェクトのメモリが解放されますが、それがいつになるかは不明であり、割り当てられたビットマップやサーフェスの数がシステムによって許容される最大数を上回った場合、新しいフレームのフローが停止します。Otherwise, the garbage collector will eventually free the memory for these objects, but you can't know when this will occur, and if the number of allocated bitmaps or surfaces exceeds the maximum amount allowed by the system, the flow of new frames will stop. この制約に対処するには、取得したフレームをたとえば SoftwareBitmap.Copy メソッドによってコピーし、その後、元のフレームを解放することができます。You can copy retrieved frames, using the SoftwareBitmap.Copy method for example, and then release the original frames to overcome this limitation. また、オーバーロード CreateFrameReaderAsync(Windows.Media.Capture.Frames.MediaFrameSource inputSource, System.String outputSubtype, Windows.Graphics.Imaging.BitmapSize outputSize) または CreateFrameReaderAsync(Windows.Media.Capture.Frames.MediaFrameSource inputSource, System.String outputSubtype) を使用して MediaFrameReader を作成した場合、介されるフレームは元のフレーム データのコピーであるため、それらのフレームを保持しても取得は停止しません。Also, if you create the MediaFrameReader using the overload CreateFrameReaderAsync(Windows.Media.Capture.Frames.MediaFrameSource inputSource, System.String outputSubtype, Windows.Graphics.Imaging.BitmapSize outputSize) or CreateFrameReaderAsync(Windows.Media.Capture.Frames.MediaFrameSource inputSource, System.String outputSubtype), the frames returned are copies of the original frame data and so they do not cause frame acquisition to halt when they are retained.

private void ColorFrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
    var mediaFrameReference = sender.TryAcquireLatestFrame();
    var videoMediaFrame = mediaFrameReference?.VideoMediaFrame;
    var softwareBitmap = videoMediaFrame?.SoftwareBitmap;

    if (softwareBitmap != null)
    {
        if (softwareBitmap.BitmapPixelFormat != Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8 ||
            softwareBitmap.BitmapAlphaMode != Windows.Graphics.Imaging.BitmapAlphaMode.Premultiplied)
        {
            softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
        }

        // Swap the processed frame to _backBuffer and dispose of the unused image.
        softwareBitmap = Interlocked.Exchange(ref backBuffer, softwareBitmap);
        softwareBitmap?.Dispose();

        // Changes to XAML ImageElement must happen on UI thread through Dispatcher
        var task = imageElement.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
            async () =>
            {
                // Don't let two copies of this task run at the same time.
                if (taskRunning)
                {
                    return;
                }
                taskRunning = true;

                // Keep draining frames from the backbuffer until the backbuffer is empty.
                SoftwareBitmap latestBitmap;
                while ((latestBitmap = Interlocked.Exchange(ref backBuffer, null)) != null)
                {
                    var imageSource = (SoftwareBitmapSource)imageElement.Source;
                    await imageSource.SetBitmapAsync(latestBitmap);
                    latestBitmap.Dispose();
                }

                taskRunning = false;
            });
    }

    mediaFrameReference.Dispose();
}

リソースをクリーンアップするCleanup resources

フレームの読み込みが終わったら、必ず StopAsync を呼び出してメディア フレーム リーダーを停止して、FrameArrived ハンドラーの登録を解除し、MediaCapture オブジェクトを破棄してください。When you are done reading frames, be sure to stop the media frame reader by calling StopAsync, unregistering the FrameArrived handler, and disposing of the MediaCapture object.

await mediaFrameReader.StopAsync();
mediaFrameReader.FrameArrived -= ColorFrameReader_FrameArrived;
mediaCapture.Dispose();
mediaCapture = null;

アプリケーションが中断されたときのメディア キャプチャ オブジェクトのクリーンアップについて詳しくは、「カメラ プレビューの表示」をご覧ください。For more information about cleaning up media capture objects when your application is suspended, see Display the camera preview.

FrameRenderer ヘルパー クラスThe FrameRenderer helper class

ユニバーサル Windows のカメラ フレームのサンプルは、アプリで色、赤外線、および深度のソースからフレームを表示するのを容易にするヘルパー クラスを提供します。The Universal Windows Camera frames sample provides a helper class that makes it easy to display the frames from color, infrared, and depth sources in your app. 通常、深度や赤外線のデータを画面に表示するだけでなく、データを使ってそれ以上のことを行いたいですが、このヘルパー クラスは、フレーム リーダーの機能を示したり、独自のフレーム リーダーの実装をデバッグしたりするための便利なツールです。Typically, you will want to do something more with depth and infrared data than just display it to the screen, but this helper class is a helpful tool for demonstrating the frame reader feature and for debugging your own frame reader implementation.

FrameRenderer ヘルパー クラスは、次のメソッドを実装します。The FrameRenderer helper class implements the following methods.

  • FrameRenderer コンストラクター - このコンストラクターは、メディア フレームを表示するために渡す XAML Image 要素を使うようにヘルパー クラスを初期化します。FrameRenderer constructor - The constructor initializes the helper class to use the XAML Image element you pass in for displaying media frames.
  • ProcessFrame - このメソッドは、コンストラクターに渡した Image 要素に、MediaFrameReference で表されるメディア フレームを表示します。ProcessFrame - This method displays a media frame, represented by a MediaFrameReference, in the Image element you passed into the constructor. 通常、FrameArrived イベント ハンドラーからこのメソッドを呼び出し、TryAcquireLatestFrame から返されるフレームを渡します。You should typically call this method from your FrameArrived event handler, passing in the frame returned by TryAcquireLatestFrame.
  • ConvertToDisplayableImage - このメソッドは、メディア フレームの形式をチェックし、必要な場合は表示可能な形式に変換します。ConvertToDisplayableImage - This methods checks the format of the media frame and, if necessary, converts it to a displayable format. カラー イメージの場合は、色の形式が BGRA8 であり、ビットマップのアルファ モードがプリマルチプライ済みであることを確認します。For color images, this means making sure that the color format is BGRA8 and that the bitmap alpha mode is premultiplied. 深度または赤外線フレームの場合は、各スキャン ラインを処理して、深度または赤外線の値が疑似カラー グラデーションに変換します。これには、サンプルに含まれていて以下に記載される PseudoColorHelper クラスを使います。For depth or infrared frames, each scanline is processed to convert the depth or infrared values to a psuedocolor gradient, using the PsuedoColorHelper class that is also included in the sample and listed below.

注意

SoftwareBitmap イメージ上でピクセル操作を行うには、ネイティブ メモリ バッファーにアクセスする必要があります。In order to do pixel manipulation on SoftwareBitmap images, you must access a native memory buffer. これを行うには、以下のコードに含まれている IMemoryBufferByteAccess COM インターフェイスを使う必要があり、アンセーフ コードのコンパイルを許可するようにプロジェクトのプロパティを更新する必要があります。To do this, you must use the IMemoryBufferByteAccess COM interface included in the code listing below and you must update your project properties to allow compilation of unsafe code. 詳しくは、「ビットマップ画像の作成、編集、保存」をご覧ください。For more information, see Create, edit, and save bitmap images.

[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

class FrameRenderer
{
    private Image _imageElement;
    private SoftwareBitmap _backBuffer;
    private bool _taskRunning = false;

    public FrameRenderer(Image imageElement)
    {
        _imageElement = imageElement;
        _imageElement.Source = new SoftwareBitmapSource();
    }

    // Processes a MediaFrameReference and displays it in a XAML image control
    public void ProcessFrame(MediaFrameReference frame)
    {
        var softwareBitmap = FrameRenderer.ConvertToDisplayableImage(frame?.VideoMediaFrame);
        if (softwareBitmap != null)
        {
            // Swap the processed frame to _backBuffer and trigger UI thread to render it
            softwareBitmap = Interlocked.Exchange(ref _backBuffer, softwareBitmap);

            // UI thread always reset _backBuffer before using it.  Unused bitmap should be disposed.
            softwareBitmap?.Dispose();

            // Changes to xaml ImageElement must happen in UI thread through Dispatcher
            var task = _imageElement.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                async () =>
                {
                    // Don't let two copies of this task run at the same time.
                    if (_taskRunning)
                    {
                        return;
                    }
                    _taskRunning = true;

                    // Keep draining frames from the backbuffer until the backbuffer is empty.
                    SoftwareBitmap latestBitmap;
                    while ((latestBitmap = Interlocked.Exchange(ref _backBuffer, null)) != null)
                    {
                        var imageSource = (SoftwareBitmapSource)_imageElement.Source;
                        await imageSource.SetBitmapAsync(latestBitmap);
                        latestBitmap.Dispose();
                    }

                    _taskRunning = false;
                });
        }
    }



    // Function delegate that transforms a scanline from an input image to an output image.
    private unsafe delegate void TransformScanline(int pixelWidth, byte* inputRowBytes, byte* outputRowBytes);
    /// <summary>
    /// Determines the subtype to request from the MediaFrameReader that will result in
    /// a frame that can be rendered by ConvertToDisplayableImage.
    /// </summary>
    /// <returns>Subtype string to request, or null if subtype is not renderable.</returns>

    public static string GetSubtypeForFrameReader(MediaFrameSourceKind kind, MediaFrameFormat format)
    {
        // Note that media encoding subtypes may differ in case.
        // https://docs.microsoft.com/en-us/uwp/api/Windows.Media.MediaProperties.MediaEncodingSubtypes

        string subtype = format.Subtype;
        switch (kind)
        {
            // For color sources, we accept anything and request that it be converted to Bgra8.
            case MediaFrameSourceKind.Color:
                return Windows.Media.MediaProperties.MediaEncodingSubtypes.Bgra8;

            // The only depth format we can render is D16.
            case MediaFrameSourceKind.Depth:
                return String.Equals(subtype, Windows.Media.MediaProperties.MediaEncodingSubtypes.D16, StringComparison.OrdinalIgnoreCase) ? subtype : null;

            // The only infrared formats we can render are L8 and L16.
            case MediaFrameSourceKind.Infrared:
                return (String.Equals(subtype, Windows.Media.MediaProperties.MediaEncodingSubtypes.L8, StringComparison.OrdinalIgnoreCase) ||
                    String.Equals(subtype, Windows.Media.MediaProperties.MediaEncodingSubtypes.L16, StringComparison.OrdinalIgnoreCase)) ? subtype : null;

            // No other source kinds are supported by this class.
            default:
                return null;
        }
    }

    /// <summary>
    /// Converts a frame to a SoftwareBitmap of a valid format to display in an Image control.
    /// </summary>
    /// <param name="inputFrame">Frame to convert.</param>

    public static unsafe SoftwareBitmap ConvertToDisplayableImage(VideoMediaFrame inputFrame)
    {
        SoftwareBitmap result = null;
        using (var inputBitmap = inputFrame?.SoftwareBitmap)
        {
            if (inputBitmap != null)
            {
                switch (inputFrame.FrameReference.SourceKind)
                {
                    case MediaFrameSourceKind.Color:
                        // XAML requires Bgra8 with premultiplied alpha.
                        // We requested Bgra8 from the MediaFrameReader, so all that's
                        // left is fixing the alpha channel if necessary.
                        if (inputBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8)
                        {
                            System.Diagnostics.Debug.WriteLine("Color frame in unexpected format.");
                        }
                        else if (inputBitmap.BitmapAlphaMode == BitmapAlphaMode.Premultiplied)
                        {
                            // Already in the correct format.
                            result = SoftwareBitmap.Copy(inputBitmap);
                        }
                        else
                        {
                            // Convert to premultiplied alpha.
                            result = SoftwareBitmap.Convert(inputBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
                        }
                        break;

                    case MediaFrameSourceKind.Depth:
                        // We requested D16 from the MediaFrameReader, so the frame should
                        // be in Gray16 format.
                        if (inputBitmap.BitmapPixelFormat == BitmapPixelFormat.Gray16)
                        {
                            // Use a special pseudo color to render 16 bits depth frame.
                            var depthScale = (float)inputFrame.DepthMediaFrame.DepthFormat.DepthScaleInMeters;
                            var minReliableDepth = inputFrame.DepthMediaFrame.MinReliableDepth;
                            var maxReliableDepth = inputFrame.DepthMediaFrame.MaxReliableDepth;
                            result = TransformBitmap(inputBitmap, (w, i, o) => PseudoColorHelper.PseudoColorForDepth(w, i, o, depthScale, minReliableDepth, maxReliableDepth));
                        }
                        else
                        {
                            System.Diagnostics.Debug.WriteLine("Depth frame in unexpected format.");
                        }
                        break;

                    case MediaFrameSourceKind.Infrared:
                        // We requested L8 or L16 from the MediaFrameReader, so the frame should
                        // be in Gray8 or Gray16 format. 
                        switch (inputBitmap.BitmapPixelFormat)
                        {
                            case BitmapPixelFormat.Gray16:
                                // Use pseudo color to render 16 bits frames.
                                result = TransformBitmap(inputBitmap, PseudoColorHelper.PseudoColorFor16BitInfrared);
                                break;

                            case BitmapPixelFormat.Gray8:
                                // Use pseudo color to render 8 bits frames.
                                result = TransformBitmap(inputBitmap, PseudoColorHelper.PseudoColorFor8BitInfrared);
                                break;
                            default:
                                System.Diagnostics.Debug.WriteLine("Infrared frame in unexpected format.");
                                break;
                        }
                        break;
                }
            }
        }

        return result;
    }



    /// <summary>
    /// Transform image into Bgra8 image using given transform method.
    /// </summary>
    /// <param name="softwareBitmap">Input image to transform.</param>
    /// <param name="transformScanline">Method to map pixels in a scanline.</param>

    private static unsafe SoftwareBitmap TransformBitmap(SoftwareBitmap softwareBitmap, TransformScanline transformScanline)
    {
        // XAML Image control only supports premultiplied Bgra8 format.
        var outputBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8,
            softwareBitmap.PixelWidth, softwareBitmap.PixelHeight, BitmapAlphaMode.Premultiplied);

        using (var input = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
        using (var output = outputBitmap.LockBuffer(BitmapBufferAccessMode.Write))
        {
            // Get stride values to calculate buffer position for a given pixel x and y position.
            int inputStride = input.GetPlaneDescription(0).Stride;
            int outputStride = output.GetPlaneDescription(0).Stride;
            int pixelWidth = softwareBitmap.PixelWidth;
            int pixelHeight = softwareBitmap.PixelHeight;

            using (var outputReference = output.CreateReference())
            using (var inputReference = input.CreateReference())
            {
                // Get input and output byte access buffers.
                byte* inputBytes;
                uint inputCapacity;
                ((IMemoryBufferByteAccess)inputReference).GetBuffer(out inputBytes, out inputCapacity);
                byte* outputBytes;
                uint outputCapacity;
                ((IMemoryBufferByteAccess)outputReference).GetBuffer(out outputBytes, out outputCapacity);

                // Iterate over all pixels and store converted value.
                for (int y = 0; y < pixelHeight; y++)
                {
                    byte* inputRowBytes = inputBytes + y * inputStride;
                    byte* outputRowBytes = outputBytes + y * outputStride;

                    transformScanline(pixelWidth, inputRowBytes, outputRowBytes);
                }
            }
        }

        return outputBitmap;
    }



    /// <summary>
    /// A helper class to manage look-up-table for pseudo-colors.
    /// </summary>

    private static class PseudoColorHelper
    {
        #region Constructor, private members and methods

        private const int TableSize = 1024;   // Look up table size
        private static readonly uint[] PseudoColorTable;
        private static readonly uint[] InfraredRampTable;

        // Color palette mapping value from 0 to 1 to blue to red colors.
        private static readonly Color[] ColorRamp =
        {
            Color.FromArgb(a:0xFF, r:0x7F, g:0x00, b:0x00),
            Color.FromArgb(a:0xFF, r:0xFF, g:0x00, b:0x00),
            Color.FromArgb(a:0xFF, r:0xFF, g:0x7F, b:0x00),
            Color.FromArgb(a:0xFF, r:0xFF, g:0xFF, b:0x00),
            Color.FromArgb(a:0xFF, r:0x7F, g:0xFF, b:0x7F),
            Color.FromArgb(a:0xFF, r:0x00, g:0xFF, b:0xFF),
            Color.FromArgb(a:0xFF, r:0x00, g:0x7F, b:0xFF),
            Color.FromArgb(a:0xFF, r:0x00, g:0x00, b:0xFF),
            Color.FromArgb(a:0xFF, r:0x00, g:0x00, b:0x7F),
        };

        static PseudoColorHelper()
        {
            PseudoColorTable = InitializePseudoColorLut();
            InfraredRampTable = InitializeInfraredRampLut();
        }

        /// <summary>
        /// Maps an input infrared value between [0, 1] to corrected value between [0, 1].
        /// </summary>
        /// <param name="value">Input value between [0, 1].</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]  // Tell the compiler to inline this method to improve performance

        private static uint InfraredColor(float value)
        {
            int index = (int)(value * TableSize);
            index = index < 0 ? 0 : index > TableSize - 1 ? TableSize - 1 : index;
            return InfraredRampTable[index];
        }

        /// <summary>
        /// Initializes the pseudo-color look up table for infrared pixels
        /// </summary>

        private static uint[] InitializeInfraredRampLut()
        {
            uint[] lut = new uint[TableSize];
            for (int i = 0; i < TableSize; i++)
            {
                var value = (float)i / TableSize;
                // Adjust to increase color change between lower values in infrared images

                var alpha = (float)Math.Pow(1 - value, 12);
                lut[i] = ColorRampInterpolation(alpha);
            }

            return lut;
        }



        /// <summary>
        /// Initializes pseudo-color look up table for depth pixels
        /// </summary>
        private static uint[] InitializePseudoColorLut()
        {
            uint[] lut = new uint[TableSize];
            for (int i = 0; i < TableSize; i++)
            {
                lut[i] = ColorRampInterpolation((float)i / TableSize);
            }

            return lut;
        }



        /// <summary>
        /// Maps a float value to a pseudo-color pixel
        /// </summary>
        private static uint ColorRampInterpolation(float value)
        {
            // Map value to surrounding indexes on the color ramp
            int rampSteps = ColorRamp.Length - 1;
            float scaled = value * rampSteps;
            int integer = (int)scaled;
            int index =
                integer < 0 ? 0 :
                integer >= rampSteps - 1 ? rampSteps - 1 :
                integer;

            Color prev = ColorRamp[index];
            Color next = ColorRamp[index + 1];

            // Set color based on ratio of closeness between the surrounding colors
            uint alpha = (uint)((scaled - integer) * 255);
            uint beta = 255 - alpha;
            return
                ((prev.A * beta + next.A * alpha) / 255) << 24 | // Alpha
                ((prev.R * beta + next.R * alpha) / 255) << 16 | // Red
                ((prev.G * beta + next.G * alpha) / 255) << 8 |  // Green
                ((prev.B * beta + next.B * alpha) / 255);        // Blue
        }


        /// <summary>
        /// Maps a value in [0, 1] to a pseudo RGBA color.
        /// </summary>
        /// <param name="value">Input value between [0, 1].</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]

        private static uint PseudoColor(float value)
        {
            int index = (int)(value * TableSize);
            index = index < 0 ? 0 : index > TableSize - 1 ? TableSize - 1 : index;
            return PseudoColorTable[index];
        }

        #endregion

        /// <summary>
        /// Maps each pixel in a scanline from a 16 bit depth value to a pseudo-color pixel.
        /// </summary>
        /// <param name="pixelWidth">Width of the input scanline, in pixels.</param>
        /// <param name="inputRowBytes">Pointer to the start of the input scanline.</param>
        /// <param name="outputRowBytes">Pointer to the start of the output scanline.</param>
        /// <param name="depthScale">Physical distance that corresponds to one unit in the input scanline.</param>
        /// <param name="minReliableDepth">Shortest distance at which the sensor can provide reliable measurements.</param>
        /// <param name="maxReliableDepth">Furthest distance at which the sensor can provide reliable measurements.</param>

        public static unsafe void PseudoColorForDepth(int pixelWidth, byte* inputRowBytes, byte* outputRowBytes, float depthScale, float minReliableDepth, float maxReliableDepth)
        {
            // Visualize space in front of your desktop.
            float minInMeters = minReliableDepth * depthScale;
            float maxInMeters = maxReliableDepth * depthScale;
            float one_min = 1.0f / minInMeters;
            float range = 1.0f / maxInMeters - one_min;

            ushort* inputRow = (ushort*)inputRowBytes;
            uint* outputRow = (uint*)outputRowBytes;

            for (int x = 0; x < pixelWidth; x++)
            {
                var depth = inputRow[x] * depthScale;

                if (depth == 0)
                {
                    // Map invalid depth values to transparent pixels.
                    // This happens when depth information cannot be calculated, e.g. when objects are too close.
                    outputRow[x] = 0;
                }
                else
                {
                    var alpha = (1.0f / depth - one_min) / range;
                    outputRow[x] = PseudoColor(alpha * alpha);
                }
            }
        }



        /// <summary>
        /// Maps each pixel in a scanline from a 8 bit infrared value to a pseudo-color pixel.
        /// </summary>
        /// /// <param name="pixelWidth">Width of the input scanline, in pixels.</param>
        /// <param name="inputRowBytes">Pointer to the start of the input scanline.</param>
        /// <param name="outputRowBytes">Pointer to the start of the output scanline.</param>

        public static unsafe void PseudoColorFor8BitInfrared(
            int pixelWidth, byte* inputRowBytes, byte* outputRowBytes)
        {
            byte* inputRow = inputRowBytes;
            uint* outputRow = (uint*)outputRowBytes;

            for (int x = 0; x < pixelWidth; x++)
            {
                outputRow[x] = InfraredColor(inputRow[x] / (float)Byte.MaxValue);
            }
        }

        /// <summary>
        /// Maps each pixel in a scanline from a 16 bit infrared value to a pseudo-color pixel.
        /// </summary>
        /// <param name="pixelWidth">Width of the input scanline.</param>
        /// <param name="inputRowBytes">Pointer to the start of the input scanline.</param>
        /// <param name="outputRowBytes">Pointer to the start of the output scanline.</param>

        public static unsafe void PseudoColorFor16BitInfrared(int pixelWidth, byte* inputRowBytes, byte* outputRowBytes)
        {
            ushort* inputRow = (ushort*)inputRowBytes;
            uint* outputRow = (uint*)outputRowBytes;

            for (int x = 0; x < pixelWidth; x++)
            {
                outputRow[x] = InfraredColor(inputRow[x] / (float)UInt16.MaxValue);
            }
        }
    }


    // Displays the provided softwareBitmap in a XAML image control.
    public void PresentSoftwareBitmap(SoftwareBitmap softwareBitmap)
    {
        if (softwareBitmap != null)
        {
            // Swap the processed frame to _backBuffer and trigger UI thread to render it
            softwareBitmap = Interlocked.Exchange(ref _backBuffer, softwareBitmap);

            // UI thread always reset _backBuffer before using it.  Unused bitmap should be disposed.
            softwareBitmap?.Dispose();

            // Changes to xaml ImageElement must happen in UI thread through Dispatcher
            var task = _imageElement.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                async () =>
                {
                    // Don't let two copies of this task run at the same time.
                    if (_taskRunning)
                    {
                        return;
                    }
                    _taskRunning = true;

                    // Keep draining frames from the backbuffer until the backbuffer is empty.
                    SoftwareBitmap latestBitmap;
                    while ((latestBitmap = Interlocked.Exchange(ref _backBuffer, null)) != null)
                    {
                        var imageSource = (SoftwareBitmapSource)_imageElement.Source;
                        await imageSource.SetBitmapAsync(latestBitmap);
                        latestBitmap.Dispose();
                    }

                    _taskRunning = false;
                });
        }
    }
}

MultiSourceMediaFrameReader を使用して複数のソースから時間相関フレームを取得するUse MultiSourceMediaFrameReader to get time-corellated frames from multiple sources

Windows 10 Version 1607 以降では、MultiSourceMediaFrameReader を使って複数のソースから時間相関フレームを取得できます。Starting with Windows 10, version 1607, you can use MultiSourceMediaFrameReader to receive time-corellated frames from multiple sources. この API により、DepthCorrelatedCoordinateMapper クラスを使う場合など、複数のソースから時間的に近接するフレームを必要とする処理が簡単になります。This API makes it easier to do processing that requires frames from multiple sources that were taken in close temporal proximity, such as using the DepthCorrelatedCoordinateMapper class. ただし、この新しいメソッドを使う場合の制限の 1 つとして、フレーム到着イベントは、最も低速なキャプチャ ソースに合わせて生成されるようになります。One limitation of using this new method is that frame-arrived events are only raised at the rate of the slowest capture source. 高速なソースからの追加のフレームは取りこぼされます。Extra frames from faster sources will be dropped. また、システムでは、さまざまなソースからさまざまな速度でフレームが到着するものと想定するため、ソースがフレームの生成を完全に停止したかどうかを自動的に認識することはできません。Also, because the system expects frames to arrive from different sources at different rates, it does not automatically recognize if a source has stopped generating frames altogether. このセクションのコード例では、イベントを使って独自のタイムアウト ロジックを作成する方法を示します。アプリで定義した制限時間内に相関フレームが到着しなかった場合、タイムアウトが発生します。The example code in this section shows how to use an event to create your own timeout logic that gets invoked if correlated frames don't arrive within an app-defined time limit.

MultiSourceMediaFrameReader を使う手順は、この記事で既に説明した MediaFrameReader を使う手順と同様です。The steps for using MultiSourceMediaFrameReader are similar to the steps for using MediaFrameReader described previously in this article. この例では、カラー ソースと深度ソースを使います。This example will use a color source and a depth source. メディア フレーム ソース ID を格納する文字列変数をいくつか宣言します。これらの ID は、各ソースからのフレームを選択するために使われます。Declare some string variables to store the media frame source IDs that will be used to select frames from each source. 次に、サンプルのタイムアウト ロジックを実装するために使う ManualResetEventSlimCancellationTokenSourceEventHandler を宣言します。Next, declare a ManualResetEventSlim, a CancellationTokenSource, and an EventHandler that will be used to implement timeout logic for the example.

private MultiSourceMediaFrameReader _multiFrameReader = null;
private string _colorSourceId = null;
private string _depthSourceId = null;


private readonly ManualResetEventSlim _frameReceived = new ManualResetEventSlim(false);
private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource();
public event EventHandler CorrelationFailed;

この記事で既に説明した手法を使って、このサンプル シナリオに必要なカラー ソースと深度ソースを含む MediaFrameSourceGroup を照会します。Using the techniques described previously in this article, query for a MediaFrameSourceGroup that includes the color and depth sources required for this example scenario. 目的のフレーム ソース グループを選択したら、各フレーム ソースの MediaFrameSourceInfo を取得します。After selecting the desired frame source group, get the MediaFrameSourceInfo for each frame source.

var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var eligibleGroups = allGroups.Select(g => new
{
    Group = g,

    // For each source kind, find the source which offers that kind of media frame,
    // or null if there is no such source.
    SourceInfos = new MediaFrameSourceInfo[]
    {
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Color),
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Depth)
    }
}).Where(g => g.SourceInfos.Any(info => info != null)).ToList();

if (eligibleGroups.Count == 0)
{
    System.Diagnostics.Debug.WriteLine("No source group with color, depth or infrared found.");
    return;
}

var selectedGroupIndex = 0; // Select the first eligible group
MediaFrameSourceGroup selectedGroup = eligibleGroups[selectedGroupIndex].Group;
MediaFrameSourceInfo colorSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[0];
MediaFrameSourceInfo depthSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[1];

MediaCapture オブジェクトを作成し、選択したフレーム ソース グループを初期化設定に渡して初期化します。Create and initialize a MediaCapture object, passing the selected frame source group in the initialization settings.

mediaCapture = new MediaCapture();

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = selectedGroup,
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
    StreamingCaptureMode = StreamingCaptureMode.Video
};

await mediaCapture.InitializeAsync(settings);

MediaCapture オブジェクトを初期化したら、カラー カメラと深度カメラの MediaFrameSource オブジェクトを取得します。After initializing the MediaCapture object, retrieve MediaFrameSource objects for the color and depth cameras. 各ソースの ID を格納して、対応するソースから到着したフレームを選択できるようにします。Store the ID for each source so that you can select the arriving frame for the corresponding source.

MediaFrameSource colorSource =
    mediaCapture.FrameSources.Values.FirstOrDefault(
        s => s.Info.SourceKind == MediaFrameSourceKind.Color);

MediaFrameSource depthSource =
    mediaCapture.FrameSources.Values.FirstOrDefault(
        s => s.Info.SourceKind == MediaFrameSourceKind.Depth);

if (colorSource == null || depthSource == null)
{
    System.Diagnostics.Debug.WriteLine("MediaCapture doesn't have the Color and Depth streams");
    return;
}

_colorSourceId = colorSource.Info.Id;
_depthSourceId = depthSource.Info.Id;

MultiSourceMediaFrameReader を作成して初期化します。そのためには、CreateMultiSourceFrameReaderAsync を呼び出して、リーダーで使用するフレーム ソースの配列を渡します。Create and initialize the MultiSourceMediaFrameReader by calling CreateMultiSourceFrameReaderAsync and passing an array of frame sources that the reader will use. FrameArrived イベントに対するイベント ハンドラーを登録します。Register an event handler for the FrameArrived event. この例では、フレームを Image コントロールにレンダリングするために、この記事で既に説明した FrameRenderer ヘルパー クラスのインスタンスを作成します。This example creates an instance the FrameRenderer helper class, described previously in this article, to render frames to an Image control. StartAsync を呼び出して、フレーム リーダーを開始します。Start the frame reader by calling StartAsync.

この例で既に宣言した CorellationFailed イベントに対するイベント ハンドラーを登録します。Register an event handler for the CorellationFailed event declared earlier in the example. 使用中のメディア フレーム ソースのいずれかがフレームの生成を停止すると、このイベントが通知されます。We will signal this event if one of the media frame sources being used stops producing frames. 最後に、Task.Run を呼び出して、タイムアウト ヘルパー メソッドである NotifyAboutCorrelationFailure を別のスレッドで実行します。Finally, call Task.Run to call the timeout helper method, NotifyAboutCorrelationFailure, on a separate thread. このメソッドの実装は後で示します。The implementation of this method is shown later in this article.

_multiFrameReader = await mediaCapture.CreateMultiSourceFrameReaderAsync(
    new[] { colorSource, depthSource });

_multiFrameReader.FrameArrived += MultiFrameReader_FrameArrived;

_frameRenderer = new FrameRenderer(imageElement);

MultiSourceMediaFrameReaderStartStatus startStatus =
    await _multiFrameReader.StartAsync();

if (startStatus != MultiSourceMediaFrameReaderStartStatus.Success)
{
    throw new InvalidOperationException(
        "Unable to start reader: " + startStatus);
}

this.CorrelationFailed += MainPage_CorrelationFailed;
Task.Run(() => NotifyAboutCorrelationFailure(_tokenSource.Token));

FrameArrived イベントは、MultiSourceMediaFrameReader で管理されているすべてのメディア フレーム ソースで新しいフレームが利用可能になったときに発生します。The FrameArrived event is raised whenever a new frame is available from all of the media frame sources that are managed by the MultiSourceMediaFrameReader. つまりこのイベントは、最も低速なメディア ソースに合わせて発生することになります。This means that the event will be raised on the cadence of the slowest media source. 低速なソースでフレームが 1 つ生成される間に別のソースで複数のフレームが生成された場合、高速なフレームからの追加のフレームは取りこぼされます。If one source produces multiple frames in the time that a slower source produces one frame, the extra frames from the fast source will be dropped.

TryAcquireLatestFrame を呼び出して、イベントに関連付けられている MultiSourceMediaFrameReference を取得します。Get the MultiSourceMediaFrameReference associated with the event by calling TryAcquireLatestFrame. TryGetFrameReferenceBySourceId を呼び出して、各メディア フレーム ソースに関連付けられている MediaFrameReference を取得します。引数には、フレーム リーダーの初期化時に格納した ID 文字列を渡します。Get the MediaFrameReference associated with each media frame source by calling TryGetFrameReferenceBySourceId, passing in the ID strings stored when the frame reader was initialized.

ManualResetEventSlim オブジェクトの Set メソッドを呼び出して、フレームが到着したことを通知します。Call the Set method of the ManualResetEventSlim object to signal that frames have arrived. このイベントは、別のスレッドで実行中の NotifyCorrelationFailure メソッドでチェックされます。We will check this event in the NotifyCorrelationFailure method that is running in a separate thread.

最後に、時間相関メディア フレームに対して任意の処理を実行します。Finally, perform any processing on the time-correlated media frames. この例では、深度ソースからのフレームを描画するだけです。This example simply displays the frame from the depth source.

private void MultiFrameReader_FrameArrived(MultiSourceMediaFrameReader sender, MultiSourceMediaFrameArrivedEventArgs args)
{
    using (MultiSourceMediaFrameReference muxedFrame =
        sender.TryAcquireLatestFrame())
    using (MediaFrameReference colorFrame =
        muxedFrame.TryGetFrameReferenceBySourceId(_colorSourceId))
    using (MediaFrameReference depthFrame =
        muxedFrame.TryGetFrameReferenceBySourceId(_depthSourceId))
    {
        // Notify the listener thread that the frame has been received.
        _frameReceived.Set();
        _frameRenderer.ProcessFrame(depthFrame);
    }
}

NotifyCorrelationFailure ヘルパー メソッドは、フレーム リーダーの開始後に別のスレッドで実行ました。The NotifyCorrelationFailure helper method was run on a separate thread after the frame reader was started. このメソッドでは、フレーム受信イベントが通知されたかどうかをチェックします。In this method, check to see if the frame received event has been signaled. 既に説明したとおり、このイベントは、相関フレームのセットが到着するたびに FrameArrived ハンドラーで設定されます。Remember, in the FrameArrived handler, we set this event whenever a set of correlated frames arrive. アプリで定義した時間内 (適切な値は 5 秒程度) にイベントが通知されなかった場合、CancellationToken を使ってタスクが取り消されたのでなければ、いずれかのメディア フレーム ソースでフレームの読み取りが停止した可能性があります。If the event hasn't been signaled for some app-defined period of time - 5 seconds is a reasonable value - and the task wasn't cancelled using the CancellationToken, then it's likely that one of the media frame sources has stopped reading frames. この場合、通常はフレーム リーダーをシャットダウンすることになります。そこで、アプリ定義の CorrelationFailed イベントを発生させます。In this case you typically want to shut down the frame reader, so raise the app-defined CorrelationFailed event. このイベントのハンドラーでフレーム リーダーを停止し、この記事で既に説明したように、関連付けられているリソースをクリーンアップします。In the handler for this event you can stop the frame reader and clean up it's associated resources as shown previously in this article.

private void NotifyAboutCorrelationFailure(CancellationToken token)
{
    // If in 5 seconds the token is not cancelled and frame event is not signaled,
    // correlation is most likely failed.
    if (WaitHandle.WaitAny(new[] { token.WaitHandle, _frameReceived.WaitHandle }, 5000)
            == WaitHandle.WaitTimeout)
    {
        CorrelationFailed?.Invoke(this, EventArgs.Empty);
    }
}
private async void MainPage_CorrelationFailed(object sender, EventArgs e)
{
    await _multiFrameReader.StopAsync();
    _multiFrameReader.FrameArrived -= MultiFrameReader_FrameArrived;
    mediaCapture.Dispose();
    mediaCapture = null;
}

バッファー処理されたフレーム取得モードを使用して、取得したフレームのシーケンスを保持するUse buffered frame acquisition mode to preserve the sequence of acquired frames

Windows 10、バージョン 1709 以降では、MediaFrameReader またはMultiSourceMediaFrameReaderAcquisitionMode プロパティを Buffered に設定することで、フレーム ソースからアプリに渡されたフレームのシーケンスを保持できます。Starting with Windows 10, version 1709, you can set the AcquisitionMode property of a MediaFrameReader or MultiSourceMediaFrameReader to Buffered to preserve the sequence of frames passed into your app from the frame source.

mediaFrameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Buffered;

既定の取得モードである Realtime では、アプリがまだ前のフレームの FrameArrived イベントを処置している間に複数のフレームがソースから取得された場合、システムは取得された最新のフレームをアプリに送信し、バッファーで待機しているその他のフレームを破棄します。In the default acquisition mode, Realtime, if multiple frames are acquired from the source while your app is still handling the FrameArrived event for a previous frame, the system will send your app the most recently acquired frame and drop additional frames waiting in the buffer. これにより、アプリには利用可能な最新のフレームが常に提供されます。This provides your app with the most recent available frame at all times. これは、通常、リアルタイム コンピューター ビジョン アプリケーションで最も有用なモードです。This is typically the most useful mode for realtime computer vision applications.

Buffered 取得モードでは、システムがすべてのフレームをバッファーに保持し、FrameArrived イベントを通じて、フレームを受け取った順にアプリに提供します。In Buffered acquisition mode, the system will keep all frames in the buffer and provide them to your app through the FrameArrived event in the order received. 注: このモードでは、システムのフレーム用バッファーがいっぱいになった場合、アプリが前のフレームの FrameArrived イベントを完了して、バッファー領域を解放するまで、システムは新しいフレームの取得を停止します。Note that in this mode, when system's buffer for frames is filled, the system will stop acquiring new frames until your app completes the FrameArrived event for previous frames, freeing up more space in the buffer.

MediaSource を使用して、MediaPlayerElement にフレームを表示するUse MediaSource to display frames in a MediaPlayerElement

Windows、バージョン 1709 以降では、MediaFrameReader から取得したフレームを XAML ページの MediaPlayerElement コントロールに直接表示できます。Starting with Windows, version 1709, you can display frames acquired from a MediaFrameReader directly in a MediaPlayerElement control in your XAML page. これを行うには、 MediaSource.CreateFromMediaFrameSource を使用して、MediaPlayerElement に関連付けられた MediaPlayer によって直接使用できる MediaSource オブジェクトを作成します。This is achieved by using the MediaSource.CreateFromMediaFrameSource to create MediaSource object that can be used directly by a MediaPlayer associated with a MediaPlayerElement. MediaPlayerMediaPlayerElement の操作について詳しくは、「MediaPlayer を使ったオーディオとビデオの再生」をご覧ください。For detailed information on working with MediaPlayer and MediaPlayerElement, see Play audio and video with MediaPlayer.

次のコード例では、前面カメラからのフレームと背面カメラからのフレームを XAML ページに同時に表示する簡単な実装を示します。The following code examples show you a simple implementation that displays the frames from a front-facing and back-facing camera simultaneously in a XAML page.

まず、2 つの MediaPlayerElement コントロールを XAML ページに追加します。First, add two MediaPlayerElement controls to your XAML page.

<MediaPlayerElement x:Name="mediaPlayerElement1" Width="320" Height="240"/>
<MediaPlayerElement x:Name="mediaPlayerElement2" Width="320" Height="240"/>

次に、この記事で前のセクションで示した手法を使用して、前面パネルと背面パネル上のカラー カメラに対し、MediaFrameSourceInfo オブジェクトが含まれている MediaFrameSourceGroup を選択します。Next, using the techniques shown in previous sections in this article, select a MediaFrameSourceGroup that contains MediaFrameSourceInfo objects for color cameras on the front panel and back panel. なお、MediaPlayer は、カラー以外の形式 (深度データや赤外線データ) のフレームを自動的にカラー データには変換しません。Note that the MediaPlayer does not automatically convert frames from non-color formats, such as a depth or infrared data, into color data. 他の種類のセンサーを使用すると、予期しない結果が生じる場合があります。Using other sensor types may produce unexpected results.

var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var eligibleGroups = allGroups.Select(g => new
{
    Group = g,

    // For each source kind, find the source which offers that kind of media frame,
    // or null if there is no such source.
    SourceInfos = new MediaFrameSourceInfo[]
    {
        g.SourceInfos.FirstOrDefault(info => info.DeviceInformation?.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front
            && info.SourceKind == MediaFrameSourceKind.Color),
        g.SourceInfos.FirstOrDefault(info => info.DeviceInformation?.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back
            && info.SourceKind == MediaFrameSourceKind.Color)
    }
}).Where(g => g.SourceInfos.Any(info => info != null)).ToList();

if (eligibleGroups.Count == 0)
{
    System.Diagnostics.Debug.WriteLine("No source group with front and back-facing camera found.");
    return;
}

var selectedGroupIndex = 0; // Select the first eligible group
MediaFrameSourceGroup selectedGroup = eligibleGroups[selectedGroupIndex].Group;
MediaFrameSourceInfo frontSourceInfo = selectedGroup.SourceInfos[0];
MediaFrameSourceInfo backSourceInfo = selectedGroup.SourceInfos[1];

選択した MediaFrameSourceGroup を使うように MediaCapture オブジェクトを初期化します。Initialize the MediaCapture object to use the selected MediaFrameSourceGroup.

mediaCapture = new MediaCapture();

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = selectedGroup,
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
    StreamingCaptureMode = StreamingCaptureMode.Video
};
try
{
    await mediaCapture.InitializeAsync(settings);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine("MediaCapture initialization failed: " + ex.Message);
    return;
}

最後に、 MediaSource.CreateFromMediaFrameSource を呼び出し、各フレーム ソースの MediaSource を作成します。これには関連付けられた MediaFrameSourceInfo オブジェクトの Id プロパティを使用して、MediaCapture オブジェクトの FrameSources コレクションでいずれか 1 つのフレーム ソースを選択します。Finally, call MediaSource.CreateFromMediaFrameSource to create a MediaSource for each frame source by using the Id property of the associated MediaFrameSourceInfo object to select one of the frame sources in the MediaCapture object's FrameSources collection. 新しい MediaPlayer オブジェクトを初期化し、 SetMediaPlayer を呼び出して、初期化したオブジェクトを MediaPlayerElement に割り当てます。Initialize a new MediaPlayer object and assign it to a MediaPlayerElement by calling SetMediaPlayer. 次に、 Source プロパティを、新しく作成した MediaSource オブジェクトに設定します。Then set the Source property to the newly created MediaSource object.

var frameMediaSource1 = MediaSource.CreateFromMediaFrameSource(mediaCapture.FrameSources[frontSourceInfo.Id]);
mediaPlayerElement1.SetMediaPlayer(new Windows.Media.Playback.MediaPlayer());
mediaPlayerElement1.MediaPlayer.Source = frameMediaSource1;
mediaPlayerElement1.AutoPlay = true;

var frameMediaSource2 = MediaSource.CreateFromMediaFrameSource(mediaCapture.FrameSources[backSourceInfo.Id]);
mediaPlayerElement2.SetMediaPlayer(new Windows.Media.Playback.MediaPlayer());
mediaPlayerElement2.MediaPlayer.Source = frameMediaSource2;
mediaPlayerElement2.AutoPlay = true;

ビデオ プロファイルを使用してフレーム ソースを選択するUse video profiles to select a frame source

MediaCaptureVideoProfile オブジェクトによって表されるカメラ プロファイルは、特定のキャプチャ デバイスに搭載されている機能のセットを表します。これらの機能には、フレーム レートや解像度のほか、HDR キャプチャなどの高度な機能が含まれます。A camera profile, represented by a MediaCaptureVideoProfile object, represents a set of capabilities that a particular capture device provides, such as frame rates, resolutions, or advanced features like HDR capture. キャプチャ デバイスは、複数のプロファイルをサポートできるため、開発者は目的のキャプチャ シナリオに最適なプロファイルを選択できます。A capture device may support multiple profiles, allowing you to select the one that is optimized for your capture scenario. Windows 10、バージョン 1803 以降では、MediaCaptureVideoProfile を使用して、特定の機能を備えたメディア フレーム ソースを選択した上で、MediaCapture オブジェクトを初期化できます。Starting with Windows 10, version 1803, you can use MediaCaptureVideoProfile to select a media frame source with particular capabilities before initializing the MediaCapture object. 次の例のメソッドは、HDR と広色域 (WCG) をサポートするビデオ プロファイルを探して、MediaCaptureInitializationSettings オブジェクトを返します。このオブジェクトを MediaCapture の初期化に使用することで、選択したデバイスとプロファイルを使用できるようになります。The following example method looks for a video profile that supports HDR with Wide Color Gamut (WCG) and returns a MediaCaptureInitializationSettings object that can be used to initialize the MediaCapture to use the selected device and profile.

まず、MediaFrameSourceGroup.FindAllAsync を呼び出して、現在のデバイス上で利用可能なすべてのメディア フレーム ソース グループの一覧を取得します。First, call MediaFrameSourceGroup.FindAllAsync to get a list of all media frame source groups available on the current device. ループ処理によって各ソース グループで MediaCapture.FindKnownVideoProfiles を呼び出し、現在のソース グループについて、指定したプロファイル (この例では HDR/WCG 写真) をサポートしているすべてのビデオ プロファイルの一覧を取得します。Loop through each source group and call MediaCapture.FindKnownVideoProfiles to get a list of all of the video profiles for the current source group that support the specified profile, in this case HDR with WCG photo. 条件に適合するプロファイルが見つかった場合、新しい MediaCaptureInitializationSettings オブジェクトが作成され、VideoProfile が選択したプロファイルに設定されると共に、VideoDeviceId が現在のメディア フレーム ソース グループの Id プロパティに設定されます。If a profile that meets the criteria is found, create a new MediaCaptureInitializationSettings object and set the VideoProfile to the select profile and the VideoDeviceId to the Id property of the current media frame source group.

public async Task<MediaCaptureInitializationSettings> FindHdrWithWcgPhotoProfile()
{
    IReadOnlyList<MediaFrameSourceGroup> sourceGroups = await MediaFrameSourceGroup.FindAllAsync();
    MediaCaptureInitializationSettings settings = null;

    foreach (MediaFrameSourceGroup sourceGroup in sourceGroups)
    {
        // Find a device that support AdvancedColorPhoto
        IReadOnlyList<MediaCaptureVideoProfile> profileList = MediaCapture.FindKnownVideoProfiles(
                                      sourceGroup.Id,
                                      KnownVideoProfile.HdrWithWcgPhoto);

        if (profileList.Count > 0)
        {
            settings = new MediaCaptureInitializationSettings();
            settings.VideoProfile = profileList[0];
            settings.VideoDeviceId = sourceGroup.Id;
            break;
        }
    }
    return settings;
}

private void StartDeviceWatcherButton_Click(object sender, RoutedEventArgs e)
{
    var remoteCameraHelper = new RemoteCameraPairingHelper(this.Dispatcher);
}

カメラ プロファイルの使用方法について詳しくは、「カメラ プロファイル」をご覧ください。For more information on using camera profiles, see Camera profiles.