本文记录h264文件解码, 以及解码如何使用硬件加速
解码需要分割h264码流, 这里不介绍h264码流NALU的结构
#include <iostream>
#include <fstream>
#include "xvideo_view.h"
using namespace std;
extern "C"{
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
//预处理指令导入库
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "swscale.lib")
#define FILENAME "test_decode.h264"
int main(int argc, char* argv[])
{
//打开h264文件
ifstream ifs(FILENAME, ios::binary);
if (!ifs) return -1;
//1,创建分割h264码流上下文
VCodecParserContext* parser = av_parser_init(AV_CODEC_ID_H264);
if(!parser) return -2;
//2,创建用于存放从h264码流分割出来的编码包
AVPacket* pkt = av_packet_alloc();
if(!pkt) return -3;
//3,创建用于存放解码后的原始视频数据
AVFrame* frame = av_frame_alloc();
if(!frame) return -4;
//4,创建解码上下文
AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
AVCodecContext* c = avcodec_alloc_context3(codec);
if(!c) return -5;
c->thread_count = 16;//设置解码线程数量
/***硬件加速部分***/
AVHWDeviceType hw_type = AV_HWDEVICE_TYPE_DXVA2;//指定硬件加速的类型
AVBufferRef *hw_ctx;
av_hwdevice_ctx_create(&hw_ctx, hw_type, NULL, NULL, 0);
c->hw_device_ctx = av_buffer_ref(hw_ctx);
AVFrame* hw_frame = av_frame_alloc();//硬解码转换
//硬解码的数据存放在显存中, 需要从显存转到内存, 才能提取出视频数据
if(!hw_frame) return -6;
/***硬件加速添加以上代码块***/
//5,打开解码上下文
int ret = avcodec_open2(c, nullptr, nullptr);
if(!ret) return -7;
unsigned char inbuf[4096] = { 0 };//申请个缓冲用于读取h264
while(!ifs.eof()){
ifs.read((char*)inbuf, sizeof(inbuf));
int data_size = ifs.gcount();//读取的字节数
if(data_size <= 0) break; //读取失败
unsigned char* data = inbuf;//用于在inbuf中灵活移动的指针
while(data_size > 0){//读取成功
//6,分割h264码流到AVPacket中
ret = av_parser_parse2(
parser, //帧分割上下文
c, //解码器上下文
&pkt->data, //目标地址
&pkt->size, //目标的空间大小
data, //源数据地址
data_size, //源数据大小
AV_NOPTS_VALUE,
AV_NOPTS_VALUE,
0
);
data += ret; //指针往后移动
data_size -= ret;//数据大小减少
if(pkt->size){//成功截取到一包AVPacket
//7,AVPacket发送到解码线程中解码
ret = avcodec_send_packet(c, pkt);
if (ret < 0)break;
while(ret >= 0){
ret = avcodec_receive_frame(c, frame);
//注意这里解码出来的frame可能会出现linesize字节对齐的问题
if(ret < 0) break;
AVFrame* pframe = frame;
//硬解码解出的像素格式是AV_PIX_FMT_DXVA2_VLD格式,需要转换
if(frame->format == AV_PIX_FMT_DXVA2_VLD){
av_hwframe_transfer_data(hw_frame, frame, NULL);
pframe = hw_frame; // hw_frame是NV12
}
/***解码出的帧数据数据存放于pframe中, 对文件进行写入即可***/
....
/***写入时需要注意pframe->data的存放格式***/
}
}
}
}
ret = avcodec_send_packet(c, nullptr);//取出缓冲中的残余帧
while(ret >= 0){
ret = avcodec_receive_frame(c, frame);
if (ret < 0)break;
/***写入文件保存***/
///...
}
av_parser_close(parser);
avcodec_free_context(&c);
av_packet_free(&pkt);
av_frame_free(&frame);
av_frame_free(&hw_frame);
av_buffer_unref(&hw_ctx);
ifs.close();
}