H.264预测之帧间预测

这是一篇阅读笔记,直接点击图片可以查看清晰大图。

参考资料
The H.264 Advanced Video Compression Standard, Second Edition,以下简称THAVCS
Information technology – Coding of audio-visual objects – Part 10: Advanced Video Coding,以下简称SPEC

CodecVisa
JM
foreman_part_qcif.264 这个是foreman_part_qcif.yuv通过JM 8.6转换来的

http://blog.csdn.net/stpeace/article/details/8115392

帧间预测就是以已经编码好的帧(在display order上可以是当前帧的前面也可以是后面)作为参考帧,确定预测区域,生成预测块,计算出殘差,这些参考帧都存放于Decoded Picture Buffer当中。与帧内预测不同的是,帧间预测是以重建的帧为预测帧的,而帧内预测是一Loop filter之前的帧为预测帧。当前区块和预测区域之间的位移为运动向量(Motion Vector,简写为MV),每个区块有各自的MV,并且MV可以是整数精度,二分之一精度或者四分之一精度(对于4:2:0的视频,C是八分之一精度),这种非整数精度的预测区域都是通过插值算法从参考帧当中计算出来的。

注意,在实际当中MV的单位都是以最小的精度为单位的,比如Y分量的MV单位就是四分之一精度,C分量的MV单位就是八分之一精度,当然这里说的都是4:2:0的视频。

主要过程就是选取参考帧,插值,确定预测区块,确定预测类型,确定运动向量,预测运动向量,编码运动向量差量和殘差,deblocking filter。。。
这里插值计算需要注意的是,先计算二分之一,再计算四分之一,参见THAVCS 6.4.2.1 Generating interpolated sub-pixel samples。

我们来看个实例,先看MV值为整数的,也就是不用插值的。

跟帧内预测所用的码流一样,用CodecVisa打开,选取第二帧(这个码流一共三帧,分别为IPP),选取第4行,第9列的那个MB。
看图inter-prediction-p-slice,
inter-prediction-p-slice
因为这里Y分量MV单位是四分之一精度,所以实际值除以4就是像素偏差。被高亮的块的值向左移动7个像素就刚刚和第一帧当中被高亮位置的值相等,这也就是说当前块是以第一帧所示区块为预测块的,注意帧间预测的参考帧都是重构出来的,都是看Final值,和帧内预测看Pre-LP值不一样,如图inter-prediction-reference-picture,
inter-prediction-reference-picture
这是帧间预测的一个实例。

再来看看非整数精度的情况,MV为(-2.75, -2),垂直方向上是整数,我们不用考虑,现在就看水平方向上。
inter-prediction-p-slice-non-integer-pixel
inter-prediction-reference-picture-non-integer-pixel
如上两张图就分别是当前块和预测块,因为这个MV不是整数的,所以要先插值算出预测块,我们把有需要的数据提取出来,

187    185    187    195 A   199    201    200    200

187    184    191    198 B   199    201    199    200

188    183    174    169 C   183    202    198    200

189    185    166    130 D   132    172    199    202

如上数据就是参考帧重构后的数据,A,B,C和D就是我们要插值算出来的数据,也就是当前块的预测值。

先计算二分之一

Aa = round((1 * 185 - 5 * 187 + 20 * 195 + 20 * 199 - 5 * 201 + 1 * 200) / 32) = 198
Bb = round((1 * 184 - 5 * 191 + 20 * 198 + 20 * 199 - 5 * 201 + 1 * 199) / 32) = 199
Cc = round((1 * 183 - 5 * 174 + 20 * 169 + 20 * 183 - 5 * 202 + 1 * 198) / 32) = 173
Dd = round((1 * 185 - 5 * 166 + 20 * 130 + 20 * 132 - 5 * 172 + 1 * 199) / 32) = 123

然后四分之一

A = round((198 + 195) / 2) = 197
B = round((199 + 198) / 2) = 199
C = round((173 + 169) / 2) = 171
D = round((123 + 130) / 2) = 127

第二列的数据,方法同样
先计算二分之一

Aa + 1 = round((1 * 187 - 5 * 195 + 20 * 199 + 20 * 201 - 5 * 200 + 1 * 200) / 32) = 200
Bb + 1 = round((1 * 191 - 5 * 198 + 20 * 199 + 20 * 201 - 5 * 199 + 1 * 200) / 32) = 200
Cc + 1 = round((1 * 174 - 5 * 169 + 20 * 183 + 20 * 202 - 5 * 198 + 1 * 200) / 32) = 195
Dd + 1 = round((1 * 166 - 5 * 130 + 20 * 132 + 20 * 172 - 5 * 199 + 1 * 202) / 32) = 150

然后四分之一

A = round((200 + 199) / 2) = 200
B = round((200 + 199) / 2) = 200
C = round((195 + 183) / 2) = 189
D = round((150 + 132) / 2) = 141

后面的就不再罗列了,从结果来看,我们这里预测的结果

197    200    ....
199    200    ....
171    189    ....
127    141    ....

和CodecVisa有些许出入,但是变化趋势相同的,所以这个实验的基本目的达到了。

H.264预测之帧内预测

这是一篇阅读笔记,直接点击图片可以查看清晰大图。

参考资料
The H.264 Advanced Video Compression Standard, Second Edition,以下简称THAVCS
Information technology – Coding of audio-visual objects – Part 10: Advanced Video Coding,以下简称SPEC

CodecVisa
JM
foreman_part_qcif.264 这个是foreman_part_qcif.yuv通过JM 8.6转换来的

http://blog.csdn.net/stpeace/article/details/8114826

简单的理解帧内预测就是利用该帧里面已经编码和重构好的块来编码数据,具体做法就是用当前块减去预测块,得到的数据再编码

比如:

编码好的块 B
预测块 P
当前块 C

Delta = C - P
P是根据B预测来的,Delta是最终进行编码的数据,也就是我们经常说的Residual

对于亮度分量,P一般是4 x 4的块或者16 x 16的宏块(8 x 8的只在high profile当中出现),对于细节比较丰富的地方使用4 x 4的块,对于比较平坦的地方使用16 x 16的块的(这样做的原因就是通常块分的越细就需要越多的位来存放这些块自身的信息,但预测的殘差会比较小;块分的越大,存放块本身信息所占用比较少的位,预测的时候殘差就会占用更多的位,所以这是一个权衡)。并且我们一般记作4 × 4 Luma Prediction和16 × 16 Luma Prediction。
4 × 4 Luma Prediction有9种预测模式,参见THAVCS书的Figure 6.10和Figure 6.11,名字就不一一列举了,这里有截图。
4-x-4-intra-prediction-modes
那这么多预测模式我们到底使用哪一个是如何决定的呢?这里就涉及到一个SAE(Sum of Absolute Errors),这个SAE表示预测的错误或者偏差,明显SAE越大越不准确,所以这里我们当然选择SAE最小的那个,至于SAE是如何算出来的,我们暂时不考虑。
16 × 16 Luma Prediction有4种预测模式,参见THAVCS书的Figure 6.13,截图如下,当然选用那种模式跟上面的方法一样,也是看SAE。
16-x-16-intra-prediction-modes
16-x-16-intra-prediction-modes-descriptions

对于色度分量,P一般是8 x 8的块,记作8 × 8 Chroma Prediction。它也是有4种预测模式,跟16 × 16 Luma Prediction一样,只是模式的编号不一样,具体如下,DC(mode 0), Horizontal(mode 1),Vertical(mode 2)和Plane(mode 3)

知道这些总体上的知识之后,我们来稍微深入看看具体的,这个时候就需要用到CodecVisa了。打开码流,可以看到MB(MacroBlock)信息如下,
从MB统计信息(h.264-foreman-1st-frame-mb-statistics)来看,该Picture是I-Slice,一共有99个I-MBs,94个I_NxN类型,2个I_16x16_0_0_0类型,1个I_16x16_2_0_0类型,1个I_16x16_3_1_0类型以及1个I_16x16_2_0_1类型,具体这些类型的含义可以参见SPEC的Table 7-11 – Macroblock types for I slices。
h.264-foreman-1st-frame-mb-statistics
各个MB的分布情况如下,当然下图只能比较容易的区分出16 x 16的5个MB,
h.264-foreman-1st-frame-mb
另外第一个MB的详细信息如下,
h.264-foreman-1st-frame-1st-mb

I_16x16_0_0_0表示预测模型是0(Vertical),而且我们知道这是Y分量,我们可以对照CodecVisa验证。通过h.264-foreman-1st-frame-i_16x16_0_0_0-mb我们可以看见这个MB的预测值都是237,而且因为它的预测模型是Vertical,所以我们可以推理出,该MB上面对应的位置的值应该也都是237,通过CodecVisa查看对应位置的数据截图如h.264-foreman-1st-frame-i_16x16_0_0_0-mb-up-mb,可以看出确实是237,这与我们说的Vertical模式正好吻合。至于实际值是236,这当然预测的值和实际值肯定是有误差的,所以这个误差-1通过Residual记录下来。
h.264-foreman-1st-frame-i_16x16_0_0_0-mb-up-mb
下面是预测值
h.264-foreman-1st-frame-i_16x16_0_0_0-mb

I_16x16_2_0_0表示预测模型是2(DC),这也是Y分量,同理我们也可以验证。DC是Up的值和Left的值的均值,即((237 * 12 + 229 + 222 + 213 + 206) + (236 * 16)) / (16 * 2),约等于234,这里取整的误差我们认为这些误差是可以接受的,通过CodecVisa我们也可以观察出来,预测值确实为234。
Up和Left的MB如下
h.264-foreman-1st-frame-i_16x16_2_0_0-up-mb
h.264-foreman-1st-frame-i_16x16_2_0_0-left-mb
预测值为
h.264-foreman-1st-frame-i_16x16_2_0_0-mb

