项目要用到视频编解码,最近半个月都在搞,说实话真是走了很多弯路,浪费了很多时间。将自己的最终成果记录于此,期望会给其他人提供些许帮助。

参考教程:

http://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide安装ffmpeg和x264,官方权威教程(注意不要用命令行安装,会少很多库的。编译安装最保险)

http://blog.csdn.net/zgyulongfei/article/details/7526249采集与编码的教程

http://www.cnblogs.com/fojian/archive/2012/09/01/2666627.html编码的好文章

http://my.oschina.net/u/555701/blog/56616?p=2#comments-解码的好文章

整体过程流程如下:

显而易见,整个过程分为三个部分:采集、编码、解码。

1.        采集视频

我是利用USB摄像头采集视频的,我的摄像头只支持YUV422格式的图像采集,因为x264编码库只能编码YUV420P(planar)格式,因此在采集到yuv422格式的图像数据后要变换成yuv420p格式。

采集视频使用官方的那个采集程序,稍加修改即可,具体点说就是修改

static void  process_image (const char * p) ;函数

参数p指向一帧采集图像的yuv数据。

关于YUV格式和RGB格式,网上有很多教程。

在这儿,我讲一下自己的理解。

假设有一幅4*4分辨率的图片,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

每个像素是由YUV数据构成,假设如下:

Y1

U1

V1

Y2

U2

V2

Y3

U3

V3

Y4

U4

V4

Y5

U5

V5

Y6

U6

V6

Y7

U7

V7

Y8

U8

V8

Y9

U9

V9

Y10

U10

V10

Y11

U11

V11

Y12

U12

V12

Y13

U13

V13

Y14

U14

V14

Y15

U15

V15

Y16

U16

V16

YUV422图像是这样的,每个像素采集Y,UV每隔两个像素采集一次:

Packed格式的YUV420是这样的,每个像素采集Y,UV隔行采集,每行是每两个像素采集一次:

以上几种格式存储就是按照从左到右,从上到下顺序存储的。

我想要得到是planar格式的YUV420,即在一段连续的内存中,先存储所有的Y,接着是所有的U,最后是所有的V。

修改后的 process_image函数如下:

[cpp] view
plain
 copy

  1. static void
  2. process_image                   (const char *           p)
  3. {
  4. //fputc ('.', stdout);
  5. //convert yuv422 to yuv420p
  6. char *y=yuv420p;
  7. char *u=&yuv420p[IMAGE_WIDTH*IMAGE_HEIGHT];
  8. char *v=&yuv420p[IMAGE_WIDTH*IMAGE_HEIGHT+IMAGE_WIDTH*IMAGE_HEIGHT/4];
  9. int i=0,j=0,l=0;
  10. for(j=0;j<IMAGE_HEIGHT;j++)
  11. for(i=0;i<IMAGE_WIDTH*2;i++,l++){
  12. if(j%2==0){//even line to sample U-Chriminance
  13. if(l==1){//sample U-Chriminance
  14. *u=p[j*IMAGE_WIDTH*2+i];
  15. u++;
  16. }
  17. else if(l==3){//abandon V-Chroma
  18. l=-1;
  19. continue;
  20. }
  21. else{
  22. *y=p[j*IMAGE_WIDTH*2+i];
  23. ++y;
  24. }
  25. }
  26. else if(j%2==1){//odd lines to sample  V-Chroma
  27. if(l==1){
  28. continue;
  29. }
  30. else if(l==3){
  31. l=-1;
  32. *v=p[j*IMAGE_WIDTH*2+i];
  33. ++v;
  34. }
  35. else {
  36. *y=p[j*IMAGE_WIDTH*2+i];
  37. ++y;
  38. }
  39. }
  40. }
  41. fwrite(yuv420p,IMAGE_WIDTH*IMAGE_HEIGHT*3>>1,1,fp_yuv420p);
  42. fflush (stdout);
  43. }

2.编码

采用x264编码库编码yuv420p文件。

程序如下:

[cpp] view
plain
 copy

  1. #include <stdint.h>
  2. #include <x264.h>
  3. #include <stdio.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #define DEBUG 0
  9. #define CLEAR(x) (memset((&x),0,sizeof(x)))
  10. #define IMAGE_WIDTH   320
  11. #define IMAGE_HEIGHT  240
  12. #define ENCODER_PRESET "veryfast"
  13. #define ENCODER_TUNE   "zerolatency"
  14. #define ENCODER_PROFILE  "baseline"
  15. #define ENCODER_COLORSPACE X264_CSP_I420
  16. typedef struct my_x264_encoder{
  17. x264_param_t  * x264_parameter;
  18. char parameter_preset[20];
  19. char parameter_tune[20];
  20. char parameter_profile[20];
  21. x264_t  * x264_encoder;
  22. x264_picture_t * yuv420p_picture;
  23. long colorspace;
  24. unsigned char *yuv;
  25. x264_nal_t * nal;
  26. } my_x264_encoder;
  27. char *read_filename="yuv420p.yuv";
  28. char *write_filename="encode.h264";
  29. int
  30. main(int argc ,char **argv){
  31. int ret;
  32. int fd_read,fd_write;
  33. my_x264_encoder * encoder=(my_x264_encoder *)malloc(sizeof(my_x264_encoder));
  34. if(!encoder){
  35. printf("cannot malloc my_x264_encoder !\n");
  36. exit(EXIT_FAILURE);
  37. }
  38. CLEAR(*encoder);
  39. /****************************************************************************
  40. * Advanced parameter handling functions
  41. ****************************************************************************/
  42. /* These functions expose the full power of x264's preset-tune-profile system for
  43. * easy adjustment of large numbers //free(encoder->yuv420p_picture);of internal parameters.
  44. *
  45. * In order to replicate x264CLI's option handling, these functions MUST be called
  46. * in the following order:
  47. * 1) x264_param_default_preset
  48. * 2) Custom user options (via param_parse or directly assigned variables)
  49. * 3) x264_param_apply_fastfirstpass
  50. * 4) x264_param_apply_profile
  51. *
  52. * Additionally, x264CLI does not apply step 3 if the preset chosen is "placebo"
  53. * or --slow-firstpass is set. */
  54. strcpy(encoder->parameter_preset,ENCODER_PRESET);
  55. strcpy(encoder->parameter_tune,ENCODER_TUNE);
  56. encoder->x264_parameter=(x264_param_t *)malloc(sizeof(x264_param_t));
  57. if(!encoder->x264_parameter){
  58. printf("malloc x264_parameter error!\n");
  59. exit(EXIT_FAILURE);
  60. }
  61. CLEAR(*(encoder->x264_parameter));
  62. x264_param_default(encoder->x264_parameter);
  63. if((ret=x264_param_default_preset(encoder->x264_parameter,encoder->parameter_preset,encoder->parameter_tune))<0){
  64. printf("x264_param_default_preset error!\n");
  65. exit(EXIT_FAILURE);
  66. }
  67. encoder->x264_parameter->i_fps_den         =1;
  68. encoder->x264_parameter->i_fps_num         =25;
  69. encoder->x264_parameter->i_width       =IMAGE_WIDTH;
  70. encoder->x264_parameter->i_height      =IMAGE_HEIGHT;
  71. encoder->x264_parameter->i_threads         =1;
  72. encoder->x264_parameter->i_keyint_max    =25;
  73. encoder->x264_parameter->b_intra_refresh =1;
  74. encoder->x264_parameter->b_annexb      =1;
  75. strcpy(encoder->parameter_profile,ENCODER_PROFILE);
  76. if((ret=x264_param_apply_profile(encoder->x264_parameter,encoder->parameter_profile))<0){
  77. printf("x264_param_apply_profile error!\n");
  78. exit(EXIT_FAILURE);
  79. }
  80. #if DEBUG
  81. printf("Line --------%d\n",__LINE__);
  82. #endif
  83. encoder->x264_encoder=x264_encoder_open(encoder->x264_parameter);
  84. encoder->colorspace=ENCODER_COLORSPACE;
  85. #if DEBUG
  86. printf("Line --------%d\n",__LINE__);
  87. #endif
  88. encoder->yuv420p_picture=(x264_picture_t *)malloc(sizeof(x264_picture_t ));
  89. if(!encoder->yuv420p_picture){
  90. printf("malloc encoder->yuv420p_picture error!\n");
  91. exit(EXIT_FAILURE);
  92. }
  93. if((ret=x264_picture_alloc(encoder->yuv420p_picture,encoder->colorspace,IMAGE_WIDTH,IMAGE_HEIGHT))<0){
  94. printf("ret=%d\n",ret);
  95. printf("x264_picture_alloc error!\n");
  96. exit(EXIT_FAILURE);
  97. }
  98. encoder->yuv420p_picture->img.i_csp=encoder->colorspace;
  99. encoder->yuv420p_picture->img.i_plane=3;
  100. encoder->yuv420p_picture->i_type=X264_TYPE_AUTO;
  101. #if DEBUG
  102. printf("Line --------%d\n",__LINE__);
  103. #endif
  104. encoder->yuv=(uint8_t *)malloc(IMAGE_WIDTH*IMAGE_HEIGHT*3/2);
  105. if(!encoder->yuv){
  106. printf("malloc yuv error!\n");
  107. exit(EXIT_FAILURE);
  108. }
  109. CLEAR(*(encoder->yuv));
  110. #if DEBUG
  111. printf("Line --------%d\n",__LINE__);
  112. #endif
  113. encoder->yuv420p_picture->img.plane[0]=encoder->yuv;
  114. encoder->yuv420p_picture->img.plane[1]=encoder->yuv+IMAGE_WIDTH*IMAGE_HEIGHT;
  115. encoder->yuv420p_picture->img.plane[2]=encoder->yuv+IMAGE_WIDTH*IMAGE_HEIGHT+IMAGE_WIDTH*IMAGE_HEIGHT/4;
  116. if((fd_read=open(read_filename,O_RDONLY))<0){
  117. printf("cannot open input file!\n");
  118. exit(EXIT_FAILURE);
  119. }
  120. if((fd_write=open(write_filename,O_WRONLY | O_APPEND | O_CREAT,0777))<0){
  121. printf("cannot open output file!\n");
  122. exit(EXIT_FAILURE);
  123. }
  124. #if DEBUG
  125. printf("Line --------%d\n",__LINE__);
  126. #endif
  127. int n_nal;
  128. x264_picture_t pic_out;
  129. x264_nal_t *my_nal;
  130. encoder->nal=(x264_nal_t *)malloc(sizeof(x264_nal_t ));
  131. if(!encoder->nal){
  132. printf("malloc x264_nal_t error!\n");
  133. exit(EXIT_FAILURE);
  134. }
  135. CLEAR(*(encoder->nal));
  136. while(read(fd_read,encoder->yuv,IMAGE_WIDTH*IMAGE_HEIGHT*3/2)>0){
  137. encoder->yuv420p_picture->i_pts++;
  138. if((ret=x264_encoder_encode(encoder->x264_encoder,&encoder->nal,&n_nal,encoder->yuv420p_picture,&pic_out))<0){
  139. printf("x264_encoder_encode error!\n");
  140. exit(EXIT_FAILURE);
  141. }
  142. unsigned int length=0;
  143. for(my_nal=encoder->nal;my_nal<encoder->nal+n_nal;++my_nal){
  144. write(fd_write,my_nal->p_payload,my_nal->i_payload);
  145. length+=my_nal->i_payload;
  146. }
  147. printf("length=%d\n",length);
  148. }
  149. /*clean_up functions*/
  150. //x264_picture_clean(encoder->yuv420p_picture);
  151. //free(encoder->nal);//???? confused conflict with x264_encoder_close(encoder->x264_encoder);
  152. free(encoder->yuv);
  153. free(encoder->yuv420p_picture);
  154. free(encoder->x264_parameter);
  155. x264_encoder_close(encoder->x264_encoder);
  156. free(encoder);
  157. close(fd_read);
  158. close(fd_write);
  159. return 0;
  160. }

