Menangani orientasi perangkat dengan MediaCapture

Saat aplikasi Anda mengambil foto atau video yang dimaksudkan untuk dilihat di luar aplikasi Anda, seperti menyimpan ke file di perangkat pengguna atau berbagi secara online, penting bagi Anda untuk mengodekan gambar dengan metadata orientasi yang tepat sehingga ketika aplikasi atau perangkat lain menampilkan gambar, gambar tersebut berorientasi dengan benar. Menentukan data orientasi yang benar untuk disertakan dalam file media bisa menjadi tugas yang kompleks karena ada beberapa variabel yang perlu dipertimbangkan, termasuk orientasi sasis perangkat, orientasi layar, dan penempatan kamera pada sasis (baik itu kamera depan atau belakang).

Untuk menyederhanakan proses penanganan orientasi, sebaiknya gunakan kelas pembantu, CameraRotationHelper, yang definisi lengkapnya disediakan di akhir artikel ini. Anda dapat menambahkan kelas ini ke proyek Anda lalu mengikuti langkah-langkah dalam artikel ini untuk menambahkan dukungan orientasi ke aplikasi kamera Anda. Kelas pembantu juga memudahkan Anda untuk memutar kontrol di antarmuka pengguna kamera Anda sehingga kontrol dirender dengan benar dari sudut pandang pengguna.

Catatan

Artikel ini dibuat berdasarkan kode dan konsep yang dibahas dalam artikel Pengambilan foto, video, dan audio dasar dengan MediaCapture. Sebaiknya Anda membiasakan diri dengan konsep dasar menggunakan kelas MediaCapture sebelum menambahkan dukungan orientasi ke aplikasi Anda.

Namespace yang digunakan dalam artikel ini

Contoh kode dalam artikel ini menggunakan API dari namespace berikut yang harus Anda sertakan dalam kode Anda.

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

Langkah pertama dalam menambahkan dukungan orientasi ke aplikasi Anda adalah mengunci layar agar tidak diputar secara otomatis saat perangkat diputar. Rotasi UI otomatis berfungsi dengan baik untuk sebagian besar jenis aplikasi, tetapi tidak identik bagi pengguna saat pratinjau kamera berputar. Kunci orientasi tampilan dengan mengatur properti DisplayInformation.AutoRotationPreferences ke DisplayOrientations.Landscape.

DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;

Melacak lokasi perangkat kamera

Untuk menghitung orientasi yang benar untuk media yang diambil, aplikasi Anda harus menentukan lokasi perangkat kamera pada sasis. Tambahkan variabel anggota boolean untuk melacak apakah kamera berada di luar perangkat, seperti cam web USB. Tambahkan variabel boolean lain untuk melacak apakah pratinjau harus dicerminkan, yang terjadi jika kamera depan digunakan. Selain itu, tambahkan variabel untuk menyimpan objek DeviceInformation yang mewakili kamera yang dipilih.

private bool _externalCamera;
private bool _mirroringPreview;
DeviceInformation _cameraDevice;

Pilih perangkat kamera dan inisialisasi objek MediaCapture

Artikel Pengambilan foto, video, dan audio dasar dengan MediaCapture menunjukkan kepada Anda cara menginisialisasi objek MediaCapture hanya dengan beberapa baris kode. Untuk mendukung orientasi kamera, kami akan menambahkan beberapa langkah lagi ke proses inisialisasi.

Pertama, panggil DeviceInformation.FindAllAsync yang melewati pemilih perangkat DeviceClass.VideoCapture untuk mendapatkan daftar semua perangkat pengambilan video yang tersedia. Selanjutnya, pilih perangkat pertama dalam daftar tempat lokasi panel kamera diketahui dan di mana perangkat tersebut cocok dengan nilai yang disediakan, yang dalam contoh ini adalah kamera depan. Jika tidak ada kamera yang ditemukan pada panel yang diinginkan, kamera pertama atau default yang tersedia akan digunakan.

Jika perangkat kamera ditemukan, objek MediaCaptureInitializationSettings baru dibuat dan properti VideoDeviceId diatur ke perangkat yang dipilih. Selanjutnya, buat objek MediaCapture dan panggil InitializeAsync, lewati objek pengaturan untuk memberi tahu sistem untuk menggunakan kamera yang dipilih.

Terakhir, periksa untuk melihat apakah panel perangkat yang dipilih null atau tidak diketahui. Jika demikian, kamera eksternal, yang berarti bahwa rotasinya tidak terkait dengan rotasi perangkat. Jika panel diketahui dan berada di bagian depan sasis perangkat, kita tahu pratinjau harus dicerminkan, sehingga pelacakan variabel ini diatur.

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

Menginisialisasi kelas CameraRotationHelper

