#include "HardH264FFmpegDecode.h"
#include <iostream>



using namespace std;

HardH264FFmpegDecode::HardH264FFmpegDecode() 
{
	;
}

HardH264FFmpegDecode::~HardH264FFmpegDecode() 
{
	;
}

// int HardH264FFmpegDecode::HardH264FFmpegDecoderInit(unsigned int uiWidth, unsigned int uiHeight, unsigned int uiFrameRate)
// {
// 	uiWidth_ = uiWidth; uiHeight_ = uiHeight;
// 	uiFrameRate_ = uiFrameRate;
// 	iFrameFinished_ = 0;

// 	av_log_set_level(AV_LOG_ERROR);
	
// 	// AVCodecID codec_id = AV_CODEC_ID_H264;	//解码H264
// 	// pCodec_ = avcodec_find_decoder(codec_id);	//获取解码器 

// 	pCodec_ = avcodec_find_decoder_by_name(NVIDIA_H264_DECODER);
//     if (!pCodec_) {
//         fprintf(stderr, "Codec '%s' not found\n", pCodec_->long_name);
//         exit(1);
//     }
// 	printf("Codec found with name %d(%s)\n", pCodec_->id, pCodec_->long_name);

// 	//创建上下文
//     pCodecCtx_ = avcodec_alloc_context3(pCodec_);	
//     if (!pCodecCtx_){
//         fprintf(stderr, "Could not allocate video codec context\n");
//         exit(1);
//     }
	
// 	//创建解析器
// 	pCodecParserCtx_ = av_parser_init(pCodec_->id);	
// 	if (!pCodecParserCtx_){
//         fprintf(stderr, "parser not found\n");
//         exit(1);
// 	}
 
//     //if(pCodec_->capabilities&CODEC_CAP_TRUNCATED)
//     //    pCodecCtx_->flags|= CODEC_FLAG_TRUNCATED; 
	
// 	//打开解码器
//     int ret = avcodec_open2(pCodecCtx_, pCodec_, nullptr);
//     if (ret < 0) {	
//         fprintf(stderr, "Could not open codec\n");
//         printf("avcodec_open2 ret is: %d\n",ret);
//         exit(1);
//     }

//     //分配packet
//     pPacket_ = av_packet_alloc();
//     if (!pPacket_){
//         fprintf(stderr, "Could not allocate video packet\n");
//         exit(1);
//     }
//     // av_init_packet(pPacket_);

// 	//分配frame
//     pSrcFrame_ = av_frame_alloc();
//     if (!pSrcFrame_) {
//         fprintf(stderr, "Could not allocate video src pFrame\n");
//         exit(1);
//     }

//     pDstFrame_ = av_frame_alloc();
//     if (!pDstFrame_) {
//         fprintf(stderr, "Could not allocate video dst pFrame\n");
//         exit(1);
//     }

//     printf("after align down video_width: %d, video_height: %d\n", uiWidth_, uiHeight_);

// 	//初始化解析器参数
// 	pCodecCtx_->time_base.num = 1;  
// 	pCodecCtx_->frame_number = 1; //每包一个视频帧  
// 	pCodecCtx_->codec_type = AVMEDIA_TYPE_VIDEO;  
// 	pCodecCtx_->bit_rate = 0;  
// 	pCodecCtx_->time_base.den = uiFrameRate_;//帧率  
// 	pCodecCtx_->width = uiWidth_;	//视频宽  
// 	pCodecCtx_->height = uiHeight_;	//视频高  
//     // pCodecCtx_->pix_fmt = AV_PIX_FMT_YUV420P;

//     int bufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
//                                               pCodecCtx_->width,
//                                               pCodecCtx_->height, 1);
//     pu8OutBuffer_ = (unsigned char *) av_malloc(bufferSize);
//     av_image_fill_arrays(pDstFrame_->data,
//                          pDstFrame_->linesize,
//                          pu8OutBuffer_,
//                          AV_PIX_FMT_YUV420P,
//                          pCodecCtx_->width,
//                          pCodecCtx_->height, 1);

//     printf("pDstFrame_->linesize: %d, bufferSize: %d\n", pDstFrame_->linesize, bufferSize);

