嵌入式流媒体消息分发队列设计

本文介绍了一种基于信号量和固定指针数组的解决方案,用于高效地在嵌入式流媒体系统中处理音频/视频数据和信令信息。作者分享了如何通过预先分配的全局数组、线程调度API和条件变量来简化数据发布/订阅,降低系统复杂度,并已将其开源以供改进。

      在流媒体系统设计中,经常会涉及多线程间的通信问题,需要相互分发消费一些信息以及实时音视频数据,如何才能高效安全的简洁的实现这一目标呢, 可以采用ringbuffer,也可以采用链表等算法。我在实践中做了一个基于信号量和固定指针数组的方案,实现了简单的数据发布订阅,pMessage可以自定义为一个适配自己的消息传递数据结构指针,用于传递消息,如数据指向的指针,数据类型,时间戳,数据长度,或者信令,在嵌入式系统中,采用预分配的全局数组,可以减少动态分配带来的风险和时间波动。通过设置一些信号量及互斥来实现系统级的线程调度,为了使使用更简洁,封装了一些简单的api,实现音频信息 视频信息以及信令信息的接口,将具体的逻辑封装起来,线程中直接调用这些接口就可以实现系统级的订阅等待和数据发布功能,当然这个代码不一定设计最科学和合理,但的确为我的系统带来了多线程处理过程的简洁明快的好处,也在实践中运行良好,现将他开源出来,也希望各位大佬指正,帮助我提高。好了,各位有啥好的意见和建议欢迎跟我交流。本代码用于我的嵌入式webrtc系统,大大简化了我的系统的复杂度。

#include <stdio.h> 
#include <pthread.h> 
#include<semaphore.h>
#include "common.h"
#define BUFFER_SIZE 50 // 缓冲区数量 
struct prodcons 
{ 
	// 缓冲区相关数据结构 
	pMessage buffer[BUFFER_SIZE]; /* 实际数据存放的数组*/ 
	pthread_mutex_t lock; /* 互斥体lock ⽤于对缓冲区的互斥操作 */ 
	int readpos, writepos; /* 读写指针*/ 
	pthread_cond_t notempty; /* 缓冲区⾮空的条件变量 */ 
	pthread_cond_t notfull; /* 缓冲区未满的条件变量 */ 
}; 
#define OVER ( - 1) 
struct prodcons pmsgbuffer; 
struct prodcons pvideo_streambuffer; 
struct prodcons paudio_streambuffer; 
struct prodcons pplayvideo_streambuffer; 
struct prodcons pplayaudio_streambuffer; 

