Отображение просмотра камеры

В этой статье описывается, как быстро вывести поток предварительного просмотра камеры на странице XAML в приложении универсальной платформы Windows (UWP). Для создания приложения, захватывающего фотографии и видео с камеры, необходимо выполнить такие задачи, как обработка положения устройства и ориентации камеры и настройка параметров кодирования записанного файла. Для некоторых сценариев приложения может потребоваться просто отобразить поток предварительного просмотра камеры, не беспокоясь о других вопросах. В данной статье показано, как это сделать с применением минимального количества кода. Обратите внимание, что необходимо всегда надлежащим образом завершать поток предварительного просмотра после окончания работы с ним, выполняя приведенные ниже действия.

Подробнее о создании приложения для захвата фотографий и видео с камеры, см. Основные принципы фото-, аудио- и видеозахвата с помощью MediaCapture.

Добавление объявлений возможностей в манифест приложения

Чтобы ваше приложение получило доступ к камере устройства, необходимо объявить, что оно использует возможности устройства webcam и microphone.

Добавление возможностей в манифест приложения

  1. В Microsoft Visual Studio откройте конструктор манифеста приложения, дважды щелкнув элемент package.appxmanifest в Обозревателе решений.
  2. Перейдите на вкладку Возможности.
  3. Выставьте флажок для пункта Веб-камера и поле для параметра Микрофон.

Добавление объекта CaptureElement на страницу

Используйте объект CaptureElement для отображения потока предварительного просмотра на странице XAML.

<CaptureElement Name="PreviewControl" Stretch="Uniform"/>

Использование объекта MediaCapture для запуска потока предварительного просмотра

Объект MediaCapture — это интерфейс приложения для взаимодействия с камерой устройства. Этот класс входит в пространство имен Windows.Media.Capture. В описанном в этой статье примере используются API из пространств имен Windows.ApplicationModel и System.Threading.Tasks в дополнение ко включенным в шаблон проекта по умолчанию.

Добавьте директивы using, чтобы включить следующие пространства имен в файл .cs вашей страницы.

using Windows.UI.Core;
using Windows.UI.Xaml.Navigation;
using Windows.Media.Capture;
using Windows.ApplicationModel;
using System.Threading.Tasks;
using Windows.System.Display;
using Windows.Graphics.Display;

Объявите переменную-член класса для объекта MediaCapture и логическую переменную для отслеживания того, выполняется ли предварительный просмотр камерой в данный момент.

MediaCapture mediaCapture;
bool isPreviewing;

Объявите переменную типа DisplayRequest, которая будет использоваться для блокировки отключения экрана во время предварительного просмотра.

DisplayRequest displayRequest = new DisplayRequest();

Создайте вспомогательный метод для запуска предварительного просмотра с камеры, названный в этом примере StartPreviewAsync. В зависимости от сценария работы приложения можно вызвать его из обработчика событий OnNavigatedTo, который вызывается при загрузке страницы или при ожидании и запуске предварительного просмотра в ответ на события пользовательского интерфейса.

Создайте новый экземпляр класса MediaCapture и вызовите метод InitializeAsync, чтобы инициализировать устройство захвата. Этот метод может завершиться ошибкой, например, на устройствах, не оборудованных камерой, поэтому его необходимо вызывать из блока try. Если пользователь отключил доступ к камере в параметрах конфиденциальности устройства, при попытке инициализировать камеру будет вызвано исключение UnauthorizedAccessException. Вы также увидите это исключение во время разработки, если в манифест приложения не были добавлены необходимые возможности.

Важно! На устройствах некоторых семейств перед тем, как приложение получит доступ к камере устройства, отображается запрос на согласие пользователя. По этой причине следует вызывать только MediaCapture.InitializeAsync из основного потока пользовательского интерфейса. Попытка инициализировать камеру через другой поток может привести к сбою инициализации.

Подключите MediaCapture к CaptureElement, задав свойство Source. Запустите предварительный просмотр путем вызова метода StartPreviewAsync. Этот метод вызовет исключение FileLoadException, если другое приложение использует устройство захвата в монопольном режиме. Информацию о прослушивании изменений в монопольном режиме см. в следующем разделе.

Вызовите RequestActive, чтобы не допустить перехода устройства в спящий режим во время предварительного просмотра. В заключение задайте свойству DisplayInformation.AutoRotationPreferences значение Landscape, чтобы предотвратить поворот интерфейса пользователя и CaptureElement при изменении ориентации устройства пользователем. Дополнительные сведения об обработке изменений ориентации устройства см. в разделе Handle device orientation with MediaCapture.

       private async Task StartPreviewAsync()
       {
           try
           {

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

               displayRequest.RequestActive();
               DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;
           }
           catch (UnauthorizedAccessException)
           {
               // This will be thrown if the user denied access to the camera in privacy settings
               ShowMessageToUser("The app was denied access to the camera");
               return;
           }

           try
           {
               PreviewControl.Source = mediaCapture;
               await mediaCapture.StartPreviewAsync();
               isPreviewing = true;
           }
           catch (System.IO.FileLoadException)
           {
               mediaCapture.CaptureDeviceExclusiveControlStatusChanged += _mediaCapture_CaptureDeviceExclusiveControlStatusChanged;
           }

       }

