C# · 12月 20, 2021

网络数据包片段拼合(连续h264片段拼接成完整h264帧)(二支持c++调用)

上一篇的改进,支持c++,主要是对 C++ 类成员函数作为外部回调的处理。

关于这个c++ 类成员函数作为回调函数,还是有点意思的,有其他博文介绍的好几种方法,后续再论。

代码:

/***

***20181221 canok

*** brief : Combine data fragments into complete frames

*** input : Continuous data flow,support fix length data input;

*** output: complete frames

**/

#include

#include

#include

#include

#include

#include

typedef unsigned char uint8_t; //无符号8位数

#define ERRO_INFO(fmt,args…) do {printf(“[%s,%s,%d]”,__FILE__,__FUNCTION__,__LINE__);printf(fmt,##args);} while(0)

#define DEBUG(fmt,args…) do {printf(“[%s,##args);} while(0)

typedef int (*FUN_GETINPUTDATA)(void *pclass,uint8_t *buf,int buflen);

typedef int (*FUN_PUTOUTDATA)(void *pclass,int buflen);

typedef int (*FUN_STOPCALLED)(void *pclass,void*pram);

#define DATALINK_IN_BACK(classtype,callfun) static int intput (void* pclass,int buflen)

{

classtype *pCall = (classtype *)pclass;

if(pCall)

{

return pCall->callfun(buf,buflen);

}

}

#define DATALINK_OUT_BACK(classtype,callfun) static int output (void* pclass,buflen);

}

}

#define DATALINK_STOP_BACK(classtype,callfun) static int stop (void* pclass,void *pram)

{

classtype *pCall = (classtype *)pclass;

if(pCall)

{

return pCall->callfun(buf,buflen);

}

}

class CDataLinkup

{

public:

CDataLinkup(int maxframelen,int fixreadlen);

~CDataLinkup();

void setDataInputCallback(void *pclass,FUN_GETINPUTDATA input);

void setDataOutputCallback(void *pclass,FUN_PUTOUTDATA output);

void setDataMark(uint8_t *mark,int marklen);

void start();

void stop();

void setStopCallback(void *pclass,FUN_STOPCALLED stopcallback);

void *workThread(void *pParam);

private:

inline bool checkHead(uint8_t *buffer,int offset);

inline int findHead(uint8_t *buffer,int offset,int len);

void initBuffer(int *mNaluOld,int* mNaluNew,int *readOffsetOld,int*readOffsetCur);

int getData(int offset);

private:

FUN_GETINPUTDATA fun_getInputData;

FUN_PUTOUTDATA fun_putOutData;

FUN_STOPCALLED fun_stopcalled;

void *pclassInput;

void *pclassOut;

void *pclassStop;

uint8_t *mBuffer;

uint8_t *mMark;

uint8_t *mBuffadd;

int mBufferlen;

int mMarklen;

int mBuffaddlen;

int mBuffadddatalen;

int mfixreadlen;//每次输入数据长度

bool mbrun;

};

static void *ThreadBody(void *pdat)

{

CDataLinkup *pDatalink = (CDataLinkup *)pdat;

if(pDatalink)

{

pDatalink->workThread(NULL);

}

return NULL;

}

inline bool CDataLinkup::checkHead(uint8_t *buffer,int offset)

{

if(NULL == mMark)

{

ERRO_INFO(“erro !please setMark firstlyn”);

return 0;

}

return !memcmp(buffer+offset,mMark,mMarklen);

}

inline int CDataLinkup::findHead(uint8_t *buffer,int len)

{

int i;

if (len < mMarklen) {

DEBUG(“too short len %d n”,len);

return 0;

}

int maxoffset = offset+len;

//DEBUG(“len %d %dn”,len,maxoffset);

for (i = offset; i <= maxoffset – mMarklen; i++) {

if (checkHead(buffer,i)){

return i;

}

}

return 0;

}

CDataLinkup::CDataLinkup(int maxframelen,int fixreadlen)

