V4L2采集YUYV数据—X264编码H264视频实例

 

  前几天在网上买个罗技的C270摄像头,它支持YUYV(YUV422)和JPEG数据输出。它规格书上写的是支持HD720P(1280*720像素),在实际的调试过程中,我使用该分辨率会导致数据采集过慢。这里需要注意一下,罗技的摄像头C270在有些虚拟机上使用是有异常的,有些是不能映射到虚拟机上,有些是映射过去操作非常缓慢。因为之前在自己的开发板上调试过YUV420的摄像头,在此基础上改为YUYV数据格式,调试的时候还是遇到不少的问题。

x264库的编译可以见之前博客:V4L2视频采集与H264编码3—X264移植_查看开发板是否有移植codec h264-CSDN博客

在PC上编译X264,可以直接执行下面三条命令:

./configure --enable-shared
make
make install

下面贴出x264部分的代码:

/*=============================================================================
#     FileName: h264encoder.c
#         Desc: this program aim to get image from USB camera,
#               used the V4L2 interface.
#       Author: licaibiao
#      Version: 
#   LastChange: 2017-02-21 
=============================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./include/h264encoder.h"

int WIDTH = 640;
int HEIGHT = 480;

void compress_begin(Encoder *en, int width, int height) {
	en->param = (x264_param_t *) malloc(sizeof(x264_param_t));
	en->picture = (x264_picture_t *) malloc(sizeof(x264_picture_t));
	x264_param_default(en->param); //set default param
	//en->param->rc.i_rc_method = X264_RC_CQP;
	// en->param->i_log_level = X264_LOG_NONE;

	en->param->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;
	en->param->i_width = width; //set frame width
	en->param->i_height = height; //set frame height
	WIDTH = width;
	HEIGHT = height;

	//en->param->i_frame_total = 0;

	//en->param->i_keyint_max = 10;
	en->param->rc.i_lookahead = 0; 
	//en->param->i_bframe = 5; 

	//en->param->b_open_gop = 0;
	//en->param->i_bframe_pyramid = 0;
	//en->param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;

	//en->param->rc.i_bitrate = 1024 * 10;//rate 10 kbps
	en->param->i_fps_num = 30; 
	en->param->i_fps_den = 1;
	en->param->i_csp = X264_CSP_I422;

	x264_param_apply_profile(en->param, x264_profile_names[4]); 

	if ((en->handle = x264_encoder_open(en->param)) == 0) {
		return;
	}
	/* Create a new pic */
	x264_picture_alloc(en->picture, X264_CSP_I422, en->param->i_width,
			en->param->i_height);
}

int compress_frame(Encoder *en, int type, uint8_t *in, uint8_t *out) {
	x264_picture_t pic_out;
	int index_y, index_u, index_v;
	int num;
	int nNal = -1;
	int result = 0;
	int i = 0;
	static long int pts = 0;
	uint8_t *p_out = out;
	char *y = en->picture->img.plane[0];   
	char *u = en->picture->img.plane[1];   
	char *v = en->picture->img.plane[2];   
	char * ptr;

	index_y = 0;
        index_u = 0;
        index_v = 0;

        num = WIDTH * HEIGHT * 2 - 4  ;

        for(i=0; i<num; i=i+4)
        {
                *(y + (index_y++)) = *(in + i);
                *(u + (index_u++)) = *(in + i + 1);
                *(y + (index_y++)) = *(in + i + 2);
                *(v + (index_v++)) = *(in + i + 3);
        }

	switch (type) {
	case 0:
		en->picture->i_type = X264_TYPE_P;
		break;
	case 1:
		en->picture->i_type = X264_TYPE_IDR;
		break;
	case 2:
		en->picture->i_type = X264_TYPE_I;
		break;
	default:
		en->picture->i_type = X264_TYPE_AUTO;
		break;
	}

	en->picture->i_pts = pts++;

	if (x264_encoder_encode(en->handle, &(en->nal), &nNal, en->picture,
			&pic_out) < 0) {
		return -1;
	}

	for (i = 0; i < nNal; i++) {
		memcpy(p_out, en->nal[i].p_payload, en->nal[i].i_payload);   
		p_out += en->nal[i].i_payload;								 
		result += en->nal[i].i_payload;
	}

	return result;
}

void compress_end(Encoder *en) {
	if (en->handle) {
		x264_encoder_close(en->handle);
	}
	if (en->picture) {
		x264_picture_clean(en->picture);
		free(en->picture);
		en->picture = 0;
	}
	if (en->param) {
		free(en->param);
		en->param = 0;
	}
}

