Partager via


Gérer l’orientation de l’appareil avec MediaCapture

Lorsque votre application capture une photo ou une vidéo destinée à être affichée en dehors de votre application, telle que l’enregistrement dans un fichier sur l’appareil de l’utilisateur ou le partage en ligne, il est important que vous encodez l’image avec les métadonnées d’orientation appropriées afin que lorsque l’autre application ou appareil affiche l’image, elle est orientée correctement. La détermination des données d’orientation correctes à inclure dans un fichier multimédia peut être une tâche complexe, car il existe plusieurs variables à prendre en compte, notamment l’orientation du châssis de l’appareil, l’orientation de l’affichage et le positionnement de la caméra sur le châssis (qu’il s’agisse d’une caméra avant ou arrière).

Pour simplifier le processus de gestion de l’orientation, nous vous recommandons d’utiliser une classe d’assistance, CameraRotationHelper, pour laquelle la définition complète est fournie à la fin de cet article. Vous pouvez ajouter cette classe à votre projet, puis suivre les étapes décrites dans cet article pour ajouter la prise en charge de l’orientation à votre application de caméra. La classe d’assistance vous permet également de faire pivoter plus facilement les contrôles dans votre interface utilisateur de caméra afin qu’ils soient rendus correctement du point de vue de l’utilisateur.

Remarque

Cet article s’appuie sur le code et les concepts abordés dans l’article Capture photo, vidéo et audio de base avec MediaCapture. Nous vous recommandons de vous familiariser avec les concepts de base de l’utilisation de la classe MediaCapture avant d’ajouter la prise en charge de l’orientation à votre application.

Espaces de noms utilisés dans cet article

L’exemple de code de cet article utilise des API à partir des espaces de noms suivants que vous devez inclure dans votre code.

using Windows.Devices.Enumeration;
using Windows.UI.Core;

La première étape de l’ajout de la prise en charge de l’orientation à votre application consiste à verrouiller l’affichage afin qu’il ne pivote pas automatiquement lorsque l’appareil est pivoté. La rotation automatique de l’interface utilisateur fonctionne bien pour la plupart des types d’applications, mais elle n’est pas intuitive pour les utilisateurs lorsque l’aperçu de l’appareil photo pivote. Verrouillez l’orientation d’affichage en définissant la propriété DisplayInformation.AutoRotationPreferences sur DisplayOrientations.Landscape.

DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;

Suivi de l’emplacement de l’appareil photo

Pour calculer l’orientation correcte du média capturé, vous devez déterminer l’emplacement de l’appareil photo sur le châssis. Ajoutez une variable membre booléenne pour déterminer si la caméra est externe à l’appareil, par exemple une caméra web USB. Ajoutez une autre variable booléenne pour déterminer si l’aperçu doit être mis en miroir, ce qui est le cas si une caméra frontale est utilisée. Ajoutez également une variable pour stocker un objet DeviceInformation qui représente l’appareil photo sélectionné.

private bool _externalCamera;
private bool _mirroringPreview;
DeviceInformation _cameraDevice;

Sélectionnez un appareil photo et initialisez l’objet MediaCapture

L’article De base photo, vidéo et capture audio avec MediaCapture vous montre comment initialiser l’objet MediaCapture avec seulement quelques lignes de code. Pour prendre en charge l’orientation de la caméra, nous allons ajouter quelques étapes supplémentaires au processus d’initialisation.

Tout d’abord, appelez DeviceInformation.FindAllAsync en passant le sélecteur d’appareil DeviceClass.VideoCapture pour obtenir la liste de tous les appareils de capture vidéo disponibles. Ensuite, sélectionnez le premier appareil dans la liste où l’emplacement du panneau de l’appareil photo est connu et où il correspond à la valeur fournie, qui dans cet exemple est une caméra frontale. Si aucune caméra n’est trouvée sur le panneau souhaité, la première ou la caméra disponible par défaut est utilisée.

