#include "wrapper.hpp" #include #include #include extern "C"{ #include #include #include } #include "ffmpeg/configure/conf.hpp" #include "ffmpeg/format/FormatIn.hpp" #include "ffmpeg/format/FormatOut.hpp" #include "ffmpeg/data/CodedData.hpp" #include "ffmpeg/property/VideoProp.hpp" #include "ffmpeg/log/log.hpp" #include "ffmpeg/bridge/cvbridge.hpp" #include "buz/recorder.hpp" #include "worker/stream.hpp" #include "worker/decoder.hpp" #include "worker/rec.hpp" #include "common.hpp" using namespace logif; using namespace ffwrapper; #define DELETE_POINTER(p) \ do \ { \ if(NULL != p) \ delete p; \ p = NULL; \ }while(0) namespace cffmpeg_wrap{ using namespace buz; Wrapper::Wrapper() :input_url_("") ,audio_(false) ,gb_(0) ,cpu_(0) ,run_dec_(false) ,run_stream_(true) ,run_rec_(false) ,thread_(nullptr) ,stop_stream_(false) ,stream_(nullptr) ,decoder_(nullptr) ,rec_(new rec) ,logit_(false) ,fps_(25) ,file_finished_(false) ,min_duration_(0) { makeTheWorld(); } Wrapper::Wrapper(const char *logfile) :input_url_("") ,audio_(false) ,gb_(0) ,cpu_(0) ,run_dec_(false) ,run_stream_(true) ,run_rec_(false) ,thread_(nullptr) ,stop_stream_(false) ,stream_(nullptr) ,decoder_(nullptr) ,rec_(new rec) ,logit_(true) ,fps_(25) ,file_finished_(false) ,min_duration_(0) { makeTheWorld(); logif::CreateLogger(logfile, true); } Wrapper::~Wrapper() { try { if(thread_){ stop_stream_.store(true); thread_->join(); } DELETE_POINTER(rec_); } catch(const std::exception& e) { logIt("WRAPPER EXCEPTION: ", e.what()); } if (logit_) logif::DestroyLogger(); } std::unique_ptr Wrapper::init_reader(const char* input){ VideoProp prop; prop.url_ = input; prop.rtsp_tcp_ = true; prop.gpu_acc_ = !cpu_; std::unique_ptr in(new FormatIn(prop.gpuAccl())); int flag = -1; if (gb_){ flag = in->openGb28181(input, NULL); }else{ AVDictionary *avdic = prop.optsFormat(); if(avdic){ flag = in->open(input, &avdic); av_dict_free(&avdic); }else{ flag = in->open(input, NULL); } } if(flag == 0){ if(!in->findStreamInfo(NULL)){ logIt("can't find video stream\n"); return nullptr; } return in; } return nullptr; } int Wrapper::RunStream(const char* input){ if(thread_){ logIt("wrapper run stream already run"); return 0; } input_url_ = input; thread_.reset(new std::thread([&]{ run_stream_thread(); })); return 0; } void Wrapper::AudioSwitch(const bool a){ audio_ = a; // if (stream_){ // stream_->AudioSwitch(a); // } } void Wrapper::init_worker(ffwrapper::FormatIn *in){ if (rec_->Loaded() && stream_ && decoder_) return; stream_ = new stream(in, 3 * in->getFPS()); // stream_->AudioSwitch(audio_); decoder_ = new decoder(in); rec_->Load(in); if(fn_rec_lazy_) { fn_rec_lazy_(); fn_rec_lazy_ = nullptr; } } int Wrapper::run_worker(ffwrapper::FormatIn *in, const CPacket &pkt){ if (gb_){ AVPacket &p = pkt.data->getAVPacket(); p.pts = p.dts = AV_NOPTS_VALUE; } int flag = 0; if (run_stream_ && stream_) stream_->SetPacket(pkt); if (run_dec_ && decoder_) flag = decoder_->SetFrame(pkt); if (run_rec_ && rec_->Loaded()) rec_->SetPacket(pkt); return flag; } void Wrapper::deinit_worker(){ DELETE_POINTER(stream_); DELETE_POINTER(decoder_); rec_->Unload(); } void Wrapper::run_stream_thread(){ int64_t file_rebootime = 0; int64_t file_frame = 0; while(!stop_stream_.load()){ auto in = init_reader(input_url_.c_str()); if (!in) { logIt("ERROR: init_reader! url: %s\n", input_url_.c_str()); sleep(2); continue; } fps_ = in->getFPS(); if (fps_ < 1 || fps_ > 200){ fps_ = 25; } int wTime = 1000000.0 / fps_ ; wTime >>= 1; init_worker(in.get()); int64_t id = gb_ ? 0 : -1; int64_t v_id = id; int64_t a_id = id; bool exist = access(input_url_.c_str(), 0) == 0; if (exist){ wTime <<= 1; file_rebootime++; } logIt("WAIT TIME PER FRAME: %d", wTime); while(!stop_stream_.load()){ auto data(std::make_shared()); if (in->readPacket(&data->getAVPacket()) != 0){ logIt("read packet error, id: %lld", id); break; } // 非音视频 if (in->notVideoAudio(&data->getAVPacket())){ continue; } // 非国标跳过第一帧,测试第一帧有问题 if (!gb_ && id < 0){ id++; v_id++; a_id++; continue; } CPacket pkt{data, v_id, a_id, id}; // decode error if (run_worker(in.get(), pkt) == -1){ break; } if (in->isVideoPkt(&data->getAVPacket())){ v_id++; }else{ a_id++; } id++; //本地文件太快sleep一下 if (exist){ usleep(wTime); // 记录解码的文件有多少帧 file_frame++; } } deinit_worker(); if (exist) { // 三次一帧都没解出来,退出 if (file_frame == 0 && file_rebootime < 3){ continue; } // 最小需要解出多少帧 if (file_frame < min_duration_ * fps_){ continue; } logIt("libcffmpeg.so-> local file %s run %lld time, all frames %lld", input_url_.c_str(), file_rebootime, file_frame); file_finished_ = true; break; } } } void Wrapper::SetRecMinCacheTime(const int mind, const int maxd){ min_duration_ = mind; } void Wrapper::OpenRecorder(){ run_rec_ = true; } void Wrapper::BuildRecorder(const char* id, const char *output, const int64_t &fid, const int mindur, const int maxdur, const bool audio){ bool a = audio; if (gb_) a = false; if (rec_->Loaded()){ rec_->NewRec(id, output, fid, mindur, maxdur, a); }else{ std::string rid(id), dir(output); fn_rec_lazy_ = [=]{rec_->NewRec(rid.c_str(), dir.c_str(), fid, mindur, maxdur, a);}; } } int Wrapper::FireRecorder(const char* sid,const int64_t &id){ if (rec_->Loaded()){ rec_->FireRecSignal(sid, id); } return 0; } int Wrapper::GetInfoRecorder(std::string &recID, int &index, std::string &path){ if (rec_){ rec_->GetRecInfo(recID, index, path); } return 0; } ////////decoder void Wrapper::OpenDecoder(){ run_dec_ = true; } int Wrapper::GetPicDecoder(unsigned char **data, int *w, int *h, int *format, int *length, int64_t *id){ if (file_finished_){ return -1; } if (decoder_){ decoder_->GetFrame(data, w, h, format, length, id); } return 0; } void Wrapper::CloseStream(){ run_stream_ = false; } int Wrapper::GetPacket(unsigned char **pktData, int *size, int *key){ if (stream_){ stream_->GetPacket(pktData, size, key); } return 0; } } // end class wrapper /////////////////////////////////////////////////////////// ///single decode or encoder ////// decoder #include "ffmpeg/data/FrameData.hpp" // return val: -1 open error; -2, find stream error; -3, converter create namespace cffmpeg_wrap{ // start test functions /////// for encoder typedef struct _PicEncoder{ FormatOut *enc; int w; int h; int fps; int br; int gi; int pix_fmt; int flag; cvbridge *bridge; } PicEncoder; void *CreateEncoder(const int w, const int h, const int fps, const int br, const int pix_fmt, const int scale_flag, const int gi){ PicEncoder *e = (PicEncoder*)malloc(sizeof(PicEncoder)); e->enc = NULL; e->w = w; e->h = h; e->fps = fps; e->br = br; e->gi = gi; e->pix_fmt = pix_fmt; e->flag = scale_flag; e->bridge = NULL; VideoProp prop_; prop_.width_ = w; prop_.height_ = h; prop_.fps_ = fps; prop_.bit_rate_ = br; gi < 0 ? prop_.gpu_acc_ = false : prop_.gpu_acc_ = true; FormatOut *enc = new FormatOut(prop_, "./88.mp4"); e->enc = enc; return e; } void DestroyEncoder(void *h){ PicEncoder *e = (PicEncoder*)h; if (e == NULL){ return; } delete e->bridge; delete e->enc; free(e); } int Encode(void *hdl, uint8_t *in, const int w, const int h, uint8_t **out, int *size, int *key){ PicEncoder *e = (PicEncoder*)hdl; auto ctx = e->enc->getCodecContext(); if (e->bridge == NULL){ e->bridge = new cvbridge( w, h, e->pix_fmt, e->w, e->h, ctx->pix_fmt, e->flag); } AVFrame *bgr_frame = cvbridge::fillFrame(in, w, h, e->pix_fmt); AVFrame *frame = e->bridge->convert2Frame(bgr_frame); av_frame_free(&bgr_frame); AVPacket *pkt = av_packet_alloc(); auto flag = e->enc->encode(pkt, frame); if(flag == 0){ int extradata_size = ctx->extradata_size; uint8_t *extra = ctx->extradata; *key = pkt->flags & AV_PKT_FLAG_KEY; if(!(*key)){ extradata_size = 0; } *size = pkt->size + extradata_size; *out = (unsigned char *)malloc(*size); memcpy(*out, extra, extradata_size); memcpy(*out + extradata_size, pkt->data, pkt->size); }else{ logIt("encode error or need more packet\n"); } av_packet_free(&pkt); av_frame_free(&frame); return flag; } int GetGb28181Pic(const char *rtspUrl, char *retData, int *retDataLen){ int ret = 0; std::string fn = rtspUrl; auto handle_gb28181 = new GB28181API; if(handle_gb28181->addCamera(fn) == -1){ delete(handle_gb28181); logIt("do addCamera Error\n"); return -1; } int retLen = handle_gb28181->capturePic(handle_gb28181, retData, retDataLen); if(retLen == 0){ logIt("do capturePic failed:%d"); ret = -1; } handle_gb28181->deleteCamera(); return ret; } }