另外比如4 x 4的Horizontal Up/Down这些要额外插值计算的预测原理也是如此,只不过稍微复杂点。
这里我们来简单分析下,首先是MB的划分,拆分成8 x 8,里面再拆分成4 x 4,处理的顺序就是先对第1个8 x 8块里面的4个4 x 4块按照光栅顺序处理,然后对第2个8 x 8块进行处理,如下图所示,
h-264-16-x-16-8-x-8-4-x-4-hierarchy
那么对某个具体的4 x 4块预测的时候就好办了,比如我们以Horizontal Up为例子,参照SPEC 8.3.1.2.9 Specification of Intra_4x4_Horizontal_Up prediction mode,截图如下,
4-x-4-intra-prediction-modes-horizontal-up
这就是我们预测的公式,当中p[-1, y]就是我们用来预测的像素,如果没有的话,是不能完成这种模式的预测的。这个预测公式也很容易看懂,x和y就是你要预测的4 x 4块里面每个像素的坐标,所以它们范围只能是0至3。
另外下图我画了比较形象的图来说明,p[-1, y]就代表这里的I,J,K和L,同样图形的坐标里面的值是一样的,图形里面数字一样的表示是用同一个公式计算出来的(公式一样,代入值不一样,所以结果值不同,另外这种内部标记的我没有画完全,只是画出来一个来说明这种情况,就是坐标为[1, 0]和[1, 1]的两个位置),看图,
h-264-horizontal-up-prediction
JM的代码也有比较直观的展示,

  case  HOR_UP_PRED:/* diagonal prediction -22.5 deg to horizontal plane */
    if (!block_available_left)
      printf ("warning: Intra_4x4_Horizontal_Up prediction mode not allowed at mb %d\n",img->current_mb_nr);

	// 一个MB会被拆分成16个4 x 4的块(当然先拆分成4个8 x 8的块),一个4 x 4的块又会包含16个像素,就是这里

	// zHU = x + 2 * y; // x = 0..3, y = 0..3

    img->mpr[0+ioff][0+joff] = (P_I + P_J + 1) / 2; // (p[−1, y + (x >> 1)] + p[−1, y + (x >> 1) + 1] + 1) >> 1 代入 x = 0, y = 0
    img->mpr[1+ioff][0+joff] = (P_I + 2*P_J + P_K + 2) / 4; // (p[−1, y + (x >> 1)] + 2 * p[−1, y + (x >> 1) + 1] + p[−1, y + (x >> 1) + 2] + 2) >> 2 // 代入 x = 1, y = 0 注意right shift
    img->mpr[2+ioff][0+joff] = 
    img->mpr[0+ioff][1+joff] = (P_J + P_K + 1) / 2;
    img->mpr[3+ioff][0+joff] = 
    img->mpr[1+ioff][1+joff] = (P_J + 2*P_K + P_L + 2) / 4;
    img->mpr[2+ioff][1+joff] = 
    img->mpr[0+ioff][2+joff] = (P_K + P_L + 1) / 2;
    img->mpr[3+ioff][1+joff] = 
    img->mpr[1+ioff][2+joff] = (P_K + 2*P_L + P_L + 2) / 4;
    img->mpr[3+ioff][2+joff] = 
    img->mpr[1+ioff][3+joff] = 
    img->mpr[0+ioff][3+joff] = 
    img->mpr[2+ioff][2+joff] = 
    img->mpr[2+ioff][3+joff] = 
    img->mpr[3+ioff][3+joff] = P_L; // p[ −1, 3 ]
    break;

这就是Horizontal Up预测,应该就有了比较透彻的理解。这照相机拍的图在这里显示的orientation不对,直接点过去看大图吧,大图是正确的。

THAVCS当中关于Loop filter的 描述。
A filter is applied to every decoded macroblock to reduce blocking distortion [iv]. The de-
blocking filter is applied after the inverse transform in the encoder before reconstructing and
storing the macroblock for future predictions and in the decoder before reconstructing and
displaying the macroblock.

Intra-coded macroblocks are filtered, but intra prediction (section 6.3) is carried out using unfiltered reconstructed
macroblocks to form the prediction.

CodecVisa软件中Pre-LP表示Loop filter动作之前的数据,IDCT Coefficient表示Inverse DCT Coefficient。

在JM的ldecod和lencod当中会有与预测相关的代码,详见block.c当中名字以intrapred打头的方法,各种预测模式的算法都有。

Android Kitkat SDK 打包出现 Dex error

NOTICE: 这条issue已经被官方修掉了,下载新的19.0.1版本的build-tools就可以,https://code.google.com/p/android/issues/detail?id=61710,http://developer.android.com/tools/revisions/build-tools.html

尝鲜Android Kitkat的同学很多人都遇到了下面这个错误。

[2013-11-01 16:58:07 - Dex Loader] Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.
[2013-11-01 16:58:07 - Hello-Android] Conversion to Dalvik format failed: Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.

java.nio.BufferOverflowException
	at java.nio.Buffer.nextPutIndex(Buffer.java:499)
	at java.nio.HeapByteBuffer.putShort(HeapByteBuffer.java:296)
	at com.android.dex.Dex$Section.writeShort(Dex.java:818)
	at com.android.dex.Dex$Section.writeTypeList(Dex.java:870)
	at com.android.dx.merge.DexMerger$3.write(DexMerger.java:437)
	at com.android.dx.merge.DexMerger$3.write(DexMerger.java:423)
	at com.android.dx.merge.DexMerger$IdMerger.mergeUnsorted(DexMerger.java:317)
	at com.android.dx.merge.DexMerger.mergeTypeLists(DexMerger.java:423)
	at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:163)
	at com.android.dx.merge.DexMerger.merge(DexMerger.java:187)
	at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
	at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
	at com.android.dx.command.dexer.Main.run(Main.java:230)
	at sun.reflect.GeneratedMethodAccessor12.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.android.ide.eclipse.adt.internal.build.DexWrapper.run(DexWrapper.java:187)
	at com.android.ide.eclipse.adt.internal.build.BuildHelper.executeDx(BuildHelper.java:780)
	at com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder.build(PostCompilerBuilder.java:593)
	at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:728)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:199)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:239)
	at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:292)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:295)
	at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:351)
	at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:374)
	at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:143)
	at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:241)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)

有人第一时间在Groups上提出了这个问题
Dex issues with latest SDK
但是目前也还没有得到官方的回答,看起来应该是build-tools的影响。

我试了下,项目版本在Android 4.1之上的基本都不会有这个问题,在4.1以下的目前应该都有这个问题。

这样子似乎只能等待官方答案。。。

有人把build-tools回滚到18.1.1据说可以解决这个问题。

如果有需要大家可以按这两个方法试试~~

P.S. 亲测 Linux和Windows下都有这个问题,有人说Mac OS下没有这个问题。。。只能跪了,另外同步AOSP代码真是个痛苦的过程

一步一步解析H.264码流的NALU(SPS,PSS,IDR)

之前有说过H.264比较复杂,我是打算一块一块来分析。这里是学习笔记以及自己的理解,很多可能是摘抄的相关资料,感谢原始作者!理解错误的地方也希望大家指出。
注意:目前处于草稿状态,不定期更新!

在了解了H.264编码的基础介绍之后(Overview of the H.264/AVC Video Coding Standard,H.264-MPEG-4 Part 10 White Paper,ISO_IEC_14496-10_2012,新一代视频压缩编码标准H.264,H.264码流结构解析等等有非常多的资料,这里就不一一列举了,网上有很多,然后天之骄子/firstime还有李世平/Peter Lee这些前辈写的文章),这么多资料我也是没有完全看懂,我也是采取能看懂的尽量看懂,不能看懂的多看几遍(这些资料交换看/翻来覆去的看有帮助你理解原来不懂的东西),反正是对H.264有了个大概的理解。

我看这类编/解码有个习惯就是从编/解码出来的数据文件下手。因为无论如何复杂,最终的文件(字节流)肯定是符合某种规范的。

如果你打算继续往下看,你对H.264的理解基本上应该知道VCL和NAL是什么东西,知道Exp-Golomb/Huffman编码,码流(bitstream)是由一个个的NALU组成等等这一类的基本知识。

首先我们希望有种可以分析H.264码流的工具,这样可以让我们直观的了解,但是不幸的是类似这样的工具都太贵,多数许可证要上千美金,不过幸好我们可以找到21天试用版或者缺少功能的演示版。
我在网上搜索了很多相关的工具,比如CodecVisa,StreamEye以及VM Analyzer,这些都可以玩玩。

还有个问题,码流文件去哪里找?我在网上一个人的网站看到有下载,但是解压需要密码,我给它写信,期盼着他能给我密码,不幸的是他还没有回,然后我就继续找,其实可以不用这么麻烦,我们可以自己生成码流文件,JM代码里面有个foreman_part_qcif.yuv,你用它的lencod.exe命令先把它编码成H.264码流,这样你就还得先了解下JM是什么,如何编译,如何使用,不过这些都很简单。YUV视频序列也可以到网络上去下载,这个很多。

下载YUV视频序列 http://trace.eas.asu.edu/yuv/
H.264测试模型/文档 http://iphome.hhi.de/suehring/tml/
http://wftp3.itu.int/av-arch/jvt-site/
李世平 http://blog.csdn.net/sunshine1314
天之骄子 http://bbs.chinavideo.org/viewthread.php?tid=988

这里我查看码流用的工具是StreamEye,码流文件是foreman_part_qcif.yuv编码得来的。
这些工具怎么用就不介绍了,无非就是看视频有哪些/类帧组成,各个属性(Header/MacroBlock/Picture)是什么。

