学习笔记(一)(x264编码流程)


<script type=text/javascript></script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript> </script> <script> window.google_render_ad(); </script>

经过一段时间的学习我对h264也有了一个初步的大体的了解,今天在这里说一下h264中x264的开源code的编码的解析并附一张我自己画的流程图便于大家理解,又不对的地方清大家指教一二,偶必定三顾茅庐寻得真理。:)

首先我们 进入x264.c中的main函数.
刚开始是读取默认参数,如果你设置了参数的话会修改param的.
      i_ret = Encode( &param, fin, fout );
这条语句使过程进入x264.c中的Encode函数.(这个函数就是x264的编码程序)
                                       X.264_encode函数.
 A      i_frame_total = 0;
if( !fseek( fyuv, 0, SEEK_END ) )
     {
        int64_t i_size = ftell( fyuv );
        fseek( fyuv, 0, SEEK_SET );
        i_frame_total = i_size / ( param->i_width * param->i_height * 3 / 2 )
}

这段调用了fseek()函数,对输入的视频文件计算其总帧数。

B.     函数 h = x264_encoder_open( param )对不正确的参数进行修改,并对各结构体参数和cabac编码,预测等需要的参数进行初始化.然后才能进行下一步的编码。

C.     函数 pic = x264_picture_new( h );定义在/CORE/common.c中.

此函数的作用是分给能容纳sizeof(x264_picture_t)字节数的空间,然后进行初始化.
      这里说明一下x264_picture_t和x264_frame_t的区别.前者是说明一个视频序列中每帧的特点.后者存放每帧实际的象素值.

D.       调用fread()函数一次读入一帧,分亮度和色度分别读取.这里要看到c语言中的File文件有一个文件位置指示器,调用fread()函数会使文件指示器自动移位,这就是一帧一帧读取的实现过程.

for( i_frame = 0, i_file = 0; i_ctrl_c == 0 ; i_frame++ )
    {
        int         i_nal;
        x264_nal_t  *nal;
 int         i;
/* read a frame */
        if( fread( pic->plane[0], 1, param->i_width * param->i_height, fyuv ) <= 0 ||
            fread( pic->plane[1], 1, param->i_width * param->i_height / 4, fyuv ) <= 0 ||
            fread( pic->plane[2], 1, param->i_width * param->i_height / 4, fyuv ) <= 0 )
        {
            break;
        }这里文件已经指示器发生了位移
          if( x264_encoder_encode( h, &nal, &i_nal, pic ) < 0 )
        {
            fprintf( stderr, “x264_encoder_encode failed/n” );
        }
        ……
        }

E.      进入x264_encoder_encode( h, &nal, &i_nal, pic )函数,该函数定义在/Enc/encoder.c中.

函数中先定义了如下三个参数:
                int     i_nal_type;   nal存放的数据类型, 可以是sps,pps等多种.                  
                int     i_nal_ref_idc;  nal的优先级,nal重要性的标志位.
                int     i_slice_type;   slice的类型的

这里先说明一下:我们假设一个视频序列如下:
                   I  B  B  P  B B   P
         我们编码是按I  P  B  B  P  B  B的顺序,这就是frame的编号
         但是编码器如何来区分他们并把他们重新排序呢?

我们来看看编码器是如何区分读入的一帧是I帧,P帧,或者B帧?

以I   B   B   P   B B   P为例.
 
        if( h->i_frame % (h->param.i_iframe * h->param.i_idrframe) == 0 ){
                 确定这是立即刷新片.
         }
           if( h->param.i_bframe > 0 )//判断h是否为B帧然后对其进行下一步操作.
          我们编完I帧后碰到了一个B帧,这时我们先不对它进编码.而是采用frame =         x264_encoder_frame_put_from_picture( h, h->frame_next, pic )函数将这个B帧放进h->frame_next中.
          在h中同时定义了下面几个帧数组用以实现帧的管理.
              x264_frame_t   *bframe_current[X264_BFRAME_MAX]; /* store the sequence of b frame being encoded */
              x264_frame_t    *frame_next[X264_BFRAME_MAX+1];   /* store the next sequence of  frames to be encoded *///这个是定义下一个帧,但不一定是B帧.
             x264_frame_t    *frame_unused[X264_BFRAME_MAX+1]; /* store unused frames */

