Définir le format, la résolution et la fréquence d’images pour MediaCapture

Cet article vous montre comment utiliser l’interface IMediaEncodingProperties pour définir la résolution et la fréquence d’images du flux d’aperçu de l’appareil photo et des photos et vidéos capturées. Il montre également comment s’assurer que le rapport d’aspect du flux d’aperçu correspond à celui du média capturé.

Les profils de caméra offrent un moyen plus avancé de découvrir et de définir les propriétés de flux de l’appareil photo, mais ils ne sont pas pris en charge pour tous les appareils. Pour plus d’informations, consultez profils caméra.

Le code de cet article a été adapté à partir de l’exemple CameraResolution. Vous pouvez télécharger l’exemple pour voir le code utilisé dans son contexte ou pour utiliser l’exemple comme point de départ pour votre propre application.

Remarque

Cet article repose sur les concepts et le code décrits dans Capture photo, vidéo et audio de base à l’aide de MediaCapture, qui décrit comment implémenter la capture photo et vidéo de base. Il est recommandé de vous familiariser avec le modèle de capture multimédia de base dans cet article avant de passer à des scénarios de capture plus avancés. Le code de cet article suppose que votre application a déjà une instance de MediaCapture qui a été correctement initialisée.

Classe d’assistance des propriétés d’encodage multimédia

La création d’une classe d’assistance simple pour encapsuler les fonctionnalités de l’interface IMediaEncodingProperties facilite la sélection d’un ensemble de propriétés d’encodage répondant à des critères particuliers. Cette classe d’assistance est particulièrement utile en raison du comportement suivant de la fonctionnalité de propriétés d’encodage :

Avertissement La méthode VideoDeviceController.GetAvailableMediaStreamProperties prend un membre de l’énumération MediaStreamType, telle que VideoRecord ou Photo, et retourne une liste d’objets ImageEncodingProperties ou VideoEncodingProperties qui transmettent les paramètres d’encodage de flux, tels que la résolution de la photo ou de la vidéo capturée. Les résultats de l’appel de GetAvailableMediaStreamProperties peuvent inclure ImageEncodingProperties ou VideoEncodingProperties , quelle que soit la valeur MediaStreamType spécifiée. Pour cette raison, vous devez toujours vérifier le type de chaque valeur retournée et le convertir en type approprié avant de tenter d’accéder à l’une des valeurs de propriété.

La classe d’assistance définie ci-dessous gère la vérification et le cast de type pour ImageEncodingProperties ou VideoEncodingProperties afin que le code de votre application n’ait pas besoin de faire la distinction entre les deux types. En plus de cela, la classe d’assistance expose les propriétés pour les proportions des propriétés, la fréquence d’images (pour les propriétés d’encodage vidéo uniquement) et un nom convivial qui facilite l’affichage des propriétés d’encodage dans l’interface utilisateur de l’application.

Vous devez inclure l’espace de noms Windows.Media.MediaProperties dans le fichier source de la classe d’assistance.

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

Déterminer si les flux d’aperçu et de capture sont indépendants

Sur certains appareils, la même broche matérielle est utilisée pour les flux d’aperçu et de capture. Sur ces appareils, la définition des propriétés d’encodage de l’un définit également l’autre. Sur les appareils qui utilisent différentes broches matérielles pour la capture et la préversion, les propriétés peuvent être définies pour chaque flux indépendamment. Utilisez le code suivant pour déterminer si les flux d’aperçu et de capture sont indépendants. Vous devez ajuster votre interface utilisateur pour activer ou désactiver le paramètre des flux indépendamment en fonction du résultat de ce 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");
    }
}

Obtenir la liste des propriétés de flux disponibles

