报名参加CloudWeGo黑客松,奖金直推双丰收! 了解详情
写点什么

基于 Agora SDK 实现 Android 端的多人视频互动

  • 2020-02-26
  • 本文字数:3910 字

    阅读完需:约 13 分钟

基于 Agora SDK 实现 Android 端的多人视频互动

本系列教程将分为三期,分享基于 Agora SDK 在各系统平台应用中实现一对一视频通话、多人互动直播,以及结合跨平台技术进行开发。本期推送在 Android、iOS、Windows、Web、macOS 上实现多人视频互动直播。


本篇基于上一篇「Android 端一对一视频通话」教程,讲分享如何使用 Agora SDK 开发多人互动直播。

分屏

本文是采用瀑布流结合动态聊天窗实现分屏显示,这样比较方便的能够适应 UI 的变化。所谓瀑布流,就是目前比较流行的一种列表布局,会在界面上呈现参差不齐的多栏布局。我们先实现一个瀑布流:


瀑布流的实现方式很多,本文采用结合 GridLayoutManager 的 RecyclerView 来实现。我们首先自定义一个 RecyclerView,命名为 GridVideoViewContainer。核心代码如下:


int count = uids.size();if(count <= 2) {// 只有本地视频或聊天室内只有另外一个人this.setLayoutManager(newLinearLayoutManager(activity.getApplicationContext(), orientation, false));} elseif(count > 2) {// 多人聊天室int itemSpanCount = getNearestSqrt(count);this.setLayoutManager(newGridLayoutManager(activity.getApplicationContext(), itemSpanCount, orientation, false));}
复制代码


根据上面的代码可以看出,在聊天室里只有自己的本地视频或者只有另外一个人的时候,采用 LinearLayoutManager,这样的布局其实与前文的一对一聊天类似;而在真正意义的多人聊天室里,则采用 GridLayoutManager 实现瀑布流,其中 itemSpanCount 就是瀑布流的列数。


有了一个可用的瀑布流之后,下面我们就可以实现动态聊天窗了:动态聊天窗的要点在于 item 的大小由视频的宽高比决定,因此 Adapter 及其对应的 layout 就该注意不要写死尺寸。在 Adapter 里控制 item 具体尺寸的代码如下:



if(force || mItemWidth == 0|| mItemHeight == 0) {WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics outMetrics = newDisplayMetrics(); windowManager.getDefaultDisplay().getMetrics(outMetrics);
int count = uids.size();intDividerX= 1;intDividerY= 1;
if(count == 2) {DividerY= 2;} elseif(count >= 3) {DividerX= getNearestSqrt(count);DividerY= (int) Math.ceil(count * 1.f/ DividerX);}
int width = outMetrics.widthPixels;int height = outMetrics.heightPixels;
if(width > height) { mItemWidth = width / DividerY; mItemHeight = height / DividerX;} else{ mItemWidth = width / DividerX; mItemHeight = height / DividerY;}}
复制代码


以上代码根据视频的数量确定了列数和行数,然后根据列数和屏幕宽度确定了视频的宽度,接着根据视频的宽高比和视频宽度确定了视频高度。同时也考虑了手机的横竖屏情况(就是 if (width > height)这行代码)。


该 Adapter 对应的 layout 的代码如下:


<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/user_control_mask"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">
<ImageViewandroid:id="@+id/default_avatar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:visibility="gone"android:src="@drawable/icon_default_avatar"android:contentDescription="DEFAULT_AVATAR"/>
<ImageViewandroid:id="@+id/indicator"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_alignParentBottom="true"android:layout_marginBottom="@dimen/video_indicator_bottom_margin"android:contentDescription="VIDEO_INDICATOR"/>
<LinearLayoutandroid:id="@+id/video_info_container"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentTop="true"android:layout_marginTop="24dp"android:layout_marginStart="15dp"android:layout_marginLeft="15dp"android:visibility="gone"android:orientation="vertical">
<TextViewandroid:id="@+id/video_info_metadata"android:layout_width="wrap_content"android:layout_height="wrap_content"android:singleLine="true"style="@style/NotificationUIText"/></LinearLayout>
</RelativeLayout>
复制代码


我们可以看到,layout 中有关尺寸的属性都 是 wrap_content,这就使得 item 大小随视频宽高比变化成为可能。


把分屏的布局写好之后,我们就可以在每一个 item 上播放聊天视频了。

播放聊天视频

在 Agora SDK 中一个远程视频的显示只和该用户的 UID 有关,所以使用的数据源只需要简单定义为包含 UID 和对应的 SurfaceView 即可,就像这样:


privatefinalHashMap<Integer, SurfaceView> mUidsList = newHashMap<>();
复制代码


每当有人加入了我们的聊天频道,都会触发 onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed)方法,第一个 uid 就是他们的 UID;接下来我们要为每个 item 新建一个 SurfaceView 并为其创建渲染视图,最后将它们加入刚才创建好的 mUidsList 里并调用 setupRemoteVideo( VideoCanvas remote )方法播放这个聊天视频。这个过程的完整代码如下:


