专栏首页音视频直播技术专家medooze源码分析--启动服务器

medooze源码分析--启动服务器

createEndpoint

当我们打开index.js 后,看到的第一个重要的 API就是createEndpoint函数了。我们就以这个 API为开头,一步一步的分析一下medooze的运行机制。

createEndpoint函数定义在 medooze-media-server 库中的 lib 目录下的 MediaServer.js 中。代码如下:

/**
 * Create a new endpoint object
 * @memberof MediaServer
 * @param {String} ip   - External IP address of server, to be used when announcing the local ICE candidate
 * @returns {Endpoint} The new created endpoing
 */
MediaServer.createEndpoint = function(ip)
{
        //Cretate new rtp endpoint
        return new Endpoint(ip);
};

它的作用是公布 ICE 本地候选者,当做一个终端。

顺着这个函数我们继续往下看,在该函数中会创建一个 Endpoint 对象。

Endpoint

medooze-media-server 库中的 lib 目录下的 Endpoint.js 文件是 Endpoint 的类文件。该类的作用如下:

/**
 * An endpoint represent an UDP server socket.
 * The endpoint will process STUN requests in order to be able to associate the remote ip:port with the registered transport and forward any further data comming from that transport.
 * Being a server it is ICE-lite.
 */
/**
 *endpoint 表示UDP服务器套接字。
 *endpoint 将处理STUN请求,以便能够将远程ip:port与注册的传输相关联,并转发来自该传输的任何进一步的通信数据。
 *作为一个服务器,它是轻量级的ICE。
*/

在创建 Endpoint 对象时,会调用它的构造函数。其代码如下:

constructor(ip)
 {
     //Store ip address of the endpoint
     this.ip = ip;
     //Create native endpoint
     this.bundle = new Native.RTPBundleTransport();
     //Start it
     this.bundle.Init();
     //Store all transports
     this.transports = new Set();
     //Create candidate
     this.candidate = new CandidateInfo("1", 1, "UDP", 33554431, ip, this.bundle.GetLocalPort(), "host");
     //Get fingerprint (global at media server level currently)
     this.fingerprint = Native.MediaServer.GetFingerprint().toString();
}

通过上面的代码可以看到 Endpoint 包括以下几个成员:

  • ip : STUN 服务器IP地址。
  • bundle:native层的Endpoint。
  • transports: 这是一个集合,用于存放所有用到的传输协议
  • candidate: ICE 候选者
  • fingerprint: 指纹,用于网络安全。

接下来,我们分别看一下 Native.RTPBundleTransport 和 CandidateInfo。

RTPBundleTransport

RTPBundleTransport 类定义在 media-server 中的 include/RTPBundleTransport.h文件中。该类的构造函数如下:

RTPBundleTransport::RTPBundleTransport()
{
        //Init values
        socket = FD_INVALID;
        port = 0;
        
        //No thread
        setZeroThread(&thread);
        running = false;
        //Mutex
        pthread_mutex_init(&mutex,0);
        pthread_cond_init(&cond,0);
}

Endpoint 类的构函数中,首先创建了 RTPBundleTransport对象,然后又调用了该对象的 Init方法,其代码如下:

int RTPBundleTransport::Init()
{
        int retries = 0;

        sockaddr_in recAddr;

        //Clear addr
        memset(&recAddr,0,sizeof(struct sockaddr_in));
        //Init ramdon
        srand (time(NULL));

        //Set family
        recAddr.sin_family      = AF_INET;

        //Get two consecutive ramdom ports
        while (retries++<100)
        {
                ...

                //Create new sockets
                socket = ::socket(PF_INET,SOCK_DGRAM,0);
                //Get random
                port = (RTPTransport::GetMinPort()+(RTPTransport::GetMaxPort()-RTPTransport::GetMinPort())*double(rand()/double(RAND_MAX)));
                //Make even
                port &= 0xFFFFFFFE;
                //Try to bind to port
                recAddr.sin_port = htons(port);
                //Bind the rtp socket
                if(bind(socket,(struct sockaddr *)&recAddr,sizeof(struct sockaddr_in))!=0)
                        //Try again
                        continue;

                ...
                Start();
                //Done
                Log("<RTPBundleTransport::Init()\n");
                //Opened
                return port;
        }

        //Error
        Error("-RTPBundleTransport::Init() | too many failed attemps opening sockets\n");

        //Failed
        return 0;
}

