srs_app_server.cpp 

int SrsServer::listen()
{
int ret = ERROR_SUCCESS; if ((ret = listen_rtmp()) != ERROR_SUCCESS) {
return ret;
} if ((ret = listen_http_api()) != ERROR_SUCCESS) {
return ret;
} if ((ret = listen_http_stream()) != ERROR_SUCCESS) {
return ret;
} if ((ret = listen_stream_caster()) != ERROR_SUCCESS) {
return ret;
} return ret;
}

UDP的侦听是在 listen_stream_caster() 中

int SrsServer::listen_stream_caster()
{
int ret = ERROR_SUCCESS; #ifdef SRS_AUTO_STREAM_CASTER
close_listeners(SrsListenerMpegTsOverUdp); std::vector<SrsConfDirective*>::iterator it;
std::vector<SrsConfDirective*> stream_casters = _srs_config->get_stream_casters(); for (it = stream_casters.begin(); it != stream_casters.end(); ++it) {
SrsConfDirective* stream_caster = *it;
if (!_srs_config->get_stream_caster_enabled(stream_caster)) {
continue;
} SrsListener* listener = NULL; std::string caster = _srs_config->get_stream_caster_engine(stream_caster);
if (srs_stream_caster_is_udp(caster)) {
listener = new SrsUdpCasterListener(this, SrsListenerMpegTsOverUdp, stream_caster);
} else if (srs_stream_caster_is_rtsp(caster)) {
listener = new SrsRtspListener(this, SrsListenerRtsp, stream_caster);
} else if (srs_stream_caster_is_flv(caster)) {
listener = new SrsHttpFlvListener(this, SrsListenerFlv, stream_caster);
} else {
ret = ERROR_STREAM_CASTER_ENGINE;
srs_error("unsupported stream caster %s. ret=%d", caster.c_str(), ret);
return ret;
}
srs_assert(listener != NULL); listeners.push_back(listener); int port = _srs_config->get_stream_caster_listen(stream_caster);
if (port <= ) {
ret = ERROR_STREAM_CASTER_PORT;
srs_error("invalid stream caster port %d. ret=%d", port, ret);
return ret;
} // TODO: support listen at <[ip:]port>
if ((ret = listener->listen("0.0.0.0", port)) != ERROR_SUCCESS) {
srs_error("StreamCaster listen at port %d failed. ret=%d", port, ret);
return ret;
}
}
#endif return ret;
}

注意首先要在配置文档里有UDP的配置,可以参考 push.mpegts.over.udp.conf

new 了一个 SrsUdpCasterListener 类实例,并调用 listen()

SrsUdpCasterListener 继承自 SrsUdpStreamListener 没有override listen,

