集成腾讯随心播1.8.4代码流程分析

之前写过关于集成腾讯直播的一些关键地方,但是比较分散,加之随心播新版本对权限角色做了升级,这对优化流量计费有很大帮助,因此,升级必不可少。

随心播下载地址:https://github.com/zhaoyang21cn/Android_Suixinbo

首先是集成流程,文档都提到。

1,配置为jcenter库

这里写图片描述

2,使用proguard等工具做了代码混淆

-keep class com.tencent.**{*;}
-dontwarn com.tencent.**

-keep class tencent.**{*;}
-dontwarn tencent.**

-keep class qalsdk.**{*;}
-dontwarn qalsdk.**

3,配置权限和服务

 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <!--<uses-feature android:name="android.hardware.camera" />-->

服务


        <!--TLS Qal 一些服务 -->
        <service
            android:name="com.tencent.qalsdk.service.QalService"
            android:exported="false"
            android:process=":QALSERVICE" />

        <receiver
            android:name="com.tencent.qalsdk.QALBroadcastReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="com.tencent.qalsdk.broadcast.qal" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="com.tencent.qalsdk.core.NetConnInfoCenter"
            android:process=":QALSERVICE">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.TIME_SET" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.TIMEZONE_CHANGED" />
            </intent-filter>
        </receiver>

初步配置搭建完成。

其次看一下代码结构

这里写图片描述

demo采用的是MVP结构,便于重构。

Views: 所有界面类,包括登录、主界面、直播界面以及一些自定义控件。

Presenters : 所有界面的逻辑操作,包括初始化逻辑,进出房间逻辑,直播交互逻辑,登录逻辑等。以及逻辑操作的回调接口,这些接口会被需要对应功能的界面实现。

Model : 数据类包括当前房间信息类,个人状态类,文本消息类,AV成员类。

AVControllers 里面保留了AVSDK一些操作类包括显示控制类,AVSDK初始化类

典型MVP操作流程示例 View有某些功能,持有对应功能的Presenter类,View触发功能,调用对应的Persenter方法,Presenter将处理结果通过ViewInface接口类回调给对应的View. View根据数据进行界面显示。 View类只做界面相关事情,数据逻辑都丢给Presenter处理。

申请创建应用后,会得到两个参数,在constans中修改如下:

   public static final int SDK_APPID = 1400001692;

   public static final int ACCOUNT_TYPE = 884;

然后是登录注册流程

这里写图片描述

在LoginActivity中处理如上逻辑

在onCreate()中

        //获取个人数据本地缓存
        MySelfInfo.getInstance().getCache(getApplicationContext());
        initView();
        if (!needLogin()) {
            //本地没有账户需要登录
            mTvWelcome.setVisibility(View.VISIBLE);
            //有账户登录直接IM登录
            SxbLog.i(TAG, "LoginActivity onCreate");
            mLoginHeloper.imLogin(MySelfInfo.getInstance().getId(),     MySelfInfo.getInstance().getUserSig());
        }

首先从本地获取账号,如果本地存在账号就用本地存在账号进行登录,调用imLogin()方法,传入用户id和usersig

needLogin


    /**
     * 判断是否需要登录
     *
     * @return true 代表需要重新登录
     */
    public boolean needLogin() {
        if (MySelfInfo.getInstance().getId() != null) {
            return false;//有账号不需要登录
        } else {
            return true;//需要登录
        }

    }

至于用户id和用户签名(usersig)如何获取,在点击登录按钮的的时候,会调用

mLoginHeloper.tlsLogin(mUserName.getText().toString(), mPassWord.getText().toString());

