国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

使用 Agora SDK 實現視頻對話應用 HouseParty-附 Android 源碼

CocoaChina / 2564人閱讀

摘要:叔想做個直播很久了,最近終于得空,做了一個視頻群聊,以饗觀眾。主界面在主界面,我們需要檢查先和權限,以適配及以上版本。但提供了相關可以直接實現前置攝像頭預覽的功能。最多支持六人同時聊天。直接繼承,根據不同的顯示模式來完成孩子的測量和布局。

叔想做個直播demo很久了,最近終于得空,做了一個視頻群聊Demo,以饗觀眾。 直播云有很多大廠在做,經老鐵介紹,Agora不錯,遂入坑。Agora提供多種模式,一個頻道可以設置一種模式。

Agora SDK集成

叔專注SDK集成幾十年,Agora SDK集成也并沒有搞什么事情,大家按照下面步驟上車就行。

1.注冊

登錄官網,注冊個人賬號,這個叔就不介紹了。

2.創建應用

注冊賬號登錄后,進入后臺,找到“添加新項目”按鈕,點擊創建新項目,創建好后就會獲取到一個App ID, 做過SDK集成的老鐵們都知道這是干啥用的。

3.下載SDK

進入官方下載界面, 這里我們選擇視頻通話 + 直播 SDK中的Android版本下載。下載后解壓之后又兩個文件夾,分別是libs和samples, libs文件夾存放的是庫文件,samples是官方Demo源碼,大叔曾說過欲練此SDK,必先跑Sample, 有興趣的同學可以跑跑。

4.集成SDK

1. 導入庫文件

將libs文件夾的下的文件導入Android Studio, 最終效果如下:

2.添加必要權限

在AndroidManifest.xml中添加如下權限




3.配置APP ID

在values文件夾下創建strings-config.xml, 配置在官網創建應用的App ID。


    6ffa586315ed49e6a8cdff064ad8a0b0
主界面(MainActivity)

在主界面,我們需要檢查先Camera和Audio權限,以適配Andriod6.0及以上版本。

private static final int PERMISSION_REQ_ID_RECORD_AUDIO = 0;
private static final int PERMISSION_REQ_ID_CAMERA = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //檢查Audio權限
    if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO)) {
        //檢查Camera權限
        checkSelfPermission(Manifest.permission.CAMERA, PERMISSION_REQ_ID_CAMERA);
    }
}

public boolean checkSelfPermission(String permission, int requestCode) {
    if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode);
        return false;
    }
    return true;
}
頻道界面 (ChannelActivity)

點擊開PA!,進入頻道選擇界面

創建頻道列表

這里使用RecyclerView創建頻道列表。

/**
 * 初始化頻道列表
 */private void initRecyclerView() {
    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    mRecyclerView.setAdapter(new ChannelAdapter(this, mockChannelList()));
}
前置攝像頭預覽

頻道界面背景為前置攝像頭預覽,這個可以使用Android SDK自己實現。但Agora SDK提供了相關API可以直接實現前置攝像頭預覽的功能。具體實現如下:

1. 初始化RtcEngineZ

RtcEngine是Agora SDK的核心類,叔用一個管理類AgoraManager進行了簡單的封裝,提供操作RtcEngine的核心功能。

初始化如下:

/**
 * 初始化RtcEngine
 */
public void init(Context context) {
    //創建RtcEngine對象, mRtcEventHandler為RtcEngine的回調
    mRtcEngine = RtcEngine.create(context, context.getString(R.string.private_app_id), mRtcEventHandler);
    //開啟視頻功能
    mRtcEngine.enableVideo();
    //視頻配置,設置為360P
    mRtcEngine.setVideoProfile(Constants.VIDEO_PROFILE_360P, false);
    mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_COMMUNICATION);//設置為通信模式(默認)
    //mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);設置為直播模式
    //mRtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_GAME);設置為游戲模式
}


/**
 * 在Application類中初始化RtcEngine,注意在AndroidManifest.xml中配置下Application
 */
public class LaoTieApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        AgoraManager.getInstance().init(getApplicationContext());
    }
}

2. 設置本地視頻

/**
 * 設置本地視頻,即前置攝像頭預覽
 */
public AgoraManager setupLocalVideo(Context context) {
    //創建一個SurfaceView用作視頻預覽
    SurfaceView surfaceView = RtcEngine.CreateRendererView(context);
    //將SurfaceView保存起來在SparseArray中,后續會將其加入界面。key為視頻的用戶id,這里是本地視頻, 默認id是0
    mSurfaceViews.put(mLocalUid, surfaceView);
    //設置本地視頻,渲染模式選擇VideoCanvas.RENDER_MODE_HIDDEN,如果選其他模式會出現視頻不會填充滿整個SurfaceView的情況,
    //具體渲染模式的區別是什么,官方也沒有詳細的說明
    mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_HIDDEN, mLocalUid));
    return this;//返回AgoraManager以作鏈式調用
}