int SrsUdpStreamListener::listen(string i, int p)
{
int ret = ERROR_SUCCESS; // the caller already ensure the type is ok,
// we just assert here for unknown stream caster.
srs_assert(type == SrsListenerMpegTsOverUdp); ip = i;
port = p; srs_freep(listener);
listener = new SrsUdpListener(caster, ip, port); if ((ret = listener->listen()) != ERROR_SUCCESS) {
srs_error("udp caster listen failed. ret=%d", ret);
return ret;
} srs_info("listen thread current_cid=%d, "
"listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
_srs_context->get_id(), p, type, listener->fd(), i.c_str(), p); // notify the handler the fd changed.
if ((ret = caster->on_stfd_change(listener->stfd())) != ERROR_SUCCESS) {
srs_error("notify handler fd changed. ret=%d", ret);
return ret;
} srs_trace("%s listen at udp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd()); return ret;
}

又new了一个SrsUdpListener 调用 listen(),里面具体实现就是绑定socket

srs_app_listener.cpp

SrsUdpListener::cycle() 不停的接受数据,并响应:

int SrsUdpListener::cycle()
{
int ret = ERROR_SUCCESS; // TODO: FIXME: support ipv6, @see man 7 ipv6
sockaddr_in from;
int nb_from = sizeof(sockaddr_in);
int nread = ; if ((nread = st_recvfrom(_stfd, buf, nb_buf, (sockaddr*)&from, &nb_from, ST_UTIME_NO_TIMEOUT)) <= ) {
srs_warn("ignore recv udp packet failed, nread=%d", nread);
return ret;
} if ((ret = handler->on_udp_packet(&from, buf, nread)) != ERROR_SUCCESS) {
srs_warn("handle udp packet failed. ret=%d", ret);
return ret;
} if (SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS > ) {
st_usleep(SRS_UDP_PACKET_RECV_CYCLE_INTERVAL_MS * );
} return ret;
}

其中的hanler就是SrsUdpStreamListener::caster 也即 SrsMpegtsOverUdp 类的实例

srs_app_mpegts_udp.cpp

其中的 on_udp_packet() 响应接收数据事件,并保存到 buffer 变量中,最后调用 on_udp_bytes() 进行处理

int SrsMpegtsOverUdp::on_udp_packet(sockaddr_in* from, char* buf, int nb_buf)
{
std::string peer_ip = inet_ntoa(from->sin_addr);
int peer_port = ntohs(from->sin_port); // append to buffer.
buffer->append(buf, nb_buf); srs_info("udp: got %s:%d packet %d/%d bytes",
peer_ip.c_str(), peer_port, nb_buf, buffer->length()); return on_udp_bytes(peer_ip, peer_port, buf, nb_buf);
}

on_udp_bytes 收到数据后进行处理:

int SrsMpegtsOverUdp::on_udp_bytes(string host, int port, char* buf, int nb_buf)
{
// ... // use stream to parse ts packet.
int nb_packet = buffer->length() / SRS_TS_PACKET_SIZE;
for (int i = ; i < nb_packet; i++) {
char* p = buffer->bytes() + (i * SRS_TS_PACKET_SIZE);
if ((ret = stream->initialize(p, SRS_TS_PACKET_SIZE)) != ERROR_SUCCESS) {
return ret;
} // process each ts packet
if ((ret = context->decode(stream, this)) != ERROR_SUCCESS) {
srs_warn("mpegts: ignore parse ts packet failed. ret=%d", ret);
continue;
}
srs_info("mpegts: parse ts packet completed");
}
srs_info("mpegts: parse udp packet completed"); // erase consumed bytes
if (nb_packet > ) {
buffer->erase(nb_packet * SRS_TS_PACKET_SIZE);
} return ret;
}

注意参数里的buf没有用到,而是用的成员变量 buffer

首先,将数据流切成ts组(188字节,sync byte 0x47已经切除)

然后,调用 SrsStream 类来存储每个 ts 分组(其实就是封装一下)

最后,调用 SrsTsContext::decode 解码

int SrsTsContext::decode(SrsStream* stream, ISrsTsHandler* handler)
{
int ret = ERROR_SUCCESS; // parse util EOF of stream.
// for example, parse multiple times for the PES_packet_length(0) packet.
while (!stream->empty()) {
SrsTsPacket* packet = new SrsTsPacket(this);
SrsAutoFree(SrsTsPacket, packet); SrsTsMessage* msg = NULL;
if ((ret = packet->decode(stream, &msg)) != ERROR_SUCCESS) {
srs_error("mpegts: decode ts packet failed. ret=%d", ret);
return ret;
} if (!msg) {
continue;
}
SrsAutoFree(SrsTsMessage, msg); if ((ret = handler->on_ts_message(msg)) != ERROR_SUCCESS) {
srs_error("mpegts: handler ts message failed. ret=%d", ret);
return ret;
}
} return ret;
}

参数 handler 就是 SrsMpegtsOverUdp 本身

首先还是调用 SrsTsPacket::decode 进行解码

int SrsTsPacket::decode(SrsStream* stream, SrsTsMessage** ppmsg)
{
int ret = ERROR_SUCCESS; int pos = stream->pos(); // 4B ts packet header.
if (!stream->require()) {
ret = ERROR_STREAM_CASTER_TS_HEADER;
srs_error("ts: demux header failed. ret=%d", ret);
return ret;
} sync_byte = stream->read_1bytes();
if (sync_byte != 0x47) {
ret = ERROR_STREAM_CASTER_TS_SYNC_BYTE;
srs_error("ts: sync_bytes must be 0x47, actual=%#x. ret=%d", sync_byte, ret);
return ret;
} int16_t pidv = stream->read_2bytes();
transport_error_indicator = (pidv >> ) & 0x01;
payload_unit_start_indicator = (pidv >> ) & 0x01;
transport_priority = (pidv >> ) & 0x01;
pid = (SrsTsPid)(pidv & 0x1FFF); int8_t ccv = stream->read_1bytes();
transport_scrambling_control = (SrsTsScrambled)((ccv >> ) & 0x03);
adaption_field_control = (SrsTsAdaptationFieldType)((ccv >> ) & 0x03);
continuity_counter = ccv & 0x0F; // TODO: FIXME: create pids map when got new pid. srs_info("ts: header sync=%#x error=%d unit_start=%d priotiry=%d pid=%d scrambling=%d adaption=%d counter=%d",
sync_byte, transport_error_indicator, payload_unit_start_indicator, transport_priority, pid,
transport_scrambling_control, adaption_field_control, continuity_counter); // optional: adaptation field
if (adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) {
srs_freep(adaptation_field);
adaptation_field = new SrsTsAdaptationField(this); if ((ret = adaptation_field->decode(stream)) != ERROR_SUCCESS) {
srs_error("ts: demux af faield. ret=%d", ret);
return ret;
}
srs_verbose("ts: demux af ok.");
} // calc the user defined data size for payload.
int nb_payload = SRS_TS_PACKET_SIZE - (stream->pos() - pos); // optional: payload.
if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) {
if (pid == SrsTsPidPAT) {
// 2.4.4.3 Program association Table
srs_freep(payload);
payload = new SrsTsPayloadPAT(this);
} else {
SrsTsChannel* channel = context->get(pid);
if (channel && channel->apply == SrsTsPidApplyPMT) {
// 2.4.4.8 Program Map Table
srs_freep(payload);
payload = new SrsTsPayloadPMT(this);
} else if (channel && (channel->apply == SrsTsPidApplyVideo || channel->apply == SrsTsPidApplyAudio)) {
// 2.4.3.6 PES packet
srs_freep(payload);
payload = new SrsTsPayloadPES(this);
} else {
// left bytes as reserved.
stream->skip(nb_payload);
}
} if (payload && (ret = payload->decode(stream, ppmsg)) != ERROR_SUCCESS) {
srs_error("ts: demux payload failed. ret=%d", ret);
return ret;
}
} return ret;
}

