linux系统V4L2架构OV3640摄像头视频捕获保存图片jpg格式

大家好,我aiku,最近在做一个linux摄像头的应用程序,主要功能是:arm板子210跑linux系统,进行摄像头视频采集,捕获一帧视频然后保存成图片。功能很简单,但是我确搞了很久,过程中遇到了很多问题,在此写下点滴记录以备忘,还有很多问题待决解……

硬件平台:arm cotex-A8(s5pv210开发板) + ov3640 CMOS摄像头

软件平台: (1)开发平台:xp系统上装的virtualbox-2.6.8虚拟机 + ubuntu12.04

(2)arm板子系统:linux 2.6.35 内核 + qtopia文件系统

一、摄像头程序:

1、源代码:camera3640.c

  1. #include "classroom.h"
  2. /*******************************************************************************************************************************************************************/
  3. extern char * chpt_lcd_mmap_addr; //lcd的缓存指针
  4. extern unsigned int int_lcd_width; //lcd的宽度
  5. extern unsigned int int_lcd_height; //lcd的高度
  6. extern unsigned int int_lcd_pixel; //lcd的像素
  7. extern unsigned int cameratimes; //Camera采集的次数控制变量
  8. extern const unsigned int camMaxtime; //Camera最多采集的次数
  9. struct buffer // 每个缓冲帧的数据结构
  10. {
  11. void * start;
  12. size_t length;
  13. }*buffers;
  14. const char * CameraName = "/dev/video0"; //摄像头设备名
  15. int cam_fd = -1; //摄像头打开文件
  16. static int n_buffers = 0;
  17. unsigned int times = 0;
  18. unsigned int bufferLenth = 0;
  19. /*******************************************************************************************************************************************************************/
  20. void cameraOpen(void)
  21. {
  22. cam_fd = open( CameraName, O_RDWR | O_NONBLOCK, 0 ); //阻塞方式打开摄像头
  23. if(cam_fd < 0)
  24. {
  25. printf("Open fimc0 error.\n");
  26. exit(1);
  27. }
  28. }
  29. void cameraInit(void)
  30. {
  31. struct v4l2_capability cap;
  32. int ret = 0;
  33. unsigned int min;
  34. struct v4l2_input input;
  35. ret = ioctl( cam_fd, VIDIOC_QUERYCAP, &cap );
  36. if( ret < 0 )
  37. {
  38. printf("set VIDIOC_QUERYCAP error.\n");
  39. exit(1);
  40. }
  41. if( !(cap.capabilities & V4L2_CAP_STREAMING) )
  42. {
  43. printf("%s can not streaming.\n");
  44. exit(1);
  45. }
  46. input.index = 0;
  47. if ((ioctl(cam_fd, VIDIOC_S_INPUT, &input)) < 0) //单输入模式
  48. {
  49. printf("set s_input error.\n");
  50. exit(1);
  51. }
  52. //设置视频的格式
  53. struct v4l2_format fmt;
  54. CLEAR(fmt);
  55. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //数据流类型,永远是:V4L2_BUF_TYPE_VIDEO_CAPTURE
  56. fmt.fmt.pix.width = int_lcd_width; //800 宽,必须是16的倍数
  57. fmt.fmt.pix.height = int_lcd_height; //400 高,必须是16的倍数
  58. fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; //视频数据存储类型,例如是YUV4:2:2还是RGB
  59. fmt.fmt.pix.field = V4L2_FIELD_ANY;
  60. if ( ioctl(cam_fd, VIDIOC_S_FMT, &fmt) == -1 )
  61. {
  62. printf("set format error\n");
  63. }
  64. //如果该视频设备驱动不支持你所设定的图像格式,视频驱动会重新修改struct v4l2_format结构体变量的值为该视频设备所支持的图像格式,
  65. //所以在程序设计中,设定完所有的视频格式后,要获取实际的视频格式,要重新读取struct v4l2_format结构体变量。
  66. if(ioctl(cam_fd, VIDIOC_G_FMT, &fmt) == -1)
  67. {
  68. printf("Unable to get format\n");
  69. exit(1);
  70. }
  71. {
  72. printf("fmt.type:\t\t%d\n",fmt.type);
  73. printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
  74. (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
  75. printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
  76. printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
  77. printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
  78. }
  79. //printf("real format is %d X %d,pixel is %d!\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.pixelformat);
  80. if( int_lcd_width != fmt.fmt.pix.width )
  81. {
  82. int_lcd_width = fmt.fmt.pix.width;
  83. fprintf(stderr,"Image width set to %i by device %s.\n", int_lcd_width, CameraName );
  84. }
  85. if( int_lcd_height != fmt.fmt.pix.height )
  86. {
  87. int_lcd_height = fmt.fmt.pix.height;
  88. fprintf(stderr, "Image height set to %i by device %s.\n", int_lcd_height, CameraName );
  89. }
  90. min = fmt.fmt.pix.width * 2;
  91. if( fmt.fmt.pix.bytesperline < min )
  92. {
  93. fmt.fmt.pix.bytesperline = min;
  94. }
  95. min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
  96. if( fmt.fmt.pix.sizeimage < min )
  97. {
  98. fmt.fmt.pix.sizeimage = min;
  99. }
  100. mmapInit();
  101. }
  102. void mmapInit(void) //内存映射
  103. {
  104. struct v4l2_requestbuffers req;
  105. CLEAR (req);
  106. req.count = 4;
  107. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  108. req.memory = V4L2_MEMORY_MMAP;
  109. if (-1 == xioctl(cam_fd, VIDIOC_REQBUFS, &req))//申请缓存,count是申请的数量
  110. {
  111. if (EINVAL == errno)
  112. {
  113. fprintf(stderr, "%s does not support memory mapping\n", CameraName);
  114. exit(EXIT_FAILURE);
  115. }
  116. else
  117. {
  118. errno_exit("VIDIOC_REQBUFS");
  119. }
  120. }
  121. if (req.count < 2)
  122. {
  123. fprintf(stderr, "Insufficient buffer memory on %s\n", CameraName);
  124. exit(EXIT_FAILURE);
  125. }
  126. buffers = (struct buffer*)calloc(req.count, sizeof(*buffers));//内存中建立对应的空间
  127. if (!buffers)
  128. {
  129. fprintf(stderr, "Out of memory\n");
  130. exit(EXIT_FAILURE);
  131. }
  132. for (n_buffers = 0; n_buffers < req.count; ++n_buffers)//申请4帧缓存
  133. {
  134. struct v4l2_buffer buf; //驱动中的一帧
  135. CLEAR (buf);
  136. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  137. buf.memory = V4L2_MEMORY_MMAP;
  138. buf.index = n_buffers;
  139. if (-1 == xioctl(cam_fd, VIDIOC_QUERYBUF, &buf))//映射用户空间
  140. {
  141. errno_exit("VIDIOC_QUERYBUF");
  142. }
  143. buffers[n_buffers].length = buf.length;
  144. //通过mmap()建立映射关系
  145. buffers[n_buffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, cam_fd, buf.m.offset);
  146. if (MAP_FAILED == buffers[n_buffers].start)
  147. {
  148. errno_exit("mmap");
  149. }
  150. }
  151. }
  152. void captureStart(void)
  153. {
  154. unsigned int i;
  155. enum v4l2_buf_type type;
  156. for ( i = 0; i < n_buffers; ++i )
  157. {
  158. struct v4l2_buffer buf;
  159. CLEAR (buf);
  160. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  161. buf.memory = V4L2_MEMORY_MMAP;
  162. buf.index = i;
  163. if (-1 == xioctl(cam_fd, VIDIOC_QBUF, &buf))//申请到的缓存进入队列
  164. {
  165. errno_exit("VIDIOC_QBUF");
  166. }
  167. }
  168. type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  169. if ( -1 == xioctl(cam_fd, VIDIOC_STREAMON, &type) )//开始捕获图像数据
  170. {
  171. errno_exit("VIDIOC_STREAMON");
  172. }
  173. }
  174. void mainLoop(void)
  175. {
  176. unsigned int count;
  177. count = 1;
  178. while (count-- > 0)
  179. {
  180. for ( ; ; )
  181. {
  182. fd_set fds;
  183. struct timeval tv;
  184. int r;
  185. FD_ZERO(&fds); //将指定的文件描述符集清空
  186. FD_SET(cam_fd, &fds); //在文件描述符集中增加一个新的文件描述符
  187. tv.tv_sec = 2;
  188. tv.tv_usec = 0;
  189. r = select(cam_fd + 1, &fds, NULL, NULL, &tv); //判断是否可读(即摄像头是否准备好),tv是等待的时间
  190. if (-1 == r)
  191. {
  192. if (EINTR == errno)
  193. continue;
  194. errno_exit("select");
  195. }
  196. if (0 == r)
  197. {
  198. fprintf (stderr, "select timeout\n");
  199. exit(EXIT_FAILURE);
  200. }
  201. if ( frameRead() )//如果可读则执行frameRead()并跳出循环
  202. {
  203. break;
  204. }
  205. }
  206. }
  207. }
  208. unsigned char* frameRead(void)
  209. {
  210. struct v4l2_buffer buf;
  211. CLEAR (buf);
  212. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  213. buf.memory = V4L2_MEMORY_MMAP;
  214. if (-1 == xioctl(cam_fd, VIDIOC_DQBUF, &buf)) //列出采集的帧缓存
  215. {
  216. switch (errno)
  217. {
  218. case EAGAIN:
  219. return 0;
  220. case EIO:
  221. default:
  222. errno_exit("VIDIOC_DQBUF");
  223. }
  224. }
  225. assert (buf.index < n_buffers);
  226. imageProcess(buffers[buf.index].start); //拷贝视频到lcd
  227. bufferLenth = buffers[buf.index].length;
  228. if (-1 == xioctl(cam_fd, VIDIOC_QBUF, &buf))//再将其入列
  229. {
  230. errno_exit("VIDIOC_QBUF");
  231. }
  232. return (unsigned char*)buffers[buf.index].start;
  233. }
  234. void imageProcess(const void* p)
  235. {
  236. unsigned char* src = (unsigned char*)p;//摄像头采集的图像数据
  237. cameratimes++;
  238. printf("\n--------- this is %d times.\n",cameratimes);
  239. printf("========= bufferLenth = %d.\n",bufferLenth);
  240. printf("+++++++++ string length = %d.\n",strlen(src));
  241. if(cameratimes >= camMaxtime)
  242. {
  243. //productBmp(src); //生成bmp图片
  244. jpgImageProduct(src); //生成jpg图片
  245. memcpy( chpt_lcd_mmap_addr, src, int_lcd_width*int_lcd_height*4 ); //在LCD液晶屏显示
  246. }
  247. }
  248. void errno_exit(const char * s)
  249. {
  250. fprintf( stderr, "%s error %d, %s\n", s, errno, strerror(errno) );
  251. exit(EXIT_FAILURE);
  252. }
  253. int xioctl( int ffd, int request, void * argp)
  254. {
  255. int r;
  256. do
  257. {
  258. r = ioctl( ffd, request, argp );
  259. }
  260. while( r == -1 && EINTR == errno );
  261. return r;
  262. }  

2、执行流程:

(1)打开设备:cameraOpen()

(2)设备初始化:cameraInit()

(3)建立内存映射:mmapInit()

(4)开始视频采集并捕获图像数据:captureStart()

(5)循环采集:mainLoop()

(6)读取数据:frameRead()

(7)数据处理:imageProcess()

3、常见错误及解决方法:

(1) No capture device info

VIDIOC_REQBUFS error 19, No such device

这个错误纠结了很久很久,在网上搜了很久,在群里问了很多人,都没解决,最后求助出售此试验箱设备公司的工程师,他也没给出原因,只是给了我一个可以运行的例程,我就自己对照了下,然后调试,查出来,原因在与,没有设置

二、保存jpg图片程序:

1、源代码:jpg.c

[cpp] view plain copy print ?
  1. /*
  2. * jpg.c
  3. *
  4. */
  5. #include "classroom.h"
  6. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  7. #define jpgImageName "test.jpg"
  8. extern char * chpt_lcd_mmap_addr;
  9. extern unsigned int int_lcd_height;
  10. extern unsigned int int_lcd_width;
  11. extern unsigned int int_lcd_pixel;
  12. static unsigned int width;
  13. static unsigned int height;
  14. static unsigned int channel;
  15. static unsigned char jpegQuality = 100;
  16. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  17. static void jpegWrite(unsigned char* img)
  18. {
  19. struct jpeg_compress_struct cinfo;
  20. struct jpeg_error_mgr jerr;
  21. JSAMPROW row_pointer[1];
  22. FILE *outfile = fopen( jpgImageName, "w");
  23. // try to open file for saving
  24. if (!outfile) {
  25. errno_exit("jpeg");
  26. }
  27. // create jpeg data
  28. cinfo.err = jpeg_std_error( &jerr );
  29. jpeg_create_compress(&cinfo);
  30. jpeg_stdio_dest(&cinfo, outfile);
  31. // set image parameters
  32. cinfo.image_width = width;
  33. cinfo.image_height = height;
  34. cinfo.input_components = 3;
  35. cinfo.in_color_space = JCS_RGB;
  36. // set jpeg compression parameters to default
  37. jpeg_set_defaults(&cinfo);
  38. // and then adjust quality setting
  39. jpeg_set_quality(&cinfo, jpegQuality, TRUE);
  40. // start compress
  41. jpeg_start_compress(&cinfo, TRUE);
  42. // feed data
  43. while (cinfo.next_scanline < cinfo.image_height)
  44. {
  45. row_pointer[0] = &img[(cinfo.image_height - cinfo.next_scanline - 1) * cinfo.image_width*3];
  46. jpeg_write_scanlines(&cinfo, row_pointer, 1);
  47. }
  48. // finish compression
  49. jpeg_finish_compress(&cinfo);
  50. // destroy jpeg data
  51. jpeg_destroy_compress(&cinfo);
  52. // close output file
  53. fclose(outfile);
  54. }
  55. void jpgImageProduct(const void* p)
  56. {
  57. usleep(500000);
  58. unsigned char* dst;
  59. unsigned char* src = (unsigned char*)p;
  60. unsigned int j;
  61. unsigned int i;
  62. width = int_lcd_width;
  63. height = int_lcd_height;
  64. dst = (unsigned char*)malloc (width*height*3+66);
  65. for (i=0; i< height; i++)
  66. {
  67. for(j=0;j
  68. {
  69. memcpy(dst+(i*width+j)*3, src+(i*width+j)*4+2,1);
  70. memcpy(dst+(i*width+j)*3+1, src+(i*width+j)*4+1,1);
  71. memcpy(dst+(i*width+j)*3+2, src+(i*width+j)*4,1);
  72. }
  73. }
  74. jpegWrite(dst);
  75. free(dst);
  76. }
/*
 *     jpg.c
 *
 */

#include "classroom.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define jpgImageName "test.jpg"

extern char * chpt_lcd_mmap_addr;
extern unsigned int int_lcd_height;
extern unsigned int int_lcd_width;
extern unsigned int int_lcd_pixel;

static unsigned int width;
static unsigned int height;
static unsigned int channel;

static unsigned char jpegQuality = 100;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void jpegWrite(unsigned char* img)
{
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
	
  JSAMPROW row_pointer[1];
  FILE *outfile = fopen( jpgImageName, "w");

  // try to open file for saving
  if (!outfile) {
    errno_exit("jpeg");
  }

  // create jpeg data
  cinfo.err = jpeg_std_error( &jerr );
  jpeg_create_compress(&cinfo);
  jpeg_stdio_dest(&cinfo, outfile);

  // set image parameters
  cinfo.image_width = width;	
  cinfo.image_height = height;
  cinfo.input_components = 3;
  cinfo.in_color_space = JCS_RGB;

  // set jpeg compression parameters to default
  jpeg_set_defaults(&cinfo);
  // and then adjust quality setting
  jpeg_set_quality(&cinfo, jpegQuality, TRUE);

  // start compress 
  jpeg_start_compress(&cinfo, TRUE);

  // feed data
  while (cinfo.next_scanline < cinfo.image_height) 
  {
	row_pointer[0] = &img[(cinfo.image_height - cinfo.next_scanline - 1) * cinfo.image_width*3];
    jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  // finish compression
  jpeg_finish_compress(&cinfo);

  // destroy jpeg data
  jpeg_destroy_compress(&cinfo);

  // close output file
  fclose(outfile);
}


void jpgImageProduct(const void* p)
{
	usleep(500000);

	unsigned char* dst;
	unsigned char* src = (unsigned char*)p;
	unsigned int j;
	unsigned int i;

	width = int_lcd_width;
    height = int_lcd_height;

	dst = (unsigned char*)malloc (width*height*3+66);

	for (i=0; i< height; i++)
	{
		for(j=0;j


你可能感兴趣的:(多媒体开发)