NikTalk
Hi!大家好,我是Nik。一名在人工智能+物联网行业混迹多年的AIOT架构工程师兼产品经理。欢迎您来到我的博客!这里记录了我的AIOT职业生涯,从最开始的一名普通嵌入式软件工程师->到嵌入式系统工程师->再到AIOT架构师兼产品经理一路上的所学所感所得。希望这些知识,经验,技术以及感悟对后来的你,在技术学习以及职业发展的道路上有所帮助。记得关注我,我将不定期分享一些AIOT相关的技术和行业信息。唯一微信号:aiotnik

rtsp服务器搭建

rtsp音视频流媒体网络协议常用于无线图传,目前主要应用领域用:

  • 无人机图传
  • 安防摄像头图传
  • 相机图传
  • 其他应用场景媒体传输等

可以说有相机,摄像头的产品上只要有无线音视频传输都应用到了rtsp协议。随着这几年的媒体娱乐,以及各种各样的影像类产品的不断增多,生活中常见的手机,相机,电视,电脑,监控摄像头,无人机,智能家居等产品都包含了音视频传输的功能,让可视,可听,可说带入了人机交互。让产品与用户之间更加具备交互性,可玩性,实用性。所以说对应一个涉及音视频应用的开发者来说,掌握如何使用rtsp协议来实现音视频流媒体传输是非常重要的一项技能。我这里以安防摄像头为例,演示摄像头怎么通过rtsp协议将摄像头采集到的音视频媒体数据传输到手机,PC机等终端设备进行实时显示播放。

本案例使用到的硬件设备有:一台PC电脑、一台手机、一个带网络的摄像头模块。

本案例使用的软件有:live555、vlc、rtsp手机客户端、ubuntu系统(16.04.版本以上)。

要查看更多文章内容,请您先登录/注册

我们分三步来完成上图框架的搭建:1、摄像头模组端的rtsp服务器搭建。2、摄像头模组采集音视频编码并进行rtsp推流。3、pc电脑和手机的拉流解码显示。

一、摄像头模组端的rtsp服务器搭建

  1. 下载live555源码
wget http://www.live555.com/liveMedia/public/live555-latest.tar.gz
  • 减压源码包。
tar xvf live555-latest.tar.gz

2. 根据平台配置live555的config配置文件

  • 复制live555源码目录下的任意一个config文件,并修改配置文件的工具链为对应平台的工具链, 以及添加安装路径PREFIX=$(pwd)/_install。
cp config.armlinux config.mips-linux-uclibc
  • 打开配置文件vim config.mips-linux-uclibc,修改对应平台工具链

3. 编译live555源码

  • 执行genMakefiles生成对应平台的Makefile文件
./genMakefiles mips-linux-uclibc
  • 执行make -j8进行编译
  • 编写一个install.sh文件,将对应的编译出来的库文件和头文件拷贝到对应目录, 内容如下:
  • 执行./install.sh。
./install.sh
  • 可见编译出来的bin lib include已经存放到了指定的live555-lib安装目录下。
二、摄像头模组采集音视频编码并进行rtsp推流

1、找到对应芯片平台获取音视频的sample。不管你手头的摄像头模组是海思、rk、sigmastar、安霸、君正、全智还是其他的平台的芯片,平台都有对应的SDK,SDK里面都有对应获取到音视频的sample例子。这里我们找到获取H264/H265编码的视频sample,然后找到获取一帧一帧H264的视频接口。因为这里我们会将获取到的H264/H265视频数据打包成rtp包上传到rtsp服务器。大概的逻辑图如下:

2、基于以上编译出来的live555函数库和头文件,调用live555相应的函数接口和类创建一个rtsp服务器,其服务器实现代码如下:

#include <liveMedia.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>

// 定义一个RTSP服务器的类
class RTSPServer {
public:
    RTSPServer(UsageEnvironment& env, Port rtspPort);
    virtual ~RTSPServer();

private:
    UsageEnvironment& fEnv;
    RTSPServer* fRtspServer;
};

// 构造函数
RTSPServer::RTSPServer(UsageEnvironment& env, Port rtspPort)
    : fEnv(env) {
    fRtspServer = RTSPServer::createNew(env, rtspPort, NULL, 65);
    if (fRtspServer == NULL) {
        env << "创建RTSP服务器失败: " << env.getResultMsg() << "\n";
        exit(1);
    }
}

// 析构函数
RTSPServer::~RTSPServer() {
    Medium::close(fRtspServer);
}

// 主函数
int main(int argc, char** argv) {
    // 初始化live555环境
    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
    UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

    // 设置RTSP服务器
    Port rtspPort = 8554; // 根据需要更改端口号
    RTSPServer rtspServer(*env, rtspPort);

    // 启动事件循环
    env->taskScheduler().doEventLoop();

    return 0;
}

