Skip to main content

Multi-streaming

Some special scenarios require live streaming over two or more separate channels. For example, consider the case of a real-time monitoring system, where camera feed is shared over two channels for two types of users. There are also certain applications where you want to send multiple streams over the same channel. Consider the case of a monitoring system where feeds from multiple cameras are shared on the same channel.

Agora IoT SDK multi-streaming allows you to join multiple channels at the same time or send multiple streams over a single channel. This page shows you how to implement two different multi-streaming methods into your app using IoT SDK. Choose the method that best fits your particular scenario.

Understand the tech

Agora IoT SDK provides the following approaches to implementing multi-streaming:

  • Push multiple streams to a single channel

    To push multiple streams to a single channel, you create multiple connections using Agora engine. You call the join channel method multiple times with the same channel name but different connection Ids and distinct user Ids to set up multiple streams. To send audio or video data, you use the connection Id of the intended stream.

  • Stream to multiple channels

    To stream over multiple channels, you create multiple connections using Agora engine. To join each channel, you use a distinct channel name and a dedicated connection Id. You use the connection Id to specify the intended channel when sending audio or video data, when leaving a particular channel, or when closing a connection.

The following figure shows the workflow you need to implement to add multi-streaming to your app:

Live streaming over multiple channels

Prerequisites

To follow this procedure you must have implemented the SDK quickstart for IoT SDK.

Project setup

In order to create the environment necessary to implement Agora multi-streaming feature into your app, open the IoT SDK SDK quickstart project you created previously.

Implement multi-streaming

This section shows you how to implement the following multi-streaming methods:

To implement either of the two multi-streaming methods, update the UI and then follow the step-by-step procedure in the relevant section.

Implement the user interface

You add two buttons to the UI to enable users to start and stop sending two streams.

  1. In /app/res/layout/activity_main.xml, add the following code before </RelativeLayout>:


    _17
    <Button
    _17
    android:id="@+id/StreamButton1"
    _17
    android:layout_margin="5dp"
    _17
    android:layout_width="match_parent"
    _17
    android:layout_height="wrap_content"
    _17
    android:layout_below="@id/JoinButton"
    _17
    android:onClick="stream1"
    _17
    android:text="Start Stream 1" />
    _17
    _17
    <Button
    _17
    android:id="@+id/StreamButton2"
    _17
    android:layout_margin="5dp"
    _17
    android:layout_width="match_parent"
    _17
    android:layout_height="wrap_content"
    _17
    android:layout_below="@id/StreamButton1"
    _17
    android:onClick="stream2"
    _17
    android:text="Start Stream 2" />

    You see errors in your IDE. This is because this layout refers to methods that you create later.

  2. To access and modify the buttons from your code, add the following to the list of import statements in MainActivity.java:


    _1
    import android.widget.Button;

Push multiple streams to a single channel

