Bagikan melalui


Menampilkan pratinjau kamera di aplikasi WinUI 3

Dalam pendahuluan cepat ini, Anda akan belajar cara membuat aplikasi dasar kamera WinUI yang memperlihatkan pratinjau kamera. Dalam aplikasi WinUI, Anda menggunakan kontrol MediaPlayerElement dalam namespace Microsoft.UI.Xaml.Controls untuk merender pratinjau kamera dan kelas WinRT MediaCapture untuk mengakses aliran pratinjau kamera perangkat. MediaCapture menyediakan API untuk melakukan berbagai tugas terkait kamera seperti menangkap foto dan video dan mengonfigurasi driver perangkat kamera. Lihat artikel lain di bagian ini untuk detail tentang fitur MediaCapture lainnya.

Kode dalam panduan ini diadaptasi dari sampel winUI MediaCapture pada github.

Petunjuk / Saran

Untuk versi UWP artikel ini, lihat Putar pratinjau kamera dalam dokumentasi UWP.

Prasyarat

  • Perangkat Anda harus mengaktifkan mode pengembang. Untuk informasi selengkapnya lihat Pengaturan untuk pengembang.
  • Visual Studio 2022 atau yang lebih baru dengan beban kerja pengembangan aplikasi WinUI.

Membuat aplikasi WinUI baru

Di Visual Studio, buat project baru. Dalam dialog Buat proyek baru, atur filter bahasa ke "C#" dan filter platform ke "Windows", lalu pilih templat proyek "Aplikasi Kosong, Dikemas (WinUI di desktop)".

Buat antarmuka pengguna

Antarmuka pengguna sederhana untuk contoh ini mencakup kontrol MediaPlayerElement untuk menampilkan pratinjau kamera, ComboBox yang memungkinkan Anda memilih dari kamera perangkat, dan tombol untuk menginisialisasi kelas MediaCapture , memulai dan menghentikan pratinjau kamera, dan mengatur ulang sampel. Kami juga menyertakan TextBlock untuk menampilkan pesan status.

Dalam file MainWindow.xml project Anda, ganti kontrol default StackPanel dengan XAML berikut.

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

Memperbarui definisi kelas MainWindow

Sisa kode dalam artikel ini akan ditambahkan ke definisi kelas MainWindow dalam file MainWindow.xaml.cs project Anda. Pertama, tambahkan beberapa variabel kelas yang akan bertahan sepanjang masa pakai jendela. Variabel-variabel ini meliputi:

  • DeviceInformationCollection yang akan menyimpan objek DeviceInformation untuk setiap kamera yang tersedia. Objek DeviceInformation menyampaikan informasi seperti pengidentifikasi unik dan nama yang mudah diingat untuk kamera.
  • Objek MediaCapture yang menangani interaksi dengan driver kamera yang dipilih dan memungkinkan Anda mengambil aliran video kamera.
  • Objek MediaFrameSource yang mewakili sumber bingkai media, seperti aliran video.
  • Boolean untuk dilacak saat pratinjau kamera sedang berjalan. Beberapa pengaturan kamera tidak dapat diubah saat pratinjau berjalan, jadi ini adalah praktik yang baik untuk melacak status pratinjau kamera.
private DeviceInformationCollection m_deviceList;
private MediaCapture m_mediaCapture;
private MediaFrameSource m_frameSource;
private MediaPlayer m_mediaPlayer;
private bool m_isPreviewing;

Mengisi daftar kamera yang tersedia

Selanjutnya kita akan membuat metode pembantu untuk mendeteksi kamera yang ada di perangkat saat ini dan mengisi ComboBox di UI dengan nama kamera, memungkinkan pengguna untuk memilih kamera untuk dipratinjau. DeviceInformation.FindAllAsync memungkinkan Anda mengkueri berbagai jenis perangkat. Kami menggunakan MediaDevice.GetVideoCaptureSelector untuk mengambil pengidentifikasi yang menentukan bahwa kami hanya ingin mengambil perangkat pengambilan video.

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

Tambahkan panggilan ke metode pembantu ini ke konstruktor kelas MainWindow sehingga ComboBox diisi saat jendela dimuat.

public MainWindow()
{
    this.InitializeComponent();

    PopulateCameraList();
    
}

Menginisialisasi objek MediaCapture

