Compartilhar via


Definir formato, resolução e taxa de quadros para MediaCapture em um aplicativo WinUI

Este artigo mostra como usar a interface IMediaEncodingProperties para definir a resolução e a taxa de quadros do fluxo de visualização da câmera e fotos e vídeos capturados. Ele também mostra como garantir que a proporção do fluxo de visualização corresponda à da mídia capturada.

Os perfis de câmera oferecem um mecanismo de nível mais simples e superior para descobrir e definir as propriedades de fluxo da câmera, mas eles não têm suporte para todos os dispositivos. Para obter mais informações, consulte perfis de câmera.

Determinar se os fluxos de visualização e captura são independentes

Em alguns dispositivos, o mesmo pin de hardware é usado para visualizar e capturar fluxos. Nesses dispositivos, definir as propriedades de codificação, como o formato, a resolução e a taxa de quadros em um deles, afetará ambos. Em dispositivos que usam pins de hardware diferentes para captura e visualização, as propriedades podem ser definidas para cada fluxo de forma independente. Use o código a seguir para determinar se os fluxos de visualização e captura são independentes. Este exemplo define uma variável global booliana que pode ser usada para alternar o comportamento do aplicativo se os fluxos forem compartilhados ou independentes.

if (m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
    m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
{
    m_captureAndPreviewStreamsIdentical = true;
}

Uma classe auxiliar de propriedades de codificação de mídia

A criação de uma classe auxiliar simples para encapsular a funcionalidade da interface IMediaEncodingProperties facilita a seleção de um conjunto de propriedades de codificação que atendam a critérios específicos. Essa classe auxiliar é particularmente útil devido ao seguinte comportamento do recurso de propriedades de codificação:

Observação

O método VideoDeviceController.GetAvailableMediaStreamProperties usa um membro da enumeração MediaStreamType , como VideoRecord ou Photo, e retorna uma lista de objetos ImageEncodingProperties ou VideoEncodingProperties que transmitem as configurações de codificação de fluxo, como a resolução da foto ou vídeo capturado. Os resultados de chamar GetAvailableMediaStreamProperties podem incluir ImageEncodingProperties ou VideoEncodingProperties , independentemente do valor de MediaStreamType especificado. Por esse motivo, você sempre deve verificar o tipo de cada valor retornado e convertê-lo no tipo apropriado antes de tentar acessar qualquer um dos valores da propriedade.

A classe auxiliar definida abaixo manipula a verificação e a conversão de tipos para ImageEncodingProperties ou VideoEncodingProperties para que o código do aplicativo não precise distinguir entre os dois tipos. Além disso, a classe auxiliar expõe propriedades como a relação de aspecto, a taxa de quadros por segundo (somente para propriedades de codificação de vídeo) e um nome legível, que facilita a exibição das propriedades de codificação em sua interface do usuário.

class StreamPropertiesHelper
{
    private IMediaEncodingProperties _properties;

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

        // This helper class only uses ImageEncodingProperties 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;
    }

}

Obter uma lista de propriedades de streaming disponíveis

Obtenha uma lista das propriedades de fluxo disponíveis para um dispositivo de captura obtendo o VideoDeviceController para o objeto MediaCapture do seu aplicativo e, em seguida, chamando GetAvailableMediaStreamProperties e passando um dos valores MediaStreamType , VideoPreview, VideoRecord ou Photo. Neste exemplo, uma lista de objetos StreamPropertiesHelper , definida anteriormente neste artigo, é criada para cada um dos valores IMediaEncodingProperties retornados de GetAvailableMediaStreamProperties. Este exemplo ordena as propriedades retornadas com base primeiro na resolução e, em seguida, na taxa de quadros.

Se seu aplicativo tiver requisitos específicos de resolução ou taxa de quadros, você poderá selecionar um conjunto de propriedades de codificação de mídia programaticamente. Em vez disso, um aplicativo de câmera típico exporá a lista de propriedades disponíveis na interface do usuário e permitirá que o usuário selecione as configurações desejadas. Um ComboBoxItem é criado para cada item na lista de objetos StreamPropertiesHelper na lista. O conteúdo é definido pelo nome amigável retornado pela classe auxiliar, e a etiqueta é definida pela própria classe auxiliar para que possa ser usada posteriormente para recuperar as propriedades de codificação associadas. Cada ComboBoxItem é adicionado a um ComboBox definido na interface do usuário.

