MediaCodec and Camera: colorspaces don't match
I solved it by swapping the byteplanes myself on Android level, using a simple function:
public byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {
byte[] i420bytes = new byte[yv12bytes.length];
for (int i = 0; i < width*height; i++)
i420bytes[i] = yv12bytes[i];
for (int i = width*height; i < width*height + (width/2*height/2); i++)
i420bytes[i] = yv12bytes[i + (width/2*height/2)];
for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++)
i420bytes[i] = yv12bytes[i - (width/2*height/2)];
return i420bytes;
}
I think it's more efficient to swap the values in place.
int wh4 = input.length/6; //wh4 = width*height/4
byte tmp;
for (int i=wh4*4; i<wh4*5; i++)
{
tmp = input[i];
input[i] = input[i+wh4];
input[i+wh4] = tmp;
}
Maybe even better, you can instead replace
inputBuffer.put(input);
With the 3 planar slices in the correct order
inputBuffer.put(input, 0, wh4*4);
inputBuffer.put(input, wh4*5, wh4);
inputBuffer.put(input, wh4*4, wh4);
I think that should only have a tiny overhead
It seems Android is transmitting in YV12, but the format set in the H264 headers is YUV420. These formats are equal except the U and V channels are in different order, which explains the swapping of red and blue.
Best would be of course to fix the setting on the Android side. But if there is no way to set compatible settings for camera and encoder, you will have to force the format on the GStreamer side.
This can be done by adding capssetter
element after the ffdec_h264
... ! ffdec_h264 ! capssetter caps="video/x-raw-yuv, format=(fourcc)YV12" ! colorspace ! ...