Compartilhar via


Mostrar a visualização da câmera em um aplicativo WinUI 3

Neste início rápido, você aprenderá a criar um aplicativo de câmera básico do WinUI 3 que exibe a visualização da câmera. Em um aplicativo WinUI 3, você usa o controle MediaPlayerElement no namespace Microsot.UI.Xaml.Controls para renderizar a visualização da câmera e a classe WinRT MediaCapture para acessar o fluxo de visualização da câmera do dispositivo. MediaCapture fornece APIs para executar uma ampla gama de tarefas relacionadas à câmera, como capturar fotos e vídeos e configurar o driver de dispositivo da câmera. Consulte os outros artigos nesta seção para obter detalhes sobre outros MediaCapture recursos.

O código neste passo a passo é adaptado do exemplo MediaCapture WinUI 3 no github.

Dica

Para obter a versão UWP deste artigo, consulte Exibir a visualização da câmera na documentação do UWP.

Pré-requisitos

  • Seu dispositivo deve ter o modo de desenvolvedor habilitado. Para obter mais informações, consulte Habilite seu dispositivo para desenvolvimento.
  • Visual Studio 2022 ou posterior com a carga de trabalho Desenvolvimento de aplicação do Windows.

Criar um novo aplicativo WinUI 3

No Visual Studio, crie um novo projeto. Na caixa de diálogo Criar um projeto, defina o filtro de linguagem como "C#" e o filtro de plataforma como "Windows" e selecione o modelo de projeto "Aplicativo em branco, empacotado (WinUI 3 no desktop)".

Criar a interface do usuário

A interface do usuário simples para este exemplo inclui um controle MediaPlayerElement para exibir a visualização da câmera, um ComboBox que permite selecionar entre as câmeras do dispositivo, e botões para inicializar a classe MediaCapture, iniciar e parar a visualização da câmera, além de redefinir o exemplo. Também incluímos um TextBlock para exibir mensagens de status.

No arquivo MainWindow.xml do seu projeto, substitua o controle padrão StackPanel pelo seguinte XAML.

<Grid ColumnDefinitions="4*,*" ColumnSpacing="4">
    <MediaPlayerElement x:Name="mpePreview" Grid.Row="0" Grid.Column="0"  AreTransportControlsEnabled="False" ManipulationMode="None"/>
    <StackPanel Orientation="Vertical"  Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch"  VerticalAlignment="Top">
        <TextBlock Text="Status:" Margin="0,0,10,0"/>
        <TextBlock x:Name="tbStatus" Text=""/>
        <TextBlock Text="Preview Source:" Margin="0,0,10,0"/>
        <ComboBox x:Name="cbDeviceList" HorizontalAlignment="Stretch" SelectionChanged="cbDeviceList_SelectionChanged"></ComboBox>
        <Button x:Name="bStartMediaCapture" Content="Initialize MediaCapture" IsEnabled="False" Click="bStartMediaCapture_Click"/>
        <Button x:Name="bStartPreview" Content="Start preview" IsEnabled="False" Click="bStartPreview_Click"/>
        <Button x:Name="bStopPreview" Content="Stop preview" IsEnabled="False" Click="bStopPreview_Click"/>
        <Button x:Name="bReset" Content="Reset" Click="bReset_Click" />
    </StackPanel>
</Grid>

Atualizar a definição da classe MainWindow

O restante do código neste artigo será adicionado à definição de classe MainWindow no arquivo MainWindow.xaml.cs do projeto. Primeiro, adicione algumas variáveis de classe que persistirão durante todo o tempo de vida da janela. Essas variáveis incluem:

  • Um DeviceInformationCollection que armazenará um objeto DeviceInformation para cada câmera disponível. O objeto DeviceInformation transmite informações como o identificador exclusivo e o nome amigável da câmera.
  • Um objeto MediaCapture que lida com interações com o driver da câmera selecionada e permite que você recupere o fluxo de vídeo da câmera.
  • Um objeto MediaFrameSource que representa uma fonte de quadros de mídia, como um fluxo de vídeo.
  • Um booleano para acompanhar quando a visualização da câmera está em execução. Algumas configurações de câmera não podem ser alteradas enquanto a visualização está em execução, portanto, é uma boa prática acompanhar o estado da visualização da câmera.
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

Preencher a lista de câmeras disponíveis

Em seguida, criaremos um método auxiliar para detectar as câmeras presentes no dispositivo atual e preencher o ComboBox na interface do usuário com os nomes das câmeras, permitindo que o usuário selecione uma câmera para visualizar. O DeviceInformation.FindAllAsync permite consultar vários tipos diferentes de dispositivos. Usamos MediaDevice.GetVideoCaptureSelector para recuperar o identificador que especifica que só queremos recuperar dispositivos de captura de vídeo.

