zoukankan      html  css  js  c++  java
  • 转:关于视频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 ! ");  
    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! ");  
    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! ");  
    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! ");  
    90.         exit(EXIT_FAILURE);  
    91.     }  
    92.   
    93. #if DEBUG  
    94.     printf("Line --------%d ",__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 ",__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! ");  
    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 ",ret);  
    112.         printf("x264_picture_alloc error! ");  
    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 ",__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! ");  
    129.         exit(EXIT_FAILURE);  
    130.     }  
    131.     CLEAR(*(encoder->yuv));  
    132.   
    133. #if DEBUG  
    134.     printf("Line --------%d ",__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! ");  
    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! ");  
    148.         exit(EXIT_FAILURE);  
    149.     }  
    150.   
    151. #if DEBUG  
    152.     printf("Line --------%d ",__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! ");  
    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! ");  
    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 ",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! ",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! ");  
    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版本

     

     

    欢迎批评指正!

  • 相关阅读:
    cmder
    navicat 查询保存的位置
    git使用
    怎么保证测试用例的覆盖率
    python3.7-初学篇-19-良好的习惯
    python基础篇-使用list和tuple
    python基础篇-字符串和编码
    python基础篇-输入和输出
    python3.7-初学篇-21
    python3.7-初学者-20
  • 原文地址:https://www.cnblogs.com/jhj117/p/5456084.html
Copyright © 2011-2022 走看看