Android WebRTC远程流未显示在SurfaceView上,得到0帧

问题描述

我正在尝试将本地网络摄像头从计算机上的网页本地传输到Android应用(本机WebRTC)。我将WebRTC用于对等连接,将NodeJS与Socket.io用于信令。当我启动视频流时,所有sdp似乎都已正确设置,但是在我的Andorid表面视图上没有轨道播放。我得到的只是一个黑屏,该控制台输出

2020-08-19 13:23:19.667 30492-31575/com.example.demowebrtcclient I/Org.webrtc.Logging: Eglrenderer: video_viewDuration: 4050 ms. Frames received: 0. Dropped: 0. Rendered: 0. Render fps: .0. Average render time: NA. Average swapBuffer time: NA.

这是我的代码,抱歉,有太多我只是不知道问题的根源在何处。

对等1(网页,JS)

 navigator.mediaDevices.getUserMedia({audio: true,video: true})
    .then(function(s) {
      stream = s;
      video.srcObject = stream;
      video.play();
    });

  function startStream() {

    socket.emit('broadcaster');
  }

  socket.on('answer',function(id,description) {
    let RTCDescription = description;

    if (isAndroid) {
      RTCDescription = new RTCSessionDescription();
      RTCDescription.sdp = description;
      RTCDescription.type = "answer";
      console.log("Answer received");
    }

    console.log(RTCDescription);
    peerConnection.setRemoteDescription(RTCDescription); 
  });

  socket.on('watcher',function(id) {
    peerConnection = new RTCPeerConnection(config);
    // peerConnections[id] = peerConnection;
    stream.getTracks().forEach(track => peerConnection.addTrack(track,stream));
    peerConnection.createOffer()
    .then(function(sdp) {
      peerConnection.setLocalDescription(sdp);
    }) 
    .then(function() {
      socket.emit('offer',id,peerConnection.localDescription);
    })
    peerConnection.onicecandidate = function(event) {
      if (event.candidate) {
        socket.emit('candidate',event.candidate);
      }
    };
  });

  socket.on('candidate',candidate) {
    console.log("Candidate recieved");
    peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
  });

  socket.on('close',function(id) {
    peerConnection.close();
    delete peerConnection;
  });

Peer 2(Android,Java)

private void initializePeerConnectionFactory() {
    PeerConnectionFactory.Initializationoptions initOptions = PeerConnectionFactory.Initializationoptions.builder(getApplicationContext())
        .setEnableInternalTracer(true)
        .setFieldTrials("WebRTC-H264HighProfile/Enabled/")
        .createInitializationoptions();

    PeerConnectionFactory.initialize(initOptions);
    VideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(rootEglBaseContext,/* enableIntelVp8Encoder */true,/* enableH264HighProfile */true);
    VideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBaseContext);

    PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
    options.disableEncryption = true;
    options.disableNetworkMonitor = true;

    factory = PeerConnectionFactory.builder()
            .setVideoEncoderFactory(defaultVideoEncoderFactory)
            .setVideoDecoderFactory(defaultVideoDecoderFactory)
            .setoptions(options)
            .createPeerConnectionFactory();
}



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    rootEglBase = EglBase.create();
    rootEglBaseContext = rootEglBase.getEglBaseContext();
    videoView = findViewById(R.id.video_view);
    videoView.setMirror(true);
    videoView.setEnableHardwareScaler(true);
    videoView.init(rootEglBaseContext,null);

    videosink = new ProxyVideosink();

    iceServers = new ArrayList<>();

    stunServer = (PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
    iceServers.add(stunServer);



    executor = Executors.newSingleThreadScheduledExecutor();

    mediaConstraints = new MediaConstraints();
    mSocket.on(Socket.EVENT_CONNECT,onConnect);
    mSocket.on("offer",handleOffer);
    mSocket.on("broadcaster",onbroadcast);
    mSocket.on("candidate",onCandidate);

    mSocket.connect();

    initializePeerConnectionFactory();
}


