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. 部署etcd中使用ansible进行变量初始化

    ansible-playbook 要进行默认变量的生产,可以依靠jinja 的模板渲染功能 看几个官方给出的例子 调用setup 中的变量 例如 setup 中的变量层级为 ansible_eth0_ ...

  2. Codeforces Round #601 (Div. 2)E(寻找质因子,DP)

    先分解质因数,对于当前a[i],假设当前的质因数为x,这个位置要满足能被k整除,有两个可能,要么是它向后一个转移x%k个,要么是后一个向它转移k-x%k个. 对于每一个a[i]满足后,因为只会对下一个 ...

  3. Java数组和方法

    1. 数组可以作为方法的参数 package cn.itcast.day05.demo04; /* 数组可以作为方法的参数. 当调用方法的时候,向方法的小括号进行传参,传递进去的其实是数组的地址值. ...

  4. 【C语言】字符数组,碎碎念

      存储方法: (1)字符数组赋值 ①初始化 ]={"China'} 或 ]="China' 注意:字符串可以不加{},单字符必须加 ]={,,} ②键盘输入 () char a; ...

  5. 【C语言】赋值运算中的类型转换

    #include<stdio.h> int main() { int a, b; double x = 1.54; char ch; a = x; x = ; b = 'a'; ch = ...

  6. redhat7.6 配置主从DNS

    主DNS配置include指向的配置文件 /etc/named.rfc1912.zone 下面图片配置内容/etc/named.rfc1912.zones 从DNS配置 /etc/named.conf ...

  7. acm数论之旅(转载)---最大公约数与最小公倍数

    gcd(a, b),就是求a和b的最大公约数 lcm(a, b),就是求a和b的最小公倍数 然后有个公式 a*b = gcd * lcm     ( gcd就是gcd(a, b), ( •̀∀•́ ) ...

  8. JavaSE复习~基本语法规则

    java程序的基本结构 java程序的文件后缀为 .java 一个java文件中可以写许多内容,但有且只能有一个 public 修饰的 class 类(class)是Java程序中所有源代码的基本组成 ...

  9. 《Web安全攻防 渗透测试实战指南》 学习笔记 (二)

    Web安全攻防 渗透测试实战指南   学习笔记 (二)   第二章  漏洞环境及实践  

  10. 吴裕雄 python 神经网络——TensorFlow训练神经网络:全模型

    import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data INPUT_NODE = 784 ...