3、将获取到的H264/H265打包成rtp推流到rtsp服务器,要实现将获取到的H.264编码数据打包成RTP包并推流到RTSP服务器,你需要做以下几步:

(1)调用 get_h264_encodeframe() 函数获取H.264编码数据。
(2)将H.264编码数据打包成RTP包。
(3)使用live555库中的 FramedSource 类创建一个新的 RTP 数据源。
(4)将 RTP 数据源添加到 RTSP 服务器中。
实现代码如下:

#include <liveMedia.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>

// 获取H.264编码数据的函数接口
extern int get_h264_encodeframe(unsigned char* buffer, int bufferSize);

// RTP 数据源类,用于将H.264数据打包成RTP包
class H264RTPSource : public FramedSource {
public:
    static H264RTPSource* createNew(UsageEnvironment& env);
    virtual ~H264RTPSource();

    void doGetNextFrame();
    static void deliverFrameStub(void* clientData);

private:
    H264RTPSource(UsageEnvironment& env);
    void deliverFrame();

private:
    unsigned char* fBuffer;
    int fBufferSize;
};

H264RTPSource::H264RTPSource(UsageEnvironment& env)
    : FramedSource(env), fBuffer(nullptr), fBufferSize(0) {
    // Allocate buffer for H.264 frame
    fBufferSize = 100000; // Adjust as needed
    fBuffer = new unsigned char[fBufferSize];
}

H264RTPSource::~H264RTPSource() {
    delete[] fBuffer;
}

void H264RTPSource::doGetNextFrame() {
    if (fBuffer == nullptr) {
        handleClosure();
        return;
    }

    // Get H.264 frame from the function interface
    int frameSize = get_h264_encodeframe(fBuffer, fBufferSize);
    if (frameSize <= 0) {
        handleClosure();
        return;
    }

    // Deliver the frame
    fFrameSize = frameSize;
    fPresentationTime.tv_sec = fPresentationTime.tv_usec = 0;
    fDurationInMicroseconds = 0;
    fFrameType = 0;
    fNumTruncatedBytes = 0;
    afterGetting(this);
}

void H264RTPSource::deliverFrameStub(void* clientData) {
    reinterpret_cast<H264RTPSource*>(clientData)->deliverFrame();
}

void H264RTPSource::deliverFrame() {
    if (!isCurrentlyAwaitingData()) {
        return;
    }

    // Deliver H.264 frame as RTP packet
    if (fFrameSize > 0) {
        // Create RTP packet and send it
        // Code to create RTP packet and send it to RTSP server
    }

    // Continue fetching next frame
    doGetNextFrame();
}

// RTSP服务器类
class RTSPServer {
public:
    RTSPServer(UsageEnvironment& env, Port rtspPort);
    virtual ~RTSPServer();

    void addH264RTPSource(H264RTPSource* rtpSource);

private:
    UsageEnvironment& fEnv;
    RTSPServer* fRtspServer;
    ServerMediaSession* fSession;
};

RTSPServer::RTSPServer(UsageEnvironment& env, Port rtspPort)
    : fEnv(env), fRtspServer(nullptr), fSession(nullptr) {
    fRtspServer = RTSPServer::createNew(env, rtspPort, NULL, 65);
    if (fRtspServer == NULL) {
        env << "Failed to create RTSP server: " << env.getResultMsg() << "\n";
        exit(1);
    }
}

RTSPServer::~RTSPServer() {
    Medium::close(fRtspServer);
}

void RTSPServer::addH264RTPSource(H264RTPSource* rtpSource) {
    fSession = ServerMediaSession::createNew(fEnv, "H264Stream", NULL, "H.264 RTP Stream");
    fSession->addSubsession(RTPSink::createNew(fEnv, rtpSource));
    fRtspServer->addServerMediaSession(fSession);
}

// 主函数
int main(int argc, char** argv) {
    // 初始化live555环境
    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
    UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

    // 设置RTSP服务器
    Port rtspPort = 8554; // 根据需要更改端口号
    RTSPServer rtspServer(*env, rtspPort);

    // 创建H.264 RTP数据源
    H264RTPSource* rtpSource = H264RTPSource::createNew(*env);
    rtspServer.addH264RTPSource(rtpSource);

    // 启动事件循环
    env->taskScheduler().doEventLoop();

    return 0;
}

三、pc电脑或者手机端拉流解码显示

1、pc电脑vlc拉流看图,输入rtsp拉流地址,点击播放即可。

简单记录,有什么不足的地方还请各位指出,非常感谢!