[TI TDA4 J721E]USB摄像头获取YUV数据并显示Demo的实现
因为公司使用的TDA4开发板目前没有提供LVDS接口的摄像头,所以我们只能先使用USB摄像头获取YUV数据,用来调试。这里和小伙伴们分享以下如何通过USB摄像头获取YUV的数据。USB摄像头使用的V4L2的协议,Video for Linux 2.0 版本。(USB摄像头要支持YUYV图像输出格式)1、将USB摄像头连接到开发板上,如图:2、登录到开发板,设备会被挂载到/dev/ 下,使用ls -
首先感谢阅读,如果您也对TDA4相关的开发感兴趣,我们这边有个学习交流微信群,可以入群和大家一起交流学习。
资历较浅,水平有限,如遇错误,请大家多指正!
保持开源精神,共同分享、进步!
博主WX : AIR_12 我会拉你入群。
链接:TDA4 相关专栏 链接:TDA4 Demo Gitee开源库
欢迎大家加入,一起维护这个开源库,给更多的朋友提供帮助。
因为公司使用的TDA4开发板目前没有提供LVDS接口的摄像头,所以我们只能先使用USB摄像头获取YUV数据,用来调试。这里和小伙伴们分享以下如何通过USB摄像头获取YUV的数据。
USB摄像头使用的V4L2的协议,Video for Linux 2.0 版本。(USB摄像头要支持YUYV图像输出格式)
可以直接将此工程放入TI SDK 0800版本的vision_apps内进行编译。完成工程下载地址:
https://download.csdn.net/download/AIRKernel/36113518
效果如下图:
1、将USB摄像头连接到开发板上,如图:
2、登录到开发板,设备会被挂载到/dev/ 下,使用ls -l查看一下
开发板默认好像有一个video0,所以我们这里使用video1。
3、 下面是完整代码,需要分为三个文件,大家可以拷贝到两个文件,然后自己试一下。
外部调用一个usb_camera_init、然后运行usb_camera_running,即可获取YUYV图像。
程序内提供了YUYV图像转I420格式;I420格式转NV12格式的格式转换功能,大家可以自行参考。
比较简单,直接上代码了。希望对大家有帮助!
生成的 saved.yuyv 文件为 i420格式,可以使用Ubuntu 的mplayer进行查看。
w和摄像头设置的WIDTH相同;h和摄像头设置的HEIGHT相同。
format可以根据不同的图片格式选择不同的格式。后面的loop 0 表示循环播放。大家自行摸索一下。
mplayer -demuxer rawvideo -rawvideo w=1280:h=720:format=i420 saved.yuyv -loop 0
usb_camera.h | usb_camera.c |
usb_camera_file_save.h | usb_camera_file_save.c |
formatConverter.h | formatConverter.c |
第一个文件,主程序:
头文件:
#ifndef __APP_USB_CAMERA_H__
#define __APP_USB_CAMERA_H__
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#define TRUE 1
#define FALSE 0
#define FILE_VIDEO "/dev/video1"
#define YUV "/home/root/image_yuv.yuyv"
#define YUV420 "/home/root/converter.yuv"
#define IMAGEWIDTH 1280
#define IMAGEHEIGHT 720
extern FILE *fp2, *fp3;
extern char *pmap;
extern char *pmap420;
int init_v4l2(void);
int v4l2_grab(void);
int close_v4l2(void);
int app_usb_camera(void);
int usb_camera_init(void);
void usb_camera_running(void);
#endif
源文件:
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "usb_camera.h"
#include "usb_camera_file_save.h"
#include "formatConverter.h"
static int fd;
static struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt, fmtack;
struct v4l2_streamparm setfps;
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
enum v4l2_buf_type type;
//unsigned char frame_buffer[IMAGEWIDTH * IMAGEHEIGHT * 3];
FORMAT_CONVERTER_DEF format;
char *pmap;
char *pmap420;
void yuvI420ToNV12(char *I420, char *NV12, int width, int height);
//FILE *fp3;
struct buffer
{
void *start;
unsigned int length;
} * buffers;
int init_v4l2(void)
{
//opendev
if ((fd = open(FILE_VIDEO, O_RDWR)) == -1)
{
printf("Error opening V4L interface\n");
return (FALSE);
}
//query cap
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) /* 获取设备支持的操作 */
{
printf("Error opening device %s: unable to query device.\n", FILE_VIDEO);
return (FALSE);
}
else
{
printf("driver:\t\t%s\n", cap.driver);
printf("card:\t\t%s\n", cap.card);
printf("bus_info:\t%s\n", cap.bus_info);
printf("version:\t%d\n", cap.version);
printf("capabilities:\t%x\n", cap.capabilities);
if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
{
printf("Device %s: supports capture.\n", FILE_VIDEO);
}
if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
{
printf("Device %s: supports streaming.\n", FILE_VIDEO);
}
}
//emu all support fmt
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:\n");
while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) // 列举设备所支持的格式
{
printf("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description);
fmtdesc.index++;
}
//set fmt
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.height = IMAGEHEIGHT;
fmt.fmt.pix.width = IMAGEWIDTH;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) /* 设置捕获视频的格式 */
{
printf("Unable to set format\n");
return FALSE;
}
if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1) /* 获取设置支持的视频格式 */
{
printf("Unable to get format\n");
return FALSE;
}
{
printf("fmt.type:\t\t%d\n", fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n", fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF, (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
printf("pix.height:\t\t%d\n", fmt.fmt.pix.height);
printf("pix.width:\t\t%d\n", fmt.fmt.pix.width);
printf("pix.field:\t\t%d\n", fmt.fmt.pix.field);
}
//set fps
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
setfps.parm.capture.timeperframe.numerator = 10;
setfps.parm.capture.timeperframe.denominator = 10;
pmap = malloc(IMAGEWIDTH * IMAGEHEIGHT * 3 / 2);
memset(pmap, 0, IMAGEWIDTH * IMAGEHEIGHT * 3 / 2);
pmap420 = malloc(IMAGEWIDTH * IMAGEHEIGHT * 3 / 2);
memset(pmap420, 0, IMAGEWIDTH * IMAGEHEIGHT * 3 / 2);
printf("init %s \t[OK]\n", FILE_VIDEO);
return TRUE;
}
int v4l2_grab(void)
{
unsigned int n_buffers;
//request for 4 buffers
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) /* 向驱动提出申请内存的请求 */
{
printf("request for buffers error\n");
}
//mmap for buffers
buffers = malloc(req.count * sizeof(*buffers));
if (!buffers)
{
printf("Out of memory\n");
return (FALSE);
}
for (n_buffers = 0; n_buffers < req.count; n_buffers++)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
//query buffers
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) /* 向驱动查询申请到的内存 */
{
printf("query buffer error\n");
return (FALSE);
}
buffers[n_buffers].length = buf.length;
//map
buffers[n_buffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffers[n_buffers].start == MAP_FAILED)
{
printf("buffer map error\n");
return (FALSE);
}
}
//queue
for (n_buffers = 0; n_buffers < req.count; n_buffers++)
{
buf.index = n_buffers;
ioctl(fd, VIDIOC_QBUF, &buf); /* 将空闲的内存加入可捕获视频的队列 */
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type); /* 打开视频流 */
// ioctl(fd, VIDIOC_DQBUF, &buf); /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
printf("grab yuyv OK\n");
return (TRUE);
}
int close_v4l2(void)
{
ioctl(fd, VIDIOC_STREAMOFF, &cap); /* 关闭视频流 */
if (fd != -1)
{
close(fd);
return (TRUE);
}
return (FALSE);
}
int usb_camera_init(void)
{
if (init_v4l2() == FALSE)
{
return FALSE;
}
printf("\nADDInfo: usbCamera init ok!\n");
v4l2_grab();
return TRUE;
}
void usb_camera_delete(void)
{
close_v4l2();
}
void usb_camera_running(void)
{
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_DQBUF, &buf); /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
ioctl(fd, VIDIOC_QBUF, &buf); /* 将空闲的内存加入可捕获视频的队列 */
//设置格式转换相关
format.inbuf = buffers[buf.index].start;
format.outbuf = pmap420;
format.width = IMAGEWIDTH;
format.height = IMAGEHEIGHT;
yuyv2yuv420(&format);
usb_camera_file_save(&format);
yuvI420ToNV12(pmap420,pmap,IMAGEWIDTH,IMAGEHEIGHT);
// FILE *pic_fd = fopen("/opt/vision_apps/test_data/psdkra/tidl_demo_images/0000000501.yuv", "r+");
// if(pic_fd)
// {
// int size_read = fread(pmap, 1, (1024 * 512 * 3) / 2, pic_fd);
// if (size_read!=(1024*512*3)/2)
// return;
// }
// fclose(pic_fd);
// printf("\nADDInfo: usbCamera running ok!\n");
}
void yuvI420ToNV12(char *I420, char *NV12, int width, int height)
{
int ySize = width * height;
int yuvSize = width * height * 3 / 2;
int uIdx = ySize;
int vIdx = ySize * 5 / 4;
memcpy(NV12, I420, ySize);
for (int i = ySize; i < yuvSize; i += 2) {
*(NV12 + i) = *(I420 + uIdx++);
*(NV12 + i + 1) = *(I420 + vIdx++);
}
}
第二个文件:格式转换
头文件:
#ifndef __FORMAT_CONVERTER_H__
#define __FORMAT_CONVERTER_H__
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct FormatConverter
{
char *outbuf;
char *inbuf;
uint32_t height;
uint32_t width;
} FORMAT_CONVERTER_DEF;
void yuyv2yuv420(FORMAT_CONVERTER_DEF *format);
//void yuyv2yuv420(char *inbuf, char *outbuf, uint32_t height, uint32_t width);
#endif
源文件:
#include "formatConverter.h"
void yuyv2yuv420(FORMAT_CONVERTER_DEF *format)
{
char *y = NULL;
char *u = NULL;
char *v = NULL;
int u_c = 0;
int v_c = 0;
y = format->outbuf;
u = y + format->height * format->width;
v = u + format->height * format->width / 4;
bool swith = true;
uint32_t i = 0, j = 0;
for (i = 0; i < (format->height * format->width * 2); i += 2)
{
*y++ = format->inbuf[i];
}
for (i = 0; i < format->height; i += 2)
{
for (j = 1; j < format->width << 1; j += 2)
{
if (swith)
{
*u++ = ((uint8_t *)(format->inbuf + (i * (format->width << 1))))[j];
swith = false;
u_c++;
}
else
{
*v++ = ((uint8_t *)(format->inbuf + (i * (format->width << 1))))[j];
swith = true;
v_c++;
}
}
}
}
第三个文件:图像保存
头文件:
#ifndef __USB_CAMERA_FILE_SAVE_H__
#define __USB_CAMERA_FILE_SAVE_H__
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <stdbool.h>
#define DES_TO_SAVE "/home/root/"
#define TO_SAVE_NAME "saved.yuyv"
#include "formatConverter.h"
int usb_camera_file_save(FORMAT_CONVERTER_DEF *image);
#endif
源文件
#include "usb_camera_file_save.h"
FILE *usb_fd ;
int usb_camera_file_save(FORMAT_CONVERTER_DEF *image)
{
int counter = 0;
char *file_name;
file_name=malloc(256);
memset(file_name,0,256);
sprintf(file_name,"%s%s",DES_TO_SAVE,TO_SAVE_NAME);
usb_fd = fopen(file_name, "w+");
if ( usb_fd == NULL) //打开二进制代码,允许读写(创建)
{
printf("Error opening %s\n",file_name);
free(file_name);
return (-1);
}
counter = fwrite(image->outbuf, 1, image->height * image->width * 3 / 2, usb_fd);
if(counter!=image->height * image->width * 3 / 2)
{
printf("Error writing %s\n",file_name);
fclose(usb_fd);
free(file_name);
return -1;
}
fclose(usb_fd);
free(file_name);
return 0;
}
【声明】
【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】
【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】
【如有嵌入式相关项目需求,欢迎私信】
更多推荐
所有评论(0)