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

live555 rtsp server

编译加上 

-DALLOW_RTSP_SERVER_PORT_REUSE=1 -DNO_OPENSSL=1 -DNO_STD_LIB

rtsp server 代码


#include <string.h>
#include "stdlib.h"
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <queue>
#include <mutex>
#include <iostream>
#include <thread>
#include <cstring>
#include <atomic>
#include <fstream>
#include <condition_variable>
#include <vector>
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include <chrono>void play(); // forward
bool getSPSPPS(std::vector<uint8_t>& sps, std::vector<uint8_t>& pps); // forwardUsageEnvironment* env;
RTSPServer* rtspServer=nullptr;
std::atomic<char> exitLoop ;// 存储提取出的 SPS 和 PPS(只保存第一次)
struct H264ParameterSets {std::vector<uint8_t> sps;std::vector<uint8_t> pps;bool haveSPS = false;bool havePPS = false;
};// 全局或静态变量,用于保存 SPS/PPS
H264ParameterSets g_h264Params;class Frame {
public:std::vector<unsigned char> data;unsigned long long timestamp; // microsecondsFrame(const std::vector<unsigned char>& d, unsigned long long ts) : data(d), timestamp(ts) {}
};class SharedBuffer {
public:void pushFrame(const std::vector<unsigned char>& frameData, unsigned long long timestamp);bool getFrame(std::vector<unsigned char>& frameData, unsigned long long& timestamp);void signalEnd(); // 通知结束private:std::queue<Frame> buffer;std::mutex mtx;std::condition_variable cv;bool endSignal = false;
};SharedBuffer gSharedBuffer;void SharedBuffer::pushFrame(const std::vector<unsigned char>& frameData, unsigned long long timestamp) {std::lock_guard<std::mutex> lock(mtx);buffer.push(Frame(frameData, timestamp));cv.notify_one();
}bool SharedBuffer::getFrame(std::vector<unsigned char>& frameData, unsigned long long& timestamp) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, [this] { return !buffer.empty() || endSignal; });if (buffer.empty()) return false;Frame f = buffer.front(); buffer.pop();frameData = f.data;timestamp = f.timestamp;return true;
}void SharedBuffer::signalEnd() {std::lock_guard<std::mutex> lock(mtx);endSignal = true;cv.notify_all();
}// 自定义数据源类:从 SharedBuffer 读取
class H264LiveBufferSource : public FramedSource {
public:static H264LiveBufferSource* createNew(UsageEnvironment& env) {return new H264LiveBufferSource(env);}void doStopGettingFrames() override {// 可选:停止标志FramedSource::doStopGettingFrames();}protected:H264LiveBufferSource(UsageEnvironment& env) : FramedSource(env) {}~H264LiveBufferSource() {}private:void doGetNextFrame() override {std::vector<unsigned char> frameData;unsigned long long timestamp;if (!gSharedBuffer.getFrame(frameData, timestamp)) {handleClosure(this);return;}// 复制数据到 live555 缓冲区if (frameData.size() > fMaxSize) {frameData.resize(fMaxSize); // 截断}memcpy(fTo, frameData.data(), frameData.size());fFrameSize = frameData.size();fNumTruncatedBytes = 0;fPresentationTime = timeval{ (long)(timestamp / 1000000), (long)(timestamp % 1000000) };fDurationInMicroseconds = 33333; // 30fps// 通知框架数据已准备好afterGetting(this);}
};class H264LiveServerMediaSubsession : public OnDemandServerMediaSubsession {
public:static H264LiveServerMediaSubsession* createNew(UsageEnvironment& env) {return new H264LiveServerMediaSubsession(env);}protected:H264LiveServerMediaSubsession(UsageEnvironment& env): OnDemandServerMediaSubsession(env, True) {}~H264LiveServerMediaSubsession() {}private:FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) override {estBitrate = 2000; // kbps//return H264LiveBufferSource::createNew(envir());// 1. 先创建你的原始 sourceFramedSource* innerSource = H264LiveBufferSource::createNew(envir());if (!innerSource) return nullptr;// 2. 用 H264VideoStreamDiscreteFramer 包装它(关键!)// 注意:你需要提供 SPS 和 PPS(Base64 解码后的二进制)std::vector<uint8_t> sps, pps;getSPSPPS(sps, pps);if (sps.empty() || pps.empty()) {envir() << " [createNewStreamSource] Warning: SPS/PPS not available yet!\n";}return H264VideoStreamDiscreteFramer::createNew(envir(), innerSource, True,True);}H264VideoRTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadType,FramedSource* inputSource) override {//return H264VideoRTPSink::createNew(envir(), rtpGroupsock,rtpPayloadType);std::vector<uint8_t> sps, pps;getSPSPPS(sps, pps);if (sps.empty() || pps.empty()) {envir() << " [createNewRTPSink ] Warning: SPS/PPS not available yet!\n";}return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadType,sps.data(), sps.size(),pps.data(), pps.size());}
};unsigned long long getCurrentTimestampUs() {struct timespec ts;clock_gettime(CLOCK_MONOTONIC, &ts);  // 推荐使用 MONOTONICreturn (unsigned long long)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}// 工具函数:从 NALU 头部获取 NAL type
uint8_t getNALUType(const uint8_t* data, size_t len) {if (len < 1) return 0;return (data[0] & 0x1F); // NAL Unit Type is in lower 5 bits
}bool getSPSPPS(std::vector<uint8_t>& sps, std::vector<uint8_t>& pps) {if (g_h264Params.haveSPS && g_h264Params.havePPS) {sps = g_h264Params.sps;pps = g_h264Params.pps;return true;} else {sps.clear();pps.clear();return false;}
}// 增强版:读取一帧,并提取 SPS/PPS(如果遇到)
bool readNextFrameWithSPSPPS(std::ifstream& file, std::vector<uint8_t>& frame,std::vector<uint8_t>& sps, std::vector<uint8_t>& pps) {frame.clear();//if (file.eof()) return false;//文件结束停止,退出线程//if (file.eof()) {//循环往复的读取文件file.clear();           // 清除 eof 标志file.seekg(0, std::ios::beg);  // 将文件指针移到开头}char byte;std::vector<uint8_t> buffer;// 寻找起始码 0x00000001 或 0x000001while (file.get(byte)) {buffer.push_back(static_cast<uint8_t>(byte));size_t len = buffer.size();if (len >= 4 && buffer[len-4] == 0 && buffer[len-3] == 0 && buffer[len-2] == 0 && buffer[len-1] == 1) {// 找到 0x00000001break;}if (len >= 3 && buffer[len-3] == 0 && buffer[len-2] == 0 && buffer[len-1] == 1) {// 找到 0x000001break;}}if (buffer.size() < 3) return false;// 确定起始码长度(3 或 4 字节)int startCodeLen = 0;size_t bufSize = buffer.size();if (bufSize >= 4 && buffer[bufSize-4] == 0 && buffer[bufSize-3] == 0 && buffer[bufSize-2] == 0 && buffer[bufSize-1] == 1) {startCodeLen = 4;} else if (bufSize >= 3 && buffer[bufSize-3] == 0 && buffer[bufSize-2] == 0 && buffer[bufSize-1] == 1) {startCodeLen = 3;} else {return false;}frame = buffer;  // 包含起始码buffer.clear();// 继续读取,直到下一个起始码或 EOFwhile (file.get(byte)) {buffer.push_back(static_cast<uint8_t>(byte));size_t len = buffer.size();// 检查 0x00000001if (len >= 4 && buffer[len-4] == 0 && buffer[len-3] == 0 && buffer[len-2] == 0 && buffer[len-1] == 1) {file.seekg(-4, std::ios::cur); // 回退 4 字节break;}// 检查 0x000001if (len >= 3 && buffer[len-3] == 0 && buffer[len-2] == 0 && buffer[len-1] == 1) {file.seekg(-3, std::ios::cur); // 回退 3 字节break;}}// 将剩余数据追加为当前帧内容frame.insert(frame.end(), buffer.begin(), buffer.end());// === 提取 NALU 类型并判断是否为 SPS/PPS ===if (frame.size() > (size_t)startCodeLen) {uint8_t nalType = getNALUType(&frame[startCodeLen], frame.size() - startCodeLen);if (nalType == 7 && !g_h264Params.haveSPS) {g_h264Params.sps.assign(frame.begin(), frame.end());g_h264Params.haveSPS = true;} else if (nalType == 8 && !g_h264Params.havePPS) {g_h264Params.pps.assign(frame.begin(), frame.end());g_h264Params.havePPS = true;}}// 输出调试信息(可选)if (g_h264Params.haveSPS && g_h264Params.havePPS) {static bool printed = false;if (!printed) {std::cout << "SPS and PPS extracted.\n";std::cout << "SPS size: " << g_h264Params.sps.size() << " bytes\n";std::cout << "PPS size: " << g_h264Params.pps.size() << " bytes\n";printed = true;}}// 返回给调用者(即使没找到,也返回当前状态)sps = g_h264Params.sps;pps = g_h264Params.pps;return true;
}// 生产者线程函数
int run=0;
void h264ProducerThread(const std::string& filename, int fps) {std::ifstream file(filename, std::ios::binary);if (!file.is_open()) {std::cerr << "Cannot open file: " << filename << std::endl;gSharedBuffer.signalEnd();return;}std::vector<uint8_t> frame;int frameCount = 0;auto frameInterval = std::chrono::milliseconds(1000 / fps); // 帧间隔std::vector<uint8_t> sps, pps;while (run && readNextFrameWithSPSPPS(file, frame,sps ,pps)) {gSharedBuffer.pushFrame(frame, getCurrentTimestampUs());frameCount++;//printf(" ########## frameCount:%d \n",frameCount);// 模拟实时帧率std::this_thread::sleep_for(frameInterval);}file.close();gSharedBuffer.signalEnd(); // 文件读完,关闭队列std::cout << "Producer finished. Total frames: " << frameCount << std::endl;
}void signalHandler(int sig) {if (rtspServer) {rtspServer->envir() << "Shutting down RTSP server...\n";exitLoop=1; run =0;}
}int main(int argc, char** argv) {// 注册信号处理(Ctrl+C)signal(SIGINT, signalHandler);// Step 1: 初始化 live555 环境TaskScheduler* scheduler = BasicTaskScheduler::createNew();env = BasicUsageEnvironment::createNew(*scheduler);// Step 2: 创建 RTSP 服务器rtspServer = RTSPServer::createNew(*env, 8554);if (rtspServer == nullptr) {*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";exit(1);}// Step 3: 添加媒体会话ServerMediaSession* sms = ServerMediaSession::createNew(*env, "h264Stream", "H.264 Live Stream","Session streamed by \"testH264VideoStreamer\"", True);sms->addSubsession(H264LiveServerMediaSubsession::createNew(*env));rtspServer->addServerMediaSession(sms);char* url = rtspServer->rtspURL(sms);*env << "Play this stream using the URL: " << url << "\n";delete[] url;// 启动生产者线程,读 test.264,模拟 30fpsrun =1;std::thread producer(h264ProducerThread, "./test.264", 30);exitLoop =0;// Step 4: 启动事件循环env->taskScheduler().doEventLoop(&exitLoop);// 当 exitLoop != 0 时,此函数返回// Cleanupproducer.join();Medium::close(rtspServer);rtspServer = nullptr;*env << "Shutting down...\n";env->reclaim();  delete scheduler;return 0;
}

编译

g++ -o h264_rtsp_server \h264_rtsp_server.cpp  \-I ../_install/include/liveMedia \-I ../_install/include/groupsock \-I ../_install/include/UsageEnvironment \-I ../_install/include/BasicUsageEnvironment \-L ../_install/lib  -lliveMedia -lBasicUsageEnvironment -lUsageEnvironment -lgroupsock \-lpthread -lstdc++ -ldl

运行

VLC拉流出图

测试视频 test.264

http://www.dtcms.com/a/336069.html

相关文章:

  • 通达信【二板爆量涨停】副图/选股指标,首板次日继续强势封板,整合MACD和KDJ指标确保趋势向上,专注二板机会
  • 【计算机网络面试】TCP/IP网络模型有哪几层
  • Python中f - 字符串(f-string)
  • 软考 系统架构设计师系列知识点之杂项集萃(127)
  • 第2章 高并发IO的底层原理
  • 数据结构:二叉搜索树(Binary Search Tree)
  • 【Android】Activity创建、显式和隐式跳转、清单文件声明
  • Pytorch模型复现笔记-VGG讲解+架构搭建(可直接copy运行)+冒烟测试
  • MLArena:一款不错的AutoML工具介绍
  • 【股票数据API接口33】如何获取股票所属指数数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据
  • PCA 实现多向量压缩:首个主成分的深层意义
  • JZ57 和为S的两个数字
  • Traefik网关DNS解析超时问题优化
  • Agent开发进阶路线:从基础响应到自主决策的架构演进
  • C++类型转换详解:从C风格到C++风格
  • 如何理解事件循环和JS的异步?
  • LintCode第137-克隆图
  • PostgreSQL导入mimic4
  • SQL详细语法教程(四)约束和多表查询
  • C语言相关简单数据结构:双向链表
  • Rust Async 异步编程(五):执行器和系统 I/O
  • Effective C++ 条款47: 使用traits classes表现类型信息
  • 基于强化学习的柔性机器人控制研究
  • 【大模型微调系列-07】Qwen3全参数微调实战
  • 关于虾的智能养殖系统的开发与实现(LW+源码+讲解+部署)
  • 【LeetCode题解】LeetCode 33. 搜索旋转排序数组
  • 详解flink java基础(一)
  • 嵌入式软件--->任务间通信
  • 【C++知识杂记1】智能指针及其分类
  • 05-实施任务控制