To send multiple audio and video streams to a single channel, take the following steps:

  1. Declare the variables you need

    To manage a second connection and a second stream, in /app/java/com.example.<projectname>/MainActivity, add the following variable declarations to the MainActivity class:


    _4
    private int connectionId2; // Id of the second connection
    _4
    private boolean isJoined2 = false; // Status of the second connection
    _4
    private int uid2 = 2; // User id for the second connection
    _4
    private VideoSendThread videoThread2 = null; // A second video thread

  2. Create multiple connections

    When the app starts, you create multiple connections. You can use each connection to send an audio and a video stream. To create two connections, in setupAgoraRtcService replace the code after // Create a connection with the following:


    _14
    connectionId = agoraEngine.createConnection();
    _14
    if (connectionId == AgoraRtcService.ConnectionIdSpecial.CONNECTION_ID_INVALID) {
    _14
    showMessage("Failed to create connection 1");
    _14
    } else {
    _14
    showMessage("Connection1 created");
    _14
    }
    _14
    _14
    // Create a second connection
    _14
    connectionId2 = agoraEngine.createConnection();
    _14
    if (connectionId2 == AgoraRtcService.ConnectionIdSpecial.CONNECTION_ID_INVALID) {
    _14
    showMessage("Failed to create connection 2");
    _14
    } else {
    _14
    showMessage("Connection2 created");
    _14
    }

  3. Join the same channel multiple times

    When a user presses the Join button, you join the same channel twice with different user Ids and different connection Ids. To join the channel a second time, add the following code at the end of joinChannel(View view):


    _9
    // Join the same channel using the second connection
    _9
    ret = agoraEngine.joinChannel(connectionId2, channelName,
    _9
    uid2, token, channelOptions);
    _9
    if (ret != AgoraRtcService.ErrorCode.ERR_OKAY) {
    _9
    showMessage("joinChannel on connection2 failed!");
    _9
    isJoined2 = false;
    _9
    } else {
    _9
    isJoined2 = true;
    _9
    }

  4. Set joined status

    When your call to agoraEngine.joinChannel succeeds, you receive a notification through the onJoinChannelSuccess callback. You use connection Id to identify the connection and set the corresponding isJoined variable to true. To do this, replace the onJoinChannelSuccess method under agoraRtcEvents with the following:


    _12
    @Override
    _12
    public void onJoinChannelSuccess(int connId, int uid, int elapsed_ms) {
    _12
    if (connId == connectionId) {
    _12
    // Successfully joined channel 1
    _12
    isJoined = true;
    _12
    showMessage("Successfully joined channel on connection1");
    _12
    } else if (connId == connectionId2) {
    _12
    // Successfully joined channel 2
    _12
    isJoined2 = true;
    _12
    showMessage("Successfully joined channel on connection2");
    _12
    }
    _12
    }

  5. Stream video over the first connection

    In this example, you start a video stream over the first connection and a second video stream over the second connection to the same channel. To start or stop the first stream when a user taps the Start stream 1 button, add the following method to the MainActivity class:


    _20
    public void stream1(View view) {
    _20
    Button button = (Button) view;
    _20
    _20
    if (!isJoined) {
    _20
    showMessage("Join a channel first");
    _20
    return;
    _20
    } else if (videoThread != null && videoThread.isAlive()) {
    _20
    videoThread.sendStop();
    _20
    videoThread = null;
    _20
    button.setText("Start stream 1");
    _20
    return;
    _20
    }
    _20
    // Create a thread to send video frames
    _20
    videoThread = new VideoSendThread(getApplicationContext(), agoraEngine,
    _20
    channelName, connectionId);
    _20
    // Start the video thread
    _20
    videoThread.sendStart();
    _20
    showMessage("Video thread started on connection1");
    _20
    button.setText("Stop video stream on connection1");
    _20
    }

  6. Stream video over the second connection

    To push another video stream to the same channel when a user taps the Start stream 2 button, add the following method to the MainActivity class:


    _20
    public void stream2(View view) {
    _20
    Button button = (Button) view;
    _20
    _20
    if (!isJoined2) {
    _20
    showMessage("Join a channel first");
    _20
    return;
    _20
    } else if (videoThread2 != null && videoThread2.isAlive()) {
    _20
    videoThread2.sendStop();
    _20
    videoThread2 = null;
    _20
    button.setText("Start stream 2");
    _20
    return;
    _20
    }
    _20
    // Create a second thread to send video frames
    _20
    videoThread2 = new VideoSendThread(getApplicationContext(), agoraEngine,
    _20
    channelName, connectionId2);
    _20
    // Start the second video thread
    _20
    videoThread2.sendStart();
    _20
    showMessage("Video thread started on connection2");
    _20
    button.setText("Stop video stream on connection2");
    _20
    }

  7. Leave all connections to a channel

    To leave the channel on the second connection when a user taps Leave, add the following lines to leaveChannel(View view) after int ret = agoraEngine.leaveChannel(connectionId);:


    _2
    agoraEngine.leaveChannel(connectionId2);
    _2
    isJoined2 = false;

  8. Destroy all connections

    To close all connections when a user exits the app, add the following lines to onDestroy after agoraEngine.destroyConnection(connectionId);


    _2
    agoraEngine.destroyConnection(connectionId2);
    _2
    connectionId2 = AgoraRtcService.ConnectionIdSpecial.CONNECTION_ID_INVALID;

