目录

  • 概要
  • 输入数据
  • 宏块

概要

本文的源码基于复旦大学的开源芯片—开源H.265/H.264视频编码器项目,本文的工作主要是在梳理源码的同时学习H.264视频编解码的原理及其硬件实现。

输入数据

CSDN的编辑器么有verilog格式…是看不起我HDL了咩!(狗头),所以暂时用C的格式代替吧,感觉好看一点(颜控昂)

读取YUV格式的视频数据,放在pixel_ram

$readmemh(YUV_FILE, pixel_ram);

变量的定义:

reg [31:0]                       addr_r, cnt;
reg [31:0]                      pixel_ram[1<<25:0];  // 1左移25位

这里可以稍微计算一下,pixel_ram的容量是2^25+1,即4MB
实际上使用的文件的大小:

寄存器数组pixel_ram中的数据读取:

always @(posedge clk or negedge rst_n) beginif (!rst_n) beginrdata_i  <= 'b0;rvalid_i <= 1'b0;addr_r   <= 'b0;end else if (rinc_o && cnt!='d48) beginrdata_i  <= {pixel_ram[2*addr_r+0], pixel_ram[2*addr_r+1]};rvalid_i <= 1'b1;addr_r   <= addr_r+1;endelse beginrdata_i  <= 'b0;rvalid_i <= 1'b0;addr_r   <= addr_r;end
end

rinc_o相当于一个计数使能信号,cnt计数到48就清零一次
每次读取两个地址的数据,也就是64bit,赋值给rdata_i

top u_top     (.clk                  ( clk               ),.rst_n                ( rst_n             ),.sys_start            ( sys_start         ),      .sys_done           ( sys_done          ),      .sys_intra_flag     ( sys_intra_flag    ), .sys_qp              ( sys_qp            ),         .sys_mode            ( sys_mode          ),       .sys_x_total       ( sys_x_total       ),    .sys_y_total      ( sys_y_total       ),  .enc_ld_start       ( enc_ld_start      ),          .enc_ld_x           ( enc_ld_x          ),  .enc_ld_y           ( enc_ld_y          ),.rdata_i              ( rdata_i           ),  // 数据输入.rvalid_i            ( rvalid_i          ),.rinc_o               ( rinc_o            ),.wdata_o              ( wdata_o           ),.wfull_i              ( wfull_i           ),.winc_o               ( winc_o            ),.ext_mb_x_o       ( ext_mb_x          ),         .ext_mb_y_o      ( ext_mb_y          ), .ext_start_o     ( ext_start         ),        .ext_done_i       ( ext_done          ),        .ext_mode_o       ( ext_mode          ),   .ext_wen_i         ( ext_wen           ),  .ext_ren_i          ( ext_ren           ),.ext_addr_i         ( ext_addr          ),.ext_data_i         ( ext_data_i        ),.ext_data_o         ( ext_data_o        )
);

在综合后的模块连接图中追踪这个输入信号

可以看到rdata_i是连接到了cur_mb模块的pdata_i即输入,rvalid_i连接到该模块的pvalid_i

宏块

在H.264进行编码的过程中,每一帧的H图像被分为一个或多个slice(条带)进行编码。每个条带包含多个Macroblock宏块)。宏块是H.264标准中的基本编码单元,其基本结构包含一个1616亮度像素块和两个88色度像素块。每一个宏块会分割成多种不同大小的子块进行预测。帧内预测采用的块大小可能为1616或44;而帧间预测采用的块可能有7种不同的形状:1616,168,816,88,84,48,4*4

assign addr_y = addr_p[4:0];
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin:cur_lumainteger i;for(i=0; i<256; i=i+1) begincur_y[i] <= 0;endend              else if(pvalid_i && ~addr_p[5]){cur_y[{addr_y,3'b000}],cur_y[{addr_y, 3'b001}],cur_y[{addr_y, 3'b010}],cur_y[{addr_y, 3'b011}],cur_y[{addr_y,3'b100}],cur_y[{addr_y, 3'b101}],cur_y[{addr_y, 3'b110}],cur_y[{addr_y, 3'b111}]}<= pdata_i;
end

数据的分割方法如上
addr_p的计数方法:

always @(posedge clk or negedge rst_n)beginif(!rst_n)pinc_o <= 1'b0;else if((addr_p == 8'd47) && pvalid_i) // read complete pinc_o <= 1'b0;    else if(load_start)pinc_o <= 1'b1;
end

可以看到同样是计数48次归零
归零之后pinc_0置零,从前面的电路连接图可以看到这个变量作为一个输出变量,连接到top模块的rinc_o,而只有rinc_o为1时,才会从RAM中读取数据
也就是说,pinc_0是一个指示top模块,可以读取下一个像素的指示信号

addr_p计数到48,也就是0x110000,而addr_y取其低5位,也就是0x10000

reg [7:0] cur_y[0:255];