private async void PopulateCameraList()
{
    cbDeviceList.Items.Clear();

    m_deviceList = await DeviceInformation.FindAllAsync(MediaDevice.GetVideoCaptureSelector());

    if(m_deviceList.Count == 0)
    {
        tbStatus.Text = "No video capture devices found.";
        return;
    } 

    foreach (var device in m_deviceList)
    {
        cbDeviceList.Items.Add(device.Name);
        bStartMediaCapture.IsEnabled = true;
    }
}

Adicione uma chamada a esse método auxiliar ao construtor da classe MainWindow para que o ComboBox seja preenchido quando a janela for carregada.

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

Inicializar o objeto MediaCapture

Inicialize o objeto MediaCapture chamando InitializeAsync, passando um objeto MediaCaptureInitializationSettings que contém os parâmetros de inicialização solicitados. Há muitos parâmetros de inicialização opcionais que permitem cenários diferentes. Consulte a página de referência da API para obter a lista completa. Neste exemplo simples, especificamos algumas configurações básicas, incluindo:

  • A propriedade VideoDeviceId especifica o identificador exclusivo da câmera à qual o MediaCapture será conectado. Obtemos o ID do dispositivo a partir do DeviceInformationCollection, usando o índice selecionado do ComboBox.
  • A propriedade SharingMode especifica se o aplicativo está solicitando acesso compartilhado ou somente leitura à câmera, o que permite visualizar e capturar do fluxo de vídeo, ou controle exclusivo da câmera, o que permite alterar a configuração da câmera. Vários aplicativos podem ser lidos de uma câmera simultaneamente, mas apenas um aplicativo por vez pode ter controle exclusivo.
  • A propriedade StreamingCaptureMode especifica se queremos capturar vídeo, áudio ou áudio e vídeo.
  • O MediaCaptureMemoryPreference nos permite solicitar o uso específico da memória da CPU para quadros de vídeo. O valor Automático permite que o sistema use memória GPU se estiver disponível.

Antes de inicializar o objeto MediaCapture, chamamos método AppCapability.CheckAccess para determinar se o usuário negou ao nosso aplicativo acesso à câmera nas Configurações do Windows.

Nota

O Windows permite que os usuários concedam ou neguem acesso à câmera do dispositivo no aplicativo Configurações do Windows, em Privacidade & Segurança –> Câmera. Ao inicializar o dispositivo de captura, os aplicativos devem verificar se têm acesso à câmera e lidar com o caso em que o acesso é negado pelo usuário. Para obter mais informações, consulte Manipular a configuração de privacidade da câmera do Windows.

A chamada InitializeAsync é feita dentro de um bloco try para que possamos nos recuperar no caso de a inicialização falhar. Os aplicativos devem lidar com a falha de inicialização normalmente. Neste exemplo simples, exibiremos apenas uma mensagem de erro sobre falha.

private async void bStartMediaCapture_Click(object sender, RoutedEventArgs e)
{
    if (m_mediaCapture != null)
    {
        tbStatus.Text = "MediaCapture already initialized.";
        return;
    }

    // Supported in Windows Build 18362 and later
    if(AppCapability.Create("Webcam").CheckAccess() != AppCapabilityAccessStatus.Allowed)
    {
        tbStatus.Text = "Camera access denied. Launching settings.";

        bool result = await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-webcam"));

        if (AppCapability.Create("Webcam").CheckAccess() != AppCapabilityAccessStatus.Allowed)
        {
            tbStatus.Text = "Camera access denied in privacy settings.";
            return;
        }
    }

    try
    {  
        m_mediaCapture = new MediaCapture();
        var mediaCaptureInitializationSettings = new MediaCaptureInitializationSettings()
        {
            VideoDeviceId = m_deviceList[cbDeviceList.SelectedIndex].Id,
            SharingMode = MediaCaptureSharingMode.ExclusiveControl,
            StreamingCaptureMode = StreamingCaptureMode.Video,
            MemoryPreference = MediaCaptureMemoryPreference.Auto
        };

        await m_mediaCapture.InitializeAsync(mediaCaptureInitializationSettings);

        tbStatus.Text = "MediaCapture initialized successfully.";

        bStartPreview.IsEnabled = true;
    }
    catch (Exception ex)
    {
        tbStatus.Text = "Initialize media capture failed: " + ex.Message;
    }
}

Inicializar a visualização da câmera

