| | |
| | | ,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_->isVideoPkt(&op)){ |
| | | |
| | | if(pkt.id == id_frame_){ |
| | | id_frame_in_file_ = cur_frame; |
| | | } |
| | | 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.v_id == id_frame_){ |
| | | id_frame_in_file_ = v_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); |
| | | return -2; |
| | | 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"); |
| | |
| | | return -5; |
| | | } |
| | | if (out->JustWriter(v, NULL, outfile)){ |
| | | logIt("mux hevc start record file: %s", outfile); |
| | | logIt("mux hevc start record file: %s", outfile); |
| | | } |
| | | |
| | | int64_t id = 0; |
| | |
| | | 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(); |
| | | }); |
| | | |
| | | |
| | | if (!status || 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; |
| | | } |
| | | |
| | | }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); |
| | | |
| | | // logIt("FIRST FIRE RECORD ID: %lld, cur_frame: %d, end_frame: %d", id, cur_frame, end_frame); |
| | | |
| | | }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; |
| | | } |
| | | if (!(i.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ |
| | | continue; |
| | | } |
| | | } |
| | | last_rec_id_ = i.id; |
| | | list_pkt_.push_back(i); |
| | | } |
| | | |
| | | maybe_dump_gop(); |
| | | |
| | | list_pkt_.push_back(pkt); |
| | | // cv_.notify_one(); |
| | | |
| | | }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; |
| | | |
| | | // 第一次录像,设置触发帧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_); |
| | | bool i = false; |
| | | // 将传入的所有packets保存如缓存 |
| | | int index = -1; |
| | | for (auto &p : lst){ |
| | | if (!audio_ && in_->isAudioPkt(&p.data->getAVPacket())){ |
| | | continue; |
| | | } |
| | | 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 |