通过上面的代码我们可以看到, 在Init函数中,主要做了两件事儿:

  • 绑定端口号,打开sokcet服务。
  • 通过 Start() 启动了一个新的线程。对于线程的启动与所做的事情我们将在下篇文章中再做介绍。

CandidateInfo

在另外一个称为 semantic-sdp-js的项目中的lib中可以找到 CandidateInfo 类的定义。其构造函数如下:

/**
* CanditateInfo constructor
* @constructor
* @alias CandidateInfo
* @param {String} foundation
* @param {Number} componentId
* @param {String} transport
* @param {Number} priority
* @param {String} address
* @param {Number} port
* @param {String} type
* @param {String} relAddr
* @param {String} relPort
*/
constructor(foundation, componentId, transport, priority, address, port, type, relAddr, relPort) {
    this.foundation         = foundation;
    this.componentId        = componentId;
    this.transport          = transport;
    this.priority           = priority;
    this.address            = address;
    this.port               = port;
    this.type               = type;
    this.relAddr            = relAddr;
    this.relPort            = relPort;
}

其代码非常简单,就是将传入的一些参数保存起来。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 大话WebRTC的前世今生

    音视频可以说是人类与生俱来的需求,人一出生就要用耳朵听,用眼睛看。中国的古代神话中为此还专门设置了两位神仙(千里眼和顺风耳),他们可以听到或看到千里之外的声音或...

    音视频_李超
  • 通过WebRTC进行实时通信-概述

    使用WebRTC构建获取视频、从 webcam获取快照,端与端共享应用。通过这种方法,我们来学习如何使用核心 WebRTC API ,并通过 Node.js建立...

    音视频_李超
  • Android端实现1对1音视频实时通话

    在学习 WebRTC 的过程中,学习的一个基本步骤是先通过 JS 学习 WebRTC的整体流程,在熟悉了整体流程之后,再学习其它端如何使用 WebRTC 进行互...

    音视频_李超
  • 原创|长文|孙子兵法| 百万级订单如何“拆零拣选”?

    大家好,我是智能仓储物流技术研习社,社长,老K。距离前一篇:“整箱自动化拣选的秘密”有一段时间了,老K打算这次斗胆再分享一下拆零拣选的自动化拣选的内容。

    老King
  • 【原创】葵花宝典!自动化拣选的秘密(整箱拣选)- 偷摸收藏,练吧

    之前的文章为大家整理了物料单元在仓储系统中如何实现自动化存储以及出入库环节的一些内容:

    老King
  • C#中返回值封装

          在平时开发过程中常常需要取一个方法的返回值,BOSS写了一个返回值类,做个练习以备不时之需: 返回值支持泛型和非泛型 先贴上代码: 非泛型返回值类:...

    用户1055830
  • 【更正】“给自定义控件(Web Control)添加事件的几种方法”有一个不太准确的地方。

        上一篇写了一下如何在自定义控件里面添加事件,由简单的开始,一步一步实现了几种添加事件的方式,由于当时只给自定义控件添加了一种外部事件,测试的时候没有什...

    用户1174620
  • 23.opengl高级-抗锯齿

    参考上图,几何图形是连续的坐标连接实现的,实际屏幕上的像素是离散化的点,分辨率越低的屏幕离散越剧烈,在图形的边缘必然会产生锯齿。

    用户1068165
  • 逻辑漏洞之密码找回漏洞(semcms)

    1 首先尝试正常找回密码流程,选择不同的找回方式,记录所有数据包 2 分析数据包,找到敏感部分 3 分析后台找回机制所采用的验证手段 4 修改数据...

    宸寰客
  • docker使用记录

    最近小编在测试过程中,不少测试的任务都是在docker下进行,整体没有像之前那么直观和明了,于是对近期的使用做一个简单的记录和整理,也方便进一步...

    用户5521279

扫码关注云+社区

领取腾讯云代金券