上面的代码是配置x264编码器的,有下面几个地方需要特别注意:

(1)CSP参数的配置

en->param->i_csp = X264_CSP_I422;

在X264中默认的i_csp值是3,也就是X264_CSP_NV12 的值,如果采用YUYV(422)输入格式,这个值一定需要重新设置,不然会出现错误提示:x264 [error]: Invalid input colorspace 。这是因为在x264内核中他会把输入格式装换为下面三种中的一种:X264_CSP_NV12,X264_CSP_NV16,X264_CSP_I444.转换如下:

static int x264_frame_internal_csp( int external_csp )
{
    switch( external_csp & X264_CSP_MASK )
    {
        case X264_CSP_NV12:
        case X264_CSP_NV21:
        case X264_CSP_I420:
        case X264_CSP_YV12:
            return X264_CSP_NV12;
        case X264_CSP_NV16:
        case X264_CSP_I422:
        case X264_CSP_YV16:
        case X264_CSP_V210:
            return X264_CSP_NV16;
        case X264_CSP_I444:
        case X264_CSP_YV24:
        case X264_CSP_BGR:
        case X264_CSP_BGRA:
        case X264_CSP_RGB:
            return X264_CSP_I444;
        default:
            return X264_CSP_NONE;
    }
}

(2)profile类型设置

x264_param_apply_profile(en->param, x264_profile_names[4]); 

在YUV422中,它不支持baseline,默认设置会提示:x264 [error]: baseline profile doesn't support 4:2:2 可以设置下面的其他参数:

x264_profile_names[] = { "baseline", "main", "high", "high10", "high422", "high444", 0 };

(3)图片内存分配

x264_picture_alloc(en->picture, X264_CSP_I422, en->param->i_width,
			en->param->i_height);

这里的第二个参数一定要与你的输入格式先对应,不然的话会出现内存溢出的错误。因为默认的分配图片内存大小是YUV420的。以640*480 分辨率来举例,YUV420 分配一帧图像的内存是450K,而我们YUV422的数据量是600K。

(4)Y,U,V 数据需要分离

for(i=0; i<num; i=i+4)
        {
                *(y + (index_y++)) = *(in + i);
                *(u + (index_u++)) = *(in + i + 1);
                *(y + (index_y++)) = *(in + i + 2);
                *(v + (index_v++)) = *(in + i + 3);
        }

    YUYV的数据是交错存储的,因此需要把他们分离出来单独存储,如果这里不做处理,图像就会出现异常。

(5)i_pts 参数需要递增

en->picture->i_pts = pts++;

    i_pts = pts的参数是需要递增的,不让回出现警告:x264 [warning]: non-strictly-monotonic PTS

    完整的编译运行结果如下:

[root@redhat test]# ls
h264encoder.c  include  lib  main.c  Makefile  out
[root@redhat test]# make
gcc -g    -c -o main.o main.c
gcc -g    -c -o h264encoder.o h264encoder.c
gcc -g -o x264_test main.o  h264encoder.o -lpthread -lx264 -lm
[root@redhat test]# ls
h264encoder.c  h264encoder.o  include  lib  main.c  main.o  Makefile  out  x264_test
[root@redhat test]# ./x264_test 

camera driver name is : uvcvideo
camera device name is : UVC Camera (046d:0825)
camera bus information: usb-0000:00:1a.0-1.1
n_buffer = 4
x264 [warning]: lookaheadless mb-tree requires intra refresh or infinite keyint
x264 [info]: using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX
x264 [info]: profile High 4:2:2, level 3.0, 4:2:2 8-bit
spend time 85.082031 s
x264 [info]: frame I:8     Avg QP:20.27  size: 21592
x264 [info]: frame P:503   Avg QP:21.18  size:  3119
x264 [info]: frame B:1485  Avg QP:22.03  size:   952
x264 [info]: consecutive B-frames:  0.8%  0.0%  0.0% 99.2%
x264 [info]: mb I  I16..4: 11.9% 55.2% 32.9%
x264 [info]: mb P  I16..4:  0.4%  0.2%  0.1%  P16..4: 44.8%  7.9%  8.5%  0.0%  0.0%    skip:38.2%
x264 [info]: mb B  I16..4:  0.0%  0.0%  0.0%  B16..8: 25.9%  0.6%  0.1%  direct: 1.7%  skip:71.7%  L0:51.6% L1:47.0% BI: 1.4%
x264 [info]: 8x8 transform intra:46.7% inter:95.7%
x264 [info]: coded y,uvDC,uvAC intra: 60.5% 87.6% 59.7% inter: 5.7% 23.2% 0.9%
x264 [info]: i16 v,h,dc,p:  4%  8%  1% 87%
x264 [info]: i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 16% 39% 12%  3%  5%  4% 10%  4%  7%
x264 [info]: i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 24% 41%  8%  3%  4%  3%  8%  3%  4%
x264 [info]: i8c dc,h,v,p: 50% 22% 21%  6%
x264 [info]: Weighted P-Frames: Y:0.2% UV:0.0%
x264 [info]: ref P L0: 40.2%  4.8% 39.3% 15.7%
x264 [info]: ref B L0: 65.6% 20.4% 14.0%
x264 [info]: ref B L1: 91.2%  8.8%
x264 [info]: kb/s:379.47
 [root@redhat test]# 

    这里设置的分辨率是640*480 ,这样采集数据比较快。我编码2000帧数据需要的是大约85s,帧率大约在23fps 。也不知道具体是什么原因导致耗时这么长时间。

