# Cross-channel media stream relay (/en/realtime-media/broadcast-streaming/build/connect-across-channels/cross-channel-media-relay)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

Some special use-cases require cross-channel media stream forwarding functionality. Video SDK enables you to relay the media stream of a host, from a source channel, to multiple target channels simultaneously. This functionality allows you to realize the following interactions:

* The hosts publish and receive each other's audio and video streams while engaging in *cross-channel* real-time interaction.

* The audience receive all audio and video streams from hosts and watch multiple hosts interact at the same time.

Due to its real-time and interactive nature, this feature enriches live broadcasts and game-play, It is especially suitable for live scenes such as co-hosting PK and online choir. It provides the audience with a better viewing experience, while bringing more traffic and revenue to the hosts.

<_PlatformTabsGroup groupMode="structured" canonicalPlatform="web" platforms="[&#x22;android&#x22;,&#x22;ios&#x22;,&#x22;macos&#x22;,&#x22;web&#x22;,&#x22;windows&#x22;,&#x22;electron&#x22;,&#x22;flutter&#x22;,&#x22;react-native&#x22;,&#x22;unity&#x22;]" showTabs="true">
  <_PlatformPanel platform="android">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```java
    // Configure source channel information
    ChannelMediaInfo srcChannelInfo = new ChannelMediaInfo(et_channel.getText().toString(), null, myUid);
    ChannelMediaRelayConfiguration mediaRelayConfiguration = new ChannelMediaRelayConfiguration();
    mediaRelayConfiguration.setSrcChannelInfo(srcChannelInfo);
    // Configure target channel information
    ChannelMediaInfo destChannelInfo = new ChannelMediaInfo(destChannelName, null, myUid);
    mediaRelayConfiguration.setDestChannelInfo(destChannelName, destChannelInfo);
    // Start cross-channel media stream relay
    engine.startOrUpdateChannelMediaRelay(mediaRelayConfiguration);
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * Best practice is to set the UID of the source channel to `0`, allowing the SDK to assign a random UID.
          * The source channel token in `srcChannelInfo` should be different from the one used when joining the source channel. Generate a new token using the source channel name and `uid = 0`.
          * For the destination channel, set the uid to `0`, to allow the SDK to assign a random `uid`, or specify a `uid`, ensuring that it is different from all UIDs in the target channel.
      </CalloutDescription>
    </CalloutContainer>

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after staring channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```java
    engine.pauseAllChannelMediaRelay();
    isPaused = true;
    pause.setText(R.string.resume);
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```java
    engine.resumeAllChannelMediaRelay();
    isPaused = false;
    pause.setText(R.string.pause);
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay`. When forwarding stops, the host exits all target channels.

    ```java
    engine.stopChannelMediaRelay();
    et_channel_ex.setEnabled(true);
    pause.setEnabled(false);
    join_ex.setText(getString(R.string.join));
    mediaRelaying = false;
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, call `leaveChannel` to leave the channel and stop cross-channel media stream relay.
      </CalloutDescription>
    </CalloutContainer>

    5. Monitor cross-channel media stream status

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement the relevant business logic based on the [status codes](#status-codes).

    ```java
    public void onChannelMediaRelayStateChanged(int state, int code) {
      switch (state) {
        case RELAY_STATE_CONNECTING:
          mediaRelaying = true;
          handler.post(() -> {
            et_channel_ex.setEnabled(false);
            join_ex.setEnabled(true);
            join_ex.setText(getText(R.string.stop));
            showLongToast("channel media Relay connected.");
          });
          break;
        case RELAY_STATE_FAILURE:
          mediaRelaying = false;
          handler.post(() -> {
            showLongToast(String.format("channel media Relay failed at error code: %d", code));
          });
        default:
          break;
      }
    }
    ```

    ### Development considerations [#development-considerations]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference]

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

    ### Status codes [#status-codes]

    The main media stream forwarding states and their corresponding status codes are as follows:

    | Media stream forwarding status                                                                                                                                                                                                                                                | status code                                |
    | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                                                                                                                            | `RELAY_STATE_RUNNING`(2) and `RELAY_OK`(0) |
    | Cross-channel media stream forwarding encounters an exception. You can troubleshoot based on the [error code](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged__code). | `RELAY_STATE_FAILURE`(3)                   |
    | Media stream forwarding has stopped.                                                                                                                                                                                                                                          | `RELAY_STATE_IDLE`(0) and `RELAY_OK`(0)    |

    ### Sample project [#sample-project]

    Agora provides an open-source [HostAcrossChannels](https://github.com/AgoraIO/API-Examples/blob/main/Android/APIExample/app/src/main/java/io/agora/api/example/examples/advanced/HostAcrossChannel.java) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengine.html#api_irtcengine_startorupdatechannelmediarelay)
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengine.html#api_irtcengine_stopchannelmediarelay)
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengine.html#api_irtcengine_pauseallchannelmediarelay)
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengine.html#api_irtcengine_resumeallchannelmediarelay)
    * [`onChannelMediaRelayStateChanged`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged)
    * [`ChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_channelmediarelayconfiguration.html)
    * [`ChannelMediaInfo`](https://api-ref.agora.io/en/video-sdk/android/4.x/API/class_channelmediainfo.html)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="ios">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-1]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-1]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```swift
    // Configure source channel information
    let config = AgoraChannelMediaRelayConfiguration()
    config.sourceInfo = AgoraChannelMediaRelayInfo(token: nil)
    isProcessing = true
    // Configure target channel information
    let destinationInfo = AgoraChannelMediaRelayInfo(token: nil)
    config.setDestinationInfo(destinationInfo, forChannelName: destinationChannelName)
    // Start cross-channel media stream forwarding
    agoraKit.startOrUpdateChannelMediaRelay(config)
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * Best practice is to set the UID of the source channel to `0`, allowing the SDK to assign a random UID.
          * The source channel token in `srcChannelInfo` should be different from the one used when joining the source channel. Generate a new token using the source channel name and `uid = 0`.
          * For the destination channel, set the uid to `0`, to allow the SDK to assign a random `uid`, or specify a `uid`, ensuring that it is different from all UIDs in the target channel.
      </CalloutDescription>
    </CalloutContainer>

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after starting channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```swift
    isPauseRelaying = true
    agoraKit.pauseAllChannelMediaRelay()
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```swift
    isPauseRelaying = false
    agoraKit.resumeAllChannelMediaRelay()
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay` . When forwarding stops, the host exits all target channels.

    ```swift
    isProcessing = true
    isPauseRelaying = false
    agoraKit.stopChannelMediaRelay()
    pauseRelayButton.isEnabled = false
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, you can call `leaveChannel` to leave the channel, and cross-channel media stream relay will automatically stop.
      </CalloutDescription>
    </CalloutContainer>

    5. Listen for cross-channel media stream status changes

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement relevant business logic based on the [status codes](#status-codes).

    ```swift
    func rtcEngine(_ engine: AgoraRtcEngineKit,
          channelMediaRelayStateDidChange state: AgoraChannelMediaRelayState,
          error: AgoraChannelMediaRelayError) {

      LogUtils.log(message: "channelMediaRelayStateDidChange: \(state.rawValue) error \(error.rawValue)", level: .info)
      isProcessing = false

      switch state {
      case .running:
        isRelaying = true

      case .failure:
        showAlert(message: "Media Relay Failed: \(error.rawValue)")
        isRelaying = false

      case .idle:
        isRelaying = false

      default:
        break
      }
    }
    ```

    ### Development considerations [#development-considerations-1]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference-1]

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

    ### Status codes [#status-codes-1]

    The main media stream forwarding states and their corresponding status codes are as follows:

    | Media stream forwarding status                                                                                                                                                                                      | status code                                                                      |
    | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                                                                  | `AgoraChannelMediaRelayStateRunning`(2) and `AgoraChannelMediaRelayErrorNone`(0) |
    | Cross-channel media stream forwarding encounters an exception. You can troubleshoot based on the [error code](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorachannelmediarelayerror). | `AgoraChannelMediaRelayStateFailure`(3)                                          |
    | Media stream forwarding has stopped.                                                                                                                                                                                | `AgoraChannelMediaRelayStateIdle`(0) and `AgoraChannelMediaRelayErrorNone`(0)    |

    ### Sample project [#sample-project-1]

    Agora provides an open-source [MediaChannelRelay](https://github.com/AgoraIO/API-Examples/blob/main/iOS/APIExample/APIExample/Examples/Advanced/MediaChannelRelay/MediaChannelRelay.swift) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference-1]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/startorupdatechannelmediarelay\(_:\))
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/stopchannelmediarelay\(\))
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/pauseallchannelmediarelay\(\))
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/resumeallchannelmediarelay\(\))
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginekit/resumeallchannelmediarelay\(\))
    * [`channelMediaRelayStateDidChange`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorartcenginedelegate/rtcengine\(_\:channelmediarelaystatedidchange\:error:\))
    * [`AgoraChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorachannelmediarelayconfiguration)
    * [`AgoraChannelMediaRelayInfo`](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorachannelmediarelayinfo)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="macos">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="macos" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-2]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-2]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```swift
    // Configure source channel information
    let config = AgoraChannelMediaRelayConfiguration()
    config.sourceInfo = AgoraChannelMediaRelayInfo(token: nil)
    isProcessing = true
    // Configure target channel information
    let destinationInfo = AgoraChannelMediaRelayInfo(token: nil)
    config.setDestinationInfo(destinationInfo, forChannelName: destinationChannelName)
    // Start cross-channel media stream forwarding
    agoraKit.startOrUpdateChannelMediaRelay(config)
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * Best practice is to set the UID of the source channel to `0`, allowing the SDK to assign a random UID.
          * The source channel token in `srcChannelInfo` should be different from the one used when joining the source channel. Generate a new token using the source channel name and `uid = 0`.
          * For the destination channel, set the uid to `0`, to allow the SDK to assign a random `uid`, or specify a `uid`, ensuring that it is different from all UIDs in the target channel.
      </CalloutDescription>
    </CalloutContainer>

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after starting channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```swift
    isPauseRelaying = true
    agoraKit.pauseAllChannelMediaRelay()
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```swift
    isPauseRelaying = false
    agoraKit.resumeAllChannelMediaRelay()
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay` . When forwarding stops, the host exits all target channels.

    ```swift
    isProcessing = true
    isPauseRelaying = false
    agoraKit.stopChannelMediaRelay()
    pauseRelayButton.isEnabled = false
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, you can call `leaveChannel` to leave the channel, and cross-channel media stream relay will automatically stop.
      </CalloutDescription>
    </CalloutContainer>

    5. Listen for cross-channel media stream status changes

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement relevant business logic based on the [status codes](#status-codes).

    ```swift
    func rtcEngine(_ engine: AgoraRtcEngineKit,
          channelMediaRelayStateDidChange state: AgoraChannelMediaRelayState,
          error: AgoraChannelMediaRelayError) {

      LogUtils.log(message: "channelMediaRelayStateDidChange: \(state.rawValue) error \(error.rawValue)", level: .info)
      isProcessing = false

      switch state {
      case .running:
        isRelaying = true

      case .failure:
        showAlert(message: "Media Relay Failed: \(error.rawValue)")
        isRelaying = false

      case .idle:
        isRelaying = false

      default:
        break
      }
    }
    ```

    ### Development considerations [#development-considerations-2]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference-2]

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

    ### Status codes [#status-codes-2]

    The main media stream forwarding states and their corresponding status codes are as follows:

    | Media stream forwarding status                                                                                                                                                                                      | status code                                                                      |
    | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                                                                  | `AgoraChannelMediaRelayStateRunning`(2) and `AgoraChannelMediaRelayErrorNone`(0) |
    | Cross-channel media stream forwarding encounters an exception. You can troubleshoot based on the [error code](https://api-ref.agora.io/en/video-sdk/ios/4.x/documentation/agorartckit/agorachannelmediarelayerror). | `AgoraChannelMediaRelayStateFailure`(3)                                          |
    | Media stream forwarding has stopped.                                                                                                                                                                                | `AgoraChannelMediaRelayStateIdle`(0) and `AgoraChannelMediaRelayErrorNone`(0)    |

    ### Sample project [#sample-project-2]

    Agora provides an open-source [ChannelMediaRelay](https://github.com/AgoraIO/API-Examples/blob/main/macOS/APIExample/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelay.swift) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference-2]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorartcenginekit/startorupdatechannelmediarelay\(_:\))
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorartcenginekit/stopchannelmediarelay\(\))
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorartcenginekit/pauseallchannelmediarelay\(\))
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorartcenginekit/resumeallchannelmediarelay\(\))
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorartcenginekit/resumeallchannelmediarelay\(\))
    * [`channelMediaRelayStateDidChange`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorartcenginedelegate/rtcengine\(_\:channelmediarelaystatedidchange\:error:\))
    * [`AgoraChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorachannelmediarelayconfiguration)
    * [`AgoraChannelMediaRelayInfo`](https://api-ref.agora.io/en/video-sdk/macos/4.x/documentation/agorartckit/agorachannelmediarelayinfo)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="web">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-3]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-3]

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Configure cross-channel media stream relay

    To create a cross-channel media stream relay configuration object, and set the source and target channel information, call `createChannelMediaRelayConfiguration`.

    ```javascript
    const channelMediaConfig = AgoraRTC.createChannelMediaRelayConfiguration();
    // Set source channel information
    channelMediaConfig.setSrcChannelInfo({
     channelName: "srcChannel",
     uid: <USER_UID>,
     token: "yourSrcToken",
    });

    // Set target channel information.
    // It can be called multiple times, for up to 4 target channels
    channelMediaConfig.addDestChannelInfo({
     channelName: "destChannel1",
     uid: 123,
     token: "yourDestToken",
    });
    ```

    2. Start cross-channel media stream relay

    To start cross-channel media stream relay, call `startChannelMediaRelay` after calling `AgoraRTCClient.publish`.

    ```javascript
    client.startChannelMediaRelay(channelMediaConfig).then(() => {
     console.log(`startChannelMediaRelay success`);
    }).catch(e => {
     console.log(`startChannelMediaRelay failed`, e);
    });
    ```

    `client` refers to the local client object you create using `AgoraRTC.createClient`.

    3. Listen for cross-channel media stream status changes

    During cross-channel media stream relay, the SDK reports the status of media stream relay through `AgoraRTCClient.on("channel-media-relay-state")` callback with state codes and error codes. Listen to the `AgoraRTCClient.on("channel-media-relay-event")` callback for relay events.

    4. Update media stream relay channels

    To add or remove target channels after successfully calling `startChannelMediaRelay`, call `updateChannelMediaRelay`.

    ```javascript
    // Remove a target channel
    channelMediaConfig.removeDestChannelInfo("destChannel1");
    // Update cross-channel media stream relay settings
    client.updateChannelMediaRelay(channelMediaConfig).then(() => {
     console.log("updateChannelMediaRelay success");
    }).catch(e => {
     console.log("updateChannelMediaRelay failed", e);
    });
    ```

    5. Stop cross-channel media stream relay

    To stop cross-channel media stream relay, call `stopChannelMediaRelay`.

    ```javascript
    client.stopChannelMediaRelay().then(() => {
     console.log("stop media relay success");
    }).catch(e => {
     console.log("stop media relay failed", e);
    });
    ```

    ### Development considerations [#development-considerations-3]

    * This feature supports forwarding media streams to up to 4 target channels. If you want to add or remove target channels during forwarding, call the `startOrUpdateChannelMediaRelay` method.
    * This feature does not support String usernames.
    * Multiple hosts within a channel can forward media streams. The SDK forwards the stream of the host who calls the `startChannelMediaRelay` method.
    * When setting the source channel information using `setSrcChannelInfo`, ensure that the uid is different from the current host's uid. Best practice is to set this uid to `0`, allowing the server to randomly assign one.
    * After successfully forwarding media streams by calling `startChannelMediaRelay` or `updateChannelMediaRelay`, users in the target channel receive the `AgoraRTCClient.on("user-published")` callback. During the media stream forwarding process, if a host in the target channel goes offline or leaves the channel, the host in the source channel receives the `AgoraRTCClient.on("user-left")` callback.
    * After successfully calling `startChannelMediaRelay`, if you wish to call it again, you must first call `stopChannelMediaRelay` to exit the current forwarding state.

    ## Reference [#reference-3]

    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 [#api-reference-3]

    * [`startChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#startchannelmediarelay)
    * [`updateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#updatechannelmediarelay)
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#stopchannelmediarelay)
    * [`AgoraRTCClient.on("channel-media-relay-state")`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#event_channel_media_relay_state)
    * [`AgoraRTCClient.on("channel-media-relay-event")`](https://api-ref.agora.io/en/video-sdk/web/4.x/interfaces/iagorartcclient.html#event_channel_media_relay_event)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="windows">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="windows" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-4]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-4]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```cpp
    // Configure source channel information
    ChannelMediaInfo *m_srcInfo = new ChannelMediaInfo;
    m_srcInfo->channelName = new char[szChannelId.size() + 1];
    strcpy_s(const_cast<char *>(m_srcInfo->channelName), szChannelId.size() + 1,
    		szChannelId.data());
    // Note: The token for source channel is different from the token used when joining the source channel.
    // It needs to be regenerated using the source channel name and uid = 0
    m_srcInfo->token = APP_TOKEN;
    // It is recommended to set the uid of the source channel to 0, allowing the SDK to assign a random uid.
    m_srcInfo->uid = 0;

    // Configure destination channel information
    int nDestCount = m_vecChannelMedias.size();
    ChannelMediaInfo *lpDestInfos = new ChannelMediaInfo[nDestCount];
    for (int nIndex = 0; nIndex < nDestCount; nIndex++) {
    	lpDestInfos[nIndex].channelName = m_vecChannelMedias[nIndex].channelName;
    	lpDestInfos[nIndex].token = m_vecChannelMedias[nIndex].token;
    	// Set the uid to 0, allowing the SDK to assign a random uid, or specify your own uid
    	// Ensure that it is different from all uids in the target channels
    	lpDestInfos[nIndex].uid = m_vecChannelMedias[nIndex].uid;
    }

    ChannelMediaRelayConfiguration cmrc;
    cmrc.srcInfo = m_srcInfo;
    cmrc.destInfos = lpDestInfos;
    cmrc.destCount = nDestCount;
    int ret = 0;
    // Start or update the cross-channel media stream relay
    ret = m_rtcEngine->startOrUpdateChannelMediaRelay(cmrc);
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * Best practice is to set the UID of the source channel to `0`, allowing the SDK to assign a random UID.
          * The source channel token in `srcChannelInfo` should be different from the one used when joining the source channel. Generate a new token using the source channel name and `uid = 0`.
          * For the destination channel, set the uid to `0`, to allow the SDK to assign a random `uid`, or specify a `uid`, ensuring that it is different from all uids in the target channel.
      </CalloutDescription>
    </CalloutContainer>

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after staring channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```cpp
    m_rtcEngine->pauseAllChannelMediaRelay();
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```cpp
    m_rtcEngine->resumeAllChannelMediaRelay();
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay` . When forwarding stops, the host exits all target channels.

    ```cpp
    m_rtcEngine->stopChannelMediaRelay();
    m_lstInfo.AddString(_T("stopChannelMediaRelay"));
    m_btnStartMediaRelay.SetWindowText(CrossChannelStartMediaRelay);
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, you can call `leaveChannel` to leave the channel, and cross-channel media stream relay will automatically stop.
      </CalloutDescription>
    </CalloutContainer>

    5. Listen for cross-channel media stream status changes

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement relevant business logic based on the [status codes](#status-codes).

    ```cpp
    class CAgoraCrossChannelEventHandler : public IRtcEngineEventHandler {
    	virtual void onChannelMediaRelayStateChanged(
      CHANNEL_MEDIA_RELAY_STATE state,
      CHANNEL_MEDIA_RELAY_ERROR code) override {
    		if (m_hMsgHanlder)
    			::PostMessage(m_hMsgHanlder,
               WM_MSGID(EID_CHANNEL_MEDIA_RELAY_STATE_CHNAGENED),
               state, code);
    	}
    };
    ```

    ### Development considerations [#development-considerations-4]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference-4]

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

    ### Status codes [#status-codes-3]

    The main media stream forwarding states and their corresponding status codes are as follows:

    | Media stream forwarding status                                                                                                                                                                     | status code                                |
    | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                                                 | `RELAY_STATE_RUNNING`(2) and `RELAY_OK`(0) |
    | Cross-channel media stream forwarding encounters an exception. You can troubleshoot based on the [error code](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/enum_channelmediarelayerror.html). | `RELAY_STATE_FAILURE`(3)                   |
    | Media stream forwarding has stopped.                                                                                                                                                               | `RELAY_STATE_IDLE`(0) and `RELAY_OK`(0)    |

    ### Sample project [#sample-project-3]

    Agora provides an open-source [CrossChannel](https://github.com/AgoraIO/API-Examples/tree/main/windows/APIExample/APIExample/Advanced/CrossChannel) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference-4]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/api_irtcengine_startorupdatechannelmediarelay.html)
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_stopchannelmediarelay)
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_pauseallchannelmediarelay)
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengine.html#api_irtcengine_resumeallchannelmediarelay)
    * [`onChannelMediaRelayStateChanged`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged)
    * [`ChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_channelmediarelayconfiguration.html)
    * [`ChannelMediaInfo`](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/class_channelmediainfo.html)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="electron">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="electron" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-5]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-5]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```typescript
    const srcChannelId = "srcChannel";
    const destChannelId = "destChannel";
    const uid = 0;
    const token = "insert-your-token";

    // Start cross-channel media stream forwarding
    rtcEngine.startOrUpdateChannelMediaRelay({
      // Configure source channel information srcInfo
      srcInfo: {
        channelName: srcChannelId,
        uid: uid,
        token: token,
      },
      // Configure the target channel information destInfos
      destInfos: [
        {
          channelName: destChannelId,
          token: '', // A token to join the target channel
          uid: 0, // SDK randomly assigns a uid
        },
      ],
      // Number of target channels
      destCount: 1,
    });
    ```

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after staring channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```typescript
    rtcEngine.pauseAllChannelMediaRelay();
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```typescript
    rtcEngine.resumeAllChannelMediaRelay();
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay`. When forwarding stops, the host exits all target channels.

    ```typescript
    rtcEngine.stopChannelMediaRelay();
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, call `leaveChannel` to leave the channel and stop cross-channel media stream relay.
      </CalloutDescription>
    </CalloutContainer>

    5. Monitor cross-channel media stream status

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement the relevant business logic based on the [status codes](#status-codes).

    ```typescript
    // Register event callback
    rtcEngine.registerEventHandler(
      onChannelMediaRelayStateChanged: (state: ChannelMediaRelayState, code: ChannelMediaRelayError) => {
        console.log(`Cross-channel media stream forwarding status：${state}，Error Code：${code}`);
      },
    );
    ```

    ### Development considerations [#development-considerations-5]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference-5]

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

    ### Status codes [#status-codes-4]

    The main media stream forwarding states and their corresponding status codes are as follows:

    | Media stream forwarding status                                                                                                                                                                          | status code                             |
    | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                                                      | `RelayStateRunning`(2) and `RelayOk`(0) |
    | Cross-channel media stream forwarding encounters an exception. You can troubleshoot based on the [error code](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/enum_channelmediarelayerror.html). | `RelayStateFailure`(3)                  |
    | Media stream forwarding has stopped.                                                                                                                                                                    | `RelayStateIdle`(0) and `RelayOk`(0)    |

    ### Sample project [#sample-project-4]

    Agora provides an open-source [ChannelMediaRelay](https://github.com/AgoraIO-Extensions/Electron-SDK/blob/main/example/src/renderer/examples/advanced/ChannelMediaRelay/ChannelMediaRelay.tsx) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference-5]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_irtcengine.html#api_irtcengine_startorupdatechannelmediarelay)
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_irtcengine.html#api_irtcengine_stopchannelmediarelay)
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_irtcengine.html#api_irtcengine_pauseallchannelmediarelay)
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_irtcengine.html#api_irtcengine_resumeallchannelmediarelay)
    * [`onChannelMediaRelayStateChanged`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged)
    * [`ChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_channelmediarelayconfiguration.html)
    * [`ChannelMediaInfo`](https://api-ref.agora.io/en/video-sdk/electron/4.x/API/class_channelmediainfo.html)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="flutter">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="flutter" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-6]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-6]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```dart
    // Configures the source channel information for media relay.
    final srcChannelInfo = ChannelMediaInfo(
     channelName: sourceChannelName,
     token: srcToken, // Token generated with UID 0.
     uid: 0,
    );

    // Configures the destination channel information for media relay.
    final destChannelInfo = ChannelMediaInfo(
     channelName: destChannelName,
     token: destToken,
     uid: 0,
    );

    // Creates the media relay configuration with source and destination channels.
    final mediaRelayConfiguration = ChannelMediaRelayConfiguration(
     srcChannelInfo: srcChannelInfo,
     destInfos: {destChannelName: destChannelInfo},
    );

    // Starts relaying media streams from the source channel to the destination channel.
    await engine.startOrUpdateChannelMediaRelay(mediaRelayConfiguration);
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        * Best practice is to set the UID of the source channel to `0`, allowing the SDK to assign a random UID.
          * The source channel token in `srcChannelInfo` should be different from the one used when joining the source channel. Generate a new token using the source channel name and `uid = 0`.
          * For the destination channel, set the uid to `0`, to allow the SDK to assign a random `uid`, or specify a `uid`, ensuring that it is different from all UIDs in the target channel.
      </CalloutDescription>
    </CalloutContainer>

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after staring channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```dart
    await engine.pauseAllChannelMediaRelay();
    isPaused = true;
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```dart
    await engine.resumeAllChannelMediaRelay();
    isPaused = false;
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay`. When forwarding stops, the host exits all target channels.

    ```dart
    await engine.stopChannelMediaRelay();
    mediaRelaying = false;
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, call `leaveChannel` to leave the channel and stop cross-channel media stream relay.
      </CalloutDescription>
    </CalloutContainer>

    5. Monitor cross-channel media stream status

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement the relevant business logic based on the [status codes](#status-codes).

    ```dart
    engine.registerEventHandler(
     RtcEngineEventHandler(
      onChannelMediaRelayStateChanged: (state, code) {
       switch (state) {
        case ChannelMediaRelayState.relaying:
         mediaRelaying = true;
         print("Channel media relay connected.");
         break;
        case ChannelMediaRelayState.failure:
         mediaRelaying = false;
         print("Channel media relay failed with error code: $code");
         break;
        default:
         break;
       }
      },
     ),
    );
    ```

    ### Development considerations [#development-considerations-6]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference-6]

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

    ### Status codes [#status-codes-5]

    The following table shows the main forwarding states and their corresponding codes:

    | Forwarding status                                                                                                                                                             | State and error code                                                            |
    | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                            | `ChannelMediaRelayState.relayRunning (2)`, `ChannelMediaRelayError.relayOk (0)` |
    | Forwarding encounters an exception. Troubleshoot the issue based on the [error code](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/enum_channelmediarelayerror.html). | `ChannelMediaRelayState.relayFailure (3)`                                       |
    | Forwarding has stopped.                                                                                                                                                       | `ChannelMediaRelayState.relayIdle (0)`, `ChannelMediaRelayError.relayOk (0)`    |

    ### API reference [#api-reference-6]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_irtcengine.html#api_irtcengine_startorupdatechannelmediarelay)
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_irtcengine.html#api_irtcengine_stopchannelmediarelay)
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_irtcengine.html#api_irtcengine_pauseallchannelmediarelay)
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_irtcengine.html#api_irtcengine_resumeallchannelmediarelay)
    * [`onChannelMediaRelayStateChanged`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged)
    * [`ChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_channelmediarelayconfiguration.html)
    * [`ChannelMediaInfo`](https://api-ref.agora.io/en/video-sdk/flutter/6.x/API/class_channelmediainfo.html)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="react-native">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="react-native" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-7]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-7]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your app, take the following steps:

    1. Start cross-channel media stream relay

    After joining a channel, call `startOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```typescript
    const srcChannelId = "srcChannel";
    const destChannelId = "destChannel";
    const uid = 0;
    const token = "insert-your-token";

    // Start cross-channel media stream forwarding
    rtcEngine.startOrUpdateChannelMediaRelay({
      // Configure source channel information srcInfo
      srcInfo: {
        channelName: srcChannelId,
        uid: uid,
        token: token,
      },
      // Configure the target channel information destInfos
      destInfos: [
        {
          channelName: destChannelId,
          token: '', // A token to join the target channel
          uid: 0, // SDK randomly assigns a uid
        },
      ],
      // Number of target channels
      destCount: 1,
    });
    ```

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after staring channel media relay, call `startOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `pauseAllChannelMediaRelay`.

    ```typescript
    rtcEngine.pauseAllChannelMediaRelay();
    ```

    To resume forwarding the media stream to all target channels, call `resumeAllChannelMediaRelay`.

    ```typescript
    rtcEngine.resumeAllChannelMediaRelay();
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `stopChannelMediaRelay`. When forwarding stops, the host exits all target channels.

    ```typescript
    rtcEngine.stopChannelMediaRelay();
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, call `leaveChannel` to leave the channel and stop cross-channel media stream relay.
      </CalloutDescription>
    </CalloutContainer>

    5. Monitor cross-channel media stream status

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `onChannelMediaRelayStateChanged` callback. Implement the relevant business logic based on the [status codes](#status-codes).

    ```typescript
    // Register event callback
    rtcEngine.registerEventHandler(
      onChannelMediaRelayStateChanged: (state: ChannelMediaRelayState, code: ChannelMediaRelayError) => {
        console.log(`Cross-channel media stream forwarding status：${state}，Error Code：${code}`);
      },
    );
    ```

    ### Development considerations [#development-considerations-7]

    * In live broadcast use cases, only users with the role of host can call `startOrUpdateChannelMediaRelay` to initiate cross-channel media stream forwarding.

    * Call `startOrUpdateChannelMediaRelay` **after** successfully joining a channel; otherwise, the method call fails.

    * Within a single channel, multiple hosts can forward media streams. Each host can forward a media stream to up to six target channels.

    * This feature does not support String-type `uid`. To use cross-channel co-hosting, you must also use an int-type `uid` in regular co-hosting. Otherwise, cross-channel co-hosting will not work.

    ## Reference [#reference-7]

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

    ### Status codes [#status-codes-6]

    The main media stream forwarding states and their corresponding status codes are as follows:

    | Media stream forwarding status                                                                                                                                                                              | status code                             |
    | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------- |
    | The source channel starts transmitting data to the target channel.                                                                                                                                          | `RelayStateRunning`(2) and `RelayOk`(0) |
    | Cross-channel media stream forwarding encounters an exception. You can troubleshoot based on the [error code](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/enum_channelmediarelayerror.html). | `RelayStateFailure`(3)                  |
    | Media stream forwarding has stopped.                                                                                                                                                                        | `RelayStateIdle`(0) and `RelayOk`(0)    |

    ### Sample project [#sample-project-5]

    Agora provides an open-source [ChannelMediaRelay](https://github.com/AgoraIO-Extensions/react-native-agora/blob/main/example/src/examples/advanced/ChannelMediaRelay/ChannelMediaRelay.tsx) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference-7]

    * [`startOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_startorupdatechannelmediarelay)
    * [`stopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_stopchannelmediarelay)
    * [`pauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_pauseallchannelmediarelay)
    * [`resumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengine.html#api_irtcengine_resumeallchannelmediarelay)
    * [`onChannelMediaRelayStateChanged`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged)
    * [`ChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_channelmediarelayconfiguration.html)
    * [`ChannelMediaInfo`](https://api-ref.agora.io/en/video-sdk/react-native/4.x/API/class_channelmediainfo.html)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>

  <_PlatformPanel platform="unity">
    <_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="unity" />

    <CalloutContainer type="info">
      <CalloutDescription>
        Cross-channel media stream relay is included in Agora's policy of [10,000 free minutes](/en/api-reference/faq/account/billing_free) every month. For usage beyond the free quota, please refer to [Pricing](../../reference/pricing).
      </CalloutDescription>
    </CalloutContainer>

    ## Prerequisites [#prerequisites-8]

    * Ensure that you have implemented the [SDK quickstart](../../index) in your project.
    * Contact [technical support](https://agora-ticket.agora.io/) to activate the cross-channel media stream relay feature.

    ## Implement cross-channel media stream relay [#implement-cross-channel-media-stream-relay-8]

    The following figure shows the workflow you implement to facilitate cross-channel media stream relay:

    ![CrossChannelMediaStreamForwarding](https://assets-docs.agora.io/images/video-sdk/cross-channel-forwarding.svg)

    To implement cross-channel media stream relay in your game, take the following steps:

    1. Start cross-channel media stream relay

    After joining the channel, call `StartOrUpdateChannelMediaRelay` to configure the source and target channel information and start forwarding a media stream.

    ```csharp
    private void OnStartButtonClick()
    {
      ChannelMediaRelayConfiguration config = new ChannelMediaRelayConfiguration();

      // Configure source channel
      config.srcInfo = new ChannelMediaInfo
      {
        channelName = this._appIdInput.channelName,
        uid = 0, // Let the SDK assign a random UID
        token = this._appIdInput.token // Regenerate token for source channel with UID 0
      };

      // Configure target channel (up to 6 supported)
      config.destInfos = new ChannelMediaInfo[1];
      config.destInfos[0] = new ChannelMediaInfo
      {
        channelName = this._appIdInput.channelName + "_2",
        uid = 0, // Random UID or specify a unique one
        token = this._appIdInput.token
      };
      config.destCount = 1;

      // Start media stream forwarding
      var nRet = RtcEngine.StartOrUpdateChannelMediaRelay(config);
      this.Log.UpdateLog(`StartOrUpdateChannelMediaRelay nRet: ${nRet}, New Channel: ${this._appIdInput.channelName}_2`);
    }
    ```

    2. Update media stream relay channels

    To forward the stream to multiple target channels or exit the current forwarding channel after staring channel media relay, call `StartOrUpdateChannelMediaRelay` again to add or remove target channels for forwarding.

    <CalloutContainer type="info">
      <CalloutDescription>
        The updated configuration completely **replaces** the previous configuration.
      </CalloutDescription>
    </CalloutContainer>

    3. Pause or resume media stream relay

    To pause forwarding the media stream to all target channels, call `PauseAllChannelMediaRelay`.

    ```csharp
    private void onPauseAllButtonClick()
    {
      var nRet = RtcEngine.PauseAllChannelMediaRelay();
      this.Log.UpdateLog(`onPauseAllButtonClick nRet: ${nRet}`);
    }
    ```

    To resume forwarding the media stream to all target channels, call `ResumeAllChannelMediaRelay`:

    ```csharp
    private void OnResumeAllButtonClick()
    {
      var nRet = RtcEngine.ResumeAllChannelMediaRelay();
      this.Log.UpdateLog(`OnResumeAllButtonClick nRet: ${nRet}`);
    }
    ```

    4. Stop cross-channel media stream relay

    To stop forwarding the media stream, call `StopChannelMediaRelay`. When forwarding stops, the host exits all target channels.

    ```csharp
    private void OnStopButtonClick()
    {
      var nRet = RtcEngine.StopChannelMediaRelay();
      this.Log.UpdateLog(`OnStopButtonClick nRet: ${nRet}`);
    }
    ```

    <CalloutContainer type="info">
      <CalloutDescription>
        If this method fails, call `LeaveChannel` to leave the channel and stop cross-channel media stream relay.
      </CalloutDescription>
    </CalloutContainer>

    5. Monitor cross-channel media stream status

    During cross-channel media stream relay, the SDK reports changes in the status of media stream relay through the `OnChannelMediaRelayStateChanged` callback. Implement the relevant business logic based on the [status codes](#status-codes).

    ```csharp
    public override void OnChannelMediaRelayStateChanged(int state, int code)
    {
      _channelMediaRelay.Log.UpdateLog(`OnChannelMediaRelayStateChanged state: ${state}, code: ${code}`);
    }
    ```

    ### Development considerations [#development-considerations-8]

    * In a live broadcast use-case, only hosts can call `StartOrUpdateChannelMediaRelay`.
    * `StartOrUpdateChannelMediaRelay` must be called after successfully joining the channel, otherwise an error is reported.
    * Multiple hosts can forward media streams in a channel. Each host can forward media streams to up to 6 target channels.
    * This function does not support `String` `uid`s. Additionally, to use the cross-channel microphone function, use the `Int` type in the normal microphone function `uid`.

    ## Reference [#reference-8]

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

    ### Status codes [#status-codes-7]

    To understand and respond to changes in the media relay state, refer to the [`CHANNEL_MEDIA_RELAY_STATE`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/enum_channelmediarelaystate.html) and [`CHANNEL_MEDIA_RELAY_ERROR`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/enum_channelmediarelayerror.html) enums in the API reference.

    ### Sample project [#sample-project-6]

    Agora provides an open-source [ChannelMediaRelay](https://github.com/AgoraIO-Extensions/Agora-Unity-Quickstart/blob/main/API-Example-Unity/Assets/API-Example/Examples/Advanced/ChannelMediaRelay/ChannelMediaRelay.cs) sample project for your reference. Download the project or view the source code for a more detailed example.

    ### API reference [#api-reference-8]

    * [`StartOrUpdateChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengine.html#api_irtcengine_startorupdatechannelmediarelay)
    * [`StopChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengine.html#api_irtcengine_stopchannelmediarelay)
    * [`PauseAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengine.html#api_irtcengine_pauseallchannelmediarelay)
    * [`ResumeAllChannelMediaRelay`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengine.html#api_irtcengine_resumeallchannelmediarelay)
    * [`OnChannelMediaRelayStateChanged`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onchannelmediarelaystatechanged)
    * [`ChannelMediaRelayConfiguration`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_channelmediarelayconfiguration.html)
    * [`ChannelMediaInfo`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_channelmediainfo.html)

    <_PlatformProcessedMarker close="true" />
  </_PlatformPanel>
</_PlatformTabsGroup>
