使用 MediaFrameSourceGroup 從多個來源擷取

本文說明如何同時將多個來源的影片擷取到具有多個嵌入影片軌道的單一檔案中。 從 RS3 開始,您可以為單一 MediaEncodingProfile 指定多個 VideoStreamDescriptor 物件。 這可讓您將多個串流同時編碼成單一檔案。 在此作業中編碼的影片串流必須包含在單一 MediaFrameSourceGroup 中,該 MediaFrameSourceGroup 指定目前裝置上可同時使用的一組相機。

有關將 MediaFrameSourceGroupMediaFrameReader 類別結合使用以啟用使用多個相機的即時電腦視覺案例的資訊,請參閱使用 MediaFrameReader 處理媒體畫面

本文的其餘部分將逐步引導您完成將影片從兩個彩色相機錄製到具有多個影片播放軌的單一檔案的步驟。

尋找可用的感應器群組

MediaFrameSourceGroup 表示可以同時存取的影片來源 (通常是相機) 的集合。 每個裝置的可用畫面來源組集合都不同,因此本範例中的第一步是獲取可用畫面來源群組的清單,並找到包含案例所需相機的畫面來源群組,在本案例中需要兩台彩色相機。

MediaFrameSourceGroup.FindAllAsync 方法傳回目前裝置上可用的所有來源群組。 每個傳回的 MediaFrameSourceGroup 都有一個 MediaFrameSourceInfo 物件清單,用於描述群組中的每個畫面來源。 Linq 查詢可用來尋找來源群組,其中包含兩個彩色相機,一個在前面板上,另一個位於背面。 傳回匿名物件,其中包含每個彩色相機的已選取 MediaFrameSourceGroupMediaFrameSourceInfo。 您可以不使用 Linq 語法,而是改為迴圈執行每個群組,然後改為迴圈執行每個 MediaFrameSourceInfo 以尋找滿足您要求的群組。

請注意,並非所有裝置都會包含包含兩個彩色相機的來源群組,因此您應該先檢查,確定在嘗試擷取影片之前找到來源群組。

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 應用程式中大多數音訊、影片和相片擷取作業的主要類別。 透過呼叫 InitializeAsync 並傳入包含初始化參數的 MediaCaptureInitializationSettings 物件來初始化該物件。 在此範例中,唯一指定的設定是 SourceGroup 屬性,該屬性設定為在前面的程式碼範例中擷取到的 MediaFrameSourceGroup

有關可以使用 MediaCapture 和其他 UWP 應用程式功能來擷取媒體執行的其他操作的資訊,請參閱 Camera

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

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

建立 MediaEncodingProfile

MediaEncodingProfile 類別告訴媒體擷取管道在將擷取的音訊和影片寫入檔案時應如何編碼。 對於典型的擷取和轉碼案例,此類別提供了一組用於建立通用設定檔的靜態方法,例如 CreateAviCreateMp3。 在此範例中,會使用 Mpeg4 容器和 H264 影片編碼手動建立編碼設定檔。 影片編碼設定使用 VideoEncodingProperties 物件指定。 對於此案例中使用的每個彩色相機,都會設定一個 VideoStreamDescriptor 物件。 此描述項是使用指定編碼的 VideoEncodingProperties 物件建構的。 VideoStreamDescriptorLabel 屬性必須設定為將擷取到串流中的媒體畫面來源的 ID。 這就是擷取管線知道每個相機應該使用哪一個串流描述項和編碼屬性的方式。 當選取 MediaFrameSourceGroup 時,畫面來源的 ID 會由上一節找到的 MediaFrameSourceInfo 物件所公開。

從 Windows 10 版本 1709 開始,可以透過呼叫 SetVideoTracksMediaEncodingProfile 上設定多個編碼屬性。 您可以透過呼叫 GetVideoTracks 來擷取影片串流描述項清單。 請注意,如果您設定儲存單一串流描述項的 Video 屬性,則透過呼叫 SetVideoTracks 設定的描述項清單將替換為包含您指定的單一描述項的清單。

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,該 GUID 能識別將包含在串流中的中繼資料的類型。 此範例使用 GoPro 中繼資料的 GUID (gpmd)。 呼叫 SetFormatUserData 方法來設定特定於格式的資料。 針對 MP4 檔案,格式特定資料會儲存在 SampleDescription 方塊 (stsd) 中。 接下來,根據編碼屬性建立一個新的 TimedMetadataStreamDescriptor。 設定 LabelName 屬性來識別要編碼的串流。

           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 將中繼資料串流描述項新增至編碼設定檔。 以下範例顯示了一種協助程式方法,該方法採用兩個影片串流描述項、一個音訊串流描述項和一個計時中繼資料流描述項,並傳回可用於對串流進行編碼的 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 進行錄製

此範例的最後一步是透過呼叫 StartRecordToStorageFileAsync、傳入寫入擷取媒體的 StorageFileMediaEncodingProfile 來啟動影片擷取。 等待幾秒鐘後,透過呼叫 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();

作業完成後,將建立一個影片檔案,其中包含從每個相機擷取的影片,編碼為檔案中的單獨串流。 有關播放包含多個影片軌道的媒體檔案的資訊,請參閱媒體項目、播放清單和軌道