最近项目用的网络摄像头,从网络层获取到连续的h264片段数据,于是写了一个将连续输入数据按 指定字符串分割成一个slice数据的 class, 稍作改动,也可以用来处理 文本文件中查找 替换等等功能,或者从一个h264文件中分离出单独的h264帧

linux 环境 ubunt 上已测试,编译:g++ *.cpp -lpthread

用到了回调,但是没有处理C++成员函数作为回调问题,后续改良

 

2.0 (连续h264片段拼接成完整h264帧)(二)支持c++调用  (纠正下,是拼接或分离出h264slice)

                                  https://blog.csdn.net/u012459903/article/details/85263959

3.0(连续h264片段拼接成完整h264帧)(三)循环体移到外部调用,便于调用者控制 (纠正下,是拼接或分离出h264slice)

                                https://blog.csdn.net/u012459903/article/details/85263959

4.0 c++ 回调(还可继续修改为c++风格常见的虚函数)

                               https://blog.csdn.net/u012459903/article/details/86623018

代码:

/***
***20181221 canok
***	 brief : Combine data fragments into complete frames
*** input : Continuous data flow, support fix length data input;
*** output: complete frames
**/
#include<stdio.h>
#include<stdlib.h>
		
#include <unistd.h>
	
#include <string.h>
#include <errno.h>

#include <pthread.h>
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,%s,%d]",__FILE__,__FUNCTION__,__LINE__);printf(fmt,##args);} while(0)
class CDataLinkup 
{
	public:
		CDataLinkup(int maxframelen,int fixreadlen);
		~CDataLinkup();
		void setDataInputCallback(int (*input)(uint8_t *buf,int buflen));
		void setDataOutputCallback(int (*output)(uint8_t *buf,int buflen));
		void setDataMark(uint8_t *mark, int marklen);
		void start();
		void stop();

		void setStopCallback(int(*stopcallback)(void*pram));
		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:
		int (*fun_getInputData)(uint8_t *buf,int buflen);
		int (*fun_putOutData)(uint8_t *buf,int buflen);
		int (*fun_stopcalled)(void*pram);
		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);
	}
}

inline  bool CDataLinkup::checkHead(uint8_t *buffer, int offset) 
{
	if(NULL == mMark)
	{	
		ERRO_INFO("erro !please setMark firstly\n");
		return 0;
	}
	return !memcmp(buffer+offset,mMark,mMarklen);
}

inline int CDataLinkup::findHead(uint8_t *buffer, int offset, int len) 
{
	int i;
	if (len < mMarklen) {
		DEBUG("too short len %d \n",len);
		return 0;
	}
	int maxoffset = offset+len;
	
	//DEBUG("len %d %d\n",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,%d\n",mBufferlen,mBuffaddlen);
	}
	memset(mBuffer,0,mBufferlen);
	memset(mBuffadd,0,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 :%d\n",marklen);
	}
	memcpy(mMark,mark,marklen);
	mMarklen = marklen;
}

void CDataLinkup::setDataInputCallback(int (*input)(uint8_t *buf,int buflen))
{
	fun_getInputData = input;
}


void CDataLinkup::setDataOutputCallback(int (*output)(uint8_t *buf,int buflen))
{
	fun_putOutData = output;
}
void CDataLinkup::setStopCallback(int (*stopcallback)(void*pram))
{
	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, NULL, ThreadBody, this);
	if (iRet != 0)
	{
		printf("[%d %s]pthread_create	failed (%d)\n",__LINE__, __FUNCTION__, iRet);
	}
	else
	{
		pthread_detach(pid);
	}
}

void CDataLinkup::stop()
{//加锁等待完全退出才返回
	mbrun = false;
}





