Установка формата, разрешения и частоты кадров для MediaCaptureSet format, resolution, and frame rate for MediaCapture

В этой статье рассказывается, как с помощью интерфейса IMediaEncodingProperties задать разрешение и частоту кадров потока предварительного просмотра камеры, а также снятых фотографий и видео.This article shows you how to use the IMediaEncodingProperties interface to set the resolution and frame rate of the camera preview stream and captured photos and video. Кроме того, здесь рассказывается, как обеспечить соответствие пропорций потока предварительного просмотра пропорциям записи мультимедиа.It also shows how to ensure that the aspect ratio of the preview stream matches that of the captured media.

Профили камеры предлагают более современный способ обнаружения и задания свойств потока камеры, но они поддерживаются не на всех устройствах.Camera profiles offer a more advanced way of discovering and setting the stream properties of the camera, but they are not supported for all devices. Дополнительные сведения см. в разделе Профили камеры.For more information, see Camera profiles.

В этой статье используется код, адаптированный из примера CameraResolution.The code in this article was adapted from the CameraResolution sample. Вы можете скачать этот пример, чтобы просмотреть код в контексте или использовать пример как отправную точку для настройки собственного приложения.You can download the sample to see the code used in context or to use the sample as a starting point for your own app.

Примечание

В этой статье используются понятия и код из статьи Основные принципы фото-, аудио- и видеозахвата с помощью MediaCapture, в которой описаны этапы реализации основных принципов фото- и видеозахвата.This article builds on concepts and code discussed in Basic photo, video, and audio capture with MediaCapture, which describes the steps for implementing basic photo and video capture. Мы рекомендуем ознакомиться с базовым шаблоном захвата мультимедиа в этой статье, прежде чем перейти к более сложным сценариям захвата.It is recommended that you familiarize yourself with the basic media capture pattern in that article before moving on to more advanced capture scenarios. Код в этой статье подразумевает, что ваше приложение уже содержит экземпляр MediaCapture, инициализированный надлежащим образом.The code in this article assumes that your app already has an instance of MediaCapture that has been properly initialized.

Вспомогательный класс свойств кодирования мультимедиаA media encoding properties helper class

Если создать простой вспомогательный класс, включающий функции интерфейса IMediaEncodingProperties, будет удобнее выбирать набор свойств кодирования, которые отвечают конкретным критериям.Creating a simple helper class to wrap the functionality of the IMediaEncodingProperties interface makes it easier to select a set of encoding properties that meet particular criteria. Этот вспомогательный класс особенно полезен для следующего поведения свойств кодирования.This helper class is particularly useful due to the following behavior of the encoding properties feature:

Предупреждение об ошибке    Метод видеодевицеконтроллер. жетаваилаблемедиастреампропертиес принимает член перечисления Медиастреамтипе , например видеорекорд или Photo, и возвращает список объектов ImageEncodingProperties или VideoEncodingProperties , которые передают параметры кодирования потока, такие как разрешение захваченной фотографии или видео.Warning   The VideoDeviceController.GetAvailableMediaStreamProperties method takes a member of the MediaStreamType enumeration, such as VideoRecord or Photo, and returns a list of either ImageEncodingProperties or VideoEncodingProperties objects that convey the stream encoding settings, such as the resolution of the captured photo or video. Результаты вызова GetAvailableMediaStreamProperties могут включать ImageEncodingProperties или VideoEncodingProperties, независимо от того, какое указано значение MediaStreamType.The results of calling GetAvailableMediaStreamProperties may include ImageEncodingProperties or VideoEncodingProperties regardless of what MediaStreamType value is specified. По этой причине необходимо всегда проверять тип каждого возвращенного значения и приводить его к соответствующему типу, прежде чем пытаться получить доступ к какому-либо значению свойства.For this reason, you should always check the type of each returned value and cast it to the appropriate type before attempting to access any of the property values.

