zoukankan      html  css  js  c++  java
  • 声网实现视频会议(二)

    实现视频通话

    本节介绍如何实现视频通话。视频通话的 API 调用时序见下图:

    1. 创建用户界面

    根据场景需要,为你的项目创建视频通话的用户界面。若已有界面,可以直接查看导入类

    我们推荐你添加如下 UI 元素来实现一个视频通话,

    • 本地视频窗口
    • 远端视频窗口
    • 结束通话按钮

    你也可以参考 Agora-Android-Tutorial-1to1 示例项目的 activity_video_chat_view.xml 文件中的代码。

    创建 UI 示例

    2. 导入类

    在项目的 Activity 文件中添加如下行:

    import io.agora.rtc.IRtcEngineEventHandler;
    import io.agora.rtc.RtcEngine;
    import io.agora.rtc.video.VideoCanvas;
    
    import io.agora.rtc.video.VideoEncoderConfiguration;
     

    3. 获取设备权限

    调用 checkSelfPermission 方法,在开启 Activity 时检查并获取 Android 移动设备的摄像头和麦克风使用权限。

    // Java
    private static final int PERMISSION_REQ_ID = 22;
    
    // App 运行时确认麦克风和摄像头设备的使用权限。
    private static final String[] REQUESTED_PERMISSIONS = {
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.CAMERA
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_chat_view);
    
        // 获取权限后,初始化 RtcEngine,并加入频道。
        if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
                checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID)) {
            initEngineAndJoinChannel();
        }
    }
    
    private void initEngineAndJoinChannel() {
         initializeEngine();
         setupLocalVideo();
         joinChannel();
    }
    
    private boolean checkSelfPermission(String permission, int requestCode) {
        if (ContextCompat.checkSelfPermission(this, permission) !=
                PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
            return false;
        }
    
        return true;
    }
     
    // Kotlin
    // app 运行时确认麦克风和摄像头的使用权限。
    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_voice_chat_view)
    
      // 获取权限后,初始化 RtcEngine,并加入频道。
      if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO) && checkSelfPermission(Manifest.permission.CAMERA, PERMISSION_REQ_ID_CAMERA)) {
        initAgoraEngineAndJoinChannel()
      }
    }
    
    private fun initAgoraEngineAndJoinChannel() {
      initializeAgoraEngine()
      setupLocalVideo()
      joinChannel()
    }
    
    private fun checkSelfPermission(permission: String, requestCode: Int): Boolean {
      Log.i(LOG_TAG, "checkSelfPermission $permission $requestCode")
      if (ContextCompat.checkSelfPermission(this, 
              permission) != PackageManager.PERMISSION_GRANTED) {
    
        ActivityCompat.requestPermissions(this,
                arrayOf(permission),
                requestCode)
        return false
      }
      return true
    }

    4. 初始化 RtcEngine

    在调用其他 Agora API 前,需要创建并初始化 RtcEngine 对象。

    将获取到的 App ID 添加到 string.xml 文件中的 agora_app_id 一栏。调用 create 方法,传入获取到的 App ID,即可初始化 RtcEngine。

    你还根据场景需要,在初始化时注册想要监听的回调事件,如本地用户加入频道,及远端用户加入频道等。注意不要在这些回调中进行 UI 操作。

    // Java
    private RtcEngine mRtcEngine;
    private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
        @Override
        // 注册 onJoinChannelSuccess 回调。
        // 本地用户成功加入频道时,会触发该回调。
        public void onJoinChannelSuccess(String channel, final int uid, int elapsed) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.i("agora","Join channel success, uid: " + (uid & 0xFFFFFFFFL));
                }
            });
        }
    
       @Override
       // 注册 onUserJoined 回调。
       // 远端用户成功加入频道时,会触发该回调。
       // 可以在该回调中调用 setupRemoteVideo 方法设置远端视图。
       public void onUserJoined(final int uid, int elapsed) {
           runOnUiThread(new Runnable() {
               @Override
               public void run() {
                   Log.i("agora","Remote user joined, uid: " + (uid & 0xFFFFFFFFL));
                   setupRemoteVideo(uid);
               }
           });
       }
    
        @Override
        // 注册 onUserOffline 回调。
        // 远端用户离开频道或掉线时,会触发该回调。
        public void onUserOffline(final int uid, int reason) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.i("agora","User offline, uid: " + (uid & 0xFFFFFFFFL));
                    onRemoteUserLeft();
                }
            });
        }
    };
    
    ...
    
    // 初始化 RtcEngine 对象。
    private void initializeEngine() {
        try {
            mRtcEngine = RtcEngine.create(getBaseContext(), getString(R.string.agora_app_id), mRtcEventHandler);
        } catch (Exception e) {
            Log.e(TAG, Log.getStackTraceString(e));
            throw new RuntimeException("NEED TO check rtc sdk init fatal error
    " + Log.getStackTraceString(e));
        }
    }
     
    // Kotlin
    private var mRtcEngine: RtcEngine? = null
    private val mRtcEventHandler = object : IRtcEngineEventHandler() {
    
      // 注册 onUserJoined 回调。
      // 远端用户成功加入频道时,会触发该回调。
      // 可以在该回调用调用 setupRemoteVideo 方法设置远端视图。
      override fun onUserJoined(uid: Int, elapsed: Int) {
        runOnUiThread { setupRemoteVideo(uid) }
      }
    
      // 注册 onUserOffline 回调。远端用户离开频道后,会触发该回调。
      override fun onUserOffline(uid: Int, reason: Int) {
        runOnUiThread { onRemoteUserLeft() }
      }
    
    }
    
    ...
    
    // 调用 Agora SDK 的方法初始化 RtcEngine。
    private fun initializeAgoraEngine() {
      try {
        mRtcEngine = RtcEngine.create(baseContext, getString(R.string.agora_app_id), mRtcEventHandler)
      } catch (e: Exception) {
        Log.e(LOG_TAG, Log.getStackTraceString(e))
    
        throw RuntimeException("NEED TO check rtc sdk init fatal error
    " + Log.getStackTraceString(e))
      }
    }

    5. 设置本地视图

    成功初始化 RtcEngine 对象后,需要在加入频道前设置本地视图,以便在通话中看到本地图像。参考以下步骤设置本地视图:

    • 调用 enableVideo 方法启用视频模块。
    • 调用 createRendererView 方法创建一个 SurfaceView 对象。
    • 调用 setupLocalVideo 方法设置本地视图。
    // Java
    private void setupLocalVideo() {
    
        // 启用视频模块。
        mRtcEngine.enableVideo();
    
        // 创建 SurfaceView 对象。
        private FrameLayout mLocalContainer;
        private SurfaceView mLocalView;
    
        mLocalView = RtcEngine.CreateRendererView(getBaseContext());
        mLocalView.setZOrderMediaOverlay(true);
        mLocalContainer.addView(mLocalView);
        // 设置本地视图。
        VideoCanvas localVideoCanvas = new VideoCanvas(mLocalView, VideoCanvas.RENDER_MODE_HIDDEN, 0);
        mRtcEngine.setupLocalVideo(localVideoCanvas);
    }
     
    // Kotlin
    private fun setupLocalVideo() {
    
      // 启用视频模块。
      mRtcEngine!!.enableVideo()
    
      val container = findViewById(R.id.local_video_view_container) as FrameLayout
    
      // 创建 SurfaceView。
      val surfaceView = RtcEngine.createRendererView(baseContext)
      surfaceView.setZorderMediaOverlay(true)
      container.addView(surfaceView)
      // 设置本地视图。
      mRtcEngine!!.setupLocalVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0))
    }

    6. 加入频道

    完成初始化和设置本地视图后(视频通话场景),你就可以调用 joinChannel 方法加入频道。你需要在该方法中传入如下参数:

    • token:传入用于鉴权的 Token,可设为如下一个值:

      • 临时 Token。你可以在控制台里生成一个临时 Token,详见获取临时 Token。加入频道时,请确保填入的频道名和生成临时 Token 时填入的频道名一致。
      • 在你的服务器端生成的 Token。在安全要求高的场景下,我们推荐你使用此种方式生成的 Token,详见从服务端生成 Token。加入频道时,请确保填入的频道名和 uid 与生成 Token 时填入的频道名和 uid 一致。
      • 若项目已启用 App 证书,请使用 Token。
      • 请勿将 token 设为 ""。
    • channelName:传入能标识频道的频道 ID。App ID 相同、频道 ID 相同的用户会进入同一个频道。

    • uid: 本地用户的 ID。数据类型为整型,且频道内每个用户的 uid 必须是唯一的。若将 uid 设为 0,则 SDK 会自动分配一个 uid,并在 onJoinChannelSuccess 回调中报告。

      用户成功加入频道后,会默认订阅频道内其他所有用户的音频流和视频流,因此产生用量并影响计费。如果想取消订阅,可以通过调用相应的 mute 方法实现。

    更多的参数设置注意事项请参考 joinChannel 接口中的参数描述。

    // Java
    private void joinChannel() {
    
        // 调用 joinChannel 方法 加入频道。
        mRtcEngine.joinChannel(YOUR_TOKEN, "demoChannel1", "Extra Optional Data", 0);
    }
     
    // Kotlin
    private fun joinChannel() {
    
      // 调用 joinChannel 方法加入频道。
      mRtcEngine!!.joinChannel(token, "demoChannel1", "Extra Optional Data", 0)
    }

    7. 设置远端视图

    视频通话中,通常你也需要看到其他用户。在加入频道后,可通过调用 setupRemoteVideo 方法设置远端用户的视图。远端视图和本地视图的区别就是需要设置远端用户的 UID。

    远端用户成功加入频道后,SDK 会触发 onUserJoined 回调,该回调中会包含这个远端用户的 uid 信息。在该回调中调用 setupRemoteVideo 方法,传入获取到的 uid,设置远端用户的视图。

    // Java
        @Override
        // 监听 onUserJoined 回调。
        // 远端用户加入频道时,会触发该回调。
        // 可以在该回调中调用 setupRemoteVideo 方法设置远端视图。
        public void onUserJoined(final int uid, int elapsed) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.i("agora","Remote user joined, uid: " + (uid & 0xFFFFFFFFL));
                    setupRemoteVideo(uid);
                }
            });
        }
    
    private void setupRemoteVideo(int uid) {
    
        // 创建一个 SurfaceView 对象。
        private RelativeLayout mRemoteContainer;
        private SurfaceView mRemoteView;
    
        mRemoteView = RtcEngine.CreateRendererView(getBaseContext());
        mRemoteContainer.addView(mRemoteView);
        // 设置远端视图。
        mRtcEngine.setupRemoteVideo(new VideoCanvas(mRemoteView, VideoCanvas.RENDER_MODE_HIDDEN, uid));
    
    }
     
    // Kotlin  
      // 注册 onUserJoined 回调。
      // 远端用户加入频道时,会触发该回调。
      // 可以在该回调用调用 setupRemoteVideo 方法设置远端视图。
      override fun onUserJoined(uid: Int, elapsed: Int) {
        runOnUiThread { setupRemoteVideo(uid) }
      }
    
    
    private fun setupRemoteVideo(uid: Int) {
      val container = findViewById(R.id.remote_video_view_container) as FrameLayout
    
      if (container.childCount >= 1) {
        return
      }
    
      // 创建一个 SurfaceView 对象。
      val surfaceView = RtcEngine.CreateRendererView(baseContext)
      container.addView(surfaceView)
    
      // 设置远端视图。
      mRtcEngine!!.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid))
    }

    8. 更多步骤

    你还可以在通话中参考如下代码实现更多功能及场景。

    停止发送本地音频流切换前后摄像头

    9. 离开频道

    根据场景需要,如结束通话、关闭 App 或 App 切换至后台时,调用 leaveChannel 离开当前通话频道。

    // Java
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (!mCallEnd) {
            leaveChannel();
        }
        RtcEngine.destroy();
    }
    
    private void leaveChannel() {
        // 离开当前频道。
        mRtcEngine.leaveChannel();
    }
     
    // Kotlin
    override fun onDestroy() {
      super.onDestroy()
    
      leaveChannel()
      RtcEngine.destroy()
      mRtcEngine = null
    }
    
    private fun leaveChannel() {
      // 离开当前频道。
      mRtcEngine!!.leaveChannel()
    }
     

    示例代码

    你可以在 Agora-Android-Tutorial-1to1 示例项目的 VideoChatViewActivity.java 文件中查看完整的源码和代码逻辑。

    运行项目

    在 Android 设备中运行该项目。当成功开始视频通话时,你可以同时看到本地和远端的视图。

  • 相关阅读:
    事件委托
    关于瀑布流布局的一些想法
    关于鼠标坐标点的小事
    关于offset,client,scroll的认识
    松下抛却Jungle掌机研发项目
    投行麦格理维持百度股票“跑赢大盘”评级
    任地狱新一代掌机3DS出售2天销量达37万台
    淘宝称与500权利人协作删除侵权商品13万件
    麦考林将于3月2日宣布2010第四季度及全年财报
    美国将投资107亿美元打造4G无线收集
  • 原文地址:https://www.cnblogs.com/wangdayang/p/14913526.html
Copyright © 2011-2022 走看看