Android 安卓腾讯云互动直播开发新手入坑几点建议

    现在直播特别火,绝大多数的中小型都没有独立开发这种功能的能力.比较火的第三方就是腾讯云了.但是腾讯云有些坑,入行须知.

     先说它的Demo吧,Demo是有点大,运用MVP设计模式,SDK1.7到1.8实现了代码重构,基本上方法放置的位置全都变了,但是把 调用流程是不变的.

    这里面主要有两种sdk 1,AVSDK 这个主要是管理视频的 包括直播,推流....  2,IMSDK这个主要是管理聊天的.就是所说的即时通讯

     调用流程:--------------1-----------------------------------------------------------------------------------

                       

        在  ReleaseLiveActivity中调用的

                        直播主页面之前的输入直播标题的页面 

                       1,向自己后台获取sig sig相当于一个登录腾讯云服务器的平台,在申请sig异步任务回调的接口初始化IMSDK

                       2,初始化IMSDK 就是初始化聊天系统的sdk,初始化代码如下 ,其中 new TIMCallBack() 这个是个接口回调,如果登录成功就回调onSuccess()方法,失败就回调onError()方法.

TIMManager.getInstance().init(mContext);



TIMUser userId = new TIMUser();
userId.setAccountType(DemoConstants.ACCOUNTTYPE);
userId.setAppIdAt3rd(mConfig.appIdAt3rd);
userId.setIdentifier(mConfig.identifier);

/**
 * 登陆所需信息
 * 1.sdkAppId : 创建应用时页面上分配的 sdkappid
 * 2.uid : 创建应用账号集成配置页面上分配的 accounttype
 * 3.app_id_at3rd : 第三方开放平台账号 appid,如果是自有的账号,那么直接填 sdkappid 的字符串形式
 * 4.identifier :用户标示符,也就是我们常说的用户 id
 * 5.user_sig :使用 tls 后台 api tls_gen_signature_ex 或者工具生成的 user_sig
 */
TIMManager.getInstance().login(
        DemoConstants.APPID,
        userId,
        mUserSig,
        new TIMCallBack() {

            @Override
            public void onSuccess() {
                Log.w(TAG, "initAVSDKStep 3 : IMLogin succ  ");
                Log.i(TAG, "init successfully. tiny id = " + IMSdkInt.get().getTinyId());

                onLogin(true, IMSdkInt.get().getTinyId(), 0);
            }

            @Override
            public void onError(int code, String desc) {
                Log.w(TAG, "initAVSDKStep 3 : IMLogin fail  ");
                Log.e(TAG, "init failed, imsdk error code  = " + code + ", desc = " + desc);
                onLogin(false, 0, code);
            }
        });

                               3,在初始化IMSDK的回调接口之后,1.8.2是开启了两个线程,初始化AVSDK 也就是得到AVContext 我是用1.7开发的 在1.8.2中 官方Demo换了一下顺序,先申请房间号roomId再进行初始化AVSDK 其实结果都是一样的.初始化AVSDK也是一个接口回调 ,下面是初始化AVSDK的代码

private void onLogin(boolean result, long tinyId, int errorCode) {

    if (result) {

        mAVContext = AVContext.createInstance(mContext,false);
        Log.w(TAG, "initAVSDKStep 4 : AVContext createContext "+mAVContext);
        Log.d(TAG, "WL_DEBUG startContext mAVContext is null? " + (mAVContext == null));

        mSelfIdentifier = mConfig.identifier;

        int ret = mAVContext.start(mConfig, mStartContextCompleteCallback);
        Log.w(TAG, "initAVSDKStep 5 : AVContext createContext startContext "+ret);
        mIsInStartContext = true;
    } else {
        mStartContextCompleteCallback.onComplete(errorCode,"");
    }
}
                 初始化AVSDK的回调接口

/**
 * 启动SDK系统的回调函数
 */
private AVCallback mStartContextCompleteCallback = new AVCallback() {
    public void onComplete(int result, String s) {
        mIsInStartContext = false;

        mContext.sendBroadcast(new Intent(
                Util.ACTION_START_CONTEXT_COMPLETE).putExtra(
                Util.EXTRA_AV_ERROR_RESULT, result));

        if (result != AVError.AV_OK) {
            mAVContext = null;
            Log.d(TAG, "WL_DEBUG mStartContextCompleteCallback mAVContext is null");
        }
    }
};
                        

 4,不知道别人怎么个调用的次序,我就是这个调用次序,是可以出来的 可能顺序不一样也能成功,不过1.7的demo里面是这个循序,,,,我在这之后 在开始直播的点击按钮的点击事件中 申请房间号roomid的 这个申请数据是向自己的后台申请的房间号,具体实现如下

