#include "stdafx.h"
#include "videoDecoder.h"
videoDecoder::videoDecoder()
{
decCodecCtx = NULL;
decFrame = NULL;
hwDevCtx = NULL;
decWidth = 0;
decHeight = 0;
_have_init_dec = -1;
}
videoDecoder::~videoDecoder()
{
}
int videoDecoder::initHWDevice(AVCodecContext *ctx,AVCodec *decoder)
{
int s32Ret = 0;
string hwDevStr="";
if(decoder==NULL||ctx==NULL)return DEC_FAIL;
vector<string> hwDevs;
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
hwDevs.clear();
while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
//TRACE("%s\n",av_hwdevice_get_type_name(type));
hwDevs.push_back(av_hwdevice_get_type_name(type));
s32Ret ++;
}
if(s32Ret<=0)return -1;
for(vector<string>::iterator iter=hwDevs.begin(); iter != hwDevs.end(); iter++){
string itStr = (string)*iter;
if(itStr.compare("dxva2")==0){
hwDevStr = "dxva2";
break;
}
}
if(hwDevStr.empty()){
hwDevStr = hwDevs.at(0);
}
type = av_hwdevice_find_type_by_name(hwDevStr.c_str());
// TRACE("use %s decode \n",hwDevStr.c_str());
for (int i = 0;; i++) {
const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i);
if (!config) {
fprintf(stderr, "Decoder %s does not support device type %s.\n",
decoder->name, av_hwdevice_get_type_name(type));
return -1;
}
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
config->device_type == type) {
hwPixFmt = config->pix_fmt;
break;
}
}
ctx->pix_fmt = hwPixFmt;
s32Ret = av_hwdevice_ctx_create(&hwDevCtx, type,NULL, NULL, 0);
if (s32Ret < 0) {
TRACE("Failed to create specified HW device.\n");
return DEC_FAIL;
}
ctx->hw_device_ctx = av_buffer_ref(hwDevCtx);
return DEC_SUCC;
}
int videoDecoder::initDecoder(enum VideoType mVideoType)
{
AVCodec *dec = NULL;
int ret = 0;
if (_have_init_dec > 0)return 1;
avcodec_register_all();
av_init_packet(&decPkt);
if (mVideoType == H264_VIDEO){
dec = avcodec_find_decoder(AV_CODEC_ID_H264);
}else if (mVideoType == H265_VIDEO){
dec = avcodec_find_decoder(AV_CODEC_ID_HEVC);
}
if (!dec){
printf("Failed to find %s codec\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
return DEC_FAIL;
}
decCodecCtx = avcodec_alloc_context3(dec);
decCodecCtx->flags |= AV_CODEC_FLAG_LOW_DELAY;
decCodecCtx->thread_count = 0;
ret = initHWDevice(decCodecCtx,dec);
if(ret<=0){
printf("failed to init decoder hw\n");
}
if ((ret = avcodec_open2(decCodecCtx, dec, NULL)) < 0){
TRACE("Failed to open %s codec\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
return DEC_FAIL;
}
decFrame = av_frame_alloc();
if (!decFrame){
return DEC_FAIL;
}
_have_init_dec = 1;
return DEC_SUCC;
}
int videoDecoder::decoderPacket(AVCodecContext *avctx, AVPacket *packet,unsigned char *pDst)
{
AVFrame *frame = NULL, *sw_frame = NULL;
AVFrame *tmp_frame = NULL;
uint8_t *buffer = NULL;
int size;
int ret = 0;
if(pDst==NULL)return -1;
ret = avcodec_send_packet(avctx, packet);
if (ret < 0) {
//TRACE("Error during decoding\n");
return DEC_FAIL;
}
while (ret>=0) {
if (!(frame = av_frame_alloc()) || !(sw_frame = av_frame_alloc())) {
TRACE("Can not alloc frame\n");
ret = AVERROR(ENOMEM);
goto fail;
}
ret = avcodec_receive_frame(avctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
av_frame_free(&frame);
av_frame_free(&sw_frame);
return DEC_SUCC;
} else if (ret < 0) {
//TRACE("Error while decoding\n");
goto fail;
}
if (frame->format == hwPixFmt) {
/* retrieve data from GPU to CPU */
if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) {
TRACE("Error transferring the data to system memory\n");
goto fail;
}
tmp_frame = sw_frame;
} else{
tmp_frame = frame;
}
size = av_image_get_buffer_size((AVPixelFormat)tmp_frame->format, tmp_frame->width,
tmp_frame->height, tmp_frame->format);
ret = av_image_copy_to_buffer(pDst, size,
(const uint8_t * const *)tmp_frame->data,
(const int *)tmp_frame->linesize, (AVPixelFormat)tmp_frame->format,
tmp_frame->width, tmp_frame->height, tmp_frame->format);
if (ret < 0) {
TRACE("Can not copy image to buffer\n");
goto fail;
}
fail:
if(frame){
av_frame_free(&frame);
frame = NULL;
}
if(sw_frame){
av_frame_free(&sw_frame);
frame = NULL;
}
if (ret < 0){
return DEC_FAIL;
}
}
return DEC_SUCC;
}
#if 0
int videoDecoder::decVideo(unsigned char *src, int srcSize, unsigned char *dst)
{
int ret = 0, BpS = 0;
int gotFrame = 0;
int videoWidth = 0, videoHeight = 0;
int uvlineStep, uvHei, i;
unsigned char *PtrY, *PtrV, *PtrU;
unsigned char *PtrYY, *PtrUU, *PtrVV;
decPkt.data = src;
decPkt.size = srcSize;
long long start,end,cost=0;
start = win_getMillTime();
ret = avcodec_decode_video2(decCodecCtx, decFrame, &gotFrame, &decPkt);
if (ret < 0) {
TRACE("avcodec_decode_video2 ret %d\n",ret);
return DEC_FAIL;
}
if (!gotFrame){
TRACE("avcodec_decode_video2 gotFrame %d\n",gotFrame);
return DEC_FAIL;
}
videoWidth = decCodecCtx->width;
videoHeight = decCodecCtx->height;
decWidth = videoWidth;
decHeight = videoHeight;
PtrYY = decFrame->data[0];
PtrVV = decFrame->data[1];
PtrUU = decFrame->data[2];
PtrY = dst;
PtrU = dst + videoWidth*videoHeight;
PtrV = dst + ((videoWidth*videoHeight * 5) >> 2);
BpS = decFrame->linesize[0];
if (BpS < videoHeight){
return -3;
}
for (i = 0; i < videoHeight; i++){
memcpy(PtrY, PtrYY, videoWidth);
PtrYY += BpS;
PtrY += videoWidth;
}
BpS = decFrame->linesize[1];
uvlineStep = videoWidth >> 1;
uvHei = videoHeight >> 1;
for (i = 0; i < uvHei; i++)
{
memcpy(PtrV, PtrVV, uvlineStep);
PtrVV += BpS;
PtrV += uvlineStep;
}
BpS = decFrame->linesize[2];
for (i = 0; i < uvHei; i++)
{
memcpy(PtrU, PtrUU, uvlineStep);
PtrUU += BpS;
PtrU += uvlineStep;
}
end = win_getMillTime();
TRACE("cost :%lld ms\n",(end-start));
return DEC_SUCC;
}
#else
int videoDecoder::decVideo(unsigned char *src, int srcSize, unsigned char *dst)
{
int s32Ret = 0;
AVPacket decPkt;
av_init_packet(&decPkt);
decPkt.data = src;
decPkt.size = srcSize;
s32Ret = decoderPacket(decCodecCtx,&decPkt,dst);
if(s32Ret<0){
decWidth = 0;
decHeight = 0;
av_packet_unref(&decPkt);
return DEC_FAIL;
}
av_packet_unref(&decPkt);
decWidth = decCodecCtx->width;
decHeight = decCodecCtx->height;
return DEC_SUCC;
}
#endif
void videoDecoder::releaseDecoder()
{
if (decFrame) {
av_frame_free(&decFrame);
decFrame = NULL;
}
// Close the codec
if (decCodecCtx) {
avcodec_free_context(&decCodecCtx);
avcodec_close(decCodecCtx);
av_free(decCodecCtx);
decCodecCtx = NULL;
}
av_free_packet(&decPkt);
av_buffer_unref(&hwDevCtx);
hwDevCtx = NULL;
}