private void bGetStreamProperties_Click(object sender, RoutedEventArgs e)
{
    // Query all properties of the specified stream type 
    IEnumerable<StreamPropertiesHelper> allStreamProperties =
        m_mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).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();
        comboBoxItem.Tag = property;
        cbStreamProperties.Items.Add(comboBoxItem);
    }
}

Definir as propriedades de fluxo desejadas

Diga ao controlador de dispositivo de vídeo para usar as propriedades de codificação desejadas chamando SetMediaStreamPropertiesAsync, passando o valor MediaStreamType indicando se as propriedades de foto, vídeo ou visualização devem ser definidas. Este exemplo usa o ComboBox que foi preenchido no exemplo da seção anterior, onde as propriedades do fluxo de mídia são recuperadas da propriedade de 'tag' do item selecionado.

private async void bSetStreamProperties_Click(object sender, RoutedEventArgs e)
{

    if (m_exclusiveCameraAccess)
    {
        var selectedItem = cbStreamProperties.SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await m_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, encodingProperties);
    }
}

Observe que seu aplicativo deve ter controle exclusivo do dispositivo de captura para alterar as propriedades do fluxo de mídia.

Corresponder a relação de aspecto dos fluxos de visualização e captura

Um aplicativo de câmera típico fornecerá a interface do usuário para que o usuário selecione a resolução de captura de vídeo ou foto, mas definirá programaticamente a resolução de visualização. Há algumas estratégias diferentes para selecionar a melhor resolução de fluxo de visualização para seu aplicativo:

  • Selecione a resolução de visualização mais alta disponível, permitindo que a estrutura de interface do usuário execute qualquer dimensionamento necessário da versão prévia.

  • Selecione a resolução de visualização mais próxima da resolução de captura para que a visualização exiba a representação mais próxima da mídia capturada final.

  • Selecione a resolução de pré-visualização mais próxima do tamanho do CaptureElement para que não passem mais pixels do que o necessário pelo pipeline do fluxo de pré-visualização.

Observação

É possível, em alguns dispositivos, definir uma proporção de aspecto diferente para o fluxo de visualização e o fluxo de captura da câmera. O corte de quadros causado por essa incompatibilidade pode resultar na presença de conteúdo na mídia capturada que não estava visível na visualização, o que pode resultar em uma experiência negativa do usuário. É altamente recomendável que você use a mesma relação de aspecto nos fluxos de visualização e captura, dentro de uma pequena janela de tolerância. É aceitável ter resoluções totalmente diferentes habilitadas para captura e visualização, desde que a proporção de aspecto seja bastante próxima.

Para garantir que os fluxos de captura de foto ou vídeo correspondam à proporção de aspecto do fluxo de visualização, este exemplo chama VideoDeviceController.GetMediaStreamProperties e passa o valor da enumeração VideoPreview para solicitar as propriedades atuais do fluxo de visualização. Em seguida, uma pequena janela de tolerância de proporção de aspecto é definida para que possamos incluir proporções que não são exatamente iguais ao fluxo de pré-visualização, desde que estejam próximas. Em seguida, os objetos StreamPropertiesHelper cuja relação de aspecto está dentro do alcance de tolerância definido para o fluxo de visualização são selecionados.

// Query all properties of the specified stream type
IEnumerable<StreamPropertiesHelper> allVideoProperties =
    m_mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).Select(x => new StreamPropertiesHelper(x));

// Query the current preview settings
StreamPropertiesHelper previewProperties = new StreamPropertiesHelper(m_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
cbStreamProperties.Items.Clear();
foreach (var property in allVideoProperties)
{
    ComboBoxItem comboBoxItem = new ComboBoxItem();
    comboBoxItem.Content = property.GetFriendlyName();
    comboBoxItem.Tag = property;
    cbStreamProperties.Items.Add(comboBoxItem);
}

SnippetMatchPreviewAspectRatio