
let src = TRTCSwitchRoomConfig()// 根据业务生成对应的房间号以及进房凭证,示例中使用客户端生成进房凭证,线上业务请从后台获取src.strRoomId = strRoomIdsrc.userSig = GenerateTestUserSig.genTestUserSig(identifier: userId) as StringtrtcCloud.switchRoom(src)

bgView = UIImageView(frame: self.view.bounds)// 该图片需为对应直播间的占位图,需业务上提前获取bgView.image = UIImage(named: "1.png")bgView.contentMode = .scaleAspectFillbgView.translatesAutoresizingMaskIntoConstraints = falseself.view.insertSubview(bgView, at: 0)NSLayoutConstraint.activate([bgView.topAnchor.constraint(equalTo: view.topAnchor),bgView.bottomAnchor.constraint(equalTo: view.bottomAnchor),bgView.leadingAnchor.constraint(equalTo: view.leadingAnchor),bgView.trailingAnchor.constraint(equalTo: view.trailingAnchor),])
DispatchQueue.main.async {UIView.transition(with: self.bgView,duration: 0,options: .transitionCrossDissolve,animations: {// 业务上切换对应的占位图self.bgView.image = UIImage(named: strRoomId)}, completion: nil)}let src = TRTCSwitchRoomConfig()// 根据业务生成对应的房间号以及进房凭证,示例中使用客户端生成进房凭证,线上业务请从后台获取src.strRoomId = strRoomIdsrc.userSig = GenerateTestUserSig.genTestUserSig(identifier: userId) as StringtrtcCloud.switchRoom(src)
// 拉视频流func onUserVideoAvailable(_ userId: String, available: Bool) {if available {trtcCloud.startRemoteView(userId, streamType: .big, view: view)} else {trtcCloud.stopRemoteView(userId, streamType: .big)}}// 在开始渲染第一帧视频画面时,切换占位图到背景,显示视频画面func onFirstVideoFrame(_ userId: String, streamType: TRTCVideoStreamType, width: Int32, height: Int32) {// 这里是调整背景图和视频渲染控件的前后顺序,实际业务中根据实际情况调整self.view.exchangeSubview(at: 1, withSubviewAt: 0)}


import Foundationimport ObjectiveCimport TXLiteAVSDK_Professional@objc protocol SubCloudHelperDelegate : NSObjectProtocol {@objc optional func onUserVideoAvailableWithSubId(subId: Int, userId: String, available: Bool)}class SubCloudHelper:NSObject,TRTCCloudDelegate {var trtcCloud: TRTCCloud!var subId: Int!weak var delegate : SubCloudHelperDelegate? = nilfunc initWithSubId(subId: Int, trtcIns: TRTCCloud) {self.subId = subIdself.trtcCloud = trtcInsself.trtcCloud.addDelegate(self)}func getCloud()->TRTCCloud {return trtcCloud}func onUserVideoAvailable(_ userId: String, available: Bool) {if self.delegate?.onUserVideoAvailableWithSubId?(subId: subId, userId: userId, available: available) == nil {return}}}
let trtcCloud = TRTCCloud()let subCloudHelper = SubCloudHelper()override func viewDidLoad() {super.viewDidLoad()subCloudHelper.initWithSubId(subId: 0, trtcIns: trtcCloud.createSub())subCloudHelper.delegate = self}
private var atRoom: Bool = falsevar pageViewController: UIPageViewController!var pageZero: UIViewController!var pageOne: UIViewController!var pageTwo: UIViewController!var pages: [UIViewController] = []var curPageIdx = 0var curIsSub = falsefunc setupPages() {pageViewController = UIPageViewController(transitionStyle: .scroll,navigationOrientation: .vertical)pageViewController.dataSource = selfpageViewController.delegate = selfaddChild(pageViewController)view.addSubview(pageViewController.view)pageViewController.didMove(toParent: self)// pagespageZero = UIViewController()pageZero.view.backgroundColor = .blackpageOne = UIViewController()pageOne.view.backgroundColor = .blackpageTwo = UIViewController()pageTwo.view.backgroundColor = .blackpages = [pageZero, pageOne, pageTwo]pageViewController.setViewControllers([pages[curPageIdx]], direction: .forward, animated: false)}
// 获取下/上一个Pagefunc getShowPage(isNext: Bool) -> UIViewController {var newPageIdx = 0if isNext {newPageIdx = curPageIdx + 1} else {newPageIdx = curPageIdx - 1}if newPageIdx >= pages.count {newPageIdx = 0} else if newPageIdx < 0 {newPageIdx = pages.count - 1}return pages[newPageIdx]}extension RtcDuplexVC: UIPageViewControllerDataSource {func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {return getShowPage(isNext: false)}func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {return getShowPage(isNext: true)}}
// 主实例进房// 根据实际业务替换sdkAppId,roomID,strRoomId,userId和userSig// 示例中使用客户端生成userSig,线上业务请从后台获取let params = TRTCParams()params.sdkAppId = UInt32(SDKAppID)params.roomId = 0params.strRoomId = strRoomIdLst.first ?? "1"params.userId = userIdparams.role = .anchorparams.userSig = GenerateTestUserSig.genTestUserSig(identifier: userId) as StringtrtcCloud.addDelegate(self)trtcCloud.enterRoom(params, appScene: .LIVE)// 子实例预加载进入下一个房间// 根据实际业务替换sdkAppId,roomID,strRoomId,userId和userSig// 示例中使用客户端生成userSig,线上业务请从后台获取let subParams = TRTCParams()subParams.sdkAppId = UInt32(SDKAppID)subParams.roomId = 0subParams.strRoomId = strRoomIdLst[1]subParams.userId = userIdsubParams.role = .anchorsubParams.userSig = GenerateTestUserSig.genTestUserSig(identifier: userId) as StringsubCloudHelper.trtcCloud.enterRoom(subParams, appScene: .LIVE)subCloudHelper.trtcCloud.muteAllRemoteAudio(true)
func getPageByIdx(isNext: Bool) -> UIViewController {var newPageIdx = curPageIdxif isNext {newPageIdx += 1}if newPageIdx >= pages.count {newPageIdx = 0}return pages[newPageIdx]}extension RtcDuplexVC: TRTCCloudDelegate {func onUserVideoAvailable(_ userId: String, available: Bool) {if available {trtcCloud.startRemoteView(userId, streamType: .big, view: getPageByIdx(isNext: curIsSub).view)} else {trtcCloud.stopRemoteView(userId, streamType: .big)}}}extension RtcDuplexVC: SubCloudHelperDelegate {func onUserVideoAvailableWithSubId(subId: Int, userId: String, available: Bool) {if available {subCloudHelper.trtcCloud.startRemoteView(userId, streamType: .big, view: getPageByIdx(isNext: !curIsSub).view)} else {subCloudHelper.trtcCloud.stopRemoteView(userId, streamType: .big)}}}
func updateCurRoomIdx(isNext: Bool) {if isNext {curRoomIdx += 1if curRoomIdx >= strRoomIdLst.count {curRoomIdx = 0}} else {curRoomIdx -= 1if curRoomIdx < 0 {curRoomIdx = strRoomIdLst.count - 1}}}// 这里需要根据实际的业务逻辑切换房间号func updateNewRoom(isNext: Bool) {var newRoomIdx = 0if isNext{newRoomIdx = curRoomIdx + 1} else {newRoomIdx = curRoomIdx - 1}if newRoomIdx >= strRoomIdLst.count {newRoomIdx = 0} else if newRoomIdx < 0 {newRoomIdx = strRoomIdLst.count - 1}let newRoomStrId = strRoomIdLst[newRoomIdx]let src = TRTCSwitchRoomConfig()src.strRoomId = newRoomStrIdsrc.userSig = GenerateTestUserSig.genTestUserSig(identifier: userId) as Stringif curIsSub {trtcCloud.switchRoom(src)trtcCloud.muteAllRemoteAudio(true)} else {subCloudHelper.trtcCloud.switchRoom(src)subCloudHelper.trtcCloud.muteAllRemoteAudio(true)}}extension RtcDuplexVC: UIPageViewControllerDelegate {func pageViewController(_ pageViewController: UIPageViewController,didFinishAnimating finished: Bool,previousViewControllers: [UIViewController],transitionCompleted completed: Bool) {if completed {guard let currentVC = pageViewController.viewControllers?.first else {return}if let index = pages.firstIndex(of: currentVC) {// 获取上下滑的判断依据let iden = index - curPageIdx// 更新当前显示页面的序号curPageIdx = index// 向下滑if iden == 1 || iden == -2 {// 更新当前所在房间的序号updateCurRoomIdx(isNext: true)// 更新当前显示页面的实例curIsSub.toggle()// 更新房间updateNewRoom(isNext: true)}// 向上滑if iden == -1 || iden == 2 {// 更新房间updateNewRoom(isNext: false)// 更新当前所在房间的序号updateCurRoomIdx(isNext: false)// 更新当前显示页面的实例curIsSub.toggle()trtcCloud.muteAllRemoteAudio(true)subCloudHelper.trtcCloud.muteAllRemoteAudio(true)}// 解除当前房间的静音if curIsSub {subCloudHelper.trtcCloud.muteAllRemoteAudio(false)} else {trtcCloud.muteAllRemoteAudio(false)}}}}}

// 处理进入直播间逻辑@objc func enterBtnClick() {// 隐藏上下滑列表中的UI组件self.enterBtn.isHidden = true// 显示进入直播间后的UI组件self.exitBtn.isHidden = false// ...self.atRoom = true// 进入直播间后禁止上下滑动pageViewController.dataSource = nil// 停止预加载if curIsSub{trtcCloud.muteAllRemoteVideoStreams(true)} else {subCloudHelper.trtcCloud.muteAllRemoteAudio(true)}}// 处理离开直播间逻辑@objc func exitBtnClick() {// 隐藏直播间中的UI组件self.enterBtn.isHidden = false// 显示上下滑列表中的UI组件self.exitBtn.isHidden = trueself.atRoom = false// 进入上下滑列表后恢复上下滑动pageViewController.dataSource = self// 恢复预加载if curIsSub{trtcCloud.muteAllRemoteVideoStreams(false)} else {subCloudHelper.trtcCloud.muteAllRemoteAudio(false)}}

public class ScrollSwitchRoomActivity extends TRTCBaseActivity {PageAdapter mAdapter;public String[] mRoomIds;private TRTCCloud mTRTCCloud;private TXCloudVideoView mRemoteVideoView;private int mCurPos = -1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_scroll_switch_room);//隐藏标题栏getSupportActionBar().hide();mRoomIds = new String[]{"1231", "1232", "1233"};if (checkPermission()) {initView();}}@Overrideprotected void onPermissionGranted() {initView();}private void initView() {mAdapter = new PageAdapter(this, mRoomIds);ViewPager2 viewPager = findViewById(R.id.viewPager);viewPager.setAdapter(mAdapter);//设置 viewPager 滑动方向viewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);// 设置 viewPager 预加载viewPager.setOffscreenPageLimit(1);// 添加页面切换监听viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {public void onPageSelected(int position) {Log.d("ScrollSwitchRoom", "onPageSelected: " + position);if (mCurPos == position) {return;}RecyclerView recyclerViewImpl = (RecyclerView) viewPager.getChildAt(0);// 先退出当前房间exitRoom();// 进入下一个房间View itemView = recyclerViewImpl.getChildAt(position);mRemoteVideoView = itemView.findViewById(R.id.txcvv_main_local);enterRoom(position);mCurPos = position;}});// Initialize your views heremTRTCCloud = TRTCCloud.sharedInstance(getApplicationContext());mTRTCCloud.addListener(mTRTCCloudListener);}private void enterRoom(int roomIdIndex) {TRTCCloudDef.TRTCParams mTRTCParams = new TRTCCloudDef.TRTCParams();mTRTCParams.sdkAppId = GenerateTestUserSig.SDKAPPID;mTRTCParams.userId = "123";mTRTCParams.strRoomId = mRoomIds[roomIdIndex];mTRTCParams.userSig = GenerateTestUserSig.genTestUserSig(mTRTCParams.userId);mTRTCParams.role = TRTCCloudDef.TRTCRoleAudience;mTRTCCloud.enterRoom(mTRTCParams, TRTCCloudDef.TRTC_APP_SCENE_LIVE);}private TRTCCloudListener mTRTCCloudListener = new TRTCCloudListener() {public void onEnterRoom(long result) {if (result == 0) {// Enter room success} else {// Enter room failed}}public void onExitRoom(int reason) {// Exit room}@Overridepublic void onUserVideoAvailable(String userId, boolean available) {super.onUserVideoAvailable(userId, available);if (available) {mTRTCCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, mRemoteVideoView);} else {mTRTCCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);}}public void onError(int errCode, String errMsg, Bundle extraInfo) {// print ErrorLog.e("ScrollSwitchRoom", "Error: " + errCode + " " + errMsg);}};private void exitRoom() {mTRTCCloud.stopAllRemoteView();mTRTCCloud.exitRoom();// mTRTCCloud.setListener(null);}public class PageAdapter extends RecyclerView.Adapter<PageAdapter.PageViewHolder> {private Context context;public String[] mRoomIds;public PageAdapter(Context context, String[] roomIds) {this.context = context;this.mRoomIds = roomIds;}public PageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.item_scroll_page, parent, false);return new PageViewHolder(view);}public void onBindViewHolder(@NonNull PageViewHolder holder, int position) {TextView textView = holder.itemView.findViewById(R.id.tv_room_number);textView.setText(getString(R.string.switchroom_roomid) + ":" + mRoomIds[position]);}public int getItemCount() {return mRoomIds.length;}public int getItemViewType(int position) {return position;}class PageViewHolder extends RecyclerView.ViewHolder {PageViewHolder(@NonNull View itemView) {super(itemView);}}}@Overrideprotected void onDestroy() {super.onDestroy();exitRoom();}}
<?xml version="1.0" encoding="utf-8"?><androidx.viewpager2.widget.ViewPager2 xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="match_parent" />
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/item_layout"android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/iv_placeholder"android:scaleType="centerCrop"android:background="@drawable/placeholder_img"android:layout_width="match_parent"android:layout_height="match_parent"/><com.tencent.rtmp.ui.TXCloudVideoViewandroid:id="@+id/txcvv_main_local"android:layout_width="match_parent"android:layout_height="match_parent" /><TextViewandroid:id="@+id/tv_room_number"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintEnd_toEndOf="@id/item_layout"app:layout_constraintStart_toStartOf="@id/item_layout" /></androidx.constraintlayout.widget.ConstraintLayout>

public class ScrollSwitchRoomDualActivity extends TRTCBaseActivity {PageAdapter mAdapter;public String[] mRoomIds;private TRTCCloud mTRTCCloud;private TRTCCloud mSubCloud;private TXCloudVideoView mRemoteVideoView;private TXCloudVideoView mSubRemoteVideoView;private Boolean mIsInMainRoom = null;private int mCurPos = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_scroll_switch_room);//隐藏标题栏getSupportActionBar().hide();mRoomIds = new String[]{"1231", "1232", "1233"};if (checkPermission()) {initView();}}@Overrideprotected void onPermissionGranted() {initView();}private void initView() {mAdapter = new PageAdapter(this,mRoomIds);ViewPager2 viewPager = findViewById(R.id.viewPager);viewPager.setAdapter(mAdapter);//设置 viewPager 滑动方向viewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);// 设置 viewPager 预加载viewPager.setOffscreenPageLimit(1);// 添加页面切换监听viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {public void onPageSelected(int position) {Log.d("ScrollSwitchRoom", "onPageSelected: " + position);RecyclerView recyclerViewImpl = (RecyclerView) viewPager.getChildAt(0);if (mIsInMainRoom == null) {View itemView = recyclerViewImpl.getChildAt(position);mRemoteVideoView = itemView.findViewById(R.id.txcvv_main_local);View subItemView = recyclerViewImpl.getChildAt(position + 1);mSubRemoteVideoView = subItemView.findViewById(R.id.txcvv_main_local);enterRoom();mIsInMainRoom = true;} else {if (mIsInMainRoom) {mTRTCCloud.muteAllRemoteAudio(true);mSubCloud.muteAllRemoteAudio(false);} else {mTRTCCloud.muteAllRemoteAudio(false);mSubCloud.muteAllRemoteAudio(true);}if (position != (mRoomIds.length - 1)) {String roomId;TRTCCloud trtcCloud;if (mCurPos < position) {// 屏幕向上滑roomId = mRoomIds[position + 1];trtcCloud = mIsInMainRoom ? mTRTCCloud : mSubCloud;View itemView = recyclerViewImpl.getChildAt(position + 1);if (mIsInMainRoom) {mRemoteVideoView = itemView.findViewById(R.id.txcvv_main_local);} else {mSubRemoteVideoView = itemView.findViewById(R.id.txcvv_main_local);}} else {//屏幕向下滑roomId = mRoomIds[position];trtcCloud = mIsInMainRoom ? mSubCloud : mTRTCCloud;View itemView = recyclerViewImpl.getChildAt(position);if (mIsInMainRoom) {mSubRemoteVideoView = itemView.findViewById(R.id.txcvv_main_local);} else {mRemoteVideoView = itemView.findViewById(R.id.txcvv_main_local);}}switchRoom(roomId, trtcCloud);}mIsInMainRoom = !mIsInMainRoom;}mCurPos = position;}});// Initialize your views heremTRTCCloud = TRTCCloud.sharedInstance(getApplicationContext());mSubCloud = mTRTCCloud.createSubCloud();}/*** 只有在初始化时,才调用进入房间,后续切换房间,调用 switchRoom*/private void enterRoom() {mTRTCCloud.addListener(mTRTCCloudListener);mSubCloud.addListener(mSubCloudListener);TRTCCloudDef.TRTCParams mTRTCParams = new TRTCCloudDef.TRTCParams();mTRTCParams.sdkAppId = GenerateTestUserSig.SDKAPPID;mTRTCParams.userId = "123";// mTRTCParams.roomId = Integer.parseInt(roomId);mTRTCParams.strRoomId = mRoomIds[0];mTRTCParams.userSig = GenerateTestUserSig.genTestUserSig(mTRTCParams.userId);mTRTCParams.role = TRTCCloudDef.TRTCRoleAudience;mTRTCCloud.enterRoom(mTRTCParams, TRTCCloudDef.TRTC_APP_SCENE_LIVE);mTRTCParams.strRoomId = mRoomIds[1];mSubCloud.muteAllRemoteAudio(true);mSubCloud.enterRoom(mTRTCParams, TRTCCloudDef.TRTC_APP_SCENE_LIVE);}private TRTCCloudListener mTRTCCloudListener = new TRTCCloudListener() {public void onEnterRoom(long result) {if (result == 0) {// Enter room success} else {// Enter room failed}}public void onExitRoom(int reason) {// Exit room}@Overridepublic void onUserVideoAvailable(String userId, boolean available) {super.onUserVideoAvailable(userId, available);if (available) {mTRTCCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, mRemoteVideoView);} else {mTRTCCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);}}public void onError(int errCode, String errMsg, Bundle extraInfo) {// print ErrorLog.e("ScrollSwitchRoom", "Error: " + errCode + " " + errMsg);}public void onSwitchRoom(long err, String errMsg) {// Switch room}};private TRTCCloudListener mSubCloudListener = new TRTCCloudListener() {public void onEnterRoom(long result) {if (result == 0) {// Enter room success} else {// Enter room failed}}public void onExitRoom(int reason) {// Exit room}@Overridepublic void onUserVideoAvailable(String userId, boolean available) {super.onUserVideoAvailable(userId, available);if (available) {mSubCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, mSubRemoteVideoView);} else {mSubCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);}}};private void exitRoom() {mTRTCCloud.stopAllRemoteView();mTRTCCloud.exitRoom();mTRTCCloud.setListener(null);mSubCloud.stopAllRemoteView();mSubCloud.exitRoom();mSubCloud.setListener(null);}private void switchRoom(String roomId, TRTCCloud trtcCloud) {TRTCCloudDef.TRTCSwitchRoomConfig config = new TRTCCloudDef.TRTCSwitchRoomConfig();// config.roomId = Integer.parseInt(roomId);config.strRoomId = roomId;trtcCloud.switchRoom(config);}public class PageAdapter extends RecyclerView.Adapter<PageAdapter.PageViewHolder> {private Context context;public String[] mRoomIds;public PageAdapter(Context context, String[] roomIds) {this.context = context;this.mRoomIds = roomIds;}public PageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.item_scroll_page, parent, false);return new PageViewHolder(view);}public void onBindViewHolder(@NonNull PageViewHolder holder, int position) {TextView textView = holder.itemView.findViewById(R.id.tv_room_number);textView.setText(getString(R.string.switchroom_roomid) + ":" + mRoomIds[position]);}public int getItemCount() {return mRoomIds.length;}public int getItemViewType(int position) {return position;}class PageViewHolder extends RecyclerView.ViewHolder {PageViewHolder(@NonNull View itemView) {super(itemView);}}}@Overrideprotected void onDestroy() {super.onDestroy();exitRoom();}}
文档反馈