Room demo tutorial - Kurento Room 6.1.1 documentation

Room demo tutorial

本教程是使用Room API SDK开发多协作应用程序的指南,共有三个基本结构,也就是三个文件夹,三个工程——kurento-room-server, kurento-room-client-jskurento-room-demo
代码

一、服务器端代码(server side code)

主类是KurentoRoomServerApp.java,同样为一个springboot应用类,我们将会在这个类里用Spring bean的方式来实例化构成服务器端的各个组件。

1.带通知的房间管理(room management)

这里用了 Room SDK 来管理房间和用户,我们选择的API——NotificationRoomManager类是一个通知风格的API,把它定义为一个Spring bean,以便以后依赖的注入。
但首先我们要给NotificationRoomManager的构造器提供一个 UserNotificationService 的实例作为参数,代码中为notificationService,它是一个 JsonRpcNotificationService 类型的对象,用于存储JSON-RPC会话,以支持向客户按发送响应和通知。另一个参数为kmsManager。下图代码先定义了两个参数,再用两个参数送到构造器来定义 NotificationRoomManager

  @Bean
  @ConditionalOnMissingBean
  public KurentoClientProvider kmsManager() {

    JsonArray kmsUris = getPropertyJson(KMSS_URIS_PROPERTY, KMSS_URIS_DEFAULT, JsonArray.class);
    List kmsWsUris = JsonUtils.toStringList(kmsUris);

    if (kmsWsUris.isEmpty()) {
      throw new IllegalArgumentException(KMSS_URIS_PROPERTY
          + " should contain at least one kms url");
    }

    String firstKmsWsUri = kmsWsUris.get(0);

    if (firstKmsWsUri.equals("autodiscovery")) {
      log.info("Using autodiscovery rules to locate KMS on every pipeline");
      return new AutodiscoveryKurentoClientProvider();
    } else {
      log.info("Configuring Kurento Room Server to use first of the following kmss: " + kmsWsUris);
      return new FixedOneKmsManager(firstKmsWsUri);
    }
  }

  @Bean
  @ConditionalOnMissingBean
  public JsonRpcNotificationService notificationService() {
    return new JsonRpcNotificationService();
  }

  @Bean
  @ConditionalOnMissingBean
  public NotificationRoomManager roomManager() {
    return new NotificationRoomManager(notificationService(), kmsManager());
  }

2.通信(signaling)

我们的demo用Kureto 提供的 JSON-RPC 服务器库来实现与客户端的交互。
我们为进来的信息注册了一个handler RoomJsonRpcHandler(整个RoomJsonRpcHandler.java就定义了这一个类)以便之后可以根据方法名字来处理请求。这个类 RoomJsonRpcHandler实现了之前提到的Websocket API。
在向这个API的registerJsonRpcHandlers方法中(一看到register应该反应过来这个方法在主类中,即KurentoRoomServerApp.java)添加这个handler(RoomJsonRpcHandler类实例化而来的roomHandler)时,要指定路径。

  @Bean
  @ConditionalOnMissingBean
  public RoomJsonRpcHandler roomHandler() {
    return new RoomJsonRpcHandler(userControl(), notificationService());
  }

  @Override
  public void registerJsonRpcHandlers(JsonRpcHandlerRegistry registry) {
    registry.addHandler(roomHandler().withPingWatchdog(true), "/room");
  }

