ffmpeg解码步骤及使用硬解码

本文记录h264文件解码, 以及解码如何使用硬件加速

解码需要分割h264码流, 这里不介绍h264码流NALU的结构

#include 
#include 
#include "xvideo_view.h"
using namespace std;
extern "C"{
#include 
#include 
}
//预处理指令导入库
#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();
}


你可能感兴趣的:(音视频开发,C++,音视频)