//     pSwsContext_ = sws_getContext(pCodecCtx_->width, pCodecCtx_->height, pCodecCtx_->pix_fmt,
//                                      pCodecCtx_->width, pCodecCtx_->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, nullptr, nullptr, nullptr);
//     printf("pCodecCtx_->width: %d, pCodecCtx_->height: %d, pCodecCtx_->pix_fmt: %d\n", pCodecCtx_->width, pCodecCtx_->height, pCodecCtx_->pix_fmt);


// 	return 0;
// }

int HardH264FFmpegDecode::HardH264FFmpegDecoderInit(unsigned int width, unsigned int height, unsigned int frame_rate)
{
    width_= width;
    height_= height;
    frame_rate_= frame_rate;
    frameFinished_= 0;

    av_log_set_level(AV_LOG_ERROR);

    avcodec_register_all();   // 注册编解码器
    avformat_network_init();  // 注册网络格式,如果为本地文件则可以去掉该代码

    // AVCodecID codec_id = AV_CODEC_ID_H264;	//解码H264
    // pCodec_ = avcodec_find_decoder(codec_id);	//获取解码器

    pCodec_ = avcodec_find_decoder_by_name(NVIDIA_H264_DECODER);
    if (!pCodec_)
    {
        // fprintf(stderr, "Codec '%s' not found\n", pCodec_->long_name);
        exit(1);
    }
    // printf("Codec found with name %d(%s)\n", pCodec_->id, pCodec_->long_name);

    // 创建上下文
    pCodecCtx_ = avcodec_alloc_context3(pCodec_);
    if (!pCodecCtx_)
    {
        // fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    // 创建解析器
    pCodecParserCtx_ = av_parser_init(pCodec_->id);
    if (!pCodecParserCtx_)
    {
        // fprintf(stderr, "parser not found\n");
        exit(1);
    }

    // if(pCodec_->capabilities&CODEC_CAP_TRUNCATED)
    //     pCodecCtx_->flags|= CODEC_FLAG_TRUNCATED;

    // 打开解码器
    int ret = avcodec_open2(pCodecCtx_, pCodec_, nullptr);
    if (ret < 0)
    {
        // fprintf(stderr, "Could not open codec\n");
        // printf("avcodec_open2 ret is: %d\n",ret);
        exit(1);
    }

    // 初始化解析器参数
    pCodecCtx_->time_base.num = 1;
    pCodecCtx_->frame_number  = 1;  // 每包一个视频帧
    pCodecCtx_->codec_type    = AVMEDIA_TYPE_VIDEO;
    pCodecCtx_->bit_rate      = 0;
    pCodecCtx_->time_base.den = frame_rate_;  // 帧率
    pCodecCtx_->width         = width_;       // 视频宽
    pCodecCtx_->height        = height_;      // 视频高


    // 分配frame
    pFrame_= av_frame_alloc();
    if (!pFrame_)
    {
        // fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }


    // 分配packet
    pPacket_ = av_packet_alloc();
    if (!pPacket_)
    {
        // fprintf(stderr, "Could not allocate video packet\n");
        exit(1);
    }

    // av_init_packet(pPacket_);

    return 0;
}



int HardH264FFmpegDecode::HardH264FFmpegDecoderDeInit()
{
	if(pu8OutBuffer_){
        av_free(pu8OutBuffer_);
        pu8OutBuffer_ = nullptr;
    }

	if(pSrcFrame_){
		av_frame_free(&pSrcFrame_);
		pSrcFrame_ = nullptr;
	}
	if(pDstFrame_){
		av_frame_free(&pDstFrame_);
		pDstFrame_ = nullptr;
	}
	if(pPacket_){
        av_packet_free(&pPacket_);  
        pPacket_ = nullptr;
    }
	if(pCodecParserCtx_){
		av_parser_close(pCodecParserCtx_);
		pCodecParserCtx_ = nullptr;
	}
	if(pCodecCtx_){
		avcodec_close(pCodecCtx_);
		av_free(pCodecCtx_);
		pCodecCtx_ = nullptr;
	}

	if(pSwsContext_){
		sws_freeContext(pSwsContext_);
		pSwsContext_ = nullptr;
	}
}

int HardH264FFmpegDecode::HardH264FFmpegDecoderFilterGraph(AVFilterGraph *pGraph, AVFilterContext *pSourceCtx, AVFilterContext *pSinkCtx)
{
    int ret;
    AVFilterInOut *pOutputs = nullptr, *pInputs = nullptr;
    if ((ret = avfilter_link(pSourceCtx, 0, pSinkCtx, 0)) >= 0){
       ret = avfilter_graph_config(pGraph, nullptr);
    }

    avfilter_inout_free(&pOutputs);
    avfilter_inout_free(&pInputs);
    return ret;
}

int HardH264FFmpegDecode::HardH264FFmpegDecoderConfigureVideoFilters(AVFilterGraph *pGraph, AVFilterContext* &pDecoderFilterIn, AVFilterContext* &pDecoderFilterOut, const int iWidth, const int iHeight, const int iFormat)
{
    int iPixFormats[2] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
    char BufferSrcArgs[256] = {0};
    AVFilterContext *pFiltSrc = nullptr, *pFiltDst = nullptr;
    int ret;

    snprintf(BufferSrcArgs, sizeof(BufferSrcArgs),
             "video_size=%dx%d:pix_fmt=%d:time_base=1/1200000",
             iWidth, iHeight, iFormat);
    if ((ret = avfilter_graph_create_filter(&pFiltSrc,
        avfilter_get_by_name("buffer"), "ffplay_buffer", BufferSrcArgs,
        nullptr, pGraph)) < 0){
        goto fail;
    }

    ret = avfilter_graph_create_filter(&pFiltDst,
          avfilter_get_by_name("buffersink"),
          "ffplay_buffersink", nullptr, nullptr, pGraph);
    if (ret < 0){
        goto fail;
    }

    if ((ret = av_opt_set_int_list(pFiltDst, "pix_fmts", iPixFormats, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0){
        goto fail;
    }
    if ((ret = HardH264FFmpegDecoderFilterGraph(pGraph, pFiltSrc, pFiltDst)) < 0){
        goto fail;
    }

    pDecoderFilterIn  = pFiltSrc;
    pDecoderFilterOut = pFiltDst;

fail:
    return ret;
}

int HardH264FFmpegDecode::HardH264FFmpegDecoder(AVCodecContext *pDecCtx, AVFrame *pFrame, AVPacket *pPkt, void* pOutputData, unsigned int* puiOutputDataSize)
{
	int ret;
	AVFilterGraph* pDecoderGraph = nullptr;

    ret = avcodec_send_packet(pDecCtx, pPkt);    //接收packet解码
    if (ret < 0) {
        fprintf(stderr, "Error sending a packet for decoding\n");
        exit(1);
    }

    while (ret >= 0) {
        ret = avcodec_receive_frame(pDecCtx, pFrame);    //解码
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
			fprintf(stderr, "During decoding eof\n");
            return -1;
        }
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }

        //printf("saving frame %3d\n", pDecCtx->frame_number);
        fflush(stdout);

		AVFilterContext *pDecoderFilterIn = nullptr, *pDecoderFilterOut = nullptr;

        // pFrame->width = ALIGN_DOWN(pFrame->width, 32);
        // pFrame->height = ALIGN_DOWN(pFrame->height, 32);
        // printf("pFrame->width: %d\tpFrame->height: %d\n", pFrame->width, pFrame->height);
	
        pDecoderGraph = avfilter_graph_alloc();
        HardH264FFmpegDecoderConfigureVideoFilters(pDecoderGraph, pDecoderFilterIn, pDecoderFilterOut, pFrame->width, pFrame->height, pFrame->format);

		if (pFrame->format != AV_PIX_FMT_YUV420P){
            DUMP_FRAME(pFrame);
			ret = av_buffersrc_add_frame(pDecoderFilterIn, pFrame);
            ret = av_buffersink_get_frame_flags(pDecoderFilterOut, pFrame, 0);
            DUMP_FRAME(pFrame);
			
			int iSize = pFrame->width * pFrame->height;
			memcpy(pOutputData, pFrame->data[0], iSize); 	//Y
			memcpy(pOutputData+iSize,  pFrame->data[1], iSize/4);    //U 
			memcpy(pOutputData+iSize+iSize/4, pFrame->data[2], iSize/4);   //V
			*puiOutputDataSize = iSize*3/2;
			return iSize*3/2;
		}
	}	
	return 0;
}

// int HardH264FFmpegDecode::HardH264FFmpegDecoderV2(AVCodecContext *pDecCtx, SwsContext *pSwsCtx, AVFrame *pSrcFrame, AVFrame *pDstFrame, AVPacket *pPkt, void* pOutputData,unsigned int* puiOutputDataSize)
// {
//      std::cout << "HardH264FFmpegDecoderV2--in " << std::endl;
// 	int ret;

//     ret = avcodec_send_packet(pDecCtx, pPkt);    //接收packet解码
//     if (ret < 0) {
//         fprintf(stderr, "Error sending a packet for decoding\n");
//         exit(1);
//     }
//     std::cout << "HardH264FFmpegDecoderV2--in " << std::endl;

//     while (ret >= 0) {
//         ret = avcodec_receive_frame(pDecCtx, pSrcFrame);    //解码
//         if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
// 			fprintf(stderr, "During decoding eof\n");
//             return -1;
//         }
//         else if (ret < 0) {
//             fprintf(stderr, "Error during decoding\n");
//             exit(1);
//         }

// 		// pDecCtx->width = ALIGN_DOWN(pDecCtx->width, 32);
//         // pDecCtx->height = ALIGN_DOWN(pDecCtx->height, 32);

//         sws_scale(pSwsCtx,
//                   (const uint8_t *const *)pSrcFrame->data,
//                   pSrcFrame->linesize,
//                   0,
//                   pDecCtx->height,
//                   pDstFrame->data,
//                   pDstFrame->linesize);

//         //printf("saving frame %3d\n", pDecCtx->frame_number);
//         fflush(stdout);

// 		int iSize = pDecCtx->width * pDecCtx->height;

// 		memcpy(pOutputData, pDstFrame->data[0], iSize); 	//Y
// 		memcpy(pOutputData+iSize, pDstFrame->data[1], iSize/4);    //U 
// 		memcpy(pOutputData+iSize+iSize/4, pDstFrame->data[2], iSize/4);   //V
// 		*puiOutputDataSize = iSize*3/2;
// 		return iSize*3/2;
// 	}	
// 	return 0;
// }

int HardH264FFmpegDecode::HardH264FFmpegDecoderV2(AVCodecContext* pDecCtx, AVFrame* pSrcFrame, AVPacket* pPkt, void* pOutputData, unsigned int* puiOutputDataSize)
{
    int ret;

    ret = avcodec_send_packet(pDecCtx, pPkt);  // 接收packet解码
    if (ret < 0)
    {
        // fprintf(stderr, "Error sending a packet for decoding\n");
        return -9;
    }

    while (ret >= 0)
    {
        ret = avcodec_receive_frame(pDecCtx, pSrcFrame);  // 解码
        if (ret == AVERROR(EAGAIN))
        {
            // fprintf(stderr, "During decoding eof\n");
            return -1;
        }
        else if (ret == AVERROR_EOF)
        {
            return -1;
        }
        else if (ret < 0)
        {
            // fprintf(stderr, "Error during decoding\n");
            return -2;
        }

        ////printf("saving frame %3d\n", pDecCtx->frame_number);
        // fflush(stdout);

        memcpy(pOutputData, pSrcFrame->data[0], pSrcFrame->width * pSrcFrame->height);                                                                       // Y
        memcpy(pOutputData + pSrcFrame->width * pSrcFrame->height, pSrcFrame->data[1], pSrcFrame->width * pSrcFrame->height / 4);                                     // U
        memcpy(pOutputData + pSrcFrame->width * pSrcFrame->height + pSrcFrame->width * pSrcFrame->height / 4, pSrcFrame->data[2], pSrcFrame->width * pSrcFrame->height / 4);  // V
        *puiOutputDataSize = pSrcFrame->width * pSrcFrame->height * 3 / 2;
        return pSrcFrame->width * pSrcFrame->height * 3 / 2;
    }
    return 0;
}