下面这段信息是从Headers Info拷贝出来的,粗看下它是SPS,PPS和Slice Header,那这有什么用,这些数据是什么意思?

  [00]seq_parameter_set_rbsp() {
    profile_idc                                    = 66 (Baseline)
    constraint_set0_flag                           = 0 (false)
    constraint_set1_flag                           = 0 (false)
    constraint_set2_flag                           = 0 (false)
    constraint_set3_flag                           = 0 (false)
    constraint_set4_flag                           = 0 (false)
    constraint_set5_flag                           = 0 (false)
    reserved_zero_2bits                            = 0
    level_idc                                      = 30
    seq_parameter_set_id                           = 0
    if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) {
      chroma_format_idc                            = na
      if (chroma_format_idc == 3)
        separate_colour_plane_flag                 = na
      bit_depth_luma_minus8                        = na
      bit_depth_chroma_minus8                      = na
      qpprime_y_zero_transform_bypass_flag         = na
      seq_scaling_matrix_present_flag              = na
      if (seq_scaling_matrix_present_flag)
        for (i = 0; i < 8; i++) {
          seq_scaling_list_present_flag&#91;0&#93;         = na
          if (seq_scaling_list_present_flag&#91;0&#93;)
            scaling_list_4x4&#91;00&#93;                   = na
            scaling_list_4x4&#91;01&#93;                   = na
            scaling_list_4x4&#91;02&#93;                   = na
            scaling_list_4x4&#91;03&#93;                   = na
            scaling_list_4x4&#91;04&#93;                   = na
            scaling_list_4x4&#91;05&#93;                   = na
            scaling_list_4x4&#91;06&#93;                   = na
            scaling_list_4x4&#91;07&#93;                   = na
            scaling_list_4x4&#91;08&#93;                   = na
            scaling_list_4x4&#91;09&#93;                   = na
            scaling_list_4x4&#91;10&#93;                   = na
            scaling_list_4x4&#91;11&#93;                   = na
            scaling_list_4x4&#91;12&#93;                   = na
            scaling_list_4x4&#91;13&#93;                   = na
            scaling_list_4x4&#91;14&#93;                   = na
            scaling_list_4x4&#91;15&#93;                   = na
          seq_scaling_list_present_flag&#91;1&#93;         = na
          if (seq_scaling_list_present_flag&#91;1&#93;)
            scaling_list_4x4&#91;00&#93;                   = na
            scaling_list_4x4&#91;01&#93;                   = na
            scaling_list_4x4&#91;02&#93;                   = na
            scaling_list_4x4&#91;03&#93;                   = na
            scaling_list_4x4&#91;04&#93;                   = na
            scaling_list_4x4&#91;05&#93;                   = na
            scaling_list_4x4&#91;06&#93;                   = na
            scaling_list_4x4&#91;07&#93;                   = na
            scaling_list_4x4&#91;08&#93;                   = na
            scaling_list_4x4&#91;09&#93;                   = na
            scaling_list_4x4&#91;10&#93;                   = na
            scaling_list_4x4&#91;11&#93;                   = na
            scaling_list_4x4&#91;12&#93;                   = na
            scaling_list_4x4&#91;13&#93;                   = na
            scaling_list_4x4&#91;14&#93;                   = na
            scaling_list_4x4&#91;15&#93;                   = na
          seq_scaling_list_present_flag&#91;2&#93;         = na
          if (seq_scaling_list_present_flag&#91;2&#93;)
            scaling_list_4x4&#91;00&#93;                   = na
            scaling_list_4x4&#91;01&#93;                   = na
            scaling_list_4x4&#91;02&#93;                   = na
            scaling_list_4x4&#91;03&#93;                   = na
            scaling_list_4x4&#91;04&#93;                   = na
            scaling_list_4x4&#91;05&#93;                   = na
            scaling_list_4x4&#91;06&#93;                   = na
            scaling_list_4x4&#91;07&#93;                   = na
            scaling_list_4x4&#91;08&#93;                   = na
            scaling_list_4x4&#91;09&#93;                   = na
            scaling_list_4x4&#91;10&#93;                   = na
            scaling_list_4x4&#91;11&#93;                   = na
            scaling_list_4x4&#91;12&#93;                   = na
            scaling_list_4x4&#91;13&#93;                   = na
            scaling_list_4x4&#91;14&#93;                   = na
            scaling_list_4x4&#91;15&#93;                   = na
          seq_scaling_list_present_flag&#91;3&#93;         = na
          if (seq_scaling_list_present_flag&#91;3&#93;)
            scaling_list_4x4&#91;00&#93;                   = na
            scaling_list_4x4&#91;01&#93;                   = na
            scaling_list_4x4&#91;02&#93;                   = na
            scaling_list_4x4&#91;03&#93;                   = na
            scaling_list_4x4&#91;04&#93;                   = na
            scaling_list_4x4&#91;05&#93;                   = na
            scaling_list_4x4&#91;06&#93;                   = na
            scaling_list_4x4&#91;07&#93;                   = na
            scaling_list_4x4&#91;08&#93;                   = na
            scaling_list_4x4&#91;09&#93;                   = na
            scaling_list_4x4&#91;10&#93;                   = na
            scaling_list_4x4&#91;11&#93;                   = na
            scaling_list_4x4&#91;12&#93;                   = na
            scaling_list_4x4&#91;13&#93;                   = na
            scaling_list_4x4&#91;14&#93;                   = na
            scaling_list_4x4&#91;15&#93;                   = na
          seq_scaling_list_present_flag&#91;4&#93;         = na
          if (seq_scaling_list_present_flag&#91;4&#93;)
            scaling_list_4x4&#91;00&#93;                   = na
            scaling_list_4x4&#91;01&#93;                   = na
            scaling_list_4x4&#91;02&#93;                   = na
            scaling_list_4x4&#91;03&#93;                   = na
            scaling_list_4x4&#91;04&#93;                   = na
            scaling_list_4x4&#91;05&#93;                   = na
            scaling_list_4x4&#91;06&#93;                   = na
            scaling_list_4x4&#91;07&#93;                   = na
            scaling_list_4x4&#91;08&#93;                   = na
            scaling_list_4x4&#91;09&#93;                   = na
            scaling_list_4x4&#91;10&#93;                   = na
            scaling_list_4x4&#91;11&#93;                   = na
            scaling_list_4x4&#91;12&#93;                   = na
            scaling_list_4x4&#91;13&#93;                   = na
            scaling_list_4x4&#91;14&#93;                   = na
            scaling_list_4x4&#91;15&#93;                   = na
          seq_scaling_list_present_flag&#91;5&#93;         = na
          if (seq_scaling_list_present_flag&#91;5&#93;)
            scaling_list_4x4&#91;00&#93;                   = na
            scaling_list_4x4&#91;01&#93;                   = na
            scaling_list_4x4&#91;02&#93;                   = na
            scaling_list_4x4&#91;03&#93;                   = na
            scaling_list_4x4&#91;04&#93;                   = na
            scaling_list_4x4&#91;05&#93;                   = na
            scaling_list_4x4&#91;06&#93;                   = na
            scaling_list_4x4&#91;07&#93;                   = na
            scaling_list_4x4&#91;08&#93;                   = na
            scaling_list_4x4&#91;09&#93;                   = na
            scaling_list_4x4&#91;10&#93;                   = na
            scaling_list_4x4&#91;11&#93;                   = na
            scaling_list_4x4&#91;12&#93;                   = na
            scaling_list_4x4&#91;13&#93;                   = na
            scaling_list_4x4&#91;14&#93;                   = na
            scaling_list_4x4&#91;15&#93;                   = na
          seq_scaling_list_present_flag&#91;6&#93;         = na
          if (seq_scaling_list_present_flag&#91;6&#93;)
            scaling_list_8x8&#91;00&#93;                   = na
            scaling_list_8x8&#91;01&#93;                   = na
            scaling_list_8x8&#91;02&#93;                   = na
            scaling_list_8x8&#91;03&#93;                   = na
            scaling_list_8x8&#91;04&#93;                   = na
            scaling_list_8x8&#91;05&#93;                   = na
            scaling_list_8x8&#91;06&#93;                   = na
            scaling_list_8x8&#91;07&#93;                   = na
            scaling_list_8x8&#91;08&#93;                   = na
            scaling_list_8x8&#91;09&#93;                   = na
            scaling_list_8x8&#91;10&#93;                   = na
            scaling_list_8x8&#91;11&#93;                   = na
            scaling_list_8x8&#91;12&#93;                   = na
            scaling_list_8x8&#91;13&#93;                   = na
            scaling_list_8x8&#91;14&#93;                   = na
            scaling_list_8x8&#91;15&#93;                   = na
            scaling_list_8x8&#91;16&#93;                   = na
            scaling_list_8x8&#91;17&#93;                   = na
            scaling_list_8x8&#91;18&#93;                   = na
            scaling_list_8x8&#91;19&#93;                   = na
            scaling_list_8x8&#91;20&#93;                   = na
            scaling_list_8x8&#91;21&#93;                   = na
            scaling_list_8x8&#91;22&#93;                   = na
            scaling_list_8x8&#91;23&#93;                   = na
            scaling_list_8x8&#91;24&#93;                   = na
            scaling_list_8x8&#91;25&#93;                   = na
            scaling_list_8x8&#91;26&#93;                   = na
            scaling_list_8x8&#91;27&#93;                   = na
            scaling_list_8x8&#91;28&#93;                   = na
            scaling_list_8x8&#91;29&#93;                   = na
            scaling_list_8x8&#91;30&#93;                   = na
            scaling_list_8x8&#91;31&#93;                   = na
            scaling_list_8x8&#91;32&#93;                   = na
            scaling_list_8x8&#91;33&#93;                   = na
            scaling_list_8x8&#91;34&#93;                   = na
            scaling_list_8x8&#91;35&#93;                   = na
            scaling_list_8x8&#91;36&#93;                   = na
            scaling_list_8x8&#91;37&#93;                   = na
            scaling_list_8x8&#91;38&#93;                   = na
            scaling_list_8x8&#91;39&#93;                   = na
            scaling_list_8x8&#91;40&#93;                   = na
            scaling_list_8x8&#91;41&#93;                   = na
            scaling_list_8x8&#91;42&#93;                   = na
            scaling_list_8x8&#91;43&#93;                   = na
            scaling_list_8x8&#91;44&#93;                   = na
            scaling_list_8x8&#91;45&#93;                   = na
            scaling_list_8x8&#91;46&#93;                   = na
            scaling_list_8x8&#91;47&#93;                   = na
            scaling_list_8x8&#91;48&#93;                   = na
            scaling_list_8x8&#91;49&#93;                   = na
            scaling_list_8x8&#91;50&#93;                   = na
            scaling_list_8x8&#91;51&#93;                   = na
            scaling_list_8x8&#91;52&#93;                   = na
            scaling_list_8x8&#91;53&#93;                   = na
            scaling_list_8x8&#91;54&#93;                   = na
            scaling_list_8x8&#91;55&#93;                   = na
            scaling_list_8x8&#91;56&#93;                   = na
            scaling_list_8x8&#91;57&#93;                   = na
            scaling_list_8x8&#91;58&#93;                   = na
            scaling_list_8x8&#91;59&#93;                   = na
            scaling_list_8x8&#91;60&#93;                   = na
            scaling_list_8x8&#91;61&#93;                   = na
            scaling_list_8x8&#91;62&#93;                   = na
            scaling_list_8x8&#91;63&#93;                   = na
          seq_scaling_list_present_flag&#91;7&#93;         = na
          if (seq_scaling_list_present_flag&#91;7&#93;)
            scaling_list_8x8&#91;00&#93;                   = na
            scaling_list_8x8&#91;01&#93;                   = na
            scaling_list_8x8&#91;02&#93;                   = na
            scaling_list_8x8&#91;03&#93;                   = na
            scaling_list_8x8&#91;04&#93;                   = na
            scaling_list_8x8&#91;05&#93;                   = na
            scaling_list_8x8&#91;06&#93;                   = na
            scaling_list_8x8&#91;07&#93;                   = na
            scaling_list_8x8&#91;08&#93;                   = na
            scaling_list_8x8&#91;09&#93;                   = na
            scaling_list_8x8&#91;10&#93;                   = na
            scaling_list_8x8&#91;11&#93;                   = na
            scaling_list_8x8&#91;12&#93;                   = na
            scaling_list_8x8&#91;13&#93;                   = na
            scaling_list_8x8&#91;14&#93;                   = na
            scaling_list_8x8&#91;15&#93;                   = na
            scaling_list_8x8&#91;16&#93;                   = na
            scaling_list_8x8&#91;17&#93;                   = na
            scaling_list_8x8&#91;18&#93;                   = na
            scaling_list_8x8&#91;19&#93;                   = na
            scaling_list_8x8&#91;20&#93;                   = na
            scaling_list_8x8&#91;21&#93;                   = na
            scaling_list_8x8&#91;22&#93;                   = na
            scaling_list_8x8&#91;23&#93;                   = na
            scaling_list_8x8&#91;24&#93;                   = na
            scaling_list_8x8&#91;25&#93;                   = na
            scaling_list_8x8&#91;26&#93;                   = na
            scaling_list_8x8&#91;27&#93;                   = na
            scaling_list_8x8&#91;28&#93;                   = na
            scaling_list_8x8&#91;29&#93;                   = na
            scaling_list_8x8&#91;30&#93;                   = na
            scaling_list_8x8&#91;31&#93;                   = na
            scaling_list_8x8&#91;32&#93;                   = na
            scaling_list_8x8&#91;33&#93;                   = na
            scaling_list_8x8&#91;34&#93;                   = na
            scaling_list_8x8&#91;35&#93;                   = na
            scaling_list_8x8&#91;36&#93;                   = na
            scaling_list_8x8&#91;37&#93;                   = na
            scaling_list_8x8&#91;38&#93;                   = na
            scaling_list_8x8&#91;39&#93;                   = na
            scaling_list_8x8&#91;40&#93;                   = na
            scaling_list_8x8&#91;41&#93;                   = na
            scaling_list_8x8&#91;42&#93;                   = na
            scaling_list_8x8&#91;43&#93;                   = na
            scaling_list_8x8&#91;44&#93;                   = na
            scaling_list_8x8&#91;45&#93;                   = na
            scaling_list_8x8&#91;46&#93;                   = na
            scaling_list_8x8&#91;47&#93;                   = na
            scaling_list_8x8&#91;48&#93;                   = na
            scaling_list_8x8&#91;49&#93;                   = na
            scaling_list_8x8&#91;50&#93;                   = na
            scaling_list_8x8&#91;51&#93;                   = na
            scaling_list_8x8&#91;52&#93;                   = na
            scaling_list_8x8&#91;53&#93;                   = na
            scaling_list_8x8&#91;54&#93;                   = na
            scaling_list_8x8&#91;55&#93;                   = na
            scaling_list_8x8&#91;56&#93;                   = na
            scaling_list_8x8&#91;57&#93;                   = na
            scaling_list_8x8&#91;58&#93;                   = na
            scaling_list_8x8&#91;59&#93;                   = na
            scaling_list_8x8&#91;60&#93;                   = na
            scaling_list_8x8&#91;61&#93;                   = na
            scaling_list_8x8&#91;62&#93;                   = na
            scaling_list_8x8&#91;63&#93;                   = na
          }
        }
      }
    log2_max_frame_num_minus4                      = 0 (4)
    pic_order_cnt_type                             = 0
    if (pic_order_cnt_type == 0)
      log2_max_pic_order_cnt_lsb_minus4            = 0 (4)
    else if (pic_order_cnt_type == 1) {
      delta_pic_order_always_zero_flag             = na
      offset_for_non_ref_pic                       = na
      offset_for_top_to_bottom_field               = na
      num_ref_frames_in_pic_order_cnt_cycle        = na
      for(i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++)
      }
    max_num_ref_frames                             = 10
    gaps_in_frame_num_value_allowed_flag           = 0
    pic_width_in_mbs_minus1                        = 10 (176)
    pic_height_in_map_units_minus1                 = 8 (144)
    frame_mbs_only_flag                            = 1
    if (!frame_mbs_only_flag)
      mb_adaptive_frame_field_flag                 = na
    direct_8x8_inference_flag                      = 0 (false)
    frame_cropping_flag                            = 0 (false)
    if (frame_cropping_flag) {
      frame_crop_left_offset                       = na
      frame_crop_right_offset                      = na
      frame_crop_top_offset                        = na
      frame_crop_bottom_offset                     = na
      }
    vui_parameters_present_flag                    = 0 (false)
    if (vui_parameters_present_flag)
      }
      vui_parameters()
    }
  &#91;00&#93;pic_parameter_set_rbsp() {
    pic_parameter_set_id                           = 0
    seq_parameter_set_id                           = 0
    entropy_coding_mode_flag                       = 0 (CAVLC)
    pic_order_present_flag                         = 0 (false)
    num_slice_groups_minus1                        = 0 (1)
    if (num_slice_groups_minus1 > 0) {
      slice_group_map_type                         = na
      if (slice_group_map_type == 0)
        for (iGroup = 0; iGroup <= num_slice_groups_minus1; iGroup++) {
        }
      else if (slice_group_map_type == 2)
        for (iGroup = 0; iGroup < num_slice_groups_minus1; iGroup++) {
        }
      else if ((slice_group_map_type == 3) || (slice_group_map_type == 4) || (slice_group_map_type == 5)) {
        slice_group_change_direction_flag          = na
        slice_group_change_rate_minus1             = na
      } else if (slice_group_map_type == 6) {
        pic_size_in_map_units_minus1               = na
        for (i = 0; i <= pic_size_in_map_units_minus1; i++)
        }
      }
    num_ref_idx_l0_active_minus1                   = 9 (10)
    num_ref_idx_l1_active_minus1                   = 9 (10)
    weighted_pred_flag                             = 0 (false)
    weighted_bipred_idc                            = 0
    pic_init_qp_minus26                            = 0 (26)
    pic_init_qs_minus26                            = 0 (26)
    chroma_qp_index_offset                         = 0
    deblocking_filter_control_present_flag         = 0 (false)
    constrained_intra_pred_flag                    = 0 (false)
    redundant_pic_cnt_present_flag                 = 0 (false)
    }
    if (more_rbsp_data()) {
      transform_8x8_mode_flag                      = na
      pic_scaling_matrix_present_flag              = na
      if (pic_scaling_matrix_present_flag) {
        for (i = 0; i < 6 + 2 * transform_8x8_mode_flag; i++) {
          pic_scaling_list_present_flag&#91;0&#93;         = na
          if (pic_scaling_list_present_flag&#91;0&#93;)
            scaling_list_4x4&#91;0&#93;&#91;00&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;01&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;02&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;03&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;04&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;05&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;06&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;07&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;08&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;09&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;10&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;11&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;12&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;13&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;14&#93;                = na
            scaling_list_4x4&#91;0&#93;&#91;15&#93;                = na
          pic_scaling_list_present_flag&#91;1&#93;         = na
          if (pic_scaling_list_present_flag&#91;1&#93;)
            scaling_list_4x4&#91;1&#93;&#91;00&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;01&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;02&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;03&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;04&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;05&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;06&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;07&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;08&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;09&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;10&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;11&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;12&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;13&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;14&#93;                = na
            scaling_list_4x4&#91;1&#93;&#91;15&#93;                = na
          pic_scaling_list_present_flag&#91;2&#93;         = na
          if (pic_scaling_list_present_flag&#91;2&#93;)
            scaling_list_4x4&#91;2&#93;&#91;00&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;01&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;02&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;03&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;04&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;05&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;06&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;07&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;08&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;09&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;10&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;11&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;12&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;13&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;14&#93;                = na
            scaling_list_4x4&#91;2&#93;&#91;15&#93;                = na
          pic_scaling_list_present_flag&#91;3&#93;         = na
          if (pic_scaling_list_present_flag&#91;3&#93;)
            scaling_list_4x4&#91;3&#93;&#91;00&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;01&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;02&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;03&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;04&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;05&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;06&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;07&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;08&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;09&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;10&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;11&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;12&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;13&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;14&#93;                = na
            scaling_list_4x4&#91;3&#93;&#91;15&#93;                = na
          pic_scaling_list_present_flag&#91;4&#93;         = na
          if (pic_scaling_list_present_flag&#91;4&#93;)
            scaling_list_4x4&#91;4&#93;&#91;00&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;01&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;02&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;03&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;04&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;05&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;06&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;07&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;08&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;09&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;10&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;11&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;12&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;13&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;14&#93;                = na
            scaling_list_4x4&#91;4&#93;&#91;15&#93;                = na
          pic_scaling_list_present_flag&#91;5&#93;         = na
          if (pic_scaling_list_present_flag&#91;5&#93;)
            scaling_list_4x4&#91;5&#93;&#91;00&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;01&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;02&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;03&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;04&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;05&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;06&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;07&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;08&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;09&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;10&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;11&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;12&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;13&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;14&#93;                = na
            scaling_list_4x4&#91;5&#93;&#91;15&#93;                = na
          }
        }
      second_chroma_qp_index_offset                = na
      }
    }
  &#91;00&#93;slice_header() {
    nal_unit_header_svc_extension() {
      idr_flag                                     = na
      priority_id                                  = na
      no_inter_layer_pred_flag                     = na
      dependency_id                                = na
      quality_id                                   = na
      temporal_id                                  = na
      use_ref_base_pic_flag                        = na
      discardable_flag                             = na
      output_flag                                  = na
      }
    first_mb_in_slice                              = 0
    slice_type                                     = 7 (I slice)
    pic_parameter_set_id                           = 0
    frame_num                                      = 0
    if (!frame_mbs_only_flag) {
      field_pic_flag                               = na
      if (field_pic_flag)
        bottom_field_flag                          = na
      }
    if (nal_unit_type == 5)
      idr_pic_id                                   = 0
    if (pic_order_cnt_type == 0) {
      pic_order_cnt_lsb                            = 0
      if (pic_order_present_flag && !field_pic_flag)
        delta_pic_order_cnt_bottom                 = na
      }
    if (pic_order_cnt_type == 1 && !delta_pic_order_always_zero_flag) {
      delta_pic_order_cnt&#91;0&#93;                       = na
      if (pic_order_present_flag && !field_pic_flag)
        delta_pic_order_cnt&#91;1&#93;                     = na
      }
    if (redundant_pic_cnt_present_flag)
      redundant_pic_cnt                            = na
    if (slice_type == B)
      direct_spatial_mv_pred_flag                  = na
    if (slice_type == P || slice_type == SP || slice_type == B) {
      num_ref_idx_active_override_flag             = na
      if (num_ref_idx_active_override_flag) {
        num_ref_idx_l0_active_minus1               = na
        if (slice_type == B )
          num_ref_idx_l1_active_minus1             = na
        }
      }
    if (nal_unit_type == 20)
      ref_pic_list_mvc_modification()
    else
      ref_pic_list_modification()
    if ((weighted_pred_flag && (slice_type == P || slice_type == SP)) || (weighted_bipred_idc == 1 && slice_type == B))
      pred_weight_table()
    if (nal_ref_idc != 0)
      dec_ref_pic_marking()
    if (entropy_coding_mode_flag && slice_type != I && slice_type != SI)
      cabac_init_idc                               = na
    slice_qp_delta                                 = 2
    if (slice_type == SP || slice_type == SI) {
      if (slice_type == SP)
        sp_for_switch_flag                         = na
      slice_qs_delta                               = na
      }
    if (deblocking_filter_control_present_flag) {
      disable_deblocking_filter_idc                = na
      if (disable_deblocking_filter_idc != 1) {
        slice_alpha_c0_offset_div2                 = na
        slice_beta_offset_div2                     = na
        }
      }
    if (num_slice_groups_minus1 > 0 && slice_group_map_type >= 3 && slice_group_map_type <= 5)
      slice_group_change_cycle                     = na
    }
&#91;/c&#93;
我们现在来分析,我们知道码流是由一个个的NAL Unit组成的,NALU是由NALU头和RBSP数据组成,而RBSP可能是SPS,PPS,Slice或SEI,目前我们这里SEI不会出现,而且SPS位于第一个NALU,PPS位于第二个NALU,其他就是Slice(严谨点区分的话可以把IDR等等再分出来)了。foreman_part_qcif.yuv只有3帧,那这里编码出来是不是就有5个NALU?我们这里可以大胆假设,然后仔细验证。
NALU头是什么东西,参见Spec的7.3 Syntax in tabular form,如果你有看过天之骄子的文章,就知道在Spec的7.3和7.4是相对应的,所以这两部分都要看,而且7.3就是编码算法的伪代码实现。C(ategory)和Descriptor都要熟悉,f(1),u(2),b(8),ue(v)等等是什么意思,这些在7.2和9.1都有详细说明,大概说下这里比如ue(v),se(v)等等这样的就是Exp-Golomb编码,f(1),u(2)这里就是通常的按位数,比如2位的无符号整数,32位的整数等等。

所以7.3和7.4一定要看明白,只有看明白了才能在码流基础上分析H.264。

现在我们来开始分析,下面是一段H.264码流文件的十六进制数据,所以你得有个十六进制编辑器。
&#91;c&#93;
00 00 00 01 67 42 00 1E F1 61 62 62 00 00 00 01 68 C8 A1 43 88 00
&#91;/c&#93;
我们知道00 00 00 01是NALU的开始标记,所以你打开这个完整的码流文件应该可以看到5个00 00 00 01,所以这就是我们之前说的有5个NALU,分别是SPS,PPS和3个Slice。

先贴段数据,这是Spec(Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes)规定的,NALU的类型,现在我们只要看看SPS,PPS,IDR和Slice就行。
&#91;c&#93;
#define NALU_TYPE_SLICE    1
#define NALU_TYPE_DPA      2
#define NALU_TYPE_DPB      3
#define NALU_TYPE_DPC      4
#define NALU_TYPE_IDR      5
#define NALU_TYPE_SEI      6
#define NALU_TYPE_SPS      7
#define NALU_TYPE_PPS      8
#define NALU_TYPE_AUD      9
#define NALU_TYPE_EOSEQ    10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL     12
&#91;/c&#93;
A) 我们先看第一个NALU的RBSP(8个字节)
&#91;c&#93;
67 42 00 1E F1 61 62 62
&#91;/c&#93;
转换成二进制流
&#91;c&#93;
01100111 01000010 00000000 00011110 11110001 01100001 01100010 01100010
&#91;/c&#93;
先看NALU头
forbidden_zero_bit
nal_ref_idc
nal_unit_type
这三个属性共占8位(Spec上都有写,分别占1,2和5位),那我们对着解析下就看出
forbidden_zero_bit = 0 // 0
nal_ref_idc = 3 // 11
nal_unit_type = 7 // 00111
这就对了,看看
#define NALU_TYPE_SPS      7
Spec当中后面有些放在if判断里的就是只有符合某个值的时候才会出现,我们这里nal_unit_type为7,不符合,所以直接跳过,进入到RBSP当中,这里是SPS,所以对照Spec
profile_idc
constraint_set0_flag
constraint_set1_flag
constraint_set2_flag
constraint_set3_flag
constraint_set4_flag
constraint_set5_flag
reserved_zero_2bits
level_idc
seq_parameter_set_id
这几个属性,直到seq_parameter_set_id之前都还比较好解析,我们就直接写出它们的值了
profile_idc = 66 // 01000010
constraint_set0_flag = 0 // 0
constraint_set1_flag = 0 // 0
constraint_set2_flag = 0 // 0
constraint_set3_flag = 0 // 0
constraint_set4_flag = 0 // 0
constraint_set5_flag = 0 // 0
reserved_zero_2bits = 0 // 00
level_idc = 30 // 00011110
对于seq_parameter_set_id,我们看到它是ue(v),这是一种Exp-Golomb编码,每个编码所占的位数不是固定的,我们现在还剩下的数据是11110001 01100001 01100010 01100010。
公式参考Spec(9.1 Parsing process for Exp-Golomb codes),
&#91;c&#93;
leadingZeroBits = −1
for (b = 0; !b; leadingZeroBits++)
	b = read_bits(1)

codeNum = 2^(leadingZeroBits) − 1 + read_bits(leadingZeroBits)
&#91;/c&#93;
<strong>类似于2^k这种写法表示幂运算</strong>

过程就是读取1位,在这里结果是1,所以会跳出循环,但是leadingZeroBits++还是会执行,所以leadingZeroBits为0,后面read_bits也不会读取数据了。
codeNum = 2^0 - 1 + 0 = 0
也就是说编码为1的属性实际值为0
seq_parameter_set_id = 0 // Exp-Golomb解1
同样后面是if判断不会走到,现在直接到
log2_max_frame_num_minus4 = 0 // Exp-Golomb解1
pic_order_cnt_type = 0 // Exp-Golomb解1
log2_max_pic_order_cnt_lsb_minus4 = 0 // Exp-Golomb解1  

max_num_ref_frames = 10 // 这里二进制流从0001开始了(前面的4个1被上面4个属性用掉了),所以有leadingZeroBits为3,结果就是2^3 - 1 + read_bits(011)

gaps_in_frame_num_value_allowed_flag = 0 // 0

pic_width_in_mbs_minus1 = 10 // Exp-Golomb解0001 011
pic_height_in_map_units_minus1 = 8 // Exp-Golomb解00010 01

对于Exp-Golomb不明白的请参见<a href="http://guoh.ai/lifelog/2013/10/exp-golomb-coding/" target="_blank">Exponential-Golomb coding</a>解码部分,剩下的

frame_mbs_only_flag = 1 // 1

direct_8x8_inference_flag = 0 // 0
frame_cropping_flag = 0 // 0
vui_parameters_present_flag = 0 // 0

还剩下10两个位的数据没有用到,之前的这么多数据(除了NALU头之外的)都是seq_parameter_set_data,而根据Spec我们知道还有结尾补齐位
[c]
seq_parameter_set_rbsp( ) {
	seq_parameter_set_data( ) // 数据
	rbsp_trailing_bits( ) // 按字节补齐
}

补齐规则参见7.3.2.11 RBSP trailing bits syntax,实际就是按照字节对齐来补齐,所以这就是10这两位数据的由来。

回头看起来,这就是SPS的数据,也就是第一个NALU,同前面从Headers Info拷贝出来的SPS也是完全吻合的,所以这里我们就算是把Spec和实际的用法/码流对照起来了。另外值得说一下的就是从Headers Info拷贝出来的数据当中”na”就是未定义的,也就是if条件没有覆盖的情况。

B) 现在我们以同样的方式来看PPS(5个字节)

68 C8 A1 43 88

转换成二进制流

01101000 11001000 10100001 01000011 10001000

同样先看NALU头,解析结果如下
forbidden_zero_bit = 0 // 0
nal_ref_idc = 3 // 11
nal_unit_type = 8 // 01000
也就对应于
#define NALU_TYPE_PPS 8
就可以知道此处的RBSP是PPS
pic_parameter_set_id = 0 // Exp-Golomb解1
seq_parameter_set_id = 0 // Exp-Golomb解1
entropy_coding_mode_flag = 0 // 0
bottom_field_pic_order_in_frame_present_flag = 0 // 0
num_slice_groups_minus1 = 0 // Exp-Golomb解1

num_ref_idx_l0_default_active_minus1 = 9 // Exp-Golomb解000 1010
num_ref_idx_l1_default_active_minus1 = 9 // Exp-Golomb解0001 010

weighted_pred_flag = 0 // 0
weighted_bipred_idc = 0 // 00

pic_init_qp_minus26 = 0 // Exp-Golomb解1
pic_init_qs_minus26 = 0 // Exp-Golomb解1
chroma_qp_index_offset = 0 // Exp-Golomb解1

deblocking_filter_control_present_flag = 0 // 0
constrained_intra_pred_flag = 0 // 0
redundant_pic_cnt_present_flag = 0 // 0

还剩下1000这四位,这就是按字节补齐的数据。

C) 这就是Slice开始的数据了
先看部分数据(前4个字节)

65 88 84 02

转换成二进制流

01100101 10001000 10000100 00000010

同样先看NALU头,解析结果如下
forbidden_zero_bit = 0 // 0
nal_ref_idc = 3 // 11
nal_unit_type = 5 // 00101
也就对应于
#define NALU_TYPE_IDR 5
可以知道这个是IDR帧(关于什么是IDR,IDR和I片有什么区别)

first_mb_in_slice = 0 // Exp-Golomb解1
slice_type = 7 // Exp-Golomb解0001000 也就是I slice,关于slice_type请参考Table 7-6 – Name association to slice_type
pic_parameter_set_id = 0 // Exp-Golomb解1

frame_num = 0 // u(v)根据占用的位数(log2_max_frame_num_minus4 + 4)解出值 // 0000

对于frame_num这个属性要特别说下,它的Descriptor是u(v),那么我们查看u(v)得知
u(n): unsigned integer using n bits. When n is “v” in the syntax table, the number of bits varies in a manner dependent on the value of other syntax elements.
也就是说这个属性占用的位数是取决于其它属性的,那就再搜索下frame_num得到
frame_num is used as an identifier for pictures and shall be represented by log2_max_frame_num_minus4 + 4 bits in the bitstream.
于是我们就大概清楚了,frame_num占用的位数跟log2_max_frame_num_minus4相关,之前在SPS当中我们知道log2_max_frame_num_minus4 = 0,所以这里frame_num占用4位,也就是0000,解析出来也就是0,另外也需要知道frame_num有很多限制,比如在IDR当中必须为0,具体参见7.4.3 Slice header semantics。这里要指出的是,这是一份完整优秀的Spec,基本上已经涵盖了我们需要的所有东西,只是需要我们去找,去分析(尽管这个过程可能很麻烦,有时让人摸不着头脑,但是需要相信我们需要的答案就在里面)。

idr_pic_id = 0 // Exp-Golomb解1

pic_order_cnt_lsb = 0 // u(v)根据占用的位数(log2_max_pic_order_cnt_lsb_minus4 + 4)解出值 // 0000

剩下的000010

现在要进入ref_pic_list_modification( )这个function了,但是里面所有if判断条件不符合

然后进入dec_ref_pic_marking( )
no_output_of_prior_pics_flag = 0 // 0
long_term_reference_flag = 0 // 0

现在只剩下0010这四位了,我们继续补充3个字节(63 61 7C)进来01100011 01100001 01111100
于是我们继续做slice_qp_delta的解码,注意这里它的Descriptor是se(v),所以要先对进Exp-Golomb解码,然后进行mapping得出值。
0010 01100011 01100001 01111100 // 这里两个0,求出Exp-Golomb编码值为00100 // 长度5,后缀为0可以被解析成2 实际可以通过Exp-Golomb(2^2 – 1 + 0)算出值为3 然后代入(-1)^(k + 1) * Ceil(k divide 2)求出值为2。详细可以参见9.1.1 Mapping process for signed Exp-Golomb codes。

slice_qp_delta = 2 // 00100 // se(v)

到这里Slice Header就解析完成了。

暂时就到这里,需要说明的是,我们只写出了前三个NALU部分解析方法(第一个Slice,也就是IDR,我们只写出了Header部分,还有数据部分我们留到后面来分析),还剩两个Slice我们留着有必要的时候来分析。

Exponential-Golomb coding

参考资料:
http://en.wikipedia.org/wiki/Exponential-Golomb_coding
http://zh.wikipedia.org/zh/指数哥伦布码
Information technology — Coding of audio-visual objects — Part 10: Advanced Video Coding

摘抄自以上资料,简单总结。

指數哥倫布碼(Exp-Golomb code),一种壓縮編碼方法。
用来表示非负整数的k阶指数哥伦布码可用如下步骤生成:
1. 将数字以二进制形式写出,去掉最低的k个比特位,之后加1
2. 计算留下的比特数,将此数减一,即是需要增加的前导零个数(注意是比特位数,非该比特串表示的值)
3. 将第一步中去掉的最低k个比特位补回比特串尾部
0阶指数哥伦布码如下所示:

0 => 1 => 1
1 => 10 => 010
2 => 11 => 011
3 => 100 => 00100
4 => 101 => 00101
5 => 110 => 00110
6 => 111 => 00111
7 => 1000 => 0001000
8 => 1001 => 0001001
...

从二进制比特串形式来看,0阶哥伦布编码的码字(codeword)由3部分构成:
码字 = [M个0] + [1] + [内容]
内容也是M位数据,所以每个0阶哥伦布码的位的长度为2M + 1,每个码字由原始的数字(codenumber)产生。

比如上面的数字7,二进制表示为111,0阶编码,所以不用去最后的比特为,加1之后变成1000,比特数为4位,所以前缀需要增加3(4-1)个0,即0001000,0阶所以结尾也不需要补码,于是最终码字就为0001000。

那如何解码呢?
参见如下伪代码

leadingZeroBits = −1
for (b = 0; !b; leadingZeroBits++)
	b = read_bits(1)

codeNum = 2^(leadingZeroBits + k) − 2^k + read_bits(leadingZeroBits + k)

这里类似于2^k这种写法表示幂运算,对于通常H.264当中用的是0阶的,所以可以简化成如下图

Exponential-Golomb Decoding

这里read_bits(…)的返回值使用高位在先的二进制无符号整数表示

二进制比特串       长度             解析值
1001             1               0
001 1001         5               5
01 1010          3               2
010              3               1
000 1011         7               10
0001 001         7               8

如上表,传入的比特串可能多余实际需要的数据,我们只需要解析需要的部分就可以了。

P.S.
为什么叫做指数哥伦布编码?
因为它对信源符号的分组大小按照指数增长。

追拍

要学H.264也不容易,涉及到的知识可谓是方方面面(代码,人体视觉系统构造,实际拍摄场景等等,因为它就是利用这些原理,来做到数据压缩的),看了一段时间的论文,书,对大体的理论知识算是有了个初步的掌握,想要一下吃进去不大现实,所以现在就先从简单的知识入手,记录一些阅读中遇到的认为有价值的知识。

今天来翻译(应该算看了英文的东西,然后用中文写点笔记)下照相有关的东西,主要参考资料Panning (camera),也就是我们日常所说的追拍。

比如你的小狗在跑,你想拍下它的样子,你就要随着它跑动的方位来移动你的镜头,当然是在拍摄过程当中,并且你移动镜头速度要和它跑动的速度差不多,就是镜头和狗相对静止,然后背景感觉在运动的效果。快门速度不能太快也不能太慢(太短了背景不会模糊,太长了整个画面都会模糊)。
效果图可以参见WikiPedia上的Racing Car,https://en.wikipedia.org/wiki/File:DTM_Mercedes_W204_DiResta09_amk.jpg。

File:DTM Mercedes W204 DiResta09 amk.jpg

当然我们这里说的追拍是指水平(horizontal plane)移动追拍,还有一种追拍是垂直移动的,叫做Tilt-shift拍摄,我们不考虑。

所以要想拍好这样的照片移动镜头的稳定性也是很重要的,所以有人把相机固定在三/单脚架上,或者更专业的是为相机架设一个固定的轨道,让相机在轨道上运行(电视里面拍戏经常见,不是吗)。

看完这个描述,你会觉得这不很简单吗?为什么会根H.264还有关系?其实是在Motion Compensation(MC)当中可能会用到这个。

参考资料:
https://en.wikipedia.org/wiki/Tilt_(camera)
https://en.wikipedia.org/wiki/Panning_(camera)

二叉树

炒现饭的帖子,内容均来自于网络或者书籍整理。

I)树
它是由N(N>=1)个有限结点组成一个具有层次关系的集合,它具有以下的特点:
a)每个结点有零个或多个子结点,没有子节点的节点称为叶节点(通常称为叶子)
b)没有父结点的结点称为根结点(通常称为根)
c)每一个非根结点有且只有一个父结点(没有多个引用指向同一个节点)
d)除了根结点外,每个子结点可以分为M个不相交的子树

