无法让agora 工作。本地端给我一个黑屏,而远程端永远连接不上

Posted

技术标签:

【中文标题】无法让agora 工作。本地端给我一个黑屏,而远程端永远连接不上【英文标题】:Can't get agora to work. The local side gives me a black screen and the remote side never connects 【发布时间】:2021-01-12 01:29:34 【问题描述】:

我同时在手机和模拟器上运行 android Studio 中的代码。 两者都具有适当的权限。我检查了系统设置。

这是我制作的 AgoraVideoChat 类:

package com.example.dnaire.camera

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.graphics.PorterDuff
import android.os.Bundle
import android.util.Log
import android.view.SurfaceView
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.dnaire.R
import com.example.dnaire.databinding.VideoCallingBinding
import com.example.dnaire.firebase.firebase
import com.google.firebase.database.FirebaseDatabase
import io.agora.rtc.IRtcEngineEventHandler
import io.agora.rtc.RtcEngine
import io.agora.rtc.video.VideoCanvas
import io.agora.rtc.video.VideoEncoderConfiguration

class VideoChatViewActivity: AppCompatActivity() 
    lateinit var binding : VideoCallingBinding
    private var mRtcEngine: RtcEngine? = null

    companion object 
        private val LOG_TAG = VideoChatViewActivity::class.java.simpleName
        private val PERMISSION_REQ_ID_RECORD_AUDIO = 22
        private val PERMISSION_REQ_ID_CAMERA = PERMISSION_REQ_ID_RECORD_AUDIO + 1
    

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        binding = VideoCallingBinding.inflate(layoutInflater)
        setContentView(binding.root)

        if (checkSelfPermission(Manifest.permission.RECORD_AUDIO, PERMISSION_REQ_ID_RECORD_AUDIO) && checkSelfPermission(
                Manifest.permission.CAMERA,
                PERMISSION_REQ_ID_CAMERA
            )) 
            initAgoraEngineAndJoinChannel()
        

        firebase()
    

    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
    

    @SuppressLint("MissingSuperCall")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>, grantResults: IntArray
    ) 
        Log.i(LOG_TAG, "onRequestPermissionsResult " + grantResults[0] + " " + requestCode)

        when (requestCode) 
            PERMISSION_REQ_ID_RECORD_AUDIO -> 
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                    checkSelfPermission(Manifest.permission.CAMERA, PERMISSION_REQ_ID_CAMERA)
                 else 
                    showLongToast("No permission for " + Manifest.permission.RECORD_AUDIO)
                    finish()
                
            
            PERMISSION_REQ_ID_CAMERA -> 
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                    initAgoraEngineAndJoinChannel()
                 else 
                    showLongToast("No permission for " + Manifest.permission.CAMERA)
                    finish()
                
            
        
    

    fun showLongToast(msg: String) 
        this.runOnUiThread  Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG).show() 
    

    private fun initAgoraEngineAndJoinChannel() 
        initializeAgoraEngine()
        setupLocalVideo()
        joinChannel()
    

    private val mRtcEventHandler = object : IRtcEngineEventHandler() 

        // Listen for the onFirstRemoteVideoDecoded callback.
        // This callback occurs when the first video frame of a remote user is received and decoded after the remote user successfully joins the channel.
        // You can call the setupRemoteVideo method in this callback to set up the remote video view.
        override fun onFirstRemoteVideoDecoded(uid: Int, width: Int, height: Int, elapsed: Int) 
            Log.i("testen", "VideoChatViewActivity/onFireRemoteVideoDecoded called")
            runOnUiThread  setupRemoteVideo(uid) 
        

        // Listen for the onUserOffline callback.
        // This callback occurs when the remote user leaves the channel or drops offline.
        override fun onUserOffline(uid: Int, reason: Int) 
            runOnUiThread  onRemoteUserLeft() 
        

    

    // Initialize the RtcEngine object.
    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\n" + Log.getStackTraceString(e))
        
    

    private fun setupLocalVideo() 
        mRtcEngine!!.enableVideo()
        val container = binding.localVideoViewContainer
        val surfaceView = RtcEngine.CreateRendererView(baseContext)
        surfaceView.setZOrderMediaOverlay(true)
        container.addView(surfaceView)
        mRtcEngine!!.setupLocalVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0))
    

    private fun joinChannel() 
        Log.i("testen", "joinChannel called")
        mRtcEngine!!.joinChannel(
            "<credential>", "SeriChannel", "Extra Optional Data", 0) // if you do not specify the uid, we will generate the uid for you
    

    private fun setupRemoteVideo(uid: Int) 
        val container = binding.remoteVideoViewContainer

        if (container.childCount >= 1) 
            return
        

        val surfaceView = RtcEngine.CreateRendererView(baseContext)
        container.addView(surfaceView)
        mRtcEngine!!.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid))

        surfaceView.tag = uid // for mark purpose
        val tipMsg = binding.uidText
        tipMsg.visibility = View.GONE
    

    private fun onRemoteUserLeft() 
        val container = binding.remoteVideoViewContainer
        container.removeAllViews()

        val tipMsg = binding.uidText
        tipMsg.visibility = View.VISIBLE
    

    private fun onRemoteUserVideoMuted(uid: Int, muted: Boolean) 
        val container = binding.remoteVideoViewContainer

        val surfaceView = container.getChildAt(0) as SurfaceView

        val tag = surfaceView.tag
        if (tag != null && tag as Int == uid) 
            surfaceView.visibility = if (muted) View.GONE else View.VISIBLE
        
    

    override fun onDestroy() 
        super.onDestroy()
        leaveChannel()
        RtcEngine.destroy()
        mRtcEngine = null
    
    
    // Button ClickListeners in .xml
    private fun leaveChannel() 
        mRtcEngine!!.leaveChannel()
    

    fun onEncCallClicked(view: View) 
        finish()
    

    fun onSwitchCameraClicked(view: View) 
        mRtcEngine!!.switchCamera()
    

    fun onLocalAudioMuteClicked(view: View) 
        val iv = view as ImageView
        if (iv.isSelected) 
            iv.isSelected = false
            iv.clearColorFilter()
         else 
            iv.isSelected = true
            iv.setColorFilter(resources.getColor(R.color.black), PorterDuff.Mode.MULTIPLY)
        
        mRtcEngine!!.muteLocalAudiostream(iv.isSelected)
    

    fun onLocalVideoMuteClicked(view: View) 
        val iv = view as ImageView
        if (iv.isSelected) 
            iv.isSelected = false
            iv.clearColorFilter()
         else 
            iv.isSelected = true
            iv.setColorFilter(resources.getColor(R.color.black), PorterDuff.Mode.MULTIPLY)
        

        mRtcEngine!!.muteLocalVideoStream(iv.isSelected)

        val container = binding.localVideoViewContainer
        val surfaceView = container.getChildAt(0) as SurfaceView
        surfaceView.setZOrderMediaOverlay(!iv.isSelected)
        surfaceView.visibility = if (iv.isSelected) View.GONE else View.VISIBLE
    


