Establecer el formato, la resolución y la velocidad de fotogramas para MediaCaptureSet format, resolution, and frame rate for MediaCapture

En este artículo se muestra cómo usar la interfaz IMediaEncodingProperties para establecer la resolución y la velocidad de fotogramas de la secuencia de vista previa de la cámara y las fotos y el vídeo capturados.This article shows you how to use the IMediaEncodingProperties interface to set the resolution and frame rate of the camera preview stream and captured photos and video. También se muestra cómo asegurarse de que la relación de aspecto de la secuencia de vista previa coincida con la de la secuencia multimedia capturada.It also shows how to ensure that the aspect ratio of the preview stream matches that of the captured media.

Los perfiles de cámara ofrecen una forma más avanzada de descubrir y establecer las propiedades de la secuencia de la cámara, pero no se admiten en todos los dispositivos.Camera profiles offer a more advanced way of discovering and setting the stream properties of the camera, but they are not supported for all devices. Para obtener más información, consulte perfiles de cámara.For more information, see Camera profiles.

El código de este artículo es una adaptación de la muestra de CameraResolution.The code in this article was adapted from the CameraResolution sample. Puedes descargar la muestra para ver el código usado en contexto o para emplearla como punto de partida para tu propia aplicación.You can download the sample to see the code used in context or to use the sample as a starting point for your own app.

Nota

Este artículo se basa en los conceptos y el código analizados en Captura básica de fotos, audio y vídeo con MediaCapture, donde se describen los pasos para implementar la captura básica de fotos y vídeo.This article builds on concepts and code discussed in Basic photo, video, and audio capture with MediaCapture, which describes the steps for implementing basic photo and video capture. Se recomienda que te familiarices con el patrón de captura de multimedia básico de ese artículo antes de pasar a escenarios más avanzados de captura.It is recommended that you familiarize yourself with the basic media capture pattern in that article before moving on to more advanced capture scenarios. El código que encontrarás en este artículo se ha agregado suponiendo que la aplicación ya tiene una instancia de MediaCapture inicializada correctamente.The code in this article assumes that your app already has an instance of MediaCapture that has been properly initialized.

Clase auxiliar de propiedades de codificación multimediaA media encoding properties helper class

Crear una clase auxiliar simple para encapsular la funcionalidad de la interfaz IMediaEncodingProperties facilita la selección de un conjunto de propiedades de codificación que cumplan criterios particulares.Creating a simple helper class to wrap the functionality of the IMediaEncodingProperties interface makes it easier to select a set of encoding properties that meet particular criteria. Esta clase auxiliar resulta especialmente útil debido al comportamiento de la característica de propiedades de codificación siguiente:This helper class is particularly useful due to the following behavior of the encoding properties feature:

ADVERTENCIA    de El método VideoDeviceController. GetAvailableMediaStreamProperties toma un miembro de la enumeración MediaStreamType , como VideoRecord o Photo, y devuelve una lista de objetos ImageEncodingProperties o VideoEncodingProperties que transmiten la configuración de la codificación de la secuencia, como la resolución de la fotografía o el vídeo capturado.Warning   The VideoDeviceController.GetAvailableMediaStreamProperties method takes a member of the MediaStreamType enumeration, such as VideoRecord or Photo, and returns a list of either ImageEncodingProperties or VideoEncodingProperties objects that convey the stream encoding settings, such as the resolution of the captured photo or video. Los resultados de la llamada a GetAvailableMediaStreamProperties puede incluir ImageEncodingProperties o VideoEncodingProperties, independientemente de qué valor MediaStreamType se especifique.The results of calling GetAvailableMediaStreamProperties may include ImageEncodingProperties or VideoEncodingProperties regardless of what MediaStreamType value is specified. Por este motivo, siempre debe comprobar el tipo de cada valor devuelto y convertirlo al tipo apropiado antes de intentar acceder a cualquiera de los valores de propiedad.For this reason, you should always check the type of each returned value and cast it to the appropriate type before attempting to access any of the property values.

La clase auxiliar que se define a continuación controla la comprobación y la conversión del tipo de ImageEncodingProperties o VideoEncodingProperties para que el código de la aplicación no tenga que distinguir entre los dos tipos.The helper class defined below handles the type checking and casting for ImageEncodingProperties or VideoEncodingProperties so that your app code doesn't need to distinguish between the two types. Además, la clase auxiliar expone las propiedades de la relación de aspecto de las propiedades, la velocidad de fotogramas (solo de las propiedades de codificación de vídeo) y un nombre descriptivo que facilita la visualización de las propiedades de codificación en la interfaz de usuario de la aplicación.In addition to this, the helper class exposes properties for the aspect ratio of the properties, the frame rate (for video encoding properties only), and a friendly name that makes it easier to display the encoding properties in the app's UI.

