You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android WebRTC本地/远程视频黑屏无音频,Firebase信令正常求助

WebRTC视频通话黑屏&音频故障排查修复

你的代码中存在多个逻辑错误和初始化缺失,导致本地/远程视频黑屏、音频无法传输,以下是具体问题和修复方案:

1. 媒体设备初始化逻辑完全颠倒

在init()方法中,你先调用setupMediaDevices(),但此时videoCapturer还未初始化,触发setupMediaDevices()开头的if (videoCapturer == null) return直接退出,音视频轨道、媒体流完全没有创建,PeerConnection也没添加任何流,对方自然收不到你的音视频,本地也没有捕获到摄像头数据。

修复:

调整init()方法的调用顺序,先初始化摄像头,再设置媒体设备:

private fun init() {
    Log.d(TAG, " videocall onCreate: here 9")

    val iceServers = arrayListOf(
            PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()
    )

    createPeerConnection(iceServers)
    // 先初始化摄像头,再设置媒体设备
    videoCapturer = createFrontCameraCapturer(if (useCamera2()) Camera2Enumerator(context) else Camera1Enumerator(false))
    setupMediaDevices()

    call()
}

同时修改setupMediaDevices()开头的判断,确保摄像头初始化失败时有明确提示:

private fun setupMediaDevices() {
    // Ensure that the videoCapturer is initialized before calling this function
    if (videoCapturer == null) {
        Log.e(TAG, "setupMediaDevices: 摄像头初始化失败,无法创建媒体流")
        onStatusChangedListener(VideoCallStatus.FAILED)
        return
    }
    // ... 剩余代码不变
}

2. SurfaceViewRenderer未绑定到VideoRenderers

你的VideoRenderers类需要通过updateViewRenders()方法接收Activity中布局的SurfaceViewRenderer实例,但代码中没有任何地方调用这个方法。如果localView和remoteView为null,所有视频帧都会被sink()丢弃,导致黑屏。

修复:

在Activity的onCreate或onStart中,调用videoCallSession.videoRenderers.updateViewRenders(localSurfaceView, remoteSurfaceView),将布局中的SurfaceViewRenderer实例传入。

3. 摄像头初始化缺少SurfaceTextureHelper

setupVideoTrack()中调用videoCapturer!!.initialize(null, context, null)时,第三个参数SurfaceTextureHelper传null,这会导致摄像头无法正确捕获视频帧,因为WebRTC需要SurfaceTextureHelper来处理摄像头的纹理数据。

修复:

创建SurfaceTextureHelper并传入初始化方法:

private fun setupVideoTrack(front: Boolean): VideoTrack? {
    val camera = if (useCamera2()) Camera2Enumerator(context) else Camera1Enumerator(false)

    videoCapturer = if (front) createFrontCameraCapturer(camera) else createBackCameraCapturer(camera)

    if (videoCapturer == null) {
        Log.e(TAG, "setupVideoTrack: 摄像头初始化失败")
        return null
    }

    // 创建SurfaceTextureHelper,使用EGL上下文
    val surfaceTextureHelper = SurfaceTextureHelper.create("CameraCaptureThread", eglBase.eglBaseContext)
    videoCapturer!!.initialize(surfaceTextureHelper, context, null)

    val videoSource = factory.createVideoSource(videoCapturer!!.isScreencast())
    // 保存videoSource到成员变量,方便后续释放
    this.videoSource = videoSource

    videoCapturer!!.startCapture(videoWidth, videoHeight, videoFPS)

    val videoRenderer = VideoSink(videoRenderers.localRenderer)
    val videoTrack = factory.createVideoTrack(VIDEO_TRACK_LABEL, videoSource)
    videoTrack?.addSink(videoRenderer)
    // 保存videoTrack到成员变量
    this.videoTrack = videoTrack

    Log.d(TAG, "createFrontCameraCapturer: 摄像头初始化完成,视频轨道创建成功")
    return videoTrack
}

4. 创建Answer时使用空MediaConstraints

在handleRemoteDescriptor()中,接收Offer后创建Answer时用了MediaConstraints()空约束,这会导致不请求接收音视频,对方的流无法被正确处理。

修复:

将创建Answer的约束改为defaultPcConstraints():

peerConnection?.createAnswer(SDPCreateCallback(this::createDescriptorCallback), defaultPcConstraints())

5. 视频源成员变量未赋值

setupVideoTrack()中创建的videoSource是局部变量,类的videoSource成员变量始终为null,导致terminate()时无法正确释放资源,可能引发内存泄漏或后续初始化问题。

修复:

在setupVideoTrack()中添加this.videoSource = videoSource,如上述修复代码所示。

6. 权限检查缺失

确保你的AndroidManifest.xml中添加了必要的权限,并且在运行时请求:

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

在Activity中,启动通话前必须检查并请求CAMERA和RECORD_AUDIO权限,否则摄像头和麦克风无法正常工作。


内容的提问来源于stack exchange,提问作者hithere

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 05:09:55