|
r        55
o       /  \
o      /    \
t     88     3
.           /|\
.          / | \
.         67 7 23
l        / \     \
e       /   \     \
a      53    5     9
f
|

        Figure. A

如上图(Figure. A),它像一棵自然界中倒生长的树,每个枝丫处就是一个节点,方向是由根到叶子,它实际上是一个有向图。

这里我们介绍下节点,节点是包含一个数据项和一个(多个)指向其他节点的引用项的数据结构/记录,通常我们用类似如下代码来表示一个节点:
1)

record SinglyLinkedNode {
    next, // A reference to the next node
    data // Data or reference to data
}

2)

record DoublyLinkedNode {
    previous, // A reference to the previous node
    next, // A reference to the next node
    data // Data or reference to data
}

3)

record BinaryNode {
    parent, // A reference to the parent node                                
    left_child, // A reference to the left child node
    right_child, // A reference to the right child node
    data // Data or reference to data
}

结合上图(Figure. A)我们就很好理解了。

一些有关树的基本概念:
节点的度(node’s degree): 子节点的个数叫做节点的度
树的度: 一棵树中,最大的节点的度称为树的度
节点层次(level): 根为第一层,根的子节点为第二层,以此类推
数的高度或深度(depth): 树中最大的节点层次
兄弟节点(siblings): 具有相同父节点的节点
森林: M(M>=0)棵互不相交的树的集合(删去一棵树的根,就得到一个森林;加上一个结点作树根,森林就变为一棵树)
有序树(ordered tree)/无序树(unodered tree): 若将树中每个结点的各子树看成是从左到右有次序的(即不能互换),则称该树为有序树(ordered tree);否则称为无序树(unodered tree)
还有诸如子孙/祖先/堂兄弟节点这些概念就不列举了