3.        解码

利用ffmpeg进行解码

程序如下:

[cpp] view
plain
 copy

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <libavcodec/avcodec.h>
  7. #include <libavformat/avformat.h>
  8. #include <libavutil/mathematics.h>
  9. #define DECODED_OUTPUT_FORMAT  AV_PIX_FMT_YUV420P
  10. #define INPUT_FILE_NAME "encode.h264"
  11. #define OUTPUT_FILE_NAME "decode.yuv"
  12. #define IMAGE_WIDTH  320
  13. #define IMAGE_HEIGHT 240
  14. void
  15. error_handle(const char *errorInfo ){
  16. printf("%s error!\n",errorInfo);
  17. exit(EXIT_FAILURE);
  18. }
  19. int
  20. main(int argc,char ** argv){
  21. int  write_fd,ret,videoStream;
  22. AVFormatContext * formatContext=NULL;
  23. AVCodec * codec;
  24. AVCodecContext * codecContext;
  25. AVFrame * decodedFrame;
  26. AVPacket packet;
  27. uint8_t *decodedBuffer;
  28. unsigned int decodedBufferSize;
  29. int finishedFrame;
  30. av_register_all();
  31. write_fd=open(OUTPUT_FILE_NAME,O_RDWR | O_CREAT,0666);
  32. if(write_fd<0){
  33. perror("open");
  34. exit(1);
  35. }
  36. ret=avformat_open_input(&formatContext, INPUT_FILE_NAME, NULL,NULL);
  37. if(ret<0)
  38. error_handle("avformat_open_input error");
  39. ret=avformat_find_stream_info(formatContext,NULL);
  40. if(ret<0)
  41. error_handle("av_find_stream_info");
  42. videoStream=0;
  43. codecContext=formatContext->streams[videoStream]->codec;
  44. codec=avcodec_find_decoder(AV_CODEC_ID_H264);
  45. if(codec==NULL)
  46. error_handle("avcodec_find_decoder error!\n");
  47. ret=avcodec_open2(codecContext,codec,NULL);
  48. if(ret<0)
  49. error_handle("avcodec_open2");
  50. decodedFrame=avcodec_alloc_frame();
  51. if(!decodedFrame)
  52. error_handle("avcodec_alloc_frame!");
  53. decodedBufferSize=avpicture_get_size(DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT);
  54. decodedBuffer=(uint8_t *)malloc(decodedBufferSize);
  55. if(!decodedBuffer)
  56. error_handle("malloc decodedBuffer error!");
  57. av_init_packet(&packet);
  58. while(av_read_frame(formatContext,&packet)>=0){
  59. ret=avcodec_decode_video2(codecContext,decodedFrame,&finishedFrame,&packet);
  60. if(ret<0)
  61. error_handle("avcodec_decode_video2 error!");
  62. if(finishedFrame){
  63. avpicture_layout((AVPicture*)decodedFrame,DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT,decodedBuffer,decodedBufferSize);
  64. ret=write(write_fd,decodedBuffer,decodedBufferSize);
  65. if(ret<0)
  66. error_handle("write yuv stream error!");
  67. }
  68. av_free_packet(&packet);
  69. }
  70. while(1){
  71. packet.data=NULL;
  72. packet.size=0;
  73. ret=avcodec_decode_video2(codecContext,decodedFrame,&finishedFrame,&packet);
  74. if(ret<=0 && (finishedFrame<=0))
  75. break;
  76. if(finishedFrame){
  77. avpicture_layout((AVPicture*)decodedFrame,DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT,decodedBuffer,decodedBufferSize);
  78. ret=write(write_fd,decodedBuffer,decodedBufferSize);
  79. if(ret<0)
  80. error_handle("write yuv stream error!");
  81. }
  82. av_free_packet(&packet);
  83. }
  84. avformat_close_input(&formatContext);
  85. free(decodedBuffer);
  86. av_free(decodedFrame);
  87. avcodec_close(codecContext);
  88. return 0;
  89. }

