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-953383063
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=131072, 256KB=262144, 512KB=524288
// 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=100000B(about 128KB).
// other examples:
// 2000*3000/8=750000B(about 732KB).
// 2000*5000/8=1250000B(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=131072, 256KB=262144, 512KB=524288
// 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=100000B(about 128KB).
// other examples:
// 2000*3000/8=750000B(about 732KB).
// 2000*5000/8=1250000B(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);
Docker搭建SRS视频服务器
Docker 搭建 SRS(Simple Realtime Server)视频服务器是一种便捷的部署方式,利用 Docker 容器化技术可以快速实现流媒体服务环境的一键安装与运行。SRS 是一个高性能、易于扩展的实时流媒体服务器,支持 RTMP、WebRTC、HLS、HTTP-FLV 等多种流媒体协议。
搭建步骤简述如下:
拉取官方镜像:通过 docker pull ossrs/srs:<version> 命令从 Docker Hub 获取指定版本的 SRS 镜像,例如使用最新稳定版 ossrs/srs:4。
创建网络与数据卷:为了持久化数据和便于容器间通信,可创建自定义的 Docker bridge 网络,并关联必要的数据卷以存储配置文件和日志等。
映射端口:在运行容器时通过 -p 参数将宿主机与容器内部端口进行映射,如 -p 1935:1935 (RTMP 推流)、 -p 8080:8080 (HTTP API 和管理界面)以及其它所需的端口。
配置文件挂载:若需自定义 SRS 的配置,将本地的配置文件挂载到容器内部的相应位置,确保 SR
敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告: 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。我将带领大家一起来学习:SRS4的配置安装与直播推流拉流、FFmpeg使用API摄像头直播。具体内容包括:1.Ubuntu18和Centos7下下载源代码与快速编译srs4。2.SRS4集群Cluster。3.SRS4配置文件讲解。4.SRS4之启用webrtc播放。5.FFmpeg使用API摄像头直播。 音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢? 因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。 梅老师从事音视频与流媒体行业18年;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。
本文采用的 SRS 版本是 4.0-b8 , 下载地址:github
本文主要对 SRS RTMP 直播做延迟测试,然后介绍如何优化 RTMP 的延迟。为了不嵌套界面,介绍一下ffmpeg如何抓取某个应用屏幕。
先下载一个秒表程序,ledcount,运行界面如下:
FFmpeg 抓取 某个 窗口的命令如下:
ffmpeg.exe -f gdigrab -framerate 6 -i "title=abcds" out2.flv
由于 FFmpeg 不支持中文的窗口名称,所以需要 下载...
最近华为云提供华为云-云耀云服务器出了一项征文活动,可以免费试用。于是本人也迫不及待的白嫖体验一下华为云云耀云服务器L实例,该实例配置如下图,系统版本为本次测试方向是使用SRS开源服务。SRS是一个简单高效的实时视频服务器,支持RTMP/WebRTC/HLS/HTTP-FLV/SRT/GB28181。本次测试RTMP和WebRTC相关功能。
C++框架,SRS封装了SrsConfig类,配置指令类(SrsConfDirective),缓存区(SrsConfigBuffer),定义了使用方便且功能强大的配置文件,部分参考了nginx的配置文件,形式上类似与JSON,一个配置项里面可以包含子配置项,套娃的形式和JSON数组很像;可重新加载配置,重载配置时通过回调函数的形式执行其他模块的重载。
关于直播的技术文章不少,成体系的不多。我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面、深入地了解视频直播技术,更好地技术选型。
本系列文章大纲如下:
(一)采集
(二)处理
(三)编码和封装
(四)推流和传输
(五)延迟优化
(六)现代播放器原理
(七)SDK 性能测试模型
在上一篇推流和传输中
直播应用中,RTMP和HLS基本上可以覆盖所有客户端观看(参考:DeliveryHLS),HLS主要是延时比较大,RTMP主要优势在于延时低。
低延迟的部署实例参考:Usage: Realtime
低延时应用场景包括:
互动式直播:譬如2013年大行其道的美女主播,游戏直播等等各种主播,流媒体分发给用户观看。用户可以文字聊天和主播互动。
视频会议:SRS...
SRS 的信号处理也是一个比较有趣的设计,他会把信号事件 传递给 pipe 管道,因为 pipe 是 IO 操作,所以就可以用协程来处理了。配置文件的代码还是比较简单的,稍微比较复杂的是 配置文件的 reload 功能。你看一遍他的代码注释,然后自己断点调试一下,就知道是解析处理之后的结果是怎样的了。以外的配置选项进行热加载,但是我测试下来还是有一些配置是不支持热加载的,例如。命令行参数的解析逻辑比较简单,这里就不说了,跳过。开头的,这是 SRS 的命名习惯。开头的,而订阅者里面的方法是。
1、下载rtsp-simple-server
我下载的是windows版本的。下载完后直接运行exe文件即可启动。2、ubuntu 16.04环境下载、编译、运行SRS
3、使用ffmpeg给rtsp-simple-server推流
4、使用ffmpeg从rtsp-simple-server拉取rtsp流并转换为rtmp流后推送到srs服务器
5、从srs服务器拉流
或者用VLC打开RTMP直播流......