카메라 미리 보기 표시

이 문서에서는 UWP(유니버설 Windows 플랫폼) 앱의 XAML 페이지 내에서 카메라 미리 보기 스트림을 빠르게 표시하는 방법을 설명합니다. 카메라를 사용해 사진 및 비디오를 캡처하는 앱을 만들려면 디바이스 및 카메라 방향을 처리하거나 캡처된 파일의 인코딩 옵션을 설정하는 것과 같은 작업을 수행해야 합니다. 일부 앱 시나리오에서는 이러한 기타 고려사항에 대한 염려 없이 카메라에서 미리 보기 스트림을 표시하기만 하면 됩니다. 이 문서에서는 최소한의 코드로 이를 수행하는 방법을 설명합니다. 아래 단계에 따라 미리 보기 스트림을 완료한 경우에는 반드시 미리 보기 스트림을 확실하게 종료해야 합니다.

사진 또는 비디오를 캡처하는 카메라 앱을 작성하는 방법은 MediaCapture를 사용하여 기본 사진, 비디오 및 오디오 캡처를 참조하세요.

앱 매니페스트에 기능 선언 추가

앱이 디바이스의 카메라에 액세스하려면 앱이 웹캠마이크 디바이스 기능을 사용한다고 선언해야 합니다.

앱 매니페스트에 접근 권한 값 추가

  1. Microsoft Visual Studio의 솔루션 탐색기에서 package.appxmanifest 항목을 두 번 클릭하여 응용 프로그램 매니페스트 디자이너를 엽니다.
  2. 기능 탭을 선택합니다.
  3. 웹캠 확인란과 마이크 상자를 선택합니다.

사용자 페이지에 CaptureElement 추가

CaptureElement사용하여 XAML 페이지 내에 미리 보기 스트림을 표시합니다.

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

MediaCapture를 사용하여 미리 보기 스트림 시작

MediaCapture 개체는 디바이스의 카메라에 대한 앱 인터페이스입니다. 이 클래스는 Windows.Media.Capture 네임스페이스의 멤버입니다. 이 문서의 예제에서는 기본 프로젝트 템플릿에 포함된 것뿐만 아니라 Windows.ApplicationModelSystem.Threading.Tasks 네임스페이스의 API도 사용합니다.

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 이벤트 처리기에서 이를 호출하거나, 대기한 후 UI 이벤트에 대한 응답으로 미리 보기를 시작할 수 있습니다.

MediaCapture 클래스의 새 인스턴스를 만들고 InitializeAsync를 호출하여 캡처 디바이스를 초기화합니다. 예를 들어, 카메라가 없는 디바이스에서는 이 메서드가 실패할 수 있으므로 try 블록 내에서 호출해야 합니다. 사용자가 디바이스의 개인 정보 설정에서 카메라 액세스를 사용하지 않도록 설정한 경우에 카메라를 초기화하려고 하면 UnauthorizedAccessException이 throw됩니다. 앱 매니페스트에 적절한 기능을 추가하지 않은 경우 개발 중에도 이 예외가 표시됩니다.

중요 일부 디바이스 패밀리에서는 디바이스의 카메라에 대한 액세스 권한이 앱에 부여되기 전에 사용자 동의 프롬프트가 사용자에게 표시됩니다. 따라서 기본 UI 스레드에서만 MediaCapture.InitializeAsync를 호출해야 합니다. 다른 스레드에서 카메라를 초기화하려고 하면 초기화에 실패할 수도 있습니다.

소스속성을 설정하여MediaCaptureCaptureElement에 연결합니다. StartPreviewAsync를 호출하여 미리 보기를 시작합니다. 이 메서드는 다른 앱이 캡처 장치를 단독으로 제어할 수 있는 경우 FileLoadException을 throw합니다. 단독 제어의 변경에 대한 정보는 다음 섹션을 참조하세요.

RequestActive를 호출하여 미리 보기가 실행될 때 디바이스가 절전 모드로 이동하지 않게 합니다. 마지막으로 사용자가 디바이스 방향을 변경할 때 DisplayInformation.AutoRotationPreferences 속성을 가로로 설정하여 사용자가 디바이스 방향을 변경할 때 UI와 CaptureElement가 회전하지 않게 합니다. 디바이스 방향 변경 처리에 대한 자세한 내용은 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을 throw합니다. Windows 10 버전 1703부터 디바이스의 단독 제어 상태가 변경될 때마다 발생하는 MediaCapture.CaptureDeviceExclusiveControlStatusChanged 이벤트에 대한 처리기를 등록할 수 있습니다. 이 이벤트에 대한 처리기에서 MediaCaptureDeviceExclusiveControlStatusChangedEventArgs.Status 속성을 확인하여 현재 상태를 확인합니다. 새 상태가 SharedReadOnlyAvailable인 경우 미리 보기를 시작할 수 없음을 알 수 있으며 사용자에게 경고하기 위해 UI를 업데이트할 수 있습니다. 새 상태가 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를 호출하면 예외가 throw 됩니다.
  • CaptureElement소스 속성을 null로 설정합니다. CoreDispatcher.RunAsync를 사용하여 이 호출이 UI 스레드에서 실행되게 합니다.
  • MediaCapture 개체의 Dispose 메서드를 호출하여 개체를 해제합니다. 이 경우에도 CoreDispatcher.RunAsync를 사용하여 이 호출이 UI 스레드에서 실행되게 합니다.
  • 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;
}

일시 중단 이벤트 처리기에서, 먼저 페이지 형식을 CurrentSourcePageType 속성과 비교하여 페이지가 애플리케이션의 Frame으로 표시되는지 확인합니다. 페이지가 현재 표시되지 않는 경우 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();
    }
}