| | |
| | | ,out_(NULL) |
| | | ,maxduration(30 * 25) |
| | | ,minduration(10 * 25) |
| | | ,end_frame(minduration) |
| | | ,cur_frame(0) |
| | | ,fp_(NULL) |
| | | ,stop_recorder_(false) |
| | | ,id_(id) |
| | | ,id_frame_(-1) |
| | | ,id_frame_in_file_(-1) |
| | | ,id_frame_(0) |
| | | ,id_frame_in_file_(0) |
| | | ,file_path_("") |
| | | ,func_rec_info_(nullptr) |
| | | ,thrd_(nullptr) |
| | | ,error_occured_(false) |
| | | ,audio_(false) |
| | | ,cur_frame_a(0) |
| | | ,fp_(NULL) |
| | | ,end_frame_(0) |
| | | ,v_cur_frame_(0) |
| | | ,a_cur_frame_(0) |
| | | ,error_occured_(false) |
| | | ,last_rec_id_(-1) |
| | | { |
| | | if (in){ |
| | | maxduration = 30 * in->getFPS(); |
| | |
| | | } |
| | | bool ret = out_->JustWriter(v, a, file_path_.c_str()); |
| | | if (ret){ |
| | | logIt("start record file: %s", file_path_.c_str()); |
| | | logIt("start record h264 file: %s", file_path_.c_str()); |
| | | return 0; |
| | | }else{ |
| | | file_path_ = "./" + filename; |
| | | ret = out_->JustWriter(v, a, file_path_.c_str()); |
| | | logIt("failed in dir %s, try file %s to start record file", dir_.c_str(), file_path_.c_str()); |
| | | if (ret){ |
| | | logIt("start record file: %s", file_path_.c_str()); |
| | | logIt("start record h264 file: %s", file_path_.c_str()); |
| | | return 0; |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | int pid = getpid(); |
| | | std::string filename(sole::uuid4().base62() + "-" + std::to_string(pid) + ".mp4"); |
| | | std::string filename(sole::uuid4().base62() + "-" + std::to_string(pid) + ".hevc"); |
| | | file_path_ = dir_ + "/" + filename; |
| | | |
| | | fp_ = fopen(file_path_.c_str(), "wb"); |
| | |
| | | return -1; |
| | | } |
| | | } |
| | | logIt("start record file: %s", file_path_.c_str()); |
| | | logIt("start record hevc file: %s", file_path_.c_str()); |
| | | |
| | | return 0; |
| | | } |
| | |
| | | //////////////////////// |
| | | int Recorder::write_h264(const CPacket &pkt){ |
| | | //reader failed, break stream |
| | | if(pkt.id == -1 && !pkt.data){ |
| | | if(!pkt.data){ |
| | | return -1; |
| | | } |
| | | |
| | | if (cur_frame == end_frame){ |
| | | if (v_cur_frame_ == end_frame_){ |
| | | return 1; |
| | | } |
| | | |
| | | AVPacket &op = pkt.data->getAVPacket(); |
| | | |
| | | if (!audio_ && in_->isAudioPkt(&op)) { |
| | | return 0; |
| | | } |
| | | |
| | | AVPacket np(op); |
| | | av_copy_packet(&np, &op); |
| | | |
| | | int64_t cur = cur_frame; |
| | | int64_t cur = v_cur_frame_; |
| | | if (in_->isVideoPkt(&np)){ |
| | | |
| | | if(pkt.id == id_frame_){ |
| | | id_frame_in_file_ = cur_frame; |
| | | if(pkt.v_id == id_frame_){ |
| | | id_frame_in_file_ = v_cur_frame_; |
| | | } |
| | | cur_frame++; |
| | | v_cur_frame_++; |
| | | |
| | | }else if (in_->isAudioPkt(&np)) { |
| | | cur = cur_frame_a++; |
| | | cur = a_cur_frame_++; |
| | | } |
| | | |
| | | auto ret = out_->writeFrame(&np, cur); |
| | |
| | | logIt("write hevc packet error, file not open"); |
| | | return -1; |
| | | } |
| | | if (cur_frame == end_frame){ |
| | | if (v_cur_frame_ == end_frame_){ |
| | | return 1; |
| | | } |
| | | |
| | | AVPacket &op = pkt.data->getAVPacket(); |
| | | int64_t cur = cur_frame; |
| | | |
| | | if (in_->isAudioPkt(&op)) { |
| | | return 0; |
| | | } |
| | | if (op.data == NULL){ |
| | | logIt("hevc avpacket data null"); |
| | | return 0; |
| | | } |
| | | |
| | | if (in_->isVideoPkt(&op)){ |
| | | |
| | | if(pkt.id == id_frame_){ |
| | | id_frame_in_file_ = cur_frame; |
| | | if(pkt.v_id == id_frame_){ |
| | | id_frame_in_file_ = v_cur_frame_; |
| | | } |
| | | cur_frame++; |
| | | v_cur_frame_++; |
| | | } |
| | | |
| | | fwrite(op.data, op.size, 1, fp_); |
| | | logIt("hevc write data len: %d frame id: %d key %d", |
| | | op.size, v_cur_frame_, op.flags & AV_PKT_FLAG_KEY); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | int Recorder::mux_hevc(FILE *fp, const char *outfile){ |
| | | std::unique_ptr<FormatIn> in(new FormatIn(false)); |
| | | |
| | | if (!fp) { |
| | | logIt("mux hevc file handle is null"); |
| | | return -1; |
| | | } |
| | | |
| | | std::unique_ptr<FormatIn> in(nullptr); |
| | | int tryTime = 0; |
| | | while (in->openWithCustomIO(fp, read_buffer) < 0) { |
| | | usleep(10000); |
| | | if (tryTime++ < 100){ |
| | | logIt("mux hevc mux: %d failed open custom io %s, try again", tryTime, outfile); |
| | | continue; |
| | | |
| | | while(true){ |
| | | std::unique_ptr<FormatIn> tmp(new FormatIn(false)); |
| | | auto ret = tmp->openWithCustomIO(fp, read_buffer); |
| | | if (ret == 0){ |
| | | in = std::move(tmp); |
| | | break; |
| | | } |
| | | logIt("mux hevc try %d time to open custom io, failed", tryTime); |
| | | usleep(10000); |
| | | if (tryTime++ > 100){ |
| | | logIt("mux hevc try %d time to open custom io %s, failed", tryTime, outfile); |
| | | return -2; |
| | | } |
| | | } |
| | | if (in->open(NULL, NULL) < 0){ |
| | | logIt("mux hevc open stream error"); |
| | |
| | | end_write_h264(); |
| | | } |
| | | |
| | | logIt("finished record : %s frames: %d", file_path_.c_str(), cur_frame); |
| | | logIt("finished record : %s frames: %d, frame in file id: %d", |
| | | file_path_.c_str(), end_frame_, id_frame_in_file_); |
| | | { |
| | | std::lock_guard<std::mutex> l(mutex_pkt_); |
| | | list_pkt_.clear(); |
| | | } |
| | | // logIt("INDEX %d, REAL-FRAME-ID %d, FILE %s, CURFrame %d, ENDFrame %d\n", |
| | | // id_frame_in_file_, id_frame_, file_path_.c_str(), cur_frame, end_frame); |
| | | |
| | | if(func_rec_info_){ |
| | | func_rec_info_(id_,id_frame_in_file_, file_path_); |
| | |
| | | std::list<CPacket> pkts; |
| | | { |
| | | std::unique_lock<std::mutex> locker(mutex_pkt_); |
| | | auto status = cv_.wait_for(locker, std::chrono::seconds(3), [&]{ |
| | | int sec = minduration/50; |
| | | if (in_) sec = minduration/in_->getFPS()/2; |
| | | auto status = cv_.wait_for(locker, std::chrono::seconds(sec), [&]{ |
| | | return !list_pkt_.empty() || stop_recorder_.load(); |
| | | }); |
| | | |
| | |
| | | error_occured_ = !status; |
| | | break; |
| | | } |
| | | |
| | | list_pkt_.swap(pkts); |
| | | } |
| | | |
| | |
| | | |
| | | stop_recorder_.store(true); |
| | | end_writer(); |
| | | list_pkt_.clear(); |
| | | } |
| | | |
| | | int Recorder::Run(const char* output, const int mind, const int maxd, const bool audio){ |
| | |
| | | if(fps > 1.0){ |
| | | maxduration = fps * maxd; |
| | | minduration = fps * mind; |
| | | end_frame = minduration; |
| | | end_frame_ = minduration; |
| | | } |
| | | |
| | | audio_ = a; |
| | | |
| | | logIt("minduration %d maxduration %d curduration %d", minduration, maxduration, end_frame); |
| | | logIt("minduration %d maxduration %d", minduration, maxduration); |
| | | |
| | | thrd_.reset(new std::thread([&]{ |
| | | run_thread(); |
| | |
| | | int Recorder::FireRecorder(const int64_t &id){ |
| | | if (stop_recorder_.load()) return -1; |
| | | |
| | | if(id_frame_ == -1){ |
| | | if(id_frame_ == 0){ |
| | | id_frame_ = id; |
| | | |
| | | std::lock_guard<std::mutex> locker(mutex_pkt_); |
| | | if (list_pkt_.size() > end_frame){ |
| | | end_frame = list_pkt_.size() + minduration/2; |
| | | if (end_frame > maxduration) |
| | | end_frame = maxduration; |
| | | if (list_pkt_.size() > end_frame_){ |
| | | end_frame_ = list_pkt_.size() + minduration/2; |
| | | if (end_frame_ > maxduration) |
| | | end_frame_ = maxduration; |
| | | } |
| | | |
| | | // logIt("FIRST FIRE RECORD ID: %lld, cur_frame: %d, end_frame: %d", id, cur_frame, end_frame); |
| | | }else if(v_cur_frame_ > minduration/2 && end_frame_ < maxduration){ |
| | | logIt("cur frame: %d, end frame: %d, duration: [%d-%d]", |
| | | v_cur_frame_, end_frame_, minduration, maxduration); |
| | | |
| | | }else if(cur_frame > minduration/2 && end_frame < maxduration){ |
| | | end_frame = end_frame + minduration / 2; |
| | | if(end_frame > maxduration){ |
| | | end_frame = maxduration; |
| | | end_frame_ = end_frame_ + minduration / 2; |
| | | if(end_frame_ > maxduration){ |
| | | end_frame_ = maxduration; |
| | | } |
| | | // logIt("PROLONG REC, cur_frame: %d, end_frame: %d", cur_frame, end_frame); |
| | | } |
| | | // logIt("FIRE REC FRAME ID: %lld", id); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int Recorder::PushPacket(const CPacket &pkt){ |
| | | int Recorder::PushPacket(std::list<CPacket> &lst){ |
| | | if (stop_recorder_.load()) return 0; |
| | | |
| | | std::lock_guard<std::mutex> locker(mutex_pkt_); |
| | | |
| | | if(id_frame_ == -1){ |
| | | //wait I |
| | | if (!audio_ && in_->isAudioPkt(&pkt.data->getAVPacket())){ |
| | | return 0; |
| | | // 没有开始录制 |
| | | if (last_rec_id_ < 0){ |
| | | logIt("last rec id is 0 cache size: %ld", lst.size()); |
| | | for (auto &i : lst){ |
| | | // 从第一个非音频关键帧开始 |
| | | if (last_rec_id_ < 0){ |
| | | if (!in_->isVideoPkt(&i.data->getAVPacket())){ |
| | | continue; |
| | | } |
| | | |
| | | maybe_dump_gop(); |
| | | |
| | | list_pkt_.push_back(pkt); |
| | | // cv_.notify_one(); |
| | | |
| | | if (!(i.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ |
| | | continue; |
| | | } |
| | | } |
| | | last_rec_id_ = i.id; |
| | | list_pkt_.push_back(i); |
| | | } |
| | | }else{ |
| | | list_pkt_.push_back(pkt); |
| | | cv_.notify_one(); |
| | | for(auto &i : lst){ |
| | | if (i.id > last_rec_id_){ |
| | | list_pkt_.push_back(i); |
| | | last_rec_id_++; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | cv_.notify_one(); |
| | | |
| | | return list_pkt_.size(); |
| | | } |
| | | |
| | | int Recorder::PushPackets(std::list<CPacket> &lst){ |
| | | int Recorder::StartWritePacket(std::list<CPacket> &lst, const int64_t &id, const int start, const int end){ |
| | | |
| | | if (stop_recorder_.load()) return 0; |
| | | |
| | | std::lock_guard<std::mutex> locker(mutex_pkt_); |
| | | bool i = false; |
| | | for (auto &p : lst){ |
| | | if (!audio_ && in_->isAudioPkt(&p.data->getAVPacket())){ |
| | | continue; |
| | | // 第一次录像,设置触发帧id |
| | | id_frame_ = id; |
| | | |
| | | |
| | | if (start < 0) { |
| | | logIt("start write packet [%d-%d] in pkt size: %d, frame id: %lld, " |
| | | "cur frame: %d, end frame: %d, duration: [%d-%d], last rec id: %lld", |
| | | start, end, lst.size(), id_frame_, |
| | | v_cur_frame_, end_frame_, minduration, maxduration, last_rec_id_); |
| | | return -1; |
| | | } |
| | | |
| | | std::lock_guard<std::mutex> locker(mutex_pkt_); |
| | | // 将传入的所有packets保存如缓存 |
| | | int index = -1; |
| | | for (auto &p : lst){ |
| | | index++; |
| | | if (index < start) continue; |
| | | |
| | | list_pkt_.push_back(p); |
| | | |
| | | if (index == end){ |
| | | last_rec_id_ = p.id; |
| | | break; |
| | | } |
| | | maybe_dump_gop(); |
| | | } |
| | | |
| | | |
| | | logIt("start write packet [%d-%d] in pkt size: %d, frame id: %lld, " |
| | | "cur frame: %d, end frame: %d, duration: [%d-%d], last rec id: %lld", |
| | | start, end, lst.size(), id_frame_, |
| | | v_cur_frame_, end_frame_, minduration, maxduration, last_rec_id_); |
| | | |
| | | |
| | | // maybe_dump_gop(); |
| | | cv_.notify_one(); |
| | | |
| | | // logIt("CACHE PACKET : %d", list_pkt_.size()); |
| | | return list_pkt_.size(); |
| | | } |
| | | |
| | | void Recorder::maybe_dump_gop(){ |
| | | //超过min/2,丢弃gop |
| | | while (list_pkt_.size() > minduration) { |
| | | |
| | | while (list_pkt_.size() > maxduration) { |
| | | list_pkt_.pop_front(); |
| | | while(!list_pkt_.empty()){ |
| | | auto &i = list_pkt_.front(); |
| | |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }// end clase |
| | | }// end namespace |
| | |
| | | |
| | | public: |
| | | int Run(const char* output, const int mind, const int maxd, const bool audio); |
| | | int PushPacket(const CPacket &pkt); |
| | | int PushPackets(std::list<CPacket> &lst); |
| | | int PushPacket(std::list<CPacket> &lst); |
| | | int StartWritePacket(std::list<CPacket> &lst, const int64_t &id, const int start, const int end); |
| | | int FireRecorder(const int64_t &id); |
| | | |
| | | void SetCallback(FUNC_REC_INFO cb){ |
| | |
| | | ffwrapper::FormatIn *in_; |
| | | ffwrapper::FormatOut *out_; |
| | | |
| | | int maxduration; |
| | | int minduration; |
| | | int end_frame; |
| | | int cur_frame; |
| | | int cur_frame_a; |
| | | |
| | | std::list<CPacket> list_pkt_; |
| | | |
| | | std::atomic_bool stop_recorder_; |
| | | std::mutex mutex_pkt_; |
| | | std::condition_variable cv_; |
| | | std::string dir_; |
| | | std::string id_; |
| | | int64_t id_frame_; |
| | | int id_frame_in_file_; |
| | | |
| | | std::string file_path_; |
| | | FUNC_REC_INFO func_rec_info_; |
| | | FILE *fp_; |
| | | bool audio_; |
| | | |
| | | std::unique_ptr<std::thread> thrd_; |
| | | |
| | | std::string dir_; |
| | | std::string id_; |
| | | |
| | | int64_t id_frame_; |
| | | int id_frame_in_file_; |
| | | std::string file_path_; |
| | | FUNC_REC_INFO func_rec_info_; |
| | | int end_frame_; |
| | | int v_cur_frame_; |
| | | int a_cur_frame_; |
| | | |
| | | int64_t last_rec_id_; |
| | | |
| | | int maxduration; |
| | | int minduration; |
| | | |
| | | bool error_occured_; |
| | | |
| | | bool audio_; |
| | | |
| | | FILE *fp_; |
| | | }; |
| | | } |
| | | } |
| | |
| | | class CodedData; |
| | | } |
| | | // 缓存的视频帧 |
| | | typedef struct _cache_pkt{ |
| | | class CPacket{ |
| | | public: |
| | | std::shared_ptr<ffwrapper::CodedData> data; |
| | | int64_t v_id; |
| | | int64_t a_id; |
| | | int64_t id; |
| | | }CPacket; |
| | | }; |
| | | |
| | | #endif |
| | |
| | | } |
| | | ctx_->pb = io_ctx_; |
| | | |
| | | auto err = av_probe_input_buffer(ctx_->pb, &ctx_->iformat, NULL, NULL, 0, read_io_buff_size_); |
| | | 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; |
| | |
| | | int out_idx = -1; |
| | | std::vector<AVStream*> in_streams{in_v_stream_, in_a_stream_}; |
| | | for (auto i : in_streams){ |
| | | if (i->index == pkt->stream_index){ |
| | | if (i && (i->index == pkt->stream_index)){ |
| | | if (i->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){ |
| | | out_idx = v_idx_; |
| | | in_stream = i; |
| | |
| | | #include "../ffmpeg/format/FormatIn.hpp" |
| | | #include "../ffmpeg/data/CodedData.hpp" |
| | | #include "../ffmpeg/log/log.hpp" |
| | | #include "../common.hpp" |
| | | |
| | | extern "C"{ |
| | | #include <libavformat/avformat.h> |
| | |
| | | return 0; |
| | | } |
| | | |
| | | int decoder::saveFrame(AVFrame *frame, int64_t &id){ |
| | | int decoder::saveFrame(AVFrame *frame, const int64_t &id){ |
| | | FRM frm; |
| | | frm.width = frame->width; |
| | | frm.height = frame->height; |
| | |
| | | return list_frm_.size(); |
| | | } |
| | | |
| | | int decoder::SetFrame(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id){ |
| | | int decoder::SetFrame(const CPacket &pkt){ |
| | | auto data = pkt.data; |
| | | |
| | | if (!data) return -10; |
| | | if (!decRef_->isVideoPkt(&data->getAVPacket())) return -20; |
| | |
| | | av_packet_unref(&np); |
| | | |
| | | if (ret == 0){ |
| | | saveFrame(frame, id); |
| | | saveFrame(frame, pkt.v_id); |
| | | } |
| | | av_frame_free(&frame); |
| | | return ret; |
| | |
| | | struct AVFrame; |
| | | struct AVCodecContext; |
| | | |
| | | class CPacket; |
| | | |
| | | namespace ffwrapper |
| | | { |
| | | class FormatIn; |
| | |
| | | |
| | | private: |
| | | int initDecoder(); |
| | | int saveFrame(AVFrame *frame, int64_t &id); |
| | | int saveFrame(AVFrame *frame, const int64_t &id); |
| | | public: |
| | | void Start(); |
| | | int SetFrame(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id); |
| | | int SetFrame(const CPacket &pkt); |
| | | void GetFrame(unsigned char **data, int *w, int *h, int *format, int *length, int64_t *id); |
| | | |
| | | public: |
| | |
| | | using namespace ffwrapper; |
| | | using namespace cffmpeg_wrap::buz; |
| | | |
| | | static const int cache_time = 30 * 60; |
| | | |
| | | namespace cffmpeg_wrap |
| | | { |
| | | rec::rec() |
| | | :recRef_(NULL) |
| | | ,min_cache_len_(10 * 60 * 25) // 最小缓存?分钟的视频,因为整个流程会有延迟,暂定?分钟 |
| | | ,min_cache_len_(cache_time * 25) // 最小缓存?分钟的视频,因为整个流程会有延迟,暂定?分钟 |
| | | {} |
| | | |
| | | rec::~rec() |
| | |
| | | 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"); |
| | |
| | | } |
| | | |
| | | 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(){ |
| | |
| | | // 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()) { |
| | | if (!recRef_->isVideoPkt(&pkt.data->getAVPacket())){ |
| | | return; |
| | | } |
| | | |
| | | if (!(data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ |
| | | if (!(pkt.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ |
| | | return; |
| | | } |
| | | } |
| | | |
| | | list_pkt_.push_back({data, id}); |
| | | list_pkt_.push_back(pkt); |
| | | |
| | | // 超过缓存最大长度,删除一个gop |
| | | shrinkCache(); |
| | |
| | | |
| | | int rec::shrinkCache(){ |
| | | //超过最大缓存,丢弃gop |
| | | //缓存最小长度的,用于记录 |
| | | int fps = 25; |
| | | if (recRef_){ |
| | | fps = recRef_->getFPS(); |
| | | } |
| | | // 最小5秒长度 |
| | | int mincache = fps * 5; |
| | | int md = min_cache_len_ < mincache ? mincache : min_cache_len_; |
| | | while (list_pkt_.size() > md) { |
| | | |
| | | while (list_pkt_.size() > min_cache_len_) { |
| | | list_pkt_.pop_front(); |
| | | while(!list_pkt_.empty()){ |
| | | auto &i = list_pkt_.front(); |
| | | if (!(i.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ |
| | | // 音频丢弃 |
| | | 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; |
| | |
| | | #include "../buz/recorder.hpp" |
| | | |
| | | struct AVPacket; |
| | | class CPacket; |
| | | |
| | | namespace ffwrapper |
| | | { |
| | |
| | | std::mutex mtx_pkt_; |
| | | |
| | | private: |
| | | // 查找缓存中的录制帧 |
| | | void findRecFramesIndex(const int64_t &fired_id, const int duration, int &start, int &end); |
| | | |
| | | // 录像实例的回调函数,录像完成后设置录像文件路径,id和帧id |
| | | void setRecInfo(std::string &id, int &index, std::string &path); |
| | | // 缓存视频包 |
| | | void cachePacket(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id); |
| | | void cachePacket(const CPacket &pkt); |
| | | // 丢弃缓存 |
| | | int shrinkCache(); |
| | | // 创建录像实例开始录像 |
| | |
| | | void Unload(); |
| | | const bool Loaded() const; |
| | | // 缓存录像的视频包,等待触发录像,或直接放到录像缓存 |
| | | void SetPacket(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id); |
| | | void SetPacket(const CPacket &pkt); |
| | | // 触发录像 |
| | | void FireRecSignal(const char* sid,const int64_t &id); |
| | | // 获取录像文件路径和帧id |
| | |
| | | |
| | | #include "../ffmpeg/format/FormatIn.hpp" |
| | | #include "../ffmpeg/data/CodedData.hpp" |
| | | #include "../ffmpeg/log/log.hpp" |
| | | using namespace logif; |
| | | |
| | | namespace cffmpeg_wrap{ |
| | | stream::stream(ffwrapper::FormatIn *in, const int maxSize) |
| | |
| | | list_pkt_.clear(); |
| | | } |
| | | |
| | | int stream::SetPacket(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id){ |
| | | if (data){ |
| | | |
| | | int stream::SetPacket(const CPacket &pkt){ |
| | | if (pkt.data){ |
| | | // 如果包是音频包,但是不使用音频,直接返回 |
| | | if (!audio_ && streamRef_->isAudioPkt(&data->getAVPacket())){ |
| | | if (!audio_ && streamRef_->isAudioPkt(&pkt.data->getAVPacket())){ |
| | | return 0; |
| | | } |
| | | |
| | | std::lock_guard<std::mutex> locker(mutex_avpkt_); |
| | | list_pkt_.push_back({data, id}); |
| | | list_pkt_.push_back(pkt); |
| | | |
| | | while(list_pkt_.size() > max_size_/2*3){ |
| | | list_pkt_.pop_front(); |
| | |
| | | stream(ffwrapper::FormatIn *in, const int maxSize); |
| | | ~stream(); |
| | | |
| | | int SetPacket(std::shared_ptr<ffwrapper::CodedData> data, int64_t &id); |
| | | int SetPacket(const CPacket &pkt); |
| | | void GetPacket(unsigned char **pktData, int *size, int *key); |
| | | void AudioSwitch(const bool a){audio_ = a;} |
| | | }; |
| | |
| | | #include "worker/stream.hpp" |
| | | #include "worker/decoder.hpp" |
| | | #include "worker/rec.hpp" |
| | | #include "common.hpp" |
| | | |
| | | using namespace logif; |
| | | using namespace ffwrapper; |
| | |
| | | } |
| | | } |
| | | |
| | | int Wrapper::run_worker(ffwrapper::FormatIn *in, std::shared_ptr<ffwrapper::CodedData> data, int64_t &id){ |
| | | int Wrapper::run_worker(ffwrapper::FormatIn *in, const CPacket &pkt){ |
| | | if (gb_){ |
| | | AVPacket &pkt = data->getAVPacket(); |
| | | pkt.pts = pkt.dts = AV_NOPTS_VALUE; |
| | | AVPacket &p = pkt.data->getAVPacket(); |
| | | p.pts = p.dts = AV_NOPTS_VALUE; |
| | | } |
| | | int flag = 0; |
| | | if (stream_) stream_->SetPacket(data, id); |
| | | if (decoder_ && run_dec_) flag = decoder_->SetFrame(data, id); |
| | | if (rec_->Loaded()) rec_->SetPacket(data, id); |
| | | if (stream_) stream_->SetPacket(pkt); |
| | | if (decoder_ && run_dec_) flag = decoder_->SetFrame(pkt); |
| | | if (rec_->Loaded()) rec_->SetPacket(pkt); |
| | | |
| | | return flag; |
| | | } |
| | |
| | | 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 ? true : false; |
| | | |
| | |
| | | logIt("read packet error, id: %lld", id); |
| | | break; |
| | | } |
| | | // 非音视频 |
| | | if (in->notVideoAudio(&data->getAVPacket())){ |
| | | continue; |
| | | } |
| | | |
| | | // 非国标跳过第一帧,测试第一帧有问题 |
| | | if (!gb_ && id < 0){ |
| | | id++; |
| | | id++; v_id++; a_id++; |
| | | continue; |
| | | } |
| | | |
| | | CPacket pkt{data, v_id, a_id, id}; |
| | | // decode error |
| | | if (run_worker(in.get(), data, id) == -1){ |
| | | if (run_worker(in.get(), pkt) == -1){ |
| | | break; |
| | | } |
| | | |
| | | if (in->isVideoPkt(&data->getAVPacket())){ |
| | | v_id++; |
| | | }else{ |
| | | a_id++; |
| | | } |
| | | |
| | | id++; |
| | | |
| | | //本地文件太快sleep一下 |
| | | if (exist){ |
| | | usleep(wTime); |
| | | } |
| | | |
| | | id++; |
| | | } |
| | | |
| | | deinit_worker(); |
| | |
| | | #include <memory> |
| | | #include "common/callback.hpp" |
| | | |
| | | |
| | | class CPacket; |
| | | |
| | | namespace ffwrapper{ |
| | | class FormatIn; |
| | |
| | | std::unique_ptr<ffwrapper::FormatIn> init_reader(const char* input); |
| | | |
| | | void init_worker(ffwrapper::FormatIn *in); |
| | | int run_worker(ffwrapper::FormatIn *in, std::shared_ptr<ffwrapper::CodedData> data, int64_t &id); |
| | | int run_worker(ffwrapper::FormatIn *in, const CPacket &pkt); |
| | | void deinit_worker(); |
| | | public: |
| | | int RunStream(const char* input); |