Si un appareil photo est trouvé, un nouvel objet MediaCaptureInitializationSettings est créé et la propriété VideoDeviceId est définie sur l’appareil sélectionné. Ensuite, créez l’objet MediaCapture et appelez InitializeAsync, en passant l’objet settings pour indiquer au système d’utiliser la caméra sélectionnée.

Enfin, vérifiez si le panneau d’appareil sélectionné est null ou inconnu. Dans ce cas, la caméra est externe, ce qui signifie que sa rotation n’est pas liée à la rotation de l’appareil. Si le panneau est connu et se trouve devant le châssis de l’appareil, nous savons que l’aperçu doit être mis en miroir, de sorte que le suivi des variables est défini.

var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
DeviceInformation desiredDevice = allVideoDevices.FirstOrDefault(x => x.EnclosureLocation != null 
    && x.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
_cameraDevice = desiredDevice ?? allVideoDevices.FirstOrDefault();


if (_cameraDevice == null)
{
    System.Diagnostics.Debug.WriteLine("No camera device found!");
    return;
}

var settings = new MediaCaptureInitializationSettings { VideoDeviceId = _cameraDevice.Id };

mediaCapture = new MediaCapture();
mediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;
mediaCapture.Failed += MediaCapture_Failed;

try
{
    await mediaCapture.InitializeAsync(settings);
}
catch (UnauthorizedAccessException)
{
    System.Diagnostics.Debug.WriteLine("The app was denied access to the camera");
    return;
}

// Handle camera device location
if (_cameraDevice.EnclosureLocation == null || 
    _cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown)
{
    _externalCamera = true;
}
else
{
    _externalCamera = false;
    _mirroringPreview = (_cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
}

Initialiser la classe CameraRotationHelper

Maintenant, nous commençons à utiliser la classe CameraRotationHelper . Déclarez une variable membre de classe pour stocker l’objet. Appelez le constructeur, en passant l’emplacement du boîtier de la caméra sélectionnée. La classe d’assistance utilise ces informations pour calculer l’orientation correcte pour les supports capturés, le flux d’aperçu et l’interface utilisateur. Inscrivez un gestionnaire pour l’événement OrientationChanged de la classe d’assistance, qui sera déclenché lorsque nous devons mettre à jour l’orientation de l’interface utilisateur ou du flux d’aperçu.

private CameraRotationHelper _rotationHelper;
_rotationHelper = new CameraRotationHelper(_cameraDevice.EnclosureLocation);
_rotationHelper.OrientationChanged += RotationHelper_OrientationChanged;

Ajouter des données d’orientation au flux d’aperçu de la caméra

L’ajout de l’orientation correcte aux métadonnées du flux d’aperçu n’affecte pas la façon dont l’aperçu apparaît à l’utilisateur, mais il aide le système à encoder correctement les images capturées à partir du flux d’aperçu.

Vous démarrez l’aperçu de la caméra en appelant MediaCapture.StartPreviewAsync. Avant de procéder ainsi, vérifiez la variable membre pour voir si l’aperçu doit être mis en miroir (pour une caméra frontale). Si c’est le cas, définissez la propriété FlowDirection de CaptureElement, nommée PreviewControl dans cet exemple, sur FlowDirection.RightToLeft. Après avoir démarré la préversion, appelez la méthode d’assistance SetPreviewRotationAsync pour définir la rotation de l’aperçu. Voici l’implémentation de cette méthode.

PreviewControl.Source = mediaCapture;
PreviewControl.FlowDirection = _mirroringPreview ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;

await mediaCapture.StartPreviewAsync();
await SetPreviewRotationAsync();

Nous définissons la rotation de l’aperçu dans une méthode distincte afin qu’elle puisse être mise à jour lorsque l’orientation du téléphone change sans réinitialiser le flux d’aperçu. Si l’appareil photo est externe à l’appareil, aucune action n’est effectuée. Sinon, la méthode CameraRotationHelper GetCameraPreviewOrientation est appelée et retourne l’orientation appropriée pour le flux d’aperçu.

Pour définir les métadonnées, les propriétés du flux d’aperçu sont récupérées en appelant VideoDeviceController.GetMediaStreamProperties. Ensuite, créez le GUID représentant l’attribut MFT (Media Foundation Transform) pour la rotation du flux vidéo. En C++, vous pouvez utiliser la constante MF_MT_VIDEO_ROTATION, mais en C# vous devez spécifier manuellement la valeur GUID.

Ajoutez une valeur de propriété à l’objet de propriétés de flux, en spécifiant le GUID comme clé et la rotation d’aperçu comme valeur. Cette propriété s’attend à ce que les valeurs soient en unités de degrés au niveau du compteur, de sorte que la méthode CameraRotationHelper ConvertSimpleOrientationToClockwiseDegrees est utilisée pour convertir la valeur d’orientation simple. Enfin, appelez SetEncodingPropertiesAsync pour appliquer la nouvelle propriété de rotation au flux.

private async Task SetPreviewRotationAsync()
{
    if (!_externalCamera)
    {
        // Add rotation metadata to the preview stream to make sure the aspect ratio / dimensions match when rendering and getting preview frames
        var rotation = _rotationHelper.GetCameraPreviewOrientation();
        var props = mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview);
        Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1");
        props.Properties.Add(RotationKey, CameraRotationHelper.ConvertSimpleOrientationToClockwiseDegrees(rotation));
        await mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, props, null);
    }
}

