订阅Apollo摄像头NPP颜色空间转换NVMPI硬编码输出RTSP服务
订阅Apollo摄像头NPP颜色空间转换NVMPI硬编码输出RTSP服务
-
- 一、技术解析
-
-
- 1. Apollo摄像头数据订阅
- 2. NPP颜色空间转换
- 3. NVMPI硬件编码
- 4. ZLMediaKit流媒体服务器
-
- 二、详细实现步骤
-
- 1、环境准备与依赖编译
-
- 1.1、项目目录结构创建
- 1.2、FFmpeg定制编译(支持NVMPI)
- 1.3、配置支持硬件编码的FFmpeg
- 1.4、编译ZLMediaKit流媒体服务
- 2、代码实现解析
- 3、编译配置说明
- 4、运行与测试
-
- 4.1、准备测试数据
- 4.2、启动流媒体服务
- 4.3、验证视频流
- 三、性能优化要点
-
- 1. 内存管理
- 2. GPU利用率
- 3. 编码效率
- 四、应用场景
- 五、故障排除
-
- 1、常见问题
- 2、调试技巧
本文介绍了如何在Apollo自动驾驶平台上,通过订阅摄像头数据,利用NVIDIA硬件加速技术实现高效的视频流处理,并构建RTSP流媒体服务器的完整解决方案。
一、技术解析
1. Apollo摄像头数据订阅
- 功能:实时获取自动驾驶车辆摄像头采集的RGB图像数据
- 数据源:Apollo Cyber RT框架中的传感器消息总线
- 订阅主题:
/apollo/sensor/camera/CAM_FRONT/image
和/apollo/sensor/camera/CAM_BACK/image
2. NPP颜色空间转换
- NPP (NVIDIA Performance Primitives):NVIDIA提供的GPU加速图像处理库
- 转换过程:RGB → YUV420P
- RGB格式:每个像素用红、绿、蓝三个分量表示
- YUV420P格式:亮度(Y)全分辨率,色度(U,V)半分辨率,适合视频编码
- 优势:GPU并行处理,相比CPU转换速度提升显著
3. NVMPI硬件编码
- NVMPI (NVIDIA Media Programming Interface):Jetson平台专用的硬件编解码接口
- 编码标准:H.264视频编码
- 优势:
- 专用硬件编码器,极低CPU占用
- 高编码效率,适合实时视频流
- 低功耗,适合嵌入式平台
4. ZLMediaKit流媒体服务器
- 功能:轻量级、高性能的流媒体服务器框架
- 协议支持:RTSP、RTMP、HLS等
- 输出:标准的RTSP视频流,可通过VLC等播放器观看
二、详细实现步骤
1、环境准备与依赖编译
1.1、项目目录结构创建
# 创建目录
mkdir apollo_rtsp_server
cd apollo_rtsp_server
1.2、FFmpeg定制编译(支持NVMPI)
# 获取FFmpeg源码(使用release/7.1分支)
git clone git://source.ffmpeg.org/ffmpeg.git -b release/7.1 --depth=1# 下载Jetson-FFmpeg补丁
git clone https://github.com/Keylost/jetson-ffmpeg# 应用补丁(使FFmpeg支持NVIDIA硬件编解码)
cd jetson-ffmpeg
git checkout f0a52dfae54bdb2d42b72064d8be3e6b2f66244b
./ffpatch.sh ../ffmpeg# 编译安装nvmpi(NVIDIA Media Programming Interface)
mkdir build
cd build
cmake ..
make
sudo make install
sudo ldconfig
1.3、配置支持硬件编码的FFmpeg
# 配置FFmpeg启用NVMPI
cd ../../ffmpeg/
./configure --enable-nvmpi --prefix=`pwd`/_install
make -j4
sudo make install
cd ..
1.4、编译ZLMediaKit流媒体服务
git clone https://github.com/ZLMediaKit/ZLMediaKit.git
cd ZLMediaKit
git checkout c82dd750548e136d80b08e57febea15ed62bc130
git submodule update --init --recursive
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=OFF -DENABLE_WEBRTC=OFF -DENABLE_SCTP=OFF ..
make -j8
cd ../../
2、代码实现解析
cat > main.cpp << 'EOF'
#include <npp.h>
#include <cuda_runtime.h>
#include <vector>
#include <atomic>
#include <csignal>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <thread>#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "mk_mediakit.h"extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
}#include <opencv2/opencv.hpp>
#include "cyber/cyber.h"
#include "modules/common_msgs/sensor_msgs/sensor_image.pb.h"#include <vpi/OpenCVInterop.hpp>
#include <vpi/Image.h>
#include <vpi/Status.h>
#include <vpi/Stream.h>
#include <vpi/algo/ConvertImageFormat.h>
#include <vpi/algo/Rescale.h>#include <cassert>
#include <cstring>
#include <iostream>
#include <sstream>#define CHECK_STATUS(STMT) \do \{ \VPIStatus status = (STMT); \if (status != VPI_SUCCESS) \{ \char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \vpiGetLastStatusMessage(buffer, sizeof(buffer)); \std::ostringstream ss; \ss << vpiStatusGetName(status) << ": " << buffer; \throw std::runtime_error(ss.str()); \} \} while (0);using apollo::cyber::Node;
using apollo::cyber::Reader;
using apollo::drivers::Image;#undef av_err2str
inline const char* av_err2str(int errnum) {thread_local char buf[AV_ERROR_MAX_STRING_SIZE];av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, errnum);return buf;
}class RtspStreamer {
public:RtspStreamer(int width = 1920, int height = 1080,int framerate = 10, int bitrate = 4000000);~RtspStreamer();bool StartStreaming(std::string &m_stream_name);bool WriteFrame(const cv::Mat& frame);void StopStreaming();bool IsStreaming() const;private:bool InitializeConverter();bool InitializeCodec();bool InitializeNPP();bool RGBToYUV420P_NPP(const cv::Mat& frame, AVFrame* yuv_frame);AVCodecContext* codec_context_;const AVCodec* codec_;AVFrame* yuv_frame_;AVPacket* packet_;// NPP资源 - 使用单个连续内存块Npp8u* d_rgb_;Npp8u* d_