//#include "LOG_manager.h" #include #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(); }