适用场景

用户发布公共流以后,使用同一 appID 的用户,无需进入房间,也可以订阅和播放该流。适合大规模的,低延时和低互动需求的音视频场景,例如以下业务。

前提条件

你已经集成 RTC SDK,实现了基本的音视频通话。

功能实现

你可以在应用服务端和应用客户端发布公共流。查看调用 OpenAPI 发布公共流。本文将介绍如何在客户端发布和订阅公共流。

说明:不同平台的实现的步骤相同,但接口名称、参数名称可能略有差异。以下指南以 Android RTC SDK 为例,参考对应平台的 API 文档获取更多信息。

发送端
  1. 进房。
public int joinRoom(String token, String channelName, String uid, RTCRoomConfig config) {
    return 0;
}
java
  1. 调用 startPushPublicStream 开始发布公共流。除指定 publicStreamId 以外,你还需要传入布局。
    1. publicStreamId: 是公共流标识,在同一个 appId 内唯一。你需要自行实现 publicStreamId 的生成逻辑。

    2. 编码参数:设置音视频的编码参数。你可以通过调用 getDefualtPublicStreaming 获取默认编码参数。

    3. 布局:将多个用户发布的流进行合并,这些流可以是在不同房间内发布的。通过指定流的发布方 uid 和所在的房间 roomId,指向了参与合并到公共流的媒体流。在 Region 属性中,为不同的流指定位置、裁剪等布局配置。

平台AndroidiOSMacWindows
音频编码参数AudioConfigByteRTCPublicStreamAudioConfigByteRTCPublicStreamAudioConfigPublicStreamAudioParam
视频编码参数VideoConfigByteRTCPublicStreamVideoConfigByteRTCPublicStreamVideoConfigPublicStreamVideoParam
布局设置LayoutByteRTCPublicStreamLayoutByteRTCPublicStreamLayoutPublicStreamLayout
public int startPushPublicStream(String publicStreamId) {
    //获取默认参数
	PublicStreaming streaming = PublicStreaming.getDefualtPublicStreaming();
    // 修改音频编码参数
    streaming.getAudio()
    		.setKBitRate(16000)
            .setChannels(2)
            .setSampleRate(16000);

    // 修改视频编码参数
    streaming.getVideo()
    		.setKBitRate(500000)
            .setFps(30)
            .setHeight(640)
            .setWidth(360);

    PublicStreaming.Layout.Builder builder = new PublicStreaming.Layout.Builder();
    for (int i = 0;i < 2;++i) {
        PublicStreaming.Layout.Region region = new PublicStreaming.Layout.Region();
        user.x = 第i个视频所在的左上角横坐标;
        user.y = 第i个视频所在的左上角纵坐标;
        user.width = 第i个视频的宽;
        user.height = 第i个视频的高;
        region.userId(第i个用户的ID)
                .roomId(第i个用户所在的房间ID)
                .zorder(合成后所在层数)
                .alpha(1)
                .position(user.x, user.y) //用户视频的左上角位置
                .size(user.width, user.height) //用户视频的宽高
                .renderMode(1);
        builder.addRegoin(region);
    }
    PublicStreaming.Layout layout = builder.build();
    streaming.setLayout(layout);
    streaming.setRoomId(推流所在房间ID);
    return startPushPublicStream(publicStreamId,streaming);
}
java
- (void)startPushPublicStream:(NSString *_Nonnull)streamId {
    ByteRTCPublicStreaming *streaming = [ByteRTCPublicStreaming defaultPublicStreaming];
    streaming.video.width = 360;
    streaming.video.height = 640;
    streaming.video.fps = 30;
    streaming.video.bitrate = 500000;
    streaming.audio.bitrate = 5000;
    streaming.audio.sampleRate = 16000;
    streaming.audio.channels = 2;
    NSMutableArray<ByteRTCPublicStreamRegion *> *regionArray = [[NSMutableArray alloc] init];

    for (i = 0;i < 2;++i) {
    	ByteRTCSourceCropInfo* crop = [[ByteRTCSourceCropInfo alloc] init];
	    crop.LocationX = 0.0;
	    crop.LocationY = 0.0;
	    crop.WidthProportion = 0.0;
	    crop.HeightProportion = 0.0;
	    ByteRTCPublicStreamRegion* region = [[ByteRTCPublicStreamRegion alloc] init];
	    region.roomId = 第i个用户所在的房间ID;
	    EngingState *enginState = [MultiPageViewController getEngineState];
	    region.userId = 第i个用户的ID;
	    region.x = 第i个视频所在的左上角横坐标;
	    region.y = 第i个视频所在的左上角纵坐标;
	    region.width = 第i个视频的宽;
	    region.height = 第i个视频的高;
	    region.zOrder = 所在层数;
	    region.alpha = 1;
	    region.streamType = 0;
	    region.mediaType = 0;
	    region.sourceCrop = crop;
	    region.renderMode = 1;
	    [regionArray addObject:region];
    }

    streaming.layout.regions = regionArray;
    streaming.roomId = 推流所在房间ID;
    [startPushPublicStream:streamId withLayout:streaming];
objectivec
  1. 调用 stopPushPublicStream 停止发布公共流。
public int stopPushPublicStream(String streamId) {
	if (mRtcEventHandlerHost != null) {
  	mRtcEventHandlerHost.notifyApiCall("stopPushPublicStream", streamId);
  }
  if (mRtcEngine != null) {
  	mRtcEngine.stopPushPublicStream( streamId );
  }
  return 0;
}
java
接收端
  1. 调用 setPublicStreamVideoCanvas 绑定内部视图。需要解绑视图时,把 videoCanvas 设置为空。

如果你需要使用外部渲染器,调用 setPublicStreamVideoSink 详见自定义视频渲染。

public int setPublicStreamVideoCanvas(String streamId, VideoCanvas canvas) {...}
java
  1. 调用 startPlayPublicStream 开始订阅公共流。
public int startPlayPublicStream(String streamId) {...}
java
  1. 通过 回调获取 SEI 信息。远端调用 sendSEIMessage 将 SEI 信息插入视频帧中,流中的 SEI 信息将透传并融合到公共流中。
    • 公共流将透传的 SEI 数据添加到当前视频帧开始的连续 30 个视频帧中,提高 SEI 信息的可靠性。

    • 当多条视频流都包含 SEI 信息时,每条流中的 SEI 信息均将透传到公共流中。 然而,公共流中单个视频帧允许携带的 SEI 信息不超过 4 KB。如果合并后的 SEI 信息超过 4 KB,RTC将丢弃超出的部分。例如,组成公共流的三条视频流某视频帧中携带的 SEI 信息分别为 A, B, C,当 A+B+C > 4 KB 且 A+B < 4 KB 时,公共流最终携带的 SEI 信息为 A + B

    • 你需要自行实现 SEI信息的处理逻辑。

public void onPublicStreamSEIMessageReceived(String publicStreamId, ByteBuffer message) {
    RTCCallEngineController controller = getController();
    String callbackLog = "onPublicStreamSEIMessageReceived[streamid:" + publicStreamId
                    + " sei:" + RtmpUrlUtils.byteBufferToString(message) + "]";);
}
java
  1. 调用 stopPlayPublicStream 取消订阅公共流。
public int stopPlayPublicStream(String streamId) {...}
java
API 参考
Android
iOS
Windows