Stream to multiple channels

To send audio and video streams to multiple channels, take the following steps:

  1. Declare the variables you need

    To manage a second connection and join an additional channel, in /app/java/com.example.<projectname>/MainActivity, add the following variable declarations to the MainActivity class:


    _4
    private int connectionId2; // Id of the second connection
    _4
    private final String channelName2 = "demo2"; // Name of the second channel
    _4
    private String token2 = "<Authentication token generated using channelName2>";
    _4
    private boolean isJoined2 = false;

  2. Create multiple connections

    When the app starts, you create multiple connections. You can use each connection to send an audio and a video stream. To create two connections, in setupAgoraEngine replace the code after // Create a connection with the following:


    _14
    connectionId = agoraEngine.createConnection();
    _14
    if (connectionId == AgoraRtcService.ConnectionIdSpecial.CONNECTION_ID_INVALID) {
    _14
    showMessage("Failed to create connection 1");
    _14
    } else {
    _14
    showMessage("Connection1 created");
    _14
    }
    _14
    _14
    // Create a second connection
    _14
    connectionId2 = agoraEngine.createConnection();
    _14
    if (connectionId2 == AgoraRtcService.ConnectionIdSpecial.CONNECTION_ID_INVALID) {
    _14
    showMessage("Failed to create connection 2");
    _14
    } else {
    _14
    showMessage("Connection2 created");
    _14
    }

  3. Join multiple channels

    When a user presses the Join button, you join two channels. To join a second channel, add the following code at the end of joinChannel(View view):


    _9
    // Join a second channel
    _9
    ret = agoraEngine.joinChannel(connectionId2, channelName2,
    _9
    uid, token2, channelOptions);
    _9
    if (ret != AgoraRtcService.ErrorCode.ERR_OKAY) {
    _9
    showMessage("Join channel2 failure!");
    _9
    isJoined2 = false;
    _9
    } else {
    _9
    isJoined2 = true;
    _9
    }

  4. Set joined status

    When your call to agoraEngine.joinChannel succeeds, you receive a notification through the onJoinChannelSuccess callback. You use connection Id to identify the channel and set the corresponding isJoined variable to true. To do this, replace the onJoinChannelSuccess method under agoraRtcEvents with the following:


    _12
    @Override
    _12
    public void onJoinChannelSuccess(int connId, int uid, int elapsed_ms) {
    _12
    if (connId == connectionId) {
    _12
    // Successfully joined channel 1
    _12
    isJoined = true;
    _12
    showMessage("Successfully joined channel " + channelName);
    _12
    } else if (connId == connectionId2) {
    _12
    // Successfully joined channel 2
    _12
    isJoined2 = true;
    _12
    showMessage("Successfully joined channel " + channelName2);
    _12
    }
    _12
    }

  5. Stream audio to the first channel

    In this example, you stream audio to the first channel and video to the second channel. To start or stop the audio stream when a user taps the Start stream 1 button, add the following method to the MainActivity class:


    _20
    public void stream1(View view) {
    _20
    Button button = (Button) view;
    _20
    _20
    if (!isJoined) {
    _20
    showMessage("Join a channel first");
    _20
    return;
    _20
    } else if (audioThread != null && audioThread.isAlive()) {
    _20
    // Audio thread is already running
    _20
    audioThread.sendStop();
    _20
    audioThread = null;
    _20
    button.setText("Start stream 1");
    _20
    return;
    _20
    }
    _20
    // Create a thread to send audio frames
    _20
    audioThread = new AudioSendThread(getApplicationContext(), agoraEngine,
    _20
    channelName, connectionId, 16000, 1, 2);
    _20
    audioThread.sendStart();
    _20
    showMessage("Audio thread started on channel " + channelName);
    _20
    button.setText("Stop audio stream on channel " + channelName);
    _20
    }

  6. Stream video to the second channel

    To stream video to the second channel when a user taps the Start stream 2 button, add the following method to the MainActivity class:


    _21
    public void stream2(View view) {
    _21
    Button button = (Button) view;
    _21
    _21
    if (!isJoined2) {
    _21
    showMessage("Join a channel first");
    _21
    return;
    _21
    } else if (videoThread != null && videoThread.isAlive()) {
    _21
    // Video thread is already running
    _21
    videoThread.sendStop();
    _21
    videoThread = null;
    _21
    button.setText("Start stream 2");
    _21
    return;
    _21
    }
    _21
    // Create a thread to send video frames
    _21
    videoThread = new VideoSendThread(getApplicationContext(), agoraEngine,
    _21
    channelName2, connectionId2);
    _21
    // Start the audio and video threads
    _21
    videoThread.sendStart();
    _21
    showMessage("Video thread started on channel " + channelName2);
    _21
    button.setText("Stop video stream on channel " + channelName2);
    _21
    }

  7. Leave all channels

    To leave both channels when a user taps Leave, add the following lines to leaveChannel(View view) after int ret = agoraEngine.leaveChannel(connectionId);:


    _2
    agoraEngine.leaveChannel(connectionId2);
    _2
    isJoined2 = false;

  8. Destroy all connections

    To close all connections when a user exits the app, add the following lines to onDestroy after agoraEngine.destroyConnection(connectionId);


    _2
    agoraEngine.destroyConnection(connectionId2);
    _2
    connectionId2 = AgoraRtcService.ConnectionIdSpecial.CONNECTION_ID_INVALID;

