V4L2视频采集及H264实时压缩

来源:互联网 发布:oracle数据库教程 编辑:程序博客网 时间:2024/06/05 16:55

这里转载一片文章,是通过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数据采集在前面已经介绍:

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

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

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

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

[objc] view plain copy print?
  1. /*============================================================================= 
  2. #     FileName: h264encoder.c 
  3. #         Desc: this program aim to get image from USB camera, 
  4. #               used the V4L2 interface. 
  5. #       Author: Licaibiao 
  6. #      Version:  
  7. #   LastChange: 2016-12-11  
  8. #      History: 
  9.  
  10. =============================================================================*/  
  11. #include <stdio.h>  
  12. #include <stdlib.h>  
  13. #include <string.h>  
  14. #include "./include/h264encoder.h"  
  15.   
  16. void compress_begin(Encoder *en, int width, int height) {  
  17.     en->param = (x264_param_t *) malloc(sizeof(x264_param_t));  
  18.     en->picture = (x264_picture_t *) malloc(sizeof(x264_picture_t));  
  19.     x264_param_default(en->param); //set default param  
  20.     //en->param->rc.i_rc_method = X264_RC_CQP;  
  21.     // en->param->i_log_level = X264_LOG_NONE;  
  22.   
  23.      en->param->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;  
  24.   
  25.     en->param->i_width = width; //set frame width  
  26.     en->param->i_height = height; //set frame height  
  27.   
  28.     en->param->i_frame_total = 0;  
  29.   
  30.     en->param->i_keyint_max = 10;  
  31.     en->param->rc.i_lookahead = 0;   
  32.     en->param->i_bframe = 5;   
  33.   
  34.     en->param->b_open_gop = 0;  
  35.     en->param->i_bframe_pyramid = 0;  
  36.     en->param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;  
  37.   
  38.     en->param->rc.i_bitrate = 11024 * 10;//rate 10 kbps  
  39.     en->param->i_fps_num = 25;   
  40.     en->param->i_fps_den = 1;  
  41.     x264_param_apply_profile(en->param, x264_profile_names[0]);   
  42.   
  43.     if ((en->handle = x264_encoder_open(en->param)) == 0) {  
  44.         return;  
  45.     }  
  46.     /* Create a new pic */  
  47.     x264_picture_alloc(en->picture, X264_CSP_I420, en->param->i_width,  
  48.             en->param->i_height);  
  49.     en->picture->img.i_csp = X264_CSP_I420;  
  50.     en->picture->img.i_plane = 3;  
  51. }  
  52.   
  53. int compress_frame(Encoder *en, int type, uint8_t *in, uint8_t *out) {  
  54.     x264_picture_t pic_out;  
  55.     int nNal = -1;  
  56.     int result = 0;  
  57.     int i = 0;  
  58.     uint8_t *p_out = out;  
  59.   
  60.     charchar *y = en->picture->img.plane[0];     
  61.     charchar *u = en->picture->img.plane[1];     
  62.     charchar *v = en->picture->img.plane[2];     
  63.   
  64.     //yuv420_length = 1.5 * en->param->i_width * en->param->i_height  
  65.     //int length =  en->param->i_width * en->param->i_height;  
  66.     //y = 640*480 ; U = 640*480*0.25, V = 640*480*0.25;   
  67.     memcpy(y,in,307200);  
  68.     memcpy(u,in+307200,76800);  
  69.     memcpy(v,in+384000,76800);  
  70.       
  71.     switch (type) {  
  72.     case 0:  
  73.         en->picture->i_type = X264_TYPE_P;  
  74.         break;  
  75.     case 1:  
  76.         en->picture->i_type = X264_TYPE_IDR;  
  77.         break;  
  78.     case 2:  
  79.         en->picture->i_type = X264_TYPE_I;  
  80.         break;  
  81.     default:  
  82.         en->picture->i_type = X264_TYPE_AUTO;  
  83.         break;  
  84.     }  
  85.   
  86.     if (x264_encoder_encode(en->handle, &(en->nal), &nNal, en->picture,  
  87.             &pic_out) < 0) {  
  88.         return -1;  
  89.     }  
  90.   
  91.     for (i = 0; i < nNal; i++) {  
  92.         memcpy(p_out, en->nal[i].p_payload, en->nal[i].i_payload);     
  93.         p_out += en->nal[i].i_payload;                                  
  94.         result += en->nal[i].i_payload;  
  95.     }  
  96.   
  97.     return result;  
  98. }  
  99.   
  100. void compress_end(Encoder *en) {  
  101.     if (en->picture) {  
  102.         x264_picture_clean(en->picture);  
  103.         free(en->picture);  
  104.         en->picture = 0;  
  105.     }  
  106.     if (en->param) {  
  107.         free(en->param);  
  108.         en->param = 0;  
  109.     }  
  110.     if (en->handle) {  
  111.         x264_encoder_close(en->handle);  
  112.     }  
  113.     //free(en);  
  114. }  
    函数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数据格式

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

[objc] view plain copy print?
  1. /tmp #   
  2. /tmp # ls  
  3. hostapd    lib        messages   utmp       x264_test  
  4. /tmp # ls lib  
  5. libx264.so.148  
  6. /tmp # export LD_LIBRARY_PATH=/tmp/lib:$LD_LIBRARY_PATH  
  7. /tmp # ./x264_test   
  8.   
  9. camera driver name is : sunxi-vfe  
  10. camera device name is : sunxi-vfe  
  11. camera bus information: sunxi_vfe vfe.2  
  12. n_buffer = 3  
  13. x264 [warning]: lookaheadless mb-tree requires intra refresh or infinite keyint  
  14. x264 [info]: using cpu capabilities: none!  
  15. x264 [info]: profile Constrained Baseline, level 3.0  
  16. x264 [warning]: non-strictly-monotonic PTS  
  17. encode_frame num = 0  
  18. x264 [warning]: non-strictly-monotonic PTS  
  19. encode_frame num = 1  
  20.     ....  
  21.     ....  
  22. x264 [warning]: non-strictly-monotonic PTS  
  23. encode_frame num = 98  
  24. x264 [info]: frame I:10    Avg QP:23.29  size15057  
  25. x264 [info]: frame P:89    Avg QP:26.69  size:  2080  
  26. x264 [info]: mb I  I16..463.6%  0.036.4%  
  27. x264 [info]: mb P  I16..410.6%  0.0%  0.2%  P16..439.0%  3.5%  1.8%  0.0%  0.0%    skip:44.8%  
  28. x264 [info]: coded y,uvDC,uvAC intra17.283.339.6% inter4.030.00.1%  
  29. x264 [info]: i16 v,h,dc,p: 49241314%  
  30. x264 [info]: i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 133923%  5%  4%  2%  8%  2%  4%  
  31. x264 [info]: i8c dc,h,v,p: 631816%  3%  
  32. x264 [info]: ref P L062.121.616.2%  
  33. x264 [info]: kb/s:inf  
  34. /tmp #   
  35. /tmp # ls -l  
  36. total 588  
  37. drwxr-x---    2 root     root            60 Jan  1 00:00 hostapd  
  38. drwxr-xr-x    2 root     root            60 Jan  1 00:00 lib  
  39. -rw-r--r--    1 root     root         28372 Jan  1 00:37 messages  
  40. -rw-r--r--    1 root     root        335669 Jan  1 00:37 test.h264  
  41. -rw-r--r--    1 root     root          1152 Jan  1 00:00 utmp  
  42. -rwxrwxrwx    1 root     root         20674 Dec 11  2016 x264_test  
  43. /tmp #  

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

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



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

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



原文链接:点击打开链接