最近要在开源的平板上做Linux的项目,需要用到视频流服务器,首选肯定是MJPG-Streamer,但是按照之前的调试记录发现有问题,总是报一个I2C的错误,错误信息如下所示:
-
MJPG-streamer [81]: starting application
-
-
MJPG Streamer Version.: 2.0
-
MJPG-streamer [81]: MJPG Streamer Version.: 2.0
-
-
-
i: Using V4L2 device.: /dev/video0
-
MJPG-streamer [81]: Using V4L2 device.: /dev/video0
-
-
-
i: Desired Resolution: 640 x 480
-
MJPG-streamer [81]: Desired Resolution: 640 x 480
-
-
-
i: Frames Per Second.: 5
-
MJPG-streamer [81]: Frames Per Second.: 5
-
-
-
i: Format............: YUV
-
MJPG-streamer [81]: Format............: YUV
-
-
-
i: JPEG Quality......: 80
-
MJPG-streamer [81]: JPEG Quality......: 80
-
-
-
[ 37.075219] [i2c2] incomplete xfer (0x20)
-
[ 37.079274] [CSI_ERR][GC0308]sensor_write error!
-
[ 37.087464] [CSI_ERR][GC0308]sensor_write_err! reg_num = $? value = ?
-
[ 37.094063] [CSI]buffer_setup, buffer count=4, size=614400
-
-
Unable to map buffer: Invalid argument
-
Init v4L2 failed !! exit fatal
-
i: init_VideoIn failed
-
MJPG-streamer [81]: init_VideoIn failed
实际上这里面包含两个错误,最后四行报的错误很明显,就是mmap的时候参数出错了。
-
Unable to map buffer: Invalid argument
-
Init v4L2 failed !! exit fatal
-
i: init_VideoIn failed
-
MJPG-streamer [81]: init_VideoIn failed
在plugins/input_uvc/v4l2uvc.c源码的第200行static int init_v4l2(struct vdIn *vd)函数中找到mmap函数:
-
vd->mem[i] = mmap(0 ,
-
-
-
vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
-
vd->buf.m.offset);
在第96行我们找到 init_v4l2函数中open函数:
-
if ((vd->fd = open(vd->videodevice, O_RDWR)) == -1) {
-
perror("ERROR opening V4L interface");
-
return -1;
-
}
我们发现打开的时候是使用读写方式打开的,而mmap的时候参数是以只读的方式打开的。在man手册里描述mmap()函数有这么一段话:
-
The prot argument describes the desired memory protection of the mapping (and must not conflict with the open mode of the file). It is either PROT_NONE or the bitwise OR of one or more of the following flags:
所以看的出我们用读写打开,mmap的时候也必须以读写的方式映射,所以mmap函数应该改成:
-
vd->mem[i] = mmap(0 ,
-
* FSPAD_702 Linux added by LeeSheen */
-
vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,
-
vd->buf.m.offset);
改完之后测试,发现mmap的错误消失了,但I2C的错误仍然存在。错误信息是:
-
[ 37.075219] [i2c2] incomplete xfer (0x20)
-
[ 37.079274] [CSI_ERR][GC0308]sensor_write error!
-
[ 37.087464] [CSI_ERR][GC0308]sensor_write_err! reg_num = $? value = ?
I2C跟到驱动的sensor_write函数中,位置是drivers/media/video/sun5i_csi/device/gc0308.c:
-
static int sensor_write(struct v4l2_subdev *sd, unsigned char *reg,
-
unsigned char *value)
-
{
-
struct i2c_client *client = v4l2_get_subdevdata(sd);
-
struct i2c_msg msg;
-
unsigned char data[REG_STEP];
-
int ret,i;
-
-
for(i = 0; i < REG_ADDR_STEP; i++)
-
data[i] = reg[i];
-
for(i = REG_ADDR_STEP; i < REG_STEP; i++)
-
data[i] = value[i-REG_ADDR_STEP];
-
-
msg.addr = client->addr;
-
msg.flags = 0;
-
msg.len = REG_STEP;
-
msg.buf = data;
-
-
-
-
ret = i2c_transfer(client->adapter, &msg, 1);
-
if (ret > 0) {
-
ret = 0;
-
}
-
else if (ret < 0) {
-
csi_dev_err("sensor_write error!\n");
-
}
-
return ret;
-
}
发现就是I2C写函数出错了,可是安卓中摄像头是正常的,所以猜想问题还是应该出现摄像头初始化上,我看了看csi驱动,发现了这样一个函数:
-
static int internal_s_input(struct csi_dev *dev, unsigned int i)
-
{
-
struct v4l2_control ctrl;
-
int ret;
-
-
if (i > dev->dev_qty-1) {
-
csi_err("set input error!\n");
-
return -EINVAL;
-
}
-
-
if (i == dev->input)
-
return 0;
-
-
csi_dbg(0,"input_num = %d\n",i);
-
-
if(dev->input != -1) {
-
-
ret = v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_ON);
-
if(ret < 0)
-
goto altend;
-
}
-
-
ret = update_ccm_info(dev, dev->ccm_cfg[i]);
-
if (ret < 0)
-
{
-
csi_err("Error when set ccm info when selecting input!,input_num = %d\n",i);
-
goto recover;
-
}
-
-
-
csi_dbg(0,"dev->ccm_info->vref = %d\n",dev->ccm_info->vref);
-
csi_dbg(0,"dev->ccm_info->href = %d\n",dev->ccm_info->href);
-
csi_dbg(0,"dev->ccm_info->clock = %d\n",dev->ccm_info->clock);
-
csi_dbg(0,"dev->ccm_info->mclk = %d\n",dev->ccm_info->mclk);
-
-
dev->csi_mode.vref = dev->ccm_info->vref;
-
dev->csi_mode.href = dev->ccm_info->href;
-
dev->csi_mode.clock = dev->ccm_info->clock;
-
-
csi_clk_out_set(dev);
-
-
-
ret = v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_OFF);
-
if (ret!=0) {
-
csi_err("sensor standby off error when selecting target device!\n");
-
goto recover;
-
}
-
-
ret = v4l2_subdev_call(dev->sd,core, init, 0);
-
if (ret!=0) {
-
csi_err("sensor initial error when selecting target device!\n");
-
goto recover;
-
}
-
-
-
ctrl.id = V4L2_CID_VFLIP;
-
ctrl.value = dev->vflip;
-
ret = v4l2_subdev_call(dev->sd,core, s_ctrl, &ctrl);
-
if (ret!=0) {
-
csi_err("sensor sensor_s_ctrl V4L2_CID_VFLIP error when vidioc_s_input!input_num = %d\n",i);
-
}
-
-
ctrl.id = V4L2_CID_HFLIP;
-
ctrl.value = dev->hflip;
-
ret = v4l2_subdev_call(dev->sd,core, s_ctrl, &ctrl);
-
if (ret!=0) {
-
csi_err("sensor sensor_s_ctrl V4L2_CID_HFLIP error when vidioc_s_input!input_num = %d\n",i);
-
}
-
-
dev->input = i;
-
ret = 0;
-
-
……
果然,驱动中把本应该写在open里函数初始化流程写到了vidioc_s_input函数中,那下面的步骤就简单了,要么就是改驱动,要么就是改MJPG-Streamer,在MJPG-Streamer中只需要加一个ioctl函数即可,所以我们选择修改MJPG-Streamer。
在MJPG-Streamer源码中plugins/input_uvc/v4l2uvc.c第99行,init_v4l2函数中,open函数之后,添加下面代码:
-
-
#if 1
-
-
struct v4l2_input inp;
-
-
inp.index = 0;
-
-
-
if (-1 == ioctl(vd->fd, VIDIOC_S_INPUT, &inp))
-
printf("VIDIOC_S_INPUT error\n");
-
#endif
再次运行,果然没有I2C传输错误的提示了。
阅读(2359) | 评论(0) | 转发(0) |