结果:

1.      利用USB摄像头采集的YUV420P,大小11.0MB,可以用pyuv播放器正常播放。

2.      编码后的文件encode.h264,大小262.4kb,可用vlc播放器正常播放。

3.      解码后的文件decode.yuv,大小11.0MB,可以用pyuv播放器正常播放。

相关文件在我的资源里,里面包含:

1.      采集、编码、解码程序、对应的可执行程序和Makefile文件;

2.      Pyuv播放器(用于XP)

3.      实验文件-yuv420p.yuv 、encode.h264、 decode.yuv

4.      相关参考文档pdf版本

欢迎批评指正!

转自:http://blog.csdn.net/scalerzhangjie/article/details/8273410

视频编解码---x264用于编码,ffmpeg用于解码的更多相关文章

  1. 集显也能硬件编码:Intel SDK && 各种音视频编解码学习详解

    http://blog.sina.com.cn/s/blog_4155bb1d0100soq9.html INTEL MEDIA SDK是INTEL推出的基于其内建显示核心的编解码技术,我们在播放高清 ...

  2. 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    目录(?)[-] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列AAC音频 编解码学习笔 ...

  3. 视频编解码的理论和实践2:Ffmpeg视频编解码

    近几年,视频编解码技术在理论及应用方面都取得了重大的进展,越来越多的人想要了解编解码技术.因此,网易云信研发工程师为大家进行了归纳梳理,从理论及实践两个方面简单介绍视频编解码技术. 相关阅读推荐 &l ...

  4. FFmpeg音视频编解码实践总结

    PS:由于目前开发RTSP服务器传输模块时用到了h264文件,所以攻了一段时间去实现h264的视频编解码,借用FFmpeg SDK实现了任意文件格式之间的转换,并实现了流媒体实时播放,目前音视频同步需 ...

  5. 【视频编解码·学习笔记】8. 熵编码算法:基本算法列举 & 指数哥伦布编码

    一.H.264中的熵编码基本方法: 熵编码具有消除数据之间统计冗余的功能,在编码端作为最后一道工序,将语法元素写入输出码流 熵解码作为解码过程的第一步,将码流解析出语法元素供后续步骤重建图像使用 在H ...

  6. WebUtility(提供在处理 Web 请求时用于编码和解码 URL 的方法。)

    public static string UrlEncode( string str ) UrlEncode(String) 方法可用来编码整个 URL,包括查询字符串值. 如果没有编码情况下,如空格 ...

  7. 【miscellaneous】各种音视频编解码学习详解

    编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等 ...

  8. Java版流媒体编解码和图像处理(JavaCPP+FFmpeg)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. [转帖]AVS音视频编解码技术了解

    AVS高清立体视频编码器 电视技术在经历了从黑白到彩色.从模拟到数字的技术变革之后正在酝酿另一场技术革命,从单纯观看二维场景的平面电视跨越到展现三维场景的立体电视3DTV.3DTV系统的核心问题之一是 ...

