V4L2视频采集与H264编码4—X264编码H264视频

    在我们的视频采集传输设备中,先是通过摄像头采集颜色数据组成一张画面,也就是我们常说的一帧。数据格式可以是YUV数据也可以是RGB数据,他们之间可以通过计算转换。我们看到的视频其实就是由一帧一帧的画面组成,其速度一般是25帧/秒,电影《比利林恩的中场战事》采用的120帧/秒的技术。如果直接将摄像头采集到的颜色编码成视频,那么视频要求的带宽是非常非常高的。以30万像素摄像头YUV420格式来计算一帧数据大小 = 长 * 宽 * 1.5 = 640 * 480 * 1.5  / 1024  = 450 K,视频的码流将会是 450 K * 25  / 1024 = 9.88M/s。为了使视频传输更加的流畅,现在的视频都采用的压缩编码。这里我们将介绍使用X264编码器将YUV420 数据编码成H264格式视频。YUV数据采集在前面已经介绍:

V4L2视频采集与H264编码1—V4L2采集JPEG数据

V4L2视频采集与H264编码2—v4l2采集YUV数据

V4L2视频采集与H264编码3—X264移植

现在直接上本章的代码,也就是编码部分的代码:

/*=============================================================================
#     FileName: h264encoder.c
#         Desc: this program aim to get image from USB camera,
#               used the V4L2 interface.
#       Author: Licaibiao
#      Version: 
#   LastChange: 2016-12-11 
#      History:

=============================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./include/h264encoder.h"

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

	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 = 25; 
	en->param->i_fps_den = 1;
	x264_param_apply_profile(en->param, x264_profile_names[0]); 

	if ((en->handle = x264_encoder_open(en->param)) == 0) {
		return;
	}
	/* Create a new pic */
	x264_picture_alloc(en->picture, X264_CSP_I420, en->param->i_width,
			en->param->i_height);
	en->picture->img.i_csp = X264_CSP_I420;
	en->picture->img.i_plane = 3;
}

int compress_frame(Encoder *en, int type, uint8_t *in, uint8_t *out) {
	x264_picture_t pic_out;
	int nNal = -1;
	int result = 0;
	int i = 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];   

	//yuv420_length = 1.5 * en->param->i_width * en->param->i_height
	//int length =  en->param->i_width * en->param->i_height;
	//y = 640*480 ; U = 640*480*0.25, V = 640*480*0.25; 
	memcpy(y,in,307200);
	memcpy(u,in+307200,76800);
	memcpy(v,in+384000,76800);
	
	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;
	}

	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->picture) {
		x264_picture_clean(en->picture);
		free(en->picture);
		en->picture = 0;
	}
	if (en->param) {
		free(en->param);
		en->param = 0;
	}
	if (en->handle) {
		x264_encoder_close(en->handle);
	}
	//free(en);
}

    函数compress_begin 初始化的各参数的意思,可以参考 x264重要结构体详细说明 上面的参数我是随意配置的。

compress_frame 里面的

    memcpy(y,in,307200);

    memcpy(u,in+307200,76800);

    memcpy(v,in+384000,76800);

    这数值是根据我之前v4l2输出格式计算的,我设置的是YUV420 输出格式,其每个像素是每4个Y分量公用一个UV分量,所以Y = 4/4*width*hight;U = 1/4 *width*hight; V = 1/4 *width*hight。具体的可以参考:图文详解YUV420数据格式

    编译方到开发板的执行结果如下:

/tmp # 
/tmp # ls
hostapd    lib        messages   utmp       x264_test
/tmp # ls lib
libx264.so.148
/tmp # export LD_LIBRARY_PATH=/tmp/lib:$LD_LIBRARY_PATH
/tmp # ./x264_test 

camera driver name is : sunxi-vfe
camera device name is : sunxi-vfe
camera bus information: sunxi_vfe vfe.2
n_buffer = 3
x264 [warning]: lookaheadless mb-tree requires intra refresh or infinite keyint
x264 [info]: using cpu capabilities: none!
x264 [info]: profile Constrained Baseline, level 3.0
x264 [warning]: non-strictly-monotonic PTS
encode_frame num = 0
x264 [warning]: non-strictly-monotonic PTS
encode_frame num = 1
    ....
	....
x264 [warning]: non-strictly-monotonic PTS
encode_frame num = 98
x264 [info]: frame I:10    Avg QP:23.29  size: 15057
x264 [info]: frame P:89    Avg QP:26.69  size:  2080
x264 [info]: mb I  I16..4: 63.6%  0.0% 36.4%
x264 [info]: mb P  I16..4: 10.6%  0.0%  0.2%  P16..4: 39.0%  3.5%  1.8%  0.0%  0.0%    skip:44.8%
x264 [info]: coded y,uvDC,uvAC intra: 17.2% 83.3% 39.6% inter: 4.0% 30.0% 0.1%
x264 [info]: i16 v,h,dc,p: 49% 24% 13% 14%
x264 [info]: i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 13% 39% 23%  5%  4%  2%  8%  2%  4%
x264 [info]: i8c dc,h,v,p: 63% 18% 16%  3%
x264 [info]: ref P L0: 62.1% 21.6% 16.2%
x264 [info]: kb/s:inf
/tmp # 
/tmp # ls -l
total 588
drwxr-x---    2 root     root            60 Jan  1 00:00 hostapd
drwxr-xr-x    2 root     root            60 Jan  1 00:00 lib
-rw-r--r--    1 root     root         28372 Jan  1 00:37 messages
-rw-r--r--    1 root     root        335669 Jan  1 00:37 test.h264
-rw-r--r--    1 root     root          1152 Jan  1 00:00 utmp
-rwxrwxrwx    1 root     root         20674 Dec 11  2016 x264_test
/tmp #

    生成了我们所需要的H264格式视频文件test.h264,使用VLC media player 播放器可以直接播放。本来想做成实时视频流,无奈我开发板太low,编写一帧数据就需要接近两秒的时间,实在是受不了。

下面是使用播放器播放录制的视频截图

    这个工程的全部代码和X264编译生成的库文件可以到这里下载 X264编码H264工程

    最简单的视频编码到这里告一段落。

                                                                                                                                                                                            2016.12.11

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

li_wen01

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

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

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

打赏作者

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

抵扣说明:

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

余额充值