视频运行如下:

完整的工程下载路径:V4L2采集YUYV数据x264编码成H264视频


 




 


 

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 30
    评论
### 回答1: Java摄像头通过v4l2采集YUYV格式的图像数据,可以通过以下步骤将其转换为YUV420P格式,然后再通过x26编码。 首先,我们需要了解YUYVYUV420P这两种图像数据格式的特点。 YUYV格式是一种16位的颜色编码格式,它使用两个连续的像素点共享一个色度信息(Cb和Cr),以减少颜色信息的采样率。每个像素所占用的字节为YUYVYUV420P格式是一种带有Y、Cb和Cr三个分量的图像格式,它采用了4:2:0的采样比例。对于每个Y像素,只有一个Cb和一个Cr分量,但对于每4个连续的Y像素,只有一个对应的Cb和一个对应的Cr分量。 将YUYV格式转换为YUV420P格式的步骤如下: 1. 首先,我们需要计算输出图像的分辨率(width和height)。假设输入图像的分辨率为inputWidth和inputHeight。 2. 创建一个大小为inputWidth * inputHeight * 3 / 2的字节数组,用来存储转换后的YUV420P数据。 3. 遍历输入图像的每个YUYV像素对(Y1, U, Y2, V),根据下面的公式计算输出图像的Y、Cb和Cr分量: Y1 = YUYV[2 * i]; U = YUYV[2 * i + 1]; Y2 = YUYV[2 * i + 2]; V = YUYV[2 * i + 3]; 对于输出图像的每个像素,计算对应的索引: outputIndex = (i / 2) * 3; 将Y、Cb和Cr分量写入输出图像的字节数组中: output[outputIndex] = Y1; output[outputIndex + 1] = U; output[outputIndex + 2] = V; output[outputIndex + 3] = Y2; output[outputIndex + 4] = U; output[outputIndex + 5] = V; 4. 最后,你可以使用x26编码器将转换后的YUV420P格式的图像数据进行编码。 这样,你就可以通过Java摄像头采集YUYV格式的图像数据,并将其转换为YUV420P格式,然后使用x26进行编码处理。 ### 回答2: 首先,Java可以使用v4l2库来采集摄像头的图像数据v4l2是一个视频4 Linux 2 编程接口,允许开发者在Linux系统中访问和控制视频设备,包括摄像头。 在Java中,可以使用JNI(Java Native Interface)来使用v4l2库函数。通过JNI将Java程序与C/C++代码连接起来,实现通过v4l2采集yuyv格式的图像数据。 其次,要将yuyv格式的图像数据转换为yuv420p格式,可以使用算法来进行处理。yuyv格式是一种颜色编码格式,它包含了亮度和色度信息。而yuv420p格式是一种通用的图像格式,其中亮度和色度信息被分离成两个平面。 转换的算法可以根据yuyvyuv420p的颜色编码规则进行处理。例如,可以使用色度抽取和亮度补偿的算法来实现转换。具体的实现方式需要根据具体的编码规则进行相应的处理。 最后,将转换后的yuv420p格式的图像数据通过x264进行编码x264是一个开源的H.264视频编码器库,可以将视频数据压缩为H.264格式。 通过调用x264库函数,可以将yuv420p格式的图像数据输入到x264编码器中进行编码编码器会根据H.264的编码规则进行图像压缩,并输出压缩后的视频数据。 以上是关于在Java中使用v4l2采集yuyv格式图像数据,将其转换为yuv420p格式,再通过x264进行编码的基本流程的回答。具体的实现需要结合具体的编程环境和库函数使用来进行详细操作。
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值