首页 > 业界新闻 > 详情

抓不到娃娃?不能总怪运气差!


伴随科技蓬勃发展,VR/AR赋予了游戏全新的载体,让玩家获取到沉浸式的体验,但若说将游戏与O2O模式结合显得有些虚幻,但就在最近一个被“吵”热的产品进入到我们的视线之中——“在线抓娃娃”,为我们展现了一个将线上和线下巧妙结合的娱乐消费新模式。


与传统线下抓娃娃相比,它显然打破了时间和空间的界限,只需要一部手机和网络就能随时随地玩,与此同时它还注入了社交元素,想象一下,有上万人看你抓娃娃,帮你出谋划策,甚至还能让其他人来帮你抓,突破了原本的“自娱自乐”。说不定未来大家打招呼的方式也变成“你今天抓娃娃了吗?”(哈哈)


一个爆款创意如何实现至关重要,对“在线抓娃娃”而言,如何基于传统设备改造?如何保证线上线下互动的超低延迟?都是必须要完善的。今天,我们将以即构科技的“在线抓娃娃”为例,为大家介绍如何快速实现在线娃娃机产品。


在线抓娃娃硬件方案


一套完整的在线娃娃机硬件方案包含:2个摄像头、娃娃机整机(天车、硬件板子、娃娃机主板)。

271511771381113141.png

2个摄像头:用于采集娃娃机的实时场景。

硬件板子(Android开发板):可以像普通的安卓手机一样运行Android程序。比如我们要在娃娃机上推2路视频流,首先我们可以开发一个简单的Android程序集成到ZEGO SDK,然后使用API实现推2路视频流的功能,最后将Android程序刷到硬件板子上运行即可。

?娃娃机主板:控制娃娃机的核心部件。硬件板子(Android开发板)上运行的Android程序负责接收远程指令(前移、后移、左移、右移、抓娃娃等),并将远程指令转换成“串口指令”通过串口写入娃娃机主板,进而可以远程控制娃娃机。


在线抓娃娃产品架构图


“在线抓娃娃”整体架构共分为4部分:客户端、娃娃机、客户业务后台和ZEGO云后台。


271511771432485804.png

系统架构图-围观模式


271511771443635661.png

系统架构图-抓娃娃模式


客户端:主要功能包含用户注册、登录、充值、拉流观看(ZEGO API)、上机操作、展示结果等等。

客户业务后台:由客户自主开发、维护,主要功能包含用户注册、登录、充值、娃娃机状态管理、用户排队等。

ZEGO云后台:提供实时音视频和实时信令服务,以SDK的形式提供给开发者。

娃娃机:包含娃娃机控制端(运行在Android开发板上的Android程序)和下机位(娃娃机主板)两部分,主要功能包含推2路视频流(ZEGO API)、与业务后台通信以及与下位机(娃娃机主板)通信。


在线娃娃机功能实现

271511771458763928.png

娃娃机系统实现流程图


上图为娃娃机端程序与业务后台通讯的实现方案流程图,娃娃机端程序指的是集成在推流板子上的程序应用于互联网通讯和天车通讯。每一步业务侧需要对失败情况做异常处理。



271511771536552911.png

API调用时序图


在启动娃娃机后,系统会先初始化SDK,当有观众登录房间时,在loginRoom的blk回调里会download下来当前RoomID下所有的StreamID信息,当获取到StreamID后,直接调用startPlayingStream开始推流,进入房间的观众就可以看到当前实时画面。需要注意的是,娃娃机一般会同时推送2路流——正面和侧面,观众在进入房间时会同时播放这两路流,但是会先隐藏一个view,在点击切换摄像头时,同时切换的隐藏view。


当观众升级为玩家时,系统需要调用setConfig接口,切换拉流源,此时拉流服务器会切换到ZEGO服务器,实现低延迟。玩家操作手柄挤一挤控制娃娃机操作的所有指令会通过sendCustomCommand自定义信令借口发送到娃娃机端硬件板子的SDK上,再转换为娃娃机可识别指令(串口指令),从而实现玩家远程操作娃娃机。


当玩家上机结束后,系统会再次调用setConfig接口将玩家降级为观众,若需要再次上机,则重复API调用流程即可。


实现流程


登录房间


以下所有步骤均需要成功登录房间为前提。WaWaJi Client中相关源码片段如下:

[[ZegoManager api] loginRoom:self.roomID role:ZEGO_AUDIENCE withCompletionBlock:^(int errorCode, NSArray<ZegoStream *> *streamList) {

    NSLog(@"%s, error: %d", __func__, errorCode); 

 

    if (errorCode == 0) { 

        // 登录房间成功的后续操作 

    } 

}];

 

· 拉流

 

