/// <summary>
        /// Try to convert from YCbCr to RGB colors
        /// </summary>
        /// <param name="sourceColorspace">The colorspace of the luma and chroma</param>
        /// <param name="luma">The luma plane</param>
        /// <param name="blueDifferential">The blue differential (Cb) plane</param>
        /// <param name="redDifferential">The red differential (Cr) plane</param>
        /// <param name="width">The width of the frame</param>
        /// <param name="height">The height of the frame</param>
        /// <returns>An RGB plane if this function succeeds. None otherwise</returns>
        /// <remarks>
        /// FFMPEG outputs 420mpeg2 (where the chroma samples are aligned horizontally, but 
        /// shifted a half-pixel down).
        /// https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx
        /// </remarks>
        public static Maybe<Color[][]> TryConvertFrameToRGB(
            ColorSpace sourceColorspace,
            byte[][] luma,
            byte[][] blueDifferential,
            byte[][] redDifferential,
            int width,
            int height
        )
        {
            var inFrames = new YCrCbFrame
            {
                ColorSpace = sourceColorspace,
                Luma = luma,
                Cb = blueDifferential,
                Cr = redDifferential,
                Width = width,
                Height = height,
            };
            if (Equals(sourceColorspace, ColorSpace.FourFourFour))
            {
                return TryConvertYCbCr444ToRGB(inFrames);
            }

            if (Equals(sourceColorspace, ColorSpace.FourTwoTwo))
            {
                return from horizontalUpconvert in TryConvert422To444(inFrames)
                       select TryConvertYCbCr444ToRGB(horizontalUpconvert);
            }

            if (Equals(sourceColorspace, ColorSpace.FourTwoZeroMpeg2))
            {
                return from verticalUpconvert in TryConvert420To422(inFrames)
                       from horizontalUpconvert in TryConvert422To444(verticalUpconvert)
                       select TryConvertYCbCr444ToRGB(horizontalUpconvert);
            }

            return Maybe<Color[][]>.Nothing;
        }
        private static Maybe<YCrCbFrame> TryConvert422To444(
            YCrCbFrame subsampledFrames
        )
        {
            var outFrames = new YCrCbFrame
            {
                ColorSpace = ColorSpace.FourTwoTwo,
                Luma = subsampledFrames.Luma,
                Cb = new byte[subsampledFrames.Height][],
                Cr = new byte[subsampledFrames.Height][],
                Width = subsampledFrames.Width,
                Height = subsampledFrames.Height,
            };

            UpsampleHorizontalResolution(subsampledFrames.Cb, outFrames.Cb);
            UpsampleHorizontalResolution(subsampledFrames.Cr, outFrames.Cr);

            return outFrames.ToMaybe();
        }
        private static Maybe<Color[][]> TryConvertYCbCr444ToRGB(
            YCrCbFrame inFrame
        )
        {
            try
            {
                Color[][] frame = new Color[inFrame.Height][];
                for (int row = 0; row < inFrame.Height; row++)
                {
                    frame[row] = new Color[inFrame.Width];
                    for (int col = 0; col < inFrame.Width; col++)
                    {
                        byte currentLuma = inFrame.Luma[row][col];
                        byte currentCb = inFrame.Cb[row][col];
                        byte currentCr = inFrame.Cr[row][col];
                        frame[row][col] = ConvertYUVToRGB(currentLuma, currentCb, currentCr);
                    }
                }

                if (frame.Length != inFrame.Height || frame[0].Length != inFrame.Width)
                {
                    return Maybe<Color[][]>.Nothing;
                }

                return frame.ToMaybe();
            }
            catch (Exception)
            {
            }

            return Maybe<Color[][]>.Nothing;
        }
        /// <summary>
        /// Vertically upconvert from 4:2:0 to 4:2:2
        /// </summary>
        /// <param name="subsampledFrames"></param>
        /// <returns></returns>
        private static Maybe<YCrCbFrame> TryConvert420To422(
            YCrCbFrame subsampledFrames
        )
        {
            var outFrames = new YCrCbFrame
            {
                ColorSpace = ColorSpace.FourTwoTwo,
                Luma = subsampledFrames.Luma,
                Cb = new byte[subsampledFrames.Height][],
                Cr = new byte[subsampledFrames.Height][],
                Width = subsampledFrames.Width,
                Height = subsampledFrames.Height,
            };

            // Initialize all chroma planes
            int widthOfChromaRow = subsampledFrames.Cb[0].Length;
            for (int row = 0; row < outFrames.Height; row++)
            {
                outFrames.Cb[row] = new byte[widthOfChromaRow];
                outFrames.Cr[row] = new byte[widthOfChromaRow];
            }

            UpsampleVerticalResolution(subsampledFrames.Cb, outFrames.Cb);
            UpsampleVerticalResolution(subsampledFrames.Cr, outFrames.Cr);

            return outFrames.ToMaybe();
        }