3. 添加SurfaceView到布局

@Override
protected void onResume() {
    super.onResume();
    //先清空容器
    mFrameLayout.removeAllViews();
    //設置本地前置攝像頭預覽并啟動
    AgoraManager.getInstance().setupLocalVideo(getApplicationContext()).startPreview();
    //將本地攝像頭預覽的SurfaceView添加到容器中
    mFrameLayout.addView(AgoraManager.getInstance().getLocalSurfaceView());
}

4. 停止預覽

/**
 * 停止預覽
 */
@Override
protected void onPause() {
    super.onPause();
    AgoraManager.getInstance().stopPreview();
}
聊天室 (PartyRoomActivity)

點擊頻道列表中的選項,跳轉到聊天室界面。聊天室界面顯示規則是:1個人是全屏,2個人是2分屏,3-4個人是4分屏,5-6個人是6分屏, 4分屏和6分屏模式下,雙擊一個小窗,窗會變大,其余小窗在底部排列。最多支持六人同時聊天。基于這種需求,叔決定寫一個自定義控件PartyRoomLayout來完成。PartyRoomLayout直接繼承ViewGroup,根據不同的顯示模式來完成孩子的測量和布局。

1人全屏


1人全屏其實就是前置攝像頭預覽效果。

前置攝像頭預覽
//設置前置攝像頭預覽并開啟
AgoraManager.getInstance()
        .setupLocalVideo(getApplicationContext())
        .startPreview();
//將攝像頭預覽的SurfaceView加入PartyRoomLayout
mPartyRoomLayout.addView(AgoraManager.getInstance().getLocalSurfaceView());
PartyRoomLayout處理1人全屏
/**
 * 測量一個孩子的情況,孩子的寬高和父容器即PartyRoomLayout一樣
 */
private void measureOneChild(int widthMeasureSpec, int heightMeasureSpec) {
    View child = getChildAt(0);
    child.measure(widthMeasureSpec, heightMeasureSpec);
}

/**
 * 布局一個孩子的情況
 */
private void layoutOneChild() {
    View child = getChildAt(0);
    child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
}
加入頻道

從頻道列表跳轉過來后,需要加入到用戶所選的頻道。

//更新頻道的TextView

mChannel = (TextView) findViewById(R.id.channel);
String channel = getIntent().getStringExtra(“Channel”);
mChannel.setText(channel);

//在AgoraManager中封裝了加入頻道的API

AgoraManager.getInstance()
            .setupLocalVideo(getApplicationContext())
            .joinChannel(channel)//加入頻道
            .startPreview();
掛斷

mEndCall = (ImageButton) findViewById(R.id.end_call);
mEndCall.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //AgoraManager里面封裝了掛斷的API, 退出頻道
        AgoraManager.getInstance().leaveChannel();
        finish();
    }
});
二分屏

事件監聽器

IRtcEngineEventHandler類里面封裝了Agora SDK里面的很多事件回調,在AgoraManager中我們創建了IRtcEngineEventHandler的一個對象mRtcEventHandler,并在創建RtcEngine時傳入。
private IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() { /**

private IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {

    /**
     * 當獲取用戶uid的遠程視頻的回調
     */
    @Override
    public void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) {
        if (mOnPartyListener != null) {
            mOnPartyListener.onGetRemoteVideo(uid);
        }
    }

    /**
     * 加入頻道成功的回調
     */
    @Override
    public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
        if (mOnPartyListener != null) {
            mOnPartyListener.onJoinChannelSuccess(channel, uid);
        }
    }

    /**
     * 退出頻道
     */
    @Override
    public void onLeaveChannel(RtcStats stats) {
        if (mOnPartyListener != null) {
            mOnPartyListener.onLeaveChannelSuccess();
        }
    }

    /**
     * 用戶uid離線時的回調
     */
    @Override
    public void onUserOffline(int uid, int reason) {
        if (mOnPartyListener != null) {
            mOnPartyListener.onUserOffline(uid);
        }
    }
};

同時,我們也提供了一個接口,暴露給AgoraManager外部。

public interface OnPartyListener {

    void onJoinChannelSuccess(String channel, int uid);

    void onGetRemoteVideo(int uid);

    void onLeaveChannelSuccess();

    void onUserOffline(int uid);
}

在PartyRoomActivity中監聽事件

AgoraManager.getInstance()
        .setupLocalVideo(getApplicationContext())
        .setOnPartyListener(mOnPartyListener)//設置監聽
        .joinChannel(channel)
        .startPreview();

設置遠程用戶視頻