随机推荐

  1. D. Closest Equals(线段树)

    题目链接: D. Closest Equals time limit per test 3 seconds memory limit per test 256 megabytes input stan ...

  2. 一名十年Java程序员回忆阿里面试经历——揭开阿里面试的“遮羞布”

    阿里面试经历 去阿里面试可以说非常非常的偶然和戏剧性,因为本人根本没投简历,以至于阿里hr给我电话的时候我一度认为是诈骗电话.因为深圳这家公司不错我还想在这里干个两年左右再考虑考虑. 这个时候的本人已 ...

  3. python爬虫入门(4)-补充知识:XPath 教程(转自w3school)

    http://www.w3school.com.cn/xpath/index.asp 参考手册:http://www.w3school.com.cn/xpath/xpath_functions.asp ...

  4. vue 之node.js 02

    文档 铺垫 以前网页制作web1.0 如今是web2.0-->交互式操作 前端工具 grunt gulp webpack :打包机 作用:将项目中的js,css,img,font,html等进行 ...

  5. 编程之美Ex1——求二进制中1的个数

    又被阿里机考虐了一次,决定改变策略开始刷题T^T 一个字节(8bit)的无符号整型,求其二进制中的“1”的个数,算法执行效率尽可能高. 最先想到的移位操作,末尾位&00000001,然后右移, ...

  6. 剑指offer第四章

    剑指offer第四章 1.二叉树的镜像 二叉树的镜像:输入一个二叉树,输出它的镜像 分析:求树的镜像过程其实就是在遍历树的同时,交换非叶结点的左右子结点. 求镜像的过程:先前序遍历这棵树的每个结点,如 ...

  7. WebApi系列~通过HttpClient来调用Web Api接口~续~实体参数的传递 【转】

    原文:http://www.cnblogs.com/lori/p/4045633.html 下面定义一个复杂类型对象 public class User_Info { public int Id { ...

  8. Python面试题(十一)

    1.Python中list.tuple.dict.set有什么区别,主要应用在什么样的场景?并用for语句分别进行遍历 定义: list:链表,有序的项目, 通过索引进行查找,使用方括号”[]”; t ...

  9. Cucumber 之Gherkin

     1.Gherkin简介: Cucumber是一个解释程序,就像ruby命令执行解释 .rb文件里的Ruby代码一样,Cucumber用来执行解释 .feature文件里的Gehrkin代码. 2. ...

  10. C#类、方法的访问修饰符

    这篇文章主要介绍了C#类的访问修饰符用法,较为详细的分析了C#类的访问修饰符概念与用法,具有一定的参考借鉴价值,需要的朋友可以参考下 本文详细分析了C#类的访问修饰符用法,分享给大家供大家参考.具体用 ...