#include <stdio.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
#define snprintf _snprintf
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/avcodec.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/avutil.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libswscale/swscale.h"
#include "SDL/SDL.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/avcodec.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <SDL/SDL.h>
#ifdef __cplusplus
};
#endif
#endif




int h264_to_ts(const char* in_filename, const char* out_filename)
{
    AVOutputFormat *ofmt = NULL;
    //£¨Input AVFormatContext and Output AVFormatContext£©
    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;
    int ret;
	int video_index = -1, audio_index = -1;
	int frame_index=0;
	AVStream* video_st = NULL;
    AVStream* audio_st = NULL;

    av_register_all();
    //Network
    avformat_network_init();
    //Input
	ifmt_ctx = avformat_alloc_context();
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        printf( "Could not open input file.");
        goto end;
    }
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        printf( "Failed to retrieve input stream information");
        goto end;
    }
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
 
    //Output
     ofmt = av_guess_format("mpegts", NULL, NULL);
        if (!ofmt)
        {
 
                printf("Call av_guess_format function failed!\n");
                return 0;
        }
    avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", out_filename);//mpegts/flv/mov/mkv/h264...,  RTMP/UDP/RTP...
    if (!ofmt_ctx) {
        printf( "Could not create output context\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }
    ofmt_ctx->oformat = ofmt;
	
	for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
        //Create output AVStream according to input AVStream
        AVStream *in_stream = ifmt_ctx->streams[i];
        AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
        if (!out_stream) {
            printf( "Failed allocating output stream\n");
            ret = AVERROR_UNKNOWN;
            goto end;
        }
        //Copy the settings of AVCodecContext
        ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
        if (ret < 0) {
            printf( "Failed to copy context from input to output stream codec context\n");
            goto end;
        }
        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
	}
    for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
       switch (ifmt_ctx->streams[i]->codec->codec_type)
                {
                case AVMEDIA_TYPE_VIDEO:
                        video_index = i;
                        ifmt_ctx->streams[i]->discard = AVDISCARD_NONE;
                        video_st = add_output_stream(ofmt_ctx, ifmt_ctx->streams[i]);
                        break;
                case AVMEDIA_TYPE_AUDIO:
                        audio_index = i;
                        ifmt_ctx->streams[i]->discard = AVDISCARD_NONE;
                        audio_st = add_output_stream(ofmt_ctx, ifmt_ctx->streams[i]);
                        break;
                default:
                        ifmt_ctx->streams[i]->discard = AVDISCARD_ALL;
                        break;
				}
    }
	AVCodec* codec = avcodec_find_decoder(video_st->codec->codec_id);
    if (codec == NULL)
    {
            printf("Call avcodec_find_decoder function failed!\n");
            return 0;
    }
	if (avcodec_open2(video_st->codec, codec, NULL) < 0)
    {
            printf("Call avcodec_open function failed !\n");
            return 0;
    }
    //Dump Format------------------
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    //Open output URL
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0) {
            printf( "Could not open output URL '%s'", out_filename);
            goto end;
        }
    }
    //Write file header
	AVDictionary* pAVDictionary = NULL;
    ret = avformat_write_header(ofmt_ctx, &pAVDictionary);
    if (ret < 0) {
        printf( "Error occurred when opening output URL\n");
        goto end;
    }

    int64_t start_time=av_gettime();
	int tag = 0;
    while (1) {
        AVStream *in_stream, *out_stream;
        //Get an AVPacket
        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;
		if (av_dup_packet(&pkt) < 0)
                {
                    printf("Call av_dup_packet function failed\n");
                    av_free_packet(&pkt);
                    break;
                }

        //FIX£ºNo PTS (Example: Raw H.264)
        //Simple Write PTS
        //if(pkt.pts == AV_NOPTS_VALUE){
            //Write PTS
            AVRational time_base1 = ifmt_ctx->streams[video_index]->time_base;
            //Duration between 2 frames (us)
            int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[video_index]->r_frame_rate);
            //Parameters
            pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
            pkt.dts=pkt.pts;
            pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
        //}
        //Important:Delay
        if(pkt.stream_index == video_index){
            AVRational time_base=ifmt_ctx->streams[video_index]->time_base;
            AVRational time_base_q={1,AV_TIME_BASE};
            int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
            int64_t now_time = av_gettime() - start_time;
            if (pts_time > now_time)
                av_usleep(pts_time - now_time);
        }
        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];
        /* copy packet */
        //Convert PTS/DTS
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;
        //Print to Screen
        if(pkt.stream_index==video_index) {
            printf("Send %8d video frames to output URL\n", frame_index);
            frame_index++;
        }
		memcpy(&(ofmt_ctx->streams[pkt.stream_index]->r_frame_rate), &(ifmt_ctx->streams[pkt.stream_index]->r_frame_rate), sizeof(AVRational));
		//tag++;
		//pkt.pts = pkt.dts = tag * 1000;
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);

        if (ret < 0) {
            printf( "Error muxing packet\n");
            break;
        }
        av_free_packet(&pkt);
    }
    //Write file trailer
    av_write_trailer(ofmt_ctx);
end:
    avformat_close_input(&ifmt_ctx);
    /* close output */
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    if (ret < 0 && ret != AVERROR_EOF) {
        printf( "Error occurred.\n");
        return -1;
    }
    return 0;
}

int main(int argc, char* argv[])
{
	h264tots("E:\\ffmpeg_photo_story\\Debug\\rabbit.h264", "udp://127.0.0.1:6666");//
	return 0;
}