sem_t sem_answerwait, sem_offerwait,sem_videostreamwait,sem_audiostreamwait,sem_playvideostreamwait,sem_playaudiostreamwait;
/* 初始化缓冲区结构 */ 
void init(struct prodcons *b) 
{ 
	pthread_mutex_init(&b->lock, NULL); 
	pthread_cond_init(&b->notempty, NULL); 
	pthread_cond_init(&b->notfull, NULL); 
	b->readpos = 0; 
	b->writepos = 0; 
} 
/* 将产品放⼊缓冲区,这⾥是存⼊⼀个整数*/ 
void put(struct prodcons *b, pMessage data) 
{ 
	pthread_mutex_lock(&b->lock); 
	/* 等待缓冲区未满*/ 
	if ((b->writepos + 1) % BUFFER_SIZE == b->readpos) 
	{ 
	  pthread_cond_wait(&b->notfull, &b->lock); 
	} 
	/* 写数据,并移动指针 */ 
	b->buffer[b->writepos] = data; 
	b->writepos++; 
	if (b->writepos >= BUFFER_SIZE) 
	  b->writepos = 0; 
	/* 设置缓冲区⾮空的条件变量*/ 
	pthread_cond_signal(&b->notempty); 
	pthread_mutex_unlock(&b->lock); 
}  
/* 从缓冲区中取出整数*/ 
pMessage get(struct prodcons *b) { 
	pMessage data; 
	pthread_mutex_lock(&b->lock); 
	/* 等待缓冲区⾮空*/ 
	if (b->writepos == b->readpos) 
	{ 
	  pthread_cond_wait(&b->notempty, &b->lock); 
	} 
	/* 读数据,移动读指针*/ 
	data = b->buffer[b->readpos]; 
	b->readpos++; 
	if (b->readpos >= BUFFER_SIZE) 
	   b->readpos = 0; 
	/* 设置缓冲区未满的条件变量*/
	pthread_cond_signal(&b->notfull); 
	pthread_mutex_unlock(&b->lock); 
	return data; 
} 
/* 将产品放⼊缓冲区,这⾥是存⼊⼀个整数*/ 
void putstream(struct prodcons *b, ptstreamInfo data) 
{ 
	pthread_mutex_lock(&b->lock); 
	/* 等待缓冲区未满*/ 
	if ((b->writepos + 1) % BUFFER_SIZE == b->readpos) 
	{ 
	  pthread_cond_wait(&b->notfull, &b->lock); 
	} 
	/* 写数据,并移动指针 */ 
	b->buffer[b->writepos] = data; 
	b->writepos++; 
	if (b->writepos >= BUFFER_SIZE) 
	  b->writepos = 0; 
	/* 设置缓冲区⾮空的条件变量*/ 
	pthread_cond_signal(&b->notempty); 
	pthread_mutex_unlock(&b->lock); 
}  
/* 从缓冲区中取出整数*/ 
ptstreamInfo getstream(struct prodcons *b) { 
	ptstreamInfo data; 
	pthread_mutex_lock(&b->lock); 
	/* 等待缓冲区⾮空*/ 
	if (b->writepos == b->readpos) 
	{ 
	  pthread_cond_wait(&b->notempty, &b->lock); 
	} 
	/* 读数据,移动读指针*/ 
	data = b->buffer[b->readpos]; 
	b->readpos++; 
	if (b->readpos >= BUFFER_SIZE) 
	   b->readpos = 0; 
	/* 设置缓冲区未满的条件变量*/
	pthread_cond_signal(&b->notfull); 
	pthread_mutex_unlock(&b->lock); 
	return data; 
} 
void initmsgqueue(void) {
	sem_init(&sem_offerwait,0,0);
	sem_init(&sem_answerwait,0,0);
    sem_init(&sem_videostreamwait,0,0);
	sem_init(&sem_audiostreamwait,0,0);
	sem_init(&sem_playvideostreamwait,0,0);
	sem_init(&sem_playaudiostreamwait,0,0);
	init(&pmsgbuffer);//信令消息
	init(&pvideo_streambuffer);//视频流
	init(&paudio_streambuffer);//音频流
	init(&pplayvideo_streambuffer);//视频流
	init(&pplayaudio_streambuffer);//音频流
}
void closemsgqueue(void) {
    sem_destroy(&sem_answerwait);
    sem_destroy(&sem_offerwait);
	sem_destroy(&sem_videostreamwait);
	sem_destroy(&sem_audiostreamwait);
	sem_destroy(&sem_playvideostreamwait);
	sem_destroy(&sem_playaudiostreamwait);	
}
int sendkvsmsg(pMessage pmsg){
	put(&pmsgbuffer,pmsg);
	sem_post(&sem_offerwait);
	printf("sendkvsmsg %s\r\n",pmsg->Mode);
	return 0;
}
int receivekvsmsg(pMessage  *ppmsg){
	sem_wait(&sem_offerwait);
	*ppmsg = get(&pmsgbuffer);
	printf("receivekvsmsg %s\r\n",(*ppmsg)->Mode);
	return 0;
}

