写点什么

做一个电商直播 App,跟上这波双十一

  • 2019-11-29
  • 本文字数:3974 字

    阅读完需:约 13 分钟

做一个电商直播App,跟上这波双十一

又快到一年一度的双十一了。淘宝直播一姐曾在去年双十一,一个人卖出了 3.3 亿的销售额,创造了行业的销售神话。最近淘宝直播一哥李佳琦,也成为了热门话题。近两年,很多电商平台开始关注起直播互动电商。在直播中,也可以增加互动,例如在直播过程中,抛出限量优惠商品,以题目的形式实时发送抢购的消息给观众。于是我们做了一个简单的 Demo,效果如视频所示。


为了让主播可以快速输入,我们还通过合作伙伴搜狗的技术,为应用增加了语音识别与转写功能。主播通过语音输入题目,能更方便地发送给观众。观众收到题目后,屏幕会显示弹框。

1.1 功能拆解

  • 需支持互动直播功能,且直播音视频与文字消息、题目可以同步到达观众端。

  • 需要支持文字消息、题目消息的发送,并能稳定支持海量高并发。

  • 需要语音识别功能,语音识别结果需要主播确认后通过消息系统发送。

  • 观众收到题目信息时需要主动弹窗,给用户选择结果。

1.2 实现方案

在这个场景下,首先要保证音视频直播与互动消息可以做到同步,也就是说,当主播说出“我的天啊~买它~!”的时候,购物链接、题目等形式的消息需要与这句话同时发送到观众端。那就就需要采用基于 UDP 传输的实时音视频互动方案。


另一方面,文字、题目等消息不仅需要做到实时,还需要保证能抗住高并发。所以在这里我们采用实时消息 SDK。实时消息 SDK 在传输方面,采用分布式架构、多机房多路保活机制、智能优化路径,在其它节点失效时自动转移,选择最优节点路径传输,服务可靠。同时,弹性可伸缩架构,支持单频道百万并发,轻松应对直播答题、电商互动直播、大班课等高并发场景。同时,支持动态快速扩容,可以灵活应对用户场景的快速增长。


  • 通过声网 Agora 视频互动直播 SDK 实现直播功能。

  • 通过搜狗知音开放平台来实现语音识别功能。

  • 题目信息也是一种实时消息,就像文字消息、弹幕一样,可用实时消息通道进行传输。在这里我们统一用声网 Agora 实时消息 SDK 来实现。

2.1 实现视频直播

一个简单的视频直播 Demo 按以下几个步骤就可以实现了,可以找几个 Android 设备 run 一下看看效果。

Step1 SDK 集成

SDK 支持 maven 依赖,在 build.gradle 的 dependencies 模块中加一行就行:


dependencies {...implementation 'io.agora.rtc:full-sdk:2.8.1'}
复制代码

Step2 直播引擎创建

声⽹SDK 有个重要的类——RtcEngine,负责直播功能管理,提供了上/下线、状态监听、⾳/视频设置等 API。创建引擎时,需要⽤到 APP_ID ,⼤家可以在 console.agora.io -->项⽬管理⻚⾯下获取 。


privateRtcEngine mRtcEngine;try{mRtcEngine = RtcEngine.create(context, LiveDefine.APP_ID,mRtcEventHandler);mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION);mRtcEngine.enableAudio(); // 开启⾳频功能mRtcEngine.enableVideo(); // 开启视频功能} catch(Exception e) {e.printStackTrace();}
复制代码

Step3 直播 View 关联

在⼀个直播建中,参与者的⻆⾊有主播和观众,关联 View 时有些许区别。ANCHOR_UID 为主播⽤户 id,


主播端关联 View 时为⾃⼰的⽤户 id,观众端关联 view 时为观看的主播的⽤户 id。


SurfaceView surface = RtcEngine.CreateRendererView(this);// 主播端View关联mRtcEngine.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);mRtcEngine.enableLocalAudio(true); // 主播端需要打开本地⾳频mRtcEngine.setupLocalVideo(newVideoCanvas(surface,VideoCanvas.RENDER_MODE_HIDDEN, ANCHOR_UID)); // 主播端设置的是本地videomRtcEngine.startPreview(); //主播需要开启视频预览// 观众端View关联mRtcEngine.setClientRole(Constants.CLIENT_ROLE_AUDIENCE);mRtcEngine.enableLocalAudio(false); // 观众端不需要打开本地⾳频mRtcEngine.setupRemoteVideo(newVideoCanvas(surface,VideoCanvas.RENDER_MODE_HIDDEN, ANCHOR_UID)); //观众端设置的是远端即主播video
复制代码

Step4 加⼊房间

加⼊房间时,参数 token 为当前登录账户对应的 token,应⽤⾃⼰管理,测试时可以传 null。第⼆个参数为频道 id,也是由应⽤⾃⼰管理的。第三个参数为频道名称。最后⼀个参数为当前登录的账户 id。


