MediaFrameSourceGroup を使用して複数のソースからキャプチャする

この記事では、複数のソースから同時にビデオをキャプチャして、複数のビデオ トラックが埋め込まれている 1 つのファイルを作成する方法について説明します。 RS3 以降では、1 つの MediaEncodingProfile に対して複数の VideoStreamDescriptor オブジェクトを指定できます。 これにより、複数のストリームを同時に 1 つのファイルにエンコードすることができます。 この操作でエンコードされたビデオ ストリームは、1 つの MediaFrameSourceGroup に含める必要があります。このクラスでは、現在のデバイスが備えている、同時使用が可能なカメラのセットを指定します。

MediaFrameReader クラスと共に MediaFrameSourceGroup を使用して、複数のカメラを利用するリアルタイムなコンピューター ビジョンのシナリオを実現する方法については、「MediaFrameReader を使ったメディア フレームの処理」をご覧ください。

この記事の残りの部分では、2 台のカラー カメラでビデオを録画し、複数のビデオ トラックが埋め込まれている 1 つのファイルを作成する手順について説明します。

利用可能なセンサー グループを検索する

MediaFrameSourceGroup は、同時にアクセスできるフレーム ソース (通常はカメラ) のコレクションを表します。 利用可能なフレーム ソース グループのセットはデバイスごとに異なります。そのため、この例の最初の手順では、利用可能なフレーム ソース グループの一覧を取得し、シナリオで必要となるカメラを含んでいるグループを探します。この例では、2 台のカラー カメラが必要となります。

MediaFrameSourceGroup.FindAllAsync メソッドは、現在のデバイスで利用可能なすべてのソース グループを返します。 返された各 MediaFrameSourceGroup には、グループ内にある各フレーム ソースを記述する MediaFrameSourceInfo オブジェクトの一覧が含まれています。 Linq クエリを使用して、2 台のカラー カメラ (前面カメラと背面カメラ) を含んでいるソース グループを検索します。 各カラー カメラについて選択した MediaFrameSourceGroupMediaFrameSourceInfo を含んでいる匿名オブジェクトが返されます。 Linq 構文を使用する代わりに、各グループをループ処理してから、各 MediaFrameSourceInfo をループ処理し、要件を満たしているグループを検索することもできます。

すべてのデバイスが 2 台のカラー カメラを含むソース グループを保持しているわけではありません。そのため、ビデオをキャプチャする前に、ソース グループが検出済みであるかどうかを確認する必要があります

var sensorGroups = await MediaFrameSourceGroup.FindAllAsync();

var foundGroup = sensorGroups.Select(g => new
{
    group = g,
    color1 = g.SourceInfos.Where(info => info.SourceKind == MediaFrameSourceKind.Color && info.DeviceInformation.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front).FirstOrDefault(),
    color2 = g.SourceInfos.Where(info => info.SourceKind == MediaFrameSourceKind.Color && info.DeviceInformation.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back).FirstOrDefault()
}).Where(g => g.color1 != null && g.color2 != null).FirstOrDefault();

if (foundGroup == null)
{
    Debug.WriteLine("No groups found.");
    return;
}

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

MediaCapture クラスは、UWP アプリでのほとんどのオーディオ、ビデオ、および写真のキャプチャ操作で使用されるプライマリ クラスです。 初期化パラメーターを含んでいる MediaCaptureInitializationSettings オブジェクトを渡して InitializeAsync を呼び出し、オブジェクトを初期化します。 この例では、指定した設定は SourceGroup プロパティのみで、前のコード例で取得された MediaFrameSourceGroup に設定されています。

メディアをキャプチャする場合に MediaCapture や他の UWP アプリの機能を使用して実行できるその他の操作については、「カメラ」をご覧ください。

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = foundGroup.group
};

mediaCapture = new MediaCapture();
await mediaCapture.InitializeAsync(settings);

MediaEncodingProfile を作成する

