#include "VideoEngine.h"

using namespace ai_matrix;

namespace
{
    const int LOW_THRESHOLD = 128;
    const int MAX_THRESHOLD = 4096;
    const uint16_t DELAY_TIME = 10000;
}

VideoEngine::VideoEngine() {}

VideoEngine::~VideoEngine() {}

APP_ERROR VideoEngine::Init()
{
    std::vector<DataSourceConfig> vecDataSourceConfig = Config::getins()->getAllDataSourceConfig();
    if (vecDataSourceConfig.size() <= this->engineId_)
    {
        LogWarn << " -- " << engineName_ << "_" << engineId_ << " dataSource no set, Engine DeInit";
        return APP_ERR_OK;
    }
    dataSourceConfig_ = vecDataSourceConfig.at(engineId_);

    strPort0_ = engineName_ + "_" + std::to_string(engineId_) + "_0";
    strPort1_ = engineName_ + "_" + std::to_string(engineId_) + "_1";

    LogInfo << "engineId_:" << engineId_ << " VideoEngine Init ok";
    return APP_ERR_OK;
}

APP_ERROR VideoEngine::DeInit()
{
    ResetCamera();
    LogInfo << "engineId_:" << engineId_ << " VideoEngine DeInit ok";
    return APP_ERR_OK;
}

void VideoEngine::ResetCamera()
{
    if (pFormatCtx_ != nullptr)
    {
        // clear th cache of the queue
        avformat_close_input(&pFormatCtx_);
        pFormatCtx_ = nullptr;
    }
}

APP_ERROR VideoEngine::ConnectCamera()
{
    pFormatCtx_ =  CreateFormatContext(); // create context
    if (pFormatCtx_ == nullptr)
    {
        LogError << "engineId_:" << engineId_ << " pFormatCtx_ null!";
        return APP_ERR_COMM_FAILURE;
    }

    //0-代表输入
    av_dump_format(pFormatCtx_, 0, dataSourceConfig_.strUrl.c_str(), 0);

    // get stream infomation
    int iRet = APP_ERR_OK;
    iRet = GetStreamInfo();
    if (iRet != APP_ERR_OK)
    {
        LogError << "engineId_:" << engineId_ << " Stream Info Check failed, iRet = " << iRet;
        return APP_ERR_COMM_FAILURE;
    }

    return APP_ERR_OK;
}

APP_ERROR VideoEngine::GetStreamInfo()
{
    if (pFormatCtx_ != nullptr)
    {
        iVideoStream_ = -1;
        iAudioStream_ = -1;
        //frameInfo_.iFrameId = 0; //帧号从0开始

        for (unsigned int i = 0; i < pFormatCtx_->nb_streams; i++)
        {
            AVStream *inStream = pFormatCtx_->streams[i];
            if (inStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                iVideoStream_ = i;
                frameInfo_.iHeight = inStream->codecpar->height;
                frameInfo_.iWidth = inStream->codecpar->width;

                //获取帧率,帧率的打印都在流中的两个成员.且应取平均帧率为先,为{x,0}或者{0,1}则取实时帧率
                if (inStream->avg_frame_rate.den == 0 || (inStream->avg_frame_rate.num == 0 && inStream->avg_frame_rate.den == 1))
                {
                    frameInfo_.iRate = inStream->r_frame_rate.num / inStream->r_frame_rate.den;
                }
                else
                {

                    frameInfo_.iRate = inStream->avg_frame_rate.num / inStream->avg_frame_rate.den;
                }

                frameInfo_.iRate = frameInfo_.iRate == 0 ? 25 : frameInfo_.iRate;

                LogDebug << "engineId_:" << engineId_ << " width:" << frameInfo_.iWidth << " height:" << frameInfo_.iHeight
                         << " rate:" << frameInfo_.iRate << " iVideoStream_:" << iVideoStream_;
            }
//            else if (inStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
//            {
//                iAudioStream_ = i;
//                LogDebug << "engineId_:" << engineId_ << " iAudioStream_:" << iAudioStream_;
//            }
        }

        if (iVideoStream_ == -1)
        {
            LogError << "engineId_:" << engineId_ << " Didn't find a video stream!";
            return APP_ERR_COMM_FAILURE;
        }

        if (frameInfo_.iHeight < LOW_THRESHOLD || frameInfo_.iWidth < LOW_THRESHOLD ||
            frameInfo_.iHeight > MAX_THRESHOLD || frameInfo_.iWidth > MAX_THRESHOLD)
        {
            LogError << "engineId_:" << engineId_ << " Size of frame is not supported in DVPP Video Decode!";
            return APP_ERR_COMM_FAILURE;
        }

        pCodecParameters_ = pFormatCtx_->streams[iVideoStream_]->codecpar;

    }
    return APP_ERR_OK;
}

