SRS低延时配置分析

SRS低延时配置分析SRS 低延时配置分析

注意:低延迟会带来性能的下降, 如
SRS2.0一次writev多个packet支持10000客户端。
低延时配置文件如下:
listen              1935; vhost __defaultVhost__ {     tcp_nodelay     on;     min_latency     on;     play {         gop_cache       off;         queue_length    10;         mw_latency      100;     }     publish {         mr off;     } }

1、tcp_nodelay:tcp关闭
Nagle算法,不再缓存发送
srs_error_t SrsTcpConnection::set_tcp_nodelay(bool v) {     srs_error_t err = srs_success;     int r0 = 0;     socklen_t nb_v = sizeof(int);     int fd = srs_netfd_fileno(stfd);     int ov = 0;     if ((r0 = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &ov, &nb_v)) != 0) {         return srs_error_new(ERROR_SOCKET_NO_NODELAY, "getsockopt fd=%d, r0=%d", fd, r0);     } #ifndef SRS_PERF_TCP_NODELAY     srs_warn("ignore TCP_NODELAY, fd=%d, ov=%d", fd, ov);     return err; #endif     int iv = (v? 1:0);     if ((r0 = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, nb_v)) != 0) {         return srs_error_new(ERROR_SOCKET_NO_NODELAY, "setsockopt fd=%d, r0=%d", fd, r0);     }     if ((r0 = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, &nb_v)) != 0) {         return srs_error_new(ERROR_SOCKET_NO_NODELAY, "getsockopt fd=%d, r0=%d", fd, r0);     }     srs_trace("set fd=%d TCP_NODELAY %d=>%d", fd, ov, iv);     return err; }

