Mengatur format, resolusi, dan kecepatan bingkai untuk MediaCapture

Artikel ini menunjukkan kepada Anda cara menggunakan antarmuka IMediaEncodingProperties untuk mengatur resolusi dan kecepatan bingkai aliran pratinjau kamera dan mengambil foto dan video. Ini juga menunjukkan cara memastikan bahwa rasio aspek aliran pratinjau cocok dengan media yang ditangkap.

Profil kamera menawarkan cara yang lebih canggih untuk menemukan dan mengatur properti aliran kamera, tetapi tidak didukung untuk semua perangkat. Untuk informasi selengkapnya, lihat Profil kamera.

Kode dalam artikel ini diadaptasi dari sampel CameraResolution. Anda dapat mengunduh sampel untuk melihat kode yang digunakan dalam konteks atau menggunakan sampel sebagai titik awal untuk aplikasi Anda sendiri.

Catatan

Artikel ini dibangun berdasarkan konsep dan kode yang dibahas dalam pengambilan foto, video, dan audio Dasar dengan MediaCapture, yang menjelaskan langkah-langkah untuk menerapkan pengambilan foto dan video dasar. Disarankan agar Anda membiasakan diri dengan pola pengambilan media dasar dalam artikel tersebut sebelum beralih ke skenario pengambilan yang lebih canggih. Kode dalam artikel ini mengasumsikan bahwa aplikasi Anda sudah memiliki instans MediaCapture yang telah diinisialisasi dengan benar.

Kelas pembantu properti pengodean media

Membuat kelas pembantu sederhana untuk membungkus fungsionalitas antarmuka IMediaEncodingProperties memudahkan untuk memilih sekumpulan properti pengodean yang memenuhi kriteria tertentu. Kelas pembantu ini sangat berguna karena perilaku berikut dari fitur properti pengodean:

Peringatan Metode VideoDeviceController.GetAvailableMediaStreamProperties mengambil anggota enumerasi MediaStreamType , seperti VideoRecord atau Photo, dan mengembalikan daftar objek ImageEncodingProperties atau VideoEncodingProperties yang menyampaikan pengaturan pengodean streaming, seperti resolusi foto atau video yang diambil. Hasil panggilan GetAvailableMediaStreamProperties dapat mencakup ImageEncodingProperties atau VideoEncodingProperties terlepas dari nilai MediaStreamType yang ditentukan. Untuk alasan ini, Anda harus selalu memeriksa jenis setiap nilai yang dikembalikan dan mentransmisikannya ke jenis yang sesuai sebelum mencoba mengakses salah satu nilai properti.

Kelas pembantu yang ditentukan di bawah ini menangani pemeriksaan jenis dan transmisi untuk ImageEncodingProperties atau VideoEncodingProperties sehingga kode aplikasi Anda tidak perlu membedakan antara dua jenis. Selain itu, kelas pembantu mengekspos properti untuk rasio aspek properti, kecepatan bingkai (hanya untuk properti pengodean video), dan nama yang mudah diingat yang memudahkan untuk menampilkan properti pengodean di UI aplikasi.

Anda harus menyertakan namespace Layanan Windows.Media.MediaProperties dalam file sumber untuk kelas pembantu.

using Windows.Media.MediaProperties;
using Windows.Media.Capture.Frames;
class StreamPropertiesHelper
{
    private IMediaEncodingProperties _properties;

    public StreamPropertiesHelper(IMediaEncodingProperties properties)
    {
        if (properties == null)
        {
            throw new ArgumentNullException(nameof(properties));
        }

        // This helper class only uses VideoEncodingProperties or VideoEncodingProperties
        if (!(properties is ImageEncodingProperties) && !(properties is VideoEncodingProperties))
        {
            throw new ArgumentException("Argument is of the wrong type. Required: " + typeof(ImageEncodingProperties).Name
                + " or " + typeof(VideoEncodingProperties).Name + ".", nameof(properties));
        }

        // Store the actual instance of the IMediaEncodingProperties for setting them later
        _properties = properties;
    }

    public uint Width
    {
        get
        {
            if (_properties is ImageEncodingProperties)
            {
                return (_properties as ImageEncodingProperties).Width;
            }
            else if (_properties is VideoEncodingProperties)
            {
                return (_properties as VideoEncodingProperties).Width;
            }

            return 0;
        }
    }

    public uint Height
    {
        get
        {
            if (_properties is ImageEncodingProperties)
            {
                return (_properties as ImageEncodingProperties).Height;
            }
            else if (_properties is VideoEncodingProperties)
            {
                return (_properties as VideoEncodingProperties).Height;
            }

            return 0;
        }
    }