@Overridepublicvoid onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) {   doRenderRemoteUi(uid);}
privatevoid doRenderRemoteUi(finalint uid) { runOnUiThread(newRunnable() {@Overridepublicvoid run() {if(isFinishing()) {return;}
if(mUidsList.containsKey(uid)) {return;}
SurfaceView surfaceV = RtcEngine.CreateRendererView(getApplicationContext()); mUidsList.put(uid, surfaceV);
boolean useDefaultLayout = mLayoutType == LAYOUT_TYPE_DEFAULT;
surfaceV.setZOrderOnTop(true); surfaceV.setZOrderMediaOverlay(true);
rtcEngine().setupRemoteVideo(newVideoCanvas(surfaceV, VideoCanvas.RENDER_MODE_HIDDEN, uid));
if(useDefaultLayout) { log.debug("doRenderRemoteUi LAYOUT_TYPE_DEFAULT "+ (uid & 0xFFFFFFFFL)); switchToDefaultVideoView();} else{int bigBgUid = mSmallVideoViewAdapter == null? uid : mSmallVideoViewAdapter.getExceptedUid(); log.debug("doRenderRemoteUi LAYOUT_TYPE_SMALL "+ (uid & 0xFFFFFFFFL) + " "+ (bigBgUid & 0xFFFFFFFFL)); switchToSmallVideoView(bigBgUid);}}});}
复制代码


以上代码与前文中播放一对一视频的代码如出一撤,但是细心的读者可能已经发现我们并没有将生成的 SurfaceView 放在界面里,这正是与一对一视频的不同之处:我们要在一个抽象的 VideoViewAdapter 类里将 SurfaceView 放出来,关键代码如下:


SurfaceView target = user.mView;VideoViewAdapterUtil.stripView(target);holderView.addView(target, 0, newFrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
复制代码


一般 Android 工程师看见 holderView 就明白这是 ViewHolder 的 layout 的根 layout 了,而 user 是哪儿来的,详见文末的代码,文中不做赘述。


这样在多人聊天的时候我们就能使用分屏的方式播放用户聊天视频了,如果想放大某一个用户的视频该怎么办呢?

全屏和小窗

当用户双击某一个 item 的时候,他希望对应的视频能够全屏显示,而其他的视频则变成小窗口,那么我们先定义一个双击事件接口:


publicinterfaceVideoViewEventListener{void onItemDoubleClick(View v, Object item);}
复制代码


具体实现方式如下:


mGridVideoViewContainer.setItemEventHandler(newVideoViewEventListener() {@Overridepublicvoid onItemDoubleClick(View v, Object item) {        log.debug("onItemDoubleClick "+ v + " "+ item + " "+ mLayoutType);
if(mUidsList.size() < 2) {return;}
UserStatusData user = (UserStatusData) item;int uid = (user.mUid == 0) ? config().mUid : user.mUid;
if(mLayoutType == LAYOUT_TYPE_DEFAULT && mUidsList.size() != 1) { switchToSmallVideoView(uid);} else{ switchToDefaultVideoView();}}});
复制代码


将被选中的视频全屏播放的方法很容易理解,我们只看生成小窗列表的方法:


privatevoid switchToSmallVideoView(int bigBgUid) {HashMap<Integer, SurfaceView> slice = newHashMap<>(1);    slice.put(bigBgUid, mUidsList.get(bigBgUid));Iterator<SurfaceView> iterator = mUidsList.values().iterator();while(iterator.hasNext()) {SurfaceView s = iterator.next();        s.setZOrderOnTop(true);        s.setZOrderMediaOverlay(true);}
mUidsList.get(bigBgUid).setZOrderOnTop(false); mUidsList.get(bigBgUid).setZOrderMediaOverlay(false);
mGridVideoViewContainer.initViewContainer(this, bigBgUid, slice, mIsLandscape);
bindToSmallVideoView(bigBgUid);
mLayoutType = LAYOUT_TYPE_SMALL;
requestRemoteStreamType(mUidsList.size());}
复制代码


小窗列表要注意移除全屏的那个 UID,此外一切都和正常瀑布流视图相同,包括双击小窗的 item 将其全屏播放。


到了这里我们就已经使用 Agora SDK 完成了一个有基本功能的简单多人聊天 demo。


本文转载自 声网 Agora 公众号。


原文链接:https://mp.weixin.qq.com/s/lO5pf71goWTRFhm8mo3jKw


2020-02-26 15:49737

评论

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

Vue组件入门(三)插槽和动态组件

Augus

Vue 3 10月月更

活动预告 | AI for Good 2022 峰会

第四范式开发者社区

机器学习 数据库 开源 时序数据库 特征

浅谈云安全和传统安全

HummerCloud

云计算 云原生 云安全

我决定以后一直使用Eclipse了 因为它也可以安装集成PyDec插件来实现Python开发了 哇哈哈哈

Geek_yx5md7

Python 开发环境搭建 eclipse+pyhton Python编译器 新手学编程

2022 Java零基础必备 简单易学 Eclipse免费下载安装+JDK环境搭建一站式捆绑服务到底的教程 足够全、足够详细、足够劲爆 最关键可以一直让你免费使用!

Geek_yx5md7

eclipse 工具 开发工具 环境搭建 Java 开发

【转载】Byzer + OpenMLDB 实现端到端的,基于实时特征计算的机器学习流程

第四范式开发者社区

人工智能 机器学习 数据库 开源 特征

Vector、ArrayList、LinkedList的区别

zarmnosaj

10月月更

Baklib知识分享|制作网站FAQ需要注意哪些内容?

Baklib

Vue-监听使用方法和过滤器

默默的成长

Vue 前端 10月月更

Go 语言泛型编程之切片

宇宙之一粟

Go 泛型 泛型编程 10月月更

活动预告 | Feature Store Summit 2022

第四范式开发者社区

机器学习 数据库 开源 时序数据库 特征

【Go】gitlab 内部 Go 组件 Module 私有化

非晓为骁

go语言 包管理 go module go mod

企业级低代码中“自动化工作流”的5大优势!

优秀

自动化 工作流 企业级低代码

澳鹏Appen田小鹏博士:以高质量数据赋能AI驱动的元宇宙时代

澳鹏Appen

人工智能 AR/VR 训练数据 元宇宙 数据训练

数字经济时代,企业应该如何搭建数字体验平台(DXP)?

Baklib

客户体验管理

DevOps 与 DataOps 相关吗?

雨果

DataOps

同行四载,法大大携手SAP共筑数智未来

ToB行业头条

开源机器学习数据库 OpenMLDB:线上线下一致的生产级特征平台

第四范式开发者社区

人工智能 机器学习 数据库 特征 开源、

五面腾讯,六h灵魂拷问,终拿下 58W offer

程序知音

Java 腾讯 java面试 后端技术 Java面试八股文

Github星标57.9K!阿里巴巴Java面试突击汇总(全彩版)首次公开

程序员小毕

程序员 程序人生 JVM 多线程 Java 面试

设备端零改动迁移实战——实践类

阿里云AIoT

安全 物联网 开发工具 迁移 企业实例

多图详解kafka生产者消息发送过程

石臻臻的杂货铺

Kafk Kafka实战 10月月更

连接团队知识孤岛,优化团队工作流程

Baklib

知识管理 知识库

记录第三天-Vue组件

默默的成长

Vue 前端 10月月更

重磅 | 九科加入麒麟生态图谱,携手共建自主可控信创生态

九科Ninetech

VUE 组件的计算属性

默默的成长

Vue 前端 10月月更

又一家!天数智芯天垓100 产品卡与龙蜥操作系统完成产品兼容互认证

OpenAnolis小助手

开源 适配 龙蜥操作系统 天数智芯 兼容

一颗石榴给QA带来的启示

BY林子

软件测试 敏捷测试 测试工程师 敏捷QA

成功实践丨基于昇腾,安擎助力天津银行开启加速度

科技热闻

六个方法教你如何搭建产品的在线帮助中心

Baklib

产品 帮助中心

Baklib知识分享|企业办公必备利器,在线协作文

Baklib

在线协作文档

基于 Agora SDK 实现 Android 端的多人视频互动_行业深度_声网_InfoQ精选文章