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. Python实现云服务器防止暴力密码破解

    云服务器防止暴力密码破解 云服务器暴露在公网上,每天都有大量的暴力密码破解,更换端口,无济于事,该脚本监控安全日志,获取暴力破解的对方ip,加入hosts黑名单 路径说明 描述 路径 登录安全日志 / ...

  2. 在IDEA离线安装lombok插件

    1.打开,找到自己IDEA版本,idea http://plugins.jetbrains.com/plugin/6317-lombok/versions 2.下载,导入安装

  3. T114048 [RC-02] yltx数对 (打表)

    这题如果全部打表的话,文件大小会有65kb,超了,所以只打出一半,前一半用程序算就可以了,并不会超时. 如果算法优化的好,其实可以打的更少. #include <bits/stdc++.h> ...

  4. java篇 之 集合

    集合   链接:https://blog.csdn.net/weixin_42504145/article/details/83119088 数组: java的数组既可以存储基本数据类型,也可以存储引 ...

  5. C语言与汇编的嵌入式编程:main中模拟函数的调用(两数交换)

    编写一个两数交换函数swap,具体代码如下: #include<stdio.h> void swap(int *p1,int *p2) { int temp; temp = *p1; *p ...

  6. Celery异步框架

    一.什么是celery Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统 专注于实时处理的异步任务队列 同时也支持任务调度 二.Celery架构 Celery的架构由三部分组成,消息中间 ...

  7. ProtoBuf试用与JSON的比较

    介绍 ProtoBuf 是google团队开发的用于高效存储和读取结构化数据的工具.什么是结构化数据呢,正如字面上表达的,就是带有一定结构的数据.比如电话簿上有很多记录数据,每条记录包含姓名.ID.邮 ...

  8. 在CDN不能使用的时候加载自己服务器的资源

    <script src="http://wlib.sinaapp.com/js/jquery/1.7.2/jquery.min.js"></script> ...

  9. 《创业者技能树》--创业课--14天复盘第一课candy2--HHR计划

    1,厉害的人是如何分析问题的?

  10. WinForm开发(5)——DataGridView控件(3)——DataGridView控件操作

    一.禁止用户改变DataGridView的列宽.行高.列头高度 1.// 禁止用户改变DataGridView1的所有列的列宽 DataGridView1.AllowUserToResizeColum ...