前言:因为需要将安卓设备的相机预览数据传到pc,然后在windows pc上显示出来,所以涉及到socket通信用于数据传输,以及预览数据转化问题。由于pc端软件使用c#开发,预览数据转化效率太低,于是采用c语言实现,并转化成dll供C#使用,转化效率提升30倍(测试时采用1280*720图像数据,耗时10ms左右)。
一、nv21转rgb24(rgb888)方法如下:
//将nv21数据转化成rgb24的数据
// input: nv21数据,长度为宽*高*3/2
// output: rgb数据,长度为宽*高*3
// width: 图像宽度
// height:图像高度
int nv21_to_rgb24(unsigned char* input, unsigned char* output, int width, int height) {
if (width < 1 || height < 1 || input == NULL || output == NULL)
return 0;
// bit depth
int depth = 3;
int nvOff = width * height;
int i, j, yIndex = 0;
int y, u, v;
int r, g, b, nvIndex = 0;
unsigned char* yuvData = input;
unsigned char* rgbData = output;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++, ++yIndex) {
nvIndex = (i / 2) * width + j - j % 2;
y = yuvData[yIndex] & 0xff;
v = yuvData[nvOff + nvIndex] & 0xff;
u = yuvData[nvOff + nvIndex + 1] & 0xff;
// yuv to rgb
r = y + ((351 * (v - 128)) >> 8); //r
g = y - ((179 * (v - 128) + 86 * (u - 128)) >> 8); //g
b = y + ((443 * (u - 128)) >> 8); //b
r = ((r > 255) ? 255 : (r < 0) ? 0 : r);
g = ((g > 255) ? 255 : (g < 0) ? 0 : g);
b = ((b > 255) ? 255 : (b < 0) ? 0 : b);
// RGB格式的图像存储的顺序,并非像字面的顺序,而是以:B、G、R的顺序进行存储。
*(rgbData + yIndex * depth + 0) = b;
*(rgbData + yIndex * depth + 1) = g;
*(rgbData + yIndex * depth + 2) = r;
}
}
return 1;
}
二、由于安卓相机预览数据是逆时针旋转了90度的,所以要在PC上查看得调正,所以自己在上述方法中稍作调整。
以下方法为nv21转rgb24,并顺时针旋转90,消耗时间跟nv21转rgb24方法几乎一样,两步并成一步,节省时间。
// nv21转rgb24,转化完后直接旋转。省得转化后再旋转浪费时间。
//将nv21数据转化成rgb24的数据
// input: nv21数据,长度为宽*高*3/2
// output: rgb数据,长度为宽*高*3
// width: 图像宽度(旋转前)
// height:图像高度(旋转前)
// 旋转后图像宽==height,图像高==width
int nv21_to_rgb24_clockwise_rotate90(unsigned char* input, unsigned char* output, int width, int height) {
if (width < 1 || height < 1 || input == NULL || output == NULL)
return 0;
// bit depth
int depth = 3;
int nvOff = width * height;
int i, j, yIndex = 0;
int y, u, v;
int r, g, b, nvIndex = 0;
unsigned char* yuvData = input;
unsigned char* rgbData = output;
// yuv转成rgb后,立马将数据放到旋转后的位置。第一行换到最后一列,第二行换到倒数第二列,以此类推
// 当前正在做旋转处理的数据所在列
int currTransColumn = height;
int currColumnBaseIndex = 0;
// 转化后的图像中,每一行的数据长度。
int afterTransLinesLength = height * depth;
int tmp = 0;
for (i = 0; i < height; i++) {
// 转化后图像中每列的第一个rgb数据最大下标+1,从最后一列开始计算
currColumnBaseIndex = currTransColumn * depth;
for (j = 0; j < width; j++, ++yIndex) {
nvIndex = (i / 2) * width + j - j % 2;
y = yuvData[yIndex] & 0xff;
v = yuvData[nvOff + nvIndex] & 0xff;
u = yuvData[nvOff + nvIndex + 1] & 0xff;
// yuv to rgb
r = y + ((351 * (v - 128)) >> 8); //r
g = y - ((179 * (v - 128) + 86 * (u - 128)) >> 8); //g
b = y + ((443 * (u - 128)) >> 8); //b
r = ((r > 255) ? 255 : (r < 0) ? 0 : r);
g = ((g > 255) ? 255 : (g < 0) ? 0 : g);
b = ((b > 255) ? 255 : (b < 0) ? 0 : b);
// RGB格式的图像存储的顺序,并非像字面的顺序,而是以:B、G、R的顺序进行存储。
// 旋转后放的位置
*(rgbData + currColumnBaseIndex - 3) = b;
*(rgbData + currColumnBaseIndex - 2) = g;
*(rgbData + currColumnBaseIndex - 1) = r;
// 下一次复制的下标为上一行下标+每一行的数据长度
currColumnBaseIndex = currColumnBaseIndex + afterTransLinesLength;
}
// 减一列
currTransColumn--;
}
return 1;
}
PS:想要了解安卓和PC进行socket通信的,请查看我的相关博客,附有demo。