先分析数据得到 Packet ID ,然后new出对应的解码类。

解码得到 SrsTsMessage,再回到 SrsMpegtsOverUdp::on_ts_message 进行处理

int SrsMpegtsOverUdp::on_ts_message(SrsTsMessage* msg)
{
// ... // parse the stream.
SrsStream avs;
if ((ret = avs.initialize(msg->payload->bytes(), msg->payload->length())) != ERROR_SUCCESS) {
srs_error("mpegts: initialize av stream failed. ret=%d", ret);
return ret;
} // publish audio or video.
if (msg->channel->stream == SrsTsStreamVideoH264) {
return on_ts_video(msg, &avs);
}
if (msg->channel->stream == SrsTsStreamAudioAAC) {
return on_ts_audio(msg, &avs);
} // TODO: FIXME: implements it.
return ret;
}

其重点是把 ts message 中的 payload 数据封装在 SrsStream 中,

然后调用 on_ts_video 和 on_ts_audio 分别进行处理。

对于video来说已经解码的是H.264流,所以进行了frame提取,并重新封装成flv格式,再通过rtmp转发出去。

对于audio,是对AAC数据提取frame,再封装成ADTS进行传输。

SRS源码——UDP的更多相关文章

  1. SRS源码—— Thread笔记

    SRS源码中的Thread是一层套一层,最终的Thread类是在 srs_app_thread.cpp 的 SrsThread 类 这里我们暂且先放下协程的概念,把它当线程来看,其逻辑如下: 1. 在 ...

  2. SRS源码——调用FFmpeg参数问题

    在SRS的Ingest功能中,会调用本地FFmpeg进行拉流转码, 调用的核心代码在srs_app_ffmpeg.cpp 的 SrsFFMPEG::start() 中: // memory leak ...

  3. SRS源码——Listener

    1. 整理了一下Listener相关的UML类图:

  4. docker中使用源码方式搭建SRS流媒体服务

    一.背景 搭建流媒体服务的方式一般会采用nginx+rtmp和srs服务两种,前者是nginx加上插件所用,而后者是专门为了为了流媒体而生,在这一节中我们将从头搭建srs流媒体服务 二. 运行环境 为 ...

  5. 海康摄像机使用GB28181接入SRS服务器的搭建步骤---源码安装的方式

    下载代码 地址:https://github.com/ossrs/srs-gb28181 https://github.com/ossrs/srs-gb28181.git 注意:使用的是含有gb281 ...

  6. TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端

    目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP部分的使用 框架源码结构 补充说明 源码地址 说明 之前有好几篇博客在讲TCP/UDP通信方 ...

  7. Nginx学习笔记(四) 源码分析&socket/UDP/shmem

    源码分析 在茫茫的源码中,看到了几个好像挺熟悉的名字(socket/UDP/shmem).那就来看看这个文件吧!从简单的开始~~~ src/os/unix/Ngx_socket.h&Ngx_s ...

  8. C#UDP(接收和发送源码)源码完整

    C#UDP(接收和发送源码)源码完整 最近做了一个UDP的服务接收和发送的东西.希望能对初学的朋友一点帮助. 源码如下: 一.逻辑--UdpServer.cs using System;using S ...

  9. 为SRS流媒体服务器添加HLS加密功能(附源码)

    为SRS流媒体服务器添加HLS加密功能(附源码) 之前测试使用过nginx的HLS加密功能,会使用到一个叫做nginx-rtmp-module的插件,但此插件很久不更新了,网上搜索到一个中国制造的叫做 ...

