#include "FormatIn.hpp" #include #include #include #include extern "C"{ #include #include #include #include #include #include #include } #include "../log/log.hpp" #include "../configure/conf.hpp" #include "../property/VideoProp.hpp" #include "../../common/gpu/info.h" using namespace logif; namespace ffwrapper{ FormatIn::FormatIn(bool hw) :ctx_(NULL) ,dec_ctx_(NULL) ,vs_idx_(-1) ,as_idx_(-1) ,prop_(NULL) ,hw_accl_(hw) ,io_ctx_(NULL) ,read_io_buff_(NULL) ,read_io_buff_size_(32768) ,handle_gb28181(NULL) ,fps_(25.0) {} FormatIn::FormatIn(const VideoProp &prop, bool hw/*=true*/) :ctx_(NULL) ,dec_ctx_(NULL) ,vs_idx_(-1) ,as_idx_(-1) ,prop_(NULL) ,hw_accl_(hw) ,io_ctx_(NULL) ,read_io_buff_(NULL) ,read_io_buff_size_(32768) ,handle_gb28181(NULL) ,fps_(25.0) { prop_ = new VideoProp; *prop_ = prop; } FormatIn::~FormatIn() { logIt("free format in"); if (prop_) delete prop_; if(dec_ctx_){ avcodec_close(dec_ctx_); avcodec_free_context(&dec_ctx_); dec_ctx_ = NULL; } if(ctx_){ avformat_close_input(&ctx_); ctx_ = NULL; } if (handle_gb28181){ delete handle_gb28181; } if(io_ctx_){ av_freep(&io_ctx_->buffer); av_freep(&io_ctx_); io_ctx_ = NULL; } } //////////////////////////////////////////////////////////////////////// int FormatIn::openWithCustomIO(void *opaque, read_packet fn, AVDictionary **options/*=NULL*/){ read_io_buff_ = (uint8_t*)av_malloc(read_io_buff_size_); if(!read_io_buff_){ logIt("open with custom io alloc read io buff error\n"); return -1; } io_ctx_ = avio_alloc_context(read_io_buff_, read_io_buff_size_, 0, opaque, fn, NULL, NULL);//opaque if(!io_ctx_){ logIt("open with custom io create custom avio error\n"); return -1; } ctx_ = avformat_alloc_context(); if(!ctx_){ logIt("open with custom io create format error\n"); return -1; } ctx_->pb = io_ctx_; // auto err = av_probe_input_buffer(ctx_->pb, &ctx_->iformat, NULL, NULL, 0, 0); // if(err != 0){ // logIt("open with custom io prob input buffer error:%d err: %s\n", err, getAVErrorDesc(err).c_str()); // return -1; // } return 0; } int FormatIn::openGb28181(const char *filename, AVDictionary **options){ std::string fn = filename; //GB28181API gb28181(fn); if (handle_gb28181){ delete handle_gb28181; } handle_gb28181 = new GB28181API; if(handle_gb28181->addCamera(fn) == -1){ logIt("do addCamera Error\n"); return -1; } int ret = openWithCustomIO(handle_gb28181, handle_gb28181->readData, options); if(ret < 0){ logIt("do openWithCustomIO failed:%d",ret); }else{ ret = avformat_open_input(&ctx_, "", NULL, options); } // if(ret < 0){ // logIt("open %s failed:%s",filename, // getAVErrorDesc(ret).c_str()); // } return ret; } ///////////////////////////////////////////////////////////////////////// int FormatIn::open(const char *filename, AVDictionary **options){ const int ret = avformat_open_input(&ctx_, filename, NULL, options); // if(ret < 0){ // logIt("open %s failed:%s",filename, // getAVErrorDesc(ret).c_str()); // } return ret; } bool FormatIn::findStreamInfo(AVDictionary **options){ const int ret = avformat_find_stream_info(ctx_, options); if(ret < 0){ logIt("find %s stream info failed:%s", ctx_->filename,getAVErrorDesc(ret).c_str()); return false; } // logIt("there are %d stream", ctx_->nb_streams); for (int i = 0; i < ctx_->nb_streams; ++i) { auto type = ctx_->streams[i]->codecpar->codec_type; logIt("there are %d stream, stream %d, type %d", ctx_->nb_streams, i, type); if (type == AVMEDIA_TYPE_VIDEO){ vs_idx_ = i; auto in = ctx_->streams[i]; if(in->r_frame_rate.num >=1 && in->r_frame_rate.den >= 1){ fps_ = av_q2d(in->r_frame_rate); }else if(in->avg_frame_rate.num >=1 && in->avg_frame_rate.den >= 1){ fps_ = av_q2d(in->avg_frame_rate); } logIt("in stream video fps %f, time_base: %d : %d, size: %dx%d", fps_, in->time_base.num, in->time_base.den, in->codecpar->width, in->codecpar->height); } if (type == AVMEDIA_TYPE_AUDIO){ auto in = ctx_->streams[i]; logIt("in stream audio %d time_base: %d : %d", in->codecpar->codec_id, in->time_base.num, in->time_base.den); if (in->codecpar->codec_id == AV_CODEC_ID_AAC) as_idx_ = i; else logIt("record not support audio codec: %d", in->codecpar->codec_id); } } return true; } const bool FormatIn::IsHEVC()const{ return ctx_->streams[vs_idx_]->codecpar->codec_id == AV_CODEC_ID_HEVC; } const bool FormatIn::isAVC1()const{ if (IsHEVC()) return false; char p[100] = {0}; char *sub = av_fourcc_make_string(p, ctx_->streams[vs_idx_]->codecpar->codec_tag); const int ret = strcmp(sub, "avc1"); if (ret == 0) return true; return false; } bool FormatIn::openCodec(AVDictionary **options){ if (vs_idx_ == -1) return false; AVStream *s = ctx_->streams[vs_idx_]; AVCodecParameters *codecpar = s->codecpar; AVCodec *dec = NULL; bool flag = false; AVDictionary *avdic = NULL; int idle_gpu = -1; srand((unsigned)time(NULL)); constexpr int need = 350; // M constexpr int reserved = 512; // M for (int i = 0; i < 2; ++i) { if(hw_accl_){ // 设置gpu index if (prop_->gpu_index_ > -1){ if (!gpu::satisfy(prop_->gpu_index_, need, reserved)){ hw_accl_ = false; continue; } idle_gpu = prop_->gpu_index_; }else{ idle_gpu = gpu::getGPUPrior(need, reserved, 0); // idle_gpu = gpu::getGPU(300); usleep(2000000 + rand()%3000000); if (!gpu::satisfy(idle_gpu, need, reserved)){ hw_accl_ = false; continue; } } if(idle_gpu < 0){ logIt("NO GPU RESOURCE TO DECODE"); hw_accl_ = false; continue; } std::string codec_name(avcodec_get_name(codecpar->codec_id)); codec_name += "_cuvid"; dec = avcodec_find_decoder_by_name(codec_name.c_str()); if(!dec){ hw_accl_ = false; continue; }else{ av_dict_set(&avdic, "gpu", std::to_string(idle_gpu).c_str(), 0); } }else{ dec = avcodec_find_decoder(codecpar->codec_id); } if(dec){ if(avdic){ options = &avdic; logIt("DECODE USE GPU %d", idle_gpu); } flag = allocCodec(dec, s, options); if(avdic){ av_dict_free(&avdic); } if(flag){ logIt("use decoder %s\n", dec->name); break; }else{ av_free(dec_ctx_); dec_ctx_ = NULL; hw_accl_ = false; } } } return flag; } bool FormatIn::allocCodec(AVCodec *dec, AVStream *s, AVDictionary **options){ AVCodecParameters *codecpar = s->codecpar; dec_ctx_ = avcodec_alloc_context3(dec); if (!dec_ctx_){ logIt("avcodec_alloc_context3 error"); return false; } int ret = avcodec_parameters_to_context(dec_ctx_, codecpar); if(ret < 0){ logIt("avcodec_parameters_to_context error : %s", getAVErrorDesc(ret).c_str()); return false; } av_codec_set_pkt_timebase(dec_ctx_, s->time_base); dec_ctx_->framerate = av_guess_frame_rate(ctx_, s, NULL); dec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; ret = avcodec_open2(dec_ctx_,dec, options); if(ret < 0){ logIt("open input %s codec failed:%s", ctx_->filename,getAVErrorDesc(ret).c_str()); return false; } return true; } AVStream *FormatIn::getStream(int type/*=-1*/){ if (type == -1){ return ctx_->streams[vs_idx_]; } if (vs_idx_ > -1 && type == ctx_->streams[vs_idx_]->codecpar->codec_type) return ctx_->streams[vs_idx_]; if (as_idx_ > -1 && type == ctx_->streams[as_idx_]->codecpar->codec_type) return ctx_->streams[as_idx_]; return NULL; } AVCodecContext *FormatIn::getCodecContext(int type){ return dec_ctx_; } bool FormatIn::isVideoPkt(AVPacket *pkt){ if (pkt->stream_index == vs_idx_){ return true; } return false; } bool FormatIn::isAudioPkt(AVPacket *pkt){ if (pkt->stream_index == as_idx_){ return true; } return false; } bool FormatIn::notVideoAudio(AVPacket *pkt){ return !isVideoPkt(pkt) && !isAudioPkt(pkt); } int FormatIn::readPacket(AVPacket *pkt_out){ auto flag = av_read_frame(ctx_, pkt_out); return flag; } int FormatIn::decode(AVFrame* frame, AVPacket *pkt){ AVStream *in = getStream(); av_packet_rescale_ts(pkt, in->time_base, in->codec->time_base); int ret = avcodec_send_packet(dec_ctx_, pkt); if(ret < 0){ logIt("avcodec_send_packet error : %s", getAVErrorDesc(ret).c_str()); return -1; } while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx_, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { logIt("decode frame failed : %s", getAVErrorDesc(ret).c_str()); return -1; }else{ return 0; } } return -2; } }