private AgoraManager.OnPartyListener mOnPartyListener = new AgoraManager.OnPartyListener() {

    /**
     * 獲取遠程用戶視頻的回調
     */
    @Override
    public void onGetRemoteVideo(final int uid) {
        //操作UI,需要切換到主線程
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //設置遠程用戶的視頻
                AgoraManager.getInstance().setupRemoteVideo(PartyRoomActivity.this, uid);
                //將遠程用戶視頻的SurfaceView添加到PartyRoomLayout中,這會觸發PartyRoomLayout重新走一遍繪制流程
                mPartyRoomLayout.addView(AgoraManager.getInstance().getSurfaceView(uid));
            }
        });
    }

};

測量布局二分屏

當第一次回調onGetRemoteVideo時,說明現在有兩個用戶了,所以在PartyRoomLayout中需要對二分屏模式進行處理

/**
 * 二分屏時的測量
 */
private void measureTwoChild(int widthMeasureSpec, int heightMeasureSpec) {
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        int size = MeasureSpec.getSize(heightMeasureSpec);
        //孩子高度為父容器高度的一半
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(size / 2, MeasureSpec.EXACTLY);
        child.measure(widthMeasureSpec, childHeightMeasureSpec);
    }
}

/**
 * 二分屏模式的布局
 */
private void layoutTwoChild() {
    int left = 0;
    int top = 0;
    int right = getMeasuredWidth();
    int bottom = getChildAt(0).getMeasuredHeight();
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        child.layout(left, top, right, bottom);
        top += child.getMeasuredHeight();
        bottom += child.getMeasuredHeight();
    }
}

用戶離線時的處理

當有用戶離線時,我們需要移除該用戶視頻對應的SurfaceView

private AgoraManager.OnPartyListener mOnPartyListener = new AgoraManager.OnPartyListener() {

    @Override
    public void onUserOffline(final int uid) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //從PartyRoomLayout移除遠程視頻的SurfaceView
                mPartyRoomLayout.removeView(AgoraManager.getInstance().getSurfaceView(uid));
                //清除緩存的SurfaceView
                AgoraManager.getInstance().removeSurfaceView(uid);
            }
        });
    }
};
四分屏和六分屏

當有3個或者4個老鐵開趴,界面顯示成四分屏, 當有5個或者6個老鐵開趴,界面切分成六分屏

由于之前已經處理了新進用戶就會創建SurfaceView加入PartyRoomLayout的邏輯,所以這里只需要處理四六分屏時的測量和布局

四六分屏測量
private void measureMoreChildSplit(int widthMeasureSpec, int heightMeasureSpec) {
    //列數為兩列,計算行數
    int row = getChildCount() / 2;
    if (getChildCount() % 2 != 0) {
        row = row + 1;
    }
    //根據行數平分高度
    int childHeight = MeasureSpec.getSize(heightMeasureSpec) / row;
    //寬度為父容器PartyRoomLayout的寬度一般,即屏寬的一半
    int childWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
}
四六分屏布局
private void layoutMoreChildSplit() {
    int left = 0;
    int top = 0;
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        int right = left + child.getMeasuredWidth();
        int bottom = top + child.getMeasuredHeight();
        child.layout(left, top, right, bottom);
        if ( (i + 1 )% 2 == 0) {//滿足換行條件,更新left和top,布局下一行
            left = 0;
            top += child.getMeasuredHeight();
        } else {
            //不滿足換行條件,更新left值,繼續布局一行中的下一個孩子
            left += child.getMeasuredWidth();
        }
    }
}
雙擊上下分屏布局

在四六分屏模式下,雙擊一個小窗,窗會變大,其余小窗在底部排列, 成上下分屏模式。實現思路就是監聽PartyRoomLayout的觸摸時間,當是雙擊時,則重新布局。

觸摸事件處理

/**
 *  攔截所有的事件
 */
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return true;
}

/**
 * 讓GestureDetector處理觸摸事件
 */
@Override
public boolean onTouchEvent(MotionEvent event) {
    mGestureDetector.onTouchEvent(event);
    return true;
}



//四六分屏模式
private static int DISPLAY_MODE_SPLIT = 0;
//上下分屏模式
private static int DISPLAY_MODE_TOP_BOTTOM = 1;
//顯示模式的變量,默認是四六分屏
private int mDisplayMode = DISPLAY_MODE_SPLIT;
//上下分屏時上面View的下標
private int mTopViewIndex = -1;