回到RoomJsonRpcHandler.java类中,RoomJsonRpcHandler类的主方法handleRequest()会在每次收到客户端的请求时被触发,再出发的时候所有Websocket交流会在一个会话内完成,并且JSON-RPC库会给每个会话提供一个引用。一次请求-应答交换称为一次事务。
应用会存储每个用户对应的绘画和事务,从而notificationService(主类中定义的)在从 Room SDK 中被调用的时候就可以向客户端发相应或服务器事件了。

  public final void handleRequest(Transaction transaction, Request request)
      throws Exception {

    String sessionId = null;
    try {
      sessionId = transaction.getSession().getSessionId();
    } catch (Throwable e) {
      log.warn("Error getting session id from transaction {}", transaction, e);
      throw e;
    }

    updateThreadName(HANDLER_THREAD_NAME + "_" + sessionId);

    log.debug("Session #{} - request: {}", sessionId, request);

    notificationService.addTransaction(transaction, request);

    ParticipantRequest participantRequest = new ParticipantRequest(sessionId,
        Integer.toString(request.getId()));

    transaction.startAsync();

    switch (request.getMethod()) {
      case ProtocolElements.JOINROOM_METHOD :
        userControl.joinRoom(transaction, request, participantRequest);
        break;
      case ProtocolElements.PUBLISHVIDEO_METHOD :
        userControl.publishVideo(transaction, request, participantRequest);
        break;
      case ProtocolElements.UNPUBLISHVIDEO_METHOD :
        userControl.unpublishVideo(transaction, request, participantRequest);
        break;
      case ProtocolElements.RECEIVEVIDEO_METHOD :
        userControl.receiveVideoFrom(transaction, request, participantRequest);
        break;
      case ProtocolElements.UNSUBSCRIBEFROMVIDEO_METHOD :
        userControl.unsubscribeFromVideo(transaction, request, participantRequest);
        break;
      case ProtocolElements.ONICECANDIDATE_METHOD :
        userControl.onIceCandidate(transaction, request, participantRequest);
        break;
      case ProtocolElements.LEAVEROOM_METHOD :
        userControl.leaveRoom(transaction, request, participantRequest);
        break;
      case ProtocolElements.SENDMESSAGE_ROOM_METHOD :
        userControl.sendMessage(transaction, request, participantRequest);
        break;
      case ProtocolElements.CUSTOMREQUEST_METHOD :
        userControl.customRequest(transaction, request, participantRequest);
        break;
      default :
        log.error("Unrecognized request {}", request);
        break;
    }

    updateThreadName(HANDLER_THREAD_NAME);
  }

3.管理用户请求(Manage user requests)

handler把对用户请求的处理委托给了另一个组件,JsonRpcUserControl 类的一个实例,代码中是userControl,在主类中有定义

  @Bean
  @ConditionalOnMissingBean
  public JsonRpcUserControl userControl() {
    return new JsonRpcUserControl(roomManager());
  }

这个对象(userControl)会从用户请求中提取出所需的参数,并且调用部分roomManager的必要部分。用的时候还是在handler里面用,只是具体实现封装到了userControl里面:

    switch (request.getMethod()) {
      case ProtocolElements.JOINROOM_METHOD :
        userControl.joinRoom(transaction, request, participantRequest);
        break;
      case ProtocolElements.PUBLISHVIDEO_METHOD :
        userControl.publishVideo(transaction, request, participantRequest);
        break;
      case ProtocolElements.UNPUBLISHVIDEO_METHOD :
        userControl.unpublishVideo(transaction, request, participantRequest);
        break;
      case ProtocolElements.RECEIVEVIDEO_METHOD :
        userControl.receiveVideoFrom(transaction, request, participantRequest);
        break;
      case ProtocolElements.UNSUBSCRIBEFROMVIDEO_METHOD :
        userControl.unsubscribeFromVideo(transaction, request, participantRequest);
        break;
      case ProtocolElements.ONICECANDIDATE_METHOD :
        userControl.onIceCandidate(transaction, request, participantRequest);
        break;
      case ProtocolElements.LEAVEROOM_METHOD :
        userControl.leaveRoom(transaction, request, participantRequest);
        break;
      case ProtocolElements.SENDMESSAGE_ROOM_METHOD :
        userControl.sendMessage(transaction, request, participantRequest);
        break;
      case ProtocolElements.CUSTOMREQUEST_METHOD :
        userControl.customRequest(transaction, request, participantRequest);
        break;
      default :
        log.error("Unrecognized request {}", request);
        break;
    }

比如在joinRoom()请求中,它要干的事如下:

public void joinRoom(Transaction transaction, Request request,
      ParticipantRequest participantRequest) throws IOException, InterruptedException,
      ExecutionException {
    String roomName = getStringParam(request, ProtocolElements.JOINROOM_ROOM_PARAM);
    String userName = getStringParam(request, ProtocolElements.JOINROOM_USER_PARAM);

    boolean dataChannels = false;
    if (request.getParams().has(ProtocolElements.JOINROOM_DATACHANNELS_PARAM)) {
      dataChannels = request.getParams().get(ProtocolElements.JOINROOM_DATACHANNELS_PARAM)
          .getAsBoolean();
    }

    ParticipantSession participantSession = getParticipantSession(transaction);
    participantSession.setParticipantName(userName);
    participantSession.setRoomName(roomName);
    participantSession.setDataChannels(dataChannels);

    roomManager.joinRoom(userName, roomName, dataChannels, true, participantRequest);
  }