Debes incluir el espacio de nombres Windows.Media.MediaProperties en el archivo de origen de la clase auxiliar.You must include the Windows.Media.MediaProperties namespace in the source file for the helper class.

using Windows.Media.MediaProperties;
using Windows.Media.Capture.Frames;
class StreamPropertiesHelper
{
    private IMediaEncodingProperties _properties;

    public StreamPropertiesHelper(IMediaEncodingProperties properties)
    {
        if (properties == null)
        {
            throw new ArgumentNullException(nameof(properties));
        }

        // This helper class only uses VideoEncodingProperties or VideoEncodingProperties
        if (!(properties is ImageEncodingProperties) && !(properties is VideoEncodingProperties))
        {
            throw new ArgumentException("Argument is of the wrong type. Required: " + typeof(ImageEncodingProperties).Name
                + " or " + typeof(VideoEncodingProperties).Name + ".", nameof(properties));
        }

        // Store the actual instance of the IMediaEncodingProperties for setting them later
        _properties = properties;
    }

    public uint Width
    {
        get
        {
            if (_properties is ImageEncodingProperties)
            {
                return (_properties as ImageEncodingProperties).Width;
            }
            else if (_properties is VideoEncodingProperties)
            {
                return (_properties as VideoEncodingProperties).Width;
            }

            return 0;
        }
    }

    public uint Height
    {
        get
        {
            if (_properties is ImageEncodingProperties)
            {
                return (_properties as ImageEncodingProperties).Height;
            }
            else if (_properties is VideoEncodingProperties)
            {
                return (_properties as VideoEncodingProperties).Height;
            }

            return 0;
        }
    }

    public uint FrameRate
    {
        get
        {
            if (_properties is VideoEncodingProperties)
            {
                if ((_properties as VideoEncodingProperties).FrameRate.Denominator != 0)
                {
                    return (_properties as VideoEncodingProperties).FrameRate.Numerator / 
                        (_properties as VideoEncodingProperties).FrameRate.Denominator;
                }
           }

            return 0;
        }
    }

    public double AspectRatio
    {
        get { return Math.Round((Height != 0) ? (Width / (double)Height) : double.NaN, 2); }
    }

    public IMediaEncodingProperties EncodingProperties
    {
        get { return _properties; }
    }

    public string GetFriendlyName(bool showFrameRate = true)
    {
        if (_properties is ImageEncodingProperties ||
            !showFrameRate)
        {
            return Width + "x" + Height + " [" + AspectRatio + "] " + _properties.Subtype;
        }
        else if (_properties is VideoEncodingProperties)
        {
            return Width + "x" + Height + " [" + AspectRatio + "] " + FrameRate + "FPS " + _properties.Subtype;
        }

        return String.Empty;
    }
    
}

Determinar si las secuencias de vista previa y captura son independientesDetermine if the preview and capture streams are independent

En algunos dispositivos, se usa el mismo pin de hardware para las secuencias de vista previa y captura.On some devices, the same hardware pin is used for both preview and capture streams. En estos dispositivos, al establecer las propiedades de codificación de uno también se establecen las del otro.On these devices, setting the encoding properties of one will also set the other. En los dispositivos que usan pines de hardware diferentes para la vista previa y la captura, las propiedades pueden establecerse por separado para cada secuencia.On devices that use different hardware pins for capture and preview, the properties can be set for each stream independently. Usa el siguiente código para determinar si las capturas de vista previa y captura son independientes.Use the following code to determine if the preview and capture streams are independent. Debes ajustar la interfaz de usuario para habilitar o deshabilitar la configuración de las secuencias de forma independiente en función del resultado de esta prueba.You should adjust your UI to enable or disable the setting of the streams independently based on the result of this test.