上图(Figure. A)当中,树的度是3,67节点的度是2,数的深度是4,55节点是根节点等等这些基本属性我们就能很快得出。

II)二叉树
每个结点最多有两个子树的有序树,通常有左子树(left subtree)和右子树(right subtree),最大的节点的度为2,也就是二叉树的度为2。

满二叉树(full binary tree): 有时被称为proper binary tree或者2-tree或者strictly binary tree,就是每个节点有0或2个子节点。

完全二叉树(complete binary tree): 除了最后一层之外的其他层次(当然也可能包含最后一层),每个节点都包含2个子节点,并且所有节点都尽可能靠左排列

平衡二叉树: 如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树

二叉树的遍历,前序(pre-order),中序(in-order)和后序(post-order),前序是根左右,中序是左根右,后序是左右根。

一些有关二叉树的常用算法参看http://blog.csdn.net/luckyxiaoqiang/article/details/7518888

几个简单的例子:

这里的代码实现都是简单易懂的实现,帮助理解概念,没有做过优化!

参考资料:
http://en.wikipedia.org/wiki/Tree_(data_structure)
http://en.wikipedia.org/wiki/Node_(computer_science)
http://en.wikipedia.org/wiki/Branching_factor
http://en.wikipedia.org/wiki/Binary_tree

