用户端在 MediaSideInfo(媒体次要信息) 中获取到主播端发送的题目信息。题目信息会按照预先约定的 JSON 格式组织。
如果开发者使用即构提供的主播端推流,请务必按照特定的 JSON 格式解析数据。
如果开发者自行实现主播端推流,内部约定主播端与观众端的通信格式即可。
获取 MediaSideInfo 的开发步骤如下所示。
ZEGO SDK 提供了相关接口,用于设置回调,接收媒体次要信息。
ZegoLiveRoomApi-Player.h
/**
设置回调, 接收媒体次要信息
@param onMediaSideCallback 回调函数指针, pszStreamID:流ID,标记当前回调的信息属于哪条流, buf:接收到的信息数据(具体内容参考官网对应文档中的格式说明), dataLen:buf 总长度
@discussion 开始拉流前调用。观众端在此 API 设置的回调中获取主播端发送的次要信息(要求主播端开启发送媒体次要信息开关,并调用 [ZegoLiveRoomApi (Publisher) -sendMediaSideInfo:dataLen:packet:] 发送次要信息)。当不需要接收信息时,需将 onMediaSideCallback 置空,避免内存泄漏
*/
- (void)setMediaSideCallback:(void(*)(const char *pszStreamID, const unsigned char* buf, int dataLen))onMediaSideCallback;
代码示例如下:
ZegoPlayViewController.m
[[ZegoSDKManager api] setMediaSideCallback:onReceivedMediaSideInfo];
其中,onReceivedMediaSideInfo
是满足特定格式要求的 C 语言函数。当用户端收到媒体次要信息时,SDK 会调用之。因此,对题目的处理,均在 onReceivedMediaSideInfo
中进行。
媒体次要信息会作为 onReceivedMediaSideInfo
的入参传递进去,开发者需要在该函数内,根据业务要求,处理媒体次要信息。
主播向观众发送答案的 JSON 数据格式为:
{
"seq": 1,
"type": "question",
"data": {
"id": "0ca175b9c0f726a831d895e269332461",
"index": 1,
"activity_id": "123456",
"title": "下面哪个省的面积最大? ",
"options": [
{
"answer": "A",
"option": "河北"
},
{
"answer": "B",
"option": "山东"
},
{
"answer": "C",
"option": "湖南"
}
]
}
}
JSON 字段含义为:
参数名 | 类型 | 说明 |
---|---|---|
seq | Int | 协议序列号,一轮活动内递增 |
type | String | 消息类型;可用类型为:question(题目),answer(答案),sum(用户列表汇总) |
id | String | 题目 id;题目的唯一标识,由客户保证唯一性;最大 64 bytes(包括字符串结束符), 不能包含字符 ',' |
index | Int | 题目序号;第 n 题序号为 n |
activity_id | String | 活动 id,答题场次(活动)的唯一标识;由客户保证唯一性;一个答题场次有多个题目; 最大 64 bytes(包括字符串结束符),不能包含字符 ',' |
title | String | 题目标题;可与 index 配合展示为题目描述 |
options | Array | 题目选项列表 |
answer | String | 某一选项的序号 |
option | String | 某一选项的描述 |
演示 Demo 中处理媒体次要信息的示例代码如下:
ZegoPlayViewController.m
void onReceivedMediaSideInfo(const char *pszStreamID, const unsigned char* buf, int dataLen) {
if (dataLen == 0) {
NSLog(@"%s, data is empty", __func__);
return;
}
NSData *mediaInfo = [NSData dataWithBytes:buf + 4 length:dataLen - 4];
NSError *error = nil;
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:mediaInfo options:0 error:&error];
if (error == nil) {
int seq = [info[@"seq"] intValue];
if (seq <= mediaSeq) {
NSLog(@"%s, repeat seq: %d, discard", __func__, seq);
return;
}
NSLog(@"%s, type: %@, activityId: %@, questionId: %@", __func__, info[@"type"], info[@"data"][@"id"], info[@"data"][@"activity_id"]);
mediaSeq = seq;
if ([info[@"type"] isEqualToString:questionKey]) {
[selfObject handleQuestionInfo:info];
} else if ([info[@"type"] isEqualToString:answerKey]) {
[selfObject handleAnswerInfo:info];
} else if ([info[@"type"] isEqualToString:sumKey]) {
[selfObject handleFinalResult:info];
} else {
NSLog(@"onReceivedMediaSideInfo unknown type, don't handle");
}
}
}
关于媒体次要信息功能的详细说明,可参考文档:视频进阶-媒体次要信息
开发者从媒体次要信息中,解析出题目,并正确展示给用户后,用户可以开始作答。SDK 提供了统一的接口,用于用户答题。
ZegoLiveRoomApi-Relay.h
/**
转发接口
@param data 需要转发的数据
@param type 转发类型
@param completionBlock 转发发送结果,回调 server 下发的转发结果
@return true 成功,false 失败
@discussion 这个接口用来实现大并发的调用,实现观众答题的功能就需要调用这个接口
*/
- (bool)relayData:(NSString *)data type:(ZegoRelayType)type completion:(ZegoRelayCompletionBlock)completionBlock;
调用该接口答题,需要注意以下事项:
ZEGO_RELAY_TYPE_DATI
请注意,1 中指定的 JSON 格式如下。开发者必须按照此格式传递字符串,格式不对会导致后台解析错误,答题出错。
{
"activity_id": "",
"question_id": "",
"answer": "",
"user_data": ""
}
参数说明为:
参数名 | 类型 | 说明 |
---|---|---|
activity_id | String | 活动 id,答题场次(活动)的唯一标识;由客户保证唯一性;一个答题场次有多个题目; 最大 64 bytes(包括字符串结束符),不能包含字符 ',';观众端从主播端获取活动 id |
question_id | String | 题目 id;题目的唯一标识,由客户保证唯一性;最大 64 bytes(包括字符串结束符), 不能包含字符 ',';观众端从主播端获取题目 id |
answer | String | 观众答题,要与主播下发题目中的 answer 字段值(例如 "A")保持一致 |
user_data | String | 用户自定义数据。开发者上传 user_data 后可调用拉取流水信息接口获取。该数据可以 用于进行消息一致性、完整性校验。 |
Demo 中示例代码如下:
ZegoPlayViewController.m
- (void)relayDataWithAnswer:(NSString *)answer {
self.currentQuiz.userAnswer = answer;
NSString *relayData = [ZegoQuizParser assembleRelayData:self.activityInfo.activityID questionID:self.currentQuiz.quizID answer:answer userData:nil];
if (relayData == nil) {
NSLog(@"relay data is nil");
return;
}
BOOL invokeSuccess = [[ZegoSDKManager api] relayData:relayData type:ZEGO_RELAY_TYPE_DATI completion:^(int errorCode, NSString *roomId, NSString *relayResult) {
if (errorCode == 0) {
NSLog(@"relay data send succeed");
} else {
NSLog(@"relay data send failed, errorCode: %d", errorCode);
}
}];
if (!invokeSuccess) {
NSLog(@"relay data invoke failed");
}
}
观众端仍然在媒体次要信息中接收每道题答案、统计结果。
对媒体次要信息的获取同第一小节一致,处理逻辑视业务逻辑而定。
主播向观众发送答案的 JSON 数据格式为:
{
"seq": 2,
"type": "answer",
"data": {
"id": "0ca175b9c0f726a831d895e269332461",
"correct_answer": "B",
"activity_id": "123456",
"answer_stat": [
{
"answer": "A",
"user_count": 2000
},
{
"answer": "B",
"user_count": 1300
},
{
"answer": "C",
"user_count": 420
}
]
}
}
参数说明为:
参数名 | 类型 | 说明 |
---|---|---|
seq | Int | 协议序列号,一轮活动内递增 |
type | String | 消息类型;可用类型为:question(题目),answer(答案),sum(用户列表汇总) |
id | String | 题目 id;题目的唯一标识,由客户保证唯一性;最大 64 bytes(包括字符串结束符), 不能包含字符 ',' |
correct_answer | String | 某道题目的正确答案 |
activity_id | String | 活动 id,答题场次(活动)的唯一标识;由客户保证唯一性;一个答题场次有多个题目; 最大 64 bytes(包括字符串结束符),不能包含字符 ',' |
answer_stat | Array | 结果统计列表 |
answer | String | 某一选项的序号 |
user_count | String | 选择了该选项的用户总人数 |
每轮活动结束后,观众端或需要拉取最终胜利的用户列表,该信息仍然在媒体次要信息中获取。
对媒体次要信息的获取同第一小节一致,处理逻辑视业务逻辑而定。
主播向观众发送用户列表的 JSON 数据格式为:
{
"seq": 3,
"type": "sum",
"data": {
"room_id": "roomid123",
"activity_id": "123456",
"user_list": [
{
"id_name": "555",
"nick_name": "lzp"
},
{
"id_name": "666",
"nick_name": "hhh"
}
]
}
}
参数说明为:
参数名 | 类型 | 说明 |
---|---|---|
seq | Int | 协议序列号,一轮活动内递增 |
type | String | 消息类型;可用类型为:question(题目),answer(答案),sum(用户列表汇总) |
room_id | String | 房间 id;房间的唯一标识 |
activity_id | String | 活动 id,答题场次(活动)的唯一标识;由客户保证唯一性;一个答题场次有多个题目; 最大 64 bytes(包括字符串结束符),不能包含字符 ',' |
user_list | Array | 最终胜利的用户列表 |
id_name | String | 用户 id,用户的唯一标识 |
nick_name | String | 用户昵称 |
直播答题房间内人数数量可能非常多,房间消息会出现高并发使用场景。
开发者需要调用 ZEGO SDK 提供的、用于发送/接受大房间消息的接口,来实现房间内消息处理。
如果开发者仍然使用普通房间消息收发接口(sendRoomMessage
onRecvRoomMessage
),Zego 后台服务可能会丢弃较多消息,影响业务方的正常功能。
开发者可调用如下 API 发送大房间消息。
ZegoLiveRoomApi-IM.h
/**
房间发送不可靠信道的消息
@param content 消息内容
@param type 消息类型,可以自定义
@param category 消息分类,可以自定义
@param completionBlock 消息发送结果,回调 server 下发的 messageId
@return true 成功,false 失败
@discussion 用于高并发的场景,消息可能被丢弃,当高并发达到极限时会根据策略丢弃部分消息
*/
- (bool)sendBigRoomMessage:(NSString *)content type:(ZegoMessageType)type category:(ZegoMessageCategory)category completion:(ZegoBigRoomMessageCompletion)completionBlock;
Demo 代码示例如下:
ZegoPlayViewController.m
- (void)sendRoomMessage {
[self.customCommentView.commentInput resignFirstResponder];
if (self.customCommentView.commentInput.text.length == 0) {
NSLog(@"%s,评论为空,不发送任何信息", __func__);
return;
}
NSString *comment = self.customCommentView.commentInput.text;
bool ret = [[ZegoSDKManager api] sendBigRoomMessage:comment
type:ZEGO_TEXT
category:ZEGO_CHAT
completion:nil];
if (ret) {
ZegoBigRoomMessage *roomMessage = [ZegoBigRoomMessage new];
roomMessage.fromUserId = [ZegoSetting sharedInstance].userID;
roomMessage.fromUserName = [ZegoSetting sharedInstance].userName;
roomMessage.content = comment;
roomMessage.type = ZEGO_TEXT;
roomMessage.category = ZEGO_CHAT;
roomMessage.priority = ZEGO_DEFAULT;
[self.mockedMessageList addObject:roomMessage];
self.messageViewController.messageList = self.mockedMessageList;
self.customCommentView.commentInput.text = @"";
}
}
正常接收消息前,需要先设置代理对象,调用如下 API 进行:
/**
设置 IM 代理对象
@param imDelegate 遵循 ZegoIMDelegate 协议的代理对象
@return true 成功,false 失败
@discussion 使用 IM 功能,初始化相关视图控制器时需要设置代理对象。未设置代理对象,或对象设置错误,可能导致无法正常收到相关回调
*/
- (bool)setIMDelegate:(id<ZegoIMDelegate>)imDelegate;
代理对象设置成功后,当同一房间内的其他用户发送大房间消息时,可在如下回调中接收消息:
ZegoLiveRoomApi-IM.h
/**
收到房间的不可靠消息广播
@param roomId 房间 Id
@param messageList 消息列表,包括消息内容,消息分类,消息类型,发送者等信息
@discussion 调用 [ZegoLiveRoomApi (IM) -setIMDelegate:] 设置代理对象成功后,调用 [ZegoLiveRoomApi (IM) -sendBigRoomMessage:type:category:completion:] 发送消息,会触发此通知
*/
- (void)onRecvBigRoomMessage:(NSString *)roomId messageList:(NSArray<ZegoBigRoomMessage*> *)messageList;
Demo 代码示例如下:
ZegoPlayViewController.m
- (void)onRecvBigRoomMessage:(NSString *)roomId messageList:(NSArray<ZegoBigRoomMessage*> *)messageList {
if (![roomId isEqualToString:self.roomInfo.roomID]) {
NSLog(@"%s, receive big room message, but roomId mismatch, abandon", __func__);
return;
}
if (messageList.count == 0) {
NSLog(@"%s, receive big room message, but messageList is nil", __func__);
return;
}
[self.mockedMessageList addObjectsFromArray:messageList];
self.messageViewController.messageList = self.mockedMessageList;
}
ZEGO SDK 提供如下 API,向开发者提供房间内实时在线用户数。该回调目前默认为 30s 回调一次,开发者可联系即构技术支持,自定义回调频率。
ZegoLiveRoomApi-IM.h
/**
收到在线人数更新
@param onlineCount 在线人数。默认为 30s 回调一次,开发者可联系即构技术支持,自定义回调频率。
@param roomId 房间 Id
*/
- (void)onUpdateOnlineCount:(int)onlineCount room:(NSString *)roomId;
请注意,通过该回调接收在线人数更新前,需要调用
setIMDelegate:
设置 IM 代理对象,具体设置步骤同 5.2 节中setIMDelegate:
一致。
Demo 代码示例如下:
ZegoPlayViewController.m
// 更新在线人数 label
- (void)onUpdateOnlineCount:(int)onlineCount room:(NSString *)roomId {
if ([roomId isEqualToString:self.roomInfo.roomID]) {
self.onlineCountLabel.text = [NSString stringWithFormat:@"%d", onlineCount];
}
}
联系我们
文档反馈