无法让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 工作。本地端给我一个黑屏,而远程端永远连接不上的主要内容,如果未能解决你的问题,请参考以下文章