Mangopi MQ-R:T113-s3(五)Tina系统USB摄像头推流--V4l2采集摄像头数据显示在RGB屏幕

USB摄像头推流

参考:https://bbs.aw-ol.com/topic/1158/r818-tina-linux-%E8%BF%90%E8%A1%8C-mjpg-streamer-usb-camera?lang=zh-CN

Tina文件系统配置:make menuconfig
在这里插入图片描述
Linux Kernel配置:make kernel_menuconfig

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

拷贝动态链接库到开发板

需要拷贝几个动态链接库到开发板的/usr/lib/

  • ./out/t113-mq_r/compile_dir/target/rootfs-tmp/usr/lib/input_uvc.so
  • ./out/t113-mq_r/compile_dir/target/rootfs-tmp/usr/lib/output_http.so
  • ./out/t113-mq_r/compile_dir/target/rootfs-tmp/usr/lib/libjpeg.so.9

这里使用adb的方式拷贝到开发板

liefyuan@ubuntu:~/Liefyuan/Tina-Linux/Tina-Linux/out/t113-mq_r/compile_dir/target/rootfs-tmp/usr/lib$ adb push output_http.so /lib/
5609 KB/s (34120 bytes in 0.005s)
liefyuan@ubuntu:~/Liefyuan/Tina-Linux/Tina-Linux/out/t113-mq_r/compile_dir/target/rootfs-tmp/usr/lib$ adb push input_uvc.so /lib/
5273 KB/s (35408 bytes in 0.006s)
liefyuan@ubuntu:~/Liefyuan/Tina-Linux/Tina-Linux/out/t113-mq_r/compile_dir/target/rootfs-tmp/usr/lib$ adb push libjpeg.so.9 /usr/lib/

WiFi联网

  • insmod /lib/modules/5.4.61/8723ds.ko
  • ifconfig wlan0 up
  • mkdir -p /var/run/wpa_supplicant
  • wpa_supplicant -B -c /etc/wpa_supplicant.conf -i wlan0
  • udhcpc -i wlan0

wpa_supplicant 文件:

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=0
ap_scan=1
network={
    
    
    ssid="KKKK"
    scan_ssid=1
    key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
    pairwise=TKIP CCMP
    group=CCMP TKIP WEP104 WEP40
    psk="99999999"
    priority=5
}

推流:

mjpg_streamer -i "/usr/lib/input_uvc.so -r 1280x720 -d /dev/vi
deo1"  -o "/usr/lib/output_http.so -w /www/webcam"

实际效果:

root@TinaLinux:/# mjpg_streamer -i "/usr/lib/input_uvc.so -r 1280x720 -d /dev/vi
deo0"  -o "/usr/lib/output_http.so -w /www/webcam"
MJPG-streamer [242]: starting application
MJPG Streamer Version: svn rev: Unversioned directory
MJPG-streamer [242]: MJPG Streamer Version: svn rev: Unversioned directory
 i: Using V4L2 device.: /dev/video0
MJPG-streamer [242]: Using V4L2 device.: /dev/video0
 i: Desired Resolution: 1280 x 720
MJPG-streamer [242]: Desired Resolution: 1280 x 720
 i: Frames Per Second.: not limited
MJPG-streamer [242]: Frames Per Second.: not limited
 i: Format............: JPEG
MJPG-streamer [242]: Format............: JPEG
 i: TV-Norm...........: DEFAULT
MJPG-streamer [242]: TV-Norm...........: DEFAULT
[  630.855036] uvcvideo: Failed to query (GET_DEF) UVC control 12 on unit 2: -32 (exp. 4).
 o: www-folder-path...: /www/webcam/
MJPG-streamer [242]: www-folder-path...: /www/webcam/
 o: HTTP TCP port.....: 8080
MJPG-streamer [242]: HTTP TCP port.....: 8080
 o: username:password.: disabled
MJPG-streamer [242]: username:password.: disabled
 o: commands..........: enabled