Quando o usuário clicar no botão iniciar visualizar, tentaremos criar um MediaFrameSource para um fluxo de vídeo do dispositivo de câmera com o qual o objeto MediaCapture foi inicializado. As fontes de quadro disponíveis são expostas pela propriedade MediaCapture.FrameSources.

Para encontrar uma fonte de quadros que seja dados de vídeo coloridos, em vez de uma câmera de profundidade, por exemplo, procuramos uma fonte de quadros que tenha um sourceKind de Color. Alguns drivers de câmera fornecem um fluxo de visualização dedicado, distinto do fluxo de gravação. Para obter o fluxo de vídeo de visualização, tentamos selecionar uma fonte de quadro de vídeo que tenha um MediaStreamType MediaStreamType de VideoPreview. Se nenhum fluxo de visualização for encontrado, podemos obter o fluxo de vídeo de gravação selecionando um MediaStreamType de VideoRecord. Se nenhuma dessas fontes de imagens estiver disponível, este dispositivo de captura não poderá ser usado para pré-visualização de vídeo.

Depois de selecionarmos uma fonte de quadro, criamos um novo objeto MediaPlayer que será renderizado pelo MediaPlayerElement em nossa interface do usuário. Definimos a propriedade Source do MediaPlayer para um novo objeto MediaSource que criamos a partir do nosso MediaFrameSource selecionado.

Chame Play no objeto mediaplayer para começar a renderizar o fluxo de vídeo.

private void bStartPreview_Click(object sender, RoutedEventArgs e)
{
    
    m_frameSource = null;

    // Find preview source.
    // The preferred preview stream from a camera is defined by MediaStreamType.VideoPreview on the RGB camera (SourceKind == color).
    var previewSource = m_mediaCapture.FrameSources.FirstOrDefault(source => source.Value.Info.MediaStreamType == MediaStreamType.VideoPreview
                                                                                && source.Value.Info.SourceKind == MediaFrameSourceKind.Color).Value;

    if (previewSource != null)
    {
        m_frameSource = previewSource;
    }
    else
    {
        var recordSource = m_mediaCapture.FrameSources.FirstOrDefault(source => source.Value.Info.MediaStreamType == MediaStreamType.VideoRecord
                                                                                   && source.Value.Info.SourceKind == MediaFrameSourceKind.Color).Value;
        if (recordSource != null)
        {
            m_frameSource = recordSource;
        }
    }

    if (m_frameSource == null)
    {
        tbStatus.Text = "No video preview or record stream found.";
        return;
    }



    // Create MediaPlayer with the preview source
    m_mediaPlayer = new MediaPlayer();
    m_mediaPlayer.RealTimePlayback = true;
    m_mediaPlayer.AutoPlay = false;
    m_mediaPlayer.Source = MediaSource.CreateFromMediaFrameSource(m_frameSource);
    m_mediaPlayer.MediaFailed += MediaPlayer_MediaFailed; ;

    // Set the mediaPlayer on the MediaPlayerElement
    mpePreview.SetMediaPlayer(m_mediaPlayer);

    // Start preview
    m_mediaPlayer.Play();


    tbStatus.Text = "Start preview succeeded!";
    m_isPreviewing = true;
    bStartPreview.IsEnabled = false;
    bStopPreview.IsEnabled = true;
}

Implemente um manipulador para o evento MediaFailed para poder lidar com erros ao renderizar a visualização.

private void MediaPlayer_MediaFailed(MediaPlayer sender, MediaPlayerFailedEventArgs args)
{
    tbStatus.Text = "MediaPlayer error: " + args.ErrorMessage;
}

Interromper a visualização da câmera

Para interromper a visualização da câmera, chame Pause no objeto MediaPlayer.

private void bStopPreview_Click(object sender, RoutedEventArgs e)
{
    // Stop preview
    m_mediaPlayer.Pause();
    m_isPreviewing = false;
    bStartPreview.IsEnabled = true;
    bStopPreview.IsEnabled = false;
}

Redefinir o aplicativo

Para facilitar o teste do aplicativo de exemplo, adicione um método para redefinir o estado do aplicativo. Os aplicativos de câmera sempre devem descartar a câmera e os recursos associados quando a câmera não for mais necessária.

private void bReset_Click(object sender, RoutedEventArgs e)
{
    if (m_mediaCapture != null)
    {
        m_mediaCapture.Dispose();
        m_mediaCapture = null;
    }

    if(m_mediaPlayer != null)
    {
        m_mediaPlayer.Dispose();
        m_mediaPlayer = null;
    }
    
    m_frameSource = null;
    

    bStartMediaCapture.IsEnabled = false;
    bStartPreview.IsEnabled = false;
    bStopPreview.IsEnabled = false;

    PopulateCameraList();

}