AVFormatContext *VideoEngine::CreateFormatContext()
{
    // create message for stream pull
    AVFormatContext *pFormatContext = nullptr;
    AVDictionary *pOptions = nullptr;

    // formatContext = avformat_alloc_context();
    if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos) // rtsp
    {
        //设置缓存大小,1080p可将值调大
        av_dict_set(&pOptions, "buffer_size", "8192000", 0);
        //以tcp方式打开,如果以udp方式打开将tcp替换为udp
        av_dict_set(&pOptions, "rtsp_transport", "tcp", 0);
        //设置超时断开连接时间,单位微秒,3000000表示3秒
        av_dict_set(&pOptions, "stimeout", "3000000", 0);
        //设置最大时延,单位微秒,1000000表示1秒
        av_dict_set(&pOptions, "max_delay", "1000000", 0);
        //自动开启线程数
        av_dict_set(&pOptions, "threads", "auto", 0);

    }

    //av_register_all();          //注册所有支持的格式(这里一定注册这些，否则会因为协议解析问题报错!!!)
    //avcodec_register_all();     //注册编解码器
    //avformat_network_init();    //注册网格格式，如果为本地文件则可以去掉该代码

    int iRet = avformat_open_input(&pFormatContext, dataSourceConfig_.strUrl.c_str(), nullptr, &pOptions);
    if (nullptr != pOptions)
    {
        av_dict_free(&pOptions);
    }
    if (iRet != 0)
    {
        LogError << "engineId_:" << engineId_ << " Couldn't open input stream " << dataSourceConfig_.strUrl.c_str() << ", iRet=" << iRet;
        return nullptr;
    }

    // pFormatContext->flags |= AVFMT_FLAG_NONBLOCK;
    // pFormatContext->pb->flags |= AVIO_FLAG_NONBLOCK;
    // av_dict_set(&pFormatContext->interrupt_callback.callback, "timeout", "3000", 0);
    // iRet = avio_open2(&pFormatContext->pb, dataSourceConfig_.strUrl.c_str(), AVIO_FLAG_READ, NULL, NULL) < 0;
    // {
    //     // 处理错误
    //     LogError << "engineId_:" << engineId_ << "avio_open2 iRet=" << iRet;
    //     return nullptr;
    // }

    iRet = avformat_find_stream_info(pFormatContext, nullptr);
    if (iRet != 0)
    {
        LogError << "engineId_:" << engineId_ << " Couldn't find stream information, iRet = " << iRet;
        return nullptr;
    }
    return pFormatContext;
}

//av_read_frame的中断回调函数
// int VideoEngine::InterruptCallback(void *pData)
// {
//     TimeoutContext* pTimeOutCtx = (TimeoutContext*)pData;
//     LogDebug << "InterruptCallback i64Timeout:" << pTimeOutCtx->i64Timeout;
//     return std::chrono::duration_cast<std::chrono::milliseconds>(
//                std::chrono::system_clock::now().time_since_epoch())
//                        .count() >= pTimeOutCtx->i64Timeout
//                ? AVERROR_EXIT
//                : 0;
// }

