Share via


Gestire l'orientamento del dispositivo con MediaCapture

Quando l'app acquisisce una foto o un video destinato a essere visualizzato all'esterno dell'app, ad esempio il salvataggio in un file nel dispositivo o la condivisione online dell'utente, è importante codificare l'immagine con i metadati di orientamento appropriati in modo che quando un'altra app o dispositivo visualizza l'immagine, sia orientata correttamente. Determinare i dati di orientamento corretti da includere in un file multimediale può essere un'attività complessa perché esistono diverse variabili da considerare, tra cui l'orientamento dello chassis del dispositivo, l'orientamento dello schermo e la posizione della fotocamera sullo chassis (sia una fotocamera anteriore o posteriore).

Per semplificare il processo di gestione dell'orientamento, è consigliabile usare una classe helper, CameraRotationHelper, per cui viene fornita la definizione completa alla fine di questo articolo. È possibile aggiungere questa classe al progetto e quindi seguire i passaggi descritti in questo articolo per aggiungere il supporto dell'orientamento all'app fotocamera. La classe helper semplifica anche la rotazione dei controlli nell'interfaccia utente della fotocamera in modo che vengano visualizzati correttamente dal punto di vista dell'utente.

Nota

Questo articolo si basa sul codice e sui concetti illustrati nell'articolo Acquisizione di foto, video e audio di base con MediaCapture. È consigliabile acquisire familiarità con i concetti di base dell'uso della classe MediaCapture prima di aggiungere il supporto per l'orientamento all'app.

Spazi dei nomi usati in questo articolo

Il codice di esempio in questo articolo usa le API degli spazi dei nomi seguenti da includere nel codice.

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

Il primo passaggio per aggiungere il supporto dell'orientamento all'app consiste nel bloccare lo schermo in modo che non ruota automaticamente quando il dispositivo viene ruotato. La rotazione automatica dell'interfaccia utente funziona bene per la maggior parte dei tipi di app, ma non è intenzionale per gli utenti quando l'anteprima della fotocamera ruota. Bloccare l'orientamento dello schermo impostando la proprietà DisplayInformation.AutoRotationPreferences a DisplayOrientations.Landscape.

DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;

Rilevamento della posizione del dispositivo fotocamera

Per calcolare l'orientamento corretto per i supporti acquisiti, si deve determinare la posizione del dispositivo fotocamera nello chassis. Aggiungere una variabile membro booleana per tenere traccia se la fotocamera è esterna al dispositivo, ad esempio una web cam USB. Aggiungere un'altra variabile booleana per tenere traccia del mirroring dell'anteprima, ovvero se viene usata una fotocamera anteriore. Aggiungere inoltre una variabile per archiviare un oggetto DeviceInformation che rappresenta la fotocamera selezionata.

private bool _externalCamera;
private bool _mirroringPreview;
DeviceInformation _cameraDevice;

Selezionare un dispositivo fotocamera e inizializzare l'oggetto MediaCapture

L'articolo Acquisizione di foto, video e audio di base con MediaCapture illustra come inizializzare l'oggetto MediaCapture con solo un paio di righe di codice. Per supportare l'orientamento della fotocamera, aggiungeremo altri passaggi al processo di inizializzazione.

Prima di tutto, chiamare DeviceInformation.FindAllAsync passando nel selettore dispositivo DeviceClass.VideoCapture per ottenere un elenco di tutti i dispositivi di acquisizione video disponibili. Selezionare quindi il primo dispositivo nell'elenco in cui è nota la posizione del pannello della fotocamera e dove corrisponde al valore fornito, che in questo esempio è una fotocamera anteriore. Se non viene trovata alcuna fotocamera nel pannello desiderato, viene usata la prima fotocamera disponibile o predefinita.

Se viene trovato un dispositivo fotocamera, viene creato un nuovo oggetto MediaCaptureInitialization Impostazioni e la proprietà VideoDeviceId viene impostata sul dispositivo selezionato. Creare quindi l'oggetto MediaCapture e chiamare InitializeAsync, passando l'oggetto impostazioni per indicare al sistema di usare la fotocamera selezionata.

Verificare infine se il pannello del dispositivo selezionato è null o sconosciuto. In tal caso, la fotocamera è esterna, il che significa che la sua rotazione non è correlata alla rotazione del dispositivo. Se il pannello è noto e si trova sulla parte anteriore dello chassis del dispositivo, sappiamo che l'anteprima deve essere con mirroring, quindi viene impostato il rilevamento delle variabili.

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

