#include "recorder.hpp"
|
|
#include <thread>
|
#include <unistd.h>
|
|
extern "C"{
|
#include <libavcodec/avcodec.h>
|
}
|
|
#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)
|
:in_(in)
|
,out_(NULL)
|
,maxduration(30 * 25)
|
,minduration(10 * 25)
|
,end_frame(minduration)
|
,cur_frame(-1)
|
,thread_(nullptr)
|
,stop_recorder_(false)
|
,id_frame_(0)
|
,file_frame_index_(-1)
|
,file_path_("")
|
,func_rec_info_(nullptr)
|
{}
|
|
Recorder::~Recorder(){
|
if(thread_){
|
stop_recorder_.store(true);
|
cv_.notify_one();
|
thread_->join();
|
}
|
if(out_)
|
delete out_;
|
}
|
|
int Recorder::init_writer(){
|
if (out_) {
|
delete out_;
|
}
|
|
if(!in_){
|
logIt("init_writer FormatIn not init");
|
return -1;
|
}
|
|
out_ = new FormatOut(in_->getStream(), "mp4");
|
|
return 0;
|
}
|
|
void Recorder::start_writer(){
|
if (cur_frame == 0) {
|
file_path_ = dir_ + "/" + std::to_string(random()) + ".mp4";
|
out_->JustWriter(in_->getStream(), file_path_.c_str());
|
logIt("start record %s", file_path_.c_str());
|
}
|
}
|
|
int Recorder::write_correctly(const avpacket &pkt){
|
//reader failed, break stream
|
if(pkt.id == -1 && !pkt.data){
|
end_writer();
|
return 1;
|
}
|
// writer error, reinit writer
|
int64_t cur = cur_frame++;
|
if(!out_->writeFrame(pkt.data->getAVPacket(), cur)){
|
end_writer();
|
return -1;
|
}
|
if(pkt.id == id_frame_){
|
file_frame_index_ = cur_frame;
|
}
|
return 0;
|
}
|
|
void Recorder::end_writer(){
|
if(cur_frame == -1) return;
|
out_->endWriter();
|
//reinit cur_frame clear list pkt
|
{
|
std::lock_guard<std::mutex> locker(mutex_pkt_);
|
cur_frame = -1;
|
end_frame = minduration;
|
list_pkt_.clear();
|
}
|
//callback to frame index and path
|
if(func_rec_info_){
|
func_rec_info_(file_frame_index_, file_path_);
|
}
|
}
|
|
void Recorder::run_thread(){
|
bool reinit_writer = false;
|
while(!stop_recorder_.load()){
|
if (reinit_writer) {
|
while(!stop_recorder_.load()){
|
if(init_writer() == 0)
|
break;
|
usleep(300000);
|
}
|
if(stop_recorder_.load()) break;
|
}
|
|
std::list<avpacket> pkts;
|
{
|
std::unique_lock<std::mutex> locker(mutex_pkt_);
|
cv_.wait(locker,[&]{
|
return !list_pkt_.empty() || stop_recorder_.load();
|
});
|
if(stop_recorder_.load()){
|
end_writer();
|
break;
|
}
|
if(cur_frame == -1){
|
continue;
|
}
|
list_pkt_.swap(pkts);
|
}
|
|
if (cur_frame == 0) {
|
start_writer();
|
}
|
|
for(auto &i : pkts){
|
if (cur_frame < end_frame){
|
const int ret = write_correctly(i);
|
if(ret != 0){
|
if(ret == -1) reinit_writer = true;
|
break;
|
}
|
}else{
|
end_writer();
|
break;
|
}
|
}
|
}
|
}
|
|
int Recorder::Run(const char* output, const int mind, const int maxd){
|
if(thread_){
|
logIt("recorder already run");
|
return 0;
|
}
|
|
dir_ = output;
|
int ret = init_writer();
|
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;
|
}
|
|
logIt("min %d max %d endcount %d", minduration, maxduration, end_frame);
|
|
thread_.reset(new std::thread([&]{
|
run_thread();
|
}));
|
|
return 0;
|
}
|
|
int Recorder::FireRecorder(const int64_t &id){
|
if(cur_frame == -1){
|
id_frame_ = id;
|
{
|
std::lock_guard<std::mutex> locker(mutex_pkt_);
|
cur_frame = 0;
|
}
|
}else if(end_frame - cur_frame > minduration/2 && end_frame < maxduration){
|
end_frame = end_frame + minduration / 2;
|
if(end_frame > maxduration){
|
end_frame = maxduration;
|
}
|
}
|
return 0;
|
}
|
|
int Recorder::CachePacket(const avpacket &pkt){
|
|
std::lock_guard<std::mutex> locker(mutex_pkt_);
|
|
if(cur_frame == -1){
|
//error occur, stream break
|
if(pkt.id == -1 && pkt.data == nullptr){
|
list_pkt_.clear();
|
return -1;
|
}
|
//wait I
|
if (list_pkt_.empty()) {
|
AVPacket &avpkt = pkt.data->getAVPacket();
|
if (!(avpkt.flags & AV_PKT_FLAG_KEY)){
|
return -1;
|
}
|
}
|
|
maybe_dump_gop();
|
|
list_pkt_.push_back(pkt);
|
}else{
|
list_pkt_.push_back(pkt);
|
cv_.notify_one();
|
}
|
|
return 0;
|
}
|
|
void Recorder::maybe_dump_gop(){
|
//超过min/2,丢弃gop
|
while (list_pkt_.size() > minduration /2) {
|
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)){
|
list_pkt_.pop_front();
|
}else{
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|