直播软件源码如何在Android端实现多人视频通话
本篇主要讨论直播软件源码如何在Android端实现多人视频通话。主要需要实现以下功能:
随着加入人数和他们的手机摄像头分辨率的变化,显示不同的UI,即所谓的“分屏”
点击分屏中的小窗,可以放大显示该聊天窗
分屏
根据前期技术调研,分屏显示最好的方式是采用瀑布流结合动态聊天窗实现,这样比较方便的能够适应UI的变化。所谓瀑布流,就是目前比较流行的一种列表布局,会在界面上呈现参差不齐的多栏布局。我们先实现一个瀑布流:
瀑布流的实现方式很多,本文采用结合 GridLayoutManager的RecyclerView 来实现。我们首先自定义一个 RecyclerView,命名为 GridVideoViewContainer。核心代码如下:
int count = uids.size();
if (count <= 2) { // 只有本地视频或聊天室内只有另外一个人this.setLayoutManager(new LinearLayoutManager(activity.getApplicationContext(), orientation, false));
} else if (count > 2) {// 多人聊天室int itemSpanCount = getNearestSqrt(count);this.setLayoutManager(new GridLayoutManager(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 = new DisplayMetrics();windowManager.getDefaultDisplay().getMetrics(outMetrics);int count = uids.size();int DividerX = 1;int DividerY = 1;if (count == 2) {DividerY = 2;} else if (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 即可,就像这样:
private final HashMap<Integer, SurfaceView> mUidsList = new HashMap<>();
复制代码
每当有人加入了我们的聊天频道,都会触发onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed)
方法,第一个 uid 就是他们的 UID;接下来我们要为每个 item 新建一个 SurfaceView 并为其创建渲染视图,最后将它们加入刚才创建好的mUidsList里并调用setupRemoteVideo( VideoCanvas remote )
方法播放这个聊天视频。这个过程的完整代码如下:
@Override
public void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) {doRenderRemoteUi(uid);
}private void doRenderRemoteUi(final int uid) {runOnUiThread(new Runnable() {@Overridepublic void 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(new VideoCanvas(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, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
复制代码
一般 Android 工程师看见 holderView 就明白这是 ViewHolder 的 layout 的根 layout 了,而 user 是哪儿来的,详见文末的代码,文中不做赘述。
这样在多人聊天的时候我们就能使用分屏的方式播放用户聊天视频了,如果想放大某一个用户的视频该怎么办呢?
全屏和小窗
当用户双击某一个 item 的时候,他希望对应的视频能够全屏显示,而其他的视频则变成小窗口,那么我们先定义一个双击事件接口:
public interface VideoViewEventListener {void onItemDoubleClick(View v, Object item);
}
具体实现方式如下:
mGridVideoViewContainer.setItemEventHandler(new VideoViewEventListener() {@Overridepublic void 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();}}
});
复制代码
将被选中的视频全屏播放的方法很容易理解,我们只看生成小窗列表的方法:
private void switchToSmallVideoView(int bigBgUid) {HashMap<Integer, SurfaceView> slice = new HashMap<>(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将其全屏播放。
直播软件源码如何在Android端实现多人视频通话相关推荐
- Android分屏直播方案,直播软件源码如何在Android端实现多人视频通话
本篇主要讨论直播软件源码如何在Android端实现多人视频通话.主要需要实现以下功能: 随着加入人数和他们的手机摄像头分辨率的变化,显示不同的UI,即所谓的"分屏" 点击分屏中的小 ...
- 直播软件源码(有服务端+android端+ios端)
这是我们公司的一个项目:直播软件,是给其他公司开发的,外包项目.现在公司不做了,就想着把这个项目源码分享给爱技术的人. 主播端: 把主播实时录制的视频,经过(采集.美颜处理.编码)推送到服务器 服务器 ...
- 直播软件源码开发,实现小程序直播的功能
为了方便用户观看,有些直播软件源码支持小程序端观看,那么小程序的直播软件源码功能如何实现呢,就让我们一起看一下吧. 1.添加直播组件 以mpvue为例 //app.json "plugins ...
- 一对一直播软件源码开发,直播相关技术详解
一对一直播软件源码中直播流程图 一.数据采集 通过一对一直播软件源码移动设备的端麦克风摄像头采集音视频数据. 视频采集 AVCaptureDevice 前后摄像头作为视频源生成输入 AVCapture ...
- 实现简单的直播互动功能,直播软件源码是如何做的
近两年,很多电商平台开始关注起直播互动电商,希望在直播中,也可以增加互动,例如在直播过程中,抛出限量优惠商品,实时发送抢购的消息给观众.于是我们做了一个简单的直播软件源码Demo. Demo大致的整体 ...
- 直播软件源码,实现一个简单的直播功能
概述 一直好奇直播软件源码这个东东是如何实现的,譬如音视频流是如何采集的? 音视频流是如何推送到订阅方 ? 如何支撑上万级.百万级用户同时观看直播 ? 功能设计 如上图所示为直播软件源码 Demo 实 ...
- 直播软件源码开发,直播间内消息系统的实现
在直播软件源码开发过程中,消息系统是非常关键的,无论是直播间内的消息还是平台内的消息,都关系着用户的使用体验,所以今天我们先用一个简单的"拉"模型搭建一个简单的直播间消息系统. 基 ...
- 一对一直播软件源码开发,一对一直播怎么开发
直播行业自2016年的"千播大战",时至今日,直播平台内容同质化现象严重,反倒是一对一直播软件如"雨后春笋"般进入了人们的视野.一对一直播软件源码开发核心业务模 ...
- 直播系统源码App中Android酷炫礼物动画直播平台源码搭建教程(上篇)
直播系统源码App中Android酷炫礼物动画直播平台源码搭建教程(上篇) 在当下移动直播火爆的年代,如果你曾经使用过移动端直播应用,相信会被里面那令人惊叹的礼物动画效果迷住,比如像下面这样的效果. ...
最新文章
- 未来的程序员面临着怎样的职业变化
- 请还互联网产业一个朗朗乾坤
- 创建Okhttp自定义Log
- fsocketopen域名解析错误
- 一个成功敏捷团队的失败历程
- SQL server 2008 数据库企业版安装教程图解 (转载)
- MongoDB Hot Backup 测试及痛点
- 从零开始Bootstrap3
- Cloud一分钟 | 谷歌投资的AR云平台开发商Blue Vision Labs,将由美版滴滴Lyft收购
- SilverLight Test
- java--面对对象之final关键字和static关键字
- 6.5使用外部环境的属性文件
- 根据城市编码提取出省份名和城市名
- Win10删除需要trustedinstaller权限的文件的方法
- 基于Xilinx的FPGA下载配置详解及几种电路参考设计
- Java-Mail Java程序发送Email
- Excel函数应用之查询与引用函数
- Tap4fun杨祥吉:手游大佬最怕员工知道的那些事儿
- java获取首字母_Java 获取中文首字母的方法
- NVT | 67x USB MSDC设备分析
热门文章
- 大咖丨昆仑数据陈晨:工业大数据真正要做的是智能分析和智能决策
- unsupported GNU version! gcc versions later than 6 are not supported!
- 突发奇想nico爬虫
- 005_wz_bbk_-v详细信息,-c生成中间文件,链接次序
- [python]用request库来处理Http协议-收集北航表白墙内的数据
- docxtpl使用手册
- 无线网卡(RTL8188EU)驱动编译、使用DHCP配置无线网络(1
- 最强的志愿军战俘:炸掉一飞机美军后逃离
- 用python爬取qq空间内容_用python爬取QQ空间
- 简述什么是图灵机_图灵机的工作原理是什么