Inizializzare la classe CameraRotationHelper

A questo punto si inizia a usare la classe CameraRotationHelper. Dichiarare una variabile membro della classe per archiviare l'oggetto. Chiamare il costruttore passando la posizione dell'enclosure della fotocamera selezionata. La classe helper usa queste informazioni per calcolare l'orientamento corretto per i supporti acquisiti, il flusso di anteprima e l'interfaccia utente. Registrare un gestore per l'evento OrientationChanged della classe helper, che verrà generato quando è necessario aggiornare l'orientamento dell'interfaccia utente o del flusso di anteprima.

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

Aggiungere dati di orientamento al flusso di anteprima della fotocamera

L'aggiunta dell'orientamento corretto ai metadati del flusso di anteprima non influisce sulla modalità di visualizzazione dell'anteprima all'utente, ma consente al sistema di codificare correttamente tutti i fotogrammi acquisiti dal flusso di anteprima.

Avviare l'anteprima della fotocamera chiamando MediaCapture.StartPreviewAsync. Prima di eseguire questa operazione, controllare la variabile membro per verificare se l'anteprima deve essere con mirroring (per una fotocamera anteriore). In tal caso, impostare la proprietà FlowDirection di CaptureElement, chiamata PreviewControl in questo esempio, in FlowDirection.RightToLeft. Dopo aver avviato l'anteprima, chiamare il metodo helper SetPreviewRotationAsync per impostare la rotazione dell'anteprima. Di seguito viene riportata l'implementazione di questo metodo.

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

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

La rotazione dell'anteprima viene impostata in un metodo separato in modo che possa essere aggiornata quando l'orientamento del telefono cambia senza reinizializzare il flusso di anteprima. Se la fotocamera è esterna al dispositivo, non viene eseguita alcuna azione. In caso contrario, viene chiamato il metodo CameraRotationHelperGetCameraPreviewOrientation e restituisce l'orientamento appropriato per il flusso di anteprima.

Per impostare i metadati, le proprietà del flusso di anteprima vengono recuperate chiamando VideoDeviceController.GetMediaStreamProperties. Creare quindi il GUID che rappresenta l'attributo MFT (Media Foundation Transform) per la rotazione del flusso video. In C++ è possibile usare la costante MF_MT_VIDEO_ROTATION, ma in C# è necessario specificare manualmente il valore GUID.

Aggiungere un valore della proprietà all'oggetto proprietà del flusso, specificando il GUID come chiave e la rotazione dell'anteprima come valore. Questa proprietà prevede che i valori siano in unità di gradi antiorario, pertanto il metodo CameraRotationHelperConvertSimpleOrientationToClockwiseDegrees usato per convertire il valore di orientamento semplice. Infine, chiamare SetEncodingPropertiesAsync per applicare la nuova proprietà di rotazione al flusso.

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

Aggiungere quindi il gestore per l'evento CameraRotationHelper.OrientationChanged. Questo evento passa un argomento che consente di sapere se il flusso di anteprima deve essere ruotato. Se l'orientamento del dispositivo è stato modificato in faccia verso l'alto o verso il basso, questo valore sarà false. Se l'anteprima deve essere ruotata, chiamare SetPreviewRotationAsync definito in precedenza.

Successivamente, nel gestore eventi OrientationChanged aggiornare l'interfaccia utente, se necessario. Ottenere l'orientamento dell'interfaccia utente consigliato corrente dalla classe helper chiamando GetUIOrientation e convertire il valore in gradi in senso orario, usato per le trasformazioni XAML. Creare un oggetto RotateTransform dal valore di orientamento e impostare la proprietà RenderTransform dei controlli XAML. A seconda del layout dell'interfaccia utente, potrebbe essere necessario apportare ulteriori modifiche qui oltre a ruotare semplicemente i controlli. Tenere presente anche che tutti gli aggiornamenti dell'interfaccia utente devono essere eseguiti nel thread dell'interfaccia utente, quindi è necessario inserire questo codice all'interno di una chiamata a 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;
    });
}

Acquisire una foto con dati di orientamento

L'articolo Acquisizione di foto, video e audio di base con MediaCapture illustra come acquisire una foto in un file acquisendo prima in un flusso in memoria e quindi usando un decodificatore per leggere i dati dell'immagine dal flusso e un codificatore per transcodificare i dati dell'immagine in un file. I dati di orientamento ottenuti dalla classe CameraRotationHelper possono essere aggiunti al file di immagine durante l'operazione di transcodifica.

