使用srs_librtmp实现RTMP推流

这篇具有很好参考价值的文章主要介绍了使用srs_librtmp实现RTMP推流。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、背景 

    由于项目有需求在一个现有的产品上增加RTMP推流的功能,目前只推视频流。

2、方案选择

    由于是在现有的产品上新增功能,那么为了减少总的成本,故选择只动应用软件的来实现需求。

    现有的产品中的第三方库比较有限,连个ffmpeg都没,所以要选择可以直接集成代码进来的第三方库,最后选中了srs_librtmp。虽然它已经停止维护了,但是主要功能没问题,使用简单,且可以直接集成代码。

  

3、实现代码

step1:去github上面先把源码下下来。

GitHub - ossrs/srs-librtmp at master

step2:把对应的代码文件集成到项目里。

    这里只需要src/srs目录下的srs_librtmp.h和srs_librtmp.cpp就行了,如下图

使用srs_librtmp实现RTMP推流,Linux,笔记,RTMP推流,srs_librtmp,linux

step3:封装成工具类

封装过程中使用了另一个第三方库POCO,这个库只要用来实现线程,不想要的话可以直接改掉。

RTMPPusher.h

//
// Created by zhengqiuxu on 2023/8/5.
//

#ifndef VIS_G3_SOFTWARE_RTMPPUSHER_H
#define VIS_G3_SOFTWARE_RTMPPUSHER_H


#include <Poco/Runnable.h>
#include <Poco/Thread.h>
#include <mutex>
#include "srs_librtmp.h"


class RTMPPusher : public Poco::Runnable{
public:

    // h264 nalu
    struct NaluHead
    {
        unsigned type : 5;
        unsigned nal_ref_idc : 2;
        unsigned forbidden_zero_bit : 1;
    };

    RTMPPusher();

    /**
     * 初始化
     *
     * @param url : 推流地址
     * @return 0:成功  其他:失败
     */
    int init(const std::string url);

    /**
     * 启动线程
     */
    void start();

    /**
     * 设置一帧H264数据帧
     *
     * @param h264Data : 一帧H264数据的指针
     * @param dataLen : 一帧H264数据的指针的长度
     */
    void setH264Data(uint8_t *h264Data, const int dataLen);

    /**
     * 停止推流
     */
    void stop();

    void run() override;


    int getCameraId() const;

    void setCameraId(int cameraId);

    const std::string &getRtmpUrl() const;

    void setRtmpUrl(const std::string &rtmpUrl);

    bool isInited() const;

    void setInited(bool inited);

private:
    /**
     * 推送一帧H264数据帧(真实推送到RTMP)
     *
     * @param h264Data : 一帧H264数据的指针
     * @param dataLen : 一帧H264数据的指针的长度
     */
    void pushH264Data(char *h264Data, const int dataLen);


    /* 对应的相机ID */
    int cameraId = -1;
    /* RTMP的推送地址 */
    std::string rtmpUrl;
    /* 是不是需要停止推送 */
    bool isNeedStop = true;
    /* 是否初始化了 */
    bool inited = false;
    /* 是否可以发送了?需要第一帧是sps才行 */
    bool canSen = false;

    Poco::Thread pushThread;

    srs_rtmp_t rtmp;
    uint64_t pts = 0;
    uint64_t dts = 0;


    const int MAX_H264CACHE_SIZE = 1024*1024*4;
    /* 缓冲起来的h264数据 */
    char *h264DataCache;
    /* 缓冲起来的h264数据的长度 */
    int curH264DataLen = 0;
    /* 读写H264数据的互斥锁 */
    std::mutex h264DataLock;


};


#endif //VIS_G3_SOFTWARE_RTMPPUSHER_H

RTMPPusher.cpp

//
// Created by zhengqiuxu on 2023/8/5.
//

#include "RTMPPusher.h"
#include <cstring>
#include <unistd.h>

RTMPPusher::RTMPPusher() {

}
/**
 * 初始化
 *
 * @param url : 推流地址
 * @return 0:成功  其他:失败
 */
int RTMPPusher::init(const std::string url) {

    int ret = -1;

    rtmpUrl = url;

    inited = true;

    h264DataCache = (char *)malloc(MAX_H264CACHE_SIZE);

    ret = 0;
    return ret;




}
/**
 * 推送一帧H264数据帧(真实推送到RTMP)
 *
 * @param h264Data : 一帧H264数据的指针
 * @param dataLen : 一帧H264数据的指针的长度
 */
