主播 PK 方案介绍
主播 PK 流程
一般情况主播 PK 流程如下:
PK 前:主播们各自用 RTC 地址推流。
PK 时:主播们之间相互播放对方的 RTC 流地址。
PK 后:主播们停止播放对方的 RTC 地址。
方案演示
主播 A 和主播 B | | | |
观众 | | | |
主播 A | | |
主播 B | | |
主播 A 的观众 | | |
主播 A(手机 A) | 主播 B(手机 B) | 主播 A 的观众(手机 C) |
| | |
RTC PK 功能实现
如下示意图,主播 A 有观众 A,主播 B 有观众 B,如果主播 A 和 B 进行 PK,需要做的事情非常简单:
主播 A:开始播放主播 B 的流,同时发起混流指令,把 A 和 B 的内容合成一路,供主播 A 的观众观看。
主播 B:开始播放主播 A 的流,同时发起混流指令,把 B 和 A 的内容合成一路,供主播 B 的观众观看。
观众 A 和 B:无需变化,继续 CDN 播放即可,只不过会看到各自主播混流后的 PK 画面。
1. 主播 A 开始推流
V2TXLivePusher pusher = new V2TXLivePusherImpl(this, V2TXLiveMode.TXLiveMode_RTC);pushURLA= "trtc://cloud.tencent.com/push/streamid?sdkappid=1400188888&userId=A&usersig=xxx";pusher.startPush(pushURLA);
V2TXLivePusher *pusher = [[V2TXLivePusher alloc] initWithLiveMode:V2TXLiveMode_RTC];NSString *pushURLA = @"trtc://cloud.tencent.com/push/streamid?sdkappid=1400188888&userId=A&usersig=xxx";[pusher startPush:pushURLA];
2. 主播 B 开始推流
V2TXLivePusher pusher = new V2TXLivePusherImpl(this, V2TXLiveMode.TXLiveMode_RTC);pushURLB "trtc://cloud.tencent.com/push/streamid?sdkappid=1400188888&userId=B&usersig=xxx";pusher.startPush(pushURLB);
V2TXLivePusher *pusher = [[V2TXLivePusher alloc] initWithLiveMode:V2TXLiveMode_RTC];NSString *pushURLB = @"trtc://cloud.tencent.com/push/streamid?sdkappid=1400188888&userId=B&;usersig=xxx";[pusher startPush:pushURLB];
3. 开始 PK
// 主播AV2TXLivePlayer player = new V2TXLivePlayerImpl(mContext);playURLB = "trtc://cloud.tencent.com/play/streamid?sdkappid=1400188888&userId=A&usersig=xxx&appscene=live"player.startLivePlay(playURLB);...// 主播BV2TXLivePlayer player = new V2TXLivePlayerImpl(mContext);playURLA= "trtc://cloud.tencent.com/play/streamid?sdkappid=1400188888&userId=B&usersig=xxx&appscene=live"player.startLivePlay(playURLA);
// 主播AV2TXLivePlayer *player = [[V2TXLivePlayer alloc] init];NSString *playURLB = "trtc://cloud.tencent.com/play/streamid?sdkappid=1400188888&userId=A&usersig=xxx&appscene=live"[player setRenderView:view];[player startLivePlay:playURLB];...// 主播BV2TXLivePlayer *player = [[V2TXLivePlayer alloc] init];NSString *playURLA = "trtc://cloud.tencent.com/play/streamid?sdkappid=1400188888&userId=B&usersig=xxx&appscene=live"[player setRenderView:view];[player startLivePlay:playURLA];
4. PK 成功,观看 PK 内容
PK 成功后,观众有两种方式可以观看 PK 内容。
1. 主播 A 和主播 B 的观众各自调用 V2TXLivePlayer 开始播放另外一名主播的推流内容。
2. 主播 A 和主播 B 进行混流,观众端播放 URL 保持不变。
主播 A 和主播 B 发起一次混流操作,也就是主播将自己和对方主播混合成一路流,自己直播间的观众就可以在看到自己和对方主播进行互动。 主播 A 和 B 各自调用 setMixTranscodingConfig 接口启动云端混流,调用时需要设置音频相关的参数,例如:
音频采样率 audioSampleRate
、音频码率 audioBitrate
和声道数 audioChannels
等。示例代码 :
// 主播 AV2TXLiveDef.V2TXLiveTranscodingConfig config = new V2TXLiveDef.V2TXLiveTranscodingConfig();// 设置分辨率为 720 × 1280, 码率为 1500kbps,帧率为 20FPSconfig.videoWidth = 720;config.videoHeight = 1280;config.videoBitrate = 1500;config.videoFramerate = 20;config.videoGOP = 2;config.audioSampleRate = 48000;config.audioBitrate = 64;config.audioChannels = 2;config.mixStreams = new ArrayList<>();// 主播 A 摄像头的画面位置V2TXLiveDef.V2TXLiveMixStream local = new V2TXLiveDef.V2TXLiveMixStream();local.userId = "localUserId";local.streamId = null; // 本地画面不用填写 streamID,远程需要local.x = 0;local.y = 0;local.width = videoWidth;local.height = videoHeight;local.zOrder = 0; // zOrder 为 0 代表主播画面位于最底层config.mixStreams.add(local);// PK主播 B 的画面位置V2TXLiveDef.V2TXLiveMixStream remoteB = new V2TXLiveDef.V2TXLiveMixStream();remoteB.userId = "remoteUserIdB";remoteB.streamId = "remoteStreamIdB"; // 本地画面不用填写 streamID,远程需要remoteB.x = 400; //仅供参考remoteB.y = 800; //仅供参考remoteB.width = 180; //仅供参考remoteB.height = 240; //仅供参考remoteB.zOrder = 1;config.mixStreams.add(remoteB);// 发起云端混流pusher.setMixTranscodingConfig(config);//主播 BV2TXLiveDef.V2TXLiveTranscodingConfig config = new V2TXLiveDef.V2TXLiveTranscodingConfig();// 设置分辨率为 720 × 1280, 码率为 1500kbps,帧率为 20FPSconfig.videoWidth = 720;config.videoHeight = 1280;config.videoBitrate = 1500;config.videoFramerate = 20;config.videoGOP = 2;config.audioSampleRate = 48000;config.audioBitrate = 64;config.audioChannels = 2;config.mixStreams = new ArrayList<>();// 主播 B 摄像头的画面位置V2TXLiveDef.V2TXLiveMixStream local = new V2TXLiveDef.V2TXLiveMixStream();local.userId = "localUserId";local.streamId = null; // 本地画面不用填写 streamID,远程需要local.x = 0;local.y = 0;local.width = videoWidth;local.height = videoHeight;local.zOrder = 0; // zOrder 为 0 代表主播画面位于最底层config.mixStreams.add(local);// PK主播 A 的画面位置V2TXLiveDef.V2TXLiveMixStream remoteA = new V2TXLiveDef.V2TXLiveMixStream();remoteA.userId = "remoteUserIdA";remoteA.streamId = "remoteStreamIdA"; // 本地画面不用填写 streamID,远程需要remoteA.x = 400; //仅供参考remoteA.y = 800; //仅供参考remoteA.width = 180; //仅供参考remoteA.height = 240; //仅供参考remoteA.zOrder = 1;config.mixStreams.add(remoteA);// 发起云端混流pusher.setMixTranscodingConfig(config);
// 主播 AV2TXLiveTranscodingConfig *config = [[V2TXLiveTranscodingConfig alloc] init];// 设置分辨率为 720 × 1280, 码率为 1500kbps,帧率为 20FPSconfig.videoWidth = 720;config.videoHeight = 1280;config.videoBitrate = 1500;config.videoFramerate = 20;config.videoGOP = 2;config.audioSampleRate = 48000;config.audioBitrate = 64;config.audioChannels = 2;// 主播 A 摄像头的画面位置V2TXLiveMixStream *local = [[V2TXLiveMixStream alloc] init];local.userId = @"localUserId";local.streamId = nil; // 本地画面不用填写 streamID,远程需要local.x = 0;local.y = 0;local.width = videoWidth;local.height = videoHeight;local.zOrder = 0; // zOrder 为 0 代表主播画面位于最底层// PK主播 B 的画面位置V2TXLiveMixStream *remoteB = [[V2TXLiveMixStream alloc] init];remoteB.userId = @"remoteUserIdB";remoteB.streamId = @"remoteStreamIdB"; // 本地画面不用填写 streamID,远程需要remoteB.x = 400; //仅供参考remoteB.y = 800; //仅供参考remoteB.width = 180; //仅供参考remoteB.height = 240; //仅供参考remoteB.zOrder = 1;//设置混流 streamsconfig.mixStreams = @[local,remoteB];// 发起云端混流pusher.setMixTranscodingConfig(config);// 主播 BV2TXLiveTranscodingConfig *config = [[V2TXLiveTranscodingConfig alloc] init];// 设置分辨率为 720 × 1280, 码率为 1500kbps,帧率为 20FPSconfig.videoWidth = 720;config.videoHeight = 1280;config.videoBitrate = 1500;config.videoFramerate = 20;config.videoGOP = 2;config.audioSampleRate = 48000;config.audioBitrate = 64;config.audioChannels = 2;// 主播 A 摄像头的画面位置V2TXLiveMixStream *local = [[V2TXLiveMixStream alloc] init];local.userId = @"localUserId";local.streamId = nil; // 本地画面不用填写 streamID,远程需要local.x = 0;local.y = 0;local.width = videoWidth;local.height = videoHeight;local.zOrder = 0; // zOrder 为 0 代表主播画面位于最底层// PK主播 A 的画面位置V2TXLiveMixStream *remoteA = [[V2TXLiveMixStream alloc] init];remoteA.userId = @"remoteUserIdA";remoteA.streamId = @"remoteStreamIdA"; // 本地画面不用填写 streamID,远程需要remoteA.x = 400; //仅供参考remoteA.y = 800; //仅供参考remoteA.width = 180; //仅供参考remoteA.height = 240; //仅供参考remoteA.zOrder = 1;//设置混流 streamsconfig.mixStreams = @[local,remoteA];// 发起云端混流pusher.setMixTranscodingConfig(config);
注意:
发起云端混流后,默认混流 ID,是发起混流者的 ID,如果需要指定流 ID,需要进行传入。
5. 结束 PK
注意:
RTC PK 基于实时音视频 TRTC 实现,因此 PK 结束后,需停止所有 PK 参与者(多个主播)的 RTC 推流和拉流,否则会产生额外的 TRTC 在房时长费用,具体实现请参考下方代码。
主播 A 和主播 B 分别调用
V2TXLivePlayer
停止播放对方的流。然后根据观众观看的两种方式分别处理。1. 主播端未开启混流,而是观众端自己去播放对方主播的推流内容,此时观众端停止播放即可。
2. 主播端开启了混流,那么主播端需要停止混流,观众端不影响。
// 主播 A 停止播放主播 B 的流player.stopPlay();// 主播 B 停止播放主播 A 的流player.stopPlay();// 方式1:观众各自调用 V2TXLivePlayer 停止播放对方主播的推流内容player.stopPlay();// 方式2:主播端停止混流(主播 A 和主播 B 都要执行)pusher.setMixTranscodingConfig(null);
// 主播 A 停止播放主播 B 的流[player stopPlay];// 主播 B 停止播放主播 A 的流[player stopPlay];// 方式1:观众各自调用 V2TXLivePlayer 停止播放对方主播的推流内容[player stopPlay];// 方式2:主播端停止混流(主播 A 和主播 B 都要执行)[pusher setMixTranscodingConfig: nil];
常见问题
1. 为什么使用V2TXLivePusher&V2TXLivePlayer
接口时,同一台设备不支持使用相同 streamid 同时推流和拉流,而 TXLivePusher&TXLivePlayer
可以支持?
当前
V2TXLivePusher&V2TXLivePlayer
是 腾讯云 TRTC 协议实现,其基于 UDP 的超低延时的私有协议,考虑到用户的具体使用场景,不支持同一台设备,使用相同的 streamid,一边推超低延时流,一边拉超低延时的流。2. V2TXLivePusher&V2TXLivePlayer 如何设置音质或者画质呢?
3. V2TXLivePusher#startPush
或V2TXLivePlayer#startLivePlay
收到错误码:-5
代表什么意思?
4. RTC连麦方案的时延性有可以参考的数据吗?
主播连麦的延时 < 200ms,主播和观众的延时在 100ms - 1000ms。
5. RTC 推流成功后,使用 CDN 拉流一直提示404?
检查一下是否有开启实时音视频服务的旁路直播功能,基本原理是 RTC 协议推流后,如果需要使用 CDN 播放,RTC 会在后台服务中旁路流信息到 CDN 上。
6. 如何避免额外计费?
及时主动停止推流(
V2TXLivePusher 的 stopPush
)和拉流(V2TXLivePlayer 的 stopPlay
)。只要推流或拉流在进行中,就会正常计费。7.主播 PK 支持哪些平台?
主播 PK 支持 iOS、Android、小程序和 Flutter 端。