Вспомогательный класс, определенный ниже, выполняет проверку и приведение типа для параметра ImageEncodingProperties или VideoEncodingProperties, чтобы коду приложения не нужно было различать два типа.The helper class defined below handles the type checking and casting for ImageEncodingProperties or VideoEncodingProperties so that your app code doesn't need to distinguish between the two types. В дополнение к этому, вспомогательный класс представляет свойства для пропорций свойств, частоты кадров (только для свойств кодирования видео) и понятного имени, что упрощает отображение свойств кодирования в пользовательском интерфейсе приложения.In addition to this, the helper class exposes properties for the aspect ratio of the properties, the frame rate (for video encoding properties only), and a friendly name that makes it easier to display the encoding properties in the app's UI.

Вы должны включить пространство имен Windows.Media.MediaProperties в исходный файл для вспомогательного класса.You must include the Windows.Media.MediaProperties namespace in the source file for the helper class.

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

Определение независимости потоков предварительного просмотра и захватаDetermine if the preview and capture streams are independent

На некоторых устройствах один и тот же контакт оборудования используется как для потока предварительного просмотра, так и для потока захвата.On some devices, the same hardware pin is used for both preview and capture streams. На таких устройствах при задании свойств кодирования для одного потока свойства задаются и для другого потока.On these devices, setting the encoding properties of one will also set the other. На устройствах, на которых для захвата и предварительного просмотра используются разные контакты, свойства можно задать для каждого потока независимо.On devices that use different hardware pins for capture and preview, the properties can be set for each stream independently. Используйте следующий код, чтобы определить, независимы ли потоки предварительного просмотра и захвата.Use the following code to determine if the preview and capture streams are independent. Необходимо настроить пользовательский интерфейс, чтобы он включал и отключал параметр потоков независимо на основе результата этой проверки.You should adjust your UI to enable or disable the setting of the streams independently based on the result of this test.

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

Получение списка доступных свойств потокаGet a list of available stream properties

Чтобы получить список доступных свойств потока для устройства захвата, получите VideoDeviceController для объекта MediaCapture своего приложения, а затем вызовите GetAvailableMediaStreamProperties и передайте одно из значений MediaStreamType, VideoPreview, VideoRecord или Photo.Get a list of the available stream properties for a capture device by getting the VideoDeviceController for your app's MediaCapture object and then calling GetAvailableMediaStreamProperties and passing in one of the MediaStreamType values, VideoPreview, VideoRecord, or Photo. В этом примере синтаксис Linq используется для создания списка объектов StreamPropertiesHelper, определенных ранее в этой статье, для каждого из значений IMediaEncodingProperties, которые возвращаются функцией GetAvailableMediaStreamProperties.In this example, Linq syntax is used to create a list of StreamPropertiesHelper objects, defined previously in this article, for each of the IMediaEncodingProperties values returned from GetAvailableMediaStreamProperties. В этом примере методы расширения Linq сначала используются для расстановки возвращаемых свойств сперва на основе разрешения, а потом уже на основе частоты кадров.This example first uses Linq extension methods to order the returned properties based first on resolution and then on frame rate.

Если у вашего приложения есть конкретные требования к разрешению или частоте кадров, можно выбрать набор свойств кодирования мультимедиа программным путем.If your app has specific resolution or frame rate requirements, you can select a set of media encoding properties programmatically. Тогда обычное приложение камеры отобразит список доступных свойств в пользовательском интерфейсе и позволит пользователю выбрать нужные параметры.A typical camera app will instead expose the list of available properties in the UI and allow the user to select their desired settings. ComboBoxItem создается для каждого элемента в списке объектов StreamPropertiesHelper в списке.A ComboBoxItem is created for each item in the list of StreamPropertiesHelper objects in the list. Для содержимого устанавливается понятное имя, возвращенное вспомогательным классом, а для тега устанавливается сам вспомогательный класс, чтобы его можно было использовать позже для получения связанных свойств кодирования.The content is set to the friendly name returned by the helper class and the tag is set to the helper class itself so it can be used later to retrieve the associated encoding properties. Каждый элемент ComboBoxItem затем добавляется в объект ComboBox, который передается в метод.Each ComboBoxItem is then added to the ComboBox passed into the method.

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

Установка необходимых свойств потокаSet the desired stream properties