void RTMPPusher::pushH264Data(char *h264Data, const int dataLen) {
    try {
        printf("RTMPPusher::pushH264Data  size=%d \n",dataLen);

        /* 推流到RTMP */
        pts += 40;  /* 如果是B帧的话,PTS应该等于离它最近的P帧或者I帧的的PTS  一般都是选择填上一帧数据的PTS */
        dts = pts;

        int ret = srs_h264_write_raw_frames(rtmp, h264Data, dataLen, dts, pts);
        if (ret != 0) {
            if (srs_h264_is_dvbsp_error(ret)) {
                srs_human_trace("ignore drop video error, code=%d", ret);
            } else if (srs_h264_is_duplicated_sps_error(ret)) {
                srs_human_trace("ignore duplicated sps, code=%d", ret);
            } else if (srs_h264_is_duplicated_pps_error(ret)) {
                srs_human_trace("ignore duplicated pps, code=%d", ret);
            } else {
                srs_human_trace("send h264 raw data failed. ret=%d", ret);
            }
        }
    } catch (...) {
        printf("push H264Data to %s failed! %s \n",rtmpUrl.c_str(),strerror(errno));
    }

}
/**
 * 停止推流
 */
void RTMPPusher::stop() {
    isNeedStop = true;
    srs_human_trace("h264 raw data completed");
    srs_rtmp_destroy(rtmp);
    free(h264DataCache);
    inited = false;
}
/**
 * 启动线程
 */
void RTMPPusher::start() {
    pushThread.start(*this);
}

void RTMPPusher::run() {
    std::string pthreadName = "RTMPPusher_";
    pthreadName.append(rtmpUrl);
    pthread_setname_np(pthread_self(), pthreadName.c_str());



    isNeedStop = false;


    /* 创建一个RTMP客户端对象 */
    rtmp = srs_rtmp_create(rtmpUrl.c_str());
        /* 开始跟RTMP服务器握手 */
    if (srs_rtmp_handshake(rtmp) != 0) {
        srs_human_trace("simple handshake failed.");
    }else{
        srs_human_trace("simple handshake success");
            /* 连接RTMP流 */
        if (srs_rtmp_connect_app(rtmp) != 0) {
            srs_human_trace("connect vhost/app failed.");
        }else{
            srs_human_trace("connect vhost/app success");
                /* 看看RTMP流是否可以推流 */
            if (srs_rtmp_publish_stream(rtmp) != 0) {
                srs_human_trace("publish stream failed.");
            }else{
                srs_human_trace("publish stream success");

                canSen = false;
                /* 循环从内存里读出H264并推到RTMP服务器 */
                while (!isNeedStop){
                    h264DataLock.lock();
                    if(curH264DataLen > 0){
                        if(canSen){
                            pushH264Data(h264DataCache,curH264DataLen);
                            curH264DataLen = 0;
                        }else{
                            /* 拿出NALU头用来后面判断NALU类型 */
                            struct NaluHead curNaluHead = *(struct NaluHead *)(h264DataCache+4);
                            /* 从SPSPPS开始推,有些服务器做的不好,不是从SPSPPS开始推的话会报错 */
                            if(curNaluHead.type == 7){
                                canSen = true;
                                pushH264Data(h264DataCache,curH264DataLen);
                                curH264DataLen = 0;
                            }
                        }
                    }
                    h264DataLock.unlock();
                    usleep(10000);
                }


            }
        }
    }



}
/**
 * 设置一帧H264数据帧
 *
 * @param h264Data : 一帧H264数据的指针
 * @param dataLen : 一帧H264数据的指针的长度
 */
void RTMPPusher::setH264Data(uint8_t *h264Data, const int dataLen) {
    if(dataLen > 0){
        h264DataLock.lock();
        memcpy(h264DataCache,h264Data,dataLen);
        curH264DataLen = dataLen;
        h264DataLock.unlock();
    }

}

int RTMPPusher::getCameraId() const {
    return cameraId;
}

void RTMPPusher::setCameraId(int cameraId) {
    RTMPPusher::cameraId = cameraId;
}

const std::string &RTMPPusher::getRtmpUrl() const {
    return rtmpUrl;
}

void RTMPPusher::setRtmpUrl(const std::string &rtmpUrl) {
    RTMPPusher::rtmpUrl = rtmpUrl;
}

bool RTMPPusher::isInited() const {
    return inited;
}

void RTMPPusher::setInited(bool inited) {
    RTMPPusher::inited = inited;
}

    到这里我们就已经实现完成且封装成一个方便调用的工具类了。调用的时候只要需要先调用init()函数初始化,再调用start()函数,让发送线程跑起来,一有H264数据就通过setH264Data()函数设置给工具类的就行了。这样工具类就会循环读取设置过来的H264数据并推送到RTMP服务器了。

4、其他

    1、由于这个库的pts和dts是需要自己赋值的,所以有时候推送上去的数据要么是播放速度变快,要么是卡顿,都很有可能是pts和dts的问题。由于我这里是固定25帧的,所有我直接pts固定每帧都比上一帧+40ms。pts和dts还有很多研究空间,实际使用的时候具体情况具体分析。文章来源地址https://www.toymoban.com/news/detail-783472.html