int sendkvsstream(ptstreamInfo pstream){
	switch(pstream->streamtype){
		case STREAM_VIDEO:
		    // sem_wait(&sem_videostreamwait);
			putstream(&pvideo_streambuffer,pstream);
			sem_post(&sem_videostreamwait);
			// printf("sendkvsstream %d-> %d\r\n",pstream->streamtype,pstream->size);
		break;
        case STREAM_AUDIO:
			putstream(&paudio_streambuffer,pstream);
			sem_post(&sem_audiostreamwait);
			// printf("sendkvsstream %d-> %d\r\n",pstream->streamtype,pstream->size);
		break;
		default:
		break;
		}       
	return 0;
}
int receivekvsvideostream(ptstreamInfo  *pstream){
	sem_wait(&sem_videostreamwait);
	*pstream = getstream(&pvideo_streambuffer);
	// sem_post(&sem_videostreamwait);
	// printf("receivekvsvideostream %d->%d\r\n",(*pstream)->streamtype,(*pstream)->size);
	return 0;
}
int receivekvsaudiostream(ptstreamInfo  *pstream){
	sem_wait(&sem_audiostreamwait);
	*pstream = getstream(&paudio_streambuffer);
	// printf("receivekvsaudiostream %d->%d\r\n",(*pstream)->streamtype,(*pstream)->size);
	return 0;
}
//play
int sendkvsplaystream(ptstreamInfo pstream){
	switch(pstream->streamtype){
		case STREAM_VIDEO:
		    // sem_wait(&sem_videostreamwait);
			putstream(&pplayvideo_streambuffer,pstream);
			sem_post(&sem_playvideostreamwait);
			// printf("sendkvsstream %d-> %d\r\n",pstream->streamtype,pstream->size);
		break;
        case STREAM_AUDIO:
			putstream(&pplayaudio_streambuffer,pstream);
			sem_post(&sem_playaudiostreamwait);
			// printf("sendkvsstream %d-> %d\r\n",pstream->streamtype,pstream->size);
		break;
		default:
		break;
		}       
	return 0;
}
int receivekvspalyvideostream(ptstreamInfo  *pstream){
	sem_wait(&sem_playvideostreamwait);
	*pstream = getstream(&pplayvideo_streambuffer);
	// sem_post(&sem_videostreamwait);
	// printf("receivekvsvideostream %d->%d\r\n",(*pstream)->streamtype,(*pstream)->size);
	return 0;
}
int receivekvsplayaudiostream(ptstreamInfo  *pstream){
	sem_wait(&sem_playaudiostreamwait);
	*pstream = getstream(&pplayaudio_streambuffer);
	// printf("receivekvsaudiostream %d->%d\r\n",(*pstream)->streamtype,(*pstream)->size);
	return 0;
}

