Mengatur format, resolusi, dan frame rate untuk MediaCapture

Artikel ini menunjukkan kepada Anda cara menggunakan antarmuka IMediaEncodingProperties untuk mengatur resolusi dan laju bingkai aliran pratinjau kamera dan foto dan video yang diambil. 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 didasarkan pada 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 itu sebelum beralih ke skenario pengambilan yang lebih maju. Kode dalam artikel ini mengasumsikan bahwa aplikasi Anda sudah memiliki instance MediaCapture yang telah diinsialisasi dengan benar.

Kelas pembantu properti pengkodean media

Membuat kelas pembantu sederhana untuk membungkus fungsionalitas antarmuka IMediaEncodingProperties membuatnya lebih mudah untuk memilih satu set properti pengkodean yang memenuhi kriteria tertentu. Kelas pembantu ini sangat berguna karena perilaku berikut dari fitur properti pengkodean:

Peringatan Metode VideoDeviceController.GetAvailableMediaStreamProperties mengambil anggota pencacahan MediaStreamType , seperti VideoRecord atau Photo, dan mengembalikan daftar objek ImageEncodingProperties atau VideoEncodingProperties yang menyampaikan pengaturan pengkodean aliran, seperti resolusi foto atau video yang diambil. Hasil panggilan GetAvailableMediaStreamProperties mungkin termasuk ImageEncodingProperties atau VideoEncodingProperties terlepas dari nilai MediaStreamType apa yang ditentukan. Untuk alasan ini, Anda harus selalu memeriksa jenis setiap nilai yang dikembalikan dan melemparkannya ke jenis yang sesuai sebelum mencoba mengakses salah satu nilai properti.

Kelas pembantu yang didefinisikan di bawah ini menangani pemeriksaan tipe dan casting untuk ImageEncodingProperties atau VideoEncodingProperties sehingga kode aplikasi Anda tidak perlu membedakan antara kedua jenis tersebut. Selain itu, kelas pembantu mengekspos properti untuk rasio aspek properti, frame rate (hanya untuk properti pengkodean video), dan nama ramah yang membuatnya lebih mudah untuk menampilkan properti pengkodean di UI aplikasi.

Anda harus menyertakan Windows. Media.MediaProperties namespace 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 capture stream bersifat independen

Pada beberapa perangkat, pin perangkat keras yang sama digunakan untuk pratinjau dan menangkap aliran. Pada perangkat ini, mengatur properti pengkodean 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 streaming tangkapan independen. Anda harus menyesuaikan UI Anda 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 streaming yang tersedia

Dapatkan daftar properti stream yang tersedia untuk perangkat capture dengan mendapatkan VideoDeviceController untuk objek MediaCapture aplikasi Anda dan kemudian memanggil GetAvailableMediaStreamProperties dan meneruskan salah satu nilai MediaStreamType , VideoPreview, VideoRecord, atau Photo. Dalam contoh ini, sintaks Linq digunakan untuk membuat daftar objek StreamPropertiesHelper , yang didefinisikan sebelumnya dalam artikel ini, untuk masing-masing nilai IMediaEncodingProperties yang dikembalikan dari GetAvailableMediaStreamProperties. Contoh ini pertama menggunakan metode ekstensi Linq untuk memesan properti yang dikembalikan berdasarkan resolusi dan kemudian pada frame rate.

Jika aplikasi Anda memiliki persyaratan resolusi atau frame rate tertentu, Anda dapat memilih satu set properti pengkodean media secara terprogram. Aplikasi kamera yang khas malah 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 ramah yang dikembalikan oleh kelas pembantu dan tag diatur ke kelas pembantu itu sendiri sehingga dapat digunakan nanti untuk mengambil properti pengkodean terkait. Setiap ComboBoxItem kemudian ditambahkan ke ComboBox yang diteruskan ke dalam 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 stream yang diinginkan

Beri tahu pengontrol perangkat video untuk menggunakan properti pengkodean yang Anda inginkan dengan memanggil SetMediaStreamPropertiesAsync, meneruskan nilai MediaStreamType yang menunjukkan apakah properti foto, video, atau pratinjau harus diatur. Contoh ini menetapkan properti pengkodean 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);
    }
}

Cocokkan rasio aspek pratinjau dan menangkap aliran

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

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

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

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

Penting Dimungkinkan, pada beberapa perangkat, untuk mengatur rasio aspek yang berbeda untuk aliran pratinjau kamera dan aliran tangkapan. Pemotongan bingkai yang disebabkan oleh ketidakcocokan ini dapat mengakibatkan konten hadir di media yang ditangkap yang tidak terlihat dalam pratinjau yang dapat menghasilkan pengalaman pengguna yang negatif. Sangat disarankan agar Anda menggunakan rasio aspek yang sama, dalam jendela toleransi kecil, untuk pratinjau dan menangkap aliran. Tidak apa-apa untuk mengaktifkan resolusi yang sama sekali berbeda untuk pengambilan dan pratinjau selama rasio aspeknya sangat cocok.

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 persis sama dengan aliran pratinjau, selama mereka dekat. Selanjutnya, metode ekstensi Linq digunakan untuk memilih hanya objek StreamPropertiesHelper di mana rasio aspek berada dalam kisaran 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;
}