视频通话APP的实现(利用声网SDK)2022.7

172 阅读2分钟

视频通话APP的实现(利用声网SDK)2022.7

1.在控制台获取App_Id 及 App_secrect(APP主要证书)

2.申请token

token生成器代码:

下载地址:gitee.com/chen-zhenwe…

设置参数(appId ,appCertificate,channelName(频道名)等参数),调用 RtcTokenBuilderSample的main方法可得token

图片.png

3APP的实现

3.1集成sdk

下载sdk:docs.agora.io/cn/Video/do…

3.2

图片.png

3.3在build.gradle的dependencies添加以下代码导入jdk

implementation files('libs\agora-rtc-sdk.jar')

在build.gradle的android添加

sourceSets {
    main {
        jniLibs.srcDirs = ['../../../libs']
        jniLibs.srcDirs = ['src/main/jniLibs']
    }
}

3.4权限声明

在AndroidManifest添加以下代码

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BLUETOOTH" />

3.5Layout

以FrameLayout来作为本地预览视频的容器,RelativeLayout作为对方视频的容器

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    
    <FrameLayout
       
        android:id="@+id/local_video_view_container"
        android:layout_width="414dp"
        android:layout_height="293dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:background="#827b92"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
​
        <TextView
            android:id="@+id/tv_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
      
            android:gravity="center"
            android:text="本地预览视频"
            android:textSize="20sp" />
    </FrameLayout>
​
    <Button
        android:id="@+id/btn_call"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="call"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/local_video_view_container" />
​
    <RelativeLayout
        android:id="@+id/remote_video_view_container"
        android:layout_width="match_parent"
        android:layout_height="301dp"
        android:layout_marginTop="24dp"
        android:background="#827b92"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_call" >
​
        <TextView
            android:id="@+id/tv_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
        
            android:gravity="left"
            android:text="对方视频"
            android:textSize="20sp" />
    </RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

3.6 Activity

需要设置appid,token以及频道名

class MainActivity : AppCompatActivity() {
    private val TAG: String = MainActivity::class.java.getSimpleName()
​
    private val PERMISSION_REQ_ID = 22
    // Permission WRITE_EXTERNAL_STORAGE is not mandatory
    // for Agora RTC SDK, just in case if you wanna save
    // logs to external sdcard.
    private val REQUESTED_PERMISSIONS = arrayOf(
        Manifest.permission.RECORD_AUDIO,
        Manifest.permission.CAMERA
    )
​
    private var mRtcEngine: RtcEngine? = null
    private var mCallEnd = false
    private var mMuted = false
​
    private var mLocalContainer: FrameLayout? = null
    private var mRemoteContainer: RelativeLayout? = null
    private var mLocalVideo: VideoCanvas? = null
    private var mRemoteVideo: VideoCanvas? = null
​
    private lateinit var mCallBtn: Button
​
    private val app_id="你的appId"
    private val token="你申请的token"
    private val channelName="你的频道号"
    // Customized logger view
//    private val mLogView: LoggerRecyclerView? = null
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initUi()
​
        if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID)&&
            checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID)) {
            initEngineAndJoinChannel();//初始化rtc
        }
    }
​
    fun initUi(){
        mRemoteContainer = findViewById(R.id.remote_video_view_container)
        mCallBtn =findViewById(R.id.btn_call)
        mLocalContainer = findViewById(R.id.local_video_view_container)
​
       mCallBtn.setOnClickListener {
           onCallClicked()
       }
    }
​
    //申请权限
    private fun checkSelfPermission(permission: String, requestCode: Int): Boolean {
        if (ContextCompat.checkSelfPermission(this, permission) !==
            PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this,
                this.REQUESTED_PERMISSIONS,
                requestCode
            )
            return false
        }
        return true
    }
​
    private fun initEngineAndJoinChannel() {
        // This is our usual steps for joining
        // a channel and starting a call.
        initializeEngine()
        setupVideoConfig()
        setupLocalVideo()
        joinChannel()
    }
​
    private fun initializeEngine() {
        mRtcEngine = try {
            RtcEngine.create(baseContext, app_id, mRtcEventHandler)
        } catch (e: Exception) {
            Log.e(TAG, Log.getStackTraceString(e))
            throw RuntimeException(
                """
                        NEED TO check rtc sdk init fatal error
                        ${Log.getStackTraceString(e)}
                        """.trimIndent()
            )
        }
    }
    private val mRtcEventHandler: IRtcEngineEventHandler = object : IRtcEngineEventHandler() {
​
        override fun onJoinChannelSuccess(channel: String, uid: Int, elapsed: Int) {
           Log.d("join success " ,uid.toString() )
​
        }
​
        override fun onUserJoined(uid: Int, elapsed: Int) {
            runOnUiThread {
                Log.d("join First remote video" ,uid.toString() )
                setupRemoteVideo(uid)
            }
        }
​
​
        override fun onUserOffline(uid: Int, reason: Int) {
            runOnUiThread {
                Log.d("join User offine" ,uid.toString() )
                onRemoteUserLeft(uid)
            }
        }
    }
