#include "Decode.h"
#include <iostream>
using namespace std;
enum AVPixelFormat Decode::ePixFmt_;
AVBufferRef* Decode::pDeviceCtx;
Decode::Decode() {
}
Decode::~Decode() {
}
enum AVPixelFormat Decode::get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) {
const enum AVPixelFormat *p;
for (p = pix_fmts; *p != -1; p++) {
if (*p == ePixFmt_)
return *p;
}
fprintf(stderr, "Failed to get HW surface format.\n");
return AV_PIX_FMT_NONE;
}
int Decode::hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = 0;
if ((err = av_hwdevice_ctx_create(&pDeviceCtx, type,
nullptr, nullptr, 0)) < 0) {
fprintf(stderr, "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(pDeviceCtx);
return err;
}
int Decode::InitSoftDecode(int VideoType, int PixFmt) {
pCodec_ = avcodec_find_decoder((AVCodecID)VideoType);
if (!pCodec_) {
std::cout<<"avcodec_find_decoder Failed"<<std::endl;
return -1;
}
pParseCtx_ = av_parser_init(pCodec_->id);
if (!pParseCtx_) {
std::cout<<"av_parser_init Failed"<<std::endl;
return -1;
}
pDecodeCtx_ = avcodec_alloc_context3(pCodec_);
if (!pDecodeCtx_) {
std::cout<<"avcodec_alloc_context3 Failed"<<std::endl;
return -1;
}
pDecodeCtx_->pix_fmt = (AVPixelFormat)PixFmt;
if (avcodec_open2(pDecodeCtx_, pCodec_, nullptr) < 0) {
std::cout<<"avcodec_open2 Failed"<<std::endl;
return -1;
}
bHWDecode_ = false;
return 0;
}
int Decode::InitHardDecode(AVFormatContext* input_ctx, const std::string& HWType) {
enum AVHWDeviceType type;
type = av_hwdevice_find_type_by_name(HWType.c_str());
if (type == AV_HWDEVICE_TYPE_NONE) {
std::cout<<"UnKnown HW Device Type"<<std::endl;
while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
std::cout<< type <<std::endl;
}
return -1;
}
int video_index = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1 , -1, &pCodec_, 0);
if (video_index < 0) {
cout<<"Cannot find a video stream in the input file"<<endl;
return -1;
}
for (int i = 0; ; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(pCodec_, i);
if (!config) {
cout<<"avcodec_get_hw_config Failed"<<i<<endl;
return -1;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == type) {
ePixFmt_ = config->pix_fmt;
break;
}
}
pDecodeCtx_ = avcodec_alloc_context3(pCodec_);
if (!pDecodeCtx_) {
cout<<"avcodec_alloc_context3 Failed"<<endl;
return -1;
}
pVStream_ = input_ctx->streams[video_index];
if (avcodec_parameters_to_context(pDecodeCtx_, pVStream_->codecpar) < 0) {
cout<<"avcodec_parameters_to_context Failed"<<endl;
return -1;
}
pDecodeCtx_->get_format = get_hw_format;
if (hw_decoder_init(pDecodeCtx_, type) < 0) {
return -1;
}
if (avcodec_open2(pDecodeCtx_, pCodec_, nullptr) < 0) {
cout<<"avcodec_open2 Failed"<<endl;
return -1;
}
bHWDecode_ = true;
return 0;
}
int Decode::DecodePacket(AVPacket* packet, AVFrame* frame) {
//软解码
if (!bHWDecode_) {
int nRet = avcodec_send_packet(pDecodeCtx_, packet); //将AVPacket发送至解码器中
if (nRet < 0) {
cout<<"Error sending a packet for decoding"<<endl;
return -1;
}
nRet = avcodec_receive_frame(pDecodeCtx_, frame); //从解码器中获取被解码后的帧数据AVFrame
if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF)
return 0;
else if (nRet < 0) {
cout<<"Error during decoding"<<endl;
return -1;
}
return 0;
} else { //硬解码
AVFrame* tmpFrame = nullptr, *swFrame = nullptr;
int nRet = avcodec_send_packet(pDecodeCtx_, packet); //将AVPacket发送至解码器中
if (nRet < 0) {
cout<<"Error sending a packet for decoding"<<endl;
av_frame_free(&tmpFrame);
av_frame_free(&swFrame);
return -1;
}
if (!(tmpFrame = av_frame_alloc()) || !(swFrame = av_frame_alloc())) {
cout<<"Can not alloc frame"<<endl;
av_frame_free(&tmpFrame);
av_frame_free(&swFrame);
nRet = AVERROR(ENOMEM);
return -1;
}
nRet = avcodec_receive_frame(pDecodeCtx_, tmpFrame); //从解码器中获取被解码后的帧数据AVFrame
if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF) {
av_frame_free(&tmpFrame);
av_frame_free(&swFrame);
return 0;
} else if (nRet < 0) {
cout<<"Error while decoding"<<endl;
av_frame_free(&tmpFrame);
av_frame_free(&swFrame);
return -1;
}
if (frame->format == ePixFmt_) {
/* 将GPU中的数据 移交到CPU中*/
if (av_hwframe_transfer_data(swFrame, tmpFrame, 0) < 0) {
cout<<"Error transferring the data to system memory"<<endl;
av_frame_free(&tmpFrame);
av_frame_free(&swFrame);
return -1;
}
frame = swFrame;
} else {
frame = tmpFrame;
}
av_frame_free(&tmpFrame);
av_frame_free(&swFrame);
return 0;
}
}
代码仅供参考,因为电脑太旧,硬解没识别出来支持的硬件,简单跟着深入理解FFmpeg写的Demo,有问题欢迎指正。