APP_ERROR VideoEngine::Process()
{
    int iRet = APP_ERR_OK;

    // Pull data cyclically
    AVPacket pkt;

    while (!isStop_)
    {
        //重连相机
        if (bConnectFlag_)
        {
            iRet = ConnectCamera();
            if (iRet == APP_ERR_OK)
            {
                LogInfo << "engineId_:" << engineId_ << " Start the stream......";
                bConnectFlag_ = false;
            }
            else
            {
//                outputQueMap_[strPort1_]->push(std::static_pointer_cast<void>(std::make_shared<std::string>("摄像头连接失败！")));
                ResetCamera();
                bConnectFlag_ = true;
                std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒后重连
                continue;
            }
        }

        //设置av_read_frame中断函数 (中断函数中超过1s，则中断处理)
        // TimeoutContext timeoutCtx = { std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() + 1000 };
        // pFormatCtx_->interrupt_callback.callback = &VideoEngine::InterruptCallback;
        // pFormatCtx_->interrupt_callback.opaque = &timeoutCtx;

        av_init_packet(&pkt); //init pkt

        iRet = av_read_frame(pFormatCtx_, &pkt); //需要一直读取，否则获取到的是历史数据
        if (iRet != 0)
        {
            av_packet_unref(&pkt);

            if (dataSourceConfig_.strUrl.find("rtsp:") != std::string::npos)
            {
                LogError << "engineId_:" << engineId_ << " Read frame failed, reconnect iRet:" << iRet;
                //重连相机
                ResetCamera();
                bConnectFlag_ = true;
            }
            else
            {
//                LogWarn << "----- 视频播放完毕 -----";
//                //重连相机
//                ResetCamera();
//                bConnectFlag_ = true;

                std::this_thread::sleep_for(std::chrono::seconds(3)); //3秒
            };

            //组织数据
            std::shared_ptr<ProcessData> pProcessData = std::make_shared<ProcessData>();
            pProcessData->iDataSource = this->engineId_;
            pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true);
            pProcessData->bIsEnd = true;

            //push端口0，视频解码
            iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pProcessData), true);
            if (iRet != APP_ERR_OK)
            {
                LogError << "数据推动失败，解码引擎关闭...";
            }

            continue;
        }

        if (pkt.stream_index == iVideoStream_) //只解码视频流
        {
//                LogDebug << "iRet:" << iRet << " pkt.size:" << pkt.size;
            if (pkt.size <= 0)
            {
                LogError << "engineId_:" << engineId_ << " Invalid pkt.size: " << pkt.size;
                av_packet_unref(&pkt);
                continue;
            }
            if (dataSourceConfig_.strUrl.find(".mp4") != std::string::npos)
            {
                const char szStartCode[4] = {0, 0, 0, 1};
                if (bIsAvc_ || memcmp(szStartCode, pkt.data, 4) != 0)
                { // is avc1 code, have no start code of H264
                    int iLen = 0;
                    uint8_t *p = pkt.data;
                    bIsAvc_ = true;
                    do
                    { // add start_code for each NAL, one frame may have multi NALs.
                        iLen = ntohl(*((long *)p));
                        memcpy(p, szStartCode, 4);
                        p += 4;
                        p += iLen;
                        if (p >= pkt.data + pkt.size)
                        {
                            break;
                        }
                    } while (true);
                }
            }

            void* pH264Buffer = nullptr;
            pH264Buffer = new uint8_t[pkt.size];
            memcpy(pH264Buffer, pkt.data, pkt.size);
            //组织数据
            std::shared_ptr<ProcessData> pProcessData = std::make_shared<ProcessData>();
            pProcessData->iDataSource = this->engineId_;
            pProcessData->pCodecParameters_ = this->pCodecParameters_;
            pProcessData->dataSourceInfo = frameInfo_;
            pProcessData->sourceFrameData.i64TimeStamp = TimeUtil::getins()->getCurrentTimeMillis(true);
            pProcessData->sourceFrameData.iSize = pkt.size;
            pProcessData->sourceFrameData.pData.reset(pH264Buffer, [](void* data){if(data) {delete[] data; data = nullptr;}}); //智能指针管理内存

            //push端口0，视频解码
            iRet = outputQueMap_[strPort0_]->push(std::static_pointer_cast<void>(pProcessData), true);
            if (iRet != APP_ERR_OK)
            {
                LogError << "数据推动失败，解码引擎关闭...";
            }
        }
        else
        {
//            LogError << "engineId_:" << engineId_ << " stream err stream_index:" << pkt.stream_index;
        }
        av_packet_unref(&pkt); //unref

        if (dataSourceConfig_.strUrl.find("rtsp:") == std::string::npos) // 如果不是rtsp，定时发送
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000 / frameInfo_.iRate));
            if (this->isStop_) return APP_ERR_OK;
        }
    }
    return APP_ERR_OK;
}
