| | |
| | | #include <unistd.h> |
| | | #include <sys/time.h> |
| | | |
| | | extern "C"{ |
| | | #include <libavcodec/avcodec.h> |
| | | } |
| | | |
| | | #include "../ffmpeg/format/FormatIn.hpp" |
| | | #include "../ffmpeg/data/CodedData.hpp" |
| | | #include "../ffmpeg/log/log.hpp" |
| | |
| | | using namespace ffwrapper; |
| | | using namespace cffmpeg_wrap::buz; |
| | | |
| | | static const int cache_time = 6 * 60; |
| | | |
| | | namespace cffmpeg_wrap |
| | | { |
| | | rec::rec() |
| | | :recRef_(NULL) |
| | | ,minduration_(250) |
| | | ,maxduration_(750) |
| | | ,min_cache_len_(cache_time * 25) // 最小缓存?分钟的视频,因为整个流程会有延迟,暂定?分钟 |
| | | {} |
| | | |
| | | rec::~rec() |
| | |
| | | list_recInfo_.emplace_back(info); |
| | | } |
| | | |
| | | std::unique_ptr<buz::Recorder> rec::startRec(std::string id, std::string dir, const int mind, const int maxd){ |
| | | 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<buz::Recorder> 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; |
| | |
| | | |
| | | int trycnt = 0; |
| | | while(trycnt < 100){ |
| | | auto ret = rec->Run(dir.c_str(), mind, maxd); |
| | | 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<std::mutex> locker(mtx_pkt_); |
| | | rec->PushPackets(list_pkt_); |
| | | logIt("cache size: %ld", list_pkt_.size()); |
| | | // 首次获取录像信息,先存一个最短时长 |
| | | findRecFramesIndex(frameID, duration, start, end); |
| | | rec->StartWritePacket(list_pkt_, frameID, start, end); |
| | | |
| | | return rec; |
| | | } |
| | | |
| | |
| | | |
| | | void rec::Load(ffwrapper::FormatIn *in){ |
| | | recRef_ = in; |
| | | if (in){ |
| | | min_cache_len_ = in->getFPS() * cache_time; |
| | | } |
| | | } |
| | | |
| | | void rec::Unload(){ |
| | |
| | | return recRef_ != NULL; |
| | | } |
| | | |
| | | void rec::NewRec(const char* id, const char *output, const int mindur, const int maxdur){ |
| | | 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); |
| | | |
| | | minduration_ = mindur * 25; |
| | | maxduration_ = maxdur * 25; |
| | | |
| | | { |
| | | std::lock_guard<std::mutex> l(mtx_rec_); |
| | | if (map_rec_.find(rid) != map_rec_.end()){ |
| | | map_rec_.erase(rid); |
| | | } |
| | | map_rec_[rid] = startRec(rid, dir, mindur, maxdur); |
| | | map_rec_[rid] = startRec(rid, dir, frameID, mindur, maxdur, audio); |
| | | } |
| | | |
| | | } |
| | |
| | | // logIt("recorders count: %d", map_rec_.size()); |
| | | } |
| | | |
| | | void rec::SetPacket(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id){ |
| | | if (!data) return; |
| | | void rec::SetPacket(const CPacket &pkt){ |
| | | if (!pkt.data) return; |
| | | cachePacket(pkt); |
| | | |
| | | std::lock_guard<std::mutex> l(mtx_rec_); |
| | | for(auto &i : map_rec_){ |
| | | if (i.second){ |
| | | i.second->PushPacket({data, id}); |
| | | std::lock_guard<std::mutex> pl(mtx_pkt_); |
| | | i.second->PushPacket(list_pkt_); |
| | | } |
| | | } |
| | | |
| | | cachePacket(data, id); |
| | | } |
| | | |
| | | void rec::cachePacket(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id){ |
| | | void rec::cachePacket(const CPacket &pkt){ |
| | | |
| | | std::lock_guard<std::mutex> l(mtx_pkt_); |
| | | //wait I |
| | | if (list_pkt_.empty()) { |
| | | AVPacket &avpkt = data->getAVPacket(); |
| | | if (!(avpkt.flags & AV_PKT_FLAG_KEY)){ |
| | | if (!recRef_->isVideoPkt(&pkt.data->getAVPacket())){ |
| | | return; |
| | | } |
| | | |
| | | if (!(pkt.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ |
| | | return; |
| | | } |
| | | } |
| | | list_pkt_.push_back({data, id}); |
| | | |
| | | 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() > minduration_/2) { |
| | | //超过最大缓存,丢弃gop |
| | | while (list_pkt_.size() > min_cache_len_) { |
| | | list_pkt_.pop_front(); |
| | | while(!list_pkt_.empty()){ |
| | | auto &cache = list_pkt_.front(); |
| | | AVPacket &avpkt = cache.data->getAVPacket(); |
| | | if (!(avpkt.flags & AV_PKT_FLAG_KEY)){ |
| | | 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; |