Test your implementation

To ensure that you have implemented multi-streaming into your app, follow the relevant testing procedure:

Test pushing multiple streams to a single channel

  1. Generate a temporary token in Agora Console.

  2. In your browser, navigate to the Agora web demo and update App ID, Channel, and Token with the values for your temporary token, then click Join.

  3. In Android Studio, in app/java/com.example.<projectname>/MainActivity, update appId, channelName and token with the values for your temporary token.

  4. Update uid and uid2 with distinct positive-integer values.

  5. Connect a physical Android device to your development device.

  6. In Android Studio, click Run app. A moment later you see the project installed on your device. If this is the first time you run the project, you need to grant microphone and camera access to your app.

    You see notifications confirming creation of two connections.

  7. Click Join to join a channel over each connection.

    You see notifications confirming joining success over each connection.

  8. Click Start steam 1.

    You see a video stream playing in the browser.

  9. Click Start steam 2.

    You see a second video stream playing in the same channel.

  10. Try starting and stopping the two streams.

    You see that the streams are independent of each other.

  11. Click Leave to stop the two streams and exit the channel.

Test streaming to multiple channels​

  1. Generate two temporary tokens in Agora Console.

    1. Generate token using the appId and channelName.

    2. Generate token2 using the appId and channelName2.

  2. In your browser, navigate to the Agora web demo and join a channel using appId, channelName and token.

  3. In another browser tab, join the Agora web demo using appId, channelName2 and token2.

  4. In Android Studio, open app/java/com.example.<projectname>/MainActivity, and update appId, channelName, channelName2, token and token2 with the values for your temporary tokens.

  5. Connect a physical Android device to your development device.

  6. In Android Studio, click Run app. A moment later you see the project installed on your device. If this is the first time you run the project, you need to grant microphone and camera access to your app.

    You see notifications confirming creation of two connections.

  7. Click Join to join two channels.

    You see notifications confirming success in joining each channel.

  8. Click Start steam 1.

    You hear an audio stream playing in the web demo connected to channelName.

  9. Click Start steam 2.

    You see a video stream playing in the web demo connected to channelName2.

  10. Click the buttons to stop stream 1 and stream 2.

  11. Click Leave to stop the two streams and exit the channel.

Reference

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