{

fun_getInputData = NULL;

fun_putOutData = NULL;

fun_stopcalled = NULL;

mbrun = false;

mBufferlen = maxframelen*8;//最长帧内容倍数

mMarklen = 0;

mBuffaddlen = fixreadlen;

mfixreadlen = fixreadlen;

mMark = NULL;

mBuffer = (uint8_t*)malloc(mBufferlen);

mBuffadd = (uint8_t*)malloc(mBuffaddlen);

if(NULL == mBuffer || NULL == mBuffadd)

{

ERRO_INFO(“erro to malloc! mBufferlen %d,mBuffaddlen,%dn”,mBufferlen,mBuffaddlen);

}

memset(mBuffer,mBufferlen);

memset(mBuffadd,mBuffaddlen);

}

CDataLinkup::~CDataLinkup()

{

if(mBuffer)

{

free(mBuffer);

}

}

void CDataLinkup::setDataMark(uint8_t *mark,int marklen)

{

if(NULL==mark)

{

ERRO_INFO(“parm erro n”);

return ;

}

if(mMark)

{

free(mMark);

}

mMark = (uint8_t*)malloc(marklen);

if(NULL == mMark)

{

ERRO_INFO(“malloc erro marklen :%dn”,marklen);

}

memcpy(mMark,mark,marklen);

mMarklen = marklen;

};

void CDataLinkup::setDataInputCallback(void *pclass,FUN_GETINPUTDATA input)

{

pclassInput = pclass;

fun_getInputData = input;

}

void CDataLinkup::setDataOutputCallback(void *pclass,FUN_PUTOUTDATA output)

{

pclassOut = pclass;

fun_putOutData = output;

}

void CDataLinkup::setStopCallback(void *pclass,FUN_STOPCALLED stopcallback)

{

pclassStop = pclass;

fun_stopcalled = stopcallback;

}

void CDataLinkup::start()

{

if(fun_getInputData == NULL || fun_putOutData == NULL || mMark == NULL)

{

DEBUG(“erro to start,please check init!”);

return ;

}

mbrun = true;

int iRet;

pthread_t pid;

//iRet = pthread_create(&pid,NULL,(void* (*)(void*))&CDataLinkup::workThread,0);

iRet = pthread_create(&pid,ThreadBody,this);

if (iRet != 0)

{

printf(“[%d %s]pthread_create Failed (%d)n”,__LINE__,iRet);

}

else

{

pthread_detach(pid);

}

}

void CDataLinkup::stop()

{//加锁等待完全退出才返回

mbrun = false;

}

void CDataLinkup::initBuffer(int *mNaluOld,int*readOffsetCur)

{

int naulflag =0;

int datalen =0;

while(naulflag<=0)

{

int ret = getData(datalen);

if(ret > 0)

{

datalen +=ret;

naulflag = findHead(mBuffer,datalen);

}

else

{

break;

}

}

*mNaluOld= naulflag;

*readOffsetOld = datalen;

*readOffsetCur = *readOffsetOld;

DEBUG(“init %d %d n”,*readOffsetCur,*readOffsetOld);

}

//调用输入接口读取固定长度数据到mBuffer缓冲区

//如果缓冲区已经溢出,溢出部分数据会被存储在mBuffadd 特定位置,且mBuffadddatalen被设置

int CDataLinkup::getData(int offset)

{

int fixreadlen = mfixreadlen;

int remainlen = mBufferlen- offset;

int copylen = remainlen<fixreadlen?remainlen:fixreadlen;

int ret = 0;

if(copylen < fixreadlen)

{

ret = fun_getInputData(pclassInput,mBuffadd,fixreadlen);

if(ret < fixreadlen)

{

mbrun = false;

DEBUG(“getinput %d fixreadlen %dn”,ret,fixreadlen);

return 0;

}

//DEBUG(“full !n”);

memcpy(mBuffer+offset,copylen);

mBuffadddatalen = fixreadlen – copylen;

}

else

{

//DEBUG(“offset %d n”,offset);

ret = fun_getInputData(pclassInput,mBuffer+offset,fixreadlen);

return 0;

}

mBuffadddatalen = 0;

}

return copylen;

}

void *CDataLinkup::workThread(void *pParam)