Ensuite, ajoutez le gestionnaire pour l’événement CameraRotationHelper.OrientationChanged . Cet événement passe dans un argument qui vous permet de savoir si le flux d’aperçu doit être pivoté. Si l’orientation de l’appareil a été modifiée pour faire face vers le haut ou face vers le bas, cette valeur est false. Si la préversion doit être pivotée, appelez SetPreviewRotationAsync qui a été défini précédemment.

Ensuite, dans le gestionnaire d’événements OrientationChanged , mettez à jour votre interface utilisateur si nécessaire. Obtenez l’orientation actuelle de l’interface utilisateur recommandée à partir de la classe d’assistance en appelant GetUIOrientation et convertissez la valeur en degrés d’horloge, qui est utilisée pour les transformations XAML. Créez une rotationTransform à partir de la valeur d’orientation et définissez la propriété RenderTransform de vos contrôles XAML. Selon votre disposition d’interface utilisateur, vous devrez peut-être effectuer des ajustements supplémentaires ici en plus de faire simplement pivoter les contrôles. En outre, n’oubliez pas que toutes les mises à jour de votre interface utilisateur doivent être effectuées sur le thread d’interface utilisateur. Vous devez donc placer ce code dans un appel à RunAsync.

private async void RotationHelper_OrientationChanged(object sender, bool updatePreview)
{
    if (updatePreview)
    {
        await SetPreviewRotationAsync();
    }
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
        // Rotate the buttons in the UI to match the rotation of the device
        var angle = CameraRotationHelper.ConvertSimpleOrientationToClockwiseDegrees(_rotationHelper.GetUIOrientation());
        var transform = new RotateTransform { Angle = angle };

        // The RenderTransform is safe to use (i.e. it won't cause layout issues) in this case, because these buttons have a 1:1 aspect ratio
        CapturePhotoButton.RenderTransform = transform;
        CapturePhotoButton.RenderTransform = transform;
    });
}

Capturer une photo avec des données d’orientation

L’article De base photo, vidéo et capture audio avec MediaCapture vous montre comment capturer une photo dans un fichier en capturant d’abord dans un flux en mémoire, puis en utilisant un décodeur pour lire les données d’image du flux et un encodeur pour transcoder les données d’image vers un fichier. Les données d’orientation, obtenues à partir de la classe CameraRotationHelper , peuvent être ajoutées au fichier image pendant l’opération de transcodage.