前面定义了cur_y是一个大小为256,每个元素8bit的寄存器数组
复位的时候将其所有值都置零
pvalid_i=1指示输入数据有效,并且addr_p[5]=0的时候,也就是addr_p正在向上计数的时候,此时addr_y同样在向上计数
每次将pdata_i64bit的数据,分配给从{addr_y,3'b000}{addr_y,3'b111}的八个reg,连续赋值,中间没有空的寄存器
注意大小端,应该是寄存器中数值小的分配到了data中的高位的数据

下面看一下cur_y的数据跑到哪里去了

always @(posedge clk or negedge rst_n) beginif (!rst_n) begin:y_s0integer i;for(i=0; i<256; i=i+1) begincur_y_s0[i] <= 0;cur_y_s1[i] <= 0;endendelse if(mb_switch)begin:y_s1integer i;for(i=0; i<256; i=i+1)begincur_y_s0[i] <= cur_y[i];cur_y_s1[i] <= cur_y_s0[i];endend
end

做了一个消除亚稳态的处理,打了两拍
追踪数据cur_y_s1

input           mb_switch;          // start current_mb pipeline
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin:y_s2integer i;for(i=0; i<256; i=i+1) begincur_y_s2[i] <= 0;endendelse if(mb_switch&&~intra_flag_i)begin:y_s2_1integer i;for(i=0; i<256; i=i+1)begincur_y_s2[i] <= cur_y_s1[i];endendelse if(mb_switch)begin:y_s2_2integer i;for(i=0; i<256; i=i+1)begincur_y_s2[i] <= cur_y[i];endend
end

又打了一拍?迷惑
但注意到判断条件不同
前面的亚稳态处理,是mb_switch=1即开始两级寄存,这里是同时满足mb_switch=1intra_flag_i = 0intra_flag_i这个信号是一个输入信号

input           intra_flag_i;       // all intra prediction

从电路图上来看


也就是说这个信号是从top模块给出来的一个指示信号,在官方给出的测试文件中,在初始化时将其置零,使用时通过task调用