​
    private fun setupRemoteVideo(uid: Int) {
        var parent: ViewGroup = mRemoteContainer!!
        if (parent.indexOfChild(mLocalVideo!!.view) > -1) {
            parent = mLocalContainer!!
        }
​
​
        if (mRemoteVideo != null) {
            return
        }
​
        val view = RtcEngine.CreateRendererView(baseContext)
        view.setZOrderMediaOverlay(parent === mLocalContainer)
        parent.addView(view)
        mRemoteVideo = VideoCanvas(view, VideoCanvas.RENDER_MODE_HIDDEN, uid)
        // Initializes the video view of a remote user.
        mRtcEngine!!.setupRemoteVideo(mRemoteVideo)
    }
​
    private fun onRemoteUserLeft(uid: Int) {
        if (mRemoteVideo != null && mRemoteVideo!!.uid == uid) {
            removeFromParent(mRemoteVideo)
            // Destroys remote view
            mRemoteVideo = null
        }
    }
​
    private fun removeFromParent(canvas: VideoCanvas?): ViewGroup? {
        if (canvas != null) {
            val parent = canvas.view.parent
            if (parent != null) {
                val group = parent as ViewGroup
                group.removeView(canvas.view)
                return group
            }
        }
        return null
    }
​
    private fun setupVideoConfig() {
        // In simple use cases, we only need to enable video capturing
        // and rendering once at the initialization step.
        // Note: audio recording and playing is enabled by default.
        mRtcEngine!!.enableVideo()
​
        // Please go to this page for detailed explanation
        // https://docs.agora.io/en/Video/API%20Reference/java/classio_1_1agora_1_1rtc_1_1_rtc_engine.html#af5f4de754e2c1f493096641c5c5c1d8f
        mRtcEngine!!.setVideoEncoderConfiguration(
            VideoEncoderConfiguration(
                VideoEncoderConfiguration.VD_640x360,
                VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
                VideoEncoderConfiguration.STANDARD_BITRATE,
                VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT
            )
        )
    }
​
    private fun setupLocalVideo() {
        // This is used to set a local preview.
        // The steps setting local and remote view are very similar.
        // But note that if the local user do not have a uid or do
        // not care what the uid is, he can set his uid as ZERO.
        // Our server will assign one and return the uid via the event
        // handler callback function (onJoinChannelSuccess) after
        // joining the channel successfully.
        val view = RtcEngine.CreateRendererView(baseContext)
        view.setZOrderMediaOverlay(true)
        mLocalContainer!!.addView(view)
        // Initializes the local video view.
        // RENDER_MODE_HIDDEN: Uniformly scale the video until it fills the visible boundaries. One dimension of the video may have clipped contents.
        mLocalVideo = VideoCanvas(view, VideoCanvas.RENDER_MODE_HIDDEN, 0)
        mRtcEngine!!.setupLocalVideo(mLocalVideo)
    }
​
    private fun joinChannel() {
        // 1. Users can only see each other after they join the
        // same channel successfully using the same app id.
        // 2. One token is only valid for the channel name that
        // you use to generate this token.
        var token: String? = token
        if (TextUtils.isEmpty(token) || TextUtils.equals(token, "#YOUR ACCESS TOKEN#")) {
            token = null // default, no token
        }
        mRtcEngine!!.joinChannel(token, channelName, "Extra Optional Data", 0)
    }
​
​
    fun onCallClicked() {
        if (mCallEnd) {
            startCall()
            mCallEnd = false
//            mCallBtn.setImageResource(R.drawable.btn_endcall)
        } else {
            endCall()
            mCallEnd = true
//            mCallBtn.setImageResource(R.drawable.btn_startcall)
        }
//        showButtons(!mCallEnd)
    }
​
    private fun startCall() {
        setupLocalVideo()
        joinChannel()
    }
    private fun endCall() {
        removeFromParent(mLocalVideo)
        mLocalVideo = null
        removeFromParent(mRemoteVideo)
        mRemoteVideo = null
        leaveChannel()
    }
    private fun leaveChannel() {
        mRtcEngine!!.leaveChannel()
    }
​
​
}