Чтобы контроллер видеоустройств использовал нужные свойства кодирования, вызовите SetMediaStreamPropertiesAsync и передайте значение MediaStreamType, определяющее, нужно ли задавать свойства фотографии, видео или предварительного просмотра.Tell the video device controller to use your desired encoding properties by calling SetMediaStreamPropertiesAsync, passing in the MediaStreamType value indicating whether the photo, video, or preview properties should be set. В этом примере задаются запрашиваемые свойства кодирования, когда пользователь выбирает элемент в одном из объектов ComboBox, которые заполняются вспомогательным методом PopulateStreamPropertiesUI.This example sets the requested encoding properties when the user selects an item in one of the ComboBox objects populated with the PopulateStreamPropertiesUI helper method.

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

Сопоставление пропорций потоков предварительного просмотра и захватаMatch the aspect ratio of the preview and capture streams

Типичное приложение камеры предоставляет пользовательский интерфейс для выбора разрешения захвата видео или фото, но разрешение предварительного просмотра устанавливает программным способом.A typical camera app will provide UI for the user to select the video or photo capture resolution but will programmatically set the preview resolution. Существуют несколько стратегий выбора наилучшего разрешения потока предварительного просмотра для вашего приложения.There are a few different strategies for selecting the best preview stream resolution for your app:

  • Выберите наивысшее доступное разрешение предварительного просмотра, предоставив инфраструктуре пользовательского интерфейса выполнять необходимое масштабирование предварительного просмотра.Select the highest available preview resolution, letting the UI framework perform any necessary scaling of the preview.

  • Выберите разрешение предварительного просмотра, наиболее близкое к разрешению захвата, чтобы предварительный просмотр был как можно ближе к итоговому захваченному мультимедиа.Select the preview resolution closest to the capture resolution so that the preview displays the closest representation to the final captured media.

  • Выберите разрешение предварительного просмотра, наиболее близкое к размеру CaptureElement, чтобы через конвейер потока предварительного просмотра проходило столько пикселов, сколько нужно, но не более того.Select the preview resolution closest to the size of the CaptureElement so that no more pixels than necessary are going through the preview stream pipeline.

Важно!    На некоторых устройствах можно задать различные пропорции для потока просмотра и потока записи камеры.Important   It is possible, on some devices, to set a different aspect ratio for the camera's preview stream and capture stream. Обрезка кадра в результате такого несоответствия может привести к наличию в записи мультимедиа содержимого, которого не было видно в режиме предварительного просмотра, что может вызвать недовольство пользователей.Frame cropping caused by this mismatch can result in content being present in the captured media that was not visible in the preview which can result in a negative user experience. Настоятельно рекомендуется использовать одинаковые пропорции (с небольшим диапазоном допуска) для потоков предварительного просмотра и захвата.It is strongly recommended that you use the same aspect ratio, within a small tolerance window, for the preview and capture streams. Можно иметь совершенно различные разрешения, доступные для захвата и предварительного просмотра, пока их пропорции максимально соответствуют друг другу.It is fine to have entirely different resolutions enabled for capture and preview as long as the aspect ratio match closely.

Чтобы обеспечить соответствие потоков фото- и видеозахвата пропорциям потока предварительного просмотра, в этом примере вызывается VideoDeviceController.GetMediaStreamProperties и передается значение перечисления VideoPreview для запроса текущих свойств потока предварительного просмотра.To ensure that the photo or video capture streams match the aspect ratio of the preview stream, this example calls VideoDeviceController.GetMediaStreamProperties and passes in the VideoPreview enum value to request the current stream properties for the preview stream. Затем определяется небольшой диапазон допуска для пропорций, чтобы можно было включать пропорции, которые не совпадают с потоком предварительного просмотра полностью, но которые близки к этому.Next a small aspect ratio tolerance window is defined so that we can include aspect ratios that are not exactly the same as the preview stream, as long as they are close. Далее метод расширения Linq используется для выбора только тех объектов StreamPropertiesHelper, пропорции которых входят в указанный диапазон допуска потока предварительного просмотра.Next, a Linq extension method is used to select just the StreamPropertiesHelper objects where the aspect ratio is within the defined tolerance range of the preview stream.

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