Inisialisasi objek MediaCapture dengan memanggil InitializeAsync, meneruskan objek MediaCaptureInitializationSettings yang berisi parameter inisialisasi yang diminta. Ada banyak parameter inisialisasi opsional yang memungkinkan skenario yang berbeda. Lihat halaman referensi API untuk daftar lengkapnya. Dalam contoh sederhana ini, kami menentukan beberapa pengaturan dasar, termasuk:

  • Properti VideoDeviceId menentukan pengidentifikasi unik kamera yang akan dilampirkan oleh MediaCapture . Kami mendapatkan ID perangkat dari DeviceInformationCollection, menggunakan indeks comboBox yang dipilih.
  • Properti SharingMode menentukan apakah aplikasi meminta akses bersama baca-saja ke kamera, sehingga Anda dapat melihat dan menangkap aliran video, atau kontrol eksklusif kamera, yang memungkinkan Anda menyesuaikan konfigurasi kamera. Beberapa aplikasi dapat membaca dari kamera secara bersamaan, tetapi hanya satu aplikasi pada satu waktu yang dapat memiliki kontrol eksklusif.
  • Properti StreamingCaptureMode menentukan apakah kita ingin mengambil video, audio, atau audio dan video.
  • MediaCaptureMemoryPreference memungkinkan kami meminta untuk secara khusus menggunakan memori CPU untuk bingkai video. Nilai Otomatis memungkinkan sistem menggunakan memori GPU jika tersedia.

Sebelum menginisialisasi objek MediaCapture kami memanggil AppCapability.CheckAccess untuk menentukan apakah pengguna telah menolak access aplikasi kami ke kamera di Pengaturan Windows.

Nota

Windows memungkinkan pengguna untuk memberikan atau menolak akses ke kamera perangkat di Pengaturan Windows, dalam bagian Privasi & Keamanan - Kamera. Saat menginisialisasi perangkat penangkap, aplikasi harus memeriksa apakah mereka memiliki akses ke kamera dan menangani kasus akses ditolak oleh pengguna. Untuk informasi selengkapnya, lihat Menangani pengaturan privasi kamera Windows.

Panggilan InitializeAsync dilakukan dari dalam blok percobaan sehingga kita dapat memulihkan jika inisialisasi gagal. Aplikasi harus menangani kegagalan inisialisasi dengan baik. Dalam contoh sederhana ini, kita hanya akan menampilkan pesan kesalahan saat gagal.

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

Menginisialisasi pratinjau kamera

Ketika pengguna mengklik tombol mulai pratinjau, kami akan mencoba membuat MediaFrameSource untuk aliran video dari perangkat kamera tempat objek MediaCapture diinisialisasi. Sumber bingkai yang tersedia diekspos oleh properti MediaCapture.FrameSources .

Untuk menemukan sumber bingkai yang memiliki data video berwarna, berbeda dengan kamera kedalaman misalnya, kita mencari sumber bingkai yang memiliki SourceKind dari Color. Beberapa driver kamera menyediakan aliran pratinjau khusus yang terpisah dari aliran rekaman. Untuk mendapatkan streaming video pratinjau, kami mencoba memilih sumber bingkai yang memiliki MediaStreamType dari VideoPreview. Jika tidak ada aliran pratinjau yang ditemukan, kita bisa mendapatkan streaming video rekaman dengan memilih MediaStreamType dari VideoRecord. Jika tidak ada sumber bingkai ini yang tersedia, maka perangkat pengambilan ini tidak dapat digunakan untuk pratinjau video.

Setelah memilih sumber bingkai, kami membuat objek MediaPlayer baru yang akan dirender oleh MediaPlayerElement di UI kami. Kami mengatur properti SumberMediaPlayer ke objek MediaSource baru yang kami buat dari MediaFrameSource yang kami pilih.

Panggil Putar pada objek MediaPlayer untuk mulai merender aliran video.

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

Terapkan handler untuk event MediaFailed sehingga Anda dapat menangani kesalahan yang menampilkan pratinjau.

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

Menghentikan pratinjau kamera

Untuk menghentikan pratinjau kamera, panggil Jeda pada objek MediaPlayer .

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

Mereset aplikasi

Untuk mempermudah pengujian aplikasi sampel, tambahkan metode untuk mengatur ulang status aplikasi. Aplikasi kamera harus selalu membuang kamera dan sumber daya terkait saat kamera tidak lagi diperlukan.

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

}