• 1.一般使用ffmpeg来对音视频...3.关于自己对X264编解码原理的总结。 函数总图如下: 第一阶段:x264的入口函数为main()。main()函数首先调用parse()解析输入的参数,parse()首先调用x264_param_default()为保...

    1.一般使用ffmpeg来对音视频数据进行处理,视频编码的底层为x264该链接为FFMPEG的具体使用

    2.关于X264的解码原理来自该链接:x264代码剖析

    3.关于自己对X264编解码原理的总结。

    函数总图如下:

    第一阶段:x264的入口函数为main()。main()函数首先调用parse()解析输入的参数,parse()首先调用x264_param_default()为保存参数的x264_param_t结构体赋默认值;然后在一个大循环中通过getopt_long()解析通过命令行传递来的存储在argv[]中的参数,并作相应的设置工作;最后调用select_input()和select_output()完成输入文件格式(yuv,y4m等)和输出文件格式(裸流,mp4,mkv,FLV等)的设置。

    第二阶段:然后调用encode()编码YUV数据。encode()首先调用x264_encoder_open()打开编码器;接着在一个循环中反复调用encode_frame()一帧一帧地进行编码;最后在编码完成后调用x264_encoder_close()关闭编码器。encode_frame()则调用x264_encoder_encode()将存储YUV数据的x264_picture_t编码为存储H.264数据的x264_nal_t。

    1.main函数分析:主要是parse( argc, argv, ¶m, &opt)和encode( ¶m, &opt )函数。

    2.parse()函数功能:

    x264_param_default( &defaults );

    int c = getopt_long( argc, argv, short_options, long_options, NULL );判断命令行输入是否正确

    x264_param_default_preset( param, preset, tune ) < 0 )

    int c = getopt_long( argc, argv, short_options, long_options, &long_options_index );解析命令行

    output_filename = optarg;

    opt->qpfile = x264_fopen( optarg, "rb" );

    2.encode()编码YUV为H.264码流,主要流程为:

    (1)调用x264_encoder_open()打开H.264编码器;

    h = x264_encoder_open( param );

    (2)调用x264_encoder_parameters()获得当前的参数集x264_param_t,用于后续步骤中的一些配置;

    x264_encoder_parameters( h, param );

    (3)调用输出格式(H.264裸流、FLV、mp4等)对应cli_output_t结构体的set_param()方法,为输出格式的封装器设定参数。其中参数源自于上一步骤得到的x264_param_t;

    cli_output.set_param( opt->hout, param )

    (4)如果不是在每个keyframe前面都增加文件头(SPS/PPS/SEI)的话,就调用x264_encoder_headers()在整个码流前面加输出文件头(SPS/PPS/SEI);将文件头写入到输出文件opt->out.

    (i_file = cli_output.write_headers( opt->hout, headers ))

    (5)进入一个循环中进行一帧一帧的将YUV编码为H.264,主要大概程序流程如下:

    for( ; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ )
        {
            //从输入源中获取1帧YUV数据,存于cli_pic  
            //cli_vid_filter_t可以认为是x264一种“扩展”后的输入源,可以在像素域对图像进行拉伸裁剪等工作。  
            //原本代表输入源的结构体是cli_input_t
    		if( filter.get_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
                break;
     
    		//初始化x264_picture_t结构体pic
            x264_picture_init( &pic );
     
    		//cli_pic到pic
            convert_cli_to_lib_pic( &pic, &cli_pic );
     
    		//编码pic中存储的1帧YUV数据
            i_frame_size = encode_frame( h, opt->hout, &pic, &last_dts );	
            
    		//释放处理完的YUV数据
            if( filter.release_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
                break;
            
    }

    (6)编码即将结束的时候,进入另一个循环,输出编码器中缓存的视频帧:

           a)不再传递新的YUV数据,直接调用encode_frame(),将编码器中缓存的剩余几帧数据编码输出出来。
           b)调用print_status()输出一些统计信息。

    (7)调用x264_encoder_close()关闭H.264编码器。

    encode_fram调用了x264_encoder_encode()函数是其核心部分,具体的H.264视频编码算法均在此模块。

    x264_encoder_encode()的流程大致如下:

     x264_frame_pop_unused():获取1个x264_frame_t类型结构体fenc。如果frames.unused[]队列不为空,就调用x264_frame_pop()从unused[]队列取1个现成的;否则就调用x264_frame_new()创建一个新的。

            x264_frame_copy_picture():将输入的图像数据拷贝至fenc。

            x264_frame_expand_border_mod16( h, fenc );对图像进行插值确保图像的长宽均为宏块16的倍数。

            x264_lookahead_put_frame(h,fenc):将fenc放入lookahead.next.list[]队列,等待确定帧类型。

            h->i_frame++;

            x264_lookahead_get_frames(h):通过lookahead分析帧h->frames.current[0]类型。该函数调用了x264_slicetype_decide(),x264_slicetype_analyse()和x264_slicetype_frame_cost()等函数。经过一些列分析之后,最终确定了帧类型信息,并且将帧放入frames.current[]队列。

            x264_frame_shift():从frames.current[]队列取出1帧h->fenc用于编码。是栈还是队列?队列。

            x264_reference_update():更新参考帧队列。frames.reference[]。

           根据fenc帧的类型来做选择:

            x264_reference_reset():如果fenc帧为IDR帧,调用该函数清空参考帧列表。

            x264_reference_hierarchy_reset():如果是非IDR的I帧、P帧、B帧(可做为参考帧),调用该函数。

            x264_reference_build_list():创建参考帧列表list0和list1。

            x264_ratecontrol_start():开启码率控制。

            x264_slice_init():创建 Slice Header。

            x264_slices_write():编码数据(最关键的步骤)。其中调用了x264_slice_write()完成了编码的工作(注意“x264_slices_write()”和“x264_slice_write()”名字差了一个“s”)。

            x264_encoder_frame_end():编码结束后做一些后续处理,例如记录一些统计信息。其中调用了x264_encoder_encapsulate_nals()封装NALU(添加起始码),调用x264_frame_push_unused()将fenc重新放回frames.unused[]队列,并且调用x264_ratecontrol_end()结束码率控制。

    调用x264_slices_write()进行编码。该部分是libx264的核心,在后续文章中会详细分析。H.264并没有明确规定一个编解码器如何实现,而是规定了一个编码后的视频比特流的句法和比特流的解码方法,在实现上有较大的灵活性。

       H.264/AVC编码器的功能组成如下图所示,接下来是如何解释该图:

    H264编码原理:首先对每一帧图像进行宏块划分,有不同的划分方式,然后对相邻图像进行分组。在这样一组帧中,经过编码后,我们只保留第一帖的完整数据,其它帧都通过参考上一帧计算出来。我们称第一帧为IDR/I帧,其它帧我们称为P/B帧,这样编码后的数据帧组我们称为GOP。一个GOP中可以有很多的I帧,但只能有一个IDR帧。IDR帧也属于I帧。所以把几帧图像分为一组(GOP,也就是一个序列),为防止运动变化,帧数不宜取多。然后将每组内各帧图像定义为三种类型,即I帧、B帧和P帧;定义好之后以I帧做为基础帧,以I帧预测P帧,再由I帧和P帧预测B帧,最后将I帧数据与预测的差值信息(p帧b帧)进行存储和传输。

    H264存储原理:在H264中图像以序列为单位进行组织,一个序列是一段图像编码后的数据流,以I帧开始,到下一个I帧结束。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。一个序列就是一段内容差异不太大的图像编码后生成的一串数据流。当运动变化比较少时,一个序列可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。当运动变化多时,可能一个序列就比较短了,比如就包含一个I帧和3、4个P帧。

    I帧:基本帧,帧内编码帧 ,关键帧,完整保留了该图像的所有信息,相比原始数据帧,压缩率7~10;解码时只需要本帧数据就可以完成(因为包含完整画面),不需要其他帧作为参考同时是PB帧的参考帧,I帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧;

    P帧:前向预测编码帧(只参考前面最靠近它的I帧或P帧)。是在I 帧的基础上取与I 帧的差异,压缩率20;P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面(P帧的预测与重构:P帧是以I帧为参考帧,在I帧中找出P帧“某点”的预测值和运动矢量,取预测差值和运动矢量一起传送。在接收端根据运动矢量从I帧中找出P帧“某点”的预测值并与差值相加以得到P帧“某点”样值,从而可得到完整的P帧。)。也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据。P帧是I帧后面相隔1~2帧的编码帧;

    B帧:双向预测内插编码帧(由前面的I或P帧和后面的P帧来进行预测的)。也就是B帧记录的是本帧与前后帧的差别,B帧的压缩率高,解码时CPU会比较累;压缩率50;要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面(B帧的预测与重构:B帧以前面的I或P帧和后面的P帧为参考帧,“找出”B帧“某点”的预测值和两个运动矢量,并取预测差值和运动矢量传送。接收端根据运动矢量在两个参考帧中“找出(算出)”预测值并与差值求和,得到B帧“某点”样值,从而可得到完整的B帧。)。

     我们若要学习H.264编码算法,最好就是按照帧内预测帧间预测变换与量化熵编码环路滤波的顺序依次学习。除此之外,网络适配层、率失真优化、码率控制也是非常重要的学习点。其中帧内预测与帧间预测处于Analysis模块,变换与量化处于Encode模块,熵编码处于Entropy Encoding模块,而重建过程中的滤波处于Filter模块。

    1)帧间和帧内预测(Estimation)
    帧内预测编码是H.264区别于其它编码标准的一个重要特征,用来缩减图像的空间冗余.为了提高H.264帧内编码的效率,帧内压缩是生成I帧的算法,所以压缩率不是很高。相对于直接对该帧编码而言,可以大大减小码率.

    帧间预测编码利用连续帧中的时间冗余来进行运动估计和补偿.H.264的运动补偿支持以往的视频编码标准中的大部分关键特性,而且灵活地添加了更多的功能,帧间压缩是生成B帧和P帧的算法。它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量,压缩率较高。

    宏块分析函数x264_macroblock_analyse(),该模块主要完成2大任务:一是对于帧内宏块,分析帧内预测模式;二是对于帧间宏块,进行运动估计,分析帧间预测模式。

    2)变换(Transform)
    在变换方面,H.264使用了基于4×4像素块的类似于DCT的变换,但使用的是以整数为基础的空间变换,不存在反变换.与浮点运算相比,整数DCT变换会引起一些额外的误差,但因为DCT变换后的量化也存在量化误差,与之相比,整数DCT变换引起的量化误差影响并不大.此外,整数DCT变换还具有减少运算量和复杂度,有利于向定点DSP移植的优点.变换后得到的DCT系数是与每个像素都相关的,这些系数代表了被变换数据的基础色调与细节.。

    3)量化(Quantization)
    量化是在不降低视觉效果的前提下减少图像编码长度,减少视觉恢复中不必要的信息。H264采用标量量化技术,它将每个图像样点编码映射成较小的数值。H.264中可选32种不同的量化步长,这与H.263中有31个量化步长很相似,但是在H.264中,步长是以12.5%的复合率递进的,而不是一个固定常数.在H.264中,变换系数的读出方式也有2种:之字形(Zigzag)扫描和双扫描.大多数情况下使用简单的之字形扫描;双扫描仅用于使用较小量化级的块内,有助于提高编码效率.h.264在DCT变换后对DCT系数进行了量化,量化能有效去除相邻像素间的空间冗余,也就是说会抹去元素数据的部分细节。比较理想的情况是量化抹去人眼无法识别的细节部分,但是在低码率的情况下就会导致原始数据的细节丢失过多。而且,DCT变换时基于块的,即将8x8或者4x4的像素残差进行变换后得到8x8或者4x4DCT系数,此时如果进行了低码率的量化,就会使得相邻两个块的相关性变差,从而出现块效应。.

    4)环路滤波(Loop Filter)
    为了减轻和消除视频图像中的块效应,通常会使用滤波器对块边界处的像素进行滤波以平滑像素值的突变.同时可以达到降低噪音的效果

    5)熵编码
    视频编码处理的最后一步就是熵编码,在H.264中采用了2种不同的熵编码方法:通用可变长编码(UVLC)和基于文本的自适应二进制算术编码(CABAC).熵编码即编码过程中按熵原理不丢失任何信息的编码。
     

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

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

    参考教程:

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





    欢迎批评指正!



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

    展开全文
  • x264 ffmpeg编解码

    2017-05-10 13:39:51
    x264 ffmpeg编解码 demo: https://github.com/wangzuxing/MyFFmpegH264H265YUVOpenGL H.264编码: java端的camera preview data经mediacodec编码,送jni端, jni调用x264库/ffmpeg进行编码、生成.264文件 H.264...

    x264 ffmpeg编解码

    demo: https://github.com/wangzuxing/MyFFmpegH264H265YUVOpenGL

    H.264编码:
    java端的camera preview data经mediacodec编码,送jni端, jni调用x264库/ffmpeg进行编码、生成.264文件

    H.264解码:
    java端的camera preview data经mediacodec编码,送jni端, jni调用ffmpeg进行实时解码、解码yuv数据直接送java进行刷新显示(open gl yuv渲染)

    JAVA端:
    MainActivity0:

    static {
        ...
        System.loadLibrary("x264");
        System.loadLibrary("ffmpeg");
        ...
    }
    
    //x264 编码: 直接使用x264编码库进行编码
    public native void Mp4CC(String h264_file); // 编码写入文件的路径
    public native void Mp4CE(byte[] array, int length); // camera preview data 传入jni,有jni端调用x264编码,然后写入文件
    public native void Mp4EE();     
    ...
    
    //ffmpeg + x264 编码:把x264编译成libx264.so, 并编译进ffmpeg,然后由ffmpeg通过统一的avcodec进行调用编码
    // 使用avcodec_register_all、avcodec_find_encoder、avcodec_alloc_context3等可直接产生编码操作环境、对象的方法(avcodeccontext->avcodec(avframe avpacket)
    //(当然也可使用构建更多一级的avformat_alloc_context/avformat_alloc_output_context2环境对象的方法 -- avformatcontext->avstream->avcodeccontext->avcodec(avframe avpacket))
    // 根据实际需要而定
    public native void Mp4FpgCC(String h264_file); 
    public native void Mp4FpgCE(byte[] array, int length);
    public native void Mp4FpgEE();
    ...
    

    //ffmpeg + x264 解码:
    直接调用avcodec_register_all、avcodec_find_encoder、avcodec_alloc_context3

    public native void Mp4FpgDCC0(String out_file, String h264_file); 

    // h264_file:传入需解码的.264文件,out_file:解码输出写入YUV文件,可用于测试, 此处Mp4FpgDCC0()只做初始化相关参数,不对文件进行操作

     public native void Mp4FpgDCE0(byte[] array, int length); 

    // camera preview data的mediacodec 编码数据传入jni,由 jni调用ffmpeg进行实时解码、解码yuv数据直接送java进行刷新显示(open gl yuv渲染)

     public native void Mp4FpgDEE0();

    JNI端:
    mp.c

    // 使用X264库进行编码 — X264的编解码跟X265的大致相同

    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4CC
     (JNIEnv *env, jclass clz, jstring h264_file)
    {
      int m_bitRate;
        const char* h264_file_n = (*env)->GetStringUTFChars(env, h264_file, NULL);
        h264s_file = fopen(h264_file_n, "wb");
        if(h264s_file == NULL){
           LOGI("              Mp4CC Could not open audio file             ");
           return;
        }
        yuv_size = (width * height *3)/2;
    
        x264_param_default_preset(&m_param,"fast","zerolatency"); //
        //x264_param_default(&m_param);
        //m_param.rc.i_bitrate = (int)m_bitRate/1000; 
        m_bitRate = 2000000;
        m_param.rc.i_rc_method = X264_RC_ABR;   //参数i_rc_method表示码率控制,CQP(恒定质量),CRF(恒定码率),ABR(平均码率)
                                                //CQP是以量化值作为目标,bitrate以输出文件大小为目标,CRF以“视觉质量”为目标。
        m_param.rc.i_vbv_max_bitrate=(int)((m_bitRate*1.2)/1000) ; // 平均码率模式下,最大瞬时码率,默认0(与-B设置相同)
      m_param.rc.i_bitrate = (int)m_bitRate/1000; 
        m_param.i_threads = 1;   // 工作线程数
      m_param.i_width   = width;
      m_param.i_height  = height;
      m_param.i_fps_num = fps;
      m_param.i_fps_den = 1;
      m_param.i_bframe  = 10; //两个参考帧之间B帧的数目
    
      //m_param.i_csp = (csp == 17) ? X264_CSP_NV12 : csp;//编码比特流的CSP,仅支持i420,色彩空间设置
      m_param.i_keyint_max    = 25; // 设置IDR关键帧的间隔
        m_param.b_intra_refresh = 1;
        m_param.b_annexb  = 1;        // 每个NAL单元前加一个四字节的前缀符
        //m_param.b_repeat_headers = 1;  // 重复SPS/PPS 放到关键帧前面  
    
      x264_param_apply_profile(&m_param,"baseline"); // profile "baseline" (baseline、main、high编码)
      encoder = x264_encoder_open(&m_param);
    
        x264_encoder_parameters(encoder, &m_param);    //设置编码器参数
        x264_picture_alloc(&pic_in, X264_CSP_I420, width, height); // 设定编码器视频输入格式为I420
    
        yuv_buffer = (uint8_t *)malloc(yuv_size);
    
        //pic_in.img.i_csp = X264_CSP_I420;
        //pic_in.img.i_plane = 3;
    
      pic_in.img.plane[0] = yuv_buffer;
      pic_in.img.plane[1] = pic_in.img.plane[0] + width * height;
      pic_in.img.plane[2] = pic_in.img.plane[1] + width * height / 4;
    
      run_ce = 0;
      i_pts  = 0;
    
      //* 获取允许缓存的最大帧数.
      int iMaxFrames = x264_encoder_maximum_delayed_frames(encoder);
      LOGI("              Mp4CE iMaxFrames = %d           ",iMaxFrames);
    
      //* 获取X264中缓冲帧数.
      //int iFrames = x264_encoder_delayed_frames(pX264Handle);
    
        (*env)->ReleaseStringUTFChars(env, h264_file, h264_file_n);
    }
    
    int nal_n;
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4CE
     (JNIEnv *env, jclass clz, jbyteArray data, jint size)
    {
        unsigned char *buf = (unsigned char *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);
        memcpy(yuv_buffer, buf, yuv_size);
    
        nnal  = 0;
        nal_n = 0;
        pic_in.i_pts = i_pts++;
        x264_encoder_encode(encoder, &nals, &nnal, &pic_in, &pic_out);
    
        x264_nal_t *nal;
        for (nal = nals; nal < nals + nnal; nal++) {
              nal_n++;
            fwrite(nal->p_payload, 1, nal->i_payload, h264s_file);
        }
        run_ce++;
        LOGI("              Mp4CE %d  %d           ",run_ce, nnal);
        (*env)->ReleaseByteArrayElements(env, data, (jbyte *)buf, 0);
    }
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4EE
     (JNIEnv *env, jclass clz)
    {
        while(1){
            int j;
            int ret = x264_encoder_encode(encoder, &nals, &nnal, NULL, &pic_out);
            if(ret<=0){
                break;
            }
            for (j=0; j < nnal; j++){
                fwrite(nals[j].p_payload, 1, nals[j].i_payload, h264s_file);
            }
        }
        LOGI("              Mp4EE end             ");
    
      x264_picture_clean(&pic_in);
      //x264_picture_clean(&pic_out);
      x264_encoder_close(encoder);
      fclose(h264s_file);
      //free(yuv_buffer);
    }
    
    // 使用ffmpeg的libx264库(作为ffmpeg内avcodec进行统一调用)进行编码
    AVCodec *ptrcodec;
    AVCodecContext *pctx= NULL;
    FILE *ptrf;
    FILE *ptrfo;
    AVFrame *ptrframe;
    AVPacket avpkt;
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4FpgCC
     (JNIEnv *env, jclass clz, jstring h264_file)
    {
            const char* h264_file_n = (*env)->GetStringUTFChars(env, h264_file, NULL); // 参数编码写入的文件路径
            filename = h264_file_n;
            int ret;
            int codec_id = AV_CODEC_ID_H264;
    
          //注册所有的编解码
            avcodec_register_all();
    
            LOGI("              Mp4FpgCC %s             ", filename);
    
            //根据AVCodecID查找 AVCodec
            ptrcodec = avcodec_find_encoder(codec_id);
            if (!ptrcodec) {
                LOGI("Codec not found\n");
                exit(1);
            }
    
          //根据AVCodec创建AVCodecContext
            pctx = avcodec_alloc_context3(ptrcodec);
            if (!pctx) {
                LOGI("Could not allocate video codec context\n");
                exit(1);
            }
    
            int bitrate = 1000;
            int br = 1000*1000;
            int fps = 25;
            //pctx->codec_type = CODEC_TYPE_VIDEO;       
          /*
            pctx->bit_rate = br;
            pctx->rc_min_rate =br;
            pctx->rc_max_rate = br;
            pctx->bit_rate_tolerance = br;
            pctx->rc_buffer_size=br;
            pctx->rc_initial_buffer_occupancy = pctx->rc_buffer_size*3/4;
            pctx->rc_buffer_aggressivity= (float)1.0;
            pctx->rc_initial_cplx= 0.5f;
          */
            //av_opt_set(pctx->priv_data,"crf", "1", AV_OPT_SEARCH_CHILDREN);
    
            //pctx->bit_rate = bitrate * 1000;
            //pctx->bit_rate_tolerance = 2000000; //表示有多少bit的视频流可以偏移出目前的设定.这里的"设定"是指的cbr或者vbr.
    
            //不设定bit_rate,则使用crf参数
            pctx->width  = width;
            pctx->height = height;
            pctx->time_base.den = fps;
            pctx->time_base.num = 1;
            pctx->gop_size = fps;    // * 10;
            //pctx->refs = 3;
            pctx->max_b_frames = 3;  //两个非B帧之间允许出现多少个B帧数,0表示不使用B帧,b 帧越多,图片越小
            //pctx->trellis = 2;
    
            //pctx->me_method = 8;
            //pctx->me_range = 64;//16;
    
            //pctx->me_subpel_quality = 7;
            //pctx->qmin = 10;        //介于0~31之间,值越小,量化越精细,图像质量就越高,而产生的码流也越长。
            //pctx->qmax = 51;
            //pctx->rc_initial_buffer_occupancy = 0.9;
            pctx->i_quant_factor = 1.0 / 1.40f; //p和i的量化系数比例因子,越接近1P帧越清楚、越优化, p的量化系数 = I帧的量化系数 * i_quant_factor + i_quant_offset
            // x4->params.rc.f_ip_factor = 1 / fabs(avctx->i_quant_factor);
    
            pctx->b_quant_factor = 1.30f;       //表示i/p与BQ值比例因子,值越大B帧劣化越严重,设置 i帧、p帧与B帧之间的量化系数q比例因子,这个值越大,B帧越不清楚
            //B帧量化系数 = 前一个P帧的量化系数q * b_quant_factor + b_quant_offset
            //pctx->chromaoffset = 0;
    
            //pctx->max_qdiff = 4;
            //pctx->qcompress = 0.6f; //浮点数值,表示在压制“容易压的场景”和“难压的场景”时,允许Q值之比值的变化范围。可选值是0.0-1.0。
            //pctx->qblur = 0.5f;     //浮点数,表示Q值的比例随时间消减的程度,取之范围是0.0-1.0,取0就是不消减
    
            pctx->pix_fmt = AV_PIX_FMT_YUV420P;
    
            //pctx->scenechange_threshold = 40;
            //pctx->flags |= CODEC_FLAG_LOOP_FILTER;
            //pctx->me_cmp = FF_CMP_CHROMA;
            //pctx->flags2 |= CODEC_FLAG_NORMALIZE_AQP;
            //pctx->keyint_min = 25;
    
            //pctx->rc_qsquish=1.0  //采用Qmin/Qmax的比值来限定和控制码率的方法。选1表示局部(即一个clip)采用此方法,选1表示全部采用。
            //pctx->level = 30;
            //pctx->b_frame_strategy = 2;
            //pctx->codec_tag = 7;
    
            /*
            //编码器预设
            AVDictionary *dictParam = 0;
            if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
            {
               av_dict_set(&dictParam,"preset","medium",0);
               av_dict_set(&dictParam,"tune","zerolatency",0);
               av_dict_set(&dictParam,"profile","main",0);
            }
            */
    
            if (codec_id == AV_CODEC_ID_H264) {
                //av_opt_set(pctx->priv_data, "preset", "slow", 0); //ultrafast
                av_opt_set(pctx->priv_data, "preset", "ultrafast", 0); //ultrafast
                av_opt_set(pctx->priv_data, "tune", "zerolatency", 0);
                //av_opt_set(pctx->priv_data, "profile", "main", 0);
            }
    
            /* open it */
            if (avcodec_open2(pctx, ptrcodec, NULL) < 0) {
                  LOGI("Could not open codec\n");
                exit(1);
            }
    
            ptrf = fopen(filename, "wb");
            if (!ptrf) {
                  LOGI("Could not open %s\n", filename);
                exit(1);
            }
    
            ptrframe = av_frame_alloc();
            if (!ptrframe) {
                  LOGI("Could not allocate video frame\n");
                exit(1);
            }
            ptrframe->format =  pctx->pix_fmt;
            ptrframe->width  =  pctx->width;
            ptrframe->height =  pctx->height;
    
            /* the image can be allocated by any means and av_image_alloc() is
             * just the most convenient way if av_malloc() is to be used */
            ret = av_image_alloc(ptrframe->data, ptrframe->linesize, pctx->width, pctx->height,
                    pctx->pix_fmt, 32);
            if (ret < 0) {
                LOGI("Could not allocate raw picture buffer\n");
                exit(1);
            }
    
          //根据I420格式来分配缓存区
            picture_size=  pctx->width*pctx->height*3/2; 
            picture_buf = (uint8_t *)av_malloc(picture_size);
    
            int y_size = pctx->width*pctx->height;
            LOGI(" w = %d, h = %d, picture_size= %d, y_size = %d\n", pctx->width, pctx->height, picture_size, y_size);
    
          //I420格式数据
            ptrframe->data[0] = picture_buf;              // Y
            ptrframe->data[1] = picture_buf+ y_size;      // U
            ptrframe->data[2] = picture_buf+ y_size*5/4;  // V
    
            av_init_packet(&avpkt);
            avpkt.data = NULL;    // packet data will be allocated by the encoder
            avpkt.size = 0;
    
            framecnt  = 0;
            ffmpeg_ce = 0;
            frame_pts = 0;
            (*env)->ReleaseStringUTFChars(env, h264_file, h264_file_n);
    }
    
    int total_st;
    int total_stream;
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4FpgCE
     (JNIEnv *env, jclass clz, jbyteArray data, jint size)
    {
          unsigned char *buf = (unsigned char *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);
            memcpy(picture_buf, buf, picture_size);
    
            int got_picture=0;
    
            av_init_packet(&avpkt);
          avpkt.data = NULL;    // packet data will be allocated by the encoder
            avpkt.size = 0;
    
            //视频时间戳
            //pkt.pts = inc++ *(1000/fps); //inc初始值为0,每次打完时间戳inc加1.
            //pkt.pts = m_nVideoTimeStamp++ * (pctx->time_base.num * 1000 / pctx->time_base.den);
    
            //Encode
            int ret = avcodec_encode_video2(pctx, &avpkt, ptrframe, &got_picture);
            if(ret < 0){
                LOGI("              Mp4FpgCE Failed to encode!            ");
                return ;
            }
            if (got_picture){  
              //if (pctx->coded_frame->pts != AV_NOPTS_VALUE) {
              //    avpkt.pts= av_rescale_q(pctx->coded_frame->pts, pctx->time_base, ost->st->time_base);
              //}
              //pkt.stream_index = video_st0->index;
                //avpkt.pts = frame_pts*1000*/25;
                avpkt.pts = frame_pts*(pctx->time_base.num*1000/pctx->time_base.den);
                frame_pts++;
    
              fwrite(avpkt.data, 1, avpkt.size, ptrf);
              av_packet_unref(&avpkt);
            }
    
            ffmpeg_ce++;
            (*env)->ReleaseByteArrayElements(env, data, (jbyte *)buf, 0);
    }
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4FpgEE
     (JNIEnv *env, jclass clz)
    {
        LOGI("              Mp4FpgEE              ");
        int got_output = 0;
        int ret = avcodec_encode_video2(pctx, &avpkt, NULL, &got_output);
        if (ret < 0) {
           LOGI("Error encoding frame\n");
           //exit(1);
        }
        if (got_output) {
           avpkt.pts = frame_pts*(pctx->time_base.num*1000/pctx->time_base.den);
           frame_pts++;
    
           fwrite(avpkt.data, 1, avpkt.size, ptrf);
           av_packet_unref(&avpkt);
        }
        /* add sequence end code to have a real mpeg file */
        fwrite(endcode, 1, sizeof(endcode), ptrf);
        fclose(ptrf);
    
        avcodec_close(pctx);
        av_free(pctx);
        av_freep(&ptrframe->data[0]);
        av_frame_free(&ptrframe);
    
    }
    
    //使用ffmpeg的libx264库(作为ffmpeg内avcodec进行统一调用)进行解码,
    uint8_t* packet_buf;
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4FpgDCC0
     (JNIEnv *env, jclass clz, jstring outfile_name, jstring h264_file)
    {
        const char* h264_file_n = (*env)->GetStringUTFChars(env, h264_file, NULL); //h264_file 待解码.264文档
        const char* h264_file_o = (*env)->GetStringUTFChars(env, outfile_name, NULL);
    
        filename = h264_file_n;
        outfilename = h264_file_o;
        int ret, i;
        int codec_id = AV_CODEC_ID_H264;
        avcodec_register_all();
    
        // find the mpeg1 video encoder
        ptrcodec = avcodec_find_decoder(AV_CODEC_ID_H264);
        if (!ptrcodec) {
            LOGI("Codec not found\n");
             exit(1);
        }
      pctx = avcodec_alloc_context3(ptrcodec);
      if (!pctx) {
            LOGI("Could not allocate video codec context\n");
            exit(1);
        }
        if (ptrcodec->capabilities & AV_CODEC_CAP_TRUNCATED)
            pctx->flags |= AV_CODEC_FLAG_TRUNCATED; // we do not send complete frames
        /* open it */
        if (avcodec_open2(pctx, ptrcodec, NULL) < 0) {
            LOGI("Could not open codec\n");
            exit(1);
      }  
        ptrframe = av_frame_alloc();
        if (!ptrframe) {
            LOGI("Could not allocate video frame\n");
            exit(1);
        }
        av_init_packet(&avpkt);
    
        frame_count  = 0;
        ffmpeg_ce = 0;
      frame_pts = 0;
    
        packet_buf = (unsigned char *)malloc(320*240/2);
        //(uint8_t *)av_malloc(pctx->width*pctx->height/2); //pctx->width*pctx->height*3/2
    
        LOGI("        Mp4FpgDCC0 end     \n");
    
        (*env)->ReleaseStringUTFChars(env, h264_file, h264_file_n);
        (*env)->ReleaseStringUTFChars(env, outfile_name, h264_file_o);
    }
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4FpgDCE0
     (JNIEnv *env, jobject obj, jbyteArray data, jint size)
    {
        int len;
        unsigned char *buf = (unsigned char *)(*env)->GetByteArrayElements(env, data, JNI_FALSE);
      memcpy(packet_buf, buf, size);
    
      got_frame = 0;
    
      av_init_packet(&pkt);
      pkt.size = size;
      pkt.data = packet_buf;
      while (pkt.size > 0) 
        {
            len = avcodec_decode_video2(pctx, ptrframe, &got_frame, &pkt);
            if (pkt.data) {
                pkt.size -= len;
                pkt.data += len;
            }
            if (len < 0) {
                LOGI("Error while decoding frame %d\n", frame_count);
                //av_packet_unref(&pkt);
                break;
            }
            if(got_frame){
                if(frame_size==0){
                    frame_size = ptrframe->width*ptrframe->height;
                    frame_size_l = frame_size*3/2;
                }
                if(!temp_store_a && frame_size>0){
                    temp_store_a = (unsigned char *)malloc(frame_size*3/2);
                    LOGI(" Saving frame %d, %d, %d\n",frame_count, frame_size, frame_size_l);
                }
    
                pgm_save2(ptrframe->data[0], ptrframe->linesize[0], ptrframe->width, ptrframe->height, temp_store_a);
                pgm_save2(ptrframe->data[1], ptrframe->linesize[1], ptrframe->width/2, ptrframe->height/2, temp_store_a+frame_size);
              pgm_save2(ptrframe->data[2], ptrframe->linesize[2], ptrframe->width/2, ptrframe->height/2, temp_store_a+frame_size*5/4);
    
                if(method1==0){
                    //1 . 找到java代码的 class文件
                    // jclass      (*FindClass)(JNIEnv*, const char*);
                    jclass dpclazz = (*env)->FindClass(env,"com/example/mymp4v2h264/MainActivity0"); //com_example_mymp4v2h264_MainActivity0_Mp4FpgDCE0
                    if(dpclazz==0){
                        return;
                    }
                    LOGI("find class ");
    
                    //2 寻找class里面的方法
                    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
                    method1 = (*env)->GetMethodID(env,dpclazz,"updateYUV","([BII)V");
                    if(method1==0){
                        LOGI("find method1 error");
                        return;
                    }
                    LOGI("find method1 ");
                }else{
                    //3 .调用这个方法,把解码数据上传到java端的缓存列表中刷新显示(java使用的是opengl es yuv纹理刷新)
                    // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
                     jbyteArray result = (*env)->NewByteArray(env, frame_size_l);
                   (*env)->SetByteArrayRegion(env, result, 0, frame_size_l, (jbyte *)temp_store_a); //temp_store_a
    
                   (*env)->CallVoidMethod(env, obj, method1, result, ptrframe->width, ptrframe->height);
                }
                frame_count++;
            }
        }
    
        (*env)->ReleaseByteArrayElements(env, data, (jbyte *)buf, 0);
    }
    
    JNIEXPORT void JNICALL Java_com_example_mymp4v2h264_MainActivity0_Mp4FpgDEE0
     (JNIEnv *env, jclass clz)
    {
        LOGI("              Mp4FpgDEE0              ");
        avcodec_close(pctx);
        av_free(pctx);
        av_frame_free(&ptrframe);
    }
    展开全文
  • x264编解码

    2011-12-19 16:44:09
     关于生成的x264解码,这里用ffmpeg(就是现在的libav):  1:下载ffmpeg配置好相应VC环境(我用的是VC6);  2:下载ffmpegtutorial,这里有学习ffmpeg的例程,我们可以直接用Tutorial02就可以;  3:...

    一 编码:

        网上有很多相关文章,这里不多讲了;

     

    二:解码(这里解码为bmp):

        关于生成的x264的解码,这里用ffmpeg(就是现在的libav):

       1:下载ffmpeg配置好相应VC环境(我用的是VC6);

       2:下载ffmpegtutorial,这里有学习ffmpeg的例程,我们可以直接用Tutorial02就可以;

       3:修改Tutorial02中的解码部分,将相应的数据改为bmp数据,可以参考下面文章:

    《利用FFmpeg将视频文件生成bmp图像帧》(转载自网络)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <windows.h>

    #include "avformat.h"
    #include "avcodec.h"
    #include "swscale.h"


    #pragma comment (lib, "avcodec.lib")
    #pragma comment (lib, "avformat.lib")
    #pragma comment (lib, "avutil.lib")
    #pragma comment (lib, "swscale.lib")


    #ifndef _WINGDI_
    #define _WINGDI_

    typedef struct tagBITMAPFILEHEADER {
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
    } BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

    typedef struct tagBITMAPINFOHEADER{
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
    } BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

    #endif

    void SaveAsBMP (AVFrame *pFrameRGB, int width, int height, int index, int bpp)
    {
    char buf[5] = {0};
    BITMAPFILEHEADER bmpheader;
    BITMAPINFOHEADER bmpinfo;
    FILE *fp;

    char filename[20] = "R:\\test";
    _itoa (index, buf, 10);
    strcat (filename, buf);
    strcat (filename, ".bmp");

    if ( (fp=fopen(filename,"wb+")) == NULL )
    {
    printf ("open file failed!\n");
    return;
    }

    bmpheader.bfType = 0x4d42;
    bmpheader.bfReserved1 = 0;
    bmpheader.bfReserved2 = 0;
    bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;

    bmpinfo.biSize = sizeof(BITMAPINFOHEADER);
    bmpinfo.biWidth = width;
    bmpinfo.biHeight = height;
    bmpinfo.biPlanes = 1;
    bmpinfo.biBitCount = bpp;
    bmpinfo.biCompression = BI_RGB;
    bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;
    bmpinfo.biXPelsPerMeter = 100;
    bmpinfo.biYPelsPerMeter = 100;
    bmpinfo.biClrUsed = 0;
    bmpinfo.biClrImportant = 0;

    fwrite (&bmpheader, sizeof(bmpheader), 1, fp);
    fwrite (&bmpinfo, sizeof(bmpinfo), 1, fp);
    fwrite (pFrameRGB->data[0], width*height*bpp/8, 1, fp);

    fclose(fp);
    }

    int main (void)
    {
    unsigned int i = 0, videoStream = -1;
    AVCodecContext *pCodecCtx;
    AVFormatContext *pFormatCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB;
    struct SwsContext *pSwsCtx;
    const char *filename = "test.avi";
    AVPacket packet;
    int frameFinished;
    int PictureSize;
    uint8_t *buf;

    av_register_all();

    if ( av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL) != 0 )
    {
    printf ("av open input file failed!\n");
    exit (1);
    }

    if ( av_find_stream_info(pFormatCtx) < 0 )
    {
    printf ("av find stream info failed!\n");
    exit (1);
    }

    for ( i=0; i<pFormatCtx->nb_streams; i++ )
    if ( pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
    {
    videoStream = i;
    break;
    }

    if (videoStream == -1)
    {
    printf ("find video stream failed!\n");
    exit (1);
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;

    pCodec = avcodec_find_decoder (pCodecCtx->codec_id);

    if (pCodec == NULL)
    {
    printf ("avcode find decoder failed!\n");
    exit (1);
    }



    if ( avcodec_open(pCodecCtx, pCodec)<0 )
    {
    printf ("avcode open failed!\n");
    exit (1);
    }



    pFrame = avcodec_alloc_frame();
    pFrameRGB = avcodec_alloc_frame();

    if ( (pFrame==NULL)||(pFrameRGB==NULL) )
    {
    printf("avcodec alloc frame failed!\n");
    exit (1);
    }

    PictureSize = avpicture_get_size (PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

    buf = av_malloc(PictureSize);

    if ( buf == NULL )
    {
    printf( "av malloc failed!\n");
    exit(1);
    }

    avpicture_fill ( (AVPicture *)pFrameRGB, buf, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

    pSwsCtx = sws_getContext (pCodecCtx->width,
    pCodecCtx->height,
    pCodecCtx->pix_fmt,
    pCodecCtx->width,
    pCodecCtx->height,
    PIX_FMT_BGR24,
    SWS_BICUBIC,
    NULL, NULL, NULL);

    i = 0;

    while(av_read_frame(pFormatCtx, &packet) >= 0)
    {
    if(packet.stream_index==videoStream)
    {
    avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);

    if(frameFinished)
    {
    //反转图像
    //*(pFrame->data[0]) = pCodecCtx->width * (pCodecCtx->height-1);
    //pFrame ->linesize[0] = -(pCodecCtx->height);
    pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height - 1);
    pFrame->linesize[0] *= -1;
    pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height / 2 - 1);
    pFrame->linesize[1] *= -1;
    pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height / 2 - 1);
    pFrame->linesize[2] *= -1;

    sws_scale (pSwsCtx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

    SaveAsBMP (pFrameRGB, pCodecCtx->width, pCodecCtx->height, i++, 24);
    }
    }
    av_free_packet(&packet);
    }

    sws_freeContext (pSwsCtx);
    av_free (pFrame);
    av_free (pFrameRGB);
    avcodec_close (pCodecCtx);
    av_close_input_file (pFormatCtx);

    return 0;
    }

     

     

    总结:解码生成bmp图像是为了可以更好的显示图像,方便与MFC结合。

              x264和ffmpeg和VC结合最简单的方法就是,分别生成相关的dll然后MFC调用显示,

             当然可以在MFC中配置这两个工程的相关条件,切记:MFC中包含C的头文件,要extern "C" {},

             如:

            

    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>

    #include <libsdl/SDL.h>
    #include <libsdl/SDL_thread.h>
    }

     下面给出 Tutorial02 与 MFC结合的代码:

     配置好相应的 文件路径,Lib库文件路径,dll文件路径(直接将bin目下的所有文件copy到当前工程中就可以了),以下是源码:


    #define _SDL_config_win32_h //解决重定义冲突;

    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>

    #include <libsdl/SDL.h>
    #include <libsdl/SDL_thread.h>
    }


    #if defined __MINGW32__ || defined WIN32
    #undef main /* Prevents SDL from overriding main() */
    #endif

    #include <stdio.h>
    void CMfcffmpegDlg::OnButtonFFmpeg()
    {
     // TODO: Add your control notification handler code here
     
      AVFormatContext *pFormatCtx;
      int             i, videoStream;
      AVCodecContext  *pCodecCtx;
      AVCodec         *pCodec;
      AVFrame         *pFrame;
      AVPacket        packet;
      int             frameFinished;
      float           aspect_ratio;

      SDL_Overlay     *bmp;
      SDL_Surface     *screen;
      SDL_Rect        rect;
      SDL_Event       event;

     AVPicture pict; 

     static int sws_flags = SWS_BICUBIC;
         struct SwsContext *img_convert_ctx;

     int  argc = 2;
      char * argv = "f:\\video.avi";
     
     
      // Register all formats and codecs
      av_register_all();
     
      if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
        exit(1);
      }

      // Open video file
      if(av_open_input_file(&pFormatCtx, argv, NULL, 0, NULL)!=0)
        return ; // Couldn't open file
     
      // Retrieve stream information
      if(av_find_stream_info(pFormatCtx)<0)
        return ; // Couldn't find stream information
     
      // Dump information about file onto standard error
      dump_format(pFormatCtx, 0, argv, 0);
     
      // Find the first video stream
      videoStream=-1;
      for(i=0; i<pFormatCtx->nb_streams; i++)
        if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
     {
          videoStream=i;
          break;
        }
      if(videoStream==-1)
        return ; // Didn't find a video stream
     
      // Get a pointer to the codec context for the video stream
      pCodecCtx=pFormatCtx->streams[videoStream]->codec;
     
      // Find the decoder for the video stream
      pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
      if(pCodec==NULL)
      {
        fprintf(stderr, "Unsupported codec!\n");
        return ; // Codec not found
      }
     
      // Open codec
      if(avcodec_open(pCodecCtx, pCodec)<0)
        return ; // Could not open codec
     
      // Allocate video frame
      pFrame=avcodec_alloc_frame();

      // Make a screen to put our video
    #ifndef __DARWIN__
            screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
    #else
            screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
    #endif
      if(!screen) {
        fprintf(stderr, "SDL: could not set video mode - exiting\n");
        exit(1);
      }
     
      // Allocate a place to put our YUV image on that screen
      bmp = SDL_CreateYUVOverlay(pCodecCtx->width,
         pCodecCtx->height,
         SDL_YV12_OVERLAY,
         screen);


      // Read frames and save first five frames to disk
      i=0;
      while(av_read_frame(pFormatCtx, &packet)>=0)
      {
        // Is this a packet from the video stream?
        if(packet.stream_index==videoStream)
     {
          // Decode video frame
          avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);     
         
          // Did we get a video frame?
          if(frameFinished)
       {
        SDL_LockYUVOverlay(bmp);
       
        pict.data[0] = bmp->pixels[0];
        pict.data[1] = bmp->pixels[2];
        pict.data[2] = bmp->pixels[1];
       
        pict.linesize[0] = bmp->pitches[0];
        pict.linesize[1] = bmp->pitches[2];
        pict.linesize[2] = bmp->pitches[1];

    #if 0
     // Convert the image into YUV format that SDL uses
     img_convert(&pict, PIX_FMT_YUV420P,
                        (AVPicture *)pFrame, pCodecCtx->pix_fmt,
          pCodecCtx->width, pCodecCtx->height);
    #else
      /*   img_convert(&pict, dst_pix_fmt,
            (AVPicture *)pFrame, is->video_st->codec->pix_fmt,
            is->video_st->codec->width, is->video_st->codec->height);
      */


           img_convert_ctx = sws_getContext( pCodecCtx->width,
               pCodecCtx->height,
                                                pCodecCtx->pix_fmt,
                                                pCodecCtx->width,
               pCodecCtx->height,
                                                PIX_FMT_YUV420P,
                                                sws_flags, NULL, NULL, NULL);

           sws_scale (img_convert_ctx, pFrame->data, pFrame->linesize,
               0, pCodecCtx->height,
               pict.data,pict.linesize);
           sws_freeContext(img_convert_ctx);
    #endif
     SDL_UnlockYUVOverlay(bmp);
     
     rect.x = 0;
     rect.y = 0;
     rect.w = pCodecCtx->width;
     rect.h = pCodecCtx->height;
     SDL_DisplayYUVOverlay(bmp, &rect);
         
          }
        }
       
        // Free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
        SDL_PollEvent(&event);
        switch(event.type) {
        case SDL_QUIT:
          SDL_Quit();
          exit(0);
          break;
        default:
          break;
        }
     //SDL_Delay(40);
      }
     
      // Free the YUV frame
      av_free(pFrame);
     
      // Close the codec
      avcodec_close(pCodecCtx);
     
      // Close the video file
      av_close_input_file(pFormatCtx);
     

    }

     注:直接到ffmpeg网站下载x264解码VC工程源代码,最方便了;

    展开全文
  • x264 ffmeg视频编解码

    2015-05-08 11:07:24
    求助 我用x264工程视频编码 提取到了量化后的DCT系数,用ffmeg进行解码时在宏块熵解码时得到了还未反量化的系数。 这两个系数应该是相同的,但是发现有的相同有的不同,为什么会这样呢?而且未量化的系数存在coeff...
  • X264 ffmpeg 1、码率: 码流(Data Rate),是指视频文件在单位时间内使用的数据流量 三种可选的码率控制方法(bitrate, CQP,CRF), 选择的顺序是 bitrate > QP > CRF QP是固定量化参数,bitrate是固定文件大小,...

    X264 ffmpeg

    1、码率:
    码流(Data Rate),是指视频文件在单位时间内使用的数据流量

    三种可选的码率控制方法(bitrate, CQP,CRF), 选择的顺序是 bitrate > QP > CRF
    QP是固定量化参数,bitrate是固定文件大小,crf则是固定“质量”,abr(ABR平均码率,VBR是动态码率。CBR是静态码率。),crf(限制码率),cqp(固定质量)
    –qp, –bitrate, –crf
    X264_RC_CQP 动态比特率
    X264_RC_CRF 恒定比特率
    X264_RC_ABR 平均比特率

    480P分辨率仅需1-2M的码率即可达到远超AVI格式的画质,一部电影大小仅为1G;720P分辨率也只需要3-4M码率和2-3G的体积
    720P ,码流大约在2到4或者5M;
    1080P 大约在4到8M

    高码率编码(如下 中码率 = 高码率/2, 极高码率 = 高码率*2,低码率 = 中码率/2
    480P –> 250kbps 720P –> 500kbps 1080P –> 1mps 极低码率
    480P –> 500kbps 720P –> 1mbps 1080P –> 2mps 低码率
    480P –> 1mbps 720P –> 2mbps 1080P –> 4mps 中码率
    480P –> 2mbps 720P –> 4mbps 1080P –> 8mps 高码率
    480P –> 4mbps 720P –> 8mbps 1080P –> 16mps 极高码率
    通常高编码码率:
    480P 720X480 1800Kbps
    720P 1280X720 3500Kbps
    1080P 1920X1080 8500Kbps

    ffmpeg中编码器参数设定:

    1.如果设置了AVCodecContext中bit_rate的大小,则采用abr的控制方式;
    2.如果没有设置AVCodecContext中的bit_rate,则默认按照crf方式编码,crf默认大小为23(此值类似于qp值,同样表示视频质量);
    3.如果用户想自己设置,则需要借助av_opt_set函数设置AVCodecContext的priv_data参数。下面给出三种控制方式的实现代码:
    int bpsValue; //码流控制方式的对应值
    int bpsMode; //码流控制方式,0表示平均码率(abr),1表示固定码率(crf),2表示固定质量(cqp)
    AVCodecContext* pCodecCtx;
    …….

    //码率控制方式
    string modeValue = int2String(bpsValue);
    switch (bpsMode) {
    case 0:
    pCodecCtx->bit_rate = bpsValue*1000;
    break;
    case 1:
    av_opt_set(pCodecCtx->priv_data,”crf”,modeValue.c_str(),AV_OPT_SEARCH_CHILDREN);
    break;
    case 2:
    av_opt_set(pCodecCtx->priv_data,”qp”,modeValue.c_str(),AV_OPT_SEARCH_CHILDREN);
    break;
    default:
    pCodecCtx->bit_rate = bpsValue;
    break;
    }

    ffmpeg中VBR(可变率控制)的设置:
    c->flags |= CODEC_FLAG_QSCALE;
    c->rc_min_rate =min;
    c->rc_max_rate = max;
    c->bit_rate = br;

    ffmpeg中CBR(固定码率控制)的设置:
    c->bit_rate = br;
    c->rc_min_rate =br;
    c->rc_max_rate = br;
    c->bit_rate_tolerance = br;
    c->rc_buffer_size=br;
    c->rc_initial_buffer_occupancy = c->rc_buffer_size*3/4;
    c->rc_buffer_aggressivity= (float)1.0;
    c->rc_initial_cplx= 0.5;

      //AVCodecContext 相当于虚基类,需要用具体的编码器实现来给他赋值
        pCodecCtxEnc = video_st->codec;
    
        //编码器的ID号,这里我们自行指定为264编码器,实际上也可以根据video_st里的codecID 参数赋值
        pCodecCtxEnc->codec_id = AV_CODEC_ID_H264;
    
        //编码器编码的数据类型
        pCodecCtxEnc->codec_type = AVMEDIA_TYPE_VIDEO;
    
        //目标的码率,即采样的码率;显然,采样码率越大,视频大小越大
        pCodecCtxEnc->bit_rate = 200000;
    
        //固定允许的码率误差,数值越大,视频越小
        pCodecCtxEnc->bit_rate_tolerance = 4000000;
    
        //编码目标的视频帧大小,以像素为单位
        pCodecCtxEnc->width = 640;
        pCodecCtxEnc->height = 480;
    
        //帧率的基本单位,我们用分数来表示,
        //用分数来表示的原因是,有很多视频的帧率是带小数的eg:NTSC 使用的帧率是29.97
        pCodecCtxEnc->time_base.den = 30;
        pCodecCtxEnc->time_base = (AVRational){1,25};
        pCodecCtxEnc->time_base.num = 1;
    
        //像素的格式,也就是说采用什么样的色彩空间来表明一个像素点
        pCodecCtxEnc->pix_fmt = PIX_FMT_YUV420P;
    
        //每250帧插入1个I帧,I帧越少,视频越小
    
        [cpp] view plain copy
        pCodecCtxEnc->gop_size = 250;
    
        //两个非B帧之间允许出现多少个B帧数
        //设置0表示不使用B帧
        //b 帧越多,图片越小
        pCodecCtxEnc->max_b_frames = 0;
    
        //运动估计
        pCodecCtxEnc->pre_me = 2;
    
        //设置最小和最大拉格朗日乘数
        //拉格朗日乘数 是统计学用来检测瞬间平均值的一种方法
        pCodecCtxEnc->lmin = 1;
        pCodecCtxEnc->lmax = 5;
    
        //最大和最小量化系数
        pCodecCtxEnc->qmin = 10;
        pCodecCtxEnc->qmax = 50;
    
        //因为我们的量化系数q是在qmin和qmax之间浮动的,
        //qblur表示这种浮动变化的变化程度,取值范围0.0~1.0,取0表示不削减
        pCodecCtxEnc->qblur = 0.0;
    
        //空间复杂度的masking力度,取值范围 0.0-1.0
        pCodecCtxEnc->spatial_cplx_masking = 0.3;
    
        //运动场景预判功能的力度,数值越大编码时间越长
        pCodecCtxEnc->me_pre_cmp = 2;
    
        //采用(qmin/qmax的比值来控制码率,1表示局部采用此方法,)
        pCodecCtxEnc->rc_qsquish = 1;
    
        //设置 i帧、p帧与B帧之间的量化系数q比例因子,这个值越大,B帧越不清楚
        //B帧量化系数 = 前一个P帧的量化系数q * b_quant_factor + b_quant_offset
        pCodecCtxEnc->b_quant_factor = 1.25;
    
        //i帧、p帧与B帧的量化系数便宜量,便宜越大,B帧越不清楚
        pCodecCtxEnc->b_quant_offset = 1.25;
    
        //p和i的量化系数比例因子,越接近1,P帧越清楚
        //p的量化系数 = I帧的量化系数 * i_quant_factor + i_quant_offset
        pCodecCtxEnc->i_quant_factor = 0.8;
        pCodecCtxEnc->i_quant_offset = 0.0;
    
        //码率控制测率,宏定义,查API
        pCodecCtxEnc->rc_strategy = 2;
    
        //b帧的生成策略
        pCodecCtxEnc->b_frame_strategy = 0;
    
        //消除亮度和色度门限
        pCodecCtxEnc->luma_elim_threshold = 0;
        pCodecCtxEnc->chroma_elim_threshold = 0;
    
        //DCT变换算法的设置,有7种设置,这个算法的设置是根据不同的CPU指令集来优化的取值范围在0-7之间
        pCodecCtxEnc->dct_algo = 0;
    
        //这两个参数表示对过亮或过暗的场景作masking的力度,0表示不作
        pCodecCtxEnc->lumi_masking = 0.0;
        pCodecCtxEnc->dark_masking = 0.0;
    

    x264编码时延问题:
    a:
    avcodec_encode_video2函数输出的延时仅仅跟max_b_frames的设置有关,进行实时编码,将max_b_frames设置为0便没有编码延时了

    b:使用264的API设置编码速度
    // ultrafast,superfast, veryfast, faster, fast, medium slow, slower, veryslow, placebo. 这是x264编码速度的选项
    av_opt_set(m_context->priv_data,”preset”,”ultrafast”,0);

    2、ffmpeg中的时间单位

    AV_TIME_BASE
    ffmpeg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,
    比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:

    #define         AV_TIME_BASE   1000000

    AV_TIME_BASE_Q
    ffmpeg内部时间基的分数表示,实际上它是AV_TIME_BASE的倒数。从它的定义能很清楚的看到这点:

    #define         AV_TIME_BASE_Q   (AVRational){1, AV_TIME_BASE}

    AVRatioal的定义如下:

    typedef struct AVRational{
    int num; //numerator
    int den; //denominator
    } AVRational;

    ffmpeg提供了一个把AVRatioal结构转换成double的函数:

    static inline double av_q2d(AVRational a){
    //Convert rational to double.
    //@param a rational to convert
        return a.num / (double) a.den;
    }

    现在可以根据pts来计算一桢在整个视频中的时间位置:
    timestamp(秒) = pts * av_q2d(st->time_base)

    计算视频长度的方法:
    time(秒) = st->duration * av_q2d(st->time_base)这里的st是一个AVStream对象指针。

    时间基转换公式
    timestamp(ffmpeg内部时间戳) = AV_TIME_BASE * time(秒)
    time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg内部时间戳)
    所以当需要把视频跳转到N秒的时候可以使用下面的方法:

    int64_t timestamp = N * AV_TIME_BASE;
    2
    av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);

    ffmpeg同样为我们提供了不同时间基之间的转换函数:
    int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
    这个函数的作用是计算a * bq / cq,来把时间戳从一个时基调整到另外一个时基。在进行时基转换的时候,
    我们应该首选这个函数,因为它可以避免溢出的情况发生。

    1. 视频时间戳
      pkt.pts= inc++ *(1000/fps); 其中inc是一个静态的,初始值为0,每次打完时间戳inc加1.
      ffmpeg代码:
      pkt.pts= m_nVideoTimeStamp++ * (pctx->time_base.num * 1000 / pctx->time_base.den);

    2. 音频时间戳
      pts = inc++ * (frame_size * 1000 / sample_rate)
      ffmpeg代码:
      pkt.pts= m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);

    采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。

    调用av_rescale_q(pts, timebase1, timebase2)或者av_rescale_q_rnd(pts, timebase1, timebase2, XX)时,其实就是按照下面的公式计算:
    x = pts * (timebase1.num / timebase1.den )* (timebase2.den / timebase2.num); //这个x就是转换后的时间戳。

    展开全文
  • x264编解码器 - Intel IPP

    2013-03-31 01:53:17
    1 下载intel xe composer 2011 评估版本: http://software.intel.com/en-us/articles/intel-composer-xe/ 需要填写注册信息,申请一个评估... 2 raw 264转换工具 ...http://www.videolan.org/developers/x264.html
  • 常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码 我们已经知道,H.264是一种视频压缩标准,其只规定了符合标准的码流的格式,以及码流中各个语法元素的解析方法。H.264标准并未规定编码器的实现或...
  • H264编解码框图

    2018-04-16 22:10:54
    H264 编解码框架简单介绍 阅读完H264/AVC 编解码器的介绍,脑海中仅仅是留下下面三条: 1、H264并没有明白规定一个编解码器怎样实现,仅仅是规定了一个编码后的视频比特流的句法,和该比特流的解码方法,这个与...
  • x264编解码代码分析

    2013-12-10 14:50:46
    (1) x264_param_default( x264_param_t *param )  作用: 对编码器进行参数设定 cqm:量化表相关信息  csp:   量化表相关信息里的memset( param->cqm_4iy, 16, 16 );  
  • 《H.264/AVC视频编解码技术详解》视频教程已经在“CSDN学院”上线,视频中详述了H.264的背景、标准协议和实现,并通过一个实战工程的形式对H.264的标准进行解析和实现,欢迎观看!“纸上得来终觉浅,绝知此事要躬行...
  • H.264编解码

    2019-07-14 21:32:25
    运算复杂,无优化,编解码难以实时 常用于科学研究领域 http://iphome.hhi.de/suehring/tml/ 旧版本下载 http://iphome.hhi.de/suehring/tml/download/old_jm/ X264 著名的H.264开源编码器,由开源组织VideoLan...
  • H264 编解码框架简介

    2017-05-11 10:21:11
    阅读完H264/AVC 编解码器的介绍,脑海中只是留下以下三条: 1、H264并没有明确规定一个编解码器如何实现,只是规定了一个编码后的视频比特流的句法,和该比特流的解码方法,这个与MPEG 相似。 2、H264和以前的标准...
  • 简介视频编解码网上介绍不少,不过大部分比较零散,经过近一年的学习,整理笔记反馈到社区,也加深一遍自己的理解。欢迎指正错误和讨论学习,谢谢浏览。H264是一种视频压缩标准。 根据不同类别,编码器会使用不同...
  • TX1上H.264编解码验证方案: TX1上使用GStreamer获取板载摄像头视频内容并压缩为H.264流服务器,再使用OpenCV+GStreamer接受H.264视频流。(TX1上H.264编解码验证方案:OpenCV+GStreamer+H.264编解码) ...
  • 视频编解码是一个非常消耗系统资源的过程,而树莓派自带了H.264的硬件编解码器,本文讲介绍在树莓派上配置FFmpeg使其支持硬件编解码器并编译安装的过程,所使用到的所有库文件,包括x264和ffmpeg源码及其配置文件,...
  • 公司项目原因,接触了一下视频流H264编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解。该方法比较通用,但是占用CPU资源,编解码效率不高。一般系统都会提供GPU或者专用...
  • H264 编解码协议详解

    2019-04-14 10:38:54
    1.、什么是 H264? H264 是 MPEG-4 标准所定义的最新编码格式,同时也是技术含量最高、代表最新技术水平的视频编码格式之一,标准写法应该是H.264 H264 视频格式是经过有损压缩的,但在技术上尽可能做的降低存储...
  • 整理自殷文杰的博客 ...【H.264/AVC视频编解码技术详解】一....【H.264/AVC视频编解码技术详解】二....【H.264/AVC视频编解码技术... 常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码 【H.264/AVC视频
  • 在这篇文章里,我们将介绍在H.264编解码标准的基础上,实现的各主流编解码器。我们前面曾多次提到,H.264只是一个编解码的标准协议,它同MPEG-1和MPEG-2一样,协议里并没有规定编解码器的具体结构和实现方法。但是...
1 2 3 4 5 ... 20
收藏数 47,603
精华内容 19,041
关键字:

x264 编解码