FSPAD_702 MJPG-Streamer视频流服务器移植心得

        最近要在开源的平板上做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 /* start anywhere */ ,
                      //vd->buf.length, PROT_READ, MAP_SHARED, vd->fd,
			/* FSPAD_702 Linux added by LeeSheen */
                      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 /* start anywhere */ ,
			/* 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;

	//printk("addr = %d, len = %d, buf = %d, %d, %d\n", msg.addr, msg.len, msg.buf[0], msg.buf[1], msg.buf[2]);
	
	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) {
		/*Power down current device*/
		ret = v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_ON);
		if(ret < 0)
			goto altend;
	}
	/* Alternate the device info and select target device*/
  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;
	}
	
	/* change the csi setting */
	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);
	
	/* Initial target device */
	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;
	}
	
	/* Set the initial flip */
	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函数之后,添加下面代码:

/* FSPAD_702 Linux added by LeeSheen */
#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传输错误的提示了。

FSPAD_702 MJPG-Streamer视频流服务器移植心得_第1张图片


        测试一下,显示正常,说明MJPG-Streamer移植就成功了。

FSPAD_702 MJPG-Streamer视频流服务器移植心得_第2张图片

        下一篇文章会介绍MJPG-Streamer的详细移植过程。

你可能感兴趣的:(mjpg-streamer,FSPAD,视频流服务器,gc0308,sun5i_csi)