<think>好的,我现在需要帮用户介绍实时监控系统中从相机传感器到嵌入式芯片再到PC端的软硬件架构和协议连接关系。首先,我得理清楚整个系统的各个组成部分以及它们之间的连接方式。 用户可能是在做项目或者学习相关知识,需要了解整个监控系统的架构。他们可能对嵌入式系统和数据传输协议不太熟悉,所以需要详细但易懂的解释。用户可能还关心实际应用中的技术选型,比如常用的硬件模块、通信协议以及软件框架。 首先,我需要从硬件架构开始,分三个部分:相机传感器端、嵌入式芯片端和PC端。相机传感器部分需要提到常见的传感器类型,比如CMOS或CCD,以及接口类型如MIPI、USB3.0等。还要说明传感器如何将光信号转换为数字信号。 接下来是嵌入式芯片端,这部分可能包括处理器(如ARM Cortex-A系列)、FPGA或ASIC,负责图像处理、编码和压缩。需要提到具体的芯片例子,比如Nvidia Jetson或树莓派,以及它们的作用,比如运行Linux或RTOS。 然后是PC端,主要涉及数据接收、存储和分析,可能需要提到GPU加速处理或云服务器连接。硬件接口方面,USB、以太网、Wi-Fi等连接方式需要说明。 软件架构部分,同样分为三个层级。相机传感器端的固件,比如寄存器配置和DMA传输。嵌入式端的驱动层、中间件和应用层,比如使用GStreamer或OpenCV进行图像处理,以及RTSP/ONVIF协议的应用。 通信协议是关键部分,需要详细说明各层之间的协议,比如传感器与嵌入式芯片之间的MIPI CSI-2、USB UVC,嵌入式与PC之间的RTSP、HTTP/HTTPS、WebSocket,以及传输层的TCP/UDP和视频编码标准如H.264/H.265。 数据流向方面,从采集到处理再到传输和存储,每个步骤需要简明扼要。最后,设计考量需要提到实时性、带宽、安全性和扩展性,可能用户在实际应用中会遇到这些问题。 还要考虑用户可能的深层需求,比如系统的实时性如何保证,如何选择适合的协议,或者如何处理高带宽的视频数据。可能需要给出一些典型方案,比如使用RTSP over TCP在局域网,或者WebRTC在互联网传输。 需要避免使用过于专业的术语,或者在必要时进行解释,确保用户能够理解。同时,结构要清晰,分点说明,让用户能逐步了解整个架构。最后,检查是否有遗漏的部分,比如时间同步、错误处理等,但可能不需要太深入,保持概述级别。</think>以下是一个典型的实时监控系统架构说明,涵盖从相机传感器到嵌入式芯片再到PC端的完整技术链路: --- ### **一、硬件架构** #### **1. 相机传感器端** - **核心组件**: - **图像传感器**:CMOS/CCD(如索尼IMX系列) - **接口电路**:MIPI CSI-2、LVDS、USB3.0(用于传输原始图像数据) - **ISP(图像信号处理器)**:负责去马赛克、降噪、自动曝光等预处理 #### **2. 嵌入式芯片端** - **核心硬件**: - **主控芯片**:ARM Cortex-A系列(如NXP i.MX8)、FPGA(如Xilinx Zynq)或专用SoC(如海思Hi3559A) - **协处理器**:GPU/NPU(用于AI推理,如Nvidia Jetson的Tegra芯片) - **存储单元**:DDR4内存 + eMMC闪存(缓存实时数据) - **通信模块**:以太网PHY芯片、Wi-Fi/BLE模块(如ESP32) #### **3. PC/服务器端** - **硬件组成**: - **数据接收**:千兆/万兆网卡(如Intel X550-T2) - **存储系统**:RAID阵列或NVMe SSD(用于视频存档) - **处理单元**:多核CPU(如Intel Xeon) + GPU(如Nvidia RTX A6000) --- ### **二、软件架构** #### **1. 相机传感器端** - **固件层**: - 寄存器配置(通过I2C/SPI总线控制传感器参数) - DMA传输驱动(直接内存访问降低延迟) - **协议栈**:MIPI CSI-2协议包封装(包含图像数据和同步信号) #### **2. 嵌入式芯片端** - **驱动层**: - V4L2(Video4Linux)框架(管理摄像头输入) - 网络驱动(如Linux的NetworkManager) - **中间件**: - GStreamer/FFmpeg(视频流处理管道) - OpenCV/TensorFlow Lite(实时分析算法) - **应用层**: - RTSP/ONVIF服务(流媒体协议支持) - 边缘计算(如人脸检测、运动跟踪) #### **3. PC/服务器端** - **服务软件**: - NVR系统(如Blue Iris、Milestone XProtect) - 视频分析平台(如DeepStream、OpenALPR) - **通信协议**: - WebSocket(用于控制指令下发) - RTMP/HLS(互联网流媒体分发) --- ### **三、协议连接关系** #### **1. 传感器 ↔ 嵌入式芯片** - **物理层**:MIPI CSI-2(短距高速,带宽可达4Gbps) - **控制协议**:I2C(配置传感器参数如分辨率、帧率) #### **2. 嵌入式芯片 ↔ PC/服务器** - **传输协议**: - **实时流媒体**:RTSP over TCP/UDP(端口554) - **控制指令**:HTTP/HTTPS(RESTful API)或MQTT(IoT场景) - **文件传输**:FTP/SFTP(录像文件回传) - **封装格式**: - RTP + H.264/H.265(视频编码) - AAC/Opus(音频同步) #### **3. 扩展连接** - **云服务**:WebRTC(浏览器直接播放) - **设备互联**:ONVIF Profile S(跨厂商兼容) --- ### **四、典型数据流向** 1. **数据采集** `CMOS传感器 → MIPI CSI-2 → ISP预处理 → DMA传输到内存` 2. **嵌入式处理** `V4L2捕获 → H.264编码 → RTSP流封装 → 通过TCP/IP发送` 3. **PC端处理** `网卡接收 → FFmpeg解码 → AI分析 → 存储/告警/可视化` --- ### **五、关键设计考量** 1. **实时性**: - 嵌入式端需保证帧处理延迟 < 100ms(通过硬件编码加速) - 使用PREEMPT-RT内核补丁(Linux实时性优化) 2. **带宽优化**: - 动态码率控制(如CBR/VBR) - ROI编码(关注关键区域) 3. **安全性**: - SRTP加密视频流 - TLS 1.3保护控制通道 4. **扩展性**: - Docker容器化部署分析服务 - Kafka消息队列(高并发数据处理) --- ### **典型方案示例** ``` [传感器]IMX415 → [接口]MIPI CSI-2 → [嵌入式]Jetson Nano → [协议]RTSP over UDP ↓ ↓ [配置I2C] [分析:YOLOv5] ↓ [PC]接收端(VLC/FFplay) ← [网络]千兆以太网 ``` 如需更具体的实现细节(如GStreamer管道配置或低延迟优化技巧),可进一步说明需求!
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值