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. PHP+Mysql防止SQL注入的方法

    这篇文章介绍的内容是关于PHP+Mysql防止SQL注入的方法,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 方法一: mysql_real_escape_string -- 转义 S ...

  2. itest(爱测试) 4.3.0 发布,开源BUG 跟踪管理 & 敏捷测试管理软件

    itest 简介:查看简介 test 开源敏捷测试管理,testOps 践行者.可按测试包分配测试用例执行,也可建测试迭代(含任务,测试包,BUG)来组织测试工作,也有测试环境管理,还有很常用的测试度 ...

  3. 201771010135 杨蓉庆《2018面向对象程序设计(java)课程学习进度条》

                                                                                                        ...

  4. 入门chrome插件开发教程和经验总结,一篇就搞掂!

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_44244857/articl ...

  5. 【转】直播流程,视频推流,视频拉流,简介,SMTP、RTMP、HLS、 PLPlayerKit

    原:https://www.cnblogs.com/baitongtong/p/11248966.html 1 .音视频处理的一般流程: 数据采集→数据编码→数据传输(流媒体服务器) →解码数据→播放 ...

  6. linux创建用户组、用户

    创建用户组 groupadd -g 1024 nameinfo 创建用户 首先创建目录 mkdir -p /home 创建用户 useradd -g nameinfo -u 1024 -d /home ...

  7. 【PAT甲级】1078 Hashing (25 分)(哈希表二次探测法)

    题意: 输入两个正整数M和N(M<=10000,N<=M)表示哈希表的最大长度和插入的元素个数.如果M不是一个素数,把它变成大于M的最小素数,接着输入N个元素,输出它们在哈希表中的位置(从 ...

  8. 【C语言】函数调用的简单例子

    #include<stdio.h> void p1() { printf("******************\n"); } void p2() { printf(& ...

  9. sql注入小姿势

    利用/*!union*/可以绕过对union的过滤

  10. xml配置问题--------不允许有匹配 "[xX][mM][lL]" 的处理指令目标

    剪不断,理还乱,是BUG   相遇:不允许有匹配 "[xX][mM][lL]" 的处理指令目标   在编写MyBatic框架时,纯手编写mybatic-config.xml文件,遇 ...