metaRTC通用H264文件帧获取发送方法
最近杨总的metaRTC很火(gitee.com/metaRTC/metaRTC),也决定将以前的一些应用移植过去,拥抱国产生态,在适配过程中,发现测试还是比较麻烦,不是ffmpeg,就是需要IPC得硬件版子,能在pc上实现初步调试是非常有必要的,于是通过一番查找和恶补H264编码格式得相关知识,做了一个通用的获取H264帧的函数,话不多说,直接上代码,获取某帧的段地址指针很相应长度偏移量
static int FindStartCode2 (unsigned char *Buf)
{if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //判断是否为0x000001,如果是返回1else return 1;
}static int FindStartCode3 (unsigned char *Buf)
{if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//判断是否为0x00000001,如果是返回1else return 1;
}
#define CONVERT_TIMESTAMP_TO_RTP(clockRate, pts) ((UINT64) ((DOUBLE) (pts) * ((DOUBLE) (clockRate) / HUNDREDS_OF_NANOS_IN_A_SECOND)))
#define VIDEO_CLOCKRATE (UINT64) 90000
//NALU HEADER
#define NALU_TYPE_MASK 0x1f //00011111 nal_unit_type
#define NALU_FORB_MASK 0x80 //10000000 forbidden_bit
#define NALU_REF_MASK 0x60 //01100000 nal_reference_bit
#define NALU_TYPE_SPS 0x67 //SPS
#define NALU_TYPE_PPS 0x68 //PPS
#define NALU_TYPE_IDR 0x65 //IDR关键帧
#define NALU_TYPE_PFRAME 0x61 //P帧
#define NALU_TYPE_PFRAME2 0x41
// #define NALU_TYPE_BFRAME 0x01
#define NALU_TYPE_SEI 0x06void GetNaluSlice(PBYTE scr,uint32_t scrlen,uint32_t *packetlen,uint32_t * nalutype ,BOOL * endframe,uint32_t *pscroffset){// printf("GetNaluSlice %d\n",scrlen);uint32_t destcount=0,naluendptr=0;if(FindStartCode2(scr)) destcount=3;else if(FindStartCode3(scr)) destcount=4;else return -1;*nalutype = scr[destcount];naluendptr=destcount;*pscroffset=destcount;* endframe=FALSE;do{if(FindStartCode2(scr+naluendptr)||FindStartCode3(scr+naluendptr)){*packetlen=naluendptr-destcount;if(debuglog)printf("get nalu type 0x%x len %d\r\n",*nalutype,naluendptr-destcount);break;} if(++naluendptr>=scrlen) {* endframe=TRUE;*packetlen=naluendptr-destcount;if(debuglog)printf("get nalu type 0x%x len %d frame is end\r\n",*nalutype,naluendptr-destcount);break;}}while(1);
}
发送至meta peer rtc session缓冲器
int yang_IPCEncoder_save_stream(YangVideoEncoderBuffer2* buf,YangFrame* frame,PFrame pframe ){uint32_t scrindex=0,scrlen=pframe->size;int32_t isKeyframe = YANG_Frametype_P;int32_t dataLength=pframe->size;uint8_t *data = NULL, *vbuffer = NULL;data = pframe->frameData;PBYTE pscr=pframe->frameData;isKeyframe = YANG_Frametype_P;frame->frametype = YANG_Frametype_P;uint32_t destindex=0;uint32_t nalutype;BOOL ready=FALSE,endframe=FALSE;uint32_t offset=0;do{GetNaluSlice(pscr,scrlen,& destindex,& nalutype ,& endframe,&scrindex);//offset+=scrindex;switch(nalutype){case NALU_TYPE_PPS:case NALU_TYPE_SPS:case NALU_TYPE_IDR:isKeyframe = YANG_Frametype_I;frame->frametype = YANG_Frametype_I;case NALU_TYPE_PFRAME:case NALU_TYPE_PFRAME2:ready=TRUE;//scrlen-=scrindex;data=pscr;//+scrindex;break; case NALU_TYPE_SEI:return 0; }if(ready) break;if(endframe)return 0;else{pscr+=scrindex+destindex;scrlen-=scrindex+destindex;if(scrlen<=0) return 0;}//if(endframe}while(1);frame->pts=frame->dts=pframe->presentationTs;if (isKeyframe == YANG_Frametype_I) {int32_t spsLen=0,ppsLen=0,spsPos=0,ppsPos=0,ipos=0;dataLength=scrlen;spsLen=destindex;spsPos=data+scrindex;//sps lenthyang_put_be32((char*)frame->payload,destindex);ipos+=4;//sps contentmemcpy(frame->payload+ipos, data+scrindex, destindex);ipos+=destindex;vbuffer = data+scrindex+destindex;scrlen-=destindex+scrindex;destindex=0;scrindex=0;GetNaluSlice(vbuffer,scrlen,& destindex,& nalutype ,& endframe,&scrindex);//pps lenthyang_put_be32((char*)frame->payload+ipos,destindex);ipos+=4;//pps contentmemcpy(frame->payload+ipos, vbuffer+scrindex, destindex); ipos+=destindex; vbuffer += scrindex+destindex;scrlen-=destindex+scrindex;uint32_t destcount=0;if(FindStartCode2(vbuffer)) destcount=3;else if(FindStartCode3(vbuffer)) destcount=4;else return 0;if(destcount==3){ frame->payload[ipos]=0x00;ipos+=1;}memcpy(frame->payload+ipos, vbuffer, scrlen);ipos+=scrlen;frame->nb = ipos;//dumphex(frame->payload,frame->nb);} else {int pFramePos = 0;dataLength=scrlen-scrindex;//pFramePos = yang_find_pre_start_code(data,dataLength);memcpy(frame->payload, data+scrindex, dataLength);if(frame->payload[0]==0x41) frame->payload[0]=0x61;frame->nb = dataLength;}buf->putEVideo(&buf->mediaBuffer,frame);if(debuglog){//if (isKeyframe == YANG_Frametype_I) {printf("ts-> %lld send %d bytes to rtp:",frame->dts,frame->nb);dumphex(frame->payload,frame->nb<200?frame->nb:200);printf("\n");}return 0;
}
循环发送H264文件帧线程
uint8_t buffer[2*1024*1024]={0};
uint8_t framebuffer[2*1024*1024]={0};
uint8_t h264filebuffer[5*1024*1024]={0};
PVOID sendVideoPackets(PVOID args)
{STATUS retStatus = STATUS_SUCCESS;pStreamManage pstreammanage = gpStreamManage;//(pStreamManage) args;EncoderSession* session=(EncoderSession*)args;Frame frame;UINT32 fileIndex = 0, frameSize;// CHAR filePath[MAX_PATH_LEN + 1];STATUS status;UINT32 i;UINT64 startTime, lastFrameTime, elapsed;if (pstreammanage == NULL) {printf("[metaRTC Master] sendVideoPackets(): operation returned status code: 0x%08x \n", STATUS_NULL_ARG);goto CleanUp;}YangFrame videoFrame;memset(&videoFrame,0,sizeof(YangFrame));videoFrame.payload = buffer;memset(&frame,0,sizeof(Frame));frame.frameData=framebuffer;startTime = GETTIME();lastFrameTime = startTime;frame.presentationTs = 0;pstreammanage->videoBufferSize=0;printf("sendVideoPackets starting ...\n");UINT32 h264filebuffersize=0;if(!strlen(filePath)||!strstr(filePath,".h264")){snprintf(filePath, MAX_PATH_LEN, "%s","../video/test.h264");//"../test.h264");printf("filepath is not exist use default %s \n",filePath);}retStatus = readFrameFromDisk(NULL, &h264filebuffersize, filePath);if (retStatus != STATUS_SUCCESS) {printf("[metaRTC Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus);goto CleanUp;}retStatus = readFrameFromDisk(h264filebuffer, &h264filebuffersize, filePath);if (retStatus != STATUS_SUCCESS) {printf("[metaRTC Master] readFrameFromDisk(): operation returned status code: 0x%08x \n", retStatus);goto CleanUp;} PBYTE scrpos=h264filebuffer,scr=h264filebuffer,pframeindex=frame.frameData;uint32_t scrlen=h264filebuffersize;uint32_t packetlen;uint32_t nalutype;BOOL endframe;uint32_t pscroffset=0,ipos=0,framelen=0; while (!ATOMIC_LOAD_BOOL(&pstreammanage->appTerminateFlag) &&(session->isConvert == 1)) {if(endframe){scrpos=h264filebuffer;scr=h264filebuffer;scrlen=h264filebuffersize; pframeindex=frame.frameData; frame.size=0;}GetNaluSlice(scr,scrlen,&packetlen,&nalutype ,&endframe,&pscroffset);switch(nalutype){case NALU_TYPE_PPS://continue;case NALU_TYPE_SPS:ipos+=pscroffset+packetlen;memcpy(pframeindex,scrpos,ipos);pframeindex+= ipos;framelen+=ipos;// frame.size+=ipos;scrpos+=ipos;ipos=0;//continue;break;case NALU_TYPE_IDR:case NALU_TYPE_PFRAME:case NALU_TYPE_PFRAME2:ipos+=pscroffset+packetlen;memcpy(pframeindex,scrpos,ipos);pframeindex+= ipos;frame.size+=ipos+framelen;scrpos+=ipos;ipos=0;framelen=0;// dumphex(frame.frameData,frame.size); //break;//memcpy(frame.frameData,scrpos,ipos);break; case NALU_TYPE_SEI://ipos+=pscroffset+packetlen;scrpos+=pscroffset+packetlen;;break;default:ipos+=pscroffset+packetlen;break;//continue; }if(frame.size>0){frame.presentationTs=GETTIME();MUTEX_LOCK(pstreammanage->streamingSessionListReadLock);// for (i = 0; i < pstreammanage->streamingSessionCount; ++i) {retStatus = yang_IPCEncoder_save_stream(session->out_videoBuffer,&videoFrame, &frame);if (retStatus < 0) {goto CleanUp;}// }MUTEX_UNLOCK(pstreammanage->streamingSessionListReadLock);// Adjust sleep in the case the sleep itself and writeFrame take longer than expected. Since sleep makes sure that the thread// will be paused at least until the given amount, we can assume that there's no too early frame scenario.// Also, it's very unlikely to have a delay greater than SAMPLE_VIDEO_FRAME_DURATION, so the logic assumes that this is always// true for simplicity.elapsed = lastFrameTime - startTime;THREAD_SLEEP(SAMPLE_VIDEO_FRAME_DURATION - elapsed % SAMPLE_VIDEO_FRAME_DURATION);lastFrameTime = GETTIME();pframeindex=frame.frameData; frame.size=0;ipos=0;}scr+=pscroffset+packetlen;scrlen-=pscroffset+packetlen;}CleanUp:CHK_LOG_ERR(retStatus);return (PVOID) (ULONG_PTR) retStatus;
}
感谢大佬liuping36131997的启发。
最后膜拜杨大侠,一己之力造出了metaRTC,也希望他的愿景成真,大家可以关注他的个人博客
https://blog.csdn.net/m0_56595685/category_11474470.html