android显示相关实现调试手记
首先来看一下总得初始化接口实现,里面是先打开设备,在打开设备里面会设置输入视频源,查询视频设备驱动能力,紧接着设置视频模式,是V4L2_MODE_VIDEO的,再来设置video的参数,比如宽高、pixel的格式、filed等,然后再申请buffer,绑定匹配buffer地址,最后就是启动视频设备数据流,整个设备就工作起了。代码具体如下:[cpp]
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数据接口类型等,代码示例如下:
[cpp]
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;
}
[cpp]
}
要设置video的参数;
[cpp]
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,监测代码如下:
[cpp]
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使用率非常低,这样主芯片可以干很多其他的事情。代码如下:
[cpp]
int hight = 0;
hight = (mOldSystem == 0)? 480:576;
LOGV("mOldSystem = %d, hight =%d\n", mOldSystem, hight);
[cpp]
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;
}
....
<p> /*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; &
补充:移动开发 , Android ,