/**
 * 向后台申请房间号
 */
public void createRoomNum() {
   Log.i(TAG, "CreateRoomStep 1 : Apply AV room num");
   new Thread() {
      @Override
      public void run() {
         super.run();

         // String response =
         // HttpUtil.PostUrl(HttpUtil.GETROOMNUM_URL+HttpUtil.GETROOMNUM_URL_TOKEN+token+HttpUtil.GETROOMNUM_URL_CONTENT+mLiveTitleString+HttpUtil.GETROOMNUM_URL_BKTYPE+DemoConstants.BKTYPE_ZHIBO,
         // new ArrayList<NameValuePair>());

         List<NameValuePair> pairlist = new ArrayList<NameValuePair>();
         pairlist.add(new BasicNameValuePair("token", token));
         pairlist.add(new BasicNameValuePair("content", mLiveTitleString));
         pairlist.add(new BasicNameValuePair("bkType", "3"));
         Log.i(TAG, "mLiveTitleString" + mLiveTitleString);
         String response = HttpUtil.PostUrl(HttpUtil.GETROOMNUM_URL_01,
               pairlist);

         Log.d(TAG, "createRoomNum response" + response);
         if (!response.endsWith("}")) {
            Log.e(TAG, "onSmsRegisterInfo response is not json style"
                  + response);
            return;
         }
         JSONTokener jsonTokener = new JSONTokener(response);
         try {

            JSONObject object = (JSONObject) jsonTokener.nextValue();

            JSONObject dataJsonObject = object
                  .getJSONObject(Util.JSON_KEY_DATA);
            roomNum = dataJsonObject.getInt("bokeId");
            myCoverPath=dataJsonObject.getString("coverPath");
            myShareUrl=dataJsonObject.getString("shareUrl");
            PreferenceUtil.putString(ReleaseLiveActivity.this, "bokeId", roomNum
                  + "");
            Log.d(TAG, "roomnum = " + roomNum);

         } catch (JSONException e) {
            e.printStackTrace();
         }

         mmHandler.sendEmptyMessage(MSG_SHAREUM);

      }
   }.start();
}

            5,如果已经申请到roomId的话 ,在申请完房间的回调接口中调用进入房间

/**
 * 加入直播房间
 *
 * @param roomNum
 *            讨论房间号
 */
private void createRoom(int roomNum) {
   Log.i(TAG, "CreateRoomStep 2: begin create a AVSDK Room ");
   if (Util.isNetworkAvailable(ctx)) {
      int room = roomNum;

      mQavsdkControl.enterRoom(room);

      mmHandler.sendEmptyMessageDelayed(MSG_CREATEROOM_TIMEOUT,
            MAX_TIMEOUT);
      Toast.makeText(ctx, "正在创建视频房间中...", Toast.LENGTH_SHORT).show();
      refreshWaitingDialog();
   } else {
      Toast.makeText(ctx, getString(R.string.notify_no_network),
            Toast.LENGTH_SHORT).show();
   }
}
    


       在mQavsdkControl.enterRoom(room)中 往里面走就回到这个最终的方法:

  /**
     * 创建房间
     *
     * @param relationId 讨论组号
     */
    void enterRoom(int relationId)  {
        Log.d(TAG, "WL_DEBUG enterRoom relationId = " + relationId);
//    int roomType = AVRoom.AV_ROOM_MULTI;
//    int roomId = 0;
//    int authBufferSize = 0;//权限位加密串长度;TODO:请业务侧填上自己的加密串长度。
//    String controlRole = "";//角色名;多人房间专用。该角色名就是web端音视频参数配置工具所设置的角色名。TODO:请业务侧填根据自己的情况填上自己的角色名。
//    int audioCategory = audioCat;

        QavsdkControl qavsdk = ((Myapplication) mContext).getQavsdkControl();
        AVContext avContext = qavsdk.getAVContext();
       long authBits = AVRoomMulti.AUTH_BITS_DEFAULT;//权限位;默认值是拥有所有权限。TODO:请业务侧填根据自己的情况填上权限位。
        byte[] authBuffer = null;//权限位加密串;TODO:请业务侧填上自己的加密串。
    //    AVRoom.EnterRoomParam enterRoomParam = new AVRoomMulti.EnterRoomParam(relationId, authBits, authBuffer, "", audioCat, true);
        AVRoomMulti.EnterParam.Builder enterRoomParam = new AVRoomMulti.EnterParam.Builder(relationId);



            enterRoomParam.auth(authBits, authBuffer).avControlRole("Host").autoCreateRoom(true).isEnableMic(true).isEnableSpeaker(true);//;TODO:主播权限 所有权限

        enterRoomParam.audioCategory(0).videoRecvMode(AVRoomMulti.VIDEO_RECV_MODE_SEMI_AUTO_RECV_CAMERA_VIDEO);


        if (avContext == null) {
//            Toast.makeText(mContext, "avContext is null", Toast.LENGTH_SHORT);
            Log.e(TAG, "enterRoom avContext is null");
//            retryStartContext();
//            try {
//                Thread.sleep(500);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            enterRoom(relationId);
            return;
        }

        avContext.enterRoom(mRoomDelegate, enterRoomParam.build());
        Log.d(TAG, "enterRoom done !!!!");
//    AVRoom.Info roomInfo = new AVRoom.Info(roomType, roomId, relationId, AVRoom.AV_MODE_AUDIO, "", authBits, authBuffer, authBufferSize, audioCategory, controlRole);
//    // create room
//    avContext.enterRoom(mRoomDelegate, roomInfo);
        mIsInCreateRoom = true;
    }



