video analysis2.0拆分,ffmpeg封装go接口库
zhangmeng
2023-12-26 18a05d269516a5e33d8460291c2f93e73d95adce
csrc/ffmpeg/format/FormatIn.cpp
@@ -2,6 +2,8 @@
#include <stdexcept>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
extern "C"{
#include <libavformat/avformat.h>
@@ -10,7 +12,7 @@
#include <libavutil/opt.h>
#include <libavutil/avassert.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libswscale/swscale.h>
}
#include "../log/log.hpp"
@@ -19,7 +21,6 @@
#include "../property/VideoProp.hpp"
#include "../../common/gpu/info.h"
using namespace logif;
namespace ffwrapper{
@@ -28,17 +29,25 @@
   ,dec_ctx_(NULL)
   ,vs_idx_(-1)
   ,as_idx_(-1)
   ,hw_accl_(hw)
   ,prop_(NULL)
   ,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*/)
   :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_);
@@ -49,20 +58,14 @@
         avformat_close_input(&ctx_);
         ctx_ = NULL;
      }
      if (handle_gb28181){
         delete handle_gb28181;
      }
      if(io_ctx_){
         av_freep(&io_ctx_->buffer);
         avio_context_free(&io_ctx_);
         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_){
@@ -75,7 +78,7 @@
         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");
@@ -84,66 +87,19 @@
      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());
      // }
      int ret = avformat_open_input(&ctx_, filename, NULL, options);
      return ret;
   }
   bool FormatIn::findStreamInfo(AVDictionary **options){
      const int ret = avformat_find_stream_info(ctx_, 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 false;
               ctx_->filename,getAVErrorDesc(ret).c_str());
         return ret;
      }
      // logIt("there are %d stream", ctx_->nb_streams);
      for (int i = 0; i < ctx_->nb_streams; ++i)
      {
@@ -158,12 +114,12 @@
                   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);
               }
            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);
            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
@@ -171,89 +127,72 @@
         }
      }
      return true;
      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;
      AVCodec *dec = NULL;
      bool flag = false;
      AVDictionary *avdic = NULL;
      int idle_gpu = -1;
        AVCodecID codec_id = codecpar->codec_id;
      for (int i = 0; i < 2; ++i)
      {
         if(hw_accl_){
                idle_gpu = gpu::getGPUPrior(300, 1024, 0);
                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);
        AVCodec *dec = avcodec_find_decoder(codec_id);
      if(dec){
         flag = allocCodec(dec, s, options);
         if(*options){
            av_dict_free(options);
         }
         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;
            }
         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 = s->codecpar;
      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 = avcodec_parameters_to_context(dec_ctx_, codecpar);
        if(ret < 0){
            logIt("avcodec_parameters_to_context error : %s", getAVErrorDesc(ret).c_str());
            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);
        }
        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);
@@ -266,6 +205,8 @@
   }
   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_];
      }
@@ -301,20 +242,21 @@
   }
   
   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();
      av_packet_rescale_ts(pkt, in->time_base, in->codec->time_base);
      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("avcodec_send_packet error : %s", getAVErrorDesc(ret).c_str());
           logIt("pkt size %d avcodec_send_packet error : %s", pkt->size, getAVErrorDesc(ret).c_str());
           return -1;
        }
        while (ret >= 0) {
@@ -330,4 +272,183 @@
        }
      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;
   }
}