FFGB28181Decoder.cpp 8.69 KB
//#include "LOG_manager.h"
#include <iostream>
#include "FFGB28181Decoder.h"

#include "../FFCuContextManager.h"

extern "C" {
#include "libavutil/avstring.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
}

#include "../logger.hpp"

#define ECLOSED 0
#define ECLOSING 1
#define ERUNNING 2
#define EPAUSE 3

static void RTP_Stream_CallBack(void* userdata, int videoType, char* data, int len, int isKey, uint64_t pts, uint64_t localPts)
{
    FFGB28181Decoder* session = (FFGB28181Decoder*)userdata;
    session->stream_callback(videoType, data, len, isKey, pts, localPts);
}

static void RTP_Stream_End_CallBack(void* userdata)
{
    FFGB28181Decoder* session = (FFGB28181Decoder*)userdata;
    session->stream_end_callback();
}

FFGB28181Decoder::FFGB28181Decoder() {
	m_frameSkip = 1;
    m_port = -1;
    m_dec_keyframe = false;
}

FFGB28181Decoder::~FFGB28181Decoder()
{
    close();
	
    if (m_pAVCodecCtx) {
        avcodec_close(m_pAVCodecCtx);
        avcodec_free_context(&m_pAVCodecCtx);
    }

    if (m_pAVFrame) {
        av_frame_free(&m_pAVFrame);
        m_pAVFrame = NULL;
    }

    m_dec_keyframe = false;
    
    LOG_INFO("destroy OK--{}", m_dec_name);
}

void FFGB28181Decoder::close(){
    if (m_status == ECLOSED || m_status == ECLOSING) return ;
    
    m_status = ECLOSING;

    LOG_INFO("real decode thread exit success 1--{}", m_dec_name);

    if (m_rtp.IsOpened()) {
        m_rtp.Close();
        LOG_INFO("real decode thread exit success 4--{}", m_dec_name);
    }

    stream_end_callback();

    m_status = ECLOSED;
}

bool FFGB28181Decoder::init(FFDecConfig& cfg){
    if (m_rtp.IsOpened()){
        return false;
    }

    m_dec_name = cfg.uri;
	m_frameSkip = cfg.skip_frame;
    if (m_frameSkip < 1) m_frameSkip = 1;

    m_gpuid = atoi(cfg.gpuid.c_str());

    m_rtp.SetDeviceID(m_dec_name);

    m_port = cfg.port;

    m_cfg = cfg;

    return true;
}

bool FFGB28181Decoder::start() {

    m_status = ERUNNING;

	m_rtp.SetOutputCallback(RTP_Stream_CallBack, this);
	m_rtp.SetVodEndCallback(RTP_Stream_End_CallBack, this);

    return m_rtp.Open((uint16_t)m_port);
}

void FFGB28181Decoder::setDecKeyframe(bool bKeyframe){
	m_dec_keyframe = bKeyframe;
}

static AVPixelFormat get_hw_format(AVCodecContext *avctx, const AVPixelFormat *pix_fmts){
	return AV_PIX_FMT_CUDA;
}