这里是对应的(不是很漂亮的)布局 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/activity_video_chat_view"
        android:layout_
        android:layout_>

        <FrameLayout
            android:id="@+id/remote_video_view_container"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="8dp"
            android:layout_
            android:layout_
            android:background="@color/remoteBackground">

            <RelativeLayout
                android:layout_
                android:layout_
                android:layout_above="@id/icon_padding">
                <ImageView
                    android:layout_
                    android:layout_
                    android:layout_centerInParent="true"
                    android:src="@drawable/icon_agora_largest" />
            </RelativeLayout>

            <RelativeLayout
                android:id="@+id/icon_padding"
                android:layout_
                android:layout_
                android:layout_alignParentBottom="true" />
        </FrameLayout>

        <TextView
            android:id="@+id/uidText"
            android:layout_
            android:layout_
            android:layout_marginBottom="32dp"
            app:layout_constraintBottom_toTopOf="@+id/control_panel"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.483"
            app:layout_constraintStart_toEndOf="@+id/local_video_view_container" />

        <FrameLayout
            android:id="@+id/local_video_view_container"
            android:layout_marginBottom="24dp"
            android:layout_
            android:layout_
            android:layout_alignParentTop="true"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:background="@color/localBackground"
            android:onClick="onLocalContainerClick"
            app:layout_constraintBottom_toTopOf="@+id/control_panel"
            app:layout_constraintStart_toStartOf="parent">

            <ImageView
                android:layout_
                android:layout_
                android:layout_gravity="center"
                android:scaleType="centerCrop"
                android:src="@drawable/icon_agora_large" />
        </FrameLayout>

        <RelativeLayout
            android:id="@+id/control_panel"
            android:layout_
            android:layout_
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="16dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent">

            <ImageView
                android:id="@+id/btn_call"
                android:layout_
                android:layout_
                android:layout_centerInParent="true"
                android:onClick="onEncCallClicked"
                android:scaleType="centerCrop"
                android:src="@drawable/btn_end_call" />

            <ImageView
                android:id="@+id/btn_switch_camera"
                android:layout_
                android:layout_
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/control_bottom_horizontal_margin"
                android:layout_toEndOf="@id/btn_call"
                android:layout_toRightOf="@id/btn_call"
                android:onClick="onSwitchCameraClicked"
                android:scaleType="centerCrop"
                android:src="@drawable/btn_switch_camera" />

            <ImageView
                android:id="@+id/btn_mute"
                android:layout_
                android:layout_
                android:layout_centerVertical="true"
                android:layout_marginRight="@dimen/control_bottom_horizontal_margin"
                android:layout_toStartOf="@id/btn_call"
                android:layout_toLeftOf="@id/btn_call"
                android:onClick="onLocalAudioMuteClicked"
                android:scaleType="centerCrop"
                android:src="@drawable/btn_unmute_normal" />

            <ImageView
                android:id="@+id/btn_video_mute"
                android:layout_
                android:layout_
                android:layout_toStartOf="@id/btn_mute"
                android:layout_marginEnd="8dp"
                android:layout_weight="20"
                android:onClick="onLocalVideoMuteClicked"
                android:scaleType="centerInside"
                android:src="@drawable/btn_voice" />


        </RelativeLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

