关于视频H264编解码的应用实现

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

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

参考教程:

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
 
 在CODE上查看代码片派生到我的代码片
  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.   
  10.             int i=0,j=0,l=0;  
  11.             for(j=0;j<IMAGE_HEIGHT;j++)  
  12.                 for(i=0;i<IMAGE_WIDTH*2;i++,l++){  
  13.   
  14.                     if(j%2==0){//even line to sample U-Chriminance  
  15.                         if(l==1){//sample U-Chriminance  
  16.                             *u=p[j*IMAGE_WIDTH*2+i];  
  17.                             u++;  
  18.                          }  
  19.                          else if(l==3){//abandon V-Chroma  
  20.                             l=-1;  
  21.                             continue;  
  22.   
  23.                          }  
  24.                          else{  
  25.                             *y=p[j*IMAGE_WIDTH*2+i];  
  26.                             ++y;  
  27.                          }  
  28.                     }  
  29.   
  30.                     else if(j%2==1){//odd lines to sample  V-Chroma  
  31.                         if(l==1){  
  32.                             continue;  
  33.                         }  
  34.                         else if(l==3){  
  35.                             l=-1;  
  36.                             *v=p[j*IMAGE_WIDTH*2+i];  
  37.                             ++v;  
  38.                         }  
  39.                         else {  
  40.                             *y=p[j*IMAGE_WIDTH*2+i];  
  41.                             ++y;  
  42.                         }  
  43.   
  44.                     }  
  45.   
  46.                 }  
  47.   
  48.             fwrite(yuv420p,IMAGE_WIDTH*IMAGE_HEIGHT*3>>1,1,fp_yuv420p);  
  49.   
  50.             fflush (stdout);  
  51.           
  52. }  

 

2.编码

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

程序如下:

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



3.        解码

利用ffmpeg进行解码

程序如下:

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  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.   
  10. #define DECODED_OUTPUT_FORMAT  AV_PIX_FMT_YUV420P  
  11. #define INPUT_FILE_NAME "encode.h264"  
  12. #define OUTPUT_FILE_NAME "decode.yuv"  
  13. #define IMAGE_WIDTH  320  
  14. #define IMAGE_HEIGHT 240  
  15.   
  16. void  
  17. error_handle(const char *errorInfo ){  
  18.     printf("%s error!\n",errorInfo);  
  19.     exit(EXIT_FAILURE);  
  20. }  
  21.   
  22.   
  23. int  
  24. main(int argc,char ** argv){  
  25.     int  write_fd,ret,videoStream;  
  26.     AVFormatContext * formatContext=NULL;  
  27.     AVCodec * codec;  
  28.     AVCodecContext * codecContext;  
  29.     AVFrame * decodedFrame;  
  30.     AVPacket packet;  
  31.     uint8_t *decodedBuffer;  
  32.     unsigned int decodedBufferSize;  
  33.     int finishedFrame;  
  34.   
  35.   
  36.     av_register_all();  
  37.   
  38.   
  39.     write_fd=open(OUTPUT_FILE_NAME,O_RDWR | O_CREAT,0666);  
  40.     if(write_fd<0){  
  41.         perror("open");  
  42.         exit(1);  
  43.     }  
  44.   
  45.     ret=avformat_open_input(&formatContext, INPUT_FILE_NAME, NULL,NULL);  
  46.     if(ret<0)  
  47.         error_handle("avformat_open_input error");  
  48.   
  49.     ret=avformat_find_stream_info(formatContext,NULL);  
  50.     if(ret<0)  
  51.         error_handle("av_find_stream_info");  
  52.   
  53.   
  54.     videoStream=0;  
  55.     codecContext=formatContext->streams[videoStream]->codec;  
  56.   
  57.     codec=avcodec_find_decoder(AV_CODEC_ID_H264);  
  58.     if(codec==NULL)  
  59.         error_handle("avcodec_find_decoder error!\n");  
  60.   
  61.     ret=avcodec_open2(codecContext,codec,NULL);  
  62.     if(ret<0)  
  63.         error_handle("avcodec_open2");  
  64.   
  65.     decodedFrame=avcodec_alloc_frame();  
  66.     if(!decodedFrame)  
  67.         error_handle("avcodec_alloc_frame!");  
  68.   
  69.     decodedBufferSize=avpicture_get_size(DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT);  
  70.     decodedBuffer=(uint8_t *)malloc(decodedBufferSize);  
  71.     if(!decodedBuffer)  
  72.         error_handle("malloc decodedBuffer error!");  
  73.   
  74.     av_init_packet(&packet);  
  75.     while(av_read_frame(formatContext,&packet)>=0){  
  76.             ret=avcodec_decode_video2(codecContext,decodedFrame,&finishedFrame,&packet);  
  77.             if(ret<0)  
  78.                 error_handle("avcodec_decode_video2 error!");  
  79.             if(finishedFrame){  
  80.                 avpicture_layout((AVPicture*)decodedFrame,DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT,decodedBuffer,decodedBufferSize);  
  81.                 ret=write(write_fd,decodedBuffer,decodedBufferSize);  
  82.                 if(ret<0)  
  83.                     error_handle("write yuv stream error!");  
  84.             }  
  85.   
  86.         av_free_packet(&packet);  
  87.     }  
  88.   
  89.     while(1){  
  90.         packet.data=NULL;  
  91.         packet.size=0;  
  92.         ret=avcodec_decode_video2(codecContext,decodedFrame,&finishedFrame,&packet);  
  93.         if(ret<=0 && (finishedFrame<=0))  
  94.             break;  
  95.         if(finishedFrame){  
  96.             avpicture_layout((AVPicture*)decodedFrame,DECODED_OUTPUT_FORMAT,IMAGE_WIDTH,IMAGE_HEIGHT,decodedBuffer,decodedBufferSize);  
  97.             ret=write(write_fd,decodedBuffer,decodedBufferSize);  
  98.             if(ret<0)  
  99.                 error_handle("write yuv stream error!");  
  100.         }  
  101.   
  102.         av_free_packet(&packet);  
  103.     }  
  104.   
  105.   
  106.     avformat_close_input(&formatContext);  
  107.     free(decodedBuffer);  
  108.     av_free(decodedFrame);  
  109.     avcodec_close(codecContext);  
  110.   
  111.     return 0;  
  112. }  

 

 

 

结果:

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版本

 

 

欢迎批评指正!

猜你喜欢

转载自blog.csdn.net/zdf511391565/article/details/79160908