void CDataLinkup::initBuffer(int *mNaluOld,int* mNaluNew,int *readOffsetOld,int*readOffsetCur)
{
	int naulflag =0;
	int datalen =0;
	while(naulflag<=0)
	{
		int ret = getData(datalen);
		if(ret >=0)
		{
			datalen +=ret;
			naulflag = findHead(mBuffer, 0, 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(mBuffadd,fixreadlen);
		if(ret < fixreadlen)
		{
			mbrun = false;
			DEBUG("getinput %d fixreadlen %d\n",ret,fixreadlen);
			return 0;
		}
		//DEBUG("full !\n");
		memcpy(mBuffer+offset,mBuffadd,copylen);
		mBuffadddatalen = fixreadlen - copylen;
	}
	else 
	{	
		//DEBUG("offset %d \n",offset);
		ret = fun_getInputData(mBuffer+offset,fixreadlen);
		if(ret < fixreadlen)
		{
			mbrun = false;
			DEBUG("getinput %d fixreadlen %d\n",ret,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",__LINE__,__FUNCTION__,readOffsetOld);
		if(!bFull)
		{
			
			readdata =  getData(readOffsetOld);
			if(readdata == 0)
			{
				mbrun = false;
				DEBUG("getdata erro\n");
				break;
			}
			readOffsetCur += readdata;
		}
		// 从帧缓冲中找 h264 NALU 开始标记
		if (bFull) 
		{	
			// 已经溢出,数据不连续, 跨断
			memcpy(checkbak+mMarklen-1,framebuffer,mMarklen-1);
			//uint8_t temp[4]={0x0,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, 0, checkbaklen);
			
			if(mNaluNew >0 )
			{
				mNaluNew+=mBufferlen-mMarklen-1;
				fun_putOutData(framebuffer+mNaluOld,mNaluNew-mNaluOld);
				DEBUG("specific\n");
				mNaluOld = mNaluNew;
			}
			
			{	
				mNaluNew = findHead(framebuffer, 0, readOffsetOld);
				while(!mNaluNew)
				{
				
					readdata =  getData(readOffsetOld);
					if(readdata == 0)
					{
						mbrun = false;
						DEBUG("getdata erro\n");
						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,0,olddatalen+mNaluNew);
			if(ptemp == NULL)
			{
				//printf("wang %d %s malloc fialed len :%d \n",__LINE__,__FUNCTION__,olddatalen+mNaluNew);
				return NULL;
			}
			memcpy(ptemp,framebuffer+mNaluOld,olddatalen);
			memcpy(ptemp+olddatalen,framebuffer,mNaluNew);
			fun_putOutData(ptemp,olddatalen+mNaluNew);
			free(ptemp);
			bFull = false;
			mNaluOld = mNaluNew;

		}
		else 
		{
		
			//TRACK("readOffsetOld %d readOffsetcur %d\n",readOffsetOld,readOffsetCur);
			mNaluNew = findHead(framebuffer, readOffsetOld-(mMarklen-1), readOffsetCur - readOffsetOld);
			if (mNaluNew > 0) 
			{
				// //找到一完整帧,输出
				int framelen = mNaluNew - mNaluOld;
				fun_putOutData(framebuffer+mNaluOld,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(framebuffer,mBuffadddatalen);
					//break;
				}
				else
				{
					readdata =  getData(readOffsetOld);
					if(readdata == 0)
					{
						mbrun = false;
						DEBUG("getdata erro\n");
						break;
					}
					readOffsetCur += readdata;
				}
				readOffsetOld = readOffsetCur;
				// 避免出现跨端区的NALL漏检
				memcpy(checkbak,framebuffer+mBufferlen-(mMarklen-1),mMarklen-1);
				
			}
			else
			{
				readOffsetOld = readOffsetCur;
			}

			
		}
		
	}
	free(checkbak);
	if(fun_stopcalled)
	{
		fun_stopcalled(NULL);
	}
	
}

#if 1
FILE*fpin = NULL;
FILE*fpout = NULL;
uint8_t count  =0;
int maxlen =0;
int datainput(uint8_t * buf,int buflen)
{
	int ret = fread(buf,1,buflen,fpin);
	//DEBUG("input  %d\n",ret);
	return ret;
}

int dataoutput(uint8_t * buf,int buflen)
{
	count ++;
	if(buflen >maxlen)
		{
			maxlen = buflen;
			
		}
	fwrite(&count,1,1,fpout);
	int ret = fwrite(buf,1,buflen,fpout);
	DEBUG("output  %d buflen %d\n",ret,buflen);
	return ret;
}
int fileclose(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,0x0,0x01};
	CDataLinkup *datalink = new CDataLinkup(200*1024,1024*80);
	datalink->setDataInputCallback(datainput);
	datalink->setDataOutputCallback(dataoutput);
	datalink->setDataMark(mark,sizeof(mark));
	datalink->setStopCallback(fileclose);
	datalink->start();
	//while(1);
	//主线程退出, 如果直接return则会导致调用eixt把整个进程结束
	pthread_exit(NULL);
}
#endif

 

Logo

更多推荐