{

int mNaluOld = 0;

int mNaluNew =0;

int readOffsetOld = 0;

int readOffsetCur =0;

bool bFirst = true;

bool bFull = false;//是否溢出

int checkbaklen = 2 * (mMarklen-1);

uint8_t *checkbak = (uint8_t*)malloc(checkbaklen);

uint8_t *framebuffer = mBuffer;

int readdata = 0;

if(framebuffer == NULL)

{

ERRO_INFO(“erro,mBuffer NULL n”);

return NULL;

}

while(mbrun)

{

//usleep(100);//

if (bFirst)

{

initBuffer(&mNaluOld,&mNaluNew,&readOffsetOld,&readOffsetCur);

bFirst =false;

}

//printf(“wang %d %s readOffsetOld %d n”,readOffsetOld);

if(!bFull)

{

readdata = getData(readOffsetOld);

if(readdata == 0)

{

mbrun = false;

DEBUG(“getdata erron”);

break;

}

readOffsetCur += readdata;

}

// 从帧缓冲中找 h264 NALU 开始标记

if (bFull)

{

// 已经溢出,数据不连续,跨断

memcpy(checkbak+mMarklen-1,framebuffer,mMarklen-1);

//uint8_t temp[4]={0x0,0x0,0x01};

//memcpy(checkbak,temp,checkbaklen);

//printf(“%#x %#x %#x %#x n”,checkbak[0],checkbak[1],checkbak[2],checkbak[3]);

//printf(“%#x %#x %#x n”,mMark[0],mMark[1],mMark[2]);

mNaluNew = findHead(checkbak,checkbaklen);

if(mNaluNew >0 )

{

mNaluNew+=mBufferlen-mMarklen-1;

fun_putOutData(pclassOut,framebuffer+mNaluOld,mNaluNew-mNaluOld);

DEBUG(“specificn”);

mNaluOld = mNaluNew;

}

{

mNaluNew = findHead(framebuffer,readOffsetOld);

while(!mNaluNew)

{

readdata = getData(readOffsetOld);

if(readdata == 0)

{

mbrun = false;

DEBUG(“getdata erron”);

break;

}

readOffsetCur += readdata;

mNaluNew = findHead(framebuffer,readOffsetOld-(mMarklen-1),readOffsetCur – readOffsetOld);

readOffsetOld = readOffsetCur;

}

}

int olddatalen = mBufferlen – mNaluOld;

// //找到一完整帧,输出

uint8_t *ptemp =(uint8_t*)malloc(olddatalen+mNaluNew);

memset(ptemp,olddatalen+mNaluNew);

if(ptemp == NULL)

{

//printf(“wang %d %s malloc fialed len :%d n”,olddatalen+mNaluNew);

return NULL;

}

memcpy(ptemp,olddatalen);

memcpy(ptemp+olddatalen,mNaluNew);

fun_putOutData(pclassOut,ptemp,olddatalen+mNaluNew);

free(ptemp);

bFull = false;

mNaluOld = mNaluNew;

}

else

{

//TRACK(“readOffsetOld %d readOffsetcur %dn”,readOffsetOld,readOffsetCur);

mNaluNew = findHead(framebuffer,readOffsetCur – readOffsetOld);

if (mNaluNew > 0)

{

// //找到一完整帧,输出

int framelen = mNaluNew – mNaluOld;

fun_putOutData(pclassOut,framelen);

mNaluOld = mNaluNew;

}

if(readOffsetCur >= mBufferlen)

{

// 已经溢出,需要拿新的数据

readOffsetCur = 0;

readOffsetOld = 0;

bFull = true;

if(mBuffadddatalen>0)

{//溢出的数据拷贝回来

memcpy(framebuffer,mBuffadd+(mfixreadlen-mBuffadddatalen),mBuffadddatalen);

readOffsetCur = mBuffadddatalen;

//fun_putOutData(pclassOut,mBuffadddatalen);

//break;

}

else

{

readdata = getData(readOffsetOld);

if(readdata == 0)

{

mbrun = false;

DEBUG(“getdata erron”);

break;

}

readOffsetCur += readdata;

}

readOffsetOld = readOffsetCur;

// 避免出现跨端区的NALL漏检

memcpy(checkbak,framebuffer+mBufferlen-(mMarklen-1),mMarklen-1);

}

else

{

readOffsetOld = readOffsetCur;

}

}

}

free(checkbak);

if(fun_stopcalled)

{

fun_stopcalled(pclassStop,NULL);

}

return NULL;

}

