当前位置: 首页 > 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 …...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...