这个是进入房间回调

/*
房间回调
 */
private AVRoomMulti.EventListener mRoomDelegate = new AVRoomMulti.EventListener() {


    // 创建房间成功回调
    public void onEnterRoomComplete(int result) {
        Log.d(TAG, "WL_DEBUG mRoomDelegate.onEnterRoomComplete result = " + result);
        mIsInCreateRoom = false;
        mContext.sendBroadcast(new Intent(Util.ACTION_ROOM_CREATE_COMPLETE).putExtra(Util.EXTRA_AV_ERROR_RESULT, result));
    }

    // 离开房间成功回调
    public void onExitRoomComplete() {

        mIsInCloseRoom = false;
        mMemberList.clear();
        mContext.sendBroadcast(new Intent(Util.ACTION_CLOSE_ROOM_COMPLETE));
    }

    @Override
    public void onRoomDisconnect(int i) {
        mIsInCloseRoom = false;
        mMemberList.clear();
        mContext.sendBroadcast(new Intent(Util.ACTION_CLOSE_ROOM_COMPLETE));
    }

    @Override
    public void onEndpointsUpdateInfo(int i, String[] strings) {

    }
    @Override
    public void onPrivilegeDiffNotify(int privilege) {
        Log.d(TAG, "OnPrivilegeDiffNotify. privilege = " + privilege);
    }

    @Override
    public void onSemiAutoRecvCameraVideo(String[] strings) {

    }

    @Override
    public void onCameraSettingNotify(int i, int i1, int i2) {

    }

    @Override
    public void onRoomEvent(int i, int i1, Object o) {

    }
};

之后创建聊天室

createGroup();

/**
 * IMSDK创建聊天室
 */