Nell'esempio seguente una foto viene acquisita in un oggetto InMemoryRandomAccessStream con una chiamata a CapturePhotoToStreamAsync e un BitmapDecoder è creato dallo stream. Viene quindi creato uno StorageFile e aperto per recuperare un IRandomAccessStream per la scrittura nel file.

Prima di transcodificare il file, l'orientamento della foto viene recuperato dal metodo della classe helper GetCameraCaptureOrientation. Questo metodo restituisce un oggetto SimpleOrientation convertito in un oggetto PhotoOrientation con il metodo helper ConvertSimpleOrientationToPhotoOrientation. Viene quindi creato un nuovo oggetto BitmapPropertySet e viene aggiunta una proprietà in cui la chiave è "System.Photo.Orientation" e il valore è l'orientamento della foto, espresso come BitmapTypedValue. "System.Photo.Orientation" è una delle numerose proprietà di Windows che possono essere aggiunte come metadati a un file immagine. Per un elenco di tutte le proprietà correlate alle foto, vedere Proprietà di Windows - Foto. Per altre informazioni sulla workine con i metadati nelle immagini, vedere Metadati dell'immagine.

Infine, il set di proprietà che include i dati di orientamento viene impostato per il codificatore con una chiamata a SetPropertiesAsync e l'immagine viene transcodificata con una chiamata a 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();
    }
}

Acquisire un video con dati di orientamento

L'acquisizione video di base è descritta nell'articolo Acquisizione di foto, video e audio di base con MediaCapture. L'aggiunta di dati di orientamento alla codifica del video acquisito viene eseguita usando la stessa tecnica descritta in precedenza nella sezione relativa all'aggiunta di dati di orientamento al flusso di anteprima.

Nell'esempio seguente viene creato un file in cui verrà scritto il video acquisito. Un profilo di codifica MP4 viene creato usando il metodo statico CreateMp4. L'orientamento corretto per il video viene ottenuto dalla classe CameraRotationHelper con una chiamata a GetCameraCaptureOrientation Poiché la proprietà di rotazione richiede che l'orientamento sia espresso in gradi antiorario, il metodo helper ConvertSimpleOrientationToClockwiseDegrees viene chiamato per convertire il valore di orientamento. Creare quindi il GUID che rappresenta l'attributo MFT (Media Foundation Transform) per la rotazione del flusso video. In C++ è possibile usare la costante MF_MT_VIDEO_ROTATION, ma in C# è necessario specificare manualmente il valore GUID. Aggiungere un valore della proprietà all'oggetto proprietà del flusso, specificando il GUID come chiave e la rotazione come valore. Chiamare infine StartRecordTo Archiviazione FileAsync per iniziare a registrare video codificati con i dati di orientamento.

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

Listato di codice completo di CameraRotationHelper

Il frammento di codice seguente elenca il codice completo per la classe CameraRotationHelper che gestisce i sensori di orientamento hardware, calcola i valori di orientamento appropriati per foto e video e fornisce metodi helper per la conversione tra le diverse rappresentazioni di orientamento usate da diverse funzionalità di Windows. Se si seguono le indicazioni illustrate nell'articolo precedente, è possibile aggiungere questa classe al progetto così come è senza dover apportare modifiche. Naturalmente, è possibile personalizzare il codice seguente per soddisfare le esigenze di uno scenario specifico.

Questa classe helper usa SimpleOrientationSensor del dispositivo per determinare l'orientamento corrente dello chassis del dispositivo e la classe DisplayInformation per determinare l'orientamento corrente dello schermo. Ognuna di queste classi fornisce eventi generati quando cambia l'orientamento corrente. Il pannello in cui è montato il dispositivo di acquisizione, rivolto verso il retro o esterno, viene usato per determinare se il flusso di anteprima deve essere sottoposto a mirroring. Inoltre, la proprietà EnclosureLocation.RotationAngleInDegreesClockwise, supportata da alcuni dispositivi, viene usata per determinare l'orientamento in cui la fotocamera è montata sullo chasses.

Per ottenere i valori di orientamento consigliati per le attività dell'app fotocamera specificate, è possibile usare i metodi seguenti:

  • GetUIOrientation: restituisce l'orientamento suggerito per gli elementi dell'interfaccia utente della fotocamera.
  • GetCameraCaptureOrientation: restituisce l'orientamento suggerito per la codifica nei metadati dell'immagine.
  • GetCameraPreviewOrientation: restituisce l'orientamento suggerito per il flusso di anteprima per offrire un'esperienza utente naturale.
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;
        }
    }
}