在上节的基于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