mRtcEngine.joinChannel("", CHANNEL_ID, "CHANNEL_NAME", uid);
复制代码

Step5 离开房间

// 主播端离开mRtcEngine.setupLocalVideo(null);mRtcEngine.stopPreview();mRtcEngine.leaveChannel();// 观众端离开mRtcEngine.setupRemoteVideo(null);mRtcEngine.leaveChannel();
复制代码

2.2 消息功能

直播房间消息功能可以说是相对基础而简单的了,我们选用的是声网实时信息 SDK,这是一个独立的工具类 SDK,声网将实时消息功能解耦出来,可以给各个场景提供消息支持。群聊实时消息可参考如下步骤:

Step1 依赖配置

dependencies {...implementation 'io.agora.rtm:rtm-sdk:1.0.1'}
复制代码

Step2 消息引擎创建

// APP_ID同视频互动SDK保持⼀致即可privateRtmClient mRtmClient;mRtmClient = RtmClient.createInstance(context, LiveDefine.APP_ID, listener);
复制代码

Step3 房间消息初始化

创建⼀个消息频道前需要调⼀次登录操作,第⼀个参数为应⽤账户 token,第⼆个参数为账户标识。


mRtmClient.login("", userId,newResultCallback<Void>() {@Overridepublicvoid onSuccess(Void aVoid) {Log.d(TAG, "rtmClient login success");}@Overridepublicvoid onFailure(ErrorInfo errorInfo) {Log.d(TAG, "rtmClient login fail : "+ errorInfo);}});
复制代码


创建消息频道,CHANNEL_ID 是⼀个标识,可以和直播频道不⼀致,但是建议保持⼀致:

Step4 发送消息

RtmMessage rtmMessage = mRtmClient.createMessage();rtmMessage.setText(msg);mRtmChannel.sendMessage(rtmMessage, callback);
复制代码

Step5 退出消息频道

可在退出直播房间时,调⽤该⽅法。


mRtmChannel.release();
复制代码

2.3 语⾳识别

⾸先也是需要注册账户并创建应⽤,详⻅搜狗知⾳⽂档中⼼,实现可参考如下步骤:

Step1 初始化

调⽤init⽅法初始化


// 以下信息从知⾳平台申请获得privatestaticfinalString BASE_URL = "api.zhiyin.sogou.com";privatestaticfinalString APP_ID = "";privatestaticfinalString APP_KEY = "";privateSogoSpeech mSogouSpeech;privateDefaultAudioSource mAudioSource;privateOnSogouAsrListener mListener;publicvoid init(Context context) {ZhiyinInitInfo.Builder builder = newZhiyinInitInfo.Builder();ZhiyinInitInfo initInfo = builder.setBaseUrl(BASE_URL).setUuid(UUID).setAppid(APP_ISogoSpeech.initZhiyinInfo(context, initInfo);SogoSpeechSettings settings = SogoSpeechSettings.shareInstance();settings.setProperty(SpeechConstants.Parameter.ASR_ONLINE_AUDIO_CODING_INT,1);settings.setProperty(SpeechConstants.Parameter.ASR_ONLINE_VAD_ENABLE_BOOLEAN,false);settings.setProperty(SpeechConstants.Parameter.ASR_ONLINE_VAD_LONGMODE_BOOLEAN,true); // ⻓时间ASRsettings.setProperty(Parameter.ASR_ONLINE_LANGUAGE_STRING,ASRLanguageCode.CHINESE); // 也⽀持英⽂ASR ASRLanguageCode.ENGLISmSogouSpeech = newSogoSpeech(context);mSogouSpeech.registerListener(mSpeechEventListener);mAudioSource = newDefaultAudioSource(newAudioRecordDataProviderFactory(context)mAudioSource.addAudioSourceListener(mAudioSourceListener);}privateEventListener mSpeechEventListener = newEventListener() {@Overridepublicvoid onEvent(String eventName, String param, byte[] data, int offset, int lenif(TextUtils.equals(SpeechConstants.Message.MSG_ASR_ONLINE_LAST_RESULT,eventName)) {if(null!= mListener) {mListener.onSogouAsrResult(param);}stopTransform();}}@Overridepublicvoid onError(String errorDomain, int errorCode, String errorDescription, Obje// 9002 ⽤户主动取消if(9002!= errorCode && null!= mListener) {mListener.onSogouAsrResult("");}stopTransform();}};privateIAudioSourceListener mAudioSourceListener = newIAudioSourceListener() {@Overridepublicvoid onBegin(IAudioSource iAudioSource) {Log.d(TAG, "AudioSource onBegin");mSogouSpeech.send(SpeechConstants.Command.ASR_ONLINE_START, "", null, 0, 0)}@Overridepublicvoid onNewData(IAudioSource audioSource, Object dataArray, long packIndex, lofinalshort[] data = (short[]) dataArray;mSogouSpeech.send(SpeechConstants.Command.ASR_ONLINE_RECOGIZE, "", data, (i}@Overridepublicvoid onEnd(IAudioSource audioSource, int status, Exception e, long sampleCounLog.d(TAG, "AudioSource onEnd");mSogouSpeech.send(SpeechConstants.Command.ASR_ONLINE_STOP, "", null, 0, 0);}};publicinterfaceOnSogouAsrListener{void onSogouAsrResult(String result);}
复制代码