    public uint FrameRate
    {
        get
        {
            if (_properties is VideoEncodingProperties)
            {
                if ((_properties as VideoEncodingProperties).FrameRate.Denominator != 0)
                {
                    return (_properties as VideoEncodingProperties).FrameRate.Numerator / 
                        (_properties as VideoEncodingProperties).FrameRate.Denominator;
                }
           }

            return 0;
        }
    }

    public double AspectRatio
    {
        get { return Math.Round((Height != 0) ? (Width / (double)Height) : double.NaN, 2); }
    }

    public IMediaEncodingProperties EncodingProperties
    {
        get { return _properties; }
    }

    public string GetFriendlyName(bool showFrameRate = true)
    {
        if (_properties is ImageEncodingProperties ||
            !showFrameRate)
        {
            return Width + "x" + Height + " [" + AspectRatio + "] " + _properties.Subtype;
        }
        else if (_properties is VideoEncodingProperties)
        {
            return Width + "x" + Height + " [" + AspectRatio + "] " + FrameRate + "FPS " + _properties.Subtype;
        }

        return String.Empty;
    }
    
}

Menentukan apakah pratinjau dan aliran pengambilan independen

Pada beberapa perangkat, pin perangkat keras yang sama digunakan untuk pratinjau dan menangkap aliran. Pada perangkat ini, mengatur properti pengodean dari satu juga akan mengatur yang lain. Pada perangkat yang menggunakan pin perangkat keras yang berbeda untuk pengambilan dan pratinjau, properti dapat diatur untuk setiap aliran secara independen. Gunakan kode berikut untuk menentukan apakah pratinjau dan aliran pengambilan independen. Anda harus menyesuaikan UI untuk mengaktifkan atau menonaktifkan pengaturan aliran secara independen berdasarkan hasil pengujian ini.

private void CheckIfStreamsAreIdentical()
{
    if (_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
        _mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
    {
        ShowMessageToUser("Preview and video streams for this device are identical. Changing one will affect the other");
    }
}

Mendapatkan daftar properti aliran yang tersedia

Dapatkan daftar properti streaming yang tersedia untuk perangkat pengambilan dengan mendapatkan VideoDeviceController untuk objek MediaCapture aplikasi Anda lalu memanggil GetAvailableMediaStreamProperties dan meneruskan salah satu nilai MediaStreamType , VideoPreview, VideoRecord, atau Photo. Dalam contoh ini, sintaks Linq digunakan untuk membuat daftar objek StreamPropertiesHelper , yang ditentukan sebelumnya dalam artikel ini, untuk setiap nilai IMediaEncodingProperties yang dikembalikan dari GetAvailableMediaStreamProperties. Contoh ini pertama kali menggunakan metode ekstensi Linq untuk memesan properti yang dikembalikan berdasarkan resolusi terlebih dahulu dan kemudian pada kecepatan bingkai.

Jika aplikasi Anda memiliki persyaratan resolusi atau kecepatan bingkai tertentu, Anda dapat memilih sekumpulan properti pengodean media secara terprogram. Aplikasi kamera umum akan mengekspos daftar properti yang tersedia di UI dan memungkinkan pengguna untuk memilih pengaturan yang diinginkan. ComboBoxItem dibuat untuk setiap item dalam daftar objek StreamPropertiesHelper dalam daftar. Konten diatur ke nama yang mudah diingat yang dikembalikan oleh kelas pembantu dan tag diatur ke kelas pembantu itu sendiri sehingga dapat digunakan nanti untuk mengambil properti pengodean terkait. Setiap ComboBoxItem kemudian ditambahkan ke ComboBox yang diteruskan ke metode .

private void PopulateStreamPropertiesUI(MediaStreamType streamType, ComboBox comboBox, bool showFrameRate = true)
{
    // Query all properties of the specified stream type 
    IEnumerable<StreamPropertiesHelper> allStreamProperties = 
        _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(streamType).Select(x => new StreamPropertiesHelper(x));

    // Order them by resolution then frame rate
    allStreamProperties = allStreamProperties.OrderByDescending(x => x.Height * x.Width).ThenByDescending(x => x.FrameRate);

    // Populate the combo box with the entries
    foreach (var property in allStreamProperties)
    {
        ComboBoxItem comboBoxItem = new ComboBoxItem();
        comboBoxItem.Content = property.GetFriendlyName(showFrameRate);
        comboBoxItem.Tag = property;
        comboBox.Items.Add(comboBoxItem);
    }
}

Mengatur properti aliran yang diinginkan

Beri tahu pengontrol perangkat video untuk menggunakan properti pengodean yang Anda inginkan dengan memanggil SetMediaStreamPropertiesAsync, meneruskan nilai MediaStreamType yang menunjukkan apakah properti foto, video, atau pratinjau harus diatur. Contoh ini mengatur properti pengodean yang diminta saat pengguna memilih item di salah satu objek ComboBox yang diisi dengan metode pembantu PopulateStreamPropertiesUI .

private async void PreviewSettings_Changed(object sender, RoutedEventArgs e)
{
    if (_isPreviewing)
    {
        var selectedItem = (sender as ComboBox).SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, encodingProperties);
    }
}
private async void PhotoSettings_Changed(object sender, RoutedEventArgs e)
{
    if (_isPreviewing)
    {
        var selectedItem = (sender as ComboBox).SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, encodingProperties);
    }
}
private async void VideoSettings_Changed(object sender, RoutedEventArgs e)
{
    if (_isPreviewing)
    {
        var selectedItem = (sender as ComboBox).SelectedItem as ComboBoxItem;
        var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
        await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, encodingProperties);
    }
}