2、min_latency:开启最小延迟模式
当开启最低延迟配置后,SRS会禁用mr(merged-read),并且在consumer队列中使用超时等待,大约每收到1-2个视频包就发送给客户端,达到最低延迟目标。
测试vp6纯视频流能达到0.1秒延迟
vhost mrw.srs.com {     # whether enable min delay mode for vhost.     # for min latence mode:     # 1. disable the publish.mr for vhost.     # 2. use timeout for cond wait for consumer queue.     # @see https://github.com/ossrs/srs/issues/257     # default: off     min_latency     off; }

3、queue_length: 设置队列大小
srs_utime_t queue_size = _srs_config->get_queue_length(req->vhost); publish_edge->set_queue_size(queue_size); ... void SrsMessageQueue::set_queue_size(srs_utime_t queue_size) {     max_queue_size = queue_size; } ... srs_error_t SrsMessageQueue::enqueue(SrsSharedPtrMessage* msg, bool* is_overflow) {     srs_error_t err = srs_success;     msgs.push_back(msg);     // If jitter is off, the timestamp of first sequence header is zero, which wll cause SRS to shrink and drop the     // keyframes even if there is not overflow packets in queue, so we must ignore the zero timestamps, please     // @see https://github.com/ossrs/srs/pull/2186#issuecomment-     if (msg->is_av() && msg->timestamp != 0) {         if (av_start_time == -1) {             av_start_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);         }         av_end_time = srs_utime_t(msg->timestamp * SRS_UTIME_MILLISECONDS);     }     if (max_queue_size <= 0) {         return err;     }     while (av_end_time - av_start_time > max_queue_size) {         // notice the caller queue already overflow and shrinked.         if (is_overflow) {             *is_overflow = true;         }         shrink();     }          return err; } ... srs_error_t SrsLiveConsumer::enqueue(SrsSharedPtrMessage* shared_msg, bool atc, SrsRtmpJitterAlgorithm ag) {     srs_error_t err = srs_success;     SrsSharedPtrMessage* msg = shared_msg->copy();     if (!atc) {         if ((err = jitter->correct(msg, ag)) != srs_success) {             return srs_error_wrap(err, "consume message");         }     }     if ((err = queue->enqueue(msg, NULL)) != srs_success) {         return srs_error_wrap(err, "enqueue message");     }     return err; }

4、gop_cache: 关闭两个IDR帧间的数据缓存
srs_error_t SrsRtmpConn::stream_service_cycle() { ...     bool enabled_cache = _srs_config->get_gop_cache(req->vhost);     srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%s/%s",         req->get_stream_url().c_str(), ip.c_str(), enabled_cache, info->edge, source->source_id().c_str(), source->pre_source_id().c_str());     source->set_cache(enabled_cache); ... void SrsLiveSource::set_cache(bool enabled) {     gop_cache->set(enabled); } ... void SrsGopCache::set(bool v) {     enable_gop_cache = v;          if (!v) {         clear();         return;     } } ... srs_error_t SrsGopCache::cache(SrsSharedPtrMessage* shared_msg) {     srs_error_t err = srs_success;          if (!enable_gop_cache) {         return err;     }          // the gop cache know when to gop it.     SrsSharedPtrMessage* msg = shared_msg;          // got video, update the video count if acceptable     if (msg->is_video()) {         // drop video when not h.264         if (!SrsFlvVideo::h264(msg->payload, msg->size)) {             return err;         }                  cached_video_count++;         audio_after_last_video_count = 0;     }          // no acceptable video or pure audio, disable the cache.     if (pure_audio()) {         return err;     }          // ok, gop cache enabled, and got an audio.     if (msg->is_audio()) {         audio_after_last_video_count++;     }          // clear gop cache when pure audio count overflow     if (audio_after_last_video_count > SRS_PURE_AUDIO_GUESS_COUNT) {         srs_warn("clear gop cache for guess pure audio overflow");         clear();         return err;     }          // clear gop cache when got key frame     if (msg->is_video() && SrsFlvVideo::keyframe(msg->payload, msg->size)) {         clear();                  // curent msg is video frame, so we set to 1.         cached_video_count = 1;     }          // cache the frame.     gop_cache.push_back(msg->copy());          return err; }

5、mw_latency: merge wirte混合写的延迟
srs_error_t SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { ... srs_utime_t mw_sleep = _srs_config->get_mw_sleep(req->vhost); if ((err = rohc->set_socket_buffer(mw_sleep)) != srs_success) {     return srs_error_wrap(err, "set mw_sleep %" PRId64, mw_sleep); } ... srs_error_t SrsRtmpConn::do_playing(SrsLiveSource* source, SrsLiveConsumer* consumer, SrsQueueRecvThread* rtrd) { ... mw_sleep = _srs_config->get_mw_sleep(req->vhost); skt->set_socket_buffer(mw_sleep); ... srs_error_t SrsTcpConnection::set_socket_buffer(srs_utime_t buffer_v) {     srs_error_t err = srs_success;     int r0 = 0;     int fd = srs_netfd_fileno(stfd);     socklen_t nb_v = sizeof(int);     int ov = 0;     if ((r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &ov, &nb_v)) != 0) {         return srs_error_new(ERROR_SOCKET_SNDBUF, "getsockopt fd=%d, r0=%d", fd, r0);     }     // the bytes:     //      4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536,     //      128KB=, 256KB=, 512KB=     // the buffer should set to sleep*kbps/8,     // for example, your system delivery stream in 1000kbps,     // sleep 800ms for small bytes, the buffer should set to:     //      800*1000/8=B(about 128KB).     // other examples:     //      2000*3000/8=B(about 732KB).     //      2000*5000/8=B(about 1220KB).     int kbps = 4000;     int iv = srsu2ms(buffer_v) * kbps / 8;     // socket send buffer, system will double it.     iv = iv / 2;     // set the socket send buffer when required larger buffer     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, nb_v) < 0) {         return srs_error_new(ERROR_SOCKET_SNDBUF, "setsockopt fd=%d, r0=%d", fd, r0);     }     if ((r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, &nb_v)) != 0) {         return srs_error_new(ERROR_SOCKET_SNDBUF, "getsockopt fd=%d, r0=%d", fd, r0);     }     srs_trace("set fd=%d, SO_SNDBUF=%d=>%d, buffer=%dms", fd, ov, iv, srsu2ms(buffer_v));     return err; }

6、mr off:merge read关闭混合度
SrsPublishRecvThread::SrsPublishRecvThread(SrsRtmpServer* rtmp_sdk, SrsRequest* _req,     int mr_sock_fd, srs_utime_t tm, SrsRtmpConn* conn, SrsLiveSource* source, SrsContextId parent_cid)     : trd(this, rtmp_sdk, tm, parent_cid) { ... mr_sleep = _srs_config->get_mr_sleep(req->vhost); ... void SrsPublishRecvThread::on_start() {     // we donot set the auto response to false,     // for the main thread never send message.      #ifdef SRS_PERF_MERGED_READ     if (mr) {         // set underlayer buffer size         set_socket_buffer(mr_sleep);                  // disable the merge read         rtmp->set_merge_read(true, this);     } #endif } ... void SrsPublishRecvThread::set_socket_buffer(srs_utime_t sleep_v) {     // the bytes:     //      4KB=4096, 8KB=8192, 16KB=16384, 32KB=32768, 64KB=65536,     //      128KB=, 256KB=, 512KB=     // the buffer should set to sleep*kbps/8,     // for example, your system delivery stream in 1000kbps,     // sleep 800ms for small bytes, the buffer should set to:     //      800*1000/8=B(about 128KB).     // other examples:     //      2000*3000/8=B(about 732KB).     //      2000*5000/8=B(about 1220KB).     int kbps = 5000;     int socket_buffer_size = srsu2msi(sleep_v) * kbps / 8;          int fd = mr_fd;     int onb_rbuf = 0;     socklen_t sock_buf_size = sizeof(int);     getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &onb_rbuf, &sock_buf_size);          // socket recv buffer, system will double it.     int nb_rbuf = socket_buffer_size / 2;     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, sock_buf_size) < 0) {         srs_warn("set sock SO_RCVBUF=%d failed.", nb_rbuf);     }     getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &nb_rbuf, &sock_buf_size);          srs_trace("mr change sleep %d=>%d, erbuf=%d, rbuf %d=>%d, sbytes=%d, realtime=%d",               srsu2msi(mr_sleep), srsu2msi(sleep_v), socket_buffer_size, onb_rbuf, nb_rbuf,               SRS_MR_SMALL_BYTES, realtime);          rtmp->set_recv_buffer(nb_rbuf); }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/208187.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午12:09
下一篇 2026年3月19日 下午12:09


相关推荐

  • 完毕port(CompletionPort)具体解释 – 手把手教你玩转网络编程系列之三

    完毕port(CompletionPort)具体解释 – 手把手教你玩转网络编程系列之三

    2021年12月10日
    39
  • 高级 PHP 工程师必备的编码技巧及思维

    高级 PHP 工程师必备的编码技巧及思维

    2022年2月14日
    43
  • mbus接口电路_数据总线

    mbus接口电路_数据总线本文来自我的163博客搬移春节放假前到现在,设计了一款采集器,采集器的设计按照标准的MBUS协议设计,设计容量为最大可同时对100只水表的水量数据进行采集。硬件设计:原理图设计上采用自顶向下的分层电路图设计方法,分为5个电路子图,分别为单片机小系统部分、电源部分、MBUS协议部分、短路保护部分、通信部分。1、单片机小系统部分的设计上将编程引脚的时钟和…

    2022年10月16日
    4
  • snmptrap报文_snmp指定发送IP

    snmptrap报文_snmp指定发送IP#/bin/shOidWarnHead=1.3.6.1.4.1.53151.2.15.4.1targetIp=172.16.101.34targetPort=162trapOid=1.3.6.1.4.1.53151.3.1warnIdOid=${OidWarnHead}.1.0warnIdVal=1500000200warnSnOid=${OidWarnHead}.2.0…

    2022年8月20日
    9
  • 微信拉群服务器忙请稍后再试,微信群拉人出现“未能添加新成员,请稍后再试”是什么原因?…

    微信拉群服务器忙请稍后再试,微信群拉人出现“未能添加新成员,请稍后再试”是什么原因?…很多做微信营销的朋友 当然少不了创建微信群 通过微信群做营销 在我们利用微信群做营销的过程中 经常会遇到一些问题 其中我们就是微信群拉人进入的时候就有一种情况 那就是出现 未能添加新成员 请稍后再试 这个是怎么回事 又该怎么解决呢 第 1 出现 未能添加新成员 请稍后再试 的原因 南阳微信营销马找钱自己本身也是在做微信群营销 可以说具有很强的发表权 也是遇到过这样的情况 如果你在互联网上搜索 未能

    2025年7月25日
    7
  • 前缀和、二维前缀和与差分的小总结

    前缀和、二维前缀和与差分的小总结在了解二维前缀和之前 我们首先需要了解一下什么是前缀和 如果我给你一串长度为 n 的数列 a1 a2 a3 an 再给出 m 个询问 每次询问给出 L R 两个数 要求给出区间 L R 里的数的和 你会怎么做 若是没有了解过前缀和的人看到这道题的想法可能是对于 m 次询问 我每次都遍历一遍它给的区间 计算出答案 这样子的方法固然没错 但是其时间复杂度达到了 O n m 如果数据量稍微大一点就有可能超时

    2026年3月17日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号