performance.h

#ifndef PERFORMANCE_TOOL
#define PERFORMANCE_TOOL

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <thread>
#include <memory>
#include <time.h>
#include <signal.h>
#include <mutex>
#include <condition_variable>
#include <list>
#include <utility>


#ifndef DEBUG_PERFTOOL
#define DEBUG_PERFTOOL
#endif

#define THREAD_WORK



//------------ MyTimer start -----------
#ifndef MYTIMER
#define MYTIMER
template <typename T>
class MyTimer
{
public:
typedef void (*TimerHandler)(union sigval);
public:
MyTimer();
~MyTimer();
void Init(bool triggeronstart,long long interval,TimerHandler routine,T* routineArgs,std::string desc);
void Delete();

public:
std::string m_desc;

private:
TimerHandler m_routine;
bool m_triggerOnStart = false;
long long m_interval; //ms
timer_t m_timerid;
T* m_routineArgs;
};

template <typename T>
MyTimer<T>::MyTimer()
{

}

template <typename T>
MyTimer<T>::~MyTimer()
{

}

template <typename T>
void MyTimer<T>::Init(bool triggeronstart,long long interval,TimerHandler routine,T* routineArgs,std::string desc)
{
m_triggerOnStart = triggeronstart;
m_interval = interval;
m_routine = routine;
m_routineArgs = routineArgs;
m_desc = desc;

int ret = 0;
struct sigevent sev;
memset(&sev, 0, sizeof(struct sigevent));
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = m_routine;
sev.sigev_value.sival_ptr = m_routineArgs;
ret = timer_create(CLOCK_REALTIME,&sev,&m_timerid);
if(0!=ret){
return;
}

struct itimerspec its;
its.it_interval.tv_sec = m_interval/1000;
its.it_interval.tv_nsec = (m_interval%1000)*1000000;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 1;

ret = timer_settime(m_timerid, 0, &its, NULL);
if(0!=ret){
return;
}
}

template <typename T>
void MyTimer<T>::Delete()
{
timer_delete(m_timerid);
}
#endif
//------------ MyTimer end -----------


//EsFile Dump Usage (not in use)
class EsDump{
public:
EsDump(const std::string filepath);
~EsDump();
int64_t dumpVideo(uint8_t * data,int64_t len,int pts);
private:
FILE* m_fp = NULL;
int64_t m_cnt=0;
};


//EsFile Parse Usage
class EsFileRead{
public:
EsFileRead()=default;
~EsFileRead();
bool openFile(const std::string filepath);
void closeFile();
bool readOneFrame(int64_t* offset1,int64_t* offset2);
void getData(int64_t offset1,int64_t offset2,unsigned char *dest,int64_t* size);
unsigned char* peekData(int64_t offset)const;
bool isEnd(){
return m_end;
}

private:
int64_t findTag();

private:
int m_fd=-1; //esfile fd
unsigned char* m_data; //esfile data start addr
struct stat m_fileinfo; //esfile info, eg. file size
int64_t m_offset=0; //current offset
int64_t m_idroffset=-1; //idr offset
bool m_findingEndTag=false; //if is finding frame end
bool m_end=false;//if reach file end
};


class EsFilePlayer
{
public:
enum CODEC_TYPE{
H264,
CNT
};

public:
EsFilePlayer();
~EsFilePlayer();

void Open(const std::string filepath,const CODEC_TYPE type,
const int width,const int height,const int fps,void *wnd); //open and set up espp
void Close(); //close espp
void Start(); //start render
void Stop(); //stop render

private:
static void workloop(void* param);
static void timeout(union sigval param);

private:
CODEC_TYPE m_codec; //which codec to use
int m_fps; //fps of input file
std::thread m_workloop; //prepare timer && supply thread for timeout callback func
EsFileRead m_efr;
bool m_needstop=false;
MyTimer<EsFilePlayer> m_timer;
std::mutex m_mut;
std::condition_variable m_cond;
std::list<std::pair<int64_t,int64_t> > m_frameaddr;
};

#endif

performance.cpp

#include "performance.h"
#include "log.hpp"

//------------ EsDump start ------------
EsDump::EsDump(const std::string filepath)
{
m_fp = fopen(filepath.c_str(),"w+");
if(NULL!=m_fp){
setvbuf(m_fp,NULL,_IONBF,0);
}
}

EsDump::~EsDump()
{
if(NULL!=m_fp){
fclose(m_fp);
m_fp=NULL;
}
}

int64_t EsDump::dumpVideo(uint8_t * data,int64_t len,int pts)
{
if(NULL!=m_fp){
int64_t nbytes = fwrite(data,1,len,m_fp);
m_cnt+=nbytes;
return nbytes;
}else{
return -1;
}
}
//------------ EsDump end ------------



//------------ EsFileRead start ------------
EsFileRead::~EsFileRead()
{
closeFile();
}

