Controlar la orientación del dispositivo con MediaCapture

Cuando la aplicación captura una foto o un vídeo para su visualización en otra ubicación (por ejemplo, si se guarda en un archivo en el dispositivo del usuario o se comparte en línea), es importante codificar la imagen con los metadatos de orientación correctos para que, cuando la imagen se muestre en otro dispositivo o aplicación, su orientación sea correcta. Determinar los datos de orientación correctos que se deben incluir en un archivo multimedia puede ser una tarea compleja, ya que hay que tener en cuenta distintas variables, incluida la orientación del chasis del dispositivo, la orientación de la pantalla y la colocación de la cámara en el chasis (si es una cámara frontal o posterior).

Para simplificar el control de la orientación, se recomienda usar una clase auxiliar, CameraRotationHelper, cuya definición completa se proporciona al final de este artículo. Puedes agregar esta clase al proyecto y, después, seguir los pasos de este artículo para que la aplicación de cámara admita la orientación. La clase auxiliar también facilita el giro de los controles de la interfaz de usuario de cámara para que se representen correctamente desde el punto de visión del usuario.

Nota

Este artículo se basa en el código y los conceptos tratados en el artículo Captura básica de fotos, audio y vídeo con MediaCapture. Se recomienda familiarizarse con los conceptos básicos del uso de la clase MediaCapture antes de incorporar compatibilidad con la orientación a la aplicación.

Espacios de nombres usados en este artículo

El código de ejemplo de este artículo usa las API de los siguientes espacios de nombres, que se deben incluir en el código.

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

El primer paso para incorporar compatibilidad con la orientación a la aplicación es bloquear la pantalla para que no gire automáticamente al girar el dispositivo. La rotación automática de la interfaz de usuario funciona bien para la mayoría de tipos de aplicaciones, pero no es intuitiva para los usuarios cuando la vista previa de cámara gira. Para bloquear la orientación de la pantalla, establece la propiedad DisplayInformation.AutoRotationPreferences en DisplayOrientations.Landscape.

DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;

Seguimiento de la ubicación del dispositivo de cámara

Para calcular la orientación correcta para los archivos multimedia capturados, la aplicación debe determinar la ubicación del dispositivo de cámara en el chasis. Agrega una variable de miembro booleano para comprobar si la cámara es externa al dispositivo, como una cámara web USB. Agrega otra variable booleana para comprobar si la vista previa debe reflejarse, como sucede al usar una cámara frontal. Asimismo, agrega una variable para almacenar un objeto DeviceInformation que represente la cámara seleccionada.

private bool _externalCamera;
private bool _mirroringPreview;
DeviceInformation _cameraDevice;

Seleccionar un dispositivo de cámara e inicializar el objeto MediaCapture

El artículo Captura básica de fotos, audio y vídeo con MediaCapture muestra cómo inicializar el objeto MediaCapture con solo un par de líneas de código. Para admitir la orientación de la cámara, agregaremos algunos pasos más al proceso de inicialización.

En primer lugar, llama a DeviceInformation.FindAllAsync y pasa el selector de dispositivos DeviceClass.VideoCapture para obtener una lista de todos los dispositivos de captura de vídeo disponibles. A continuación, selecciona el primer dispositivo de la lista cuya ubicación del panel de la cámara se conozca y cuyo valor proporcionado coincida, que en este ejemplo es una cámara frontal. Si no se encuentra ninguna cámara en el panel deseado, se usa la primera cámara disponible o la predeterminada.

Si se encuentra un dispositivo de cámara, se crea un nuevo objeto MediaCaptureInitializationSettings y la propiedad VideoDeviceId se establece en el dispositivo seleccionado. A continuación, crea el objeto MediaCapture, llama a InitializeAsync y pasa el objeto de configuración para indicar al sistema que use la cámara seleccionada.

Por último, comprueba si el panel de dispositivo seleccionado es nulo o desconocido. Si es así, la cámara es externa, lo que significa que su rotación no está relacionada con la rotación del dispositivo. Si el panel es conocido y se encuentra en la parte frontal del chasis del dispositivo, sabemos que la vista previa debe reflejarse, por lo que se establece la variable que lo controla.

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

Inicializar la clase CameraRotationHelper

