在上两篇文章基于V4L2驱动程序的USB摄像头Android(JNI)的编写(一)和基于V4L2驱动程序的USB摄像头Android(JNI)的编写(二)中,我详细介绍了如何配置V4L2的环境、设置捕获视频数据的格式,以及视频流数据捕获的过程。但是由于我们捕获到的视频数据是YUYV422格式的,那么在这一篇文章中,我将主要介绍如何将YUYV422格式的数据转换成ARGB格式的数据,同时将ARGB格式数据转换成bitmap格式并上传给上层应用程序。
YUYV转ARGB
在YUYV422格式的数据转换成ARGB格式数据,我们这里采用了一个比较通用的转换函数。源代码如下:
- void yuyv422toABGRY(unsigned charchar *src)
- {
-
- int width=0;
- int height=0;
-
- width = IMG_WIDTH;
- height = IMG_HEIGHT;
-
- int frameSize =width*height*2;
-
- int i;
-
- if((!rgb || !ybuf)){
- return;
- }
- intint *lrgb = NULL;
- intint *lybuf = NULL;
-
- lrgb = &rgb[0];
- lybuf = &ybuf[0];
-
- if(yuv_tbl_ready==0){
- for(i=0 ; i<256 ; i++){
-
- y1192_tbl[i] = 1192*(i-16);
- if(y1192_tbl[i]<0){
- y1192_tbl[i]=0;
- }
-
- v1634_tbl[i] = 1634*(i-128);
- v833_tbl[i] = 833*(i-128);
- u400_tbl[i] = 400*(i-128);
- u2066_tbl[i] = 2066*(i-128);
- }
- yuv_tbl_ready=1;
- }
-
- for(i=0 ; i<frameSize ; i+=4){
- unsigned char y1, y2, u, v;
-
- y1 = src[i];
- u = src[i+1];
- y2 = src[i+2];
- v = src[i+3];
-
-
- int y1192_1=y1192_tbl[y1];
- int r1 = (y1192_1 + v1634_tbl[v])>>10;
- int g1 = (y1192_1 - v833_tbl[v] - u400_tbl[u])>>10;
- int b1 = (y1192_1 + u2066_tbl[u])>>10;
-
- int y1192_2=y1192_tbl[y2];
- int r2 = (y1192_2 + v1634_tbl[v])>>10;
- int g2 = (y1192_2 - v833_tbl[v] - u400_tbl[u])>>10;
- int b2 = (y1192_2 + u2066_tbl[u])>>10;
-
- r1 = r1>255 ? 255 : r1<0 ? 0 : r1;
- g1 = g1>255 ? 255 : g1<0 ? 0 : g1;
- b1 = b1>255 ? 255 : b1<0 ? 0 : b1;
- r2 = r2>255 ? 255 : r2<0 ? 0 : r2;
- g2 = g2>255 ? 255 : g2<0 ? 0 : g2;
- b2 = b2>255 ? 255 : b2<0 ? 0 : b2;
-
- *lrgb++ = 0xff000000 | b1<<16 | g1<<8 | r1;
- *lrgb++ = 0xff000000 | b2<<16 | g2<<8 | r2;
-
- if(lybuf!=NULL){
- *lybuf++ = y1;
- *lybuf++ = y2;
- }
- }
-
- }
上面的函数是YUYV422转ARGB数据格式的一个固定写法,当然里面的framesize的大小是要根据实际大小进行改变的。同时还要注意rgb指针是一个全局变量,这里通过将rgb指针指向的值赋给lrgb指针,那么rgb指针所指向的值便和lrgb指针指向的值一同变化了。
ARGB转Bitmap
经过上面一步的yuyv422toABGRY函数,我们已经得到了32位的ARGB格式数据,但得到的ARGB数据并不能直接显示在屏幕上,虽然我们已经离这一步很近了,但是我们还是得做最后一步的转换,即将ARGB转换为Bitmap格式的数据。
- #include <android/bitmap.h>
引入这个头文件之后呢,需要在Android.mk文件中指定
LOCAL_LDLIBS` += `-ljnigraphics
在这个头文件中,有一个非常重要的结构体
AndroidBitmapInfo,还有几个重要的方法AndroidBitmap_getInfo、AndroidBitmap_lockPixels、AndroidBitmap_unlockPixels
首先是结构体
AndroidBitmapInfo,顾名思义该结构体实际上就是对Android的bitmap对象的一种简单描述,如bitmap的长、宽、像素数据格式等等。
下面介绍这三个方法以及如何使用
- AndroidBitmap_getInfo: 该函数的作用是通过给其传入bitmap参数,从而获取该bitmap的AndroidBitmapInfo描述符。
- AndroidBitmap_lockPixels:对bitmap像素缓存上锁(确保不会有多个进程同时进来这一步修改pixels数据),其实就是获取该bitmap的像素缓存的pixels的地址
- AndroidBitmap_unlockPixels:在使用完AndroidBitmap_lockPixels获取到该bitmap的像素缓存pixels地址之后,我们会对pixels指向的值进行修改,我们的ARGB数据值传递给pixels也是在这里进行的。修改完之后我们便会调用AndroidBitmap_unlockPixels方法,对bitmap进行解锁。
下面详细介绍如何实现ARGB转Bitmap的
- 第一步:在Android上层应用程序中设定好bitmap对象,设定的内容包括,长、宽、像素格式
- Bitmap bmp = Bitmap.createBitmap(IMG_WIDTH, IMG_HEIGHT, Bitmap.Config.ARGB_8888);
- 第二步:编写jni程序
-
- void
- Java_com_intel_view_CameraPreview_pixeltobmp( JNIEnv* env,jobject thiz,jobject bitmap){
-
- jboolean bo;
-
-
- AndroidBitmapInfo info;
- void* pixels;
- int ret;
- int i;
- intint *colors;
-
- int width=0;
- int height=0;
-
- if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0) {
- LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
- return;
- }
-
- width = info.width;
- height = info.height;
-
- if(!rgb || !ybuf) return;
-
- if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
- LOGE("Bitmap format is not RGBA_8888 !");
- return;
- }
-
- if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0) {
- LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
- }
-
- colors = (int*)pixels;
- intint *lrgb =NULL;
- lrgb = &rgb[0];
-
- for(i=0 ; i<width*height ; i++){
-
- *colors++ = *lrgb++;
- }
-
- AndroidBitmap_unlockPixels(env, bitmap);
-
- }
通过上面两步,这个时候bitmap已经存满的视频数据了,现在在上层应用程序就可以通过调用方法来进行显示了。