Join multiple channels

Updated

Broadcast or subscribe to multiple channels.

Agora RTC SDK enables you to simultaneously join multiple channels. This capability allows you to receive and publish audio and video streams across multiple channels concurrently.

Understand the tech

RTC SDK's multi-channel functionality is based on two key components:

  • RtcConnection

    The RtcConnection object identifies a connection. It contains the following information:

    • Channel name
    • User ID of the local user

    You create multiple RtcConnection objects, each with a different channel name and user ID. Each RtcConnection instance can independently publish multiple audio streams and a single video stream.

  • IRtcEngineEx

    The class contains methods tailored for interacting with a designated RtcConnection object.

    To join multiple channels, you call JoinChannelEx method in the IRtcEngineEx class multiple times, using a different RtcConnection instance each time.

When joining multiple channels:

  • Ensure that the user ID for each RtcConnection object is unique and nonzero.

  • Configure publishing and subscribing options for the RtcConnection object in JoinChannelEx.

  • Use the connection parameter in each callback to identify the corresponding channel.

Prerequisites

Ensure that you have implemented the SDK quickstart in your project.

Implementation

This section explains how to join a second channel as a host after you have already joined the first channel.

  1. Join the second channel

    Call JoinChannelEx to join the second channel:

    ChannelMediaOptions options2 = new ChannelMediaOptions();
    // Automatically subscribe to remote audio
    options2.autoSubscribeAudio.SetValue(true);
    // Automatically subscribe to remote video
    options2.autoSubscribeVideo.SetValue(true);
    // Publish audio from the microphone
    options2.publishMicrophoneTrack.SetValue(true);
    // Publish video from the camera
    options2.publishCameraTrack.SetValue(true);
    // Set the user role to broadcaster
    options2.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
    // Join the second channel with a unique user ID
    ret = RtcEngine.JoinChannelEx(_token, new RtcConnection(_channelName, UID2), options2);
    Debug.Log("JoinChannelEx returns: " + ret);
  2. Set up remote user video

    Listen for the OnUserJoined callback and call SetForUser to set up video rendering for the remote user:

    public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
    {
      Debug.Log(string.Format("OnUserJoined uid: {0}, elapsed: {1}", uid, elapsed));
      // Create a view for the remote user
      var videoSurface = MakeImageSurface(uid.ToString());
      // Set up video rendering
      videoSurface.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
      // Callback when the Texture size changes
      videoSurface.OnTextureSizeModify += (int width, int height) =>
      {
        var transform = videoSurface.GetComponent<RectTransform>();
        if (transform)
        {
          // If rendering in a RawImage, set the RawImage size
          transform.sizeDelta = new Vector2(width / 2, height / 2);
          transform.localScale = Vector3.one;
        }
        else
        {
          // If rendering in a MeshRenderer, set the MeshRenderer size
          float scale = (float)height / (float)width;
          videoSurface.transform.localScale = new Vector3(-1, 1, scale);
        }
        Debug.Log("OnTextureSizeModify: " + width + " " + height);
      };
    }
    
    private static VideoSurface MakeImageSurface(string goName)
    {
      GameObject go = new GameObject { name = goName };
      // Add RawImage for rendering
      go.AddComponent<RawImage>();
      // Make the object draggable
      go.AddComponent<UIElementDrag>();
    
      var canvas = GameObject.Find("VideoCanvas");
      if (canvas != null)
      {
        go.transform.parent = canvas.transform;
        Debug.Log("Video view added");
      }
      else
      {
        Debug.Log("Canvas is null, video view not added");
      }
    
      // Set transform properties
      go.transform.Rotate(0f, 0f, 180f);
      go.transform.localPosition = Vector3.zero;
      go.transform.localScale = new Vector3(2f, 3f, 1f);
    
      // Add VideoSurface component
      var videoSurface = go.AddComponent<VideoSurface>();
      return videoSurface;
    }

Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

API reference