它启动了,我可以连接而没有任何错误消息; 该活动的日志向我显示了这一点:

2021-01-12 02:21:30.175 516-516/? I/SurfaceFlinger: Display 0 HWC layers:
        type    |    handle    | flag |  format   |   source crop (l,t,r,b)    |         frame       | name 
    ------------+--------------+------+-----------+----------------------------+---------------------+------
         DEVICE | 0x75196ad080 | 0002 | RGB_565   |   0.0    0.0  600.0  600.0 |    0 1176  600 1776 | SurfaceView - com.example.dnaire/com[...]ra.VideoChatViewActivity@10775c1@0#0
         DEVICE | 0x74fbd49880 | 0000 | RGBA_8888 |   0.0    0.0 1080.0 2220.0 |    0    0 1080 2220 | com.example.dnaire/com.example.dnaire.camera.VideoChatViewActivity$_24379#0
         DEVICE | 0x75196abf00 | 0000 | RGBA_8888 |   0.0    0.0 1080.0   72.0 |    0    0 1080   72 | StatusBar$_8073#0
         DEVICE | 0x75196ad940 | 0000 | RGBA_8888 |   0.0    0.0   67.0  408.0 | 1013  328 1080  736 | com.samsung.android.app.cocktailbars[...]rservice.CocktailBarService$_13359#0
         DEVICE | 0x75196ac680 | 0000 | RGBA_8888 |   0.0    0.0 1080.0  144.0 |    0 2076 1080 2220 | NavigationBar0$_8073#0

但我不知道那有什么价值。

我添加了一张实际效果的图片。这在两种设备上完全相同。 是的,我在 Project > Edit > Generate Temp Token 下生成了一个令牌,并启用了主证书和辅助证书。

【问题讨论】:

【参考方案1】:

请务必检查以下内容:

    您正在使用资源 R.string.agora_app_id 作为您的 App ID。确保您在 xml 文件中添加了正确的 App ID。

    在您的 joinChannel 方法中,应提供的第一个参数是您的令牌,该令牌应该与您的 App ID 不同。出于开发目的,您可以对generate a temporary token 执行以下操作。将您的应用程序投入生产时,请确保setup a token server

【讨论】:

以上是关于无法让agora 工作。本地端给我一个黑屏,而远程端永远连接不上的主要内容,如果未能解决你的问题,请参考以下文章

Agora - 无法将视频 .ts 文件合并为一个视频文件

无法在本地屏幕上查看 Web 应用程序的远程源

win10远距离桌面无法使用?

vnc远程连接Linux,显示黑屏,鼠标成“X”形

如何使用 [agora.io] 接收远程流

如何让Mac,Windows可以互相远程