网络数据包片段拼合(连续h264片段拼接成完整h264帧)--纠正下,是h264 slice,不是图像帧
最近项目用的网络摄像头,从网络层获取到连续的h264片段数据,于是写了一个将连续输入数据按 指定字符串分割成一个slice数据的 class, 稍作改动,也可以用来处理 文本文件中查找 替换等等功能,或者从一个h264文件中分离出单独的h264帧linux 环境 ubunt 上已测试,编译:g++ *.cpp -lpthread用到了回调,但是没有处理C++成员函数作为回调问题,后续改良...
最近项目用的网络摄像头,从网络层获取到连续的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
更多推荐
所有评论(0)