public void joinRoom(String roomId, String userId, String token) {
Log.d(TAG, String.format("joinRoom: %s %s %s", roomId, userId, token));
// Initialize the RTCVideo object
mRTCVideo = RTCVideo.createRTCVideo(Utilities.getApplicationContext(), appId, mRTCVideoEventHandler, null, null);
// Initialize the RTCRoom object
mRTCRoom = mRTCVideo.createRTCRoom(roomId);
mRTCRoom.setRTCRoomEventHandler(mRTCRoomEventHandler);
mRTCRoomEventHandler.setBaseClient(mRTSClient);
UserInfo userInfo = new UserInfo(userId, null);
RTCRoomConfig roomConfig = new RTCRoomConfig(ChannelProfile.CHANNEL_PROFILE_COMMUNICATION,
mRTCRoom.joinRoom(token, userInfo, roomConfig);
// Set the video capture config
VideoCaptureConfig captureConfig = new VideoCaptureConfig(720, 1280, 15);
mRTCVideo.setVideoCaptureConfig(captureConfig);
// Set the video encoder config
VideoEncoderConfig config = new VideoEncoderConfig();
mRTCVideo.setVideoEncoderConfig(config);
// Start video and audio capture
mRTCVideo.startVideoCapture();
mRTCVideo.startAudioCapture();
private final IRTCRoomEventHandler mRTCRoomEventHandler = new IRTCRoomEventHandler() {
public void onRoomStateChanged(String roomId, String uid, int state, String extraInfo) {
super.onRoomStateChanged(roomId, uid, state, extraInfo);
Log.d(TAG, String.format("onRoomStateChanged: %s, %s, %d, %s", roomId, uid, state, extraInfo));
if (isFirstJoinRoomSuccess(state, extraInfo)) {
// mSingleLiveInfo 记录了主播的直播信息
if (mSingleLiveInfo != null) {
startLiveTranscoding(roomId, uid, mSingleLiveInfo.pushUrl);
* 判断 onRoomStateChanged 中的extraInfo,是不是首次加入房间成功
protected boolean isFirstJoinRoomSuccess(int state, String extraInfo) {
JSONObject json = new JSONObject(extraInfo);
// 341后 SDK传的固定键 joinType表示加入房间的类型,0为首次进房,1为重连进房。
joinType = json.getInt("join_type");
return joinType == 0 && state == 0;
* @param liveUrl rtmp 推流地址
private void startLiveTranscoding(String roomId, String userId, String liveUrl,String taskId) {
MixedStreamConfig config = MixedStreamConfig.defaultMixedStreamConfig();
config.setRoomID(roomId);
config.setUserID(userId);
config.setPushURL(liveUrl);
config.setExpectedMixingType(ByteRTCStreamMixingType.STREAM_MIXING_BY_SERVER);
MixedStreamVideoConfig videoConfig = config.getVideoConfig();
videoConfig.setWidth(mHostConfig.width);//设置合流视频宽度。单位为 px,范围为 [2, 1920],必须是偶数。默认值为 640 px。 设置值为非偶数时,自动向上取偶数
videoConfig.setHeight(mHostConfig.height);//设置合流视频高度。单位为 px,范围为 [2, 1920],必须是偶数。默认值为 360 px。 设置值为非偶数时,自动向上取偶数
videoConfig.setFps(mHostConfig.frameRate);
videoConfig.setBitrate(mHostConfig.bitRate);
config.setVideoConfig(videoConfig);
MixedStreamAudioConfig audioConfig = config.getAudioConfig();
audioConfig.setSampleRate(44100);
audioConfig.setChannels(2);
config.setAudioConfig(audioConfig);
MixedStreamLayoutRegionConfig localRegionConfig = new MixedStreamLayoutRegionConfig();
localRegionConfig.setUserID(userId);
localRegionConfig.setIsLocalUser(true);
localRegionConfig.setRoomID(roomId);
localRegionConfig.setLocationX(0);//设置本地主播画面左上角在整个画布坐标系中的 X 坐标(pixel),即以画布左上角为原点,用户画面左上角相对于原点的横向位移,取值范围为 [0, 整体画布宽度)
localRegionConfig.setLocationY(0);//设置本地主播画面左上角在整个画布坐标系中的 Y 坐标(pixel),即以画布左上角为原点,用户画面左上角相对于原点的纵向位移,取值范围为 [0, 整体画布高度)
localRegionConfig.setWidth(mHostConfig.width);//设置本地主播画面宽度。取值范围为 [0, 整体画布宽度],默认值为 360
localRegionConfig.setHeight(mHostConfig.height);//设置本地主播画面高度。取值范围为 [0, 整体画布高度],默认值为 640
localRegionConfig.setAlpha(1);
localRegionConfig.setZOrder(0);
localRegionConfig.setRenderMode(MixedStreamRenderMode.MIXED_STREAM_RENDER_MODE_HIDDEN);
MixedStreamLayoutRegionConfig[] regions = new MixedStreamLayoutRegionConfig[]{localRegionConfig};
MixedStreamLayoutConfig layoutConfig = new MixedStreamLayoutConfig();
layoutConfig.setRegions(regions);
JsonObject json = new JsonObject();
json.addProperty(KEY_SEI_KEY_SOURCE, KEY_SEI_VALUE_SOURCE_NONE);
layoutConfig.setUserConfigExtraInfo(json.toString());
config.setLayout(layoutConfig);
mRTCVideo.startPushMixedStreamToCDN(taskId, config, mMixedStreamObserver);