Dans l’exemple suivant, une photo est capturée dans un InMemoryRandomAccessStream avec un appel à CapturePhotoToStreamAsync et un BitmapDecoder est créé à partir du flux. Ensuite, un StorageFile est créé et ouvert pour retreiver un IRandomAccessStream pour l’écriture dans le fichier.

Avant de transcoder le fichier, l’orientation de la photo est récupérée à partir de la méthode de classe d’assistance GetCameraCaptureOrientation. Cette méthode retourne un objet SimpleOrientation converti en objet PhotoOrientation avec la méthode d’assistance ConvertSimpleOrientationToPhotoOrientation. Ensuite, un nouvel objet BitmapPropertySet est créé et une propriété est ajoutée où la clé est « System.Photo.Orientation » et la valeur est l’orientation de la photo, exprimée en tant que BitmapTypedValue. « System.Photo.Orientation » est l’une des nombreuses propriétés Windows qui peuvent être ajoutées en tant que métadonnées à un fichier image. Pour obtenir la liste de toutes les propriétés liées à la photo, consultez Propriétés Windows - Photo. Pour plus d’informations sur l’utilisation des métadonnées dans les images, consultez Métadonnées d’image.

Enfin, le jeu de propriétés qui inclut les données d’orientation est défini pour l’encodeur avec un appel à SetPropertiesAsync et l’image est transcodée avec un appel à FlushAsync.

private async Task CapturePhotoWithOrientationAsync()
{
    var captureStream = new InMemoryRandomAccessStream();

    try
    {
        await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), captureStream);
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine("Exception when taking a photo: {0}", ex.ToString());
        return;
    }


    var decoder = await BitmapDecoder.CreateAsync(captureStream);
    var file = await KnownFolders.PicturesLibrary.CreateFileAsync("SimplePhoto.jpeg", CreationCollisionOption.GenerateUniqueName);

    using (var outputStream = await file.OpenAsync(FileAccessMode.ReadWrite))
    {
        var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
        var photoOrientation = CameraRotationHelper.ConvertSimpleOrientationToPhotoOrientation(
            _rotationHelper.GetCameraCaptureOrientation());
        var properties = new BitmapPropertySet {
            { "System.Photo.Orientation", new BitmapTypedValue(photoOrientation, PropertyType.UInt16) } };
        await encoder.BitmapProperties.SetPropertiesAsync(properties);
        await encoder.FlushAsync();
    }
}

Capturer une vidéo avec des données d’orientation

La capture vidéo de base est décrite dans l’article De base sur la photo, la vidéo et la capture audio avec MediaCapture. L’ajout de données d’orientation à l’encodage de la vidéo capturée est effectué à l’aide de la même technique que celle décrite précédemment dans la section sur l’ajout de données d’orientation au flux d’aperçu.

Dans l’exemple suivant, un fichier est créé dans lequel la vidéo capturée sera écrite. Un profil d’encodage MP4 est créé à l’aide de la méthode statique CreateMp4. L’orientation appropriée de la vidéo est obtenue à partir de la classe CameraRotationHelper avec un appel à GetCameraCaptureOrientation , car la propriété de rotation nécessite que l’orientation soit exprimée en degrés de contre-verrouillage, la méthode d’assistance ConvertSimpleOrientationToClockwiseDegrees est appelée pour convertir la valeur d’orientation. Ensuite, créez le GUID représentant l’attribut MFT (Media Foundation Transform) pour la rotation du flux vidéo. En C++, vous pouvez utiliser la constante MF_MT_VIDEO_ROTATION, mais en C# vous devez spécifier manuellement la valeur GUID. Ajoutez une valeur de propriété à l’objet de propriétés de flux, en spécifiant le GUID comme clé et la rotation comme valeur. Enfin, appelez StartRecordToStorageFileAsync pour commencer à enregistrer la vidéo encodée avec des données d’orientation.