Mencocokkan rasio aspek pratinjau dan menangkap aliran

Aplikasi kamera umum akan menyediakan UI bagi pengguna untuk memilih resolusi pengambilan video atau foto tetapi akan secara terprogram mengatur resolusi pratinjau. Ada beberapa strategi berbeda untuk memilih resolusi streaming pratinjau terbaik untuk aplikasi Anda:

  • Pilih resolusi pratinjau tertinggi yang tersedia, memungkinkan kerangka kerja UI melakukan penskalaan pratinjau yang diperlukan.

  • Pilih resolusi pratinjau yang paling dekat dengan resolusi tangkapan sehingga pratinjau menampilkan representasi terdekat dengan media yang ditangkap akhir.

  • Pilih resolusi pratinjau yang paling dekat dengan ukuran CaptureElement sehingga tidak ada lagi piksel yang diperlukan melalui alur aliran pratinjau.

Penting Dimungkinkan, pada beberapa perangkat, untuk mengatur rasio aspek yang berbeda untuk aliran pratinjau kamera dan menangkap aliran. Pemotongan bingkai yang disebabkan oleh ketidakcocokan ini dapat mengakibatkan konten ada di media yang ditangkap yang tidak terlihat dalam pratinjau yang dapat mengakibatkan pengalaman pengguna negatif. Sangat disarankan agar Anda menggunakan rasio aspek yang sama, dalam jendela toleransi kecil, untuk pratinjau dan menangkap aliran. Tidak masalah untuk mengaktifkan resolusi yang sama sekali berbeda untuk penangkapan dan pratinjau selama rasio aspek cocok dengan erat.

Untuk memastikan bahwa aliran pengambilan foto atau video cocok dengan rasio aspek aliran pratinjau, contoh ini memanggil VideoDeviceController.GetMediaStreamProperties dan meneruskan nilai enum VideoPreview untuk meminta properti streaming saat ini untuk aliran pratinjau. Selanjutnya jendela toleransi rasio aspek kecil didefinisikan sehingga kita dapat menyertakan rasio aspek yang tidak sama persis dengan aliran pratinjau, selama dekat. Selanjutnya, metode ekstensi Linq digunakan untuk memilih hanya objek StreamPropertiesHelper di mana rasio aspek berada dalam rentang toleransi yang ditentukan dari aliran pratinjau.

private void MatchPreviewAspectRatio(MediaStreamType streamType, ComboBox comboBox)
{
    // Query all properties of the specified stream type
    IEnumerable<StreamPropertiesHelper> allVideoProperties = 
        _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(streamType).Select(x => new StreamPropertiesHelper(x));

    // Query the current preview settings
    StreamPropertiesHelper previewProperties = new StreamPropertiesHelper(_mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview));

    // Get all formats that have the same-ish aspect ratio as the preview
    // Allow for some tolerance in the aspect ratio comparison
    const double ASPECT_RATIO_TOLERANCE = 0.015;
    var matchingFormats = allVideoProperties.Where(x => Math.Abs(x.AspectRatio - previewProperties.AspectRatio) < ASPECT_RATIO_TOLERANCE);

    // Order them by resolution then frame rate
    allVideoProperties = matchingFormats.OrderByDescending(x => x.Height * x.Width).ThenByDescending(x => x.FrameRate);

    // Clear out old entries and populate the video combo box with new matching entries
    comboBox.Items.Clear();
    foreach (var property in allVideoProperties)
    {
        ComboBoxItem comboBoxItem = new ComboBoxItem();
        comboBoxItem.Content = property.GetFriendlyName();
        comboBoxItem.Tag = property;
        comboBox.Items.Add(comboBoxItem);
    }
    comboBox.SelectedIndex = -1;
}