V4L2视频采集及H264实时压缩
这里转载一片文章,是通过v4l2读取uvc摄像头的视频,并用x264编码库实时编码h.264视频的方法,文章作者的源码缺少一部分,将YUYV的图像格式转码为YUV420p,但是作者的文章提供了YUYV2yuv420p的算法。
转载如有不当之处,请联系我,谢谢!
以下是转载内容:
在我们的视频采集传输设备中,先是通过摄像头采集颜色数据组成一张画面,也就是我们常说的一帧。数据格式可以是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数据采集在前面已经介绍:
现在直接上本章的代码,也就是编码部分的代码:
- /*=============================================================================
- # 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 = 11024 * 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;
- charchar *y = en->picture->img.plane[0];
- charchar *u = en->picture->img.plane[1];
- charchar *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_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工程
最简单的视频编码到这里告一段落。
原文链接:点击打开链接