H.264视频编解码的FPGA源码分析(一)输入数据分析
目录
- 概要
- 输入数据
- 宏块
概要
本文的源码基于复旦大学的开源芯片—开源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_i
64bit的数据,分配给从{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=1
和intra_flag_i = 0
,intra_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_luma
、fme_cur_luma
、mc_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_s2
和cur_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源码分析(一)输入数据分析相关推荐
- H.264视频编解码的FPGA源码分析(二)帧内预测1
目录 帧内预测算法原理 基于论文的普通介绍 硬件实现 亮度块与色度块的划分 4×4亮度预测模块 如何产生预测像素与残差像素? 垂直模式`INTRA4x4_V` 水平模式`INTRA4x4_H` 直流模 ...
- H.264视频编解码的代码移植和优化
基于DSP系统开发的视频编解码系统,国内几乎都是走的移植,优化的路线,并且移植的代码,都是开源的.毕竟花费大量的人力,物力去开发一套自己的代码,并不见得比一些成熟的开源代码效率更高,健壮性更好.更何况 ...
- 数据压缩12 | 实验8 | H.264视频编解码
目录 一.实验准备 1. H.264编码过程 2. 调试和编码(参考JM Reference Software Manual (JVT-AE010)) 3. 编码参数(参考JM Reference S ...
- 音视频编解码技术(一):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的出现就是为了创 ...
- 解析H.264视频编解码DSP实现与优化
引言 基于互联网的数字视频产业前景看好,而3G的规模部署,也会推动移动视频通信成为现实.但数字化后的视频图像具有数据海量性,给图像的存储和传输造成较大的困难.数字视频产业,是指数字内容中以数字视频形态 ...
- H.264及编解码调试
H.264及编解码调试 H.264是国际标准化组织(ISO)和国际电信联盟(ITU)在2002年12月共同提出的继MPEG-4之后的新一代数字视频压缩格式,其具有更高的编码效率,并注重对移动和IP网络 ...
- AIR 3实现iOS下对H.264视频硬件解码
本帖最后由 nextria 于 2011-10-9 11:27 编辑 在此之前,AIR开发的应用程序在iOS下是不可能实现对H.264视频硬解码的, 也许是以一个多比特率的方式传送. 我知道,是吧? ...
- WebRTC[1]-WebRTC中h264解码过程的源码分析
目录 前言 正文 <WebRTC工作原理精讲>系列-总览_liuzhen007的专栏-CSDN博客_webrtc 原理前言欢迎大家订阅Data-Mining 的<WebRTC工作原理 ...
- H.266视频编解码标准(H.266的新技术介绍)
H.266多功能视频编码编解码器标准,新的H.266 /VCC编解码器在保持清晰度不变的情况下,数据压缩效率获得极大提高,数据量减少了50%. 新标准的公告中指出,由于改进了压缩技术,H.266将减少 ...
最新文章
- nginx虚拟主机解析php文件,window停nginx虚拟主机不能解析php
- [CODEVS1205]单词反转
- asp.net faq: 在html文件中,用js获取session
- 基于asp.net的Web开发架构探索(转)
- 更新10_linux,时隔十年,QQ更新了Linux版本
- Linux系统管理_附加控制权限-Redhat Enterprise 5
- java流水号自增长_Java自增流水号生成
- 数据导入与预处理-第8章-实战演练-数据分析师岗位分析
- Balanced Array
- python1到100奇数和_python计算1~100的和,1~100奇数的和,1~100偶数的和,一条代码求1~100的和...
- 【2016新年版】年度精品 XP,32/64位Win7,32/64位Win8,32/64位Win10系统
- 浮点数修约的法则c语言,IEEE754浮点表示法详解
- 格子玻尔兹曼机(Lattice Boltzmann Method)系列3:LBM在不可压缩流动下的边界条件算法
- 日语 罗马拼音 输入法规则
- arduino点灯程序
- Linux 设备驱动程序(二)
- Mac安装node遇到的问题记录
- [飞桨机器学习]六种常见数据降维
- 运用c++打印一个日历
- python 3.7.0如何使用_python 3.7.0 安装配置方法图文教程