private void CheckIfStreamsAreIdentical()
{
    if (_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
        _mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
    {
        ShowMessageToUser("Preview and video streams for this device are identical. Changing one will affect the other");
    }
}

Obtener una lista de propiedades de secuencia disponiblesGet a list of available stream properties

Obtén una lista de las propiedades de secuencia disponibles para un dispositivo de captura obteniendo el VideoDeviceController del objeto MediaCapture de tu aplicación y, después, llama a GetAvailableMediaStreamProperties y pasa uno de los valores de MediaStreamType, VideoPreview, VideoRecord o Photo.Get a list of the available stream properties for a capture device by getting the VideoDeviceController for your app's MediaCapture object and then calling GetAvailableMediaStreamProperties and passing in one of the MediaStreamType values, VideoPreview, VideoRecord, or Photo. En este ejemplo, se usa la sintaxis de Linq para crear una lista de objetos StreamPropertiesHelper, que se definió anteriormente en este artículo, para cada uno de los valores de IMediaEncodingProperties devueltos de GetAvailableMediaStreamProperties.In this example, Linq syntax is used to create a list of StreamPropertiesHelper objects, defined previously in this article, for each of the IMediaEncodingProperties values returned from GetAvailableMediaStreamProperties. En primer lugar, en este ejemplo se usan los métodos de extensión Linq para ordenar las propiedades devueltas primero según la resolución y, luego, según la velocidad de fotogramas.This example first uses Linq extension methods to order the returned properties based first on resolution and then on frame rate.

Si la aplicación tiene requisitos de velocidad de fotogramas o de resolución específicos, puedes seleccionar un conjunto de propiedades de codificación multimedia mediante programación.If your app has specific resolution or frame rate requirements, you can select a set of media encoding properties programmatically. En su lugar, una aplicación de cámara típica expondrá la lista de propiedades disponibles en la interfaz de usuario y permitirá que el usuario seleccione la configuración que desee.A typical camera app will instead expose the list of available properties in the UI and allow the user to select their desired settings. Se crea un ComboBoxItem para cada elemento de la lista de objetos StreamPropertiesHelper de la lista.A ComboBoxItem is created for each item in the list of StreamPropertiesHelper objects in the list. El contenido se establece en el nombre descriptivo devuelto por la clase auxiliar y la etiqueta se establece en la clase auxiliar en sí, para que se pueda usar más adelante con el fin de recuperar las propiedades de codificación asociadas.The content is set to the friendly name returned by the helper class and the tag is set to the helper class itself so it can be used later to retrieve the associated encoding properties. Cada objeto ComboBoxItem se agrega posteriormente al objeto ComboBox que se pasa al método.Each ComboBoxItem is then added to the ComboBox passed into the method.

private void PopulateStreamPropertiesUI(MediaStreamType streamType, ComboBox comboBox, bool showFrameRate = true)
{
    // Query all properties of the specified stream type 
    IEnumerable<StreamPropertiesHelper> allStreamProperties = 
        _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(streamType).Select(x => new StreamPropertiesHelper(x));

    // Order them by resolution then frame rate
    allStreamProperties = allStreamProperties.OrderByDescending(x => x.Height * x.Width).ThenByDescending(x => x.FrameRate);

    // Populate the combo box with the entries
    foreach (var property in allStreamProperties)
    {
        ComboBoxItem comboBoxItem = new ComboBoxItem();
        comboBoxItem.Content = property.GetFriendlyName(showFrameRate);
        comboBoxItem.Tag = property;
        comboBox.Items.Add(comboBoxItem);
    }
}

Establecer las propiedades de secuencia deseadasSet the desired stream properties

Indica al controlador de dispositivo de vídeo que use tus propiedades de codificación deseadas, para lo cual debe llamar a SetMediaStreamPropertiesAsync y pasar el valor MediaStreamType que indica si se deben establecer las propiedades de foto, vídeo o vista previa.Tell the video device controller to use your desired encoding properties by calling SetMediaStreamPropertiesAsync, passing in the MediaStreamType value indicating whether the photo, video, or preview properties should be set. En este ejemplo se establecen las propiedades de codificación solicitadas cuando el usuario selecciona un elemento en uno de los objetos ComboBox que se rellenan con el método auxiliar PopulateStreamPropertiesUI.This example sets the requested encoding properties when the user selects an item in one of the ComboBox objects populated with the PopulateStreamPropertiesUI helper method.

private async void PreviewSettings_Changed(object sender, RoutedEventArgs e)
{
    if (_isPreviewing)
    {
        var selectedItem = (sender as ComboBox).SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, encodingProperties);
    }
}
private async void PhotoSettings_Changed(object sender, RoutedEventArgs e)
{
    if (_isPreviewing)
    {
        var selectedItem = (sender as ComboBox).SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, encodingProperties);
    }
}
private async void VideoSettings_Changed(object sender, RoutedEventArgs e)
{
    if (_isPreviewing)
    {
        var selectedItem = (sender as ComboBox).SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, encodingProperties);
    }
}

Coincidir con la relación de aspecto de las secuencias de vista previa y capturaMatch the aspect ratio of the preview and capture streams

