当前位置: 首页 > news >正文

移植live555 上的 rtsp

一、V4L2视频采集模块(完整示例)

#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <fcntl.h>// 初始化V4L2摄像头
int init_v4l2_camera(const char* dev_path, int width, int height) {int fd = open(dev_path, O_RDWR);if (fd < 0) {perror("Failed to open V4L2 device");return -1;}// 设置视频格式(YUV422)struct v4l2_format fmt = {0};fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = width;fmt.fmt.pix.height = height;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 海思可能要求NV12fmt.fmt.pix.field = V4L2_FIELD_NONE;if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {perror("Failed to set video format");close(fd);return -1;}// 申请视频缓冲区(MMAP方式)struct v4l2_requestbuffers req = {0};req.count = 4;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {perror("Failed to request buffers");close(fd);return -1;}// 映射缓冲区到用户空间struct v4l2_buffer buf = {0};for (int i = 0; i < req.count; ++i) {buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {perror("Failed to query buffer");close(fd);return -1;}void* buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);// 保存buffer指针...}// 启动视频流enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) {perror("Failed to start streaming");close(fd);return -1;}return fd; // 返回设备句柄
}

关键点解释

  • 海思平台可能要求输入NV12格式,需根据硬件文档调整V4L2_PIX_FMT_*
  • MMAP方式减少内存拷贝,适合高帧率场景
  • 需循环调用VIDIOC_QBUFVIDIOC_DQBUF获取视频帧

二、海思H.264硬件编码模块

#include "hi_comm_venc.h"
#include "mpi_venc.h"// 初始化海思编码通道
HI_S32 init_hisi_venc(int width, int height, int fps) {HI_S32 s32Ret;VENC_CHN VencChn = 0;VENC_ATTR_S stVencAttr;// 配置编码参数memset(&stVencAttr, 0, sizeof(VENC_ATTR_S));stVencAttr.enType = PT_H264;stVencAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;stVencAttr.stRcAttr.stH264Cbr.u32Gop = fps * 2; // GOP为2秒stVencAttr.stRcAttr.stH264Cbr.u32BitRate = 2000; // 2000kbpsstVencAttr.stVencAttr.stAttrH264e.u32MaxPicWidth = width;stVencAttr.stVencAttr.stAttrH264e.u32MaxPicHeight = height;stVencAttr.stVencAttr.stAttrH264e.u32PicWidth = width;stVencAttr.stVencAttr.stAttrH264e.u32PicHeight = height;// 创建编码通道s32Ret = HI_MPI_VENC_CreateChn(VencChn, &stVencAttr);if (s32Ret != HI_SUCCESS) {printf("Create VENC channel failed: 0x%x\n", s32Ret);return s32Ret;}// 设置帧率VENC_PARAM_FRMRATE_PARA_S stFrmRate;stFrmRate.stSrcFrmRate.s32FrmRate = fps * 1000; // 以毫秒为单位stFrmRate.stDstFrmRate.s32FrmRate = fps * 1000;HI_MPI_VENC_SetFrmRate(VencChn, &stFrmRate);return HI_SUCCESS;
}// 获取编码后的H.264流
void get_h264_stream(VENC_CHN VencChn, unsigned char** ppBuffer, HI_U32* pSize) {VENC_STREAM_S stStream;HI_S32 s32Ret = HI_MPI_VENC_GetStream(VencChn, &stStream, 2000); // 2秒超时if (s32Ret == HI_SUCCESS) {*ppBuffer = stStream.pstPack[0].pu8Addr;*pSize = stStream.pstPack[0].u32Len;HI_MPI_VENC_ReleaseStream(VencChn, &stStream); // 必须释放}
}

关键点

  • 使用HI_MPI_VENC_GetStream非阻塞方式获取码流
  • 必须调用HI_MPI_VENC_ReleaseStream释放资源,否则会导致内存泄漏
  • 海思SDK要求严格的内存对齐,需按照文档配置参数

三、ALSA音频采集与AAC编码