随机推荐

  1. 前后端分离之 跨域和JWT

    书接上回:https://www.cnblogs.com/yangyuanhu/p/12081525.html 前后端分离案例 现在把自己当成是前端,要开发一个前后分离的简单页面,用于展示学生信息列表 ...

  2. Springmvc-crud-06(路径忘记加上“/”错误)

    错误: 原因:自己马虎忘记加"  /  ",罚继续写代码┭┮﹏┭┮ 前端代码: <h1>添加功能</h1> <form action="te ...

  3. HGAME 2020 week1 web

    1.Cosmos 的博客 知识点:git source code leak 2.接 头 霸 王 Description HGAME Re:Dive 开服啦~ 打开题目,提示了"头" ...

  4. 我的18vps~

    自从买了18vps的香港虚拟主机后,就面临一个问题,浏览器无法访问它的apache服务,后来发现,需要同时开启nginx服务: /usr/local/nginx/sbin/nginx -c /usr/ ...

  5. vscode设置python代码补全时函数自动加上小括号

    vscode设置python代码补全时函数自动加上小括号 vscode的python代码补全插件默认安装时是不会自动补全括号的,感觉不是和方便 以下介绍下自动补上小括号的方法 可能部分同学设置了还是没 ...

  6. springboot 服务卡死 连接池查询无响应问题解决

    排查背景:基于nacos + springboot + druid +mybatis + mysql的环境,服务突然就出现不可访问,所有连接都超时,重启就可以使用一会,过一会就又不可用了 排查出来的原 ...

  7. Linux下载安装

    博客及下载 https://www.cnblogs.com/nongzihong/p/10475753.html centos镜像 下载 https://blog.csdn.net/sinat_365 ...

  8. div 悬浮

    一个小需求,鼠标移动一个产品那, 显示这个产品的具体信息 代码如下: <a href="javascript:void(0);" onclick="frameSea ...

  9. 基于SILVACO ATLAS的a-IGZO薄膜晶体管二维器件仿真(04)

    在eetop上有好多好东西啊: Silvaco_TCAD_中文教程1 不过这个教程里是Linux系统的,而且工艺仿真占了比较大的篇幅. defect region=1 nta=5e17 wta=0.1 ...

  10. 洛谷 P5016 龙虎斗(模拟)

    嗯... 题目链接:https://www.luogu.org/problem/P5016 这道题是一道模拟,不要考虑复杂,直接暴力枚举每一个点,看看加上s2之后两个势力的差值,找最小,记录下标. 注 ...