#include "recorder.hpp" #include "sole.hpp" #include #include #include extern "C"{ #include } #include "../ffmpeg/format/FormatIn.hpp" #include "../ffmpeg/format/FormatOut.hpp" #include "../ffmpeg/data/CodedData.hpp" #include "../ffmpeg/log/log.hpp" using namespace logif; using namespace ffwrapper; namespace cffmpeg_wrap{ namespace buz{ Recorder::Recorder(FormatIn *in, const std::string &id) :in_(in) ,out_(NULL) ,maxduration(30 * 25) ,minduration(10 * 25) ,end_frame(minduration) ,cur_frame(0) ,stop_recorder_(false) ,id_(id) ,id_frame_(-1) ,id_frame_in_file_(-1) ,file_path_("") ,func_rec_info_(nullptr) ,thrd_(nullptr) ,error_occured_(false) ,audio_(false) ,cur_frame_a(0) { // logIt("RECODER ID: %s", id_.c_str()); } Recorder::~Recorder(){ try { if (thrd_){ if (!stop_recorder_.load()){ stop_recorder_.store(true); cv_.notify_one(); } thrd_->join(); // logIt("REC THREAD JOINED, QUIT!!!, %s", id_.c_str()); } } catch(const std::exception& e) { logIt("RECODER DESTRUCTOR EXCEPTION: ", e.what()); } } int Recorder::init_writer(const bool audio){ if (out_) { delete out_; } if(!in_){ logIt("init_writer FormatIn not init"); return -1; } out_ = new FormatOut(in_->getFPS(), "mp4"); int pid = getpid(); file_path_ = dir_ + "/" + sole::uuid4().base62() + "-" + std::to_string(pid) + ".mp4"; auto v = in_->getStream(AVMEDIA_TYPE_VIDEO); if (!v){ return -2; } AVStream *a = in_->getStream(AVMEDIA_TYPE_AUDIO); if (!audio){ a = NULL; } auto ret = out_->JustWriter(v, a, file_path_.c_str()); if (ret){ logIt("start record file: %s", file_path_.c_str()); return 0; } logIt("failed to start record: %s", file_path_.c_str()); return -1; } int Recorder::write_correctly(const CPacket &pkt){ //reader failed, break stream if(pkt.id == -1 && !pkt.data){ return -1; } if (cur_frame == end_frame){ return 1; } AVPacket &op = pkt.data->getAVPacket(); AVPacket np(op); av_copy_packet(&np, &op); int64_t cur = cur_frame; if (in_->isVideoPkt(&np)){ if(pkt.id == id_frame_){ id_frame_in_file_ = cur_frame; } cur_frame++; }else if (in_->isAudioPkt(&np)) { cur = cur_frame_a++; } auto ret = out_->writeFrame(&np, cur); av_packet_unref(&np); if (!ret) return -1; // logIt("WRITE FRAME ID: %d, RECORD ID: %d", pkt.id, id_frame_); return 0; } void Recorder::end_writer(){ out_->endWriter(); if (out_){ delete out_; out_ = NULL; } { std::lock_guard 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); //callback to frame index and path if(func_rec_info_){ func_rec_info_(id_,id_frame_in_file_, file_path_); } } void Recorder::run_thread(){ while(!stop_recorder_.load()){ std::list pkts; { std::unique_lock locker(mutex_pkt_); auto status = cv_.wait_for(locker, std::chrono::seconds(3), [&]{ return !list_pkt_.empty() || stop_recorder_.load(); }); if (!status || stop_recorder_.load()){ error_occured_ = !status; break; } list_pkt_.swap(pkts); } int ret = 0; for(auto &i : pkts){ ret = write_correctly(i); if (ret != 0){ break; } } if (ret != 0){ break; } } stop_recorder_.store(true); end_writer(); } int Recorder::Run(const char* output, const int mind, const int maxd, const bool audio){ dir_ = output; int ret = init_writer(audio); if(ret != 0){ logIt("recorder init writer error"); return -1; } double fps = out_->getFPS(); if(fps > 1.0){ maxduration = fps * maxd; minduration = fps * mind; end_frame = minduration; } audio_ = audio; // logIt("minduration %d maxduration %d curduration %d", minduration, maxduration, end_frame); thrd_.reset(new std::thread([&]{ run_thread(); })); //.detach(); return 0; } int Recorder::FireRecorder(const int64_t &id){ if (stop_recorder_.load()) return -1; if(id_frame_ == -1){ id_frame_ = id; std::lock_guard locker(mutex_pkt_); 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(cur_frame > minduration/2 && 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){ if (stop_recorder_.load()) return 0; std::lock_guard locker(mutex_pkt_); if(id_frame_ == -1){ //wait I if (!audio_ && in_->isAudioPkt(&pkt.data->getAVPacket())){ return 0; } maybe_dump_gop(); list_pkt_.push_back(pkt); // cv_.notify_one(); }else{ list_pkt_.push_back(pkt); cv_.notify_one(); } return list_pkt_.size(); } int Recorder::PushPackets(std::list &lst){ if (stop_recorder_.load()) return 0; std::lock_guard locker(mutex_pkt_); bool i = false; for (auto &p : lst){ if (!audio_ && in_->isAudioPkt(&p.data->getAVPacket())){ continue; } list_pkt_.push_back(p); } 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) { list_pkt_.pop_front(); while(!list_pkt_.empty()){ auto &i = list_pkt_.front(); if (!(i.data->getAVPacket().flags & AV_PKT_FLAG_KEY)){ list_pkt_.pop_front(); }else{ break; } } } } } }