class CTest

{

public :

CTest(const char*fin,const char*fout);

~Ctest();

int readdata(uint8_t * buf,int buflen);

int putdata(uint8_t * buf,int buflen);

void* init();

DATALINK_IN_BACK(CTest,readdata);

DATALINK_OUT_BACK(CTest,putdata);

/*

static int intput (void* pclass,int buflen)

{

CTest *pCall= (CTest *)pclass;

//DEBUG(“pclass %pn”,pclass);

if(pCall)

{

return pCall->readdata(buf,buflen);

}

}

static int output (void* pclass,int buflen)

{

CTest *pCall= (CTest *)pclass;

if(pCall)

{

return pCall->putdata(buf,buflen);

}

}*/

private:

FILE *mfpin;

FILE *mfpout;

CDataLinkup *datalink;

};

/*

int CTest::intput (void* pclass,buflen);

}

return 0;

}

int CTest::output(void* pclass,buflen);

}

}*/

CTest::CTest(const char*fin,const char*fout)

{

mfpin= fopen(fin,”rb”);

mfpout = fopen(fout,”w+”);

if(mfpin == NULL || mfpout == NULL)

{

ERRO_INFO(“fopen erro n”);

}

}

CTest::~Ctest()

{

if(mfpin)

{

fclose(mfpin);

}

if(mfpout)

{

fclose(mfpout);

}

}

void* CTest::init()

{

uint8_t mark[3]={0x0,0x01};

datalink = new CDataLinkup(200*1024,1024*80);

datalink->setDataInputCallback(this,intput);

datalink->setDataOutputCallback(this,output);

datalink->setDataMark(mark,sizeof(mark));

datalink->start();

return this;

}

int CTest::readdata(uint8_t * buf,int buflen)

{

int ret = fread(buf,1,buflen,mfpin);

DEBUG(“input %dn”,ret);

return ret;

}

int CTest::putdata(uint8_t * buf,int buflen)

{

int ret = fwrite(buf,mfpout);

DEBUG(“output %d buflen %dn”,buflen);

return ret;

}

#if 0 //for c

FILE*fpin = NULL;

FILE*fpout = NULL;

uint8_t count =0;

int maxlen =0;

int datainput(void * pclass,uint8_t * buf,fpin);

//DEBUG(“input %dn”,ret);

return ret;

}

int dataoutput(void * pclass,int buflen)

{

count ++;

if(buflen >maxlen)

{

maxlen = buflen;

}

fwrite(&count,fpout);

int ret = fwrite(buf,fpout);

DEBUG(“output %d buflen %dn”,buflen);

return ret;

}

int fileclose(void * pclass,void *pram)

{

DEBUG(“over maxlen %d n”,maxlen);

fclose(fpin);

fclose(fpout);

}

int main(int argc,const char* argv[])

{

if(argc != 3){

printf(“usage :input file,output file!n”);

return -1;

}

fpin = fopen(argv[1],”rb”);

fpout = fopen(argv[2],”w+”);

if(fpin == NULL || fpout == NULL){

printf(“fopen erro n”);

return -1;

}

uint8_t mark[3]={0x0,0x01};

CDataLinkup *datalink = new CDataLinkup(200*1024,1024*80);

datalink->setDataInputCallback(NULL,datainput);

datalink->setDataOutputCallback(NULL,dataoutput);

datalink->setDataMark(mark,sizeof(mark));

datalink->setStopCallback(NULL,fileclose);

datalink->start();

//while(1);

//主线程退出,如果直接return则会导致调用eixt把整个进程结束

pthread_exit(NULL);

}

#endif

#if 1//for c++

int main(int argc,output file!n”);

return -1;

}

CTest *pTest = new CTest(argv[1],argv[2]);

pTest->init();

pthread_exit(NULL);

//一定要在pthread_exit之后,保证回调里面调用时这个pTest对象还存在

delete pTest;

}

#endif