登录TLS账号系统

 /**
     * 登录TLS账号系统
     *
     * @param id
     * @param password
     */
    public void tlsLogin(String id, String password) {
        int ret = InitBusinessHelper.getmLoginHelper().TLSPwdLogin(id, password.getBytes(), new TLSPwdLoginListener() {
            @Override
            public void OnPwdLoginSuccess(TLSUserInfo tlsUserInfo) {//获取用户信息
//                Toast.makeText(mContext, "TLS login succ ! " + tlsUserInfo.identifier, Toast.LENGTH_SHORT).show();
//                SxbLog.i(TAG, "TLS OnPwdLoginSuccess " + tlsUserInfo.identifier);
                String userSig = InitBusinessHelper.getmLoginHelper().getUserSig(tlsUserInfo.identifier);
                MySelfInfo.getInstance().setId(tlsUserInfo.identifier);
                MySelfInfo.getInstance().setUserSig(userSig);
                imLogin(tlsUserInfo.identifier, userSig);
            }

            @Override
            public void OnPwdLoginReaskImgcodeSuccess(byte[] bytes) {

            }

            @Override
            public void OnPwdLoginNeedImgcode(byte[] bytes, TLSErrInfo tlsErrInfo) {

            }

            @Override
            public void OnPwdLoginFail(TLSErrInfo tlsErrInfo) {
                SxbLog.e(TAG, "OnPwdLoginFail " + tlsErrInfo.Msg);
                Toast.makeText(mContext, "OnPwdLoginFail:\n" + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void OnPwdLoginTimeout(TLSErrInfo tlsErrInfo) {
                SxbLog.e(TAG, "OnPwdLoginTimeout " + tlsErrInfo.Msg);
                Toast.makeText(mContext, "OnPwdLoginTimeout:\n" + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
            }
        });
        if (ret != -1001) {
            Toast.makeText(mContext, "input invalid !", Toast.LENGTH_SHORT).show();
        }
    }

不管是第三方账号系统还是本地账号系统都需要去腾讯的tls账号系统进行注册登录。

可以看出,tls登录成功后返回

 String userSig = InitBusinessHelper.getmLoginHelper().getUserSig(tlsUserInfo.identifier);
 MySelfInfo.getInstance().setId(tlsUserInfo.identifier);
 MySelfInfo.getInstance().setUserSig(userSig);

这里拿到id和usersig,为im登录做准备,IM登录

  /**
     * 登录imsdk
     *
     * @param identify 用户id
     * @param userSig  用户签名
     */
    public void imLogin(final String identify, String userSig) {
        TIMUser user = new TIMUser();
        user.setAccountType(String.valueOf(Constants.ACCOUNT_TYPE));
        user.setAppIdAt3rd(String.valueOf(Constants.SDK_APPID));
        user.setIdentifier(identify);
        //发起登录请求
        TIMManager.getInstance().login(
                Constants.SDK_APPID,
                user,
                userSig,                    //用户帐号签名,由私钥加密获得,具体请参考文档
                new TIMCallBack() {
                    @Override
                    public void onError(int i, String s) {
                        SxbLog.e(TAG, "IMLogin fail :" + i + " msg " + s);
                        Toast.makeText(mContext, "IMLogin fail :" + i + " msg " + s, Toast.LENGTH_SHORT).show();
                        if (mLoginView != null) {
                            mLoginView.loginFail();
                        }
                    }

                    @Override
                    public void onSuccess() {
                        SxbLog.i(TAG, "keypath IMLogin succ !");
//                        Toast.makeText(mContext, "IMLogin succ !", Toast.LENGTH_SHORT).show();
                        SxbLog.d(TAG, LogConstants.ACTION_HOST_CREATE_ROOM + LogConstants.DIV + identify + LogConstants.DIV + "request room id");
                        getMyRoomNum();
                        startAVSDK();
                    }
                });
    }

只有在IM登录成功之后,启动AVSDK,必须遵守这个顺序。

登录成功如果需要发布直播间,需要获取房间id,这个需要通过接口来获取。这样登录流程就结束了

注册流程,调用如下

    /**
     * 在TLS模块注册一个账号
     *
     * @param id
     * @param psw
     */
    public void tlsRegister(final String id, final String psw) {
        int ret = InitBusinessHelper.getmAccountHelper().TLSStrAccReg(id, psw, new TLSStrAccRegListener() {
            @Override
            public void OnStrAccRegSuccess(TLSUserInfo tlsUserInfo) {
                Toast.makeText(mContext, tlsUserInfo.identifier + " register a user succ !  ", Toast.LENGTH_SHORT).show();
                //继续登录流程
                tlsLogin(id, psw);
            }

            @Override
            public void OnStrAccRegFail(TLSErrInfo tlsErrInfo) {
                Toast.makeText(mContext, " register a user fail ! " + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void OnStrAccRegTimeout(TLSErrInfo tlsErrInfo) {
                Toast.makeText(mContext, " register timeout ! " + tlsErrInfo.Msg, Toast.LENGTH_SHORT).show();
            }
        });
        if (ret != -1001) {
            Toast.makeText(mContext, "input invalid !", Toast.LENGTH_SHORT).show();
        }
    }

这是在注册成功之后直接登录

发布一个直播流程

这里写图片描述

进入直播间(LiveActivity)

这里写图片描述

会直接调用mEnterRoomHelper.startEnterRoom();进入房间

startEnterRoom

  /**
     * 进入一个直播房间流程
     */
    public void startEnterRoom() {
        if (MySelfInfo.getInstance().isCreateRoom() == true) {
            createLive();
        } else {
            SxbLog.i(TAG, "joinLiveRoom startEnterRoom ");
            joinLive(CurLiveInfo.getRoomNum());
        }

    }

这里MySelfInfo.getInstance().isCreateRoom()在发布房间时设置为true,进入房间时设置为false,所以走
createLive()

    /**
     * 1_1 创建一个直播
     */
    private void createLive() {
        createIMChatRoom();

    }

    /**
     * 1_2创建一个IM聊天室
     */
    private void createIMChatRoom() {
        final ArrayList<String> list = new ArrayList<String>();
        final String roomName = "this is a  test";
        SxbLog.i(TAG,"createIMChatRoom  room " +MySelfInfo.getInstance().getMyRoomNum());

        TIMGroupManager.getInstance().createGroup("AVChatRoom", list, roomName, "" + MySelfInfo.getInstance().getMyRoomNum(), new TIMValueCallBack<String>() {
            @Override
            public void onError(int i, String s) {
                SxbLog.i(TAG, "onError " + i + "   " + s);
                //已在房间中,重复进入房间
                if (i == Constants.IS_ALREADY_IN_ROOM) {
                    isInChatRoom = true;
                    createAVRoom(MySelfInfo.getInstance().getMyRoomNum());
                    return;
                }
                // 创建IM房间失败,提示失败原因,并关闭等待对话框
                SxbLog.standardEnterRoomLog(TAG, "create live im group", "" + LogConstants.STATUS.FAILED, "code:" + i + " msg:" + s);
                Toast.makeText(mContext, "create IM room fail " + s + " " + i, Toast.LENGTH_SHORT).show();
                quiteLive();
            }

            @Override
            public void onSuccess(String s) {
                SxbLog.standardEnterRoomLog(TAG, "create live im group", "" + LogConstants.STATUS.SUCCEED, "group id " + MySelfInfo.getInstance().getMyRoomNum());
                isInChatRoom = true;
                //创建AV房间
                createAVRoom(MySelfInfo.getInstance().getMyRoomNum());

            }
        });

    }

创建IM聊天室成功后创建AV房间,调用createAVRoom

 /**
     * 1_3创建一个AV房间
     */
    private void createAVRoom(int roomNum) {
        SxbLog.standardEnterRoomLog(TAG, "create av room", "", "room id " + MySelfInfo.getInstance().getMyRoomNum());
        EnterAVRoom(roomNum);
    }

进入AV房间,调用EnterAVRoom,房间号之前通过getMyRoomNum方法拿到。

EnterAVRoom


    /**
     * 进入AV房间
     *
     * @param roomNum
     */
    private void EnterAVRoom(int roomNum) {
        SxbLog.i(TAG, "createlive joinLiveRoom enterAVRoom " + roomNum);
        AVContext avContext = QavsdkControl.getInstance().getAVContext();
        byte[] authBuffer = null;//权限位加密串;TODO:请业务侧填上自己的加密串

        AVRoomMulti.EnterParam.Builder enterRoomParam = new AVRoomMulti.EnterParam.Builder(roomNum);
        if (MySelfInfo.getInstance().getIdStatus() == Constants.HOST) {
            enterRoomParam.auth(Constants.HOST_AUTH, authBuffer).avControlRole(Constants.HOST_ROLE).autoCreateRoom(true).isEnableMic(true).isEnableSpeaker(true);//;TODO:主播权限 所有权限
        } else {
            enterRoomParam.auth(Constants.NORMAL_MEMBER_AUTH, authBuffer).avControlRole(Constants.NORMAL_MEMBER_ROLE).autoCreateRoom(false).isEnableSpeaker(true);
        }
        enterRoomParam.audioCategory(Constants.AUDIO_VOICE_CHAT_MODE).videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO);
//        enterRoomParam.isDegreeFixed(true);
        if (avContext != null) {
            // create room
            avContext.enterRoom(mEventListener, enterRoomParam.build());
        }

    }

这里面主要区别主播还是观众,MySelfInfo.getInstance().getIdStatus() == Constants.HOST表示是主播

主播拥有所有权限,直播的角色跟权限都与观众不同,这里涉及到流量计费问题,之前的版本都是给予主播权限,也就是所有权限,会导致DC流量峰值增高,从而增加直播成本。

主播

enterRoomParam.auth(Constants.HOST_AUTH, authBuffer).avControlRole(Constants.HOST_ROLE).autoCreateRoom(true).isEnableMic(true).isEnableSpeaker(true);//;TODO:主播权限 所有权限

观众

 enterRoomParam.auth(Constants.NORMAL_MEMBER_AUTH, authBuffer).avControlRole(Constants.NORMAL_MEMBER_ROLE).autoCreateRoom(false).isEnableSpeaker(true);

这里可以看Constans中的权限

public static final long HOST_AUTH = AVRoomMulti.AUTH_BITS_DEFAULT;//权限位;TODO:默认值是拥有所有权限。

public static final long VIDEO_MEMBER_AUTH = AVRoomMulti.AUTH_BITS_DEFAULT;//权限位;TODO:默认值是拥有所有权限。

public static final long NORMAL_MEMBER_AUTH = AVRoomMulti.AUTH_BITS_JOIN_ROOM | AVRoomMulti.AUTH_BITS_RECV_AUDIO | AVRoomMulti.AUTH_BITS_RECV_CAMERA_VIDEO | AVRoomMulti.AUTH_BITS_RECV_SCREEN_VIDEO;

这里HOST_AUTH 是主播权限,VIDEO_MEMBER_AUTH 是上麦权限,等同于直播权限,这个是动态赋予的,下麦之后要回复为NORMAL_MEMBER_AUTH ,NORMAL_MEMBER_AUTH 是观众权限,且暂无上麦。

进入房间回调mEventListener,来监听进入房间是否成功。

在EnterLiveHelper里面

private AVRoomMulti.EventListener mEventListener = new AVRoomMulti.EventListener()

这里因为代码太多,只对对调的几个方法做一下讲述。

创建房间成功回调

        // 创建房间成功回调
        public void onEnterRoomComplete(int result,String s) {
            SxbLog.i(TAG,"enterAVRoom onEnterRoomComplete: "+result+" info " +s);
            if (result == 0) {
                SxbLog.standardEnterRoomLog(TAG, "enterAVRoom", "" + LogConstants.STATUS.SUCCEED, "room id" + MySelfInfo.getInstance().getMyRoomNum());
                //只有进入房间后才能初始化AvView
                QavsdkControl.getInstance().setAvRoomMulti(QavsdkControl.getInstance().getAVContext().getRoom());
                isInAVRoom = true;
                initAudioService();
                if (null != mStepInOutView)
                    mStepInOutView.enterRoomComplete(MySelfInfo.getInstance().getIdStatus(), true);
            } else {
                quiteAVRoom();
                SxbLog.standardEnterRoomLog(TAG, "enterAVRoom", "" + LogConstants.STATUS.FAILED, "result " + result);
            }

        }

当result==0时进入房间成功enterRoomComplete,回调是否是主播跟成功状态,进入失败就退出直播页面,这个不提。

enterRoomComplete回调处理

    /**
     * 完成进出房间流程
     */
    @Override
    public void enterRoomComplete(int id_status, boolean isSucc) {
        Toast.makeText(LiveActivity.this, "EnterRoom  " + id_status + " isSucc " + isSucc, Toast.LENGTH_SHORT).show();
        //必须得进入房间之后才能初始化UI
        mEnterRoomHelper.initAvUILayer(avView);
        QavsdkControl.getInstance().setSlideListener(this);
        bInAvRoom = true;
        bDelayQuit = true;
        updateHostLeaveLayout();

        //设置预览回调,修正摄像头镜像
        mLiveHelper.setCameraPreviewChangeCallback();
        if (isSucc == true) {
            //IM初始化
            mLiveHelper.initTIMListener("" + CurLiveInfo.getRoomNum());

            if (id_status == Constants.HOST) {//主播方式加入房间成功
                //开启摄像头渲染画面
                SxbLog.i(TAG, "createlive enterRoomComplete isSucc" + isSucc);
            } else {
                //发消息通知上线
                mLiveHelper.sendGroupMessage(Constants.AVIMCMD_ENTERLIVE, "");
            }
        }

        bReadyToChange = false;
    }

主要做了一下初始化UI(初始化surfaceview),修正摄像头。这时候你会说摄像头啥时候开启的?没错,上面流程并没有看到开启摄像头,其实这个隐藏很深。必须的进入房间之后才能初始化SurfaceView,初始化完了之后才能打开摄像头。

分析一下:
mEnterRoomHelper.initAvUILayer(avView);—》initAvUILayer—mAVUIControl = new AVUIControl(context, view)—-》initCameraPreview();—》holder.addCallback(mSurfaceHolderListener);—》mSurfaceHolderListener—》mContext.sendBroadcast(new Intent(Constants.ACTION_SURFACE_CREATED)); 通过这个广播,在直播页面接收并处理

 //打开摄像头
 if (MySelfInfo.getInstance().getIdStatus() == Constants.HOST) {
 mLiveHelper.openCameraAndMic();

onExitRoomComplete退出房间回调方法

最重要的还是onEndpointsUpdateInfo房间成员变化回调。
具体处理一下几种情况

    private static final int TYPE_MEMBER_CHANGE_IN = 1;//进入房间事件。
    private static final int TYPE_MEMBER_CHANGE_OUT = 2;//退出房间事件。
    private static final int TYPE_MEMBER_CHANGE_HAS_CAMERA_VIDEO = 3;//有发摄像头视频事件。
    private static final int TYPE_MEMBER_CHANGE_NO_CAMERA_VIDEO = 4;//无发摄像头视频事件。
    private static final int TYPE_MEMBER_CHANGE_HAS_AUDIO = 5;//有发语音事件。
    private static final int TYPE_MEMBER_CHANGE_NO_AUDIO = 6;//无发语音事件。
    private static final int TYPE_MEMBER_CHANGE_HAS_SCREEN_VIDEO = 7;//有发屏幕视频事件。
    private static final int TYPE_MEMBER_CHANGE_NO_SCREEN_VIDEO = 8;//无发屏幕视频事件。

具体代码不贴了。

加入一个直播流程

加入直播跟发布直播基本一致,加入直播进入直播间也是startEnterRoom方法,只不过这时候不需要创建IM房间,只需要加入IM房间即可

joinLive

    /**
     * 2_1加入一个房间
     */
    private void joinLive(int roomNum) {
        joinIMChatRoom(roomNum);
    }

    /**
     * 2_2加入一个聊天室
     */
    private void joinIMChatRoom(final int chatRoomId) {
        SxbLog.standardEnterRoomLog(TAG, "join im chat room", "", "room id " + chatRoomId);

        TIMGroupManager.getInstance().applyJoinGroup("" + chatRoomId, Constants.APPLY_CHATROOM + chatRoomId, new TIMCallBack() {
            @Override
            public void onError(int i, String s) {
                //已经在是成员了
                if (i == Constants.IS_ALREADY_MEMBER) {
                    SxbLog.i(TAG, "joinLiveRoom joinIMChatRoom callback succ ");
                    joinAVRoom(CurLiveInfo.getRoomNum());
                    isInChatRoom = true;
                } else {
                    SxbLog.standardEnterRoomLog(TAG, "join im chat room", "" + LogConstants.STATUS.FAILED, "code:" + i + " msg:" + s);
                    if (mContext != null)
                        Toast.makeText(mContext, "join IM room fail " + s + " " + i, Toast.LENGTH_SHORT).show();
                    quiteLive();
                }
            }

            @Override
            public void onSuccess() {
                SxbLog.standardEnterRoomLog(TAG, "join im chat room", "" + LogConstants.STATUS.FAILED, "room id " + chatRoomId);
                isInChatRoom = true;
                joinAVRoom(CurLiveInfo.getRoomNum());
            }
        });

    }

这里面三个方法其实都是为了一个操作,加入聊天室joinIMChatRoom。加入成功就joinAVRoom,进入avRoom房间。

那又有问题,发布直播是本地渲染,加入直播是请求画面,那这两个方法分别在哪里实现的呢?

其实是在我们打开摄像头的时候,在EnterLiveHelper中的onEndpointsUpdateInfo会监听到TYPE_MEMBER_CHANGE_HAS_CAMERA_VIDEO 有发摄像头视频事件。他在下面的处理中发送了

 Intent intent = new Intent(Constants.ACTION_CAMERA_OPEN_IN_LIVE);
                    intent.putStringArrayListExtra("ids", video_ids);
                    mContext.sendBroadcast(intent);

在直播页面接收广播并处理

    if (action.equals(Constants.ACTION_CAMERA_OPEN_IN_LIVE)) {//有人打开摄像头
                isScreenShare = false;
                ArrayList<String> ids = intent.getStringArrayListExtra("ids");
                //如果是自己本地直接渲染
                for (String id : ids) {
                    if (!mRenderUserList.contains(id)) {
                        mRenderUserList.add(id);
                    }
                    updateHostLeaveLayout();

                    if (id.equals(MySelfInfo.getInstance().getId())) {
                        showVideoView(true, id);
                        return;
//                        ids.remove(id);
                    }
                }
                //其他人一并获取
                SxbLog.d(TAG, LogConstants.ACTION_VIEWER_SHOW + LogConstants.DIV + MySelfInfo.getInstance().getId() + LogConstants.DIV + "somebody open camera,need req data"
                        + LogConstants.DIV + LogConstants.STATUS.SUCCEED + LogConstants.DIV + "ids " + ids.toString());
                int requestCount = CurLiveInfo.getCurrentRequestCount();
                mLiveHelper.requestViewList(ids);
                requestCount = requestCount + ids.size();
                CurLiveInfo.setCurrentRequestCount(requestCount);
//                }
            }

如果是主播,调用showVideoView本地渲染,如果是观众requestViewList请求直播画面,这样整个流程就通了。

本地渲染 QavsdkControl.getInstance().setLocalHasVideo(true, MySelfInfo.getInstance().getId());

请求画面要区分是否分享屏幕,一是摄像头画面,二是分享屏幕画面

 if (isScreenShare) {
                QavsdkControl.getInstance().setRemoteHasVideo(true, id, AVView.VIDEO_SRC_TYPE_SCREEN);
                isScreenShare = false;
            } else {
                QavsdkControl.getInstance().setRemoteHasVideo(true, id, AVView.VIDEO_SRC_TYPE_CAMERA);
            }

主播请求观众连线。

1 信令请求和应答是通过IMSDK的C2C消息。应答信令消息之后是打开或者关闭自己摄像头。

2 直播过程中成员音视频状态发送改变会通过onEndpointsUpdateInfo回调传递给所有成员。根据Id去请求对方画面,这种请求是AVSDK的接口不再是C2C消息。请求成功渲染画面。

3 成员状态变化回调onEndpointsUpdateInfo回调会包括自己,所以无论视频互动成员还是普通成员只需要根据回调里面的ID去请求画面即可同步,保持一致。

通过发送 mLiveHelper.sendC2CMessage(Constants.AVIMCMD_MUlTI_HOST_INVITE, “”, id); (多人主播发送邀请消息, C2C消息),在liveHelper中处理并展示showInviteDialog给观众,观众点击确定,进行

agreeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                mVideoMemberCtrlView.setVisibility(View.VISIBLE);
//                mNomalMemberCtrView.setVisibility(View.INVISIBLE);
                SxbLog.d(TAG, LogConstants.ACTION_VIEWER_SHOW + LogConstants.DIV + MySelfInfo.getInstance().getId() + LogConstants.DIV + "accept invite" +
                        LogConstants.DIV + "host id " + CurLiveInfo.getHostID());
                //上麦 ;TODO 上麦 上麦 上麦 !!!!!;
                mLiveHelper.changeAuthandRole(true, Constants.VIDEO_MEMBER_AUTH, Constants.VIDEO_MEMBER_ROLE);
                inviteDg.dismiss();
            }
        });

        refusebtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mLiveHelper.sendC2CMessage(Constants.AVIMCMD_MUlTI_REFUSE, "", CurLiveInfo.getHostID());
                inviteDg.dismiss();
            }
        });

确定就获取到上麦权限,从而打开mic和摄像头,根据摄像头监听从而展示数据
拒绝就发送c2c消息,在liveHelper中处理

    case Constants.AVIMCMD_MUlTI_REFUSE:
                    if (null != mLiveView)
                        mLiveView.cancelInviteView(identifier);
                    if (null != mContext) {
                        Toast.makeText(mContext, identifier + " refuse !", Toast.LENGTH_SHORT).show();
                    }
                    break;

整个流程就这么多了,更详细点的就参考代码吧

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值