#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
extern "C"
{
	#include <libavutil/opt.h>
	#include <libavcodec/avcodec.h>
	#include <libavutil/channel_layout.h>
	#include <libavutil/common.h>
	#include <libavutil/imgutils.h>
	#include <libavutil/mathematics.h>
	#include <libavutil/samplefmt.h>
	#include <libavformat/avformat.h>
	
}

static AVFormatContext *input_fmt_ctx=NULL;
static int video_stream_idx=-1;
static AVStream *input_video_stream=NULL;
static AVCodecContext *video_dec_ctx = NULL;
static uint8_t *video_dst_data[4] = {NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;
static AVFrame *input_frame;
static AVPacket input_pkt;


/*video output*/
static AVFrame *frame;
static AVPicture src_picture, dst_picture;
static int frame_count;

static int open_codec_context(int *stream_idx,AVFormatContext *fmt_ctx, enum AVMediaType type)
{
	int ret;
	AVStream *st=NULL;
	AVCodecContext *dec_ctx=NULL;
	AVCodec *dec=NULL;

	ret=av_find_best_stream(fmt_ctx,type,-1,-1,NULL,0);
	if(ret<0)
	{
		return ret;
	}
	else
	{
		*stream_idx=ret;
		st=fmt_ctx->streams[*stream_idx];

		/*find decoded*/
		dec_ctx=st->codec;
		dec=avcodec_find_decoder(dec_ctx->codec_id);

		if(!dec)
		{
			printf("avcodec_find_decoder fail\n");
			return -1;
		}

		if((ret=avcodec_open2(dec_ctx,dec,NULL))<0)
		{
			printf("avcodec_open2 fail\n");
			return -1;
		}

	}
	return 0;
}

static void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
{
	int ret;
	AVCodecContext *c=st->codec;

	ret=avcodec_open2(c,codec,NULL);
	if(ret<0)
	{
		printf("avcodec_open2 fail\n");
		exit(-1);
	}

	frame=avcodec_alloc_frame();
	if(!frame)
	{
		printf("avcodec_alloc_frame fail\n");
		exit(-1);
	}

	/* Allocate the encoded raw picture. */
	ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
	if(ret<0)
	{
		printf("avpicture_alloc fail\n");
		exit(-1);
	}

	if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
	
		ret = avpicture_alloc(&src_picture, AV_PIX_FMT_YUV420P, c->width, c->height);
		if(ret<0)
		{
			printf("avpicture_alloc fail\n");
			exit(-1);
		}
	}

	/* copy data and linesize picture pointers to frame */
	*((AVPicture *)frame) = dst_picture;

}


int main()
{
	const char* input_file="input.avi";
	
	av_register_all();
	avcodec_register_all();
	int ret;


	/*Reads the file header and stores information about the file format in the AVFormatContext structure*/
	if(avformat_open_input(&input_fmt_ctx,input_file,NULL,NULL)<0)
	{
		printf("avformat_open_input fail\n");
		exit(-1);
	}


	/*Check out the stream information in the file*/
	if(avformat_find_stream_info(input_fmt_ctx,NULL)<0)
	{
		printf("avformat_find_stream_info fail\n");
		exit(-1);
	}

	av_dump_format(input_fmt_ctx,0, input_file, 0);
	
	if(open_codec_context(&video_stream_idx,input_fmt_ctx,AVMEDIA_TYPE_VIDEO)>=0)
	{
		input_video_stream=input_fmt_ctx->streams[video_stream_idx];
		video_dec_ctx=input_video_stream->codec;
		ret=av_image_alloc(video_dst_data,video_dst_linesize,video_dec_ctx->width,video_dec_ctx->height,video_dec_ctx->pix_fmt,1);
		if (ret < 0) 
		{
			printf("av_image_alloc fail\n");
		}
		video_dst_bufsize=ret; 	
	}
	else
	{
		printf("open_codec_context fail\n");
		exit(-1);
	}

	/*配置一個AVframe空間 準備放待會要解的影像資料*/
	input_frame =av_frame_alloc();
	
	if (!input_frame) {
		printf("av_frame_alloc fail\n");
		exit(-1);
	}






	/*========================================*/
	/*==========output media==================*/
	/*========================================*/

	/*配置outputfile的參數*/
	AVOutputFormat *fmt;
	AVFormatContext *oc;
	AVStream *video_st=NULL;
	AVCodec *video_codec;

	const char *outputfile;
	outputfile="output.mp4";
	/* allocate the output media context */

	avformat_alloc_output_context2(&oc,NULL,NULL,outputfile);
	if(!oc)
	{
		avformat_alloc_output_context2(&oc,NULL,"mpeg",outputfile);
	}
		
	if(!oc)
	{
		printf("avformat_alloc_output_context2 fail\n");
		exit(-1);
	}

	fmt=oc->oformat;

	/*Mp4 format為H264*/
	fmt->video_codec=AV_CODEC_ID_H264;

	/*Find the encoder*/
	video_codec=avcodec_find_encoder(fmt->video_codec);

	if(!(video_codec))
	{
		printf("avcodec_find_encoder fail\n");
		exit(-1);
	}

	/*配置output stream*/
	video_st=avformat_new_stream(oc,video_codec);
	if(!(video_st))
	{
		printf("avformat_new_stream fail\n");
		exit(-1);
	}

	video_st->id=oc->nb_streams-1;
	
	video_st->codec->codec_id=fmt->video_codec;
	video_st->codec->bit_rate=400000;
	/*frame rate*/
	video_st->codec->time_base.den=25; 
	video_st->codec->time_base.num=1;
	/*every 12 frame must have I frame*/
	video_st->codec->gop_size=12;
	video_st->codec->pix_fmt=AV_PIX_FMT_YUV420P;
	
	/* Some formats want stream headers to be separate. */
	if (oc->oformat->flags & AVFMT_GLOBALHEADER)
	{
		video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
	}

	video_st->codec->width=video_dec_ctx->width;
	video_st->codec->height=video_dec_ctx->height;

	av_dump_format(oc,0,outputfile,1);

	/*Try to open output file*/
	if((fmt->flags&AVFMT_NOFILE))
	{
		ret=avio_open(&oc->pb,outputfile,AVIO_FLAG_WRITE);
		if(ret<0)
		{
			fprintf(stderr,"Could not open\n");
			exit(-1);
		}
	}
	open_video(oc, video_codec, video_st);

	/*Write the stream header*/
	//ret=avformat_write_header(oc,NULL);
	

	/*========================================*/
	/*========================================*/
	/*========================================*/




	/*Av_read_frame*/
	int index=0;
	int frameFinished=0;
	
	while(av_read_frame(input_fmt_ctx,&input_pkt)>=0)
	{
		if(input_pkt.stream_index==video_stream_idx)
		{
			/* decode video frame */
			ret=avcodec_decode_video2(video_dec_ctx,input_frame,&frameFinished,&input_pkt);

			if(ret<0)
			{
				printf("avcodec_decode_video2 fail\n");
				exit(-1);
			}
			
			/*Aready get a video frame*/
			if(frameFinished)
			{
				/*do something*/
				printf("HI ");
			}

		}

		av_free_packet(&input_pkt);
	}

	/*Close codec*/
	avcodec_close(video_st->codec);
	av_free(src_picture.data[0]);
	av_free(dst_picture.data[0]);
	av_free(frame);

	if(!(fmt->flags&AVFMT_NOFILE))
		avio_close(oc->pb);

	avformat_free_context(oc);
	av_free(input_frame);
	av_free(video_dst_data[0]);
	avcodec_close(video_dec_ctx);
	

	system("pause");
    return 0;
}