bool EsFileRead::openFile(const std::string filepath)
{
if(-1 != m_fd){
return true;
}else{
m_fd = open(filepath.c_str(),O_RDONLY);
if(-1==m_fd){
LOG(ERROR) << "file open error";
return false;
}
if(-1==fstat(m_fd,&m_fileinfo)){
LOG(ERROR) << "get file size error";
return false;
}
m_data = (unsigned char*)mmap(NULL,m_fileinfo.st_size,PROT_READ,MAP_PRIVATE,m_fd,0);
if(MAP_FAILED == m_data){
LOG(ERROR) << "map file error";
return false;
}
LOG(INFO) << "file size : "<<m_fileinfo.st_size;
return true;
}
}

void EsFileRead::closeFile()
{
if(NULL!=m_data){
munmap(m_data,m_fileinfo.st_size);
}

if(-1!=m_fd){
close(m_fd);
}

m_fd=-1;
m_data=NULL;
memset(&m_fileinfo,0,sizeof(m_fileinfo));
m_offset=0;
m_idroffset=-1;
m_findingEndTag=false;
m_end=true;
}

int64_t EsFileRead::findTag()
{
if(m_offset <= 0){
m_offset=0;
}

if(m_offset >= m_fileinfo.st_size-1){
m_end = true;
m_offset = m_fileinfo.st_size;
return m_offset;
}

//startcg_debug_log("-----> %02x,%02x,%02x,%02x,%02x",m_data[m_offset],m_data[m_offset+1],m_data[m_offset+2],m_data[m_offset+3],m_data[m_offset+4]);


if(m_data[m_offset] != 0x00 || m_data[m_offset+1]!=0x00 || m_data[m_offset+2] != 0x00 || m_data[m_offset+3] != 0x01){ //not found,steop 1 byte
m_offset++;
findTag();
}else{ //if 00 00 00 01, found nalu
//start of frame
if(!m_findingEndTag && (m_data[m_offset+4] == 0x67 || m_data[m_offset+4] == 0x68 || m_data[m_offset+4] == 0x65)){ //found , idr frame
//only find start can enter , found 65 67 or 68
if(m_idroffset==-1){
//record first position of idr frame
m_idroffset = m_offset;
}
m_offset++;
findTag();;
}
}

return m_offset;
}

void EsFileRead::getData(int64_t offset1,int64_t offset2,unsigned char *dest,int64_t* size)
{
int64_t _size = offset2 - offset1;
*size = _size;
memcpy(dest,&m_data[offset1],_size);
}

unsigned char* EsFileRead::peekData(int64_t offset)const
{
return &m_data[offset];
}

bool EsFileRead::readOneFrame(int64_t* offset1,int64_t* offset2)
{
long offset_1 = 0;
long offset_2 = 0;

//find start tag
m_findingEndTag=false;
offset_1 = findTag();

//find end tag
if(-1!=m_idroffset){ //if idr frame , use m_offset as end tag
*offset1 = m_idroffset;
*offset2 = offset_1 - 1; //position of findTag() return is the first 00 of 00 00 00 01, so minus 1 is the end of frame
}else{ //if not idr frame , find next
m_findingEndTag=true;
m_offset++;
offset_2 = findTag();
*offset1 = offset_1;
*offset2 = offset_2 - 1;
}

m_idroffset=-1;

return true;
}

//------------ EsFileRead end ------------


//------------ EsFilePlayer start ------------

EsFilePlayer::EsFilePlayer(){

}

EsFilePlayer::~EsFilePlayer(){

}

void EsFilePlayer::Open(const std::string filepath,const CODEC_TYPE type,
const int width,const int height,const int fps,void *wnd)
{
bool bret=false;
//1.set up esfilereader
bret = m_efr.openFile(filepath);
if(!bret){
return;
}
LOG(INFO) << "file opened";
m_fps = fps;
}

void EsFilePlayer::Close()
{

}

void EsFilePlayer::Start()
{

#ifdef THREAD_WORK
//create thread
std::thread tmp(workloop,this);
m_workloop = std::move(tmp);
m_workloop.detach();

#else
//parse file
int64_t offset1,offset2;
while(!m_efr.isEnd()){
offset1=0;
offset2=0;
pins->m_efr.readOneFrame(&offset1,&offset2);
//LOG(INFO) << "start :"<<offset1<<" end:"<<offset2;
std::pair<int64_t,int64_t> tmp(offset1,offset2);
pins->m_frameaddr.push_back(tmp);
}

//dump file (for test)
#ifdef DEBUG_PERFTOOL
std::list<std::pair<int64_t,int64_t> >::iterator it;
for(it=m_frameaddr.begin();it!=m_frameaddr.end();it++){
int64_t off1,off2;
std::pair<int64_t,int64_t> p = *it;
off1=p.first;
off2=p.second;
}
#endif
*/


//start timer
int interval = (int)(1000/m_fps);
m_timer.Init(true,interval,timeout,this," 1000/fps ms send one frame");
#endif

return;
}