4.用户响应与事件

现在来到了notificationService,由上文知,他是一个JsonRpcNotificationService类型的对象,为RoomJsonRpcHandler类的私有成员。
这个类将所有用户会话存储为映射,从中可以获取向一个房间请求做出响应所需的事务对象。发送通知用了session对象的功能。
响应一个特定请求时,对应的事务对象被用完后会被移出内存(getAndRemoveTransaction()),再来相应就是新的事务了。发出错误指示回应也是一样。

  @Override
  public void sendResponse(ParticipantRequest participantRequest, Object result) {
    Transaction t = getAndRemoveTransaction(participantRequest);
    if (t == null) {
      log.error("No transaction found for {}, unable to send result {}", participantRequest, result);
      return;
    }
    try {
      t.sendResponse(result);
    } catch (Exception e) {
      log.error("Exception responding to user ({})", participantRequest, e);
    }
  }

发服务器响应或者服务器事件时,我们需要用到session对象。session对象需要一直保留,直到close session()方法被调用。close session()被调用可以有两种来源,可以是因为用户的离开被roomhandler调用,也可以是因为网络错误被websocket调用。

二、demo对服务器端的定制

这个demo替换和修改了一些服务器端代码的spring bean来进行了一些特殊订制。全部在KurentoRoomDemoApp中完成,它先导入了原始server类然后做了修改:

import org.kurento.room.KurentoRoomServerApp;
...
public class KurentoRoomDemoApp {
   ...
   public static void main(String[] args) throws Exception {
      SpringApplication.run(KurentoRoomDemoApp.class, args);
   }
}

1.自定义KurentoClientProvider

我们自定义了FixedNKmsManager作为默认的provider接口,得以管理一系列由在配置文件中指定的URI创建的KurentoClient

2.自定义用户控制(user control)

自定义了DemoJsonRpcUserControl来实现对于customRequest这一附加的websocket请求类型的支持。
在这个类中我们重写了customRequest()方法,以实现切换FaceOverlayFilter,它可以在发布者的头上加一个帽子或者移除。他把滤镜对象当作websocket session的一个属性来存储,方便了滤镜的删除:

  @Override
  public void customRequest(Transaction transaction, Request request,
      ParticipantRequest participantRequest) {
    try {
      if (request.getParams() == null
          || request.getParams().get(filterType.getCustomRequestParam()) == null) {
        throw new RuntimeException(
            "Request element '" + filterType.getCustomRequestParam() + "' is missing");
      }
      switch (filterType) {
        case MARKER:
          handleMarkerRequest(transaction, request, participantRequest);
          break;
        case HAT:
        default:
          handleHatRequest(transaction, request, participantRequest);
      }
    } catch (Exception e) {
      log.error("Unable to handle custom request", e);
      try {
        transaction.sendError(e);
      } catch (IOException e1) {
        log.warn("Unable to send error response", e1);
      }
    }
  }

3.依赖

手动删除了一些会造成冲突的依赖。

三、客户端代码

这部分描述一下kurento-room-demo中包含的AngularJS应用。

1.库

  • 在这个时候,我突然脑子一抽觉得,我应该线跑一下demo再来看代码啊。所以我去跑demo,没想到,很惨,又是一堆别人从来没遇到过的错。改了,两天半吧,改了两天半的bug。你能想象官方demo还有bug吗!!!!而且,最主要的是,我也说了,这bug都是别人没遇到过的,所以我去搜解决方案就很少,仅有的比较契合的也都是英文,剩下的那些,中文的什么什么鬼的,完全看不下去。so,到这了,两天半,我终于找到了一个看似可以采用的解决方案,然后自定义得改了改,现在把电脑放着让它自己慢慢解决maven依赖吧,也不知道是不是有效。等之后回来再看吧。虽然还不能确定是否有效,但起码能做点什么了,也不枉我搜了这么多网页,看了那么多英文文档啊。哦,对,在这里表扬一下stack overflow。甚是得朕心






































你可能感兴趣的:(Room demo tutorial - Kurento Room 6.1.1 documentation)