到了这里,关于使用srs_librtmp实现RTMP推流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android WebRTC+SRS/ZLM视频通话(4):Android使用WebRTC推流SRS/ZLMediaKit

    来自奔三人员的焦虑日志 接着上一章内容,继续来记录Android是如何使用WebRTC往SRS或ZLMediaKit进行推流。想要在Android设备上实现高质量的实时流媒体推送?那么不要错过 WebRTC、SRS 和 ZLMediaKit 这三个工具! WebRTC 是一种使用标准的 Web 技术实现 P2P 实时通信的开源技术,对于实时

    2024年02月16日
    浏览(37)
  • 树莓派3B+ /+ CSI摄像头 + FFmpeg + SRS 实现直播推流

    手头有一个树莓派3B+ 和一块CSI摄像头,想要实现一个推拉流直播的效果。 所需材料: 开发板(我用的是树莓派3B+)、CIS摄像头、云服务器(用来搭建SRS服务器) 使用树莓派+CSI摄像头来做画面采集,然后使用FFmpeg推流到SRS云服务器上,然后用户可以拉取SRS服务器上的视频流

    2024年01月22日
    浏览(51)
  • 利用ffmpeg实现rtmp推流

    http://ffmpeg.org/ 官方下载链接为:http://ffmpeg.org/ cmd进入ffmpeg.exe所在目录 输入下面的语句即可列出电脑的设备 ffmpeg -list_devices true -f dshow -i dummy ffmpeg -list_devices true -f dshow -i dummy 可以看到我电脑里面有USB2.0 PC CAMERA摄像头和一个乱码的麦克风 如果设备名称有中文,会出现乱码,想

    2024年02月13日
    浏览(77)
  • 9步实现 Docker部署 SRS rtmp/flv流媒体服务器

    这是基于centos7.6系统部署的 运行容器会直接停留在容器运行界面,通过Ctrl+P+Q可后台形式退出容器 这时候其实已经运行成功!可以通过以下命令查看SRS控制台 进入/home/docker/srs3 可查看配置文件是否复制成功 这是可能会报错: 因为步骤四我们意见运行一个容器,已经使用过

    2024年02月15日
    浏览(58)
  • 利用ffmpeg实现rtmp推流直播

    我们这次利用ffmpeg实现rtmp推流(最终推流地址统一为rtmp://127.0.0.1:1935/live/123) 1.首先下载ffmpeg和ffplay 官方下载链接为:FFmpeg 下载后开始配置环境变量: 系统属性-环境变量-path-添加ffmpeg/bin的路径; 打开命令窗口,输入ffmpeg,检测是否配置成功  以上代表正常安装配置(这一步很简单

    2023年04月08日
    浏览(36)
  • SRS OBS利用RTMP协议实现音视频推拉流;WebRTC 屏幕直播分享工具

    参考:https://ossrs.net/lts/zh-cn/docs/v5/doc/getting-started 1)docker直接运行SRS服务: 运行起来后可以http://localhost:8080/ 看到服务基本信息: 2) OBS 推流 在设置里设置直播地址: 然后回到首页点击开始直播 3)点击http://localhost:8080/ 可以网页查看流视频 SRS低延迟启动(暂时测试下来延迟

    2024年04月13日
    浏览(54)
  • OpenCV+FFmpeg 实现人脸检测Rtmp直播推流(Python快速实现)

    windows平台笔记本摄像头视频采集、人脸识别,识别后将视频推流到RTMP流媒体服务器,在任意客户端可以进行RTMP拉流播放。 效果如图: 使用VLC播放器进行拉流。 需要先安装OpenCV的python包以及FFmpeg。 对于ffmpeg有两种调用方式,但这两种方式都需要先安装ffmpeg,调用的具体区别

    2024年02月12日
    浏览(40)
  • Android音视频学习系列(九) — Android端实现rtmp推流

    Android音视频学习系列(一) — JNI从入门到精通 Android音视频学习系列(二) — 交叉编译动态库、静态库的入门 Android音视频学习系列(三) — Shell脚本入门 Android音视频学习系列(四) — 一键编译32/64位FFmpeg4.2.2 Android音视频学习系列(五) — 掌握音频基础知识并使用AudioTrack、OpenSL ES渲

    2024年02月09日
    浏览(46)
  • Android-音视频学习系列-(九)Android-端实现-rtmp-推流

    视频画面的采集主要是使用各个平台提供的摄像头 API 来实现的,在为摄像头设置了合适的参数之后,将摄像头实时采集的视频帧渲染到屏幕上提供给用户预览,然后将该视频帧传递给编码通道,进行编码。 1. 权限配置 2. 打开摄像头 2.1 检查摄像头 public static void checkCameraSe

    2024年04月12日
    浏览(68)
  • Android-音视频学习系列-(九)Android-端实现-rtmp-推流(2)

    配置好之后,检查一下 AudioRecord 当前的状态是否可以进行录制,可以通过 AudioRecord##getState 来获取当前的状态: STATE_UNINITIALIZED 还没有初始化,或者初始化失败了 STATE_INITIALIZED 已经初始化成功了。 2. 开启采集 创建好 AudioRecord 之后,就可以开启音频数据的采集了,可以通过调

    2024年04月12日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包