Step2 开始语⾳识别

publicvoid startTransform(OnSogouAsrListener listener) {mListener = listener;mSogouSpeech.send(SpeechConstants.Command.ASR_ONLINE_CREATE,null, null, 0, 0);newThread(mAudioSource, "audioRecordSource").start();}
复制代码

Step3 停⽌语⾳识别

正常情况下不需要调⽤该⽅法,在 EventListener 回调中已经调⽤过该⽅法了,为了确保状态正常也可以


在退出房间时,⼿动调⽤⼀次。


publicvoid stopTransform() {mListener= null;if(null!= mAudioSource) {mAudioSource.stop();}}
复制代码


我们已经将这个 Demo 上传⾄ Github,⼤家可以直接下载使⽤。


Android 版本:https://github.com/AgoraIO-Community/Live-Shop/tree/master/Android


iOS 版本:https://github.com/AgoraIO-Community/Live-Shop/tree/master/iOS


本文转载自公众号声网 Agora(ID:shengwang-agora)。


原文链接:


https://mp.weixin.qq.com/s/I0nhZiOTNqu8_CY4X5FzSA


2019-11-29 18:22469

评论

发布
暂无评论
发现更多内容

关于K8s集群环境工作组隔离配置多集群切换的一些笔记

山河已无恙

k8s管理 K8s 多集群管理 12月月更

最新出炉!2022年度互联网大厂(Java岗)面试真题汇总

Java全栈架构师

程序员 程序人生 后端 架构师 java面试

终于被我发现了这个推特视频下载的方法!超级简单!支持苹果安卓双系统!

frank

推特视频下载

AngularJS进阶(四十)创建模块、服务

No Silver Bullet

服务 模块 AngularJS 12月月更

AngularJS进阶(四十一)AngularJS中使用Chart.js制折线图与饼图实例

No Silver Bullet

AngularJS 12月月更 Chart.js 折线图与饼图

大数据培训工作就业前景怎么样

小谷哥

打造算力新引擎!安擎上海智能制造基地正式运营

科技热闻

AngularJS进阶(三十九)基于项目实战解析ng启动加载过程

No Silver Bullet

项目实战 AngularJS 12月月更 启动加载

几种数据库jar包获取方式

华为云开发者联盟

数据库 华为云 12 月 PK 榜 jar包

喜报 | Bonree ONE 2.0荣获信通院“2022IT新治理年度明星产品”

博睿数据

可观测性 博睿数据 荣誉 ONE平台

华为云12·12直播EI专场即将开始,满足电商行业全场景搜索需求

科技怪授

华为云

架构实战模块1作业

Geek_e3a35c

Verilog的语句块

向阳逐梦

Verilog Verilog语法 Verilog语句块

Sovit3D引擎快速构建智慧变电站三维可视化系统

2D3D前端可视化开发

物联网 智慧变电站 智能变电站 数字孪生变电站 变电站可视化

AlibabaP8,耗时182天肝出来1015页分布式全栈手册

程序知音

Java 分布式 后端 java架构

直播继续!华为云Solution as Code一键高效上云,解决方案开箱即用

科技怪授

华为云

什么样的web前端培训靠谱?

小谷哥

上海靠谱的前端培训机构有没有推荐

小谷哥

云原生 AI 的资源调度和 AI 工作流引擎设计分享

Baidu AICLOUD

AI工程化 异构计算 云原生AI 百度百舸

来聊一聊 ElasticSearch 最新版的 Java 客户端

江南一点雨

Java elasticsearch springboot ES

【计算讲谈社】第十五讲|云端即时渲染:下一代互联网的算力基座?

大咖说

数字人 云游戏

一文带你快速上手云日志服务

华为云PaaS服务小智

云计算 运维 日志管理

大数据培训出来就业前景如何

小谷哥

java自学好还是培训好?

小谷哥

声网管浩森:元宇宙派对场景的最佳实践

声网

实践 元宇宙 RTE2022

场景 | 大型电商企业运营管理数字化解决方案

九科Ninetech

面了40+岁的大叔,没有录用,并不是因为年龄

产品运营心经

工作经历 面试‘ 职场发展 大龄求职

浪潮 KaiwuDB 陈磊:布局数字能源,创新助力 “双碳”

KaiwuDB

做一个电商直播App,跟上这波双十一_文化 & 方法_声网_InfoQ精选文章