
I'm playing with some raw video streams. These raw data provide YUV (or YCrCb) components interleaved between each other and it is sometimes quite cumbersome to find a triplet of matching YUV bytes before converting them to RGB. For "packed" formats such as YUY2, the operation seems pretty straight forward in Haskell:
-- | Converting from packed YUY2 4:2:2 pixels to RGB ones. -- YUY2: Y0 U0 Y1 V0 < -- 2 macro pixels --> (Y0:U0:V0)+(Y1:U0:V0) yuy2ToRGB :: B.ByteString -> B.ByteString yuy2ToRGB bs = f [] bs where f acc xs | B.null xs = B.pack $ reverse acc -- | otherwise = let [y0, u0, y1, v0] = B.unpack (B.take 4 xs) (r0,g0,b0) = yuv2rgb (y0,u0,v0) (r1,g1,b1) = yuv2rgb (y1,u0,v0) in f (b1:g1:r1:b0:g0:r0:acc) (B.drop 4 xs)
But on the other hand, I'm having some headaches converting from planar NV12 to RGB. For example, A width=4 and height=4 NV12 frame is stored in a 1-dimensional array of (4 * 4 * 3/2 = 24) elements: | Y00 | Y01 | Y02 | Y03 | Y04 | Y05 | Y06 | Y07 | Y08 | Y09 | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 | U00 | V00 | U01 | V01 | U02 | V02 | U03 | V03 | This array could be unfold to the following matrix representation, and you see that the luminance (Y) bytes are provided first, followed by a sequence of (U,V) pairs. | Y00 | Y01 | Y02 | Y03 | | Y04 | Y05 | Y06 | Y07 | | Y08 | Y09 | Y10 | Y11 | | Y12 | Y13 | Y14 | Y15 | |-----------------------| | U00 | V00 | U01 | V01 | | U02 | V02 | U03 | V03 | Now, if you want to get the RGB value for every pixel, you have to fetch the corresponding (U,V) components at the end of the frame. pixel00 will need (Y00,U00,V00) pixel02 will need (Y02,U01,V01) ... pixel15 will need (Y15,U03,V03) So far, I have this Haskell function to convert NV12 to RGB.
nv12ToRGB w h bs = B.pack $ concatMap f [0.. w * h - 1] where f i = let m = (i `div` w) `div` 2 -- line index modulo 2 n = (i `rem` w) `div` 2 -- column index modulo 2 iuv = m * w `div` 2 + n (r,g,b) = yuv2rgb (pixelY i, pixelU iuv, pixelV iuv) in [r,g,b]
pixelY i = B.index bs i pixelU i = B.index bs (w*h+2*i) pixelV i = B.index bs (w*h+2*i+1)
I don't know if converting ByteString to list, using map with a not-so-simple f function, then concatenating and repacking the ByteString is the best solution one could find in Haskell and I would like to know if by chance some "senior" haskellers would know some tricks (like list comprehension or ByteString.unfoldWhatever) to improve the previous code. regards, /john