在上节的基于FIFO传输的例子上修改,实现了基于共享内存的rtsp传输,
结构体share_mem保存接收到的数据长度和数据,在init函数里实现了信号量和共享内存的初始化
SendH264File不再调用SendH264Data,直接把数据和长度写进共享内存。
/******************************************************************** filename: RTSPStream.cpp *********************************************************************/ #include "RTSPStream.hh" #ifdef WIN32 #else #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <limits.h> #include <errno.h> #include <sys/shm.h> #include <sys/sem.h> #endif #define FIFO_NAME "/tmp/H264_fifo" #define BUFFERSIZE PIPE_BUF union semun { int val; struct semid_ds *buf; unsigned short *arry; }; typedef struct share_mem { int len; unsigned char data[1024*512]; }share_mem; CRTSPStream::CRTSPStream(void) { } CRTSPStream::~CRTSPStream(void) { } bool CRTSPStream::Init() { /*if(access(FIFO_NAME,F_OK) == -1) { int res = mkfifo(FIFO_NAME,0777); if(res != 0) { printf("[RTSPStream] Create fifo failed.\n"); return false; } printf("PIPE_BUF:%d\n",PIPE_BUF); }*/ int shmid; printf("sizeof(struct share_mem) : %d",sizeof(struct share_mem)); m_sem_id = semget((key_t)3443, 1, 0666 | IPC_CREAT); union semun sem_union; sem_union.val = 1; if(semctl(m_sem_id, 0, SETVAL, sem_union) == -1) perror("semctl error"); shmid = shmget(SHARE_MEM_ID,sizeof(share_mem),( IPC_CREAT|0666));; if(shmid < 0) perror("shmget error"); pch_shmem = ( struct share_mem* )shmat(shmid,( const void* )0,0 ); if(pch_shmem == (void *)-1) { perror("shmem error\n"); } return true; } void CRTSPStream::Uninit() { } bool CRTSPStream::SendH264File(const char *pFileName) { if(pFileName == NULL) { return false; } FILE *fp = fopen(pFileName, "rb"); if(!fp) { printf("[RTSPStream] error:open file %s failed!",pFileName); } fseek(fp, 0, SEEK_SET); //unsigned char *buffer = new unsigned char[FILEBUFSIZE]; int pos = 0; while(1) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1;//P() sem_b.sem_flg = SEM_UNDO; if(semop(m_sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); return 0; } pch_shmem->len = fread(pch_shmem->data, sizeof(unsigned char), 1024*512, fp); //struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1;//P() sem_b.sem_flg = SEM_UNDO; if(semop(m_sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); return 0; } if(pch_shmem->len<=0) { perror("readlen \n"); break; } printf("readlen:%d,pos:%d\n",pch_shmem->len,pos); //int writelen = SendH264Data(pch_shmem,readlen); /* if(writelen<=0) { printf("writelen %d\n",writelen); perror("a\n"); break; } */ //memcpy(buffer,buffer+writelen,readlen-writelen); //memcpy(buffer,buffer+writelen,readlen-writelen); //pos = readlen-writelen; // printf("writelen:%d,readlen-writelen:%d\n",writelen,pos); mSleep(100); //mSleep(100); sleep(2); } fclose(fp); //delete[] buffer; return true; } int CRTSPStream::SendH264Data(const unsigned char *data,unsigned int size) { if(data == NULL) { printf("data is 0\n"); return 0; } // open pipe with non_block mode int pipe_fd = open(FIFO_NAME, O_WRONLY); printf("[RTSPStream] open fifo result = [%d]\n",pipe_fd); if(pipe_fd == -1) { perror("open failed\n"); return 0; } int send_size = 0; int remain_size = size; while(send_size < size) { int data_len = (remain_size<BUFFERSIZE) ? remain_size : BUFFERSIZE; int len = write(pipe_fd,data+send_size,data_len); printf("data_len:%d,send_size:%d\n",data_len,send_size); if(len == -1) { static int resend_conut = 0; if(errno == EAGAIN && ++resend_conut<=3) { printf("[RTSPStream] write fifo error,resend..\n"); continue; } resend_conut = 0; printf("[RTSPStream] write fifo error,errorcode[%d],send_size[%d]\n",errno,send_size); break; } else { send_size+= len; remain_size-= len; } //sleep(8); } close(pipe_fd); //printf("[RTSPStream] SendH264Data datalen[%d], sendsize = [%d]\n",size,send_size); return 0; } #if 0 bool CRTSPStream::SendH264File(const char *pFileName) { if(pFileName == NULL) { return false; } FILE *fp = fopen(pFileName, "rb"); if(!fp) { printf("[RTSPStream] error:open file %s failed!",pFileName); } fseek(fp, 0, SEEK_SET); unsigned char *buffer = new unsigned char[FILEBUFSIZE]; int pos = 0; while(1) { int readlen = fread(buffer, sizeof(unsigned char), FILEBUFSIZE, fp); if(readlen<=0) { perror("readlen \n"); break; } printf("readlen:%d,pos:%d\n",readlen,pos); //readlen+=pos; pos += readlen; int writelen = SendH264Data(buffer,readlen); /* if(writelen<=0) { printf("writelen %d\n",writelen); perror("a\n"); break; } */ //memcpy(buffer,buffer+writelen,readlen-writelen); //memcpy(buffer,buffer+writelen,readlen-writelen); //pos = readlen-writelen; // printf("writelen:%d,readlen-writelen:%d\n",writelen,pos); mSleep(25); } fclose(fp); delete[] buffer; return true; } #endif // 发送H264数据帧 #if 0 int CRTSPStream::SendH264Data(const unsigned char *data,unsigned int size) { if(data == NULL) { printf("data is 0\n"); return 0; } // open pipe with non_block mode int pipe_fd = open(FIFO_NAME, O_WRONLY); printf("[RTSPStream] open fifo result = [%d]\n",pipe_fd); if(pipe_fd == -1) { perror("open failed\n"); return 0; } int send_size = 0; int remain_size = size; while(send_size < size) { int data_len = (remain_size<BUFFERSIZE) ? remain_size : BUFFERSIZE; int len = write(pipe_fd,data+send_size,data_len); printf("data_len:%d,send_size:%d\n",data_len,send_size); if(len == -1) { static int resend_conut = 0; if(errno == EAGAIN && ++resend_conut<=3) { printf("[RTSPStream] write fifo error,resend..\n"); continue; } resend_conut = 0; printf("[RTSPStream] write fifo error,errorcode[%d],send_size[%d]\n",errno,send_size); break; } else { send_size+= len; remain_size-= len; } //sleep(8); } close(pipe_fd); //printf("[RTSPStream] SendH264Data datalen[%d], sendsize = [%d]\n",size,send_size); return 0; } #endif
对于rtsp server的修改如下
WW_H264VideoSource用于获取信号量和共享内存
主要的修改集中在GetFrameData,数据传给fTo地址的内存区域,长度传给全局变量fFrameSize,和FIFO模式一样
#include "WW_H264VideoSource.hh" #include "RTSPStream.hh" #include <stdio.h> #ifdef WIN32 #include <windows.h> #else #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <limits.h> #include <sys/shm.h> #include <sys/sem.h> #endif #define FIFO_NAME "/tmp/H264_fifo" #define BUFFER_SIZE PIPE_BUF //#define REV_BUF_SIZE (1024*1024) #define REV_BUF_SIZE (1024*1024) #ifdef WIN32 #define mSleep(ms) Sleep(ms) #else #define mSleep(ms) usleep(ms*1000) #endif union semun { int val; struct semid_ds *buf; unsigned short *arry; }; typedef struct share_mem { int len; unsigned char data[1024*1024]; }share_mem; WW_H264VideoSource::WW_H264VideoSource(UsageEnvironment & env) : FramedSource(env), m_pToken(0), m_pFrameBuffer(0), m_hFifo(0) { /* m_hFifo = open(FIFO_NAME,O_RDONLY|O_NONBLOCK); printf("[MEDIA SERVER] open fifo result = [%d],PIPE_BUF=%d\n",m_hFifo,PIPE_BUF); if(m_hFifo == -1) { perror("m_hFifo\n"); return; } */ m_pFrameBuffer = new char[REV_BUF_SIZE]; if(m_pFrameBuffer == NULL) { perror("m_hFifo\n"); printf("[MEDIA SERVER] error malloc data buffer failed\n"); return; } memset(m_pFrameBuffer,0,REV_BUF_SIZE); m_sem_id = semget((key_t)3443, 0, 0); int shmid; shmid = shmget(SHARE_MEM_ID,0,0); if(shmid == -1) { perror("shmem error "); } pch_shmem = ( struct share_mem* )shmat( shmid,( const void* )0,0 ); printf("success\n"); } WW_H264VideoSource::~WW_H264VideoSource(void) { /* if(m_hFifo) { ::close(m_hFifo); } */ envir().taskScheduler().unscheduleDelayedTask(m_pToken); if(m_pFrameBuffer) { delete[] m_pFrameBuffer; m_pFrameBuffer = NULL; } printf("[MEDIA SERVER] rtsp connection closed\n"); } void WW_H264VideoSource::doGetNextFrame() { // 根据 fps,计算等待时间 double delay = 1000.0 / (FRAME_PER_SEC * 2); // ms int to_delay = delay * 1000; // us m_pToken = envir().taskScheduler().scheduleDelayedTask(to_delay, getNextFrame, this); } unsigned int WW_H264VideoSource::maxFrameSize() const { return 1024*1024; } void WW_H264VideoSource::getNextFrame(void * ptr) { ((WW_H264VideoSource *)ptr)->GetFrameData(); } void WW_H264VideoSource::GetFrameData() { gettimeofday(&fPresentationTime, 0); //printf("get frame\n"); // fFrameSize = 1024*512; int len = 0; unsigned char buffer[BUFFER_SIZE] = {0}; /* while((len = read(m_hFifo,buffer,BUFFER_SIZE))>0) { //printf("len is %d\n",len); memcpy(m_pFrameBuffer+fFrameSize,buffer,len); fFrameSize+=len; //sleep(5); } printf("[MEDIA SERVER] GetFrameData len = [%d],fMaxSize = [%d]\n",fFrameSize,fMaxSize); */ // fill frame data //memcpy(fTo,m_pFrameBuffer,fFrameSize); struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1;//P() sem_b.sem_flg = SEM_UNDO; if(semop(m_sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); //return 0; } memcpy(m_pFrameBuffer,pch_shmem->data,pch_shmem->len); printf("len:%d,fMaxSize:%d\n",pch_shmem->len,fMaxSize); fFrameSize = pch_shmem->len; memcpy(fTo,m_pFrameBuffer,fFrameSize); //FILE *fp = fopen("123.264", "wb"); //fwrite(pch_shmem,1,1024*1024,fp); //fclose(fp); // struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1;//V() sem_b.sem_flg = SEM_UNDO; if(semop(m_sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_v failed\n"); //return 0; } //printf("fTo,pch_shmem\n"); /* if (fFrameSize > fMaxSize) { fNumTruncatedBytes = fFrameSize - fMaxSize; fFrameSize = fMaxSize; } else { fNumTruncatedBytes = 0; } */ afterGetting(this); }编译后,既可播放,文件读取播放一切正常,用于摄像头数据实时传输 偶尔出现花屏,据分析是摄像头一帧数据较小,而rtsp发送给客户端没有加上信号量保护,对c++不熟悉,就不再研究了,以后会用fenice实现rtsp