Обработка изменений в монопольном режиме

Как было сказано в предыдущем разделе, StartPreviewAsync вызывает исключение FileLoadException, если другое приложение использует устройство захвата в монопольном режиме. Начиная с Windows 10 версии 1703, можно регистрировать обработчик для события MediaCapture.CaptureDeviceExclusiveControlStatusChanged, которое возникает при изменении состояния использования устройства в монопольном режиме. В обработчике для этого события проверьте свойство MediaCaptureDeviceExclusiveControlStatusChangedEventArgs.Status, чтобы узнать о текущем состоянии. Если новое состояние SharedReadOnlyAvailable, в данный момент вы не можете запустить предварительный просмотр. В таком случае рекомендуется обновить пользовательский интерфейс для оповещения пользователя. Если новое состояние — ExclusiveControlAvailable, вы можете попробовать снова запустить предварительный просмотр камеры.

private async void _mediaCapture_CaptureDeviceExclusiveControlStatusChanged(MediaCapture sender, MediaCaptureDeviceExclusiveControlStatusChangedEventArgs args)
{
    if (args.Status == MediaCaptureDeviceExclusiveControlStatus.SharedReadOnlyAvailable)
    {
        ShowMessageToUser("The camera preview can't be displayed because another app has exclusive access");
    }
    else if (args.Status == MediaCaptureDeviceExclusiveControlStatus.ExclusiveControlAvailable && !isPreviewing)
    {
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
        {
            await StartPreviewAsync();
        });
    }
}

Завершение потока предварительного просмотра

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

  • Если в данный момент камера обеспечивает предварительный просмотр, вызовите StopPreviewAsync, чтобы завершить поток предварительного просмотра. Если вызов StopPreviewAsync производится, когда предварительная версия не работает, возникнет исключение.
  • Установите для свойства Source класса CaptureElement значение null. Используйте метод CoreDispatcher.RunAsync, чтобы убедиться, что этот вызов выполняется в потоке пользовательского интерфейса.
  • Вызовите метод Dispose объекта MediaCapture, чтобы освободить ресурсы объекта. Снова используйте метод CoreDispatcher.RunAsync, чтобы убедиться, что этот вызов выполняется в потоке пользовательского интерфейса.
  • Присвойте переменной-члену MediaCapture значение null.
  • Вызовите RequestRelease, чтобы разрешить отключение экрана в отсутствие активности.
private async Task CleanupCameraAsync()
{
    if (mediaCapture != null)
    {
        if (isPreviewing)
        {
            await mediaCapture.StopPreviewAsync();
        }

        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            PreviewControl.Source = null;
            if (displayRequest != null)
            {
                displayRequest.RequestRelease();
            }

            mediaCapture.Dispose();
            mediaCapture = null;
        });
    }
    
}

Когда пользователь уходит со страницы, остановите поток предварительного просмотра путем переопределения метода OnNavigatedFrom.

protected async override void OnNavigatedFrom(NavigationEventArgs e)
{
    await CleanupCameraAsync();
}

Также необходимо надлежащим образом завершать поток предварительного просмотра в случае приостановки вашего приложения. Для этого зарегистрируйте обработчик для события Application.Suspending в конструкторе страницы.

public MainPage()
{
    this.InitializeComponent();

    Application.Current.Suspending += Application_Suspending;
}

Сначала проверьте в обработчике событий Suspending, что страница отображается в классе приложения Frame, сравнив тип страницы со свойством CurrentSourcePageType. Если страница в настоящее время не отображается, событие OnNavigatedFrom уже должно быть вызвано, а поток предварительного просмотра остановлен. Если страница отображается, получите объект SuspendingDeferral из переданных в обработчик аргументов события, чтобы предотвратить приостановку приложения системой до остановки потока предварительного просмотра. После остановки потока вызовите метод отсрочки Complete, чтобы разрешить системе продолжить приостановку приложения.

private async void Application_Suspending(object sender, SuspendingEventArgs e)
{
    // Handle global application events only if this page is active
    if (Frame.CurrentSourcePageType == typeof(MainPage))
    {
        var deferral = e.SuspendingOperation.GetDeferral();
        await CleanupCameraAsync();
        deferral.Complete();
    }
}