http://zhedahht.blog.163.com/blog/static/25411174201142733927831/

用Wicd替换Ubuntu 11.10 Network Manager

Ubuntu 11.10的Network Manager不是很稳定,经常断线,特别是网络状态不是很好的时候(根据自己经验总结出来,而且断了就很难连上),dmesg显示超时。实在忍不住了在网上搜搜,别人说可以用Wicd替换掉Network Manager。
动手开始

sudo apt-get install wicd
sudo aptitude remove network-manager

其实如果你比较谨慎,不想立即删除Network Manager的话,你可以先停止服务,然后再禁止掉它。

sudo /etc/init.d/network-manager stop
sudo update-rc.d network-manager disable

然后再重启wicd

sudo /etc/init.d/wicd restart

或者重启系统。

注意了,我一般是通过WPA2来加密无线连接的(也就是路由器开了这个),我们连接Access Point的时候也应当选择这个,通常我们都会输入对应的明文密码,比如SSID为”TA’S WIFI”,PASSWORD为”cheatbot”,我们直接选择对应的SSID并且输入这个明文密码就好了。

但是Wicd这里不一样的,它需要你输入一个Passphrase,也就是Use Encryption下的WPA 1/2 (Passphrase),这个不是直接的明文密码,如果你输入直接的明文密码,连接的时候就会出现密码错误,Bad Password也就是这么出来的,那么这个值从哪里来?