private GestureDetector.SimpleOnGestureListener mOnGestureListener = new GestureDetector.SimpleOnGestureListener() {

    @Override
    public boolean onDoubleTap(MotionEvent e) {
        handleDoubleTap(e);//處理雙擊事件
        return true;
    }

    private void handleDoubleTap(MotionEvent e) {
        //遍歷所有的孩子
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            //獲取孩子view的矩形
            Rect rect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
            if (rect.contains((int)e.getX(), (int)e.getY())) {//找到雙擊位置的孩子是誰
                if (mTopViewIndex == i) {//如果點擊的位置就是上面的view, 則切換成四六分屏模式
                    mDisplayMode = DISPLAY_MODE_SPLIT;
                    mTopViewIndex = -1;//重置上面view的下標
                } else {
                    //切換成上下分屏模式,
                    mTopViewIndex = i;//保存雙擊位置的下標,即上面View的下標
                    mDisplayMode = DISPLAY_MODE_TOP_BOTTOM;
                }
                requestLayout();//請求重新布局
                break;
            }
        }
    }
};
上下分屏測量

處理完雙擊事件后,切換顯示模式,請求重新布局,這時候又會觸發測量和布局。

/**
 * 上下分屏模式的測量
 */
private void measureMoreChildTopBottom(int widthMeasureSpec, int heightMeasureSpec) {
    for (int i = 0; i < getChildCount(); i++) {
        if (i == mTopViewIndex) {
            //測量上面View
            measureTopChild(widthMeasureSpec, heightMeasureSpec);
        } else {
            //測量下面View
            measureBottomChild(i, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

/**
 *  上下分屏模式時上面View的測量
 */
private void measureTopChild(int widthMeasureSpec, int heightMeasureSpec) {
    int size = MeasureSpec.getSize(heightMeasureSpec);
    //高度為PartyRoomLayout的一半
    int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(size / 2, MeasureSpec.EXACTLY);
    getChildAt(mTopViewIndex).measure(widthMeasureSpec, childHeightMeasureSpec);
}

/**
 * 上下分屏模式時底部View的測量
 */
private void measureBottomChild(int i, int widthMeasureSpec, int heightMeasureSpec) {
    //除去頂部孩子后還剩的孩子個數
    int childCountExcludeTop = getChildCount() - 1;
    //當底部孩子個數小于等于3時
    if (childCountExcludeTop <= 3) {
        //平分孩子寬度
        int childWidth = MeasureSpec.getSize(widthMeasureSpec) / childCountExcludeTop;
        int size = MeasureSpec.getSize(heightMeasureSpec);
        //高度為PartyRoomLayout的一半
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(size / 2, MeasureSpec.EXACTLY);
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
        getChildAt(i).measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } else if (childCountExcludeTop == 4) {//當底部孩子個數為4個時
        int childWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;//寬度為PartyRoomLayout的一半
        int childHeight = MeasureSpec.getSize(heightMeasureSpec) / 4;//高度為PartyRoomLayout的1/4
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
        getChildAt(i).measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } else {//當底部孩子大于4個時
        //計算行的個數
        int row = childCountExcludeTop / 3;
        if (row  % 3 != 0) {
            row ++;
        }
        //孩子的寬度為PartyRoomLayout寬度的1/3
        int childWidth = MeasureSpec.getSize(widthMeasureSpec) / 3;
        //底部孩子平分PartyRoomLayout一半的高度
        int childHeight = (MeasureSpec.getSize(heightMeasureSpec) / 2) / row;
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY);
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
        getChildAt(i).measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
}
上下分屏布局
private void layoutMoreChildTopBottom() {
    //布局上面View
    View topView = getChildAt(mTopViewIndex);
    topView.layout(0, 0, topView.getMeasuredWidth(), topView.getMeasuredHeight());
    int left = 0;
    int top = topView.getMeasuredHeight();
    for (int i = 0; i < getChildCount(); i++) {
        //上面已經布局過上面的View, 這里就跳過
        if (i == mTopViewIndex) {
            continue;
        }
        View view = getChildAt(i);
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        //布局下面的一個View
        view.layout(left, top, right, bottom);
        left = left + view.getMeasuredWidth();
        if (left >= getWidth()) {//滿足換行條件則換行
            left = 0;
            top += view.getMeasuredHeight();
        }
    }
}

至此,一個功能類似Houseparty的demo就完成了,github地址:

https://github.com/uncleleonf...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70046.html

相關文章

  • Android實現多人音視頻聊天應用(一)

    作者:聲網Agora用戶,資深Android開發者吳東洋。本系列文章分享了基于Agora SDK 2.1實現多人視頻通話的實踐經驗。 自從2016年,鼓吹互聯網寒冬的論調甚囂塵上,2017年亦有愈演愈烈之勢。但連麥直播、在線抓娃娃、直播問答、遠程狼人殺等類型的項目卻異軍突起,成了投資人的風口,創業者的藍海和用戶的必裝App,而這些方向的項目都有一個共同的特點——都依賴視頻通話和全互動直播技術。 聲...

    raoyi 評論0 收藏0

發表評論

0條評論

CocoaChina

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<