MediaEncodingProfile クラスは、キャプチャしたオーディオやビデオをファイルに書き込む際のエンコードの方法をメディア キャプチャ パイプラインに伝えます。 一般的なキャプチャやコード変換のシナリオでは、このクラスによって、一般的なプロファイル (CreateAviCreateMp3 など) を作成するための一連の静的メソッドが提供されます。 この例では、Mpeg4 コンテナーと H264 ビデオ エンコードを使用して、エンコード プロファイルを手動で作成します。 ビデオ エンコードの設定は、VideoEncodingProperties オブジェクトを使用して指定します。 このシナリオで使用される各カラー カメラに対して、VideoStreamDescriptor オブジェクトを構成します。 記述子は、エンコードを指定する VideoEncodingProperties オブジェクトを使用して作成されます。 VideoStreamDescriptorLabel プロパティは、ストリームにキャプチャされるメディア フレーム ソースの ID に設定する必要があります。 この方法によって、キャプチャ パイプラインは、どのストリーム記述子とエンコード プロパティが各カメラで使用されるかを識別します。 フレーム ソースの ID は、MediaFrameSourceGroup が選択されたときに、MediaFrameSourceInfo オブジェクト (前のセクションをご覧ください) によって公開されます。

Windows 10, version 1709 以降では、SetVideoTracks を呼び出すことによって、MediaEncodingProfile に対して複数のエンコード プロパティを設定できます。 ビデオ ストリーム記述子の一覧は、GetVideoTracks を呼び出して取得することができます。 1 つのストリーム記述子を格納する Video プロパティを設定した場合、SetVideoTracks を呼び出して設定した記述子の一覧は、指定した 1 つの記述子を含んでいる一覧に置き換えられます。

var profile = new MediaEncodingProfile();
profile.Container = new ContainerEncodingProperties();
profile.Container.Subtype = MediaEncodingSubtypes.Mpeg4;

List<VideoStreamDescriptor> streams = new List<VideoStreamDescriptor>();

var encodeProps = VideoEncodingProperties.CreateH264();
encodeProps.Subtype = MediaEncodingSubtypes.H264;
var stream1Desc = new VideoStreamDescriptor(encodeProps);
stream1Desc.Label = foundGroup.color1.Id;
streams.Add(stream1Desc);

var encodeProps2 = VideoEncodingProperties.CreateH264();
encodeProps2.Subtype = MediaEncodingSubtypes.H264;
var stream2Desc = new VideoStreamDescriptor(encodeProps2);
stream2Desc.Label = foundGroup.color2.Id;
streams.Add(stream2Desc);

profile.SetVideoTracks(streams);
profile.Audio = null;

メディア ファイルでタイミングが設定されたメタデータをエンコードする

Windows 10、バージョン 1803 以降では、オーディオとビデオに加え、タイミングが設定されたメタデータを、サポートされている形式のメディア ファイルにエンコードできます。 たとえば、GoPro メタデータ (gpmd) を MP4 ファイルに格納して、ビデオ ストリームに関連付けられた地理的位置情報を伝達できます。

メタデータのエンコードでは、オーディオやビデオのエンコードと同様のパターンが使用されます。 TimedMetadataEncodingProperties クラスが、タイプ、サブタイプに加え、メタデータのエンコード プロパティを記述します (ビデオの VideoEncodingProperties に相当します)。 TimedMetadataStreamDescriptor は、メタデータ ストリームを識別します (ビデオ ストリームの VideoStreamDescriptor に相当します)。