- (void)playVisibleStream:(NSString *)streamID inView:(UIView *)view { 

    // 开始拉流 

    [[ZegoManager api] startPlayingStream:streamID inView:view]; 


    // 设置流展示模式 

    [[ZegoManager api] setViewMode:ZegoVideoViewModeScaleAspectFit ofStream:streamID]; 


    // 设置流播放音量为最大 

    [[ZegoManager api] setPlayVolume:100 ofStream:streamID]; 

 

- (void)playInvisibleStream:(NSString *)streamID inView:(UIView *)view { 

    // 开始拉流 

    [[ZegoManager api] startPlayingStream:streamID inView:view]; 


    // 设置流展示模式 

    [[ZegoManager api] setViewMode:ZegoVideoViewModeScaleAspectFit ofStream:streamID]; 


    // 设置流播放音量为无声 

    [[ZegoManager api] setPlayVolume:0 ofStream:streamID]; 

}

 

这里需要特别注意的是:


1. 目前WaWaJi Client使用的方案,在进入房间后会创建两个view分别播放两路流数据,用户可通过手动切换view从而切换当前可见流。

2. 如果某一路流从可见切换为不可见时,需要调用setPlayVolumn:ofStream:将隐藏的流改为无声,否则会造成流画面和声音混乱的情况,反之亦然。

 

· 上机

 

当观众升级为玩家上机操作娃娃机时,系统将会调用setConfig设置prefer_play_ultra_source=1切换拉流源。具体步骤为:

 

1. 调用setConfig设置prefer_play_ultra_source=1

2. 调用stopPlayingStream停止播放所有的流;

3. 调用startPlayingStream:inView:开始播放所有的流。

 

相关源码片段如下:

 

- (void)onBoard { 

    [ZegoLiveRoomApi setConfig:@"prefer_play_ultra_source=1"]; 

    [self switchStream]; 

 

- (void)switchStream { 

    // 停止播放所有的流 

    if (self.firstStreamID.length) { 

        [[ZegoManager api] stopPlayingStream:self.firstStreamID]; 

    } 

 

    if (self.secondStreamID.length) { 

        [[ZegoManager api] stopPlayingStream:self.secondStreamID]; 

    } 

 

    // 重新开始播放所有的流 

    if (self.currentVisibleStreamIndex == 1) { 

        [self playVisibleStream:self.firstStreamID inView:self.firstPlayView]; 

        [self playInvisibleStream:self.secondStreamID inView:self.secondPlayView]; 

    } else { 

        [self playVisibleStream:self.secondStreamID inView:self.secondPlayView]; 

        [self playInvisibleStream:self.firstStreamID inView:self.firstPlayView]; 

    }

}

 

· 发送指令

 

上机成功后,玩家需要调用ZegoLiveRoom SDK提供的sendCustomCommand:content:completion:发送指令给娃娃机(指控制娃娃机硬件的server端),娃娃机收到指令后,作出对应的响应。

 

相关源码片段如下:

 

// 娃娃机 App 端向娃娃机发送预约命令 

BOOL invokeSuccess = [[ZegoManager api] sendCustomCommand:@[self.serverUser] content:applyCommand completion:^(int errorCode, NSString *roomID) { 

    NSLog(@"%@", [NSString stringWithFormat:@"[COMMAND] CMD_APPLY 发送结果:%d(0成功,1失败),第 %ld 次发送", errorCode, (current - self.applyCountdown) / 2 + 1]); 

}]; 

 

NSLog(@"%@", [NSString stringWithFormat:@"[COMMAND] CMD_APPLY 调用结果:%d(1成功,0失败),第 %ld 次发送", invokeSuccess, (current - self.applyCountdown) / 2 + 1]);

 

· 接收指令

 

如果有必要,娃娃机App端可通过onReceiveCustomCommand:userName:content:roomID:接收娃娃机返回的命令。

 

但在接收命令之前,必须先设置代理:

 

- (void)setupLiveKit { 

    // 设置房间代理 

    [[ZegoManager api] setRoomDelegate:self]; 

}

 

然后接受命令:

 

- (void)onReceiveCustomCommand:(NSString *)fromUserID userName:(NSString *)fromUserName content:(NSString *)content roomID:(NSString *)roomID { 

    // 处理收到的自定义指令 

}

 

· 下机

 

玩家在游戏结束后(即与娃娃机的信令交互结束),玩家将会完成下机操作并降级为观众。具体步骤为:

 

1.调用 setConfig 设置 prefer_play_ultra_source = 0

2.调用 stopPlayingStream 停止播放所有的流;

3.调用 startPlayingStream:inView: 开始播放所有的流。

 

- (void)offBoard { 

    [ZegoLiveRoomApi setConfig:@"prefer_play_ultra_source=0"]; 

    [self switchStream]; 

 

- (void)switchStream { 

    // 停止播放所有的流 

    if (self.firstStreamID.length) { 

        [[ZegoManager api] stopPlayingStream:self.firstStreamID]; 

    } 

 

    if (self.secondStreamID.length) { 

        [[ZegoManager api] stopPlayingStream:self.secondStreamID]; 

    } 

 

    // 重新开始播放所有的流 

    if (self.currentVisibleStreamIndex == 1) { 

        [self playVisibleStream:self.firstStreamID inView:self.firstPlayView]; 

        [self playInvisibleStream:self.secondStreamID inView:self.secondPlayView]; 

    } else { 

        [self playVisibleStream:self.secondStreamID inView:self.secondPlayView]; 

        [self playInvisibleStream:self.firstStreamID inView:self.firstPlayView]; 

    }

}

 

· 退出房间(停止拉流)

 

当观众退出当前房间时,需要调用退出房间,确保停止推流、拉流,并清空状态。

 

- (IBAction)onClose:(id)sender { 

    // 停止拉流 

    [[ZegoManager api] stopPlayingStream:self.firstStreamID]; 

    [[ZegoManager api] stopPlayingStream:self.secondStreamID]; 

 

    // 停止推流 

    [[ZegoManager api] stopPublishing]; 

 

    // 退出房间 

    if (self.loginRoomSucceed) { 

        [[ZegoManager api] logoutRoom]; 

    } 

 

    // 其他状态清理 

}

以上是即构科技在线抓娃娃从硬件方案、产品架构到功能实现的初步讲解。


觉得不过瘾?即构科技高级研发工程师吴彪在成都等你来现场面对面讲解、交流,即构在线娃娃机还有怎样的大招,让我们先一睹为快吧。



LiveVideoStack:即构科技在线娃娃机一站式解决方案的优势是什么?


冼牛:即构科技在线娃娃机一站式解决方案的核心优势有三个:

超低的实时视频传输延迟和实时信令控制延迟,平均延迟时间300ms,最低延迟100ms。这个特征可以让用户在抓娃娃的时候操控十分灵敏。

真正的一站式解决方案。刚开始即构只是提供实时视频和信令的SDK,可是在超过100家客户的接洽中,即构团队听到最多的呼声是要求有一站式的解决方案,能提供硬件解决方案(包块娃娃机整机、硬件板子和摄像头)和软件解决方案(实时视频通信和实时信令控制),还有代运营服务(租赁和托管,还有娃娃的采购,IP和物流等)。即构做到了真正的一站式解决方案,客户只需要把即构demo app换一个皮肤就可以上线自己的产品。

能兼容微信的H5方案,不只是兼容微信,还能兼容所有的浏览器。由于采用websocket技术而不是HLS,因此平均延迟可以做到400ms,可以和原生APP的方案媲美。如果是基于webrtc的方案,是无法兼容在苹果手机上的浏览器,包括微信的,即使在安卓平台上,也只有低于30%的浏览器支持webrtc,微信对webrtc也有诸多限制。因此,基于webrtc的网页版方案不是真正的H5方案,普适性保证不了。即构的H5方案能真正做到全面兼容,这一点是真正能解决客户痛点的。


LiveVideoStack:咱们视频画面和娃娃机信令延迟大概在怎样的水平?如何去保证这种超低延迟的?


冼牛:目前即构科技视频画面和信令的延迟大约是300ms,实测最低可以达到100ms。即构科技这种超低延迟的技术其实在直播时代已经被映客、花椒和一直播等产品验证过的了。即构科技采用了基于UDP的私有协议,这样可以对整个传输链条有更好的把控,即使在弱网环境下依然可以获得超低的延迟,对传输的媒体数据采取信道编码和信源编码双重保障,信源编码能让媒体数据占用更少带宽,信道编码可以让媒体数据传输更加可靠和低延迟。另外,在传输环节中,即构采取优质的传输资源,配合智能选路和动态回源等方法。综合这些技术来获得超低的延迟。


LiveVideoStack:我也体验了一把在线抓娃娃,确实很有意思,不过通过画面传输看到的其实更像是2D的,玩家对于远近的把控并没有很清晰,不知是否有什么手段去优化?


冼牛:2D的画面并非制约用户判断远近纵深的关键因素,其实清晰度和视角广度也会影响用户的体验,这个问题可以通过采用较好的广角摄像头来解决,能让摄像头的拍摄距离更近,清晰度更高,配合推流的码流调整到720P;视角可以增加侧面的摄像头和俯视的摄像头,这样用户可以从正面、俯视和侧面三个角度来看娃娃,能补偿2D画面没有纵深的不足。


LiveVideoStack:后面的产品是否会衍生出更多好玩的形式?比如房间版聊,连麦等等。


冼牛:过去两个月,在线抓娃娃行业从火爆进入竞争白热化。差异化竞争和玩法的升级是必然的。我认为在线抓娃娃的爆发不是平白无故的,它是一个流量变现的生意,是直播和物联网发展积累到一定程度的突破。那么直播和社交等领域的一些被验证过的玩法,比如说主播抓娃娃和主播和观众连麦一起抓抓娃娃等精细化运营的创新,都是有可能的。即构科技在直播行业被验证过的直播SDK和在线抓娃娃一站式解决方案能在技术和玩法上支撑在线抓娃娃行业做很多创新。


LiveVideoStack:在成都沙龙会有怎样的大招提前和我们爆料?


冼牛:即构科技下个月初会向市场宣布在线抓娃娃方案的一个大招,还在保密阶段。即构科技的讲师吴彪老师将会在LiveVideoStack Meet的演讲中解析在线抓娃娃方案这一大招的技术细节,敬请期待。


QR code