Una aplicación de cámara típica proporcionará la interfaz de usuario para el usuario de modo que pueda seleccionar la resolución de captura de vídeo o foto, pero establecerá mediante programación la resolución de vista previa.A typical camera app will provide UI for the user to select the video or photo capture resolution but will programmatically set the preview resolution. Hay algunas estrategias diferentes para seleccionar la mejor resolución de secuencia de vista previa de la aplicación:There are a few different strategies for selecting the best preview stream resolution for your app:

  • Selecciona la mayor resolución de vista previa disponible, dejando que el marco de trabajo de la interfaz de usuario realice la escala necesaria para la vista previa.Select the highest available preview resolution, letting the UI framework perform any necessary scaling of the preview.

  • Selecciona la resolución de vista previa más cercana a la resolución de captura para que la vista previa muestre la representación más cercana al contenido multimedia capturado final.Select the preview resolution closest to the capture resolution so that the preview displays the closest representation to the final captured media.

  • Selecciona la resolución de vista previa más cercana al tamaño del objeto CaptureElement, de modo que no se canalicen en la secuencia de vista previa más píxeles que los que se necesiten.Select the preview resolution closest to the size of the CaptureElement so that no more pixels than necessary are going through the preview stream pipeline.

Importante    En algunos dispositivos, es posible establecer una relación de aspecto diferente para la secuencia de vista previa y la secuencia de captura de la cámara.Important   It is possible, on some devices, to set a different aspect ratio for the camera's preview stream and capture stream. El recorte de fotogramas que provoca este error de coincidencia puede dar como resultado contenido presente en los archivos multimedia capturados que no eran visibles en la vista previa, lo que puede provocar una experiencia de usuario negativa.Frame cropping caused by this mismatch can result in content being present in the captured media that was not visible in the preview which can result in a negative user experience. Se recomienda encarecidamente usar la misma relación de aspecto, con un período de tolerancia reducido, para las secuencias de vista previa y captura.It is strongly recommended that you use the same aspect ratio, within a small tolerance window, for the preview and capture streams. Se pueden tener resoluciones totalmente diferentes habilitadas para la captura y la vista previa, siempre que la relación de aspecto sea muy aproximada.It is fine to have entirely different resolutions enabled for capture and preview as long as the aspect ratio match closely.

Para garantizar que las secuencias de captura de foto o vídeo coincidan con la relación de aspecto de la secuencia de vista previa, este ejemplo llama a VideoDeviceController.GetMediaStreamProperties y pasa el valor de enumeración VideoPreview para solicitar las propiedades de secuencia actuales de la secuencia de vista previa.To ensure that the photo or video capture streams match the aspect ratio of the preview stream, this example calls VideoDeviceController.GetMediaStreamProperties and passes in the VideoPreview enum value to request the current stream properties for the preview stream. A continuación, se define un período de tolerancia de relación de aspecto reducido para que se puedan incluir relaciones de aspecto que no sean exactamente iguales que las de la secuencia de vista previa, siempre que sean aproximadas.Next a small aspect ratio tolerance window is defined so that we can include aspect ratios that are not exactly the same as the preview stream, as long as they are close. A continuación, se usa un método de extensión Linq para seleccionar solo los objetos StreamPropertiesHelper donde la relación de aspecto se encuentra dentro del intervalo de tolerancia definido de la secuencia de vista previa.Next, a Linq extension method is used to select just the StreamPropertiesHelper objects where the aspect ratio is within the defined tolerance range of the preview stream.

private void MatchPreviewAspectRatio(MediaStreamType streamType, ComboBox comboBox)
{
    // Query all properties of the specified stream type
    IEnumerable<StreamPropertiesHelper> allVideoProperties = 
        _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(streamType).Select(x => new StreamPropertiesHelper(x));

    // Query the current preview settings
    StreamPropertiesHelper previewProperties = new StreamPropertiesHelper(_mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview));

    // Get all formats that have the same-ish aspect ratio as the preview
    // Allow for some tolerance in the aspect ratio comparison
    const double ASPECT_RATIO_TOLERANCE = 0.015;
    var matchingFormats = allVideoProperties.Where(x => Math.Abs(x.AspectRatio - previewProperties.AspectRatio) < ASPECT_RATIO_TOLERANCE);

    // Order them by resolution then frame rate
    allVideoProperties = matchingFormats.OrderByDescending(x => x.Height * x.Width).ThenByDescending(x => x.FrameRate);

    // Clear out old entries and populate the video combo box with new matching entries
    comboBox.Items.Clear();
    foreach (var property in allVideoProperties)
    {
        ComboBoxItem comboBoxItem = new ComboBoxItem();
        comboBoxItem.Content = property.GetFriendlyName();
        comboBoxItem.Tag = property;
        comboBox.Items.Add(comboBoxItem);
    }
    comboBox.SelectedIndex = -1;
}