次の例では、TimedMetadataStreamDescriptorオブジェクトを初期化する方法を示します。 まず、TimedMetadataEncodingProperties オブジェクトが作成されると、Subtype が、ストリームに含めるメタデータのタイプを識別する GUID に設定されます。 この例では、GoPro メタデータ (gpmd) の GUID が使用されています。 SetFormatUserData メソッドが呼び出されて形式固有のデータが設定されます。 MP4 ファイルの場合、形式固有のデータが SampleDescription ボックス (stsd) に格納されます。 次に、エンコード プロパティから、新しいTimedMetadataStreamDescriptor が作成されます。 Label プロパティと Name プロパティが、エンコードするストリームの ID に設定されます。

           TimedMetadataEncodingProperties encodingProperties = new TimedMetadataEncodingProperties
           {
               Subtype = "{67706D64-BF10-48B4-BC18-593DC1DB950F}"
           };

           byte[] streamDescriptionData = GetStreamDescriptionDataForGpmdEncodingSubtype();
           encodingProperties.SetFormatUserData(streamDescriptionData);

           TimedMetadataStreamDescriptor descriptor = new TimedMetadataStreamDescriptor(encodingProperties)
           {
               Name = "GPS Info",
               Label = "GPS Info"
           };

MediaEncodingProfile.SetTimedMetadataTracks を呼び出して、メタデータ ストリーム記述子をエンコード プロファイルに追加します。 次の例では、2 つのビデオ ストリーム記述子、1 つのオーディオ ストリーム記述子、1 つのタイミングが設定されたメタデータ ストリーム記述子を受け取り、ストリームのエンコードに使用できる MediaEncodingProfile を返すヘルパー メソッドを示します。

public MediaEncodingProfile CreateProfileForTranscoder(VideoStreamDescriptor videoStream1, VideoStreamDescriptor videoStream2, AudioStreamDescriptor audioStream, TimedMetadataStreamDescriptor timedMetadataStream)
{
    ContainerEncodingProperties container = new ContainerEncodingProperties()
    {
        Subtype = MediaEncodingSubtypes.Mpeg4
    };

    MediaEncodingProfile profile = new MediaEncodingProfile()
    {
        Container = container
    };


    VideoStreamDescriptor encodingVideoStream1 = videoStream1.Copy();
    encodingVideoStream1.EncodingProperties.Subtype = MediaEncodingSubtypes.H264;
    encodingVideoStream1.Label = videoStream1.Name;

    VideoStreamDescriptor encodingVideoStream2 = videoStream2.Copy();
    encodingVideoStream2.EncodingProperties.Subtype = MediaEncodingSubtypes.H264;
    encodingVideoStream2.Label = videoStream2.Name;

    AudioStreamDescriptor encodingAudioStream = audioStream.Copy();
    encodingAudioStream.EncodingProperties.Subtype = MediaEncodingSubtypes.Ac3;
    encodingAudioStream.Label = audioStream.Name;

    TimedMetadataStreamDescriptor encodingTimedMetadataStream = timedMetadataStream.Copy();

    profile.SetTimedMetadataTracks(new TimedMetadataStreamDescriptor[] { encodingTimedMetadataStream });
    profile.SetVideoTracks(new VideoStreamDescriptor[] { encodingVideoStream1, encodingVideoStream2 });
    profile.SetAudioTracks(new AudioStreamDescriptor[] { encodingAudioStream });
    return profile;
}

マルチストリームの MediaEncodingProfile を使用して録画する

この例の最期の手順では、キャプチャしたメディアが書き込まれる StorageFile と前のコード例で作成した MediaEncodingProfile を渡して StartRecordToStorageFileAsync を呼び出し、ビデオ キャプチャを開始します。 数秒待機した後で、StopRecordAsync が呼び出され録画が停止します。

var recordFile = await Windows.Storage.KnownFolders.CameraRoll.CreateFileAsync("record.mp4", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
await mediaCapture.StartRecordToStorageFileAsync(profile, recordFile);
await Task.Delay(8000);
await mediaCapture.StopRecordAsync();

操作が完了すると、各カメラからキャプチャしたビデオを含むビデオ ファイルが作成されます。このファイルでは、それぞれのビデオが個別のストリームとしてエンコードされています。 複数のビデオ トラックが含まれているメディア ファイルを再生する方法について詳しくは、「メディア項目、プレイリスト、トラック」をご覧ください。