同时还有下面4个函数(定义在/ENCODER/encoder.c中).
              x264_encoder_frame_put_from_picture();
              x264_encoder_frame_put() ();
              x264_encoder_frame_get();
              x264_frame_copy_picture();
        这3个数组和4个函数可以说完成了整个帧的类型的判定问题.在不对P帧进行编码之前,我们不对B帧进行编码,只是把B帧放进缓冲区(就是前面提到的数组).
        例如视频序列:I B  B  P  B  B  P
先确立第一个帧的类型,然后进行编码.然后是2个B帧,我们把它放进缓冲区数组.然后是P帧,我们可以判定它的类型并进行编码.同时,我们将缓冲区的B帧放进h->bframe_current[i],不过这时P帧前的两个B帧并没有编码.当读到P帧后面的第一个B帧时,我们实际上才将h->bframe_current数组中的第一个B帧编码,也就是将在I帧后面的第一个B帧编码.依此类推.(帧的有关理解学习笔记(二)

F.     建立参考帧列表的操作,这里调用了函数x264_reference_build_list( h, h->fdec->i_poc ); (定义在/ENCODER/encoder.c中).
        光调用这个函数是不行的,它是和后面的这个函数(如下)一起配合工作的.
                       if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )//判断为B帧.
                       {
                        x264_reference_update( h );
                        }
        If条件是判断当前帧是否是B帧,如果是的话就不更新参考列表,因为B帧本来就不能作为参考帧嘛!如果是I帧或P帧的话,就更新参考帧列表.

G.     下面是写slice的操作.