man wpa_passphrase
WPA_PASSPHRASE(8)                                            WPA_PASSPHRASE(8)

NAME
       wpa_passphrase - Generate a WPA PSK from an ASCII passphrase for a SSID

SYNOPSIS
       wpa_passphrase [ ssid ] [ passphrase ]

OVERVIEW
       wpa_passphrase  pre-computes  PSK  entries  for  network  configuration
       blocks of a wpa_supplicant.conf file. An ASCII passphrase and SSID  are
       used to generate a 256-bit PSK.

OPTIONS
       ssid   The SSID whose passphrase should be derived.

       passphrase
              The  passphrase  to  use.  If  not included on the command line,
              passphrase will be read from standard input.

SEE ALSO
       wpa_supplicant.conf(5) wpa_supplicant(8)

LEGAL
       wpa_supplicant is copyright (c) 2003-2007, Jouni Malinen <[email protected]>  and
       contributors.  All Rights Reserved.

       This  program  is  dual-licensed  under  both the GPL version 2 and BSD
       license. Either license may be used at your option.

                               07 September 2010             WPA_PASSPHRASE(8)

这里就可以看到实际是执行如下命令

guohai@KNIGHT:~$ wpa_passphrase "TA'S WIFI" "cheatbot"
network={
	ssid="TA'S WIFI"
	#psk="cheatbot"
	psk=f122b3c5a2c1b50064805d44fa8b23802dadfdfa6e2dbb73bdf3d2ee702a4146
}

这里长长的psk就是需要我们填到Properties的Key里面去的,填好这些之后,试着连连看(如果连不上,就重启下系统,我是这样的,然后我还勾选了前面的Automatically connect to this network),然后它就能成功连接上了。

当然稳定性还有待观测,不过到现在还没有出现过之前断开的情况,虽然Wicd没有默认的Network Manager直观,好看,但是只要它稳定好用,我也就满意了。

手工进行图片Huffman编码

这里有篇文章讲解如何进行Huffman编码的JPEG Huffman Coding Tutorial,写的还是很清晰,我本来打算翻译一下这篇文章,但是想想觉得还是配合它的思路以不同的角度再写一篇,它是解码方式,我这里是以编码方式来描述,它的Y:Cb:Cr是1:1:1,我这里是4:1:1,当然它是英文的,我是中文的 @@。

JPEG/Huffman编码基础知识不了解的还是要先学习下基础知识,可以参考JPEG学习笔记,否则你可能无法看懂这里在说什么。
简单回顾下JPEG编码过程,这里我们假设RGB->JPEG:

色彩空间转换
正向离散余弦变换
量化(需要量化表)
Huffman编码(需要Huffman编码表)
生成图片流(按照JFIF格式)

其它的我们都不说了,直接开始吧,如果你现在觉得上面的概念/原理还不是很理解,还来的急,请跳转到文章顶部从头开始读。
有些中间数据的推理是通过JPEGsnoop或者手工进行的,所以你也要熟悉。

假设我们有RGB数据,这里我用的是test.bmp,一张16×8的的白黑位图(左DU白,右DU黑),可以用16进制工具打开来查看,实际数据就是:

FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

假设我们有SOF数据
8位数据样本(1字节)
图像高度为8(2字节)
图像宽度为16(2字节)
颜色分量数,JPEG都是YCrCb,即3(1字节)
颜色分量信息,大小是颜色分量数 multiply 3
因为分量信息中颜色分量ID占用1个字节,水平/垂直因子占用1个字节(高4位水平,低4位垂直),量化表占用1个字节
H 2:1:1
V 2:1:1
所以总体采样因子就是(2 * 2):(1 * 1):(1 * 1),即4:1:1

MCU宽是水平采样因子最大值 multiply 8(记该最大值为Hmax)
MCU高是垂直采样因子最大值 multiply 8(记该最大值为Vmax)
因此这里就是(Hmax * 8):(Vmax * 8) = 16:16

如果整幅图片的高度或者宽度不是MCU的整数倍,就需要padding,解码之后丢弃大于宽度或者高度部分的数据
在数据流当中,MCU是按从左到右,从上到下来排列的。
因为每个MCU由若干数据单元组成,而数据单元又必须是8:8的,所以MCU当中数据单元的个数就是4(Hmax * Vmax)

组装起来可能就如下:

FF C0 00 11 08 00 08 00 10 03 01 22 00 02 11 01 03 11 01

DHT(FF C4)如下(这里的Huffman编码表是标准的,没有优化过的)

FF C4 00 1F 00 00 01 05 01 01 01 01 01 01 00 00 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B FF C4 00 B5 10 00 02 01 03 03 02 04 03 05 05 04 04 00 00 01 7D 01 02 03 00 04 11 05 12 21 31 41 06 13 51 61 07 22 71 14 32 81 91 A1 08 23 42 B1 C1 15 52 D1 F0 24 33 62 72 82 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FF C4 00 1F 01 00 03 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 01 02 03 04 05 06 07 08 09 0A 0B FF C4 00 B5 11 00 02 01 02 04 04 03 04 07 05 04 04 00 01 02 77 00 01 02 03 11 04 05 21 31 06 12 41 51 07 61 71 13 22 32 81 08 14 42 91 A1 B1 C1 09 23 33 52 F0 15 62 72 D1 0A 16 24 34 E1 25 F1 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 F7 F8 F9 FA

重构Huffman编码表(这样才比较好理解,编码表如何构建出来,参考[JPEG学习笔记],这里只列出需要用到的,部分有省略),4张表分别为直流0(直流Y),交流0(交流Y),直流1(直流C),交流1(交流C)

*** Marker: DHT (Define Huffman Table) (xFFC4) ***
  OFFSET: 0x000000B1
  Huffman table length = 31
  ----
  Destination ID = 0
  Class = 0 (DC / Lossless Table)
    Codes of length 01 bits (000 total): 
    Codes of length 02 bits (001 total): 00 
    Codes of length 03 bits (005 total): 01 02 03 04 05 
    Codes of length 04 bits (001 total): 06 
    Codes of length 05 bits (001 total): 07 
    Codes of length 06 bits (001 total): 08 
    Codes of length 07 bits (001 total): 09 
    Codes of length 08 bits (001 total): 0A 
    Codes of length 09 bits (001 total): 0B 
    Codes of length 10 bits (000 total): 
    Codes of length 11 bits (000 total): 
    Codes of length 12 bits (000 total): 
    Codes of length 13 bits (000 total): 
    Codes of length 14 bits (000 total): 
    Codes of length 15 bits (000 total): 
    Codes of length 16 bits (000 total): 
    Total number of codes: 012

Length       Codeword        Code
2            00              00(End of Block)
3            010             01
3            011             02
3            100             03
3            101             04
3            110             05
4            1110            06
5            1111 0          07
6            1111 10         08
7            1111 110        09
8            1111 1110       0A
9            1111 1111 0     0B


*** Marker: DHT (Define Huffman Table) (xFFC4) ***
  OFFSET: 0x000000D2
  Huffman table length = 181
  ----
  Destination ID = 0
  Class = 1 (AC Table)
    Codes of length 01 bits (000 total): 
    Codes of length 02 bits (002 total): 01 02 
    Codes of length 03 bits (001 total): 03 
    Codes of length 04 bits (003 total): 00 04 11 
    Codes of length 05 bits (003 total): 05 12 21 
    Codes of length 06 bits (002 total): 31 41 
    Codes of length 07 bits (004 total): 06 13 51 61 
    Codes of length 08 bits (003 total): 07 22 71 
    Codes of length 09 bits (005 total): 14 32 81 91 A1 
    Codes of length 10 bits (005 total): 08 23 42 B1 C1 
    Codes of length 11 bits (004 total): 15 52 D1 F0 
    Codes of length 12 bits (004 total): 24 33 62 72 
    Codes of length 13 bits (000 total): 
    Codes of length 14 bits (000 total): 
    Codes of length 15 bits (001 total): 82 
    Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 
                                         37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 
                                         57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 
                                         77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 
                                         96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 
                                         B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA 
                                         D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 
                                         E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA 
    Total number of codes: 162

Length  Codeword                    Code
2       00                          01
2       01                          02
3       100                         03
4       1010                        00(End of Block)
4       1011                        04
4       1100                        11
5       1101 0                      05
5       1101 1                      12
5       1110 0                      21
6       1110 10                     31
6       1110 11                     41
7       1111 000                    06
7       1111 001                    13
7       1111 010                    51
7       1111 011                    61
......


*** Marker: DHT (Define Huffman Table) (xFFC4) ***
  OFFSET: 0x00000189
  Huffman table length = 31
  ----
  Destination ID = 1
  Class = 0 (DC / Lossless Table)
    Codes of length 01 bits (000 total): 
    Codes of length 02 bits (003 total): 00 01 02 
    Codes of length 03 bits (001 total): 03 
    Codes of length 04 bits (001 total): 04 
    Codes of length 05 bits (001 total): 05 
    Codes of length 06 bits (001 total): 06 
    Codes of length 07 bits (001 total): 07 
    Codes of length 08 bits (001 total): 08 
    Codes of length 09 bits (001 total): 09 
    Codes of length 10 bits (001 total): 0A 
    Codes of length 11 bits (001 total): 0B 
    Codes of length 12 bits (000 total): 
    Codes of length 13 bits (000 total): 
    Codes of length 14 bits (000 total): 
    Codes of length 15 bits (000 total): 
    Codes of length 16 bits (000 total): 
    Total number of codes: 012

Length     Codeword            Code
2          00                  00(End of Block)
2          01                  01
2          10                  02
3          110                 03
4          1110                04
5          1111 0              05
6          1111 10             06
7          1111 110            07
8          1111 1110           08
9          1111 1111 0         09
10         1111 1111 10        0A
11         1111 1111 110       0B


*** Marker: DHT (Define Huffman Table) (xFFC4) ***
  OFFSET: 0x000001AA
  Huffman table length = 181
  ----
  Destination ID = 1
  Class = 1 (AC Table)
    Codes of length 01 bits (000 total): 
    Codes of length 02 bits (002 total): 00 01 
    Codes of length 03 bits (001 total): 02 
    Codes of length 04 bits (002 total): 03 11 
    Codes of length 05 bits (004 total): 04 05 21 31 
    Codes of length 06 bits (004 total): 06 12 41 51 
    Codes of length 07 bits (003 total): 07 61 71 
    Codes of length 08 bits (004 total): 13 22 32 81 
    Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 
    Codes of length 10 bits (005 total): 09 23 33 52 F0 
    Codes of length 11 bits (004 total): 15 62 72 D1 
    Codes of length 12 bits (004 total): 0A 16 24 34 
    Codes of length 13 bits (000 total): 
    Codes of length 14 bits (001 total): E1 
    Codes of length 15 bits (002 total): 25 F1 
    Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 
                                         44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 
                                         64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 
                                         83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 
                                         9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 
                                         B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 
                                         D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 
                                         F4 F5 F6 F7 F8 F9 FA 
    Total number of codes: 162

Length  Codeword                    Code
2       00                          00(End of Block)
2       01                          01
3       100                         02
4       1010                        03
4       1011                        11
5       1100 0                      04
5       1100 1                      05
5       1101 0                      21
5       1101 1                      31
6       1110 00                     06
6       1110 01                     12
6       1110 10                     41
6       1110 11                     51
......

DQT(FF DB)如下

FF DB 00 43 00 05 03 04 04 04 03 05 04 04 04 05 05 05 06 07 0C 08 07 07 07 07 0F 0B 0B 09 0C 11 0F 12 12 11 0F 11 11 13 16 1C 17 13 14 1A 15 11 11 18 21 18 1A 1D 1D 1F 1F 1F 13 17 22 24 22 1E 24 1C 1E 1F 1E
FF DB 00 43 01 05 05 05 07 06 07 0E 08 08 0E 1E 14 11 14 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E 1E

0x0043为所占大小
1个字节为QT信息,高4位为QT精度(在这里精度数据都是0),低4位为QT号
64字节的QT数据
还原成矩阵形式(Zigzag)
QT 0

5   3   3   5   7   12  15  18

4   4   4   6   8   17  18  17

4   4   5   7   12  17  21  17

4   5   7   9   15  16  24  19

5   7   11  17  20  33  31  23

7   11  17  19  24  31  34  28

15  19  23  26  31  36  36  30

22  28  29  29  34  30  31  30

QT 1

前提条件都有了,开始做变换了。
通过SOF数据我们知道有1个MCU,Y有4个DU(2个是真实的数据,2个是填充),Cb有一个DU,Cr有一个DU。

RGB

DU 1(Y)

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

255  255  255  255  255  255  255  255

DU 1(-128)

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

127  127  127  127  127  127  127  127

DU 1(FDCT)

1016 0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

DU 1(Quantization)

203  0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

DU 2(Y)

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

DU 2(-128)

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

-128 -128 -128 -128 -128 -128 -128 -128

DU 2(FDCT)

-1024 0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

DU 2(Quantization)

-205 0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

0    0    0    0    0    0    0    0

于是得到
DC分别为203和-205(绝对),转换为相对则是203和-408
AC都为0

其它为0或者填充数据暂时就不用管了。

现在进行Huffman编码
203 = 1100 1011(查表或者计算都可以得出,最高位为1表示正数,最高位为0表示负数,取反得出其值),继而DC编码前缀为1111 10(根据Y的DC表可以得出,数据占8位)
-408 = 0011 0011 1

1111 10 1100 1011 // 直流Y
1111 10 1100 1011 1010 // 直流Y + 交流Y
1111 10 1100 1011 1010 1111 110 0011 0011 1 1010 // 直流Y + 交流Y + 直流Y + 交流Y
1111 10 1100 1011 1010 1111 110 0011 0011 1 1010 00 1010 00 1010 // 直流Y + 交流Y + 直流Y + 交流Y + 2个填充DU(因为MCU为4个DU)
1111 10 1100 1011 1010 1111 110 0011 0011 1 1010 00 1010 00 1010 00 00 // 直流Y + 交流Y + 直流Y + 交流Y + 2个填充DU(因为MCU为4个DU + 直流Cb + 交流Cb
1111 10 1100 1011 1010 1111 110 0011 0011 1 1010 00 1010 00 1010 00 00 00 00 // 直流Y + 交流Y + 直流Y + 交流Y + 2个填充DU(因为MCU为4个DU + 直流Cb + 交流Cb + 直流Cr + 交流Cr
1111 10 1100 1011 1010 1111 110 0011 0011 1 1010 00 1010 00 1010 00 00 00 00 111111 // 直流Y + 交流Y + 直流Y + 交流Y + 2个填充DU(因为MCU为4个DU + 直流Cb + 交流Cb + 直流Cr + 交流Cr + 填充位

去掉空格
1111101100101110101111110001100111101000101000101000000000111111
也就是说数据其实只有58位,补齐到字节对齐,共占64位,就得到了我们最终要的数据。
16进制表示
FB 2E BF 19 E8 A2 80 3F

这下你或许对这复杂的JPEG原理有所了解了吧,但是记住这才是开始(当然你无法全手工的为一张复杂的图片来编码,这篇文章只是帮助理解原理),JPEG正真复杂的是它对各种算法的优化,那才是重点。