#include "rec.hpp" #include #include extern "C"{ #include } #include "../ffmpeg/format/FormatIn.hpp" #include "../ffmpeg/data/CodedData.hpp" #include "../ffmpeg/log/log.hpp" #include "../common/callback.hpp" using namespace logif; using namespace ffwrapper; using namespace cffmpeg_wrap::buz; static const int cache_time = 3 * 60 + 30; namespace cffmpeg_wrap { rec::rec() :recRef_(NULL) ,min_cache_len_(cache_time * 25) // 最小缓存?分钟的视频,因为整个流程会有延迟,暂定?分钟 {} rec::~rec() { clear(); } void rec::setRecInfo(std::string &id, int &index, std::string &path){ std::lock_guard l(mtx_recInfo_); while(list_recInfo_.size() > 100){ for(int i = 0; i < 25; i++){ list_recInfo_.pop_front(); } } struct record_file_info info; info.frmIdx = index; info.fPath = path; info.recID = id; list_recInfo_.emplace_back(info); } void rec::findRecFramesIndex(const int64_t &fired_id, const int duration, int &start, int &end){ start = end = -1; if (list_pkt_.empty()){ return; } // 录像开始id在触发id之前1/2时长,保证在中间 int64_t start_id = fired_id - duration/2; // 寻找关键帧作为录像开始id int offset = recRef_ ? recRef_->getFPS() : 25; int64_t index = -1; for(auto &i : list_pkt_){ index++; // 跳过音频 if(!recRef_->isVideoPkt(&i.data->getAVPacket())){ continue; } // 寻找关键帧作为起始 if (start < 0){ if (i.data->getAVPacket().flags & AV_PKT_FLAG_KEY){ // 当前帧id > 开始id或开始id在offset内,作为起始录像帧 if (i.v_id >= start_id || start_id - i.v_id < offset){ start = index; start_id = i.v_id; } } }else if (recRef_->isVideoPkt(&i.data->getAVPacket())){ // 视频帧,看是否缓存中有所有的duration数据 if (i.v_id - start_id == duration){ end = index; } } } if (end < 0) end = index; } std::unique_ptr rec::startRec(std::string id, std::string dir, const int64_t &frameID, const int mind, const int maxd, const bool audio){ if(!recRef_){ logIt("Init wrapper first"); return nullptr; } std::unique_ptr rec(new Recorder(recRef_, id.c_str())); rec->SetCallback([&](std::string &id, int &index, std::string &path){ setRecInfo(id, index, path); }); int trycnt = 0; while(trycnt < 100){ auto ret = rec->Run(dir.c_str(), mind, maxd, audio); if(ret == 0) break; usleep(200000); } if (trycnt < 100){ int duration = mind * recRef_->getFPS(); int start=0, end=0; std::lock_guard locker(mtx_pkt_); logIt("cache size: %ld", list_pkt_.size()); // 首次获取录像信息,先存一个最短时长 findRecFramesIndex(frameID, duration, start, end); rec->StartWritePacket(list_pkt_, frameID, start, end); return rec; } return nullptr; } void rec::GetRecInfo(std::string &recID, int &index, std::string &path){ // 获取信息 { std::lock_guard l(mtx_recInfo_); if(!list_recInfo_.empty()){ auto info = list_recInfo_.front(); recID = info.recID; index = info.frmIdx; path = info.fPath; list_recInfo_.pop_front(); } } // 删除rec实例 { std::lock_guard l(mtx_rec_); if (map_rec_.empty()){ return; } if (map_rec_.find(recID) != map_rec_.end()){ map_rec_.erase(recID); return; } for (auto iter = map_rec_.begin(); iter != map_rec_.end();){ if (iter->second && iter->second->ErrorOcurred()){ recID = iter->first; index = -1; path = ""; iter == map_rec_.erase(iter); break; }else{ iter++; } } } } void rec::clear(){ { std::lock_guard l(mtx_rec_); map_rec_.clear(); } { std::lock_guard l(mtx_pkt_); list_pkt_.clear(); } } void rec::Load(ffwrapper::FormatIn *in){ recRef_ = in; if (in){ min_cache_len_ = in->getFPS() * cache_time; } } void rec::Unload(){ recRef_ = NULL; clear(); } const bool rec::Loaded() const{ return recRef_ != NULL; } void rec::NewRec(const char* id, const char *output, const int64_t &frameID, const int mindur, const int maxdur, const bool audio){ std::string rid(id); std::string dir(output); { std::lock_guard l(mtx_rec_); if (map_rec_.find(rid) != map_rec_.end()){ map_rec_.erase(rid); } map_rec_[rid] = startRec(rid, dir, frameID, mindur, maxdur, audio); } } void rec::FireRecSignal(const char* sid, const int64_t &id){ std::lock_guard l(mtx_rec_); auto iter = map_rec_.find(sid); if (iter != map_rec_.end()){ if(iter->second){ iter->second->FireRecorder(id); } } // logIt("recorders count: %d", map_rec_.size()); } void rec::SetPacket(const CPacket &pkt){ if (!pkt.data) return; cachePacket(pkt); std::lock_guard l(mtx_rec_); for(auto &i : map_rec_){ if (i.second){ std::lock_guard pl(mtx_pkt_); i.second->PushPacket(list_pkt_); } } } void rec::cachePacket(const CPacket &pkt){ std::lock_guard l(mtx_pkt_); //wait I if (list_pkt_.empty()) { if (!recRef_->isVideoPkt(&pkt.data->getAVPacket())){ return; } if (!(pkt.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ return; } } list_pkt_.push_back(pkt); // 超过缓存最大长度,删除一个gop shrinkCache(); } void rec::SetRecMinCacheTime(const int min){ // 由于整个流程耗时,补偿time_offset_ // int fps = 25; // if (recRef_){ // fps = recRef_->getFPS(); // } // if (min_cache_len_ > (cache_time+min) * fps){ // return; // } // min_cache_len_ += min * fps; } int rec::shrinkCache(){ //超过最大缓存,丢弃gop while (list_pkt_.size() > min_cache_len_) { list_pkt_.pop_front(); while(!list_pkt_.empty()){ auto &i = list_pkt_.front(); // 音频丢弃 if (!recRef_->isVideoPkt(&i.data->getAVPacket())){ list_pkt_.pop_front(); }else if (!(i.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ // 非关键帧丢弃 list_pkt_.pop_front(); }else{ break; } } } } } // namespace cffmpeg_wrap