Поделиться через


Отображение предварительной версии камеры в приложении WinUI 3

В этом кратком руководстве вы научитесь, как создать базовое приложение камеры WinUI, отображающее предварительный просмотр камеры. В приложении WinUI вы используете элемент управления MediaPlayerElement из пространства имен Microsoft.UI.Xaml.Controls для отображения предварительного просмотра камеры и класс WinRT MediaCapture для доступа к потоку предварительного просмотра камеры устройства. MediaCapture предоставляет API-интерфейсы для выполнения широких задач, связанных с камерой, таких как запись фотографий и видео и настройка драйвера устройства камеры. Дополнительные сведения о других функциях MediaCapture см. в других статьях этого раздела.

Код в этом пошаговом руководстве адаптирован из примера MediaCapture WinUI на github.

Предпосылки

  • Устройство должно быть включено в режиме разработчика. Дополнительные сведения см. в разделе "Параметры" для разработчиков.
  • Visual Studio 2026 или более поздней версии с рабочей нагрузкой разработки приложений WinUI .

Создание нового приложения WinUI

В Visual Studio создайте новый project. В диалоговом окне "Создание проекта " задайте для фильтра языка значение "C#", а фильтр платформы — "Windows", а затем выберите шаблон проекта WinUI Blank App (Packaged).

Создание пользовательского интерфейса

В этом примере простой пользовательский интерфейс включает элемент управления MediaPlayerElement для отображения предварительного просмотра камеры, ComboBox, который позволяет выбрать одну из камер устройства, а также кнопки для инициализации класса MediaCapture, запуска и остановки предварительного просмотра камеры и возврата примера к исходному состоянию. Мы также включили TextBlock для отображения сообщений о состоянии.

В файле MainWindow.xml вашего проекта замените элемент управления StackPanel на следующий 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>

Обновление определения класса MainWindow

Остальная часть кода в этой статье будет добавлена в определение класса MainWindow в файле проекта MainWindow.xaml.cs. Сначала добавьте несколько переменных класса, которые будут сохраняться в течение всего времени существования окна. К этим переменным относятся:

  • DeviceInformationCollection, который будет хранить объект DeviceInformation для каждой доступной камеры. Объект DeviceInformation передает такие сведения, как уникальный идентификатор и понятное имя камеры.
  • Объект MediaCapture , который обрабатывает взаимодействие с драйвером выбранной камеры и позволяет получить видеопоток камеры.
  • Объект MediaFrameSource , представляющий источник кадров мультимедиа, например видеопоток.
  • Логический флаг для отслеживания, когда работает просмотр камеры. Некоторые параметры камеры нельзя изменить во время выполнения предварительной версии, поэтому рекомендуется отслеживать состояние предварительной версии камеры.
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

Заполнение списка доступных камер

Затем мы создадим вспомогательный метод для обнаружения камер, присутствующих на текущем устройстве, и заполните comboBox в пользовательском интерфейсе именами камер, позволяя пользователю выбрать камеру для предварительного просмотра. DeviceInformation.FindAllAsync позволяет запрашивать различные виды устройств. Мы используем MediaDevice.GetVideoCaptureSelector для получения идентификатора, указывающего, что требуется получить только устройства записи видео.

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

Добавьте вызов в вспомогательный метод в конструктор класса MainWindow , чтобы comboBox заполнялся при загрузке окна.

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

Инициализация объекта MediaCapture

Инициализировать объект MediaCapture путем вызова InitializeAsync, передавая объект MediaCaptureInitializationSettings, содержащий запрошенные параметры инициализации. Существует множество необязательных параметров инициализации, которые позволяют использовать различные сценарии. См. страницу справочника по API для полного списка. В этом простом примере мы укажем несколько основных параметров, в том числе:

  • Свойство VideoDeviceId указывает уникальный идентификатор камеры, к которому будет присоединен MediaCapture . Мы получаем идентификатор устройства из DeviceInformationCollection, используя выбранный индекс ComboBox.
  • Свойство SharingMode указывает, запрашивает ли приложение общий доступ к камере, доступной только для чтения, что позволяет просматривать и записывать данные из видеопотока, или эксклюзивное управление камерой, что позволяет изменять ее конфигурацию. Несколько приложений могут одновременно читать с камеры, но только одно приложение одновременно может иметь монопольный контроль.
  • Свойство StreamingCaptureMode указывает, нужно ли записывать видео, звук или видео.
  • MediaCaptureMemoryPreference позволяет запрашивать специальное использование памяти ЦП для видеокадров. Значение Auto позволяет системе использовать память GPU, если она доступна.

Перед инициализацией объекта MediaCapture мы вызываем метод AppCapability.CheckAccess, чтобы определить, отказывает ли пользователь нашему приложению в доступе к камере в настройках Windows.

Замечание

Windows позволяет пользователям предоставлять или запрещать доступ камере устройства в параметрах Windows, в разделе Конфиденциальность и безопасность -> Камера. При инициализации устройства съёмки приложения должны проверить, есть ли у них доступ к камере, и обработать ситуацию в случае, если доступ запрещён пользователем. Дополнительные сведения см. в разделе "Обработка параметра конфиденциальности камеры Windows".

Вызов InitializeAsync выполняется из блока try , чтобы можно было восстановить, если инициализация завершается ошибкой. Приложения должны корректно обрабатывать сбой инициализации. В этом простом примере мы просто отобразим сообщение об ошибке при сбое.

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

Инициализация предварительного просмотра камеры

Когда пользователь нажимает кнопку начального просмотра, мы попытаемся создать MediaFrameSource для видеопотока с устройства камеры, с помощью которого был инициализирован объект MediaCapture . Доступные источники кадров предоставляются свойством MediaCapture.FrameSources .

Чтобы найти источник кадров, содержащих цветные видеоданные, в отличие, например, от камеры, измеряющей глубину, мы ищем источник кадров, имеющий SourceKind типа Color. Некоторые драйверы камеры предоставляют выделенный поток предварительной версии, который отделен от потока записи. Чтобы получить предварительный просмотр видеопотока, мы пытаемся выбрать источник кадра с типом MediaStreamTypevideoPreview. Если потоки предварительного просмотра не найдены, мы можем получить видеопоток записи, выбрав MediaStreamTypeVideoRecord. Если ни из этих источников кадров недоступны, то это устройство записи не может использоваться для предварительного просмотра видео.

После выбора источника кадра мы создадим новый объект MediaPlayer , который будет отображаться MediaPlayerElement в нашем пользовательском интерфейсе. Мы задали свойству Sourceобъекта MediaPlayer новый объект MediaSource , который мы создадим из выбранного объекта MediaFrameSource.

Вызовите функцию Play для объекта MediaPlayer, чтобы начать воспроизведение видеопотока.

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

Реализуйте обработчик события MediaFailed, чтобы обрабатывать ошибки при отображении предварительного просмотра.

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

Остановить предварительный просмотр камеры

Чтобы остановить предварительный просмотр камеры, вызовите метод Приостановить на объекте MediaPlayer.

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

Сброс приложения

Чтобы упростить тестирование примера приложения, добавьте метод для сброса состояния приложения. Приложения камеры всегда должны удалять камеры и связанные ресурсы, если камера больше не нужна.

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();

}