Let\'s say I\'ve got an image of some format (its binary representation, say, cv::Mat from OpenCV or YuvImage from Android, not compressed), and interpreted its data as YUV NV21
Well, at the end, we've found some inconsistencies in image handling.
YuvImage of Android expects you to feed it with NV21, so I had a working conversion method from I420 to NV21. When I changed a video source (same vendor, different model), I expected it to come with the same format, but, in fact, it was NV12 instead.
Reinterpret-casting NV12 as I420 and then converting it to NV21 is what happened here.
The YUV image output uses an Android library which results in different YUV formats depending on the device used.
In my case, depending on which of the two devices I used, I needed to switch the bytes of the two color channels (NV12 to NV21).
What really helped me understanding which format I have was using the following YUV player: https://github.com/Tee0125/yuvplayer
In the player you can actively change the way the data is interpreted as, just test different settings until you find the right one.
Although I don't know if you need it, I'll provide the method I used to switch the channels.
/**
* Changes a NV12 yuv Frame to the NV21 format. Basically the two color channels are switched.
*
* @param yuvFrameNV12 The YUV Frame in the NV12 format.
* @param width The width of the frame.
* @param height The height of the frame.
* @return The YUV Frame in the NV21 format.
*/
private static byte[] convertNV12toNV21(byte[] yuvFrameNV12, int width, int height) {
final int length = yuvFrameNV12.length;
for (int i1 = 0; i1 < length; i1 += 2) {
if (i1 >= width * height) { //start after the luminance bytes
byte tmp = yuvFrameNV12[i1];
yuvFrameNV12[i1] = yuvFrameNV12[i1+1];
yuvFrameNV12[i1+1] = tmp;
}
}
return yuvFrameNV12;
}