private async Task StartRecordingWithOrientationAsync()
{
    try
    {
        var videoFile = await KnownFolders.VideosLibrary.CreateFileAsync("SimpleVideo.mp4", CreationCollisionOption.GenerateUniqueName);

        var encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);

        var rotationAngle = CameraRotationHelper.ConvertSimpleOrientationToClockwiseDegrees(
            _rotationHelper.GetCameraCaptureOrientation());
        Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1");
        encodingProfile.Video.Properties.Add(RotationKey, PropertyValue.CreateInt32(rotationAngle));

        await mediaCapture.StartRecordToStorageFileAsync(encodingProfile, videoFile);
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine("Exception when starting video recording: {0}", ex.ToString());
    }
}

Liste complète du code CameraRotationHelper

L’extrait de code suivant répertorie le code complet de la classe CameraRotationHelper qui gère les capteurs d’orientation matérielle, calcule les valeurs d’orientation appropriées pour les photos et les vidéos et fournit des méthodes d’assistance pour convertir entre les différentes représentations d’orientation utilisées par différentes fonctionnalités Windows. Si vous suivez les instructions indiquées dans l’article ci-dessus, vous pouvez ajouter cette classe à votre projet tel quel sans avoir à apporter de modifications. Bien sûr, vous pouvez vous sentir libre de personnaliser le code suivant pour répondre aux besoins de votre scénario particulier.

Cette classe d’assistance utilise le SimpleOrientationSensor de l’appareil pour déterminer l’orientation actuelle du châssis de l’appareil et la classe DisplayInformation pour déterminer l’orientation actuelle de l’affichage. Chacune de ces classes fournit des événements déclenchés lorsque l’orientation actuelle change. Le panneau sur lequel l’appareil de capture est monté ( face avant, arrière ou externe) est utilisé pour déterminer si le flux d’aperçu doit être mis en miroir. En outre, la propriété EnclosureLocation.RotationAngleInDegreesClockwise , prise en charge par certains appareils, est utilisée pour déterminer l’orientation dans laquelle la caméra est montée sur les chasses.

Les méthodes suivantes peuvent être utilisées pour obtenir les valeurs d’orientation recommandées pour les tâches d’application caméra spécifiées :

  • GetUIOrientation : retourne l’orientation suggérée pour les éléments de l’interface utilisateur de l’appareil photo.
  • GetCameraCaptureOrientation : renvoie l’orientation suggérée pour l’encodage dans les métadonnées d’image.
  • GetCameraPreviewOrientation : renvoie l’orientation suggérée pour le flux d’aperçu afin de fournir une expérience utilisateur naturelle.
class CameraRotationHelper
{
    private EnclosureLocation _cameraEnclosureLocation;
    private DisplayInformation _displayInformation = DisplayInformation.GetForCurrentView();
    private SimpleOrientationSensor _orientationSensor = SimpleOrientationSensor.GetDefault();
    public event EventHandler<bool> OrientationChanged;

    public CameraRotationHelper(EnclosureLocation cameraEnclosureLocation)
    {
        _cameraEnclosureLocation = cameraEnclosureLocation;
        if (!IsEnclosureLocationExternal(_cameraEnclosureLocation))
        {
            _orientationSensor.OrientationChanged += SimpleOrientationSensor_OrientationChanged;
        }
        _displayInformation.OrientationChanged += DisplayInformation_OrientationChanged;
    }

    private void SimpleOrientationSensor_OrientationChanged(SimpleOrientationSensor sender, SimpleOrientationSensorOrientationChangedEventArgs args)
    {
        if (args.Orientation != SimpleOrientation.Faceup && args.Orientation != SimpleOrientation.Facedown)
        {
            HandleOrientationChanged(false);
        }
    }

