#include "stdafx.h"
#include "AVCodec.h"

extern "C" {
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
}

#include "DDRender.h"

CAVCodec::CAVCodec(void)
{
	m_pFrameRGB = NULL;
	m_pFrame = NULL;
	m_pCodecCtx = NULL;
}

CAVCodec::~CAVCodec(void)
{

}

void CAVCodec::Init(void)
{
	int i;

	Deinit();

	av_register_all();

	m_pCodecCtx = new AVCodecContext*[MAX_CHANNEL];

	for(i=0; i<MAX_CHANNEL; i++){
		m_pCodecCtx[i] = avcodec_alloc_context();
	}

	m_pFrame = new AVFrame*[MAX_CHANNEL];
	for(i=0; i<MAX_CHANNEL; i++){
		m_pFrame[i] = NULL;
	}
	m_pFrameRGB = new AVFrame*[MAX_CHANNEL];
	for(i=0; i<MAX_CHANNEL; i++){
		m_pFrameRGB[i] = NULL;
	}

	m_IsDecoderOpen = new bool[MAX_CHANNEL];
	for(i=0; i<MAX_CHANNEL; i++){
		m_IsDecoderOpen[i] = false;
	}
}

/*! \brief : FFMPEG 코덱을 설정한다.
 *  \param : nCh 채널번호
 *  \param : nWidth : 영상 Width
 *  \param : nHeight : 영상 Height 
  *  \param : pOutputBuf : Decode한 Frame을 담을 버퍼포인터
*/
int CAVCodec::SetDecoder(int nCh, int nWidth, int nHeight, BYTE* pOutputBuf)
{
	if(m_IsDecoderOpen[nCh]){
		avcodec_close(m_pCodecCtx[nCh]);
		av_free(m_pCodecCtx[nCh]);
		m_IsDecoderOpen[nCh] = false;

		m_pCodecCtx[nCh] = avcodec_alloc_context();
	}

	m_pCodecCtx[nCh]->pix_fmt = PIX_FMT_YUYV422;
	m_pCodecCtx[nCh]->width = nWidth;
	m_pCodecCtx[nCh]->height = nHeight;
	m_pCodecCtx[nCh]->codec_type = CODEC_TYPE_VIDEO;
	m_pCodecCtx[nCh]->codec_id = CODEC_ID_H264;

	// Find the decoder for the video stream
	AVCodec *pCodec;
	pCodec = avcodec_find_decoder(m_pCodecCtx[nCh]->codec_id);
	if(pCodec == NULL)
		return -1; // Codec not found

	// Open codec
	if(avcodec_open(m_pCodecCtx[nCh], pCodec)<0){
		m_IsDecoderOpen[nCh] = false;
		return -1; // Could not open codec
	} else {
		m_IsDecoderOpen[nCh] = true;
	}

	if(m_pFrame[nCh]){
		av_free(m_pFrame[nCh]);
		m_pFrame[nCh] = NULL;
	}

	// Allocate video frame
	m_pFrame[nCh] = avcodec_alloc_frame();
	if(m_pFrame[nCh] == NULL){
		return -1;
	}

	if(m_pFrameRGB[nCh]){
		av_free(m_pFrameRGB[nCh]);
		m_pFrameRGB[nCh] = NULL;
	}

	// Allocate an AVFrame structure
	m_pFrameRGB[nCh] = avcodec_alloc_frame();
	if(m_pFrameRGB[nCh] == NULL){
		return -1;
	}


	// Determine required buffer size and allocate buffer
	int numBytes = avpicture_get_size(PIX_FMT_YUYV422, m_pCodecCtx[nCh]->width,
		m_pCodecCtx[nCh]->height);

	// Assign appropriate parts of buffer to image planes in pFrameRGB
	avpicture_fill((AVPicture *)m_pFrameRGB[nCh], (unsigned __int8*)pOutputBuf, PIX_FMT_YUYV422,
		m_pCodecCtx[nCh]->width, m_pCodecCtx[nCh]->height);

	return 0;
}


/*! \brief 사용한 메모리를 해제하고 파일을 닫는다.
*/
void CAVCodec::Deinit(void)
{
	if(m_pFrameRGB){
		for(int i=0; i<MAX_CHANNEL; i++){
			av_free(m_pFrameRGB[i]);
			m_pFrameRGB[i] = NULL;
		}

		delete [] m_pFrameRGB;
		m_pFrameRGB = NULL;
	}

	// Free the YUV frame
	if(m_pFrame){
		for(int i=0; i<MAX_CHANNEL; i++){
			av_free(m_pFrame[i]);
			m_pFrame[i] = NULL;
		}

		delete [] m_pFrame;
		m_pFrame = NULL;
	}

	// Close the codec
	if(m_pCodecCtx){
		for(int i=0; i<MAX_CHANNEL; i++){
			if(m_IsDecoderOpen[i]){
				avcodec_close(m_pCodecCtx[i]);
			}
		}
		delete [] m_pCodecCtx;
		m_pCodecCtx = NULL;

		delete [] m_IsDecoderOpen;
		m_IsDecoderOpen = NULL;
	}
}


/*! \brief 한장의 Frame을 Decoding한다.
 *  \param pbuf : 인코딩되어 있는 Frame이 담겨진 버퍼
 *  \param dwSize : 인코딩되어 있는 Frame이 담겨진 버퍼의 사이즈
 */
void CAVCodec::DecodeFrame(int nCh, const BYTE* pbuf, DWORD dwSize)
{
	int frameFinished = 0;

	avcodec_decode_video(m_pCodecCtx[nCh], m_pFrame[nCh], &frameFinished, 
		(unsigned __int8*)pbuf, dwSize);

	// Did we get a video frame?
	if(frameFinished)
	{
		static struct SwsContext *img_convert_ctx;

		img_convert_ctx = sws_getContext(m_pCodecCtx[nCh]->width,
			m_pCodecCtx[nCh]->height, m_pCodecCtx[nCh]->pix_fmt,
			m_pCodecCtx[nCh]->width, m_pCodecCtx[nCh]->height,
			PIX_FMT_YUYV422, SWS_BICUBIC, NULL, NULL, NULL);

		sws_scale(img_convert_ctx, m_pFrame[nCh]->data, m_pFrame[nCh]->linesize, 0, m_pCodecCtx[nCh]->height, m_pFrameRGB[nCh]->data, m_pFrameRGB[nCh]->linesize);
		
		sws_freeContext(img_convert_ctx);
		img_convert_ctx = NULL; 
	}
}