private Emitter.Listener handleOffer = new Emitter.Listener() {
    @Override
    public void call(final Object... args) {
        Log.d("socket","Offer recieved");
        peerConnection = factory.createPeerConnection(iceServers,observer);
        String id = (String) args[0];
        JSONObject data = (JSONObject) args[1];
        peerConnection.setRemoteDescription(new SdpAdapter("setremote") {
            @Override
            public void onSetSuccess() {
                peerConnection.createAnswer(new SdpAdapter("createanswer") {
                    @Override
                    public void onCreateSuccess(final SessionDescription sdp) {
                        super.onCreateSuccess(sdp);
                        Log.d("socket","Session description " + sdp.toString() + " created");
                        SdpAdapter local = new SdpAdapter("setlocal") {
                            @Override
                            public void onSetSuccess() {
                                super.onSetSuccess();
                                mSocket.emit("answer",peerConnection.getLocalDescription().description);
                            }
                        };
                        peerConnection.setLocalDescription(local,sdp);
                    }
                },mediaConstraints);
            }
        },new SessionDescription(SessionDescription.Type.OFFER,data.optString("sdp")));
    }
};


private Emitter.Listener onConnect = new Emitter.Listener() {
    @Override
    public void call(final Object... args) {
        Log.d("socket","Socket connected");
        mSocket.emit("watcher");
    }
};

private Emitter.Listener onbroadcast = new Emitter.Listener() {
    @Override
    public void call(final Object... args) {
        Log.d("socket","Received broadcast request");
        mSocket.emit("watcher");
    }
};

private Emitter.Listener onCandidate = new Emitter.Listener() {
    @Override
    public void call(final Object... args) {
        Log.d("ice","New ice candidate recieved");

        JSONObject data = (JSONObject) args[1];
        IceCandidate candidate = new IceCandidate(data.optString("sdpMid"),Integer.parseInt(data.optString("sdpMLineIndex")),data.optString("candidate"));
        peerConnection.addIceCandidate(candidate);
    }
};

public class SdpAdapter implements SdpObserver {
    private String name;

    public SdpAdapter(String name) {
        this.name = name;
    }

    @Override
    public void onCreateSuccess(SessionDescription sessionDescription) {
        Log.d("socket","SDP create success for " + name );
    }

    @Override
    public void onSetSuccess() {
        Log.d("socket","SDP set success for " + name);
    }

    @Override
    public void onCreateFailure(String s) {
        Log.d("socket","SDP create failure for" + s);
    }

    @Override
    public void onSetFailure(String s) {
        Log.d("socket","SDP set failure for" + s);
    }
};


public static class ProxyVideosink implements Videosink {
    private Videosink mTarget;
    @Override
    synchronized public void onFrame(VideoFrame frame) {
        if (mTarget == null) {
            Log.d("socket","Dropping frame in proxy because target is null.");
            return;
        }
        mTarget.onFrame(frame);
    }
    synchronized void setTarget(Videosink target) {
        this.mTarget = target;
    }
}

Observer observer = new Observer() {
    @Override
    public void onIceCandidate(IceCandidate iceCandidate) {
        Log.d("ice",iceCandidate.toString());
        mSocket.emit("candidate",iceCandidate);

    }

    @Override
    public void onAddStream(MediaStream stream) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                Log.d("socket","Stream added");
                Log.d("tracks","Getting  tracks");
                VideoTrack remoteVideoTrack = (VideoTrack)stream.videoTracks.get(0);
                AudioTrack remoteAudioTrack = (AudioTrack)stream.audioTracks.get(0);
                Log.d("tracks","Enabling tracks");
                remoteAudioTrack.setEnabled(true);
                remoteVideoTrack.setEnabled(true);
                Log.d("tracks","Adding sink");
                videosink.setTarget(videoView);
                remoteVideoTrack.addSink(videosink);
                peerConnection.getStats(reports -> {
                    for (StatsReport report : reports) {
                        Log.d("Stats","Stats: " + report.toString());
                    }
                },null);



            }
        });
    }



};

}

解决方法

在 Android 中禁用加密会破坏与浏览器的互操作性,除非您以特殊(不安全)模式启动它们。

options.disableEncryption = true;

开始使用 Android 原生 WebRTC API 的一个好点是查看示例应用代码: https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java

我会建议添加更多日志并扩展描述/问题。

调试 WebRTC 问题时的推荐程序:

  1. ICE 成功了吗? (iceConnectionState 已连接)如果没有,请查看收集和接收的候选人。检查涉及的防火墙配置、NAT 和 TURN。
  2. 您是否收到数据包但无法渲染任何帧?检查您的编解码器和编码器/解码器。