private void createGroup() {
   Log.d(TAG, "CreateRoomStep 4 : Create IMChatRoom");
   ArrayList<String> list = new ArrayList<String>();
   list.add(mSelfUserInfo.getUserPhone());
   TIMGroupManager.getInstance().createGroup("ChatRoom", list,
         etContent.getText().toString(), new TIMValueCallBack<String>() {
            @Override
            public void onError(int i, String s) {
               Log.e(TAG, "create group failed: " + i + " :" + s);
               Toast.makeText(ctx, "创建群失败:" + i + ":" + s,
                     Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onSuccess(String s) {
               Log.d(TAG, "create group succ: " + s);
               groupid = s;

               PreferenceUtil
                     .putString(ReleaseLiveActivity.this, "liveChatId", s);

               ctx.sendBroadcast(new Intent(
                     Util.ACTION_CREATE_GROUP_ID_COMPLETE));
            }
         });
}

同步个人信息到后台

/**
 * 同步个人信息到后台
 */
public void createLive() {
   Log.i(TAG, "CreateRoomStep 5: upload info to user server ");
   new Thread() {
      @Override
      public void run() {
         super.run();
         JSONObject object = new JSONObject();
         try {
            object.put(Util.EXTRA_ROOM_NUM, roomNum);
            object.put(Util.EXTRA_USER_PHONE, userPhone);
            object.put(Util.EXTRA_LIVE_TITLE, mLiveTitleString);
            object.put(Util.EXTRA_GROUP_ID, groupid);
            object.put("imagetype", 2);
            Log.d(TAG, "userServer testhere " + roomNum + " phone "
                  + userPhone + " roomtitle " + mLiveTitleString);
            System.out.println(object.toString());
            ImageUtil tool = new ImageUtil();
            mQavsdkApplication.setRoomName(mLiveTitleString);
            mQavsdkApplication.setRoomCoverPath(coverPath);
            Log.i(TAG,
                  "CreateRoomStep 5.1: upload info to user server begin");

         } catch (JSONException e) {
            e.printStackTrace();
         }
      }
   }.start();
   mmHandler.sendEmptyMessage(MSG_CREATEROOM_SERVER_OK);
}
       

 最后是跳转到AVActivity中去跳入到下一个页面中去

//调到AvActivity中去
startActivityForResult(new Intent(ReleaseLiveActivity.this,
      AvActivity.class)
     );
finish();

 6,至此 准备工作已经完成 接下来是直播了把


      /

/

/

        7,在AVActivity中实现:

        首先在这个页面里面 一开始的时候 你会发现很突然的就调用摄像头 很突然的就可能一些东西就出来,完全找不到这些调用的头

        1,初始化QavsdkControl

if (mQavsdkControl.getAVContext() != null) {
   mQavsdkControl.onCreate((Application) getApplication(),
         findViewById(android.R.id.content));
} else {
   finish();
}
 


      2,其实初始化QavsdkControl的里面是这样子的  初始化了好几个control,其中 重点介绍初始化mAVUIControl

public void onCreate(Context context, View contentView) {
    mAVUIControl = new AVUIControl(context, contentView.findViewById(R.id.av_video_layer_ui));
    mAVVideoControl.initAVVideo();
    mAVAudioControl.initAVAudio();
}

   

      3这里是初始化AVUIControl的类

public AVUIControl(Context context, View rootView) {
    mContext = context;
    mRootView = rootView;
    mGraphicRenderMgr = GraphicRendererMgr.getInstance();
    initQQGlView();
    initCameraPreview();
    initVideoParam();
    mapViewAndIdentifier = new HashMap();
    mQavsdkControl = ((Application) mContext).getQavsdkControl();
}



       4,这个里面 你要注意这个方法

  void initCameraPreview() {

//    SurfaceView localVideo = (SurfaceView) mRootView.findViewById(R.id.av_video_surfaceView);
//    SurfaceHolder holder = localVideo.getHolder();
//    holder.addCallback(mSurfaceHolderListener);
//    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 3.0以下必须在初始化时调用,否则不能启动预览
//    localVideo.setZOrderMediaOverlay(true);
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        layoutParams.width = 1;
        layoutParams.height = 1;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        // layoutParams.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
        layoutParams.format = PixelFormat.TRANSLUCENT;
        layoutParams.windowAnimations = 0;// android.R.style.Animation_Toast;
        layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        //layoutParams.setTitle("Toast");
        try {
            mSurfaceView = new SurfaceView(mContext);
            SurfaceHolder holder = mSurfaceView.getHolder();
            holder.addCallback(mSurfaceHolderListener);
            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 3.0以下必须在初始化时调用,否则不能启动预览
            mSurfaceView.setZOrderMediaOverlay(true);
            windowManager.addView(mSurfaceView, layoutParams);
        } catch (IllegalStateException e) {
            windowManager.updateViewLayout(mSurfaceView, layoutParams);
            if (QLog.isColorLevel()) {
                QLog.d(TAG, QLog.CLR, "add camera surface view fail: IllegalStateException." + e);
            }
        } catch (Exception e) {
            if (QLog.isColorLevel()) {
                QLog.d(TAG, QLog.CLR, "add camera surface view fail." + e);
            }
        }
        Log.i(TAG, "initCameraPreview");
    }


         

           5,其中

holder.addCallback(mSurfaceHolderListener);

            这个方法是创建摄像头的接口回调,它的回调接口为

private SurfaceHolder.Callback mSurfaceHolderListener = new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mContext.sendBroadcast(new Intent(Util.ACTION_SURFACE_CREATED));
        mCameraSurfaceCreated = true;

        QavsdkControl qavsdk = ((Application) mContext).getQavsdkControl();

            qavsdk.getAVContext().setRenderMgrAndHolder(mGraphicRenderMgr, holder);

        Log.e("memoryLeak", "memoryLeak surfaceCreated");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (holder.getSurface() == null) {
            return;
        }
        holder.setFixedSize(width, height);
        Log.e("memoryLeak", "memoryLeak surfaceChanged");
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.e("memoryLeak", "memoryLeak surfaceDestroyed");
    }
};



       这个创建按摄像头成功的回调成功的方法中 发送广播进行下面的操作