MJPG-streamer [242]: commands..........: enabled
MJPG-streamer [242]: starting input plugin /usr/lib/input_uvc.so
MJPG-streamer [242]: starting output plugin: /usr/lib/output_http.so (ID: 00)
MJPG-streamer [242]: serving client: 192.168.1.102
MJPG-streamer [242]: serving client: 192.168.1.102
MJPG-streamer [242]: serving client: 192.168.1.102
[  654.665144] NOHZ: local_softirq_pending 08
[  654.670175] NOHZ: local_softirq_pending 08
[  654.674802] NOHZ: local_softirq_pending 08
[  654.679764] NOHZ: local_softirq_pending 08
[  654.684552] NOHZ: local_softirq_pending 08
[  655.264838] NOHZ: local_softirq_pending 08
[  655.626925] NOHZ: local_softirq_pending 08
[  655.631554] NOHZ: local_softirq_pending 08
[  655.647364] NOHZ: local_softirq_pending 08
[  655.653526] NOHZ: local_softirq_pending 08


浏览器看推流内容

然后在浏览器地址栏输入:http://192.168.1.105:8080/就可以看到视频了!

运行了一个小时很稳定、清晰,芯片也很热,烫手!
在这里插入图片描述

推流的时候看看top命令:

Mem: 83516K used, 29136K free, 0K shrd, 916K buff, 6968K cached
CPU:  29% usr  68% sys   0% nic   0% idle   0% io   0% irq   2% sirq
Load average: 2.80 2.60 1.62 4/69 257
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
  145     1 root     S     1312   1%  26% /bin/adbd -D
  206     2 root     RW       0   0%  26% [RTWHALXT]
  252   159 root     S    23520  21%  23% mjpg_streamer -i /usr/lib/input_uvc.so
  203     2 root     RW       0   0%  19% [RTW_XMIT_THREAD]
  246     2 root     IW<      0   0%   1% [kworker/u5:1-uv]
  201     2 root     SW       0   0%   1% [ksdioirqd/mmc1]
  204     2 root     SW       0   0%   1% [RTW_RECV_THREAD]
  148     1 root     S      696   1%   1% /sbin/swupdate-progress -w
   10     2 root     IW       0   0%   0% [rcu_preempt]
    9     2 root     SW       0   0%   0% [ksoftirqd/0]
   15     2 root     SW       0   0%   0% [ksoftirqd/1]
  216     1 root     S     2440   2%   0% wpa_supplicant -B -c /etc/wpa_supplica
  159     1 root     S     1052   1%   0% -/bin/sh
    1     0 root     S     1048   1%   0% /sbin/init
  257   159 root     R     1048   1%   0% top
  226     1 root     S     1048   1%   0% udhcpc -i wlan0
  205     2 root     SW       0   0%   0% [RTW_CMD_THREAD]
   21     2 root     IW       0   0%   0% [kworker/0:1-eve]
   55     2 root     SW       0   0%   0% [spi0]
    7     2 root     IW       0   0%   0% [kworker/u4:0-ev]

进程信息:

  • PID:进程的ID
  • PPID:父进程ID
  • USER:进程所有者
  • STAT:也就是当前进程的状态,其中S-睡眠,s-表示该进程是会话的先导进程,N-表示进程拥有比普通优先级更低的优先级,R-正在运行,D-短期等待,Z-僵死进程,T-被跟踪或者被停止等等
  • VSZ:进程的虚拟内存占用大小(单位:kb(killobytes))
  • %VSZ:进程的虚拟内存占用率
  • %CPU:进程占用CPU的使用率
  • COMMOND–命令的名称和参数

显示在屏幕上面(测试OK)

因为我的摄像头不支持RGB模式,所以就设置成YUV格式的输出模式,然后再将YUV格式转成RGB格式的视频流显示。
我的屏幕是32bpp(ARGB),所以一个像素是需要四个字节来描述的。
转码后的视频流是连续的。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <math.h>
#include <wchar.h>
#include <time.h>
#include <stdbool.h>

#define CAM_WIDTH 640
#define CAM_HEIGHT 480

static char *dev_video;
static char *dev_fb0;

static char *yuv_buffer;
static char *rgb_buffer;
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
#define YUVToRGB(Y)                                                            \
	((u16)((((u8)(Y) >> 3) << 11) | (((u8)(Y) >> 2) << 5) | ((u8)(Y) >> 3)))