/* Init bitstream context */
                     h->out.i_nal = 0;//out的声明在bs.h中.
                     bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );//空出8位.
 
                     /* Write SPS and PPS */
                     if( i_nal_type == NAL_SLICE_IDR )
                         {
                           /* generate sequence parameters */
                            x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
                            x264_sps_write( &h->out.bs, h->sps );
                            x264_nal_end( h );
 
                             /* generate picture parameters */
                            x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
                            x264_pps_write( &h->out.bs, h->pps );
                            x264_nal_end( h );
                          
         x264_slice_write()(定义在/ENCODER/encoder.c中),这里面是编码的最主要部分..
         下面这个循环,它是采用for循环对一帧图像的所有块依次进行编码.
                for( mb_xy = 0, i_skip = 0; mb_xy < h->sps->i_mb_width * h->sps->i_mb_height; mb_xy++ )//h->sps->i_mb_width指的是从宽度上说有多少个宏快.    {
                const int i_mb_y = mb_xy / h->sps->i_mb_width;
                const int i_mb_x = mb_xy % h->sps->i_mb_width;//这两个变量是定义宏块的位置..
 
                 /* load cache */
                x264_macroblock_cache_load( h, i_mb_x, i_mb_y );//是把当前宏块的up宏块和left宏块的intra4×4_pred_mode,non_zero_count加载进来,放到一个数组里面,这个数组用来直接得到当前宏块的左侧和上面宏块的相关值.要想得到当前块的预测值,要先知道上面,左面的预测值,它的目的是替代getneighbour函数.
                /* analyse parameters
                * Slice I: choose I_4×4 or I_16×16 mode
                * Slice P: choose between using P mode or intra (4×4 or 16×16)
                * */
                 TIMER_START( i_mtime_analyse );
                 x264_macroblock_analyse( h );//定义在analyse.h中.
                 TIMER_STOP( i_mtime_analyse );
 
                  /* encode this macrobock -> be carefull it can change the mb type to P_SKIP if needed */
                  TIMER_START( i_mtime_encode );
                  x264_macroblock_encode( h );//定义在Enc/encoder.c中.
                  TIMER_STOP( i_mtime_encode );
        到这就已经完成编码的主要过程了,后面就是熵编码的过程了.

学习笔记(一)(x264编码流程)相关推荐

  1. X264编码流程详解(转)

    http://blog.csdn.net/xingyu19871124/article/details/7671634 对H.264编码标准一直停留在理解原理的基础上,对于一个实际投入使用的编码器是如 ...

  2. Redis学习笔记-GEO经纬度编码原理地理划分

    文章目录 Redis学习笔记-GEO经纬度编码原理&地理划分 1.笔记图 2.GEO 应用场景 3.GEO 数据特点举例 4.GeoHash 的编码方法(二分区间,区间编码) 5.GEO 经纬 ...

  3. IMX6ULL学习笔记(四) —— uboot 启动流程

    IMX6ULL 学习笔记 version : v1.0 「2023.4.27」 author: Y.Z.T. 摘要: 随记, 记录 I.MX6ULL 系列 SOC 的uboot 启动流程 ⭐️ 目录 ...

  4. git serialtool_Git学习笔记---协作的一般流程

    一般的操作流程 1.pull 王小坤与另一个同事张大炮一起开发一个项目,张大炮昨天修改了数据库读写的api,优化了执行速度,并把read()函数改名成了Read(),下午下班之前把这些代码push到服 ...

  5. gulp学习笔记,基本使用流程,基本函数,使用监听、插件

    学习gulp的简单笔记.原教学视频:https://www.bilibili.com/video/BV1NE411T7Z2?p=396. gulp基本使用流程 初始化项目目录: cnpm init / ...

  6. c++代码转为go_Go语言学习笔记六--string编码

    分解探索string编码 转为byte数组 func main() {s := "Hi小智加油!"fmt.Println("len(s):",len(s)) / ...

  7. 学习笔记之centos系统启动流程

     CentOS 系统的启动流程: 简介: (内核级别)POST -读取-> BootSequence(在BIOS中) --> BootLoader(在MBR中)--> Kernel( ...

  8. 学习笔记 | 独热编码(One-Hot Encoding)

    最近学习机器学习,接触到独热编码相关内容,参考了一些资料,加上自己的思考,做出了如下总结. 一.什么是独热编码 独热编码,即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个 ...

  9. 【若依】开源框架学习笔记 07 - 登录认证流程(Spring Security 源码)

    文章目录 一.概述 二.登录过程代码实现 三.用户验证流程(Spring Security 源码) 1.处理用户认证逻辑过滤器 `UsernamePasswordAuthenticationFilte ...

最新文章

  1. 什么是码元计算机通信
  2. powerdesigner 同步mysql 报错_PowerDesigner实用技巧小结 及 导出word,想字段顺序跟模型中一致,如何设置...
  3. 宇宙条一面:十道经典面试题解析
  4. Python实现多进程的4种方式
  5. android蓝牙4.0BLE
  6. Android之IPC通信中的UID和PID识别
  7. 现代软件工程 团队作业 - 软件分析和用户需求调查 (2013)
  8. 从单体到Flink:一文读懂数据架构的演变
  9. 【深度学习】ImageDataGenerator的使用--读书笔记
  10. 电子专业 英语词汇大全(持续更新)
  11. 了解Spring的变迁从Spring3到Spring5
  12. python手工打码_python云打码
  13. 【Python笔记】pyspark.sql.functions
  14. 【功能安全】【ISO26262】生产和运行
  15. 重新做计算机老师的说说,说说我们电脑班老师 ── 刁元清
  16. sqlite怎么转换mysql_Django如何把SQLite数据库转换为Mysql数据库
  17. SQL 大厂面试真题篇
  18. 通信信号与系统分析(四 基于simulink仿真)
  19. 区块链上市公司半年报: 41家进入实际应用及研究 5家瞄准供应链金融
  20. Unity FairyGUI 自适应扩展

热门文章

  1. java删除文件夹的所有文件
  2. 格子箱被评选为12家最值得注意的亚洲初创科技公司之一
  3. 微信小程序前端支付代码
  4. php解压功能的函数
  5. java ios压缩_iOS与Java服务器GZip压缩问题【转】
  6. 计算机s1,计算机S0、S1、S2、S3、S4、S5状态
  7. 233 Matrix HDU - 5015
  8. Linux mysql生成不了随机密码,用MySQL 生成随机密码
  9. linux安装库文件下载,Linux下的Curses库的下载与安装
  10. 接口入参形式_花椒测试平台 接口篇