if (action.equals(Util.ACTION_SURFACE_CREATED)) {
   locateCameraPreview();
   wakeLock.acquire();
   // mQavsdkControl.toggleEnableCamera();
   if (mSelfUserInfo.isCreater() == true) {
      initTIMGroup();
      mEditTextInputMsg.setClickable(true);
      mIsSuccess = true;
      mVideoTimer = new Timer(true);

      mVideoTimerTask = new VideoTimerTask();
      mVideoTimer.schedule(mVideoTimerTask, 1000, 1000);
      mQavsdkControl.toggleEnableCamera();
      boolean isEnable = mQavsdkControl.getIsEnableCamera();
      refreshCameraUI();
      if (mOnOffCameraErrorCode != AVError.AV_OK) {
         showDialog(isEnable ? DIALOG_OFF_CAMERA_FAILED
               : DIALOG_ON_CAMERA_FAILED);
         mQavsdkControl.setIsInOnOffCamera(false);
         refreshCameraUI();
      }
      // Log.d(TAG, "getMemberInfo isHandleMemberRoomSuccess " +
      // mQavsdkApplication.isHandleMemberRoomSuccess());

      // if (mQavsdkApplication.isHandleMemberRoomSuccess()) {
      // Log.d(TAG, "getMemberInfo isHandleMemberRoomSuccess " +
      // mQavsdkApplication.isHandleMemberRoomSuccess() +
      // " yes  do it normally ");
      mHandler.sendEmptyMessageDelayed(GET_ROOM_INFO, 0);
      // } else {
      // Log.w(TAG, "getMemberInfo isHandleMemberRoomSuccess " +
      // mQavsdkApplication.isHandleMemberRoomSuccess() +
      // " no wait for call");
      // }
      mQavsdkControl.setRequestCount(0);
      // 上报主播心跳
      mHeartClickTimer.schedule(mHeartClickTask, 1000, 10000);
   } 

        这样 直播可以实现了,

这个里面 其中

  mVideoTimer = new Timer(true);

      mVideoTimerTask = new VideoTimerTask();
      mVideoTimer.schedule(mVideoTimerTask, 1000, 1000);
  这个方法 是研究的 时间的


这个方法

 initTIMGroup();

里面的代码如下

private void initTIMGroup() {
   Log.d(TAG, "initTIMGroup groupId" + groupId);
   if (groupId != null) {
      mConversation = TIMManager.getInstance().getConversation(
            TIMConversationType.Group, groupId);

      Log.d(TAG, "initTIMGroup mConversation" + mConversation);
   } else {

   }
   mSystemConversation = TIMManager.getInstance().getConversation(
         TIMConversationType.System, "");

   mArrayListChatEntity = new ArrayList<ChatEntity>();
   mChatMsgListAdapter = new ChatMsgListAdapter(this,
         mArrayListChatEntity, mMemberList, mSelfUserInfo);
   mListViewMsgItems.setAdapter(mChatMsgListAdapter);
   if (mListViewMsgItems.getCount() > 1)
      mListViewMsgItems.setSelection(mListViewMsgItems.getCount() - 1);
   mListViewMsgItems.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
         hideMsgIputKeyboard();
         mEditTextInputMsg.setVisibility(View.VISIBLE);
         return false;
      }
   });

   mListViewMsgItems
         .setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view,
                                     int scrollState) {
               switch (scrollState) {
                  case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                     if (view.getFirstVisiblePosition() == 0
                           && !mIsLoading && bMore) {
                        bNeverLoadMore = false;
                        mIsLoading = true;
                        mLoadMsgNum += MAX_PAGE_NUM;
                        // getMessage();
                     }
                     break;
               }
            }

            @Override
            public void onScroll(AbsListView view,
                            int firstVisibleItem, int visibleItemCount,
                            int totalItemCount) {
            }
         });
   // getMessage();

   TIMManager.getInstance().addMessageListener(msgListener);

   mChatTimer = new Timer(true);
   time = System.currentTimeMillis() / 1000;
   mChatTimerTask = new ChatTimerTask();
   mChatTimer.schedule(mChatTimerTask, 8000, 2000);
}
这里面主要是研究的信息发送功能的相当于一个初始化把      

 


              当然还有一些其他别的功能 ,但是大体上这么多,其他像美颜功能 还有邀请直播功能,自行去研究...........

             笔者到公司的时候,观看端已经做好,所以这里发微博只是播放端,但是观看端的话,笔者正要研究,,,播放端比观看端逻辑代码要简单....在这里 就不叙述了

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值