基于avd7181c解决视频输入效果差的问题<四>---android显示相关实现调试手记
笔者在前几篇文章中提到的都是AVD7181C的信号通过CSI的数据通道,然后给camera APK来显示,就可以看到YPBPR\CVBS的输入信号了。利用camera apk调试AVD7181C也是为了加快调试进度,笔者在实践中确实感觉很有益。通过它,我们可以先把AVD7181C以及CSI的驱动先调试稳定下来,调试正确,确保数据到应用之前是正确的,但是最终我们是要作产品,总不能用camera的apk来看这种视频输入的图像,从理论上当然也可以通过改造camera apk来控制ui显示,但是这样也会比较麻烦,因为系统本身还需要camera apk,因为有真正的camera也在系统中。那怎么办呢?
从HAL layer到应用重新做一套,这个工作量会有一点,主要是在硬件抽象层的代码设计上。还好全志平台提供了TVD的一套东西。笔者就可以模仿这一套东西来做,这样也省了不少事情,当然硬件抽象层是跟硬件打交道的,TVD跟CSI还是不大一样的,设置的参数、fmt这些会很大不同,因为硬件驱动是不一样的,全志TVD驱动做得不是很标准,代码写得比较乱,CSI那边的驱动做得要好一些。
借鉴TVD的框架是可以的,稍微改动一些可以为我所用。笔者同样也借鉴了Camera硬件抽象层的代码实现,两种合理结合一些,这是就成了,当然还得加入一些里面没有实现的东西。
/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/sundesheng125原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
首先来看一下总得初始化接口实现,里面是先打开设备,在打开设备里面会设置输入视频源,查询视频设备驱动能力,紧接着设置视频模式,是V4L2_MODE_VIDEO的,再来设置video的参数,比如宽高、pixel的格式、filed等,然后再申请buffer,绑定匹配buffer地址,最后就是启动视频设备数据流,整个设备就工作起了。代码具体如下:
status_t CCSIDecoderHardware::v4l2Init(int mOldSystem) { F_LOG; CHECK_ERROR(openCameraDev()); // set capture mode struct v4l2_streamparm params; params.parm.capture.timeperframe.numerator = 1; params.parm.capture.timeperframe.denominator = 25; params.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; params.parm.capture.capturemode = V4L2_MODE_VIDEO;//V4L2_MODE_IMAGE v4l2setCaptureParams(¶ms); // set v4l2 device parameters CHECK_ERROR(v4l2SetVideoParams(mOldSystem)); // v4l2 request buffers CHECK_ERROR(v4l2ReqBufs()); // v4l2 query buffers CHECK_ERROR(v4l2QueryBuf()); // stream on the v4l2 device CHECK_ERROR(v4l2StartStreaming()); return OK; }
笔者移植了tryFmt()这个用来寻找对应的fmt,会有一下参数关联,比如输入CSI的fmt格式,CSI输出数据格式、CSI数据接口类型等,代码示例如下:
struct v4l2_fmtdesc fmtdesc; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; for(int i = 0; i < 12; i++) { fmtdesc.index = i; if (-1 == ioctl (mV4l2Handle, VIDIOC_ENUM_FMT, &fmtdesc)) { break; } LOGV("format index = %d, name = %s, v4l2 pixel format = %x\n", i, fmtdesc.description, fmtdesc.pixelformat); if (fmtdesc.pixelformat == format) { return OK; }
}
要设置video的参数;
struct v4l2_format format; LOGD("#####################VIDIOC_S_PARM\n"); memset(&format, 0, sizeof(format)); format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; format.fmt.pix.width = mVideoWidth; format.fmt.pix.height = mVideoHeight; format.fmt.pix.pixelformat = mVideoFormat; //pix_fmt format.fmt.pix.field = V4L2_FIELD_NONE; ret = ioctl(mV4l2Handle, VIDIOC_S_FMT, &format); if (ret < 0) { LOGE("VIDIOC_S_FMT Failed: %s", strerror(errno)); return ret; }
得起一个线程来负责读数据帧,以及把数据帧数据处理掉。在这个线程中还需要加入实时检测视频信号的有无,以及信号的制式,是NTSC还是PAL,监测代码如下:
struct v4l2_control ctrl; ctrl.value = 0; ctrl.id = V4L2_CID_BRIGHTNESS;/*just use this channel to read fmt & detect signals*/ ret = ioctl (mV4l2Handle, VIDIOC_G_CTRL, &ctrl); if (ret < 0) { LOGE("vidioc_g_ctrl: %s", strerror(errno)); } LOGE("get_system ctrl.value 0x%02x", ctrl.value); mDetectcvbsSignal = ((ctrl.value & 0x10) == 0x10)? 0:1 ;/*bit4 1:no signal 0: has signal*/ fmt = ctrl.value & 0x7; if((0 == fmt) || (1 == fmt)) *__system = 0;/*NTSC*/ else *__system = 1;/*PAL*/
笔者在信号制式的处理上稍微做了一下变通,因为这两种制式在大小上是不一样,但是我们可以用大一点的那个buffer,这样我们就不需要重新去设置这些buffer,而只需要控制后面取buffer的数据大小就可以,这样也是简便了一下。同时,笔者在调试发现NTSC的制式上,开始会有是十几个像素高度的脏数据,显示是乱的,这样我用大buffer就可以把N制的整个数据都截下来,起始地址移动一下就可以了,这样确实真实有效,里面使用了native window的操作,直接把数据帧的buffer地址赋给了hwcoposer,也就是到内核里的硬件驱动里,这样就减少了数据拷贝的工作,cpu使用率非常低,这样主芯片可以干很多其他的事情。代码如下:
int hight = 0; hight = (mOldSystem == 0)? 480:576; LOGV("mOldSystem = %d, hight =%d\n", mOldSystem, hight);
ret = native_window_set_buffers_geometryex(mPreviewWindow , mVideoWidth, hight,HWC_FORMAT_YUV422PLANAR_UV_COMBINED, //*/HWC_FORMAT_DEFAULT,HWC_FORMAT_YUV420PLANAR 0); if (ret != NO_ERROR) { LOGE("%s: Error in set_buffers_geometry %d -> %s",__FUNCTION__, -ret, strerror(-ret)); return ret; } ..../*when NTSC shoud shift display buffer*/ overlay_para.bProgressiveSrc = 0; overlay_para.bTopFieldFirst = 1; overlay_para.pVideoInfo.frame_rate = 25000; if(1 == mOldSystem) {/*PAL*/ overlay_para.top_y = (unsigned int)buf.m.offset; overlay_para.top_c = (unsigned int)buf.m.offset + mVideoWidth * mVideoHeight; } else {/*NTSC*/ overlay_para.top_y = (unsigned int)buf.m.offset+ 1440;/* 720*20 20 point*/ overlay_para.top_c = (unsigned int)buf.m.offset + mVideoWidth * mVideoHeight+7200;/* 720*20*1/2 20 point*/ } overlay_para.bottom_y = 0; overlay_para.bottom_c = 0; overlay_para.number = 0;
if (mOverlayFirstFrame) { LOGD("first frame true"); overlay_para.first_frame_flg = 1; mOverlayFirstFrame = false; } else { overlay_para.first_frame_flg = 0; }
ret = mPreviewWindow->perform(mPreviewWindow, NATIVE_WINDOW_SETPARAMETER, HWC_LAYER_SETFRAMEPARA, (uint32_t)&overlay_para); if (ret != OK) { LOGE("NATIVE_WINDOW_SETPARAMETER failed"); return ret;
HWC_FORMAT_YUV422PLANAR_UV_COMBINED是笔者自定义的格式,全志平台里没有做这种格式,在hwc的硬件抽象层里需要添加需相应的代码实现,贴一部分如下:
case HWC_FORMAT_YUV422PLANAR_UV_COMBINED: disp_format = DISP_FORMAT_YUV422; fb_mode = DISP_MOD_NON_MB_UV_COMBINED; disp_seq = DISP_SEQ_UYVY; break;
当然,以后如果要做录像的话,就需要把数据帧截下来,通过多媒体那边的库来压缩编码存文件,这个工作可以参考camera里面的实现。硬件抽象层的代码就差不多了。JNI以及应用APK,都可以再TVD上稍微改改,难度不大,这里就不再详述。贴一张整个实现后的应用截图吧!