#include <alsa/asoundlib.h>
#include <fdk-aac/aacenc_lib.h>// 初始化ALSA音频采集
snd_pcm_t* init_alsa(const char* device, int sample_rate, int channels) {snd_pcm_t* handle;snd_pcm_hw_params_t* params;if (snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0) < 0) {fprintf(stderr, "ALSA open error\n");return NULL;}snd_pcm_hw_params_malloc(&params);snd_pcm_hw_params_any(handle, params);// 设置参数snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);snd_pcm_hw_params_set_channels(handle, params, channels);snd_pcm_hw_params_set_rate_near(handle, params, (unsigned int*)&sample_rate, 0);// 应用参数if (snd_pcm_hw_params(handle, params) < 0) {fprintf(stderr, "ALSA set params failed\n");snd_pcm_close(handle);return NULL;}return handle;
}// 初始化AAC编码器
AACENCODER* init_aac_encoder(int sample_rate, int channels) {AACENCODER* hEncoder;if (aacEncOpen(&hEncoder, 0, channels) != AACENC_OK) {return NULL;}// 设置编码参数aacEncoder_SetParam(hEncoder, AACENC_AOT, AOT_AAC_LC);aacEncoder_SetParam(hEncoder, AACENC_SAMPLERATE, sample_rate);aacEncoder_SetParam(hEncoder, AACENC_CHANNELMODE, MODE_2);aacEncoder_SetParam(hEncoder, AACENC_BITRATE, 64000); // 64kbpsif (aacEncEncode(hEncoder, NULL, NULL, NULL, NULL) != AACENC_OK) {aacEncClose(&hEncoder);return NULL;}return hEncoder;
}

四、live555自定义FramedSource实现

#include <liveMedia.hh>class Hi3516VideoSource : public FramedSource {
public:static Hi3516VideoSource* createNew(UsageEnvironment& env, VENC_CHN vencChn) {return new Hi3516VideoSource(env, vencChn);}protected:Hi3516VideoSource(UsageEnvironment& env, VENC_CHN vencChn) : FramedSource(env), mVencChn(vencChn) {}virtual void doGetNextFrame() {// 从海思编码器获取H.264数据unsigned char* pData = nullptr;HI_U32 dataSize = 0;get_h264_stream(mVencChn, &pData, &dataSize);if (dataSize > 0) {if (dataSize > fMaxSize) { // 处理分包fNumTruncatedBytes = dataSize - fMaxSize;dataSize = fMaxSize;}memcpy(fTo, pData, dataSize);fFrameSize = dataSize;// 设置时间戳(使用系统时钟)gettimeofday(&fPresentationTime, NULL);} else {fFrameSize = 0;}// 立即通知框架可以发送数据afterGetting(this);}private:VENC_CHN mVencChn;
};

关键逻辑

  • doGetNextFrame()是live555的核心回调函数,当需要发送数据时自动触发
  • 时间戳必须转换为RTP时间基准(视频:90000Hz,音频:采样率)
  • 若数据超过缓冲区大小,需设置fNumTruncatedBytes

五、RTSP服务端搭建

#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"void start_rtsp_server() {TaskScheduler* scheduler = BasicTaskScheduler::createNew();UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);// 创建RTSP服务器(端口554需要root权限)RTSPServer* rtspServer = RTSPServer::createNew(*env, 554, NULL);if (rtspServer == NULL) {*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";exit(1);}// 创建媒体会话(H.264 + AAC)ServerMediaSession* sms = ServerMediaSession::createNew(*env, "live", "Hi3516 Live Stream", "Session for Hi3516");// 添加视频子会话sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, "dummy", NULL)); // 实际应使用自定义Source// 添加音频子会话(AAC)sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(*env, "dummy", NULL));rtspServer->addServerMediaSession(sms);// 打印RTSP URLchar* url = rtspServer->rtspURL(sms);*env << "RTSP URL: " << url << "\n";delete[] url;env->taskScheduler().doEventLoop(); // 进入主循环
}

六、音视频同步策略

// 全局时间戳生成器(单位:微秒)
uint64_t get_ntp_timestamp() {struct timeval tv;gettimeofday(&tv, NULL);return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
}// 在FramedSource中设置时间戳
void Hi3516VideoSource::doGetNextFrame() {// ... 获取数据 ...uint64_t now = get_ntp_timestamp();fPresentationTime.tv_sec = now / 1000000;fPresentationTime.tv_usec = now % 1000000;
}// 音频同步需转换为采样数
uint32_t audio_rtp_timestamp(uint64_t ntp_ts, int sample_rate) {return (uint32_t)((ntp_ts * sample_rate) / 1000000);
}

七、完整流程整合

  1. 初始化硬件模块

    int v4l2_fd = init_v4l2_camera("/dev/video0", 1280, 720);
    init_hisi_venc(1280, 720, 30);
    snd_pcm_t* audio_handle = init_alsa("default", 44100, 2);
    AACENCODER* aac_enc = init_aac_encoder(44100, 2);
    
  2. 启动RTSP服务线程

    std::thread rtsp_thread(start_rtsp_server);
    rtsp_thread.detach();
    
  3. 主循环采集数据

    while (1) {// 视频采集->编码->存入队列struct v4l2_buffer buf = {0};ioctl(v4l2_fd, VIDIOC_DQBUF, &buf);HI_MPI_VENC_SendFrame(vencChn, &raw_frame); // 送入编码器ioctl(v4l2_fd, VIDIOC_QBUF, &buf);// 音频采集->编码->存入队列snd_pcm_readi(audio_handle, pcm_buffer, samples);aacEncEncode(aac_enc, ...);
    }
    