    private void DisplayInformation_OrientationChanged(DisplayInformation sender, object args)
    {
        HandleOrientationChanged(true);
    }

    private void HandleOrientationChanged(bool updatePreviewStreamRequired)
    {
        var handler = OrientationChanged;
        if (handler != null)
        {
            handler(this, updatePreviewStreamRequired);
        }
    }

    public static bool IsEnclosureLocationExternal(EnclosureLocation enclosureLocation)
    {
        return (enclosureLocation == null || enclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown);
    }

    private bool IsCameraMirrored()
    {
        // Front panel cameras are mirrored by default
        return (_cameraEnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
    }

    private SimpleOrientation GetCameraOrientationRelativeToNativeOrientation()
    {
        // Get the rotation angle of the camera enclosure
        return ConvertClockwiseDegreesToSimpleOrientation((int)_cameraEnclosureLocation.RotationAngleInDegreesClockwise);
    }

    // Gets the rotation to rotate ui elements
    public SimpleOrientation GetUIOrientation()
    {
        if (IsEnclosureLocationExternal(_cameraEnclosureLocation))
        {
            // Cameras that are not attached to the device do not rotate along with it, so apply no rotation
            return SimpleOrientation.NotRotated;
        }

        // Return the difference between the orientation of the device and the orientation of the app display
        var deviceOrientation = _orientationSensor.GetCurrentOrientation();
        var displayOrientation = ConvertDisplayOrientationToSimpleOrientation(_displayInformation.CurrentOrientation);
        return SubOrientations(displayOrientation, deviceOrientation);
    }

    // Gets the rotation of the camera to rotate pictures/videos when saving to file
    public SimpleOrientation GetCameraCaptureOrientation()
    {
        if (IsEnclosureLocationExternal(_cameraEnclosureLocation))
        {
            // Cameras that are not attached to the device do not rotate along with it, so apply no rotation
            return SimpleOrientation.NotRotated;
        }

        // Get the device orienation offset by the camera hardware offset
        var deviceOrientation = _orientationSensor.GetCurrentOrientation();
        var result = SubOrientations(deviceOrientation, GetCameraOrientationRelativeToNativeOrientation());

        // If the preview is being mirrored for a front-facing camera, then the rotation should be inverted
        if (IsCameraMirrored())
        {
            result = MirrorOrientation(result);
        }
        return result;
    }

    // Gets the rotation of the camera to display the camera preview
    public SimpleOrientation GetCameraPreviewOrientation()
    {
        if (IsEnclosureLocationExternal(_cameraEnclosureLocation))
        {
            // Cameras that are not attached to the device do not rotate along with it, so apply no rotation
            return SimpleOrientation.NotRotated;
        }

        // Get the app display rotation offset by the camera hardware offset
        var result = ConvertDisplayOrientationToSimpleOrientation(_displayInformation.CurrentOrientation);
        result = SubOrientations(result, GetCameraOrientationRelativeToNativeOrientation());

        // If the preview is being mirrored for a front-facing camera, then the rotation should be inverted
        if (IsCameraMirrored())
        {
            result = MirrorOrientation(result);
        }
        return result;
    }

    public static PhotoOrientation ConvertSimpleOrientationToPhotoOrientation(SimpleOrientation orientation)
    {
        switch (orientation)
        {
            case SimpleOrientation.Rotated90DegreesCounterclockwise:
                return PhotoOrientation.Rotate90;
            case SimpleOrientation.Rotated180DegreesCounterclockwise:
                return PhotoOrientation.Rotate180;
            case SimpleOrientation.Rotated270DegreesCounterclockwise:
                return PhotoOrientation.Rotate270;
            case SimpleOrientation.NotRotated:
            default:
                return PhotoOrientation.Normal;
        }
    }

    public static int ConvertSimpleOrientationToClockwiseDegrees(SimpleOrientation orientation)
    {
        switch (orientation)
        {
            case SimpleOrientation.Rotated90DegreesCounterclockwise:
                return 270;
            case SimpleOrientation.Rotated180DegreesCounterclockwise:
                return 180;
            case SimpleOrientation.Rotated270DegreesCounterclockwise:
                return 90;
            case SimpleOrientation.NotRotated:
            default:
                return 0;
        }
    }

    private SimpleOrientation ConvertDisplayOrientationToSimpleOrientation(DisplayOrientations orientation)
    {
        SimpleOrientation result;
        switch (orientation)
        {
            case DisplayOrientations.Landscape:
                result = SimpleOrientation.NotRotated;
                break;
            case DisplayOrientations.PortraitFlipped:
                result = SimpleOrientation.Rotated90DegreesCounterclockwise;
                break;
            case DisplayOrientations.LandscapeFlipped:
                result = SimpleOrientation.Rotated180DegreesCounterclockwise;
                break;
            case DisplayOrientations.Portrait:
            default:
                result = SimpleOrientation.Rotated270DegreesCounterclockwise;
                break;
        }

        // Above assumes landscape; offset is needed if native orientation is portrait
        if (_displayInformation.NativeOrientation == DisplayOrientations.Portrait)
        {
            result = AddOrientations(result, SimpleOrientation.Rotated90DegreesCounterclockwise);
        }

        return result;
    }

    private static SimpleOrientation MirrorOrientation(SimpleOrientation orientation)
    {
        // This only affects the 90 and 270 degree cases, because rotating 0 and 180 degrees is the same clockwise and counter-clockwise
        switch (orientation)
        {
            case SimpleOrientation.Rotated90DegreesCounterclockwise:
                return SimpleOrientation.Rotated270DegreesCounterclockwise;
            case SimpleOrientation.Rotated270DegreesCounterclockwise:
                return SimpleOrientation.Rotated90DegreesCounterclockwise;
        }
        return orientation;
    }

    private static SimpleOrientation AddOrientations(SimpleOrientation a, SimpleOrientation b)
    {
        var aRot = ConvertSimpleOrientationToClockwiseDegrees(a);
        var bRot = ConvertSimpleOrientationToClockwiseDegrees(b);
        var result = (aRot + bRot) % 360;
        return ConvertClockwiseDegreesToSimpleOrientation(result);
    }

    private static SimpleOrientation SubOrientations(SimpleOrientation a, SimpleOrientation b)
    {
        var aRot = ConvertSimpleOrientationToClockwiseDegrees(a);
        var bRot = ConvertSimpleOrientationToClockwiseDegrees(b);
        //add 360 to ensure the modulus operator does not operate on a negative
        var result = (360 + (aRot - bRot)) % 360;
        return ConvertClockwiseDegreesToSimpleOrientation(result);
    }

    private static VideoRotation ConvertSimpleOrientationToVideoRotation(SimpleOrientation orientation)
    {
        switch (orientation)
        {
            case SimpleOrientation.Rotated90DegreesCounterclockwise:
                return VideoRotation.Clockwise270Degrees;
            case SimpleOrientation.Rotated180DegreesCounterclockwise:
                return VideoRotation.Clockwise180Degrees;
            case SimpleOrientation.Rotated270DegreesCounterclockwise:
                return VideoRotation.Clockwise90Degrees;
            case SimpleOrientation.NotRotated:
            default:
                return VideoRotation.None;
        }
    }

    private static SimpleOrientation ConvertClockwiseDegreesToSimpleOrientation(int orientation)
    {
        switch (orientation)
        {
            case 270:
                return SimpleOrientation.Rotated90DegreesCounterclockwise;
            case 180:
                return SimpleOrientation.Rotated180DegreesCounterclockwise;
            case 90:
                return SimpleOrientation.Rotated270DegreesCounterclockwise;
            case 0:
            default:
                return SimpleOrientation.NotRotated;
        }
    }
}