问题描述:

SRS Version=4.0.126. 配置文件配置:

listen              1935;
max_connections     100;
daemon              off;
srs_log_tank        console;http_server {enabled         on;listen          8080;dir             ./objs/nginx/html;
}http_api {enabled         on;listen          1985;
}
stats {network         0;
}
rtc_server {enabled on;# Listen at udp://8000listen 8000;## The $CANDIDATE means fetch from env, if not configed, use * as default.## The * means retrieving server IP automatically, from all network interfaces,# @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidatecandidate 192.168.71.35; #本机IP
}vhost __defaultVhost__ {rtc {enabled     on;}http_remux {enabled     on;mount       [vhost]/[app]/[stream].flv;}http_hooks {enabled on;on_rtc_play http://127.0.0.1/control/relay/subscriber;}
}

注释:on_rtc_play http://127.0.0.1/control/relay/subscriber; 自定义开发,目的是在rtc播放拉流时,如果srs服务中没有直播流,可以通过回调到Nginx-RTMP触发RTMP推流到SRS中。

一、使用ffmpeg推流:

ffmpeg -re -i ~/Downloads/test.mp4 -c copy -f flv rtmp://127.0.0.1/live/livestream

二、使用http://127.0.0.1:8080/中SRS播放器播放。播放正常。

三、再打开一个页面使用http://127.0.0.1:8080/中SRS播放器播放,发现无法播放,并且第二部中的播放页面也会卡住。

只要开启rtc enabled on;即使不使用rtc播放,也存在如下问题
  1. 使用ffmpeg推流
  2. 使用ffplay播放流
  3. 使用ffmpeg推相同的流
  4. 第2步中的流可以播放,当时关闭播放再使用ffplay播放会失败。

问题分析

一、查看日志,发现有:

cleanup when unpublish
cleanup when unpublish, created=%u, deliver=%u

说明有触发推流断开。猜想可能是第二次进行RTC播放时,回调了http://127.0.0.1/control/relay/subscriber,使Nginx-RTMP服务又推流到SRS,并且app和name是相同的。这样第二次的RTMP推流由于是重复的流,所以会推流失败,然后进入unpublish调用链中。

二、在void SrsLiveSource::on_unpublish()函数中,将RTMP转RTC的bridger_进行了释放。

void SrsLiveSource::on_unpublish()
{。。。。。略if (bridger_) {bridger_->on_unpublish();srs_freep(bridger_);}。。。。。略
}

三、对于RTMP推流,是会经常出现重复推流的情况的。在Nginx-RTMP服务中,只会将第二次重复推流进行禁止,并不会影响第一次已经推上来的直播流。同理,SRS中的RTMP服务应该也有类似的逻辑。于是查看代码:

srs_error_t SrsRtmpConn::publishing(SrsLiveSource* source)
{srs_error_t err = srs_success;SrsRequest* req = info->req;if (_srs_config->get_refer_enabled(req->vhost)) {if ((err = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != srs_success) {return srs_error_wrap(err, "rtmp: referer check");}}if ((err = http_hooks_on_publish()) != srs_success) {return srs_error_wrap(err, "rtmp: callback on publish");}// TODO: FIXME: Should refine the state of publishing.if ((err = acquire_publish(source)) == srs_success) {// use isolate thread to recv,// @see: https://github.com/ossrs/srs/issues/237SrsPublishRecvThread rtrd(rtmp, req, srs_netfd_fileno(stfd), 0, this, source, _srs_context->get_id());err = do_publishing(source, &rtrd);rtrd.stop();}// whatever the acquire publish, always release publish.// when the acquire error in the midlle-way, the publish state changed,// but failed, so we must cleanup it.// @see https://github.com/ossrs/srs/issues/474// @remark when stream is busy, should never release it.if (srs_error_code(err) != ERROR_SYSTEM_STREAM_BUSY) {release_publish(source);}http_hooks_on_unpublish();return err;
}

可以看到只有srs_error_code(err) != ERROR_SYSTEM_STREAM_BUSY才会将RTMP publish连接进行释放。

四、断点查看生成error code的代码

srs_error_t SrsRtmpConn::acquire_publish(SrsLiveSource* source)
{srs_error_t err = srs_success;SrsRequest* req = info->req;// @see https://github.com/ossrs/srs/issues/2364// Check whether GB28181 stream is busy.
#if defined(SRS_GB28181)if (_srs_gb28181 != NULL) {SrsGb28181RtmpMuxer* gb28181 = _srs_gb28181->fetch_rtmpmuxer(req->stream);if (gb28181 != NULL) {return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "gb28181 stream %s busy", req->get_stream_url().c_str());}}
#endif// Check whether RTC stream is busy.
#ifdef SRS_RTCSrsRtcSource *rtc = NULL;bool rtc_server_enabled = _srs_config->get_rtc_server_enabled();bool rtc_enabled = _srs_config->get_rtc_enabled(req->vhost);if (rtc_server_enabled && rtc_enabled && !info->edge) {if ((err = _srs_rtc_sources->fetch_or_create(req, &rtc)) != srs_success) {return srs_error_wrap(err, "create source");}if (!rtc->can_publish()) {return srs_error_new(ERROR_RTC_SOURCE_BUSY, "rtc stream %s busy", req->get_stream_url().c_str());}}
#endif// Check whether RTMP stream is busy.if (!source->can_publish(info->edge)) {return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "rtmp: stream %s is busy", req->get_stream_url().c_str());}// Bridge to RTC streaming.
#if defined(SRS_RTC) && defined(SRS_FFMPEG_FIT)if (rtc) {SrsRtcFromRtmpBridger *bridger = new SrsRtcFromRtmpBridger(rtc);if ((err = bridger->initialize(req)) != srs_success) {srs_freep(bridger);return srs_error_wrap(err, "bridger init");}source->set_bridger(bridger);}
#endif// Start publisher now.if (info->edge) {return source->on_edge_start_publish();} else {return source->on_publish();}
}

发现其在这一步直接返回错误了:

if (!rtc->can_publish()) {return srs_error_new(ERROR_RTC_SOURCE_BUSY, "rtc stream %s busy", req->get_stream_url().c_str());}

这是由于在第一个推流时,已经将RTMP转RTC的bridger_的SrsRtcSource中is_created_ = true;所以会返回RTC的推流已经存在。错误码并不是RTMP重复推流的错误码ERROR_SYSTEM_STREAM_BUSY。

问题修复

将acquire_publish中判断RTC推流重复和RTMP推流重复调换位置。如下:

srs_error_t SrsRtmpConn::acquire_publish(SrsLiveSource* source)
{srs_error_t err = srs_success;SrsRequest* req = info->req;// @see https://github.com/ossrs/srs/issues/2364// Check whether GB28181 stream is busy.
#if defined(SRS_GB28181)if (_srs_gb28181 != NULL) {SrsGb28181RtmpMuxer* gb28181 = _srs_gb28181->fetch_rtmpmuxer(req->stream);if (gb28181 != NULL) {return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "gb28181 stream %s busy", req->get_stream_url().c_str());}}
#endif// Check whether RTMP stream is busy.if (!source->can_publish(info->edge)) {return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "rtmp: stream %s is busy", req->get_stream_url().c_str());}// Check whether RTC stream is busy.
#ifdef SRS_RTCSrsRtcSource *rtc = NULL;bool rtc_server_enabled = _srs_config->get_rtc_server_enabled();bool rtc_enabled = _srs_config->get_rtc_enabled(req->vhost);if (rtc_server_enabled && rtc_enabled && !info->edge) {if ((err = _srs_rtc_sources->fetch_or_create(req, &rtc)) != srs_success) {return srs_error_wrap(err, "create source");}if (!rtc->can_publish()) {return srs_error_new(ERROR_RTC_SOURCE_BUSY, "rtc stream %s busy", req->get_stream_url().c_str());}}
#endif// Bridge to RTC streaming.
#if defined(SRS_RTC) && defined(SRS_FFMPEG_FIT)if (rtc) {SrsRtcFromRtmpBridger *bridger = new SrsRtcFromRtmpBridger(rtc);if ((err = bridger->initialize(req)) != srs_success) {srs_freep(bridger);return srs_error_wrap(err, "bridger init");}source->set_bridger(bridger);}
#endif// Start publisher now.if (info->edge) {return source->on_edge_start_publish();} else {return source->on_publish();}
}

排查SRS问题记录一(RTMP推流,RTC播放卡住)相关推荐

  1. docker部署SRS实时视频服务器,rtmp推流,用WebRTC播放

    SRS是一个简单高效的实时视频服务器,支持RTMP/WebRTC/HLS/HTTP-FLV/SRT/GB28181 大家可以先去gitub上面看使用说明,地址是:v4_CN_Home · ossrs/ ...

  2. SRS源码分析-rtmp转rtc流程

    前言 SRS4.0支持将RTMP流转换成RTC流,本文将结合源码分析下这个过程. 配置 首先,需要在SRS4.0的启动配置文件里面开启RTC Server和RTC 能力,可以参考官方提供的配置文件./ ...

  3. 海康大华等网络摄像机监控视频RTSP/RTMP推流网页播放/直播无需插件低延迟解决方案研究

    市面上常见监控视频推流方案简介 当前如果想要将监控视频在浏览器中播放,有几种常见的办法如下: 1.获取摄像头RTSP流,使用FFmpeg或者程序如JavaCV或者其他方式,将其推流成RTMP,通过服务 ...

  4. SRS流媒体服务器:服务器读取RTMP推流数据

    目录 处理RTMP推流video message 处理RTMP推流audio message 处理RTMP推流onMetaData message 本文是第三篇,第4篇将讲解.服务器给RTMP拉流端转 ...

  5. SRS解决RTMP转RTC的爆音问题

    使用RTMP推流WEBRTC播放时音频爆音问题,已解决 留下备忘 修改文件 trunk/src/app/srs_app_rtc_codec.cpp 第122行左右: for (int i = 0; i ...

  6. SRS流媒体服务器——服务器读取RTMP推流数据

    SRS流媒体服务器--服务器读取RTMP推流数据 目录 处理RTMP推流video message 处理RTMP推流audio message 处理RTMP推流onMetaData message S ...

  7. SRS流媒体服务器——RTMP推流、拉流创建连接

    SRS流媒体服务器--RTMP推流.拉流创建连接 目录 识别客户端,然后获取或者创建SrsLiveSource 启动推流 启动拉流 SRS安装部署相关内容: SRS流媒体服务器--单机环境搭建和源码目 ...

  8. 4、SRS4.0源代码分析之RTMP推流处理

    目标:     本章我们将分析SRS4.0 RTMP服务模块与推流相关的代码处理逻辑. 内容:     根据上节内容可知,SRS4.0针对RTMP推流客户端的处理逻辑,主要在协程SrsRtmpConn ...

  9. 【网络通信 -- 直播】SRS 实战记录 -- SRS 部署与直播效果测试

    [网络通信 -- 直播]SRS 实战记录 -- SRS 部署与直播效果测试 [1]SRS(Simple Realtime Server) 部署 SRS 代码下载 git clone https://g ...

最新文章

  1. 汇编语言PTR运算符:重写操作数的大小类型
  2. 一个能描述erp系统的小故事。
  3. Java使用Redis实现分布式锁来防止重复提交问题
  4. [vue] $nextTick有什么作用?
  5. 叠箱子问题 之 动态规划
  6. 10.11 cocoapods安装
  7. 练习-图书馆管理系统
  8. 导纳矩阵 matlab,matlab实现导纳矩阵
  9. 方舟同账号一个服务器能建小号吗,你玩明日方舟开小号吗?游戏虽好但请莫贪多,多号同玩会让人疲劳...
  10. IPad一张图应用系统
  11. 常用的JScript代码整理
  12. 基于TensorRT和onnxruntime下pytorch的Bert模型加速对比实践
  13. 编写c语言数据从结构时头文件,C语言与数据结构 实验指导.doc
  14. python粒子风暴_气象雷达应用中常见的名词解释
  15. python 数据可视化工具 -- pyecharts
  16. 基于JAVA游泳馆信息管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  17. c语言程序设计江义火答案,大学就是一个群英会集的(),天下各处各地的学子到这里来,以寻求天下各种各样的知识。...
  18. 微信公众号发布时间的技巧?
  19. linux下硬盘读写速度测试
  20. SpaceX完成世界首次海上回收火箭(图)

热门文章

  1. 九尾猫如何修炼,温暖你我
  2. java线程安全的方法_Java实现线程安全的方式
  3. Android 传感器概述(一)
  4. 欢迎普华永道加入 The Sandbox 元宇宙
  5. 【Java日期时间】@JsonFormat与@DateTimeFormat注解的区分和使用
  6. yum如何安装特定版本的gcc_yum安装gcc
  7. shell自动获取一级目录和二级目录
  8. MATLAB右键编辑器和工具栏字体变小解决办法
  9. Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
  10. java调用asmx接口_java调用.net写的webserver接口(.asmx后缀)