八、调试技巧

  1. 验证视频编码

    # 将H.264流保存为文件
    ./your_program > test.h264
    # 使用ffplay播放
    ffplay -f h264 test.h264
    
  2. 分析RTSP协议

    wireshark -f "tcp port 554 or udp port range 6970-6999"
    
  3. 海思SDK调试

    HI_MPI_LOG_SetLevel(LOG_LEVEL_DEBUG); // 开启海思SDK日志
    

通过以上完整实现,您可以在hi3516平台上构建稳定的RTSP音视频服务。实际部署时需根据硬件特性调整缓冲区大小、线程优先级等参数。

相关文章:

移植live555 上的 rtsp

一、V4L2视频采集模块&#xff08;完整示例&#xff09; #include <linux/videodev2.h> #include <sys/ioctl.h> #include <fcntl.h>// 初始化V4L2摄像头 int init_v4l2_camera(const char* dev_path, int width, int height) {int fd open(dev_path, O_RD…...

Web Worker终极优化指南:4秒卡顿→0延迟的实战蜕变

&#x1f4a1; 导读&#xff1a;从4秒卡顿到丝滑响应 真实痛点场景&#xff1a;当斐波那契数列计算量达10亿次时&#xff0c;页面完全冻结4.2秒&#xff01;通过Web Worker优化后&#xff0c;UI响应时间降至16ms以内。本文手把手带您实现性能蜕变&#xff01; 一、Web Worker核…...

redis中的Lua脚本,redis的事务机制

lua脚本的特点 lua脚本可以操作redis数据库&#xff0c;并且脚本中的代码满足原子性&#xff0c;要么全部被执行&#xff0c;要么全部不执行 lua脚本的语法 脚本示例 lua脚本的草稿&#xff1a; 最终的lua脚本 lua脚本在java里调用的方法 RedisTemplete类里有一个方法&…...

CPU多级缓存与缓存一致性协议

CPU多级缓存与缓存一致性协议 CPU多级缓存和缓存一致性协议是计算机体系结构中优化性能与保证数据正确性的核心机制。以下从缓存层级设计、工作原理、一致性协议&#xff08;如MESI&#xff09;及其实现细节展开说明。 一、为什么需要多级缓存&#xff1f; CPU的计算速度远高…...

Apifox 增强 AI 接口调试功能:自动合并 SSE 响应、展示DeepSeek思考过程

在现代的API接口调试中&#xff0c;效率和精确性对于开发者和测试人员来说至关重要。Apifox&#xff0c;作为一款功能强大的API管理和调试工具&#xff0c;近年来不断提升其用户体验和智能化功能。最近&#xff0c;Apifox 推出了增强版的AI接口调试功能&#xff0c;其中包括自动…...

【电机控制】42步进电机+arduino:WHEELTEC_MS42DDC

轮趣科技 42步进电机arduino:WHEELTEC_MS42DDC 接线方式&#xff1a; WHEELTEC_MS42DDC有两个接口&#xff0c; 一端接口连接配套的DC电源&#xff0c;另外一端只需要用三根线&#xff0c;一根负极连接ardino 的GND&#xff0c;然后把该端口的tx和rx连接到arduino的rx和tx,下…...

使用LangChain构建第一个ReAct Agent

使用LangChain构建第一个ReAct Agent 准备环境 使用Anaconda 安装python 3.10 安装langchain、langchain_openai、langchain_community &#xff08;安装命令 pip install XXX&#xff09; 申请DeepSeek API&#xff1a;https://platform.deepseek.com/api_keys&#xff08;也…...

萝卜头笔作文赏析

在遥远的无寻王国&#xff0c;有这么一支小小的笔诞生了&#xff0c;人们见它又短又小&#xff0c;于是就给它取名叫萝卜头笔。萝卜头笔渐渐长大了&#xff0c;除了身子变粗些&#xff0c;其他什么都没变。一天&#xff0c;萝卜头笔来到了深山老林&#xff0c;那里枝叶繁茂&…...

RT-Thread+STM32L475VET6——USB鼠标模拟

文章目录 前言一、板载资源二、具体步骤1.配置icm20608传感器2.打开CubeMX进行USB配置3. 配置USB3.1 打开USB驱动3.2 声明USB3.3 剪切stm32xxxx_hal_msp.c中的void HAL_PCD_MspInit(PCD_HandleTypeDef* hpcd)和void HAL_PCD_MspDeInit(PCD_HandleTypeDef* hpcd)函数至board.c3.…...

rust 安全性

