摘要:我們先實(shí)現(xiàn)一個(gè)瀑布流瀑布流的實(shí)現(xiàn)方式很多,本文采用結(jié)合的來(lái)實(shí)現(xiàn)。有了一個(gè)可用的瀑布流之后,下面我們就可以實(shí)現(xiàn)動(dòng)態(tài)聊天窗了動(dòng)態(tài)聊天窗的要點(diǎn)在于的大小由視頻的寬高比決定,因此及其對(duì)應(yīng)的就該注意不要寫死尺寸。
作者:聲網(wǎng)用戶,資深A(yù)ndroid工程師吳東洋
本系列文章分享了基于Agora SDK 2.1實(shí)現(xiàn)多人視頻通話的實(shí)踐經(jīng)驗(yàn)。
在上一篇《Android 多人視頻聊天應(yīng)用的開(kāi)發(fā)(一)一對(duì)一聊天》中我們學(xué)習(xí)了如何使用聲網(wǎng)Agora SDK 進(jìn)行一對(duì)一的聊天,本篇主要討論如何使用 Agora SDK 進(jìn)行多人聊天。主要需要實(shí)現(xiàn)以下功能:
上一篇已經(jīng)實(shí)現(xiàn)過(guò)的聊天功能
隨著加入人數(shù)和他們的手機(jī)攝像頭分辨率的變化,顯示不同的UI,即所謂的“分屏”
點(diǎn)擊分屏中的小窗,可以放大顯示該聊天窗
分屏根據(jù)前期技術(shù)調(diào)研,分屏顯示最好的方式是采用瀑布流結(jié)合動(dòng)態(tài)聊天窗實(shí)現(xiàn),這樣比較方便的能夠適應(yīng)UI的變化。所謂瀑布流,就是目前比較流行的一種列表布局,會(huì)在界面上呈現(xiàn)參差不齊的多欄布局。我們先實(shí)現(xiàn)一個(gè)瀑布流:
瀑布流的實(shí)現(xiàn)方式很多,本文采用結(jié)合 GridLayoutManager的RecyclerView 來(lái)實(shí)現(xiàn)。我們首先自定義一個(gè) RecyclerView,命名為 GridVideoViewContainer。核心代碼如下:
int count = uids.size(); if (count <= 2) { // 只有本地視頻或聊天室內(nèi)只有另外一個(gè)人 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)); }
根據(jù)上面的代碼可以看出,在聊天室里只有自己的本地視頻或者只有另外一個(gè)人的時(shí)候,采用 LinearLayoutManager,這樣的布局其實(shí)與前文的一對(duì)一聊天類似;而在真正意義的多人聊天室里,則采用 GridLayoutManager 實(shí)現(xiàn)瀑布流,其中 itemSpanCount 就是瀑布流的列數(shù)。
有了一個(gè)可用的瀑布流之后,下面我們就可以實(shí)現(xiàn)動(dòng)態(tài)聊天窗了:
動(dòng)態(tài)聊天窗的要點(diǎn)在于 item 的大小由視頻的寬高比決定,因此 Adapter 及其對(duì)應(yīng)的 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; } }
以上代碼根據(jù)視頻的數(shù)量確定了列數(shù)和行數(shù),然后根據(jù)列數(shù)和屏幕寬度確定了視頻的寬度,接著根據(jù)視頻的寬高比和視頻寬度確定了視頻高度。同時(shí)也考慮了手機(jī)的橫豎屏情況(就是if (width > height)這行代碼)。
該 Adapter 對(duì)應(yīng)的 layout 的代碼如下:
我們可以看到,layout 中有關(guān)尺寸的屬性都 是wrap_content,這就使得 item 大小隨視頻寬高比變化成為可能。
把分屏的布局寫好之后,我們就可以在每一個(gè) item 上播放聊天視頻了。
播放聊天視頻在 Agora SDK 中一個(gè)遠(yuǎn)程視頻的顯示只和該用戶的 UID 有關(guān),所以使用的數(shù)據(jù)源只需要簡(jiǎn)單定義為包含 UID 和對(duì)應(yīng)的 SurfaceView 即可,就像這樣:
private final HashMapmUidsList = new HashMap<>();
每當(dāng)有人加入了我們的聊天頻道,都會(huì)觸發(fā)onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed)方法,第一個(gè) uid 就是他們的 UID;接下來(lái)我們要為每個(gè) item 新建一個(gè) SurfaceView 并為其創(chuàng)建渲染視圖,最后將它們加入剛才創(chuàng)建好的mUidsList里并調(diào)用setupRemoteVideo( VideoCanvas remote )方法播放這個(gè)聊天視頻。這個(gè)過(guò)程的完整代碼如下:
@Override public void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) { doRenderRemoteUi(uid); } private void doRenderRemoteUi(final int uid) { runOnUiThread(new Runnable() { @Override public 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); } } }); }
以上代碼與前文中播放一對(duì)一視頻的代碼如出一撤,但是細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn)我們并沒(méi)有將生成的 SurfaceView 放在界面里,這正是與一對(duì)一視頻的不同之處:我們要在一個(gè)抽象的 VideoViewAdapter 類里將 SurfaceView 放出來(lái),關(guān)鍵代碼如下:
SurfaceView target = user.mView; VideoViewAdapterUtil.stripView(target); holderView.addView(target, 0, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
一般 Android 工程師看見(jiàn) holderView 就明白這是 ViewHolder 的 layout 的根 layout 了,而 user 是哪兒來(lái)的,詳見(jiàn)文末的代碼,文中不做贅述。
這樣在多人聊天的時(shí)候我們就能使用分屏的方式播放用戶聊天視頻了,如果想放大某一個(gè)用戶的視頻該怎么辦呢?
全屏和小窗當(dāng)用戶雙擊某一個(gè) item 的時(shí)候,他希望對(duì)應(yīng)的視頻能夠全屏顯示,而其他的視頻則變成小窗口,那么我們先定義一個(gè)雙擊事件接口:
public interface VideoViewEventListener { void onItemDoubleClick(View v, Object item); } 具體實(shí)現(xiàn)方式如下: mGridVideoViewContainer.setItemEventHandler(new VideoViewEventListener() { @Override public 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) { HashMapslice = new HashMap<>(1); slice.put(bigBgUid, mUidsList.get(bigBgUid)); Iterator 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()); }
小窗列表要注意移除全屏的那個(gè) UID,此外一切都和正常瀑布流視圖相同,包括雙擊小窗的item將其全屏播放。
到了這里我們就已經(jīng)使用 Agora SDK 完成了一個(gè)有基本功能的簡(jiǎn)單多人聊天 demo,要產(chǎn)品化還有很多的東西要做,在這里先做一個(gè)簡(jiǎn)單的總結(jié)吧!
總結(jié)聲網(wǎng)Agora 提供了高質(zhì)量的視頻通信 SDK,不僅覆蓋了主流的操作系統(tǒng),集成效率也比較高,而且還支持包括聊天,會(huì)議,直播等功能在內(nèi)的多個(gè)模式的視頻通話。SDK 中 API 設(shè)計(jì)基本能夠滿足大部分的開(kāi)發(fā)需要,而且隱藏了底層開(kāi)發(fā),只需要提供 SurfaceView 和 UID 即可播放視頻,這樣對(duì)于 App 層的開(kāi)發(fā)者來(lái)說(shuō)十分友好。非常適合有視頻聊天開(kāi)發(fā)需求的開(kāi)發(fā)者。在視頻領(lǐng)域創(chuàng)業(yè)大爆發(fā)的今天,建議更多的想要從事該領(lǐng)域的開(kāi)發(fā)者可以嘗試下。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/69135.html
作者:聲網(wǎng)Agora用戶,資深A(yù)ndroid開(kāi)發(fā)者吳東洋。本系列文章分享了基于Agora SDK 2.1實(shí)現(xiàn)多人視頻通話的實(shí)踐經(jīng)驗(yàn)。 自從2016年,鼓吹互聯(lián)網(wǎng)寒冬的論調(diào)甚囂塵上,2017年亦有愈演愈烈之勢(shì)。但連麥直播、在線抓娃娃、直播問(wèn)答、遠(yuǎn)程狼人殺等類型的項(xiàng)目卻異軍突起,成了投資人的風(fēng)口,創(chuàng)業(yè)者的藍(lán)海和用戶的必裝App,而這些方向的項(xiàng)目都有一個(gè)共同的特點(diǎn)——都依賴視頻通話和全互動(dòng)直播技術(shù)。 聲...
摘要:上述就是本文根據(jù)騰訊云官方文檔指引,以為例總結(jié)的從零搭建視頻通話服務(wù)的主要步驟,如有遇到其他問(wèn)題,歡迎留言。 歡迎大家前往騰訊云+社區(qū),獲取更多騰訊海量技術(shù)實(shí)踐干貨哦~ 本文由騰訊云視頻發(fā)表于云+社區(qū)專欄 showImg(https://segmentfault.com/img/remote/1460000016553455?w=1081&h=755); 本文作者,ericxwli,...
閱讀 1774·2021-10-11 10:57
閱讀 2363·2021-10-08 10:14
閱讀 3401·2019-08-29 17:26
閱讀 3358·2019-08-28 17:54
閱讀 3031·2019-08-26 13:38
閱讀 2906·2019-08-26 12:19
閱讀 3616·2019-08-23 18:05
閱讀 1284·2019-08-23 17:04