Sekarang kita mulai menggunakan kelas CameraRotationHelper . Nyatakan variabel anggota kelas untuk menyimpan objek. Panggil konstruktor, lewat di lokasi penutup kamera yang dipilih. Kelas pembantu menggunakan informasi ini untuk menghitung orientasi yang benar untuk media yang diambil, aliran pratinjau, dan UI. Daftarkan handler untuk peristiwa OrientationChanged kelas pembantu, yang akan dinaikkan ketika kita perlu memperbarui orientasi UI atau aliran pratinjau.

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

Menambahkan data orientasi ke aliran pratinjau kamera

Menambahkan orientasi yang benar ke metadata aliran pratinjau tidak memengaruhi bagaimana pratinjau muncul kepada pengguna, tetapi membantu sistem mengodekan bingkai apa pun yang diambil dari aliran pratinjau dengan benar.

Anda memulai pratinjau kamera dengan memanggil MediaCapture.StartPreviewAsync. Sebelum Anda melakukan ini, periksa variabel anggota untuk melihat apakah pratinjau harus dicerminkan (untuk kamera depan). Jika demikian, atur properti FlowDirection dari CaptureElement, bernama PreviewControl dalam contoh ini, ke FlowDirection.RightToLeft. Setelah memulai pratinjau, panggil metode pembantu SetPreviewRotationAsync untuk mengatur rotasi pratinjau. Berikut ini adalah implementasi metode ini.

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

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

Kami mengatur rotasi pratinjau dalam metode terpisah sehingga dapat diperbarui ketika orientasi telepon berubah tanpa menginisialisasi ulang aliran pratinjau. Jika kamera berada di luar perangkat, tidak ada tindakan yang diambil. Jika tidak, metode CameraRotationHelperGetCameraPreviewOrientation dipanggil dan mengembalikan orientasi yang tepat untuk aliran pratinjau.

Untuk mengatur metadata, properti streaming pratinjau diambil dengan memanggil VideoDeviceController.GetMediaStreamProperties. Selanjutnya, buat GUID yang mewakili atribut Media Foundation Transform (MFT) untuk rotasi streaming video. Di C++ Anda dapat menggunakan MF_MT_VIDEO_ROTATION konstanta, tetapi di C# Anda harus menentukan nilai GUID secara manual.

Tambahkan nilai properti ke objek properti aliran, menentukan GUID sebagai kunci dan rotasi pratinjau sebagai nilai. Properti ini mengharapkan nilai berada dalam satuan derajat berlawanan arah jarum jam, sehingga metode CameraRotationHelperconvertSimpleOrientationToClockwiseDegrees digunakan untuk mengonversi nilai orientasi sederhana. Terakhir, panggil SetEncodingPropertiesAsync untuk menerapkan properti rotasi baru ke aliran.

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

Selanjutnya, tambahkan handler untuk peristiwa CameraRotationHelper.OrientationChanged . Kejadian ini meneruskan argumen yang memungkinkan Anda mengetahui apakah aliran pratinjau perlu diputar. Jika orientasi perangkat diubah untuk menghadap ke atas atau menghadap ke bawah, nilai ini akan menjadi false. Jika pratinjau memang perlu diputar, panggil SetPreviewRotationAsync yang ditentukan sebelumnya.

Selanjutnya, di penanganan aktivitas OrientationChanged , perbarui UI Anda jika diperlukan. Dapatkan orientasi UI yang direkomendasikan saat ini dari kelas pembantu dengan memanggil GetUIOrientation dan mengonversi nilai ke derajat searah jarang, yang digunakan untuk transformasi XAML. Buat RotateTransform dari nilai orientasi dan atur properti RenderTransform kontrol XAML Anda. Bergantung pada tata letak UI Anda, Anda mungkin perlu membuat penyesuaian tambahan di sini selain hanya memutar kontrol. Selain itu, ingatlah bahwa semua pembaruan pada UI Anda harus dilakukan pada utas UI, jadi Anda harus menempatkan kode ini di dalam panggilan ke 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;
    });
}

Mengambil foto dengan data orientasi

Artikel Pengambilan foto, video, dan audio dasar dengan MediaCapture menunjukkan kepada Anda cara mengambil foto ke file dengan mengambil terlebih dahulu ke aliran dalam memori lalu menggunakan dekoder untuk membaca data gambar dari aliran dan encoder untuk mentranskode data gambar ke file. Data orientasi, yang diperoleh dari kelas CameraRotationHelper , dapat ditambahkan ke file gambar selama operasi transkode.

Dalam contoh berikut, foto diambil ke InMemoryRandomAccessStream dengan panggilan ke CapturePhotoToStreamAsync dan BitmapDecoder dibuat dari aliran. Selanjutnya StorageFile dibuat dan dibuka untuk mengulangi IRandomAccessStream untuk menulis ke file.