Rust 是 静态类型&#xff08;statically typed&#xff09; 语言&#xff0c; 也就是说在编译时就必须知道所有变量的类型&#xff0c; 这一点将贯穿整个章节。 C/C的安全问题 内存的不正确访问引发的内存安全问题 由于多个变量指向同一块内存区域导致的数据一致性问题 由于…...

大模型驱动的围术期质控系统全面解析与应用探索

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与方法 1.3 研究创新点 二、大模型技术与围术期管理概述 2.1 大模型技术原理与发展现状 2.2 围术期管理流程与挑战 三、大模型在术前的应用 3.1 病历内涵质控 3.2 智能医学问答与知识查询 3.3 疾病风险预测与评估 3.…...

中兴B863AV3.2-T/B863AV3.1-T2/B863AV3.1-T2K_电信高安_S905L3A-B_安卓9.0_线刷固件包

中兴B863AV3.2-T&#xff0f;B863AV3.1-T2&#xff0f;B863AV3.1-T2K_电信高安_S905L3A-B_安卓9.0_线刷固件包 B863AV3.2-T B863AV3.1-T2 已知可通刷贵州、江苏、贵州、北京、河南、陕西等省份。 线刷方法&#xff1a;&#xff08;新手参考借鉴一下&#xff09; 1、准备好一…...

Android Binder机制

Binder是IPC&#xff08;进程间通信&#xff09;的一种机制&#xff0c;它允许不同的应用或系统服务在不同的进程中安全地交换数据。Binder的核心原理是基于客户端-服务器模型&#xff08;C/S架构)。 一、Binder的定义 1. Binder是Android中的一个类&#xff0c;它继承了IBind…...

【算法】初等数论

初等数论 模 取余&#xff0c;遵循尽可能让商向0靠近的原则&#xff0c;结果的正负和左操作数相同 取模&#xff0c;遵循尽可能让商向负无穷靠近的原则&#xff0c;结果的正负和右操作数相同 7/&#xff08;-3&#xff09;-2.3&#xff0c;产生了两个商-2和-3&#xff0c;取…...

Spring Boot3+Vue2极速整合:10分钟搭建DeepSeek AI对话系统

前言 在生成式AI技术蓬勃发展的今天&#xff0c;大语言模型已成为企业智能化转型和个人效率提升的核心驱动力。作为国产大模型的优秀代表&#xff0c;DeepSeek凭借其卓越的中文语义理解能力和开发者友好的API生态&#xff0c;正在成为构建本土化AI应用的首选平台。 本文将以S…...

Spring事务原理 二

在上一篇博文《Spring事务原理 一》中&#xff0c;我们熟悉了Spring声明式事务的AOP原理&#xff0c;以及事务执行的大体流程。 本文中&#xff0c;介绍了Spring事务的核心组件、传播行为的源码实现。下一篇中&#xff0c;我们将结合案例&#xff0c;来讲解实战中有关事务的易…...

JVM预热

阿里电商平台每年的各种大促活动&#xff0c;对于Java技术来说&#xff0c;其中重要一个操作环节就是预热操作。 目录 预热是什么&#xff1f;为什么要预热&#xff1f; java 程序不预热和预热的调用对比 预热是什么&#xff1f; 预热是指&#xff0c;在 JVM 启动后&#xff0…...

基于flask+vue框架的的医院预约挂号系统i1616(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能:用户,医生,科室信息,就诊信息,医院概况,挂号信息,诊断信息,取消挂号 开题报告内容 基于FlaskVue框架的医院预约挂号系统开题报告 一、研究背景与意义 随着医疗技术的不断进步和人们健康意识的日益增强&#xff0c;医院就诊量逐年增加。传统的现场…...

DeepSeek掘金——SpringBoot 调用 DeepSeek API 快速实现应用开发

Spring Boot 实现 DeepSeek API 调用 1. 项目依赖 在 pom.xml 中添加以下依赖: <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>&l…...

easelog(1)基础C++日志功能实现

EaseLog(1)基础C日志功能实现 Author: Once Day Date: 2025年2月22日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 注&#xff1a;本简易日志组件代码实现参考了Google …...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

JavaScript 数据类型详解

JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型&#xff08;Primitive&#xff09; 和 对象类型&#xff08;Object&#xff09; 两大类&#xff0c;共 8 种&#xff08;ES11&#xff09;&#xff1a; 一、原始类型&#xff08;7种&#xff09; 1. undefined 定…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

【WebSocket】SpringBoot项目中使用WebSocket

1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖&#xff0c;添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案

引言 在分布式系统的事务处理中&#xff0c;如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议&#xff08;2PC&#xff09;通过准备阶段与提交阶段的协调机制&#xff0c;以同步决策模式确保事务原子性。其改进版本三阶段提交协议&#xff08;3PC&#xf…...