Obtenez la liste des propriétés de flux disponibles pour un appareil de capture en obtenant VideoDeviceController pour l’objet MediaCapture de votre application, puis en appelant GetAvailableMediaStreamProperties et en passant l’une des valeurs MediaStreamType, VideoPreview, VideoRecord ou Photo. Dans cet exemple, la syntaxe Linq est utilisée pour créer une liste d’objets StreamPropertiesHelper, définis précédemment dans cet article, pour chacune des valeurs IMediaEncodingProperties retournées par GetAvailableMediaStreamProperties. Cet exemple utilise d’abord les méthodes d’extension Linq pour classer les propriétés retournées en fonction de la résolution, puis de la fréquence d’images.

Si votre application a des exigences de résolution ou de fréquence d’images spécifiques, vous pouvez sélectionner un ensemble de propriétés d’encodage multimédia par programmation. Une application caméra classique expose plutôt la liste des propriétés disponibles dans l’interface utilisateur et permet à l’utilisateur de sélectionner ses paramètres souhaités. Un ComboBoxItem est créé pour chaque élément de la liste des objets StreamPropertiesHelper de la liste. Le contenu est défini sur le nom convivial retourné par la classe d’assistance et la balise est définie sur la classe d’assistance elle-même afin qu’elle puisse être utilisée ultérieurement pour récupérer les propriétés d’encodage associées. Chaque ComboBoxItem est ensuite ajouté au ComboBox passé dans la méthode.

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

Définir les propriétés de flux souhaitées

Indiquez au contrôleur d’appareil vidéo d’utiliser vos propriétés d’encodage souhaitées en appelant SetMediaStreamPropertiesAsync, en passant la valeur MediaStreamType indiquant si les propriétés photo, vidéo ou d’aperçu doivent être définies. Cet exemple montre comment définir les propriétés d’encodage demandées lorsque l’utilisateur sélectionne un élément dans l’un des objets ComboBox renseignés avec la méthode d’assistance 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);
    }
}

Mettre en correspondance les proportions des flux d’aperçu et de capture

Une application de caméra classique fournit une interface utilisateur à l’utilisateur pour sélectionner la résolution de capture vidéo ou photo, mais définit par programme la résolution d’aperçu. Il existe quelques stratégies différentes pour sélectionner la meilleure résolution de flux d’aperçu pour votre application :

  • Sélectionnez la résolution de préversion la plus élevée disponible, ce qui permet à l’infrastructure d’interface utilisateur d’effectuer toute mise à l’échelle nécessaire de la préversion.

  • Sélectionnez la résolution d’aperçu la plus proche de la résolution de capture afin que l’aperçu affiche la représentation la plus proche du média capturé final.

  • Sélectionnez la résolution d’aperçu la plus proche de la taille du CaptureElement afin qu’il n’y ait plus de pixels que nécessaire dans le pipeline de flux d’aperçu.

Important , il est possible, sur certains appareils, de définir un rapport d’aspect différent pour le flux d’aperçu de l’appareil photo et le flux de capture. Le rognage d’images provoqué par cette incompatibilité peut entraîner la présence de contenu dans le média capturé qui n’a pas été visible dans l’aperçu, ce qui peut entraîner une expérience utilisateur négative. Il est fortement recommandé d’utiliser le même rapport d’aspect, dans une petite fenêtre de tolérance, pour les flux d’aperçu et de capture. Il est très bon d’avoir des résolutions entièrement différentes activées pour la capture et l’aperçu tant que le rapport d’aspect correspond étroitement.

Pour vous assurer que les flux de capture photo ou vidéo correspondent aux proportions du flux d’aperçu, cet exemple appelle VideoDeviceController.GetMediaStreamProperties et passe la valeur enum VideoPreview pour demander les propriétés de flux actuelles pour le flux d’aperçu. Ensuite, une petite fenêtre de tolérance des proportions est définie afin que nous puissions inclure des proportions qui ne sont pas exactement identiques au flux d’aperçu, tant qu’elles sont proches. Ensuite, une méthode d’extension Linq est utilisée pour sélectionner uniquement les objets StreamPropertiesHelper où le rapport d’aspect se trouve dans la plage de tolérance définie du flux d’aperçu.

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