Sebelum melakukan transkode file, orientasi foto diambil dari metode kelas pembantu GetCameraCaptureOrientation. Metode ini mengembalikan objek SimpleOrientation yang dikonversi ke objek PhotoOrientation dengan metode pembantu ConvertSimpleOrientationToPhotoOrientation. Selanjutnya, objek BitmapPropertySet baru dibuat dan properti ditambahkan di mana kuncinya adalah "System.Photo.Orientation" dan nilainya adalah orientasi foto, yang dinyatakan sebagai BitmapTypedValue. "System.Photo.Orientation" adalah salah satu dari banyak properti Windows yang dapat ditambahkan sebagai metadata ke file gambar. Untuk daftar semua properti terkait foto, lihat Properti Windows - Foto. Untuk informasi selengkapnya tentang bekerja dengan metadata dalam gambar, lihat Metadata gambar.

Terakhir, kumpulan properti yang menyertakan data orientasi diatur untuk encoder dengan panggilan ke SetPropertiesAsync dan gambar ditranskodekan dengan panggilan ke 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();
    }
}

Mengambil video dengan data orientasi

Pengambilan video dasar dijelaskan dalam artikel Pengambilan foto, video, dan audio dasar dengan MediaCapture. Menambahkan data orientasi ke pengodean video yang diambil dilakukan menggunakan teknik yang sama seperti yang dijelaskan sebelumnya di bagian tentang menambahkan data orientasi ke aliran pratinjau.

Dalam contoh berikut, file dibuat di mana video yang diambil akan ditulis. Profil pengodean MP4 dibuat menggunakan metode statis CreateMp4. Orientasi yang tepat untuk video diperoleh dari kelas CameraRotationHelper dengan panggilan ke GetCameraCaptureOrientation Karena properti rotasi memerlukan orientasi untuk dinyatakan dalam derajat berlawanan arah jarum jam, metode pembantu ConvertSimpleOrientationToClockwiseDegrees dipanggil untuk mengonversi nilai orientasi. Selanjutnya, buat GUID yang mewakili atribut Media Foundation Transform (MFT) untuk rotasi streaming video. Di C++ Anda dapat menggunakan MF_MT_VIDEO_ROTATION konstanta, tetapi di C# Anda harus menentukan nilai GUID secara manual. Tambahkan nilai properti ke objek properti aliran, menentukan GUID sebagai kunci dan rotasi sebagai nilai. Akhirnya panggil StartRecordToStorageFileAsync untuk mulai merekam video yang dikodekan dengan data orientasi.

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

CameraRotationHelper daftar kode lengkap

Cuplikan kode berikut mencantumkan kode lengkap untuk kelas CameraRotationHelper yang mengelola sensor orientasi perangkat keras, menghitung nilai orientasi yang tepat untuk foto dan video, dan menyediakan metode pembantu untuk mengonversi antara representasi orientasi yang berbeda yang digunakan oleh fitur Windows yang berbeda. Jika Anda mengikuti panduan yang ditunjukkan pada artikel di atas, Anda dapat menambahkan kelas ini ke proyek apa adanya tanpa harus membuat perubahan apa pun. Tentu saja, Anda dapat merasa bebas untuk menyesuaikan kode berikut untuk memenuhi kebutuhan skenario khusus Anda.

Kelas pembantu ini menggunakan SimpleOrientationSensor perangkat untuk menentukan orientasi sasis perangkat saat ini dan kelas DisplayInformation untuk menentukan orientasi tampilan saat ini. Masing-masing kelas ini menyediakan peristiwa yang dinaikkan ketika orientasi saat ini berubah. Panel tempat perangkat pengambilan dipasang - menghadap ke depan, menghadap ke belakang, atau eksternal - digunakan untuk menentukan apakah aliran pratinjau harus dicerminkan. Selain itu, properti EnclosureLocation.RotationAngleInDegreesClockwise , yang didukung oleh beberapa perangkat, digunakan untuk menentukan orientasi tempat kamera dipasang pada chasses.

Metode berikut dapat digunakan untuk mendapatkan nilai orientasi yang direkomendasikan untuk tugas aplikasi kamera yang ditentukan:

  • GetUIOrientation - Mengembalikan orientasi yang disarankan untuk elemen antarmuka pengguna kamera.
  • GetCameraCaptureOrientation - Mengembalikan orientasi yang disarankan untuk pengodean ke dalam metadata gambar.
  • GetCameraPreviewOrientation - Mengembalikan orientasi yang disarankan untuk aliran pratinjau untuk memberikan pengalaman pengguna alami.
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;
        }
    }
}