=====================================================
HEVC源代码分析文章列表:
【解码 -libavcodec HEVC 解码器】
FFmpeg的HEVC解码器源代码简单分析:概述
FFmpeg的HEVC解码器源代码简单分析:解析器(Parser)部分
FFmpeg的HEVC解码器源代码简单分析:解码器主干部分
FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-PU
FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-TU
FFmpeg的HEVC解码器源代码简单分析:环路滤波(LoopFilter)
=====================================================
从这篇文章开始,简单分析记录FFmpeg中libavcodec的HEVC(H.265)解码器(HEVC Decoder)的源代码。本文综述整个解码器的框架,后续几篇文章再对解码器的内部模块进行分析。
函数调用关系图
FFmpeg的HEVC(H.265)解码器的函数调用关系图如下所示。
FFmpeg和HEVC解码器之间作为接口的结构体
FFmpeg和HEVC解码器之间作为接口的结构体有2个:ff_hevc_parser和ff_hevc_decoder。
ff_hevc_parser
ff_hevc_parser是用于解析HEVC码流的AVCodecParser结构体。AVCodecParser中包含了几个重要的函数指针:
parser_init():初始化解析器。
parser_parse():解析。
parser_close():关闭解析器。
在ff_hevc_parser结构体中,上述几个函数指针分别指向下面几个实现函数:
hevc_init():初始化HEVC解析器。
hevc_parse():解析HEVC码流。
hevc_close():关闭HEVC解析器。
ff_hevc_decoder
ff_hevc_decoder是用于解码HEVC(H.265)码流的AVCodec结构体。AVCodec中包含了几个重要的函数指针:
init():初始化解码器。
decode():解码。
close():关闭解码器。
在ff_hevc_decoder结构体中,上述几个函数指针分别指向下面几个实现函数:
hevc_decode_init():初始化HEVC解码器。
hevc_decode_frame():解码HEVC码流。
hevc_decode_free():关闭HEVC解码器。
解析函数(Parser)
解析函数(Parser)用于解析HEVC码流中的一些信息(例如SPS、PPS、Slice Header等)。在parse_nal_units()和decode_nal_units()中都调用这些解析函数完成了解析。下面举几个解析函数的例子:
ff_hevc_decode_nal_vps():解析VPS。
ff_hevc_decode_nal_sps():解析SPS。
ff_hevc_decode_nal_pps():解析PPS。
ff_hevc_decode_nal_sei():解析SEI。
普通内部函数
普通内部函数指的是HEVC解码器中还没有进行分类的函数。下面举几个例子。
ff_hevc_decoder中hevc_decode_init()调用的初始化函数:
hevc_init_context():初始化HEVC解码器上下文结构体。
hevc_decode_extradata():解析AVCodecContext中的extradata。
ff_hevc_decoder中hevc_decode_frame()逐层调用的和解码Slice相关的函数:
decode_nal_units(),decode_nal_unit(),hls_slice_data(),hls_decode_entry()等。
ff_hevc_decoder中hevc_parse()逐层调用的和解析Slice相关的函数:
hevc_find_frame_end():查找NALU的结尾。
parse_nal_units():解析一个NALU。
hls_decode_entry()
hls_decode_entry()是FFmpeg的HEVC解码器真正的解码函数。其中调用了解码函数和滤波函数HEVC中的CTU进行处理。在HEVC中CTU(Coding tree unit, 编码树单元)即对应H.264中的MB(Macroblock, 宏块)。
解码函数(Decode)
解码函数(Decode)通过帧内预测、帧间预测等方法解码CTU压缩数据。CTU解码模块对应的函数是hls_coding_quadtree()。hls_coding_quadtree()用于解析HEVC码流的四叉树结构的句法,是一个递归调用的函数。当解析到单个CU的时候,会调用CU的解码函数hls_coding_unit()。
hls_coding_unit()会调用hls_prediction_unit()和hls_transform_tree()分别对CU中的PU和TU进行处理。hls_prediction_unit()会调用luma_mc_uni()或者调用luma_mc_bi()进行预测。hls_transform_tree()用于解析TU的四叉树结构的句法,是一个递归调用的函数。当解析到单个TU的时候,会调用hls_transform_unit()对TU进行处理。
环路滤波函数(Loop Filter)
环路滤波函数(Loop Filter)对解码后的数据进行滤波,去除方块效应和振铃效应。滤波模块对应的环路滤波函数是ff_hevc_hls_filters()。ff_hevc_hls_filters()调用了ff_hevc_hls_filter()。而ff_hevc_hls_filter()调用去块效应滤波器函数deblocking_filter_CTB()去除解码过程中的块效应;调用SAO滤波器函数sao_filter_CTB()去除解码过程中的振铃效应。
汇编函数(Assembly)
汇编函数(Assembly)是做过汇编优化的函数。为了提高效率,整个HEVC解码器中包含了大量的汇编函数。实际解码的过程中,FFmpeg会根据系统的特性调用相应的汇编函数(而不是C语言函数)以提高解码的效率。如果系统不支持汇编优化的话,FFmpeg才会调用C语言版本的函数。
至此FFmpeg的HEVC解码器的结构就大致梳理完毕了。
从这篇文章开始,简单分析记录FFmpeg中libavcodec的HEVC(H.265)解码器(HEVC Decoder)的源代码。本文综述整个解码器的框架,后续几篇文章再对解码器的内部模块进行分析。
FFMPEG
工程浩大,可以参考的书籍又不是很多,因此很多刚学习
FFMPEG
的人常常感觉到无从下手。因此特地分离出了一个
简单
的
视频编码
器供学习之用。
此前做过一个YUV420P像素数据编码为H.264码流的例子。对这个例子进行了升级。升级后编码器实现了YUV420P像素数据编码为H.265码流。
尽管该
视频编码
器的代码十分
简单
,但是几乎包含了使用
FFMPEG
编码一个视频所有必备的API。十分适合
FFmpeg
的初学者。
工程基于VC2010。
使用了2014.9.16编译的
FFmpeg
类库。">
FFMPEG
工程浩大,可以参考的书籍又不是很多,因此很多刚学习
FFMPEG
的人常常感觉到无从下手。因此特地分离出了一个
简单
的
视频编码
器供学习之用。
此前做过一个YUV420P像素数据编码为H.264码流的例子。对这个例子进行了升级。? [更多]
1. Build x265
$wgethttps://bitbucket.org/multicorew ... s/x265_3.2.1.tar.gz
$tar zxvf x265_3.2.1.tar.gz
$cd x265_3.2.1/build/linux
$./make-Makefiles.bash
$make && make install2...
免费下载:https://www.microsoft.com/store/productId/9N4WGH0Z6VHQ
正规的
HEVC
解码器
在微软商店里面售价是7元,但实际上这个APP是免费的,以上是正规免费的安装地址。安装后系统自带的UWP视频播放器【电影和电视】将能够播放 H.265 视频,并对 HDR 提供支持。
当然了,很多第三方软件也提供了解码支持,比如收费的PowerDVD和免费...
Flash Video(简称FLV),是一种网络视频格式,用作串流媒体格式,它的出现有效地解决了视频文件导入Flash后,使导出的SWF文件体积庞大,不能在网络上有效使用等缺点。
HEVC
是High Efficiency Video Coding的缩写(又称为H.265和MPEG-H第2部分),是一种新的视频压缩标准,用来以替代H.264/AVC编码标准.
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#define INBUF_SIZE 4096
int main(int argc, char **argv)
AVFormatContext *fmt_ctx = NULL;
AVCodecContext *dec_ctx = NULL;
AVCodec *dec = NULL;
AVPacket pkt;
AVFrame *frame = NULL;
int stream_index = 0, ret = 0, got_frame = 0;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data = NULL;
size_t data_size = 0;
if (argc <= 1) {
fprintf(stderr, "Usage: %s <input file>\n", argv[0]);
exit(0);
av_register_all();
if ((ret = avformat_open_input(&fmt_ctx, argv[1], NULL, NULL)) < 0) {
fprintf(stderr, "Could not open input file '%s' (error '%s')\n",
argv[1], av_err2str(ret));
exit(1);
if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
fprintf(stderr, "Could not find stream information (error '%s')\n",
av_err2str(ret));
exit(1);
av_dump_format(fmt_ctx, 0, argv[1], 0);
if ((stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)) < 0) {
fprintf(stderr, "Could not find audio stream in the input file\n");
exit(1);
dec_ctx = avcodec_alloc_context3(NULL);
if (!dec_ctx) {
fprintf(stderr, "Failed to allocate codec context\n");
exit(1);
if ((ret = avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[stream_index]->codecpar)) < 0) {
fprintf(stderr, "Failed to copy codec parameters to codec context (error '%s')\n",
av_err2str(ret));
exit(1);
if (!(dec = avcodec_find_decoder(dec_ctx->codec_id))) {
fprintf(stderr, "Failed to find codec\n");
exit(1);
if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) {
fprintf(stderr, "Failed to open codec (error '%s')\n", av_err2str(ret));
exit(1);
av_init_packet(&pkt);
pkt.data = inbuf;
pkt.size = 0;
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Failed to allocate frame\n");
exit(1);
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
if (pkt.stream_index == stream_index) {
ret = avcodec_decode_audio4(dec_ctx, frame, &got_frame, &pkt);
if (ret < 0) {
fprintf(stderr, "Error decoding audio frame (error '%s')\n", av_err2str(ret));
exit(1);