Captura desde varios orígenes con MediaFrameSourceGroup

En este artículo se muestra cómo capturar vídeo de varios orígenes simultáneamente a un único archivo con varias pistas de vídeo incrustadas. A partir de RS3, puede especificar varios objetos VideoStreamDescriptor para un solo Objeto MediaEncodingProfile. Esto le permite codificar varias secuencias simultáneamente en un único archivo. Las secuencias de vídeo codificadas en esta operación deben incluirse en un único MediaFrameSourceGroup que especifica un conjunto de cámaras en el dispositivo actual que se pueden usar al mismo tiempo.

Para obtener información sobre el uso de MediaFrameSourceGroup con la clase MediaFrameReader para habilitar escenarios de Computer Vision en tiempo real que usan varias cámaras, vea Procesar fotogramas multimedia con MediaFrameReader.

El resto de este artículo le guiará por los pasos para grabar vídeo de dos cámaras de color a un solo archivo con varias pistas de vídeo.

Búsqueda de grupos de sensores disponibles

MediaFrameSourceGroup representa una colección de orígenes de fotogramas, normalmente cámaras, a las que se puede acceder simulatanemente. El conjunto de grupos de origen de fotogramas disponibles es diferente para cada dispositivo, por lo que el primer paso de este ejemplo es obtener la lista de grupos de origen de fotogramas disponibles y buscar uno que contenga las cámaras necesarias para el escenario, que en este caso requiere dos cámaras de color.

El método MediaFrameSourceGroup.FindAllAsync devuelve todos los grupos de origen disponibles en el dispositivo actual. Cada objeto MediaFrameSourceGroup devuelto tiene una lista de objetos MediaFrameSourceInfo que describen cada origen de fotogramas del grupo. Se usa una consulta Linq para buscar un grupo de origen que contiene dos cámaras de color, una en el panel frontal y otra en la parte posterior. Se devuelve un objeto anónimo que contiene el objeto MediaFrameSourceGroup seleccionado y mediaFrameSourceInfo para cada cámara de color. En lugar de usar la sintaxis de Linq, podría recorrer en bucle cada grupo y, a continuación, cada MediaFrameSourceInfo para buscar un grupo que cumpla sus requisitos.

Tenga en cuenta que no todos los dispositivos contendrán un grupo de origen que contenga dos cámaras de color, por lo que debe comprobar que se encontró un grupo de origen antes de intentar capturar vídeo.

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;
}

Inicializar el objeto MediaCapture

La clase MediaCapture es la clase principal que se usa para la mayoría de las operaciones de captura de audio, vídeo y fotos en aplicaciones para UWP. Inicialice el objeto llamando a InitializeAsync, pasando un objeto MediaCaptureInitializationSettings que contiene parámetros de inicialización. En este ejemplo, el único valor especificado es la propiedad SourceGroup , que se establece en el MediaFrameSourceGroup que se recuperó en el ejemplo de código anterior.

Para obtener información sobre otras operaciones que puedes realizar con MediaCapture y otras características de la aplicación para UWP para capturar medios, consulta Cámara.

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

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

Crear un archivo MediaEncodingProfile

La clase MediaEncodingProfile indica a la canalización de captura multimedia cómo se debe codificar el audio y el vídeo capturados a medida que se escriben en un archivo. Para escenarios típicos de captura y transcodificación, esta clase proporciona un conjunto de métodos estáticos para crear perfiles comunes, como CreateAvi y CreateMp3. En este ejemplo, se crea manualmente un perfil de codificación mediante un contenedor Mpeg4 y una codificación de vídeo H264. La configuración de codificación de vídeo se especifica mediante un objeto VideoEncodingProperties . Para cada cámara de color usada en este escenario, se configura un objeto VideoStreamDescriptor . El descriptor se construye con el objeto VideoEncodingProperties que especifica la codificación. La propiedad Label del Objeto VideoStreamDescriptor debe establecerse en el identificador del origen de fotogramas multimedia que se capturará en la secuencia. Así es como la canalización de captura sabe qué propiedades de codificación y descriptor de secuencia se deben usar para cada cámara. El identificador del origen del marco se expone mediante los objetos MediaFrameSourceInfo que se encontraron en la sección anterior, cuando se seleccionó un mediaFrameSourceGroup .

A partir de Windows 10, versión 1709, puedes establecer varias propiedades de codificación en un MediaEncodingProfile llamando a SetVideoTracks. Puede recuperar la lista de descriptores de secuencia de vídeo llamando a GetVideoTracks. Tenga en cuenta que si establece la propiedad Video , que almacena un único descriptor de secuencia, la lista de descriptores que establezca llamando a SetVideoTracks se reemplazará por una lista que contenga el descriptor único que especificó.

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;

Codificación de metadatos con tiempo en archivos multimedia

A partir de Windows 10, versión 1803, además de audio y vídeo, puedes codificar metadatos con tiempo en un archivo multimedia para el que se admita el formato de datos. Por ejemplo, los metadatos de GoPro (gpmd) se pueden almacenar en archivos MP4 para transmitir la ubicación geográfica correlacionada con una secuencia de vídeo.

Los metadatos de codificación usan un patrón que es paralelo a la codificación de audio o vídeo. La clase TimedMetadataEncodingProperties describe las propiedades de tipo, subtipo y codificación de los metadatos, como VideoEncodingProperties , para vídeo. TimedMetadataStreamDescriptor identifica una secuencia de metadatos, igual que videoStreamDescriptor hace para secuencias de vídeo.

En el ejemplo siguiente se muestra cómo inicializar un objeto TimedMetadataStreamDescriptor . En primer lugar, se crea un objeto TimedMetadataEncodingProperties y el Subtype se establece en un GUID que identifica el tipo de metadatos que se incluirán en la secuencia. En este ejemplo se usa el GUID para los metadatos de GoPro (gpmd). Se llama al método SetFormatUserData para establecer datos específicos del formato. En el caso de los archivos MP4, los datos específicos del formato se almacenan en el cuadro SampleDescription (stsd). A continuación, se crea un nuevo TimedMetadataStreamDescriptor a partir de las propiedades de codificación. Las propiedades Label y Name se establecen para identificar la secuencia que se va a codificar.

           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"
           };

Llame a MediaEncodingProfile.SetTimedMetadataTracks para agregar el descriptor de secuencia de metadatos al perfil de codificación. En el ejemplo siguiente se muestra un método auxiliar que toma dos descriptores de secuencia de vídeo, un descriptor de secuencia de audio y un descriptor de secuencia de metadatos con tiempo y devuelve un mediaEncodingProfile que se puede usar para codificar las secuencias.

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;
}

Registro mediante la secuencia múltiple MediaEncodingProfile

El último paso de este ejemplo es iniciar la captura de vídeo mediante una llamada a StartRecordToStorageFileAsync, pasando el storageFile al que se escribe el medio capturado y el archivo MediaEncodingProfile creado en el ejemplo de código anterior. Después de esperar unos segundos, la grabación se detiene con una llamada a 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();

Una vez completada la operación, se creará un archivo de vídeo que contiene el vídeo capturado de cada cámara codificado como una secuencia independiente dentro del archivo. Para obtener información sobre cómo reproducir archivos multimedia que contienen varias pistas de vídeo, consulta Elementos multimedia, listas de reproducción y pistas.