#include "rec.hpp"
|
|
#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"
|
#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<std::mutex> 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<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;
|
}
|
|
std::unique_ptr<Recorder> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> l(mtx_rec_);
|
map_rec_.clear();
|
}
|
|
{
|
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> l(mtx_rec_);
|
for(auto &i : map_rec_){
|
if (i.second){
|
std::lock_guard<std::mutex> pl(mtx_pkt_);
|
i.second->PushPacket(list_pkt_);
|
}
|
}
|
|
}
|
|
void rec::cachePacket(const CPacket &pkt){
|
|
std::lock_guard<std::mutex> 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
|