美文网首页
Openvidu Server 的WebRTC通讯实现 IV

Openvidu Server 的WebRTC通讯实现 IV

作者: Charles_linzc | 来源:发表于2021-05-21 17:05 被阅读0次
    1. OpenVidu 与 Kurento的元素(类)映射关系
      我们使用下图表示openvidu中元素(类)与kurento 中元素之间的映射。
      我们要举行一个会议,首先需要参会者。每一个参会者在openvidu中使用kurentParticipate实例表示;当参会者加入一个会议时,需要先在kurento服务器上创建一个会议。但是,实际上kurento并没有会议的概念,这里存在一个映射:

    a. 在openvidu中,一个激活的会议由kurentoSession实例表示。当创参会者加入会议时,openvidu会创建一个kurentoSession实例。
    b. 在kurento服务器上,一个会议由一个pipeLine 和NN个mediaEndpoint表示。N表是参会方数量,每一个参会方会创建一个发布媒体用的MediaElement和(n-1)个订阅其它媒体流用的MediaElement,它们被编排入一个PepleLine中, 形成NN的连接。

    所以,当第一个用户加入会议室时,系统会在Openvidu上创建一个KurentoSession实例,同时在Kurento上创建一个pipleLine, KurentoSession 实例引用了这个pipepline. N个用户会有N个kurentoSession, 但只有一个pipleline。PipleLine的描述是在Kurento Client包里。

      用户加入会议后,开始真正的进行音视频会议,他需要发布本地视频给其它人,并从其它人处订阅视频。在Openvidu里创建了相应的类来做这样的映射:
    
     a. 用户发布音视频需要创建一个publisherEndpoint, 一个publishEndpoint对应Kurento 服务器上的一个webRtcEndpoint;所以发布视频的过程就是用户通过自己的kurentoSession(对应会议),在指定的pipeLine上创建一个webrtcEndpoint.
    b.  用户接收音视频需要创建(n-1)个subscribeEndpoint, 一个subscribeEndpoint也对应kurento服务器上的一个webRtcEndpoint,每一个新的用户加入会议并发布音视频,其它用户都需要新建一个subscribeEndpoint以及对应的webrtcEndpoint。
    
    image.png
    1. Openvidu里的管理器
      Openvidu中定义了5个管理器 KmsManager, SessionManager, LoadManager和RecordManager, MediaNodeStatusManager。
      KmsManager主要管理KMS,它负责Openvidu中添加、移除kms实例,以及根据负载获取kms等。它关联引用loadManager来完成获取个kmsload的工作。创建kmsManager时会关联一个sessionManager, 用来负责会议的引用。
      kmsManager中定义了一个generateKurentoConnectionListener:
      protected JsonRpcWSConnectionListener generateKurentoConnectionListener(final String kmsId)
      说明它负责Kms 远程连接的处理和维护。在社区版中KmsManager的具体实现类是FixedOneKmsManager,也就是说社区版只支持一个KMS,所以kmsManager中的load管理、状态管理实际都已经被裁剪了。而利用社区版开发可扩展的openviduserver,主要的一个工作就在实现一个支持多kms的kmsManager子类,同时增加loadManager,MediaNodeStatusManager的能力。
      image.png

    管理器中另外一个重要的是sessionManager,session代表的是会议,所以sessionMananger 实际就是所有具体会议的管理. 在io.openvidu.server.core包下的SessionManager只是一个虚类,它声明了一些会议的操作方法:

    public abstract void joinRoom(...);  //加入会议
        public abstract boolean leaveRoom(..); //离开会议
        public abstract void publishVideo(...);  //发布音视频
        public abstract void unpublishVideo(...); //取消发布音视频
        public abstract void subscribe(...);  //订阅音视频
        public abstract void unsubscribe(...); //取消订阅音视频
           public abstract void streamPropertyChanged(...); //音视频流属性变更处理器
        public abstract void onIceCandidate(...); // webrtc 通信建立时收到新candidate处理器
        public abstract boolean unpublishStream(..);//取消发布具体stream
           public abstract Participant publishIpcam(...);//打开视频流** 
    

    这些方法都和会议有关, 可以发现,上面的功能通常对应我们音视频软件进入会议室的功能。


    image.png

    开openvidu中,它的具体的实现是KurentoSessionManager,它会在server启动的时候初始化。
    在III中说了,sessionid 代表的会议号,创建会议的时候会创建一个sessionNotActive(Session类)对象,代表的是还未正式使用的会议,当第一个用户首次加入的时候,才会正式使用这个会议,KurentoSessionManager的joinRoom方法描述了相关的逻辑。
    与sessionNotActive不同,一个开始使用的会议用KurentoSession来表示(继承自Session),首次加入会议, 需要创建这个Ksession, 它会指定一个具体的Kurrento Server,ksession的创建需要指定具体kms,用来表示在具体哪个KMS创建会议。社区版实际上只有一个KMS,但在实现上如下图, 已经默认使用获取最小负载的方式获得kms。


    image.png

    sessionManager对外提供会议操作功能的统一入口,每个会议对应的kurentoSession负责实际与kurento server的通信,来完具体的会议操作。所以在kurentoSession中我们可以看到相类似的会议功能定义:

    public void join(Participant participant) //加入会议
    public void newPublisher(Participant participant)//创建音视频发布端 
    public void cancelPublisher(...) //取消音视频发布端
    public void leave()//离开会议
    public boolean close(EndReason reason) //关闭会议
    public void sendIceCandidate()//发送candidate
    
    1. Openvidu Server与WebRTC的通信


      image.png

    上图是一个包含有浏览器、application 、 openvidu server, 、kurento server 等在内的一个逻辑通讯图。
    浏览器端加载会议应用程序,通过http协议与application server通信,完成业务请求和获取用于会议的token和sessionID.
    applicaiton server只负责业务请求,它通过与openvidu server通信来生成浏览器客户端加入会议需要的token和sessionID信息。
    浏览器获取token和sessionid后,与openvidu建立websocket连接,它将openvidu作为webrtc中的singal server,与openvidu通信,完成建立webrtc所需要的singal通信。
    与webrtc中描述的P2P通讯不同,kurento server 充当代理,与每一个参与方建立p2p连接,通过创建pipleline和编排media endpoint完成多方的通讯。 但是Kurento Server与任意参与方建立的通讯仍旧是P2P通讯。所以,浏览器会与kurento server建立webrtc连接。 他们的通讯默认是RTP over UPD, 也可以是RTP over TCP。
    openvidu与kurento也是通过websocket连接进行通讯的,与kurento的通信包含两个方面:
    a. 作为控制方,创建pipleline,根据加入的用户创建media endpoint, 并编排他们。
    b。 作为信号服务器, 与Kurento进行webrtc连接时需要的信号通讯:例如发送sdp, icecandidate,sdp响应等,由于kurento并不是浏览器端,Sdp answser的创建,也是由openvidu完成的。
    Openvidu与Kurento之间的通讯编码使用json RPC方式。 kurento 提供了client 包,方便opnvidu 实现rpc调用。

    1. JsonRPC 调用
      openvidu 本身名没有提供与kurento 的jsonRPC通信的编码,它是由kurento 提供的客户端包来实现的。
      Openvidu中提供了Kms类,它逻辑上代表一个远程的kurento media server,着这个类中包含了一个kms client属性:
      private KurentoClient client;
      KurentoClient来源于org.kurento.client包,它作为openvidu方的stub与openvidu完成实际通讯。但KurentoClient的初始化并没有在kms中,而是由KmsManager定义:
      public abstract List<Kms> initializeKurentoClients(List<KmsProperties> kmsProperties, boolean disconnectUponFailure)
      社区版的FixedOneKmsManager类,给出了一个该方法的实现,如下图,kurentoclient是通过JsonRpcClientNettyWebSocket创建的,JsonRpcClientNettyWebSocket是一个实现了基本jsonRPC逻辑的webSocket封装。而JsonRpcWSConnectionListener负责连接状态的监听。
      image.png
      可以这么理解,KurentoClient作为连接器,将openvidu端的对象调用,编码为jsonrpc请求,在kurento上变为Kurento上的被调用对象,并执行方法调用。

    4.1 PipleLine
    org.kurento.client包里包含一个pipleLIne类,它代表kurento server上的media pipleLine元素; 前面已经提到,一个会议对应一个kurencto pipleLine。而在openvidu中kurentoSession代表一个会议,它包含有一个pipleliene属性:
    private MediaPipeline pipeline;
    在首个用户加入会议的时候,会创建PipleLine实例:

    @Override
        public void join(Participant participant) {
            checkClosed();
            createPipeline();
            KurentoParticipant kurentoParticipant = new KurentoParticipant(participant, this, this.kurentoEndpointConfig,
                    this.openviduConfig, this.recordingManager);
            participants.put(participant.getParticipantPrivateId(), kurentoParticipant);
            log.info("SESSION {}: Added participant {}", sessionId, participant);
            if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
                kurentoEndpointConfig.getCdr().recordParticipantJoined(participant, sessionId);
            }
        }
    

    查看createPipeline方法可以看到,pipleLine使用kurentoclient来创建的。
    kms.getKurentoClient().createMediaPipeline(...)
    PipleLine创建好一个,将作为会议的具体Rpc对象负责其它对象的创建和方法调用。

    4.2 MediaEndpoint,publisherEndpoint、SubscriberEndpoint
    openvidu里定义了一个个类:MediaEndpoint。 它对应的是kurento server上MediaElement的抽象,下图是Kurento上的元素概念:


    image.png

    在MediaEndpoint类中,定义了三个endpoint属性,代表三种连接类型:
    ···

    private WebRtcEndpoint webEndpoint = null;  //WebRtc 类型
    private RtpEndpoint endpoint = null;                // RTP类型
    private PlayerEndpoint playerEndpoint = null;
    

    ···
    WebRtcEndpoint 、RtpEndpoint 、PlayerEndpoint 这三个类来自kurent client包,代表JSonrpc的客户端类,在上图的kurento元素中能够找到对应的元素。
    MediaEndpoint对三个类做了风中,使用endpointType来表示当前是那种类型(也就是哪个引用有值)。它有两个子类publisherEndpoint和SubscriberEndpoint,分别表示一个用于publish的mediaElement 和用于subscribe的mediaElement 。 在前文提到, openvidu的会议在kurento server上表示N*N 模型的media element关系, Server会为每一个参会者创建一个用于音视频发布的mediaElement 和(n-1)个用于订阅其它用户发布的音视频的media element,publisherEndpoint和SubscriberEndpoint对应的就是这个概念。
    无论publisherEndpoint和SubscriberEndpoint是WebRtcEndpoint 、 RtpEndpoint,PlayerEndpoint中的哪个类型,它们封装的rpc对象都由 pipeline创建。

    WebRtcEndpoint.Builder builder = new WebRtcEndpoint.Builder(pipeline);
    builder.buildAsync(new Continuation<WebRtcEndpoint>() {。。。})
    

    4.3 KurentoParticipant
    openvidu中的KurentoParticipant类代表的是参会方(不同于我们平时理解的用户),每个用户加入会议后才会创建KurentoParticipant,在"3.Openvidu Server与WebRTC的通信"的示例图中表征的是一个浏览器与Kurento server的连接。一个用户可以打开多个浏览器页面,每个加入会议的页面实际上都代表一个KurentoParticipant。
    KurentoParticipant没有具体的JsonPRC对象,相反它拥有一个PublisherEndpoint和
    一组subscribers,它们在kurento server刚好表示一个会议参与方:

    private PublisherEndpoint publisher;
    private final ConcurrentMap<String, SubscriberEndpoint> subscribers = new ConcurrentHashMap<String, SubscriberEndpoint>();
    

    由于KurentoParticipant代表的是会议的参会方,这个类中定义了几乎所有与publisher和subscribers 有关的操作:

    public void createPublishingEndpoint(MediaOptions mediaOptions, String streamId) 
    public String publishToRoom(SdpType sdpType, String sdpString, boolean doLoopback, boolean silent)
    public void unpublishMedia(EndReason reason, Long kmsDisconnectionTime) 
    public String receiveMediaFrom(Participant sender, String sdpOffer, boolean silent)
    public void close(EndReason reason, boolean definitelyClosed, Long kmsDisconnectionTime) 
    public void addIceCandidate(String endpointName, IceCandidate iceCandidate)
    public void sendIceCandidate(String senderPublicId, String endpointName, IceCandidate candidate)
    

    4.4 KurentoSession
    KurentoSession 代表的是一个会议,在kurento上对应的是pipeline mediaElement,但它不属于JsonRPC对象,它对pipeline mediaElement的操作是由PipleLine Jsonrpc对象来完成的,所以它包含由pipeline属性:

    private MediaPipeline pipeline;
    

    这个类中定义了很多会议操作的方法:

    public void join(Participant participant)
    public void leave(String participantPrivateId, EndReason reason)
    public boolean close(EndReason reason)
    public void sendIceCandidate(String participantPrivateId, String senderPublicId, String endpointName,...
    private void removeParticipant(Participant participant, EndReason reason)
    

    浏览器客户端在创建连接获取token和session时,并不会直接创建KurentoSession,默认的只会创建一个session对象,并把它放在sessionNotActive组里。当用户Join会议的时候(调用KurentoSessionManager.joinRoom(Participant participant, String sessionId, Integer transactionId)),才会创建KurentoSession 实例。

    image.png

    相关文章

      网友评论

          本文标题:Openvidu Server 的WebRTC通讯实现 IV

          本文链接:https://www.haomeiwen.com/subject/bnstjltx.html