我正在尝试将我的网络摄像头从计算机上的网页本地流式传输到 Android 应用程序(本机 WebRTC)。我使用 WebRTC 进行对等连接,使用带有 Socket.io 的 NodeJS 进行信号传输。当我启动视频流时,所有 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.
这是我的代码,对不起,我只是不知道问题的根源在哪里。
Peer 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', id, event.candidate);
}
};
});
socket.on('candidate', function(id, candidate) {
console.log("Candidate recieved");
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on('close', function(id) {
peerConnection.close();
delete peerConnection;
});
对等 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" , id, 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);
}
});
}
};
}