Ahora empezamos usando la clase CameraRotationHelper. Declara una variable de miembro de clase para almacenar el objeto. Llama al constructor y pasa la ubicación de la carcasa de la cámara seleccionada. La clase auxiliar usa esta información para calcular la orientación correcta para los archivos multimedia capturados, la secuencia de vista previa y la interfaz de usuario. Registra un controlador para el evento OrientationChanged de la clase auxiliar, que se generará cuando necesitemos actualizar la orientación de la interfaz de usuario o la secuencia de vista previa.

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

Agregar datos de orientación a la secuencia de vista previa de la cámara

La adición de la orientación correcta a los metadatos de la secuencia de vista previa no afecta al modo en que la vista previa se muestra al usuario, pero ayuda al sistema a codificar los fotogramas capturados de la secuencia de vista previa correctamente.

Para iniciar la vista previa de cámara, llama a MediaCapture.StartPreviewAsync. Antes de hacerlo, comprueba la variable de miembro para ver si la vista previa debe reflejarse (para una cámara frontal). Si es así, establece la propiedad FlowDirection de la clase CaptureElement, denominada PreviewControl en este ejemplo, en FlowDirection.RightToLeft. Después de iniciar la vista previa, llama al método auxiliar SetPreviewRotationAsync para establecer la rotación de la vista previa. A continuación se muestra la implementación de este método.

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

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

Establecemos la rotación de la vista previa en un método separado para que se pueda actualizar cuando la orientación del teléfono cambie sin reinicializar la secuencia de vista previa. Si la cámara es externa al dispositivo, no se realiza ninguna acción. De lo contrario, se llama al método de CameraRotationHelperGetCameraPreviewOrientation, que devuelve la orientación correcta para la secuencia de vista previa.

Para establecer los metadatos, las propiedades de secuencia de vista previa se recuperan llamando a VideoDeviceController.GetMediaStreamProperties. A continuación, crea un GUID que represente el atributo de Media Foundation Transform (MFT) para la rotación de la secuencia de vídeo. En C++, puedes usar la constante MF_MT_VIDEO_ROTATION, pero en C#, debes especificar manualmente el valor GUID.

Agrega un valor de propiedad al objeto de propiedades de secuencia, y especifica el GUID como la clave y la rotación de la vista previa como el valor. Esta propiedad espera que los valores estén en unidades de grados en sentido contrario a las agujas del reloj, por lo que el método de CameraRotationHelperConvertSimpleOrientationToClockwiseDegrees se usa para convertir el valor de orientación simple. Por último, llama a SetEncodingPropertiesAsync para aplicar la nueva propiedad de rotación a la secuencia.

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

A continuación, agrega el controlador para el evento CameraRotationHelper.OrientationChanged. Este evento pasa un argumento que te permite saber si la secuencia de vista previa se debe girar. Si la orientación del dispositivo cambia a boca arriba o boca abajo, este valor será false. Si la vista previa no se debe girar, llama al método SetPreviewRotationAsync definido anteriormente.

Luego, en el controlador de eventos OrientationChanged, actualiza la interfaz de usuario si es necesario. Obtén la orientación de la interfaz de usuario recomendada actualmente de la clase auxiliar llamando a GetUIOrientation y convierte el valor a grados en el sentido de las agujas del reloj, que se usa para transformaciones de XAML. Crea una clase RotateTransform a partir del valor de orientación y establece la propiedad RenderTransform de los controles XAML. Según el diseño de la interfaz de usuario, deberás realizar ajustes adicionales, aparte de la simple rotación de los controles. Recuerda también que todas las actualizaciones de la interfaz de usuario deben realizarse en el subproceso de la interfaz de usuario, por lo que debes colocar este código dentro de una llamada 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;
    });
}

Capturar una foto con datos de orientación

En el artículo Captura básica de fotos, audio y vídeo con MediaCapture se muestra cómo capturar una foto en un archivo mediante la captura en primer lugar en una secuencia en memoria y, a continuación, el uso de un descodificador para leer los datos de imagen de la secuencia y un codificador para transcodificar los datos de imagen en un archivo. Los datos de orientación, obtenidos de la clase CameraRotationHelper, se pueden agregar al archivo de imagen durante la operación de transcodificación.

