#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) ,io_ctx_(NULL) ,read_io_buff_(NULL) ,read_io_buff_size_(32768) ,fps_(25.0) {} FormatIn::FormatIn(const VideoProp &prop, bool hw/*=true*/) :FormatIn(hw) { 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(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_; return 0; } int FormatIn::open(const char *filename, AVDictionary **options){ int ret = avformat_open_input(&ctx_, filename, NULL, options); ret = avformat_find_stream_info(ctx_, options); if(ret < 0){ logIt("find %s stream info failed:%s", ctx_->filename,getAVErrorDesc(ret).c_str()); return ret; } 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 0; } 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 (dec_ctx_) return true; if (vs_idx_ == -1) return false; AVStream *s = ctx_->streams[vs_idx_]; AVCodecParameters *codecpar = s->codecpar; bool flag = false; AVCodecID codec_id = codecpar->codec_id; AVCodec *dec = avcodec_find_decoder(codec_id); if(dec){ flag = allocCodec(dec, s, options); if(*options){ av_dict_free(options); } if(!flag){ av_free(dec_ctx_); dec_ctx_ = NULL; } logIt("use decoder %s\n", dec->name); } return flag; } bool FormatIn::allocCodec(AVCodec *dec, AVStream *s, AVDictionary **options){ AVCodecParameters *codecpar = NULL; if(s) codecpar = s->codecpar; dec_ctx_ = avcodec_alloc_context3(dec); if (!dec_ctx_){ logIt("avcodec_alloc_context3 error"); return false; } int ret = 0; if(s && codecpar) { 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 (vs_idx_ < 0 || !ctx_ || ctx_->nb_streams == 0 || !ctx_->streams) return NULL; 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); if (flag < 0) logIt("======>> av_read_frame error %s", getAVErrorDesc(flag).c_str()); return flag; } int FormatIn::decode(AVFrame* frame, AVPacket *pkt){ AVStream *in = getStream(); if (in){ av_packet_rescale_ts(pkt, in->time_base, in->codec->time_base); } int ret = avcodec_send_packet(dec_ctx_, pkt); if(ret < 0){ logIt("pkt size %d avcodec_send_packet error : %s", pkt->size, 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; } ////////////////////////////////////////////////////////////////////// constexpr int bs = 8192; FormatInGB::FormatInGB() :FormatIn() ,gb28181_(NULL) ,parser_ctx_(NULL) ,buffer_(NULL) ,buffer_size_(bs) { buffer_ = (unsigned char*)malloc(buffer_size_); } FormatInGB::FormatInGB(const VideoProp &prop) :FormatInGB() { prop_ = new VideoProp; *prop_ = prop; } FormatInGB::~FormatInGB(){ if (parser_ctx_){ av_parser_close(parser_ctx_); } if (gb28181_)delete gb28181_; if (buffer_) free(buffer_); for(auto &pkt : q_pkt_){ av_packet_free(&pkt); } q_pkt_.clear(); } static enum AVCodecID codecMap(const int c){ switch (c) { case E_VIDEO_STREAM_H264:// = 0, return AV_CODEC_ID_H264; case E_VIDEO_STREAM_MPEG2:// = 1, // MPEG4 // return AV_CODEC_ID_MPEG2VIDEO; case E_VIDEO_STREAM_MPEG4:// = 2, // MPEG4 return AV_CODEC_ID_MPEG4; case E_VIDEO_STREAM_SVAC:// = 3, // SVAC return AV_CODEC_ID_NONE; case E_VIDEO_STREAM_3GP:// = 4, // 3GP return AV_CODEC_ID_NONE; // audio case E_VIDEO_STREAM_H265:// = 5, //H265 return AV_CODEC_ID_HEVC; default: break; } return AV_CODEC_ID_NONE; } int FormatInGB::open(const char *filename, AVDictionary **options){ if (gb28181_){ delete gb28181_; } gb28181_ = new GB28181API; std::string fn = filename; if(gb28181_->addCamera(fn) < 0){ delete gb28181_; gb28181_ = NULL; logIt("do addCamera Error\n"); return -1; } for(int i = 0; i < 6; i++){ if (gb28181_->getDataType() >= 0){ AVCodecID id = codecMap(gb28181_->getDataType()); logIt("======>>codec name %s\n", avcodec_get_name(id)); parser_ctx_ = av_parser_init(id); if (parser_ctx_) parser_ctx_->flags |= PARSER_FLAG_USE_CODEC_TS; AVCodec* dec = avcodec_find_decoder(id); allocCodec(dec, NULL, NULL); break; } usleep(1000000); } return 0; } int FormatInGB::readPacket(AVPacket *pkt_out){ if (!q_pkt_.empty()){ auto pkt = q_pkt_.front(); q_pkt_.pop_front(); av_packet_ref(pkt_out, pkt); av_packet_free(&pkt); return 0; } if (gb28181_->getDataType() < 0){ logIt("======>> readPacket can't recv gb28181 stream"); return 1; } if (!parser_ctx_){ AVCodecID id = codecMap(gb28181_->getDataType()); parser_ctx_ = av_parser_init(id); AVCodec* dec = avcodec_find_decoder(id); allocCodec(dec, NULL, NULL); } if (parser_ctx_ && dec_ctx_){ int try_run = 0; AVPacket* pkt = av_packet_alloc(); bool got_pkt = false; while (true) { int data_size = gb28181_->readData(gb28181_, buffer_, buffer_size_); // printf("======>> data_size %d pos %d\n", data_size, buffer_pos_); if (data_size == 0){ try_run ++; if (try_run > 12){ av_packet_free(&pkt); logIt("gb28181_ readData %d failed, return -1", try_run); return -1; } continue; } try_run = 0; unsigned char* data = buffer_; while (data_size > 0) { int ret = av_parser_parse2(parser_ctx_, dec_ctx_, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); // logIt("======>> av_parser_parse2 ret %d pkt size %d", ret, pkt->size); if (ret < 0) { av_packet_free(&pkt); logIt("======>> av_parser_parse2 error %d %s", ret, getAVErrorDesc(ret).c_str()); return ret; } data += ret; data_size -= ret; if (pkt->size){ if(fps_ == 0 && dec_ctx_->framerate.den > 0 && dec_ctx_->framerate.num > 0){ fps_ = dec_ctx_->framerate.num/dec_ctx_->framerate.den; if (fps_ == 0) fps_ = 24; } if (parser_ctx_->key_frame == 1){ pkt->flags |= AV_PKT_FLAG_KEY; } got_pkt = true; AVPacket* tmpkt = av_packet_alloc(); av_packet_ref(tmpkt, pkt); q_pkt_.push_back(tmpkt); } } if (got_pkt) { av_packet_free(&pkt); auto tmpkt = q_pkt_.front(); q_pkt_.pop_front(); av_packet_ref(pkt_out, tmpkt); av_packet_free(&tmpkt); return 0; } } } return -1; } const bool FormatInGB::IsHEVC()const{ if (!gb28181_) return false; return codecMap(gb28181_->getDataType()) == AV_CODEC_ID_HEVC; } const bool FormatInGB::IsAVC1()const{ return false; } bool FormatInGB::isVideoPkt(AVPacket *pkt) { return true; } bool FormatInGB::isAudioPkt(AVPacket *pkt) { return false; } }