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

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...