En el ejemplo siguiente, se captura una foto en una clase InMemoryRandomAccessStream con una llamada a CapturePhotoToStreamAsync y se crea una clase BitmapDecoder a partir de la secuencia. A continuación, se crea una clase StorageFile, que se abre para recuperar una interfaz IRandomAccessStream para escribir en el archivo.

Antes de transcodificar el archivo, la orientación de la foto se recupera del método de clase auxiliar GetCameraCaptureOrientation. Este método devuelve un objeto SimpleOrientation que se convierte en un objeto PhotoOrientation con el método auxiliar ConvertSimpleOrientationToPhotoOrientation. Luego, se crea un nuevo objeto BitmapPropertySet y se agrega una propiedad donde la clave es "System.Photo.Orientation" y el valor es la orientación de la foto, expresado como BitmapTypedValue. "System.Photo.Orientation" es una de muchas propiedades de Windows que se pueden agregar a un archivo de imagen como metadatos. Para obtener una lista de todas las propiedades relacionadas con fotos, consulta Windows Properties - Photo. (Propiedades de Windows: Foto) Para obtener más información sobre el trabajo con metadatos en las imágenes, consulta Metadatos de imagen.

Por último, el conjunto de propiedades que incluye los datos de orientación se establece para el codificador con una llamada a SetPropertiesAsync y la imagen se transcodifica con una llamada 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();
    }
}

Capturar un vídeo con datos de orientación

La captura de vídeo básica se describe en el artículo Captura básica de fotos, audio y vídeo con MediaCapture. La adición de datos de orientación a la codificación del vídeo capturado se realiza con la misma técnica descrita anteriormente en la sección sobre la adición de datos de orientación a la secuencia de vista previa.

En el ejemplo siguiente, se crea un archivo en el que se escribirá el vídeo capturado. Un perfil de codificación MP4 se crea con el método estático CreateMp4. La orientación correcta del vídeo se obtiene de la clase CameraRotationHelper con una llamada a GetCameraCaptureOrientation. Dado que la propiedad de rotación requiere que la orientación se exprese en grados en sentido contrario a las agujas del reloj, se llama al método auxiliar ConvertSimpleOrientationToClockwiseDegrees para convertir el valor de orientación. A continuación, crea un GUID que represente el atributo de Media Foundation Transform (MFT) para la rotación de la secuencia de vídeo. En C++, puedes usar la constante MF_MT_VIDEO_ROTATION, pero en C#, debes especificar manualmente el valor GUID. Agrega un valor de propiedad al objeto de propiedades de secuencia, y especifica el GUID como la clave y la rotación como el valor. Por último, llama a StartRecordToStorageFileAsync para empezar a grabar vídeo codificado con datos de orientación.

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

Listado de código completo de CameraRotationHelper

El siguiente fragmento de código muestra el código completo de la clase CameraRotationHelper que administra los sensores de orientación de hardware, calcula los valores de orientación correctos para fotos y vídeos, y proporciona métodos auxiliares para la conversión entre las diferentes representaciones de orientación que se usan en las distintas características de Windows. Si sigues las instrucciones que se muestran en el artículo anterior, puedes agregar esta clase al proyecto tal cual sin tener que realizar ningún cambio. Por supuesto, puedes personalizar el código siguiente para satisfacer las necesidades de tu escenario concreto.

Esta clase auxiliar usa la clase SimpleOrientationSensor del dispositivo para determinar la orientación actual del chasis del dispositivo y la clase DisplayInformation para determinar la orientación actual de la pantalla. Cada una de estas clases proporciona eventos que se generan cuando la orientación actual cambia. El panel en el que se monta el dispositivo de captura (frontal, posterior o externo) se usa para determinar si la secuencia de vista previa debe reflejarse. Además, la propiedad EnclosureLocation.RotationAngleInDegreesClockwise, compatible con algunos dispositivos, se usa para determinar la orientación de montaje de la cámara en el chasis.

Los siguientes métodos pueden usarse para obtener los valores de orientación recomendados para las tareas de la aplicación de cámara especificada:

  • GetUIOrientation: devuelve la orientación sugerida para los elementos de la interfaz de usuario de la cámara.
  • GetCameraCaptureOrientation: devuelve la orientación sugerida para codificar los metadatos de imagen.
  • GetCameraPreviewOrientation: devuelve la orientación sugerida para la secuencia de vista previa a fin de proporcionar una experiencia de usuario natural.
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;
        }
    }
}