void FFGB28181Decoder::stream_callback(int videoType, char* data, int len, int isKey, uint64_t pts, uint64_t localPts) {
    if (m_status == EPAUSE) return;

    // 若设置为关键帧解码,非关键帧数据直接返回
    if(m_dec_keyframe && !isKey) return;
    
    if (len <= 0) {
        if (data == nullptr && pts == -1) {
            LOG_INFO("frame callback EOF!");
			post_decoded_cbk(m_postDecArg, nullptr);
            return ;
        }
        LOG_INFO("frame data is zero --{}", m_dec_name);
        return;
    }

	AVPacket framePacket = {}, mp4Packet = {};
    av_init_packet(&framePacket);
	av_init_packet(&mp4Packet);

    framePacket.size = len;
    framePacket.data = (uint8_t*)data;

	AVDictionary *gpu_options = nullptr;

	if (m_pAVCodecCtx == nullptr) {
		if (VIDEO_TYPE_H264 == videoType) {
            if (m_gpuid >= 0){
                m_pAVCodec = avcodec_find_decoder_by_name("h264_cuvid");
            }
            else{
				m_pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
			}
			LOG_INFO("m_avCodecName is H264");		
		}
		else if (VIDEO_TYPE_H265 == videoType)
		{
            if (m_gpuid >= 0){
                m_pAVCodec = avcodec_find_decoder_by_name("hevc_cuvid");
            }
            else{
                m_pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H265);
            }
			LOG_INFO("m_avCodecName is H265");
		}
		else{
			LOG_INFO("m_avCodecName is unknown, videoType is {}", videoType);
		}

		if (!m_pAVCodec)
		{
			LOG_ERROR("frameCallback frame decode error, ERROR_DECODER_NOT_FOUND");
			return;
		}

        m_pAVCodecCtx = avcodec_alloc_context3(m_pAVCodec);
       

        if (m_gpuid >= 0) {
            char gpui[8] = { 0 };
            sprintf(gpui, "%d", m_gpuid);
            av_dict_set(&gpu_options, "gpu", gpui, 0);

            m_pAVCodecCtx->get_format = get_hw_format;

            FFCuContextManager* pCtxMgr = FFCuContextManager::getInstance();
            m_pAVCodecCtx->hw_device_ctx = av_buffer_ref(pCtxMgr->getCuCtx(gpui));
            if (nullptr == m_pAVCodecCtx->hw_device_ctx){
                // TODO 这里应该抛出错误
                return ;
            }
        }

        if (avcodec_open2(m_pAVCodecCtx, m_pAVCodec, &gpu_options) < 0)
			return;

		m_pAVFrame = av_frame_alloc();
	}

    //开始解码
    int ret = avcodec_send_packet(m_pAVCodecCtx, &framePacket);
    if (ret < 0) {
        //send_exception(RunMessageType::E2002, e_msg);
        LOG_ERROR("Real stream视频解码失败,请检查视频设备{}: avcodec_send_packet failed. ret={}", m_dec_name, ret);
        return;
    }
 
    if (frameW < 1) {
        frameW = m_pAVCodecCtx->width;
        frameH = m_pAVCodecCtx->height;
        if (frameW <= 0 || frameH <= 0) {
            LOG_ERROR("[{}] frame W or H is error! ({},{})", m_dec_name, frameW, frameH);
            return;
        }	
    }
	// m_fps = m_pAVCodecCtx->pkt_timebase.den == 0 ? 25.0 : av_q2d(m_pAVCodecCtx->pkt_timebase);
    m_fps = av_q2d(m_pAVCodecCtx->framerate);
	LOG_DEBUG("frameW {}--frameH {}", frameW, frameH);
    while (ret  >= 0) {
        ret = avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame);
        if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
            return;
        else if (ret < 0) {       
            if (m_frameCount % 10 == 0){
                //send_exception(RunMessageType::E2002, e_msg);
                LOG_ERROR("Real stream视频解码失败,请检查视频设备{}: avcodec_receive_frame failed. ret={}", m_dec_name, ret);
            }
            continue;
        }

        if (++m_frameCount % m_frameSkip != 0) continue;
        
        if (m_pAVFrame->width != frameW || m_pAVFrame->height != frameH){
            LOG_INFO("AVFrame is inconsistent: width is {}, height is {}; original frameW is {}, frameH is {}--{}", m_pAVFrame->width, m_pAVFrame->height, frameW, frameH , m_dec_name);
            continue;
        }
        
        LOG_DEBUG("curpos is: {}", m_frameCount);

		post_decoded_cbk(m_postDecArg, m_pAVFrame);

            //LOG_count++;
            //if (LOG_count > 100000) {
            //    LOG_INFO("Real frame send_shm_videoframe pts={}-{}", localPts, m_dec_name);
            //    //LOG_count = 0;
            //}
        //}
        //catch (GeneralException2& e)
        //{
        //    LOG_ERROR("send_shm_videoframe failed! {}--{}--{}", e.err_code(), e.err_msg(), m_dec_name);
        //    if (e.err_code() == -666) {
        //        this->close();
        //    }
        //    
        //    if (e.err_code() == ERROR_MEMORY) {
        //        if (m_frameCount % 10 == 0) {
        //            string e_msg;
        //            format_string(e_msg, "服务器资源内存分配失败, 在vas模块%s文件%d行出现无法获取内存的情况!", __FILE__, __LINE__);
        //            send_exception(RunMessageType::F4001, e_msg);
        //            LOG_ERROR("{}", e_msg);
        //        }
        //    }
        //    return;
        //}
    }

	if (gpu_options) av_dict_free(&gpu_options);
}

void FFGB28181Decoder::stream_end_callback()
{
    LOG_INFO("send_video_eof--{}", m_dec_name);

	decode_finished_cbk(m_finishedDecArg);

    return;
}

void FFGB28181Decoder::pause() {
    m_status = EPAUSE;
    LOG_INFO("pause --{}", m_dec_name);
}

void FFGB28181Decoder::resume() {
    m_status = ERUNNING;
    LOG_INFO("resume --{}", m_dec_name);
}

bool FFGB28181Decoder::isRunning(){
    if (m_status == ECLOSED || m_status == ECLOSING){
        return false;
    }
    return true;
}

bool FFGB28181Decoder::isFinished(){
    if (m_status == ECLOSED || m_status == ECLOSING){
        return true;
    }
    return false;
}

bool FFGB28181Decoder::isPausing(){
    if (m_status == EPAUSE){
        return true;
    }
    return false;
}

bool FFGB28181Decoder::getResolution( int &width, int &height ){
    width = frameW;
    height = frameH;
    return true;
}

float FFGB28181Decoder::fps() {
    return m_fps;
}

bool FFGB28181Decoder::isSurport(FFDecConfig& cfg){
    // 由于是否支持需要在拿到数据后才能断定,无法事先判断,所以这个地方默认返回true
    return true;
}

int FFGB28181Decoder::getCachedQueueLength(){
    return m_rtp.GetPsFrameListSize();
}