void EsFilePlayer::Stop()
{
//stop timer
m_timer.Delete();

//stop thread
std::unique_lock<std::mutex> lg(m_mut);
m_needstop=true;
m_cond.notify_all();

return;
}

void EsFilePlayer::workloop(void* param)
{
EsFilePlayer* pins = (EsFilePlayer*)param;
if(!pins){
return;
}

//wait for end thread condition (not in use)
//std::unique_lock<std::mutex> lg(pins->m_mut);
//pins->m_cond.wait(lg,[&]{return pins->m_needstop;});

//start parse esfile (parse in workloop thread cause readoneFrame work slowly,why?)
LOG(INFO) << "parse file start";
int64_t offset1,offset2;
while(!pins->m_efr.isEnd()){
offset1=0;
offset2=0;
pins->m_efr.readOneFrame(&offset1,&offset2);
std::pair<int64_t,int64_t> tmp(offset1,offset2);
pins->m_frameaddr.push_back(tmp);
}
LOG(INFO) << "parse file end";

#ifdef DEBUG_PERFTOOL
std::list<std::pair<int64_t,int64_t> >::iterator it;
for(it=pins->m_frameaddr.begin();it!=pins->m_frameaddr.end();it++){
int64_t off1,off2;
std::pair<int64_t,int64_t> p = *it;
off1=p.first;
off2=p.second;
LOG(INFO) << "start :"<<off1<<" end:"<<off2;
}
#endif

//start timer
int interval = (int)(1000/pins->m_fps);
pins->m_timer.Init(true,interval,timeout,pins," 1000/fps ms send one frame");

return;
}

void EsFilePlayer::timeout(union sigval param)
{
EsFilePlayer* pins = (EsFilePlayer*)param.sival_ptr;

return;
}


//------------ EsFilePlayer end ------------



log.hpp

#ifndef LOGER_H
#define LOGER_H

#ifndef GLOG_NO_ABBREVIATED_SEVERITIES
#define GLOG_NO_ABBREVIATED_SEVERITIES
#endif

#include "glog/logging.h"
#include "glog/raw_logging.h"
#include "glog/log_severity.h"
#include <iostream>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif

#ifdef WIN32
#define GLOG_FOLDER ".//LOG"
#define GLOG_INFO_PATH ".//LOG//info" //directory of INFO level log
#define GLOG_WARN_PATH ".//LOG//warn" //directory of WARNING level log
#define GLOG_ERROR_PATH ".//LOG//error" //directory of ERROR level log
#define GLOG_FATAL_PATH ".//LOG//fatal" //directory of FATAL level log
#else
#define GLOG_FOLDER "./LOG"
#define GLOG_INFO_PATH "./LOG/info" //directory of INFO level log
#define GLOG_WARN_PATH "./LOG/warn" //directory of WARNING level log
#define GLOG_ERROR_PATH "./LOG/error" //directory of ERROR level log
#define GLOG_FATAL_PATH "./LOG/fatal" //directory of FATAL level log
#endif


class Gloger
{
public:
Gloger();
Gloger(const char* program){
google::InitGoogleLogging(program);
createDirect(GLOG_FOLDER);

google::SetLogDestination(google::GLOG_INFO, GLOG_INFO_PATH);
google::SetLogDestination(google::GLOG_WARNING, GLOG_WARN_PATH);
google::SetLogDestination(google::GLOG_ERROR, GLOG_ERROR_PATH);
google::SetLogDestination(google::GLOG_FATAL, GLOG_FATAL_PATH);

FLAGS_logbufsecs = 0;
FLAGS_max_log_size = 100;
FLAGS_stop_logging_if_full_disk = true;
}
~Gloger(void){
google::ShutdownGoogleLogging();
}


int createDirect(const char * dirName)
{
std::fstream _file;
_file.open(dirName,std::ios::in);

if(!_file){
#ifdef WIN32
return _mkdir(dirName);
#else
return mkdir(dirName,S_IRWXU);
#endif
}
else{
return 0;
}
}
};

//usage
//Gloger* logger = new Gloger("appname");

#endif // LOGER_H

main.cpp

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <thread>
#include "performance.h"
#include "log.hpp"

int main(int argc, char ** argv)
{
Gloger loger(argv[0]);
EsFilePlayer m_efp;

m_efp.Open("/home/ubuntu/DumpVideo.es",EsFilePlayer::H264,1920,1080,60,NULL);
m_efp.Start();

while(1);
}

pro.pro

TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt

HEADERS += performance.h \
log.hpp

SOURCES += main.cpp \
performance.cpp

LIBS += -lpthread -lglog -lrt

HEADERS += \
performance.h