1. 嵌入式平台live555交叉编译实战
在嵌入式Linux环境下编译live555,最让人头疼的就是交叉编译工具链的配置和依赖库的链接问题。我当年第一次在ARM平台编译live555时,整整折腾了两天,踩了不少坑。这里分享一些实战经验,帮你快速绕过这些坑。
先下载最新的live555源码包,解压后进入目录。关键步骤是配置交叉编译环境,官方提供了一个config.armlinux模板,我们可以复制一份进行修改:
tar -xzvf live.2024.04.19.tar.gz
cd live
cp config.armlinux config.myarmlinux
编辑config.myarmlinux文件,重点修改以下几个参数:
CROSS_COMPILE?= /your/toolchain/path/arm-linux-gnueabi-
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -DNO_SSTREAM=1
C_COMPILER = $(CROSS_COMPILE)gcc
CPLUSPLUS_COMPILER = $(CROSS_COMPILE)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 -Wno-deprecated -std=c++11
LINK = $(CROSS_COMPILE)g++ -o
PREFIX = /your/install/path
这里有几个关键点需要注意:-std=c++11选项必须加上,因为新版本的live555使用了C++11特性。如果遇到std::atomic_flag没有test方法的错误,需要修改为test_and_set()。
生成Makefile并编译:
./genMakefiles myarmlinux
make -j4
编译完成后经常遇到"undefined reference"错误,这是因为库链接顺序不对。正确的链接顺序应该是:libliveMedia.a、libBasicUsageEnvironment.a、libgroupsock.a、libUsageEnvironment.a。确保libliveMedia.a放在第一位。
2. 多路视频流性能优化策略
在安防监控或无人机图传场景中,经常需要同时处理多路视频流。这时候性能优化就特别重要了。我做过一个8路D1视频流的项目,最初CPU占用率高达60%,经过优化后降到了30%以下。
双队列缓冲设计是关键优化点。原来的单队列设计存在视频帧和音频帧混合的问题,当服务器需要视频数据时,如果队列尾部是音频帧,就会导致取不到数据而等待。我们改为音视频分离的双队列:
typedef struct {
unsigned char* video_buffer[CHANNEL_NUM];
unsigned char* audio_buffer[CHANNEL_NUM];
int video_front[CHANNEL_NUM], video_rear[CHANNEL_NUM];
int audio_front[CHANNEL_NUM], audio_rear[CHANNEL_NUM];
} DualRingBuffer;
这样设计后,视频流和音频流互不干扰,大大减少了队列满的情况。在实际测试中,8路全开运行两天都没有出现队列满的问题。
内存零拷贝优化是另一个重点。原来的流程中有多次内存拷贝:
- SNDeviceSource到H264VideoStreamDiscreteFramer一次拷贝
- H264FUAFragmenter到H264VideoRTPSink又一次拷贝
我们通过传递指针而不是拷贝数据来实现零拷贝:
void SNDeviceSource::doGetNextFrame() {
// 原来需要memcpy(fTo, fVideoFrame, fFrameSize);
// 改为直接传递指针
fTo = fVideoFrame;
fFrameSize = fVideoFrameSize;
afterGetting(this);
}
同时修改MultiFramedRTPSink中的packFrame和afterGettingFrame1函数,使用iovec结构体来管理分散的内存块:
struct iovec fVecBuf[6];
fVecBuf[3].iov_base = rtpHeader;
fVecBuf[4].iov_base = fragmentHeader;
fVecBuf[5].iov_base = frameData;
对于TCP传输,直接使用writev系统调用发送分散的内存块。对于UDP,由于需要connect到特定对端,我们仍然需要将数据拷贝到连续内存中再用sendto发送。
3. 实时流媒体服务器定制开发
live555自带的testH264VideoStreamer示例只能播放文件,我们需要将其改造为支持实时视频流的服务器。核心是继承并重写关键类。
首先创建自定义的FramedSource子类:
class LiveCameraSource : public FramedSource {
public:
static LiveCameraSource* createNew(UsageEnvironment& env, int cameraId);
protected:
LiveCameraSource(UsageEnvironment& env, int cameraId);
virtual ~LiveCameraSource();
private:
virtual void doGetNextFrame();
static void deliverFrameStub(void* clientData);
void deliverFrame();
private:
int fCameraId;
unsigned char* fBuffer;
};
在doGetNextFrame中实现从摄像头获取数据:
void LiveCameraSource::doGetNextFrame() {
// 从摄像头驱动获取一帧数据
int frameSize = get_camera_frame(fCameraId, fBuffer, fMaxSize);
if (frameSize > 0) {
fFrameSize = frameSize;
fTo = fBuffer; // 零拷贝传递指针
afterGetting(this);
} else {
handleClosure();
}
}
然后创建自定义的ServerMediaSubsession:
class LiveCameraMediaSubsession : public OnDemandServerMediaSubsession {
public:
static LiveCameraMediaSubsession* createNew(UsageEnvironment& env, int cameraId);
protected:
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
};
最后在主函数中创建RTSP服务器并添加会话:
RTSPServer* rtspServer = RTSPServer::createNew(env, 8554);
ServerMediaSession* sms = ServerMediaSession::createNew(env, "live", "Live Camera", "Live stream");
sms->addSubsession(LiveCameraMediaSubsession::createNew(env, 0));
rtspServer->addServerMediaSession(sms);
4. 实战问题排查与性能调优
在实际部署中,经常会遇到各种问题。比如客户端连接数多了之后性能下降,或者出现内存泄漏。这里分享几个排查技巧。
CPU占用率过高的排查:使用top命令查看CPU占用情况,如果发现CPU占用过高,可以使用perf工具进行性能分析:
perf top -p <pid>
perf record -p <pid> -g
perf report
常见的性能瓶颈可能在内存拷贝、锁竞争或者频繁的系统调用上。通过perf可以快速定位到热点函数。
内存泄漏的排查:使用valgrind工具检测内存泄漏:
valgrind –leak-check=full –show-leak-kinds=all ./your_program
在live555中,需要特别注意Medium对象的引用计数,确保正确调用close()方法。
网络性能优化:对于高并发的场景,可以调整内核网络参数:
echo 1024 > /proc/sys/net/core/somaxconn
echo 65535 > /proc/sys/net/ipv4/ip_local_port_range
还可以设置TCP_NODELAY选项减少延迟:
int flag = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
多路流负载均衡:当处理多路视频流时,可以考虑使用多个线程或进程来处理不同的流。虽然live555是单线程设计,但可以在数据源层面做并行化:
void* video_capture_thread(void* arg) {
int camera_id = *(int*)arg;
while (1) {
Frame frame = capture_frame(camera_id);
push_to_queue(camera_id, frame);
notify_new_frame(camera_id);
}
}
每个摄像头一个采集线程,然后将数据推送到对应的队列中,由live555的主线程来取数据。这样可以利用多核CPU的优势。
经过这些优化后,在嵌入式平台上处理8路D1视频流,CPU占用率可以控制在30%左右,完全满足实时性要求。记得在正式部署前进行长时间的压力测试,确保系统的稳定性。
网硕互联帮助中心

评论前必须登录!
注册