live555實現共享記憶體視訊直播

就是個玩耳機的發表於2015-07-20

在上節的基於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
 


相關文章