struct v4l2_buffer video_buffer;
/*全局变量*/
int lcd_fd;
int video_fd;
static unsigned char *lcd_mem_p = NULL; //保存LCD屏映射到进程空间的首地址
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;

char *video_buff_buff[4]; /*保存摄像头缓冲区的地址*/
int video_height = 0;
int video_width = 0;
unsigned char *lcd_display_buff; //LCD显存空间
unsigned char *lcd_display_buff2; //LCD显存空间
static void errno_exit(const char *s)
{
    
    
	fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
	exit(EXIT_FAILURE);
}

static int xioctl(int fh, int request, void *arg)
{
    
    
	int r;

	do {
    
    
		r = ioctl(fh, request, arg);
	} while (-1 == r && EINTR == errno);

	return r;
}

static int video_init(void)
{
    
    
	struct v4l2_capability cap;
	ioctl(video_fd, VIDIOC_QUERYCAP, &cap);

	struct v4l2_fmtdesc dis_fmtdesc;
	dis_fmtdesc.index = 0;
	dis_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("-----------------------支持格式---------------------\n");
	while (ioctl(video_fd, VIDIOC_ENUM_FMT, &dis_fmtdesc) != -1) {
    
    
		printf("\t%d.%s\n", dis_fmtdesc.index + 1,
		       dis_fmtdesc.description);
		dis_fmtdesc.index++;
	}
	struct v4l2_format video_format;
	video_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	video_format.fmt.pix.width = CAM_WIDTH;
	video_format.fmt.pix.height = CAM_HEIGHT;
	video_format.fmt.pix.pixelformat =
		V4L2_PIX_FMT_YUYV; //使用JPEG格式帧,用于静态图像采集

	ioctl(video_fd, VIDIOC_S_FMT, &video_format);

	printf("当前摄像头支持的分辨率:%dx%d\n", video_format.fmt.pix.width,
	       video_format.fmt.pix.height);
	if (video_format.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
    
    
		printf("当前摄像头不支持YUYV格式输出.\n");
		video_height = video_format.fmt.pix.height;
		video_width = video_format.fmt.pix.width;
		//return -3;
	} else {
    
    
		video_height = video_format.fmt.pix.height;
		video_width = video_format.fmt.pix.width;
		printf("当前摄像头支持YUYV格式输出.width %d height %d\n",
		       video_height, video_height);
	}
	/*3. 申请缓冲区*/
	struct v4l2_requestbuffers video_requestbuffers;
	memset(&video_requestbuffers, 0, sizeof(struct v4l2_requestbuffers));
	video_requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	video_requestbuffers.count = 4;
	video_requestbuffers.memory = V4L2_MEMORY_MMAP;
	if (ioctl(video_fd, VIDIOC_REQBUFS, &video_requestbuffers))
		return -4;
	printf("成功申请的缓冲区数量:%d\n", video_requestbuffers.count);
	/*4. 得到每个缓冲区的地址: 将申请的缓冲区映射到进程空间*/
	struct v4l2_buffer video_buffer;
	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
	int i;
	for (i = 0; i < video_requestbuffers.count; i++) {
    
    
		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		video_buffer.index = i;
		video_buffer.memory = V4L2_MEMORY_MMAP;
		if (ioctl(video_fd, VIDIOC_QUERYBUF, &video_buffer))
			return -5;
		/*映射缓冲区的地址到进程空间*/
		video_buff_buff[i] =
			mmap(NULL, video_buffer.length, PROT_READ | PROT_WRITE,
			     MAP_SHARED, video_fd, video_buffer.m.offset);
		printf("第%d个缓冲区地址:%#X\n", i, video_buff_buff[i]);
	}
	/*5. 将缓冲区放入到采集队列*/
	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
	for (i = 0; i < video_requestbuffers.count; i++) {
    
    
		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		video_buffer.index = i;
		video_buffer.memory = V4L2_MEMORY_MMAP;
		if (ioctl(video_fd, VIDIOC_QBUF, &video_buffer)) {
    
    
			printf("VIDIOC_QBUF error\n");
			return -6;
		}
	}
	printf("启动摄像头采集\n");
	/*6. 启动摄像头采集*/
	int opt_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (ioctl(video_fd, VIDIOC_STREAMON, &opt_type)) {
    
    
		printf("VIDIOC_STREAMON error\n");
		return -7;
	}

	return 0;
}
int lcd_init(void)
{
    
    
	/*2. 获取可变参数*/
	if (ioctl(lcd_fd, FBIOGET_VSCREENINFO, &vinfo))
		return -2;
	printf("屏幕X:%d   屏幕Y:%d  像素位数:%d\n", vinfo.xres, vinfo.yres,
	       vinfo.bits_per_pixel);
	//分配显存空间,完成图像显示
	lcd_display_buff =
		malloc(vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
	/*3. 获取固定参数*/
	if (ioctl(lcd_fd, FBIOGET_FSCREENINFO, &finfo))
		return -3;
	printf("smem_len=%d Byte,line_length=%d Byte\n", finfo.smem_len,
	       finfo.line_length);

	/*4. 映射LCD屏物理地址到进程空间*/
	lcd_mem_p = (unsigned char *)mmap(0, finfo.smem_len,
					  PROT_READ | PROT_WRITE, MAP_SHARED,
					  lcd_fd, 0); //从文件的那个地方开始映射
	memset(lcd_mem_p, 0xFFFFFF, finfo.smem_len);
	printf("映射LCD屏物理地址到进程空间\n");
	return 0;
}

static void close_device(void)
{
    
    
	if (-1 == close(video_fd))
		errno_exit("close");
	video_fd = -1;

	if (-1 == close(lcd_fd))
		errno_exit("close");
	lcd_fd = -1;
}

static void open_device(void)
{
    
    
	video_fd = open(dev_video, O_RDWR /* required */ | O_NONBLOCK, 0);
	if (-1 == video_fd) {
    
    
		fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_video, errno,
			strerror(errno));
		exit(EXIT_FAILURE);
	}

	lcd_fd = open(dev_fb0, O_RDWR, 0);
	if (-1 == lcd_fd) {
    
    
		fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_fb0, errno,
			strerror(errno));
		exit(EXIT_FAILURE);
	}
}
/*
将YUV格式数据转为RGB
*/
void yuv_to_rgb(unsigned char *yuv_buffer, unsigned char *rgb_buffer,
		int iWidth, int iHeight)
{
    
    
	int x;
	int z = 0;
	unsigned char *ptr = rgb_buffer;
	unsigned char *yuyv = yuv_buffer;

	for (x = 0; x < iWidth * iHeight; x++) {
    
    
		int r, g, b;
		int y, u, v;
		if (!z)
			y = yuyv[0] << 8;
		else
			y = yuyv[2] << 8;
		u = yuyv[1] - 128;
		v = yuyv[3] - 128;
		r = (y + (359 * v)) >> 8;
		g = (y - (88 * u) - (183 * v)) >> 8;
		b = (y + (454 * u)) >> 8;
		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b); // b color
		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g); // g color
		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r); // r color
		*(ptr++) = 0xff;                                // a color
		if (z++) {
    
    
			z = 0;
			yuyv += 4;
		}
	}
}

void rgb24_to_rgb565(char *rgb24, char *rgb16)
{
    
    
	int i = 0, j = 0;
	for (i = 0; i < 240 * 240 * 3; i += 3) {
    
    
		rgb16[j] = rgb24[i] >> 3; // B
		rgb16[j] |= ((rgb24[i + 1] & 0x1C) << 3); // G
		rgb16[j + 1] = rgb24[i + 2] & 0xF8; // R
		rgb16[j + 1] |= (rgb24[i + 1] >> 5); // G

		j += 2;
	}
}

static void lcd_image(unsigned int start_x, unsigned int end_x,
                    unsigned int start_y, unsigned int end_y,
                    unsigned char* color)
{
    
    
    unsigned long i;
    unsigned int j;
    /* 填充颜色 */
    i = start_y * vinfo.xres; //定位到起点行首

    for ( ; start_y <= end_y; start_y++, i+=(vinfo.xres*4))
    {
    
    
        for (j = start_x; j <= end_x*4; j++)
        {
    
    
            lcd_mem_p[i + j] = *color++;
        }
		*color--;
    }
}

int main(int argc, char **argv)
{
    
    
	dev_video = "/dev/video0";
	dev_fb0 = "/dev/fb0";
	open_device();
	video_init();
	lcd_init();
	/*3. 读取摄像头的数据*/
	struct pollfd video_fds;
	video_fds.events = POLLIN;
	video_fds.fd = video_fd;

	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
	rgb_buffer = malloc(CAM_WIDTH * CAM_HEIGHT * 4);
	yuv_buffer = malloc(CAM_WIDTH * CAM_HEIGHT * 4);
	unsigned char *rgb_p;
	int w, h, i, j;
	unsigned char r, g, b;
	unsigned int c;
	while (1) {
    
    
		/*等待摄像头采集数据*/
		poll(&video_fds, 1, -1);
		/*得到缓冲区的编号*/
		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		video_buffer.memory = V4L2_MEMORY_MMAP;
		ioctl(video_fd, VIDIOC_DQBUF, &video_buffer);
		//printf("当前采集OK的缓冲区编号:%d,地址:%#X num:%d\n",
		       //video_buffer.index, video_buff_buff[video_buffer.index],
		       //strlen(video_buff_buff[video_buffer.index]));

		/*对缓冲区数据进行处理*/
		yuv_to_rgb(video_buff_buff[video_buffer.index], yuv_buffer, video_height, video_width);
		//rgb24_to_rgb565(yuv_buffer, rgb_buffer);


		//printf("显示屏进行显示\n");
		//显示屏进行显示: 将显存空间的数据拷贝到LCD屏进行显示
		/*memcpy(lcd_mem_p, yuv_buffer,
		       vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);*/
		// video stream show
		lcd_image(0, 640, 0, 480, yuv_buffer);

		//printf("buffer size: %d\r\n", (vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8));
		/*将缓冲区放入采集队列*/
		ioctl(video_fd, VIDIOC_QBUF, &video_buffer);
		//printf("将缓冲区放入采集队列\n");
	}
	/*4. 关闭视频设备*/
	close(video_fd);

	return 0;
}

使用Tina系统的交叉编译器进行交叉编译

arm-openwrt-linux-gcc main.c -o cam2fb.elf

使用ADB传输到开发板

adb push cam2fb.elf /opt

在这里插入图片描述

看了一下top命令:CPU占了68%,其中这个从摄像头采集视频流显示到屏幕就占了46%的CPU占用率。

Mem: 32268K used, 80384K free, 0K shrd, 520K buff, 7940K cached
CPU:  68% usr  31% sys   0% nic   0% idle   0% io   0% irq   0% sirq
Load average: 2.08 2.03 2.01 3/66 497
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
  146     1 root     S     1312   1%  50% /bin/adbd -D
  495   218 root     R     8504   8%  46% ./cam2fb.elf
   74     2 root     IW<      0   0%   3% [kworker/u5:0-uv]
  148     1 root     S      696   1%   1% /sbin/swupdate-progress -w
  497   218 root     R     1048   1%   0% top
   10     2 root     IW       0   0%   0% [rcu_preempt]
  194     2 root     SW       0   0%   0% [ksdioirqd/mmc1]
  198     2 root     SW       0   0%   0% [RTW_RECV_THREAD]
  206     1 root     S     2440   2%   0% wpa_supplicant -B -c /etc/wpa_supplica
    1     0 root     S     1048   1%   0% /sbin/init
  218     1 root     S     1048   1%   0% -/bin/sh
  217     1 root     S     1048   1%   0% udhcpc -i wlan0
  430     2 root     IW<      0   0%   0% [kworker/u5:1-uv]
  199     2 root     SW       0   0%   0% [RTW_CMD_THREAD]
  238     2 root     IW       0   0%   0% [kworker/0:0-eve]
   66     2 root     SW       0   0%   0% [irq/37-mmc0]
  465     2 root     IW       0   0%   0% [kworker/u4:2-ev]
   21     2 root     IW       0   0%   0% [kworker/1:1-mm_]
    9     2 root     SW       0   0%   0% [ksoftirqd/0]
^C 67     2 root     IW       0   0%   0% [kworker/0:2-eve]

猜你喜欢

转载自blog.csdn.net/qq_28877125/article/details/127824696