/*
* Copyright (C) 2020 ~ %YEAR% Uniontech Software Technology Co.,Ltd.
*
* Author:     shicetu <shicetu@uniontech.com>
*             hujianbo <hujianbo@uniontech.com>
* Maintainer: shicetu <shicetu@uniontech.com>
*             hujianbo <hujianbo@uniontech.com>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "mp4.h"
#include <libavutil/mathematics.h>
#include "camview.h"
#include "load_libs.h"
#include <stdlib.h>  // 为abs函数添加头文件

extern int verbosity;  // 添加verbosity声明

static int64_t video_pts = 0;
static int64_t audio_pts = 0;
static int64_t first_pts = 0;
static int64_t last_pts = 0;
static int64_t last_video_timestamp = 0;  // 添加上一帧视频时间戳记录

// 添加同步检查变量
static int frame_count = 0;
static int64_t last_sync_check_time = 0;

AVFormatContext *mp4_create_context(const char *filename)
{
    AVFormatContext *mp4_ctx;

    getAvformat()->m_avformat_alloc_output_context2(&mp4_ctx, NULL, NULL, filename);

    if (!mp4_ctx) {
        printf("Could not deduce output format from file extension: using MPEG.\n");
        getAvformat()->m_avformat_alloc_output_context2(&mp4_ctx, NULL, "mpeg", filename);
    }

    if (!mp4_ctx)
    {
		fprintf(stderr, "ENCODER: FATAL memory allocation failure (mp4_create_context): %s\n", strerror(errno));
		exit(-1);
	}

    return mp4_ctx;
}

void mp4_add_video_stream(
		AVFormatContext *mp4_ctx,
        encoder_codec_data_t *video_codec_data,
        OutputStream *video_stream)
{
    video_stream->st = getAvformat()->m_avformat_new_stream(mp4_ctx, video_codec_data->codec);

    if(!video_stream->st)
    {
        fprintf(stderr,"Could not allocate stream\n");
        exit(1);
    }

    video_stream->st->id = mp4_ctx->nb_streams - 1;
    video_stream->enc = video_codec_data->codec_context;
    video_stream->st->time_base = video_codec_data->codec_context->time_base;

    if(mp4_ctx->oformat->flags & AVFMT_GLOBALHEADER)
    {
        video_stream->enc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
}

void mp4_add_audio_stream(
		AVFormatContext *mp4_ctx,
        encoder_codec_data_t *audio_codec_data,
        OutputStream *audio_stream)
{

    audio_stream->st = getAvformat()->m_avformat_new_stream(mp4_ctx,audio_codec_data->codec);
    if(!audio_stream->st)
    {
        fprintf(stderr,"Could not allocate stream\n");
        exit(1);
    }
    audio_stream->st->id = mp4_ctx->nb_streams - 1;
    audio_stream->st->time_base = audio_codec_data->codec_context->time_base;
    audio_stream->enc = audio_codec_data->codec_context;
    if(mp4_ctx->oformat->flags & AVFMT_GLOBALHEADER)
    {
        audio_stream->enc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
}

int mp4_write_packet(
        AVFormatContext *mp4_ctx,
        encoder_codec_data_t *codec_data,
        int stream_index,
        uint8_t *outbuf,
        uint32_t outbuf_size,
        uint64_t pts,
        int flags)
{
    AVPacket *outpacket = codec_data->outpkt;
    outpacket->data = (uint8_t*)calloc((unsigned int)outbuf_size, sizeof(uint8_t));

    memcpy(outpacket->data, outbuf, outbuf_size);
    outpacket->size =(int)outbuf_size;

    if(codec_data->codec_context->codec_type == AVMEDIA_TYPE_VIDEO){
        outpacket->pts = video_pts;
        outpacket->dts = video_pts;
        outpacket->duration = 0;
        outpacket->flags = flags;
        outpacket->stream_index = stream_index;
        AVRational video_time = mp4_ctx->streams[stream_index]->time_base;
        AVRational time_base = codec_data->codec_context->time_base;

        if (video_pts > last_pts || video_pts == 0) {
            getLoadLibsInstance()->m_av_packet_rescale_ts(outpacket, time_base, video_time);
            getAvformat()->m_av_write_frame(mp4_ctx, outpacket);
            set_video_time_capture((double)(pts)/1000/1000000);
        } else {
            fprintf(stderr,"pts err:video_pts: %ld  last_pts: %ld\n", video_pts, last_pts);
        }

        last_pts = video_pts;
        if (first_pts == 0) {
            first_pts = pts;
            last_video_timestamp = pts;
            video_pts = 0;  // 第一个视频包的PTS为0
        } else {
            // 使用真实时间戳计算，而不是硬编码的33ms
            // 将纳秒时间戳转换为帧数（基于time_base）
            int64_t timestamp_diff = pts - first_pts;  // 从开始录制的时间差（纳秒）
            
            // 根据编码器的time_base计算PTS
            // time_base通常是1/fps，所以pts = 时间差 * fps
            if (time_base.den > 0 && time_base.num > 0) {
                video_pts = (timestamp_diff * time_base.den) / (time_base.num * 1000000000LL);
            } else {
                // 如果time_base无效，使用动态帧率计算
                int64_t frame_duration = pts - last_video_timestamp;
                if (frame_duration > 0) {
                    // 基于实际帧间隔计算
                    video_pts = timestamp_diff / frame_duration;
                } else {
                    // 回退到固定帧率（但使用更精确的计算）
                    video_pts = timestamp_diff / 33333333LL;  // 30fps = 33.33ms
                }
            }
            
            last_video_timestamp = pts;
            
            if (video_pts == last_pts)  //pts must be strictly incremented
                video_pts++;
            if(video_pts < last_pts)
                video_pts = last_pts + 1;
                
            if (verbosity > 2) {
                fprintf(stderr, "video_pts: %ld (timestamp: %ld, first_pts: %ld)\n", 
                        video_pts, pts, first_pts);
            }
            
            // 每100帧检查一次音视频同步状态
            frame_count++;
            if (frame_count % 100 == 0) {
                int64_t video_time_ms = (pts - first_pts) / 1000000;  // 视频实际时间（毫秒）
                int64_t video_pts_time_ms = 0;
                
                // 根据time_base计算视频PTS对应的时间
                if (time_base.den > 0 && time_base.num > 0) {
                    video_pts_time_ms = (video_pts * time_base.num * 1000) / time_base.den;
                }
                
                int64_t sync_diff = video_time_ms - video_pts_time_ms;
                
                if (verbosity > 0 && abs(sync_diff) > 100) {  // 如果差异超过100ms
                    fprintf(stderr, "SYNC CHECK: Frame %d, Real time: %ldms, PTS time: %ldms, Diff: %ldms\n",
                            frame_count, video_time_ms, video_pts_time_ms, sync_diff);
                }
                
                last_sync_check_time = pts;
            }
        }
    }

    if(codec_data->codec_context->codec_type == AVMEDIA_TYPE_AUDIO) {

        outpacket->pts = audio_pts;
        outpacket->flags = flags;
        outpacket->stream_index = stream_index;
        AVRational audio_time = mp4_ctx->streams[stream_index]->time_base;
        AVRational time_base = codec_data->codec_context->time_base;

        getLoadLibsInstance()->m_av_packet_rescale_ts(outpacket, time_base, audio_time);
        getAvformat()->m_av_write_frame(mp4_ctx, outpacket);

        // 使用基于真实时间的音频PTS计算
        if (first_pts == 0) {
            first_pts = pts;  // 设置first_pts
            audio_pts = 0;    // 第一个音频包的PTS为0
        } else {
            // 将纳秒时间戳转换为音频时间基准
            int64_t timestamp_diff = pts - first_pts;  // 从开始录制的时间差（纳秒）
            
            if (time_base.den > 0 && time_base.num > 0) {
                // 根据音频编码器的time_base计算PTS
                audio_pts = (timestamp_diff * time_base.den) / (time_base.num * 1000000000LL);
            } else {
                // 如果time_base无效，使用采样率计算
                int sample_rate = codec_data->codec_context->sample_rate;
                if (sample_rate <= 0) sample_rate = 48000;  // 默认采样率
                
                // 基于真实时间和采样率计算音频PTS
                audio_pts = (timestamp_diff * sample_rate) / 1000000000LL;
            }
        }
        
        if (verbosity > 2) {
            fprintf(stderr, "audio_pts: %ld (timestamp: %ld, first_pts: %ld)\n", 
                    audio_pts, pts, first_pts);
        }
    }

    if(outpacket->data){
        free(outpacket->data);
        outpacket->data = NULL;
        getLoadLibsInstance()->m_av_packet_unref(outpacket);
    }
    return 0;
}


void mp4_destroy_context(AVFormatContext *mp4_ctx)
{
    video_pts = 0;
    audio_pts = 0;
    first_pts = 0;
    last_pts = 0;
    last_video_timestamp = 0;  // 重置新增的变量
    frame_count = 0;           // 重置帧计数
    last_sync_check_time = 0;  // 重置同步检查时间
    if(mp4_ctx != NULL)
    {
        getAvformat()->m_avformat_free_context(mp4_ctx);
        //mp4_ctx = NULL;
    }
}