// -------------------------------------------------------
//                   Config Task
// -------------------------------------------------------
task start;input intra_flag;beginif (intra_flag) #100 sys_intra_flag    = 1'b1;else #100 sys_intra_flag   = 1'b0; sys_start = 1'b1;        #10  sys_start = 1'b0;#10  wait(sys_done == 1'b1);                                                               end
endtask
if (frame_num%`GOP_LENGTH=='b0)start(1);
elsestart(0);
#500;

由于它连接到了帧内预测模块,暂时记住它是一个跟预测相关的指示变量
也就是说当需要预测的时候,才会将cur_y_s1打入cur_y_s2
但是当intra_flag_i=1的时候,就直接将cur_y打入cur_y_s2中,不经过两级寄存处理
【这里暂时没明白为什么】

那么这个cur_y_s2数据是用来干啥的呢?

genvar j;
generatefor(j=0;j<256; j=j+1) begin:j_n always @( * ) beginime_cur_luma[(j+1)*8-1:j*8] = cur_y_s0[j];fme_cur_luma[(j+1)*8-1:j*8] = cur_y_s1[j];mc_cur_luma [(j+1)*8-1:j*8] = cur_y_s2[j];   endend
endgenerate

注意这三个变量ime_cur_lumafme_cur_lumamc_cur_luma都是输出变量,模块连接图如下:



分别是整像素运动估计分像素运动估计帧内预测
这三个都是H.264中非常重要的三个算法结构,我会在接下来分别重点介绍其原理及实现

这里看一下本模块中其他的信号
读完一帧后输出一个完成信号

always @(posedge clk or negedge rst_n)beginif(!rst_n)load_done <= 1'b0;else if((addr_p == 8'd47) && pvalid_i) // load complete: 16x16x1.5/8=48 cycles load_done <= 1'b1;elseload_done <= 1'b0;
end

pdata_i还有一路流向:

assign addr_uv = addr_y[3:0];
always @(posedge clk or negedge rst_n) beginif (!rst_n) begin:cur_uvinteger i;for(i=0; i<64; i=i+1) begincur_u[i] <= 0;cur_v[i] <= 0;endend else if(pvalid_i && addr_p[5])begin{cur_u[{addr_uv, 2'b00}],cur_v[{addr_uv, 2'b00}],cur_u[{addr_uv, 2'b01}],cur_v[{addr_uv, 2'b01}],cur_u[{addr_uv, 2'b10}],cur_v[{addr_uv, 2'b10}],cur_u[{addr_uv, 2'b11}],cur_v[{addr_uv, 2'b11}]} <= pdata_i;end
end

可以看到,这里和上述的区别在于,addr_uv取的是addr_y的低4bit,而非5bit

reg [7:0] cur_u[0:63];

这是一个容量为64的寄存器数组
只有当addr_p[5]=1也就是addr_p确实计数到48的时候,才会发生一次赋值
同样有消除亚稳态,也就是打两拍的处理

数据cur_u_s2cur_v_s2的去向:

genvar k;
generate for(k=0;k<64; k=k+1) begin:k_nalways @( * ) beginmc_cur_u [(k+1)*8-1:k*8] = cur_u_s2[k];mc_cur_v [(k+1)*8-1:k*8] = cur_v_s2[k];   endend
endgenerate
output [64*8-1 : 0] mc_cur_u;      // output chroma 8x8 for mc and intra
output [64*8-1 : 0] mc_cur_v;      // output chroma 8x8 for mc and intra

由此可以看到,第一部分的三个输出是作为1616的亮度块输出,而第二部分的两个输出是作为88的色度块输出
模块连接为:


宏块的产生过程就是酱紫啦~
呼~明天写帧间预测!加油!

H.264视频编解码的FPGA源码分析(一)输入数据分析相关推荐

  1. H.264视频编解码的FPGA源码分析(二)帧内预测1

    目录 帧内预测算法原理 基于论文的普通介绍 硬件实现 亮度块与色度块的划分 4×4亮度预测模块 如何产生预测像素与残差像素? 垂直模式`INTRA4x4_V` 水平模式`INTRA4x4_H` 直流模 ...

  2. H.264视频编解码的代码移植和优化

    基于DSP系统开发的视频编解码系统,国内几乎都是走的移植,优化的路线,并且移植的代码,都是开源的.毕竟花费大量的人力,物力去开发一套自己的代码,并不见得比一些成熟的开源代码效率更高,健壮性更好.更何况 ...

  3. 数据压缩12 | 实验8 | H.264视频编解码

    目录 一.实验准备 1. H.264编码过程 2. 调试和编码(参考JM Reference Software Manual (JVT-AE010)) 3. 编码参数(参考JM Reference S ...

  4. 音视频编解码技术(一):MPEG-4/H.264 AVC 编解码标准

    一.H264 概述 H.264,通常也被称之为H.264/AVC(或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC) 1. H.264视频编解码的意义 H.264的出现就是为了创 ...

  5. 解析H.264视频编解码DSP实现与优化

    引言 基于互联网的数字视频产业前景看好,而3G的规模部署,也会推动移动视频通信成为现实.但数字化后的视频图像具有数据海量性,给图像的存储和传输造成较大的困难.数字视频产业,是指数字内容中以数字视频形态 ...

  6. H.264及编解码调试

    H.264及编解码调试 H.264是国际标准化组织(ISO)和国际电信联盟(ITU)在2002年12月共同提出的继MPEG-4之后的新一代数字视频压缩格式,其具有更高的编码效率,并注重对移动和IP网络 ...

  7. AIR 3实现iOS下对H.264视频硬件解码

    本帖最后由 nextria 于 2011-10-9 11:27 编辑 在此之前,AIR开发的应用程序在iOS下是不可能实现对H.264视频硬解码的, 也许是以一个多比特率的方式传送. 我知道,是吧? ...

  8. WebRTC[1]-WebRTC中h264解码过程的源码分析

    目录 前言 正文 <WebRTC工作原理精讲>系列-总览_liuzhen007的专栏-CSDN博客_webrtc 原理前言欢迎大家订阅Data-Mining 的<WebRTC工作原理 ...

  9. H.266视频编解码标准(H.266的新技术介绍)

    H.266多功能视频编码编解码器标准,新的H.266 /VCC编解码器在保持清晰度不变的情况下,数据压缩效率获得极大提高,数据量减少了50%. 新标准的公告中指出,由于改进了压缩技术,H.266将减少 ...

最新文章

  1. nginx虚拟主机解析php文件,window停nginx虚拟主机不能解析php
  2. [CODEVS1205]单词反转
  3. asp.net faq: 在html文件中,用js获取session
  4. 基于asp.net的Web开发架构探索(转)
  5. 更新10_linux,时隔十年,QQ更新了Linux版本
  6. Linux系统管理_附加控制权限-Redhat Enterprise 5
  7. java流水号自增长_Java自增流水号生成
  8. 数据导入与预处理-第8章-实战演练-数据分析师岗位分析
  9. Balanced Array
  10. python1到100奇数和_python计算1~100的和,1~100奇数的和,1~100偶数的和,一条代码求1~100的和...
  11. 【2016新年版】年度精品 XP,32/64位Win7,32/64位Win8,32/64位Win10系统
  12. 浮点数修约的法则c语言,IEEE754浮点表示法详解
  13. 格子玻尔兹曼机(Lattice Boltzmann Method)系列3:LBM在不可压缩流动下的边界条件算法
  14. 日语 罗马拼音 输入法规则
  15. arduino点灯程序
  16. Linux 设备驱动程序(二)
  17. Mac安装node遇到的问题记录
  18. [飞桨机器学习]六种常见数据降维
  19. 运用c++打印一个日历
  20. python 3.7.0如何使用_python 3.7.0 安装配置方法图文教程

热门文章

  1. sqlserver安装目录_SQL server2012安装图解及意外解决
  2. Parity check(奇偶校验)
  3. 加载TQ2440驱动-Hello World 模块
  4. iso9001认证机构哪家好
  5. 防止开发人员开发后门程序,数据库安全之应用程序调用的危险
  6. 产品经理和程序员之间的选择?
  7. 合作伙伴眼中的鸿蒙,专访海雀科技研发总监李尹
  8. day01 计算机的基本知识
  9. 完美世界游戏客户端程序面试
  10. 【Web技术】772- Web 中文字体性能优化实践