Настройка формата, разрешения и частоты кадров для MediaCapture в приложении WinUI 3
В этой статье показано, как использовать интерфейс IMediaEncodingProperties для настройки разрешения и частоты кадров потока предварительного просмотра камеры и записи фотографий и видео. Кроме того, показано, как убедиться, что соотношение сторон потока предварительного просмотра соответствует соотношению сторон захваченного медиа.
Профили камеры предлагают более простой механизм более высокого уровня для обнаружения и настройки свойств потока камеры, но они не поддерживаются для всех устройств. Дополнительные сведения см. в профилях камеры.
Определите, независимы ли потоки предварительного просмотра и записи
На некоторых устройствах один и тот же аппаратный контакт используется как для потока предпросмотра, так и для потока записи. На этих устройствах настройка свойств кодирования, таких как формат, разрешение и частота кадров, будут влиять на оба параметра. На устройствах, использующих различные аппаратные выводы для захвата и предварительного просмотра, свойства можно задать для каждого потока независимо. Используйте следующий код, чтобы определить, являются ли потоки предварительного просмотра и захвата независимыми. В этом примере задает логическую глобальную переменную, которую можно использовать для переключения поведения приложения, если потоки являются общими или независимыми.
if (m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
{
m_captureAndPreviewStreamsIdentical = true;
}
Вспомогательный класс свойств кодирования мультимедиа
Создание простого вспомогательного класса для упаковки функциональных возможностей интерфейса IMediaEncodingProperties упрощает выбор набора свойств кодирования, удовлетворяющих определенным критериям. Этот вспомогательный класс особенно полезен из-за следующего поведения функции свойств кодирования:
Заметка
Метод VideoDeviceController.GetAvailableMediaStreamProperties принимает член перечисления MediaStreamType, например, VideoRecord или Photo, и возвращает список ImageEncodingProperties или VideoEncodingProperties, содержащих параметры кодирования потока, например, разрешение захваченной фотографии или видео. Результаты вызова GetAvailableMediaStreamProperties могут включать ImageEncodingProperties или VideoEncodingProperties независимо от указанного значения MediaStreamType. По этой причине необходимо всегда проверять тип каждого возвращаемого значения и приведения его к соответствующему типу перед попыткой доступа к любому из значений свойств.
Вспомогательный класс, определенный ниже, работает с проверкой и приведением типов для ImageEncodingProperties или VideoEncodingProperties, чтобы код вашего приложения не нуждался в различении между двумя типами. Помимо этого, вспомогательный класс предоставляет свойства для пропорций свойств, частоты кадров (только для свойств кодирования видео) и понятного имени, что упрощает отображение свойств кодирования в пользовательском интерфейсе.
class StreamPropertiesHelper
{
private IMediaEncodingProperties _properties;
public StreamPropertiesHelper(IMediaEncodingProperties properties)
{
if (properties == null)
{
throw new ArgumentNullException(nameof(properties));
}
// This helper class only uses ImageEncodingProperties 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;
}
}
Получение списка доступных свойств потока
Получите список доступных свойств потока для устройства захвата, получив VideoDeviceController для объекта вашего приложения MediaCapture, а затем вызовите GetAvailableMediaStreamProperties и передайте одно из значений MediaStreamType: VideoPreview, VideoRecordили Photo. В этом примере для каждого из значений IMediaEncodingProperties, возвращаемых из GetAvailableMediaStreamProperties, создаётся список объектов StreamPropertiesHelper, которые были определены ранее в статье. В этом примере возвращаемые свойства упорядочены сначала по разрешению, а затем по частоте кадров.
Если приложение имеет определенные требования к разрешению или частоте кадров, можно выбрать набор свойств кодирования мультимедиа программным способом. Обычное приложение камеры вместо этого предоставит список доступных свойств в пользовательском интерфейсе и позволит пользователю выбрать нужные параметры. Для каждого элемента в списке объектов StreamPropertiesHelper создается ComboBoxItem. Содержимое присваивается понятному имени, возвращаемого вспомогательным классом, и тег задается для самого вспомогательного класса, чтобы его можно было использовать позже для получения связанных свойств кодирования. Затем каждый ComboBoxItem добавляется в ComboBox, который определяется в пользовательском интерфейсе.
private void bGetStreamProperties_Click(object sender, RoutedEventArgs e)
{
// Query all properties of the specified stream type
IEnumerable<StreamPropertiesHelper> allStreamProperties =
m_mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).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();
comboBoxItem.Tag = property;
cbStreamProperties.Items.Add(comboBoxItem);
}
}
Установите требуемые свойства потока
Сообщите контроллеру видеоустройства использовать нужные свойства кодирования путем вызова SetMediaStreamPropertiesAsync, передавая значение MediaStreamType, указывающее, следует ли задавать свойства фотографии, видео или предварительного просмотра. В этом примере используется ComboBox, заполненный в примере из предыдущего раздела, где свойства мультимедийного потока извлекаются из тега выбранного элемента.
private async void bSetStreamProperties_Click(object sender, RoutedEventArgs e)
{
if (m_exclusiveCameraAccess)
{
var selectedItem = cbStreamProperties.SelectedItem as ComboBoxItem;
var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
await m_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, encodingProperties);
}
}
Обратите внимание, что приложение должно иметь монопольный контроль над устройством записи, чтобы изменить свойства потока мультимедиа.
Сопоставление пропорций потоков предварительного просмотра и записи
Обычное приложение камеры предоставляет пользовательский интерфейс для пользователя, чтобы выбрать разрешение видео или фотосъемки, но программным способом установит разрешение предварительного просмотра. Существует несколько различных стратегий выбора оптимального разрешения потоков предварительной версии для приложения:
Выберите максимально доступное разрешение предварительной версии, позволяя платформе пользовательского интерфейса выполнять любое необходимое масштабирование предварительной версии.
Выберите разрешение предварительного просмотра, наиболее близкое к разрешению записи, чтобы предварительный просмотр отображал максимально близкое представление к окончательному захваченному медиаконтенту.
Выберите разрешение предварительного просмотра, ближайшее к размеру CaptureElement, чтобы не более пикселей, чем необходимо, проходить через конвейер потоков предварительного просмотра.
Заметка
На некоторых устройствах можно задать другое соотношение сторон для потока превью камеры и потока записи. Обрезка кадров, вызванная этим несоответствием, может привести к тому, что в захваченном материале содержится контент, который не был виден в предварительном просмотре, что может привести к негативному пользовательскому опыту. Настоятельно рекомендуется использовать одно и то же соотношение пропорций в небольшом окне терпимости для потоков предварительного просмотра и записи. Это нормально иметь совершенно разные разрешения для записи и предварительного просмотра, если пропорции тесно соответствуют.
Чтобы убедиться, что потоки фото или видеозахвата соответствуют пропорциям потока предварительного просмотра, в этом примере вызывается VideoDeviceController.GetMediaStreamProperties и передается значение перечисления VideoPreview для запроса текущих свойств потока для предварительного просмотра. Далее задается небольшое окно допустимых отклонений для соотношений сторон, чтобы мы могли включать соотношения, которые не в точности совпадают с потоком предварительного просмотра, если они близки. Затем выбираются объекты StreamPropertiesHelper, где пропорции находятся в пределах определенного диапазона допустимости потока предварительного просмотра.
// Query all properties of the specified stream type
IEnumerable<StreamPropertiesHelper> allVideoProperties =
m_mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).Select(x => new StreamPropertiesHelper(x));
// Query the current preview settings
StreamPropertiesHelper previewProperties = new StreamPropertiesHelper(m_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
cbStreamProperties.Items.Clear();
foreach (var property in allVideoProperties)
{
ComboBoxItem comboBoxItem = new ComboBoxItem();
comboBoxItem.Content = property.GetFriendlyName();
comboBoxItem.Tag = property;
cbStreamProperties.Items.Add(comboBoxItem);
}
Фрагмент кодаMatchPreviewAspectRatio
Windows developer