ヴィヴェックChanddru、2017年7月15日により、
ビデオ通話をピアツーピアはいつかのために怒りの上にあり、現在、すべてのアプリが作り付けの呼び出しオーディオ/ビデオを持っています。このチュートリアルシリーズは、すべての初心者のための簡単で理解しやすい方法で、このような機能を提供する可能性を探求についてです。
このチュートリアルシリーズは緩く基づいているのWebRTCのためCodelabs。
一連のこの部分は、主に基づいている、ステップ2、我々は、同じデバイス内の2つのピア間でのオーディオおよびビデオデータを転送するためのWebRTCでPeerConnectionを使用する上記のコードラボ。
これは、パート3「シリーズのAndroid用のWebRTC入門」、あなたはこの記事に新しいしている場合は、必ずこの部分を続行する前に、このシリーズの前の部分を読んで持っていることを確認してください。
パート1:のWebRTC入門
PeerConnection入門:パート2
パート3:ピアツーピアビデオコーリング-ループバック(この記事)
パート4:ピアツーピアビデオsocket.ioで呼び出します
コンセプト
米国は、以下のシナリオで考えてみましょう私たちの勇敢な少年が(、,!コースの提案する)レディで通話したいWHERE
素晴らしいビデオとオーディオ機能を呼び出す提供ANアプリを使用するために彼の計画の。彼は彼が提案するとき、彼女の反応(AWWWを)見たいのは...次のステップで接続のピアの両端ATで起こる
、画像を保存することが推奨され、セキュリティチェーン機構を有していてもよいソースステーション、チェーン外画像ダンプが失敗した[ダウン直接アップロード(IMG-TqhergqL-1583665359573)( https://でミロ.medium.com /最大/ 1分の2288 * X4iOI4qIKwoC8oK7AFdr6w.jpeg)]
- 最初は、我々のアプリは、ピア接続とオファーSDPを作成します。このオファーは、呼び出し元のピアに関するデータが含まれ、コーデックとピアの他のエンティティを識別するために使用されます。
- このオファーは、その後の呼び出しのピアで「ローカル説明」として保存され、その後、いくつかのシグナル伝達メカニズムを介して呼び出し先に上で送信される(一般的には、システムのほとんどは、シグナリング媒体としてWebSocketを使用しています。それはあなたの使用状況や要件に応じて異なる場合があります) 。
- 呼び出し先側で我々のアプリを提示申し出を受信すると、それが確立されるコールがあることを知っています。それは、「リモート説明」と「プラン」を記憶し、対応する「回答」SDPを作成します。
- この回答SDPは、それがそのピアに詳細特定を指摘することで、発信者のオファーSDPに似ています。
- 被呼側のアプリケーションは、店舗の「ローカル説明」として、この「アンサーSDP」とは、呼び出し元に、シグナリングチャネルを通してそれを超える送信します。
- 呼び出し側は、この答えは、その「リモート説明」として保存し、それを受け取ります。
- 呼び出し元と呼び出し先は、転送します アイス候補シグナリングチャネルを介してそれらに関係します。これらの候補を受信すると、ピアはそのPeerConnectionインスタンスにこれらの候補を追加します。
- アイス候補の転送が完了すると、ピアは、効果的にそれらの間でメディアデータを転送する方法を知っています。メディアの転送はRTPを通じて発生してのWebRTCフレームワークによって世話をしています。
ループバックPeerConnection
あなたが見ていた場合はステップ2のWebRTCのcodelabsのを、あなたは、彼らが呼び出し元と呼び出し先の間でループを作成していることに気づくかもしれません。
単一のデバイスは、ローカルピアとそれによって提供し、応答部分の両方を行うリモートピアの両方として作用します。それは任意の実用的な使用法を持っていませんが、どのように詰め込む作業の深い理解を持っているために、このステップを通過することをお勧めします。
それが実装されている方法を理解するために、次のコードを見てみましょう。
public void start() {
//Initialize PeerConnectionFactory globals.
//Params are context, initAudio,initVideo and videoCodecHwAcceleration
PeerConnectionFactory.initializeAndroidGlobals(this, true, true, true);
//Create a new PeerConnectionFactory instance.
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
peerConnectionFactory = new PeerConnectionFactory(options);
//Now create a VideoCapturer instance. Callback methods are there if you want to do something! Duh!
VideoCapturer videoCapturerAndroid = getVideoCapturer(new CustomCameraEventsHandler());
//Create MediaConstraints - Will be useful for specifying video and audio constraints.
audioConstraints = new MediaConstraints();
videoConstraints = new MediaConstraints();
//Create a VideoSource instance
videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid, videoConstraints);
localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
//create an AudioSource instance
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
localVideoView.setVisibility(View.VISIBLE);
//create a videoRenderer based on SurfaceViewRenderer instance
localRenderer = new VideoRenderer(localVideoView);
// And finally, with our VideoRenderer ready, we
// can add our renderer to the VideoTrack.
localVideoTrack.addRenderer(localRenderer);
}
private void call() {
//we already have video and audio tracks. Now create peerconnections
List<PeerConnection.IceServer> iceServers = new ArrayList<>();
//create sdpConstraints
sdpConstraints = new MediaConstraints();
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
//creating localPeer
localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("localPeerCreation") {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(localPeer, iceCandidate);
}
});
//creating remotePeer
remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("remotePeerCreation") {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(remotePeer, iceCandidate);
}
@Override
public void onAddStream(MediaStream mediaStream) {
super.onAddStream(mediaStream);
gotRemoteStream(mediaStream);
}
});
//creating local mediastream
MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
stream.addTrack(localAudioTrack);
stream.addTrack(localVideoTrack);
localPeer.addStream(stream);
//creating Offer
localPeer.createOffer(new CustomSdpObserver("localCreateOffer"){
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
//we have localOffer. Set it as local desc for localpeer and remote desc for remote peer.
//try to create answer from the remote peer.
super.onCreateSuccess(sessionDescription);
localPeer.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
remotePeer.setRemoteDescription(new CustomSdpObserver("remoteSetRemoteDesc"), sessionDescription);
remotePeer.createAnswer(new CustomSdpObserver("remoteCreateOffer") {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
//remote answer generated. Now set it as local desc for remote peer and remote desc for local peer.
super.onCreateSuccess(sessionDescription);
remotePeer.setLocalDescription(new CustomSdpObserver("remoteSetLocalDesc"), sessionDescription);
localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemoteDesc"), sessionDescription);
}
},new MediaConstraints());
}
},sdpConstraints);
}
private void hangup() {
localPeer.close();
remotePeer.close();
localPeer = null;
remotePeer = null;
}
private void gotRemoteStream(MediaStream stream) {
//we have remote video stream. add to the renderer.
final VideoTrack videoTrack = stream.videoTracks.getFirst();
AudioTrack audioTrack = stream.audioTracks.getFirst();
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
remoteRenderer = new VideoRenderer(remoteVideoView);
remoteVideoView.setVisibility(View.VISIBLE);
videoTrack.addRenderer(remoteRenderer);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void onIceCandidateReceived(PeerConnection peer, IceCandidate iceCandidate) {
//we have received ice candidate. We can set it to the other peer.
if (peer == localPeer) {
remotePeer.addIceCandidate(iceCandidate);
} else {
localPeer.addIceCandidate(iceCandidate);
}
}
あなたは上記のコードを見てみるならば、我々は3つの方法があります。
スタート()メソッドは、基本的に地元のオーディオおよびビデオソースを作成し、(これは私たちがこのビューに直接私たちのビデオフレームを描くことができるようにのWebRTCによって提供される)SurfaceViewRendererに追加します。
ハングアップ()メソッドは、コードの単純な部分である全てPeerConnectionインスタンスオフクリア
()の呼び出しはすべての楽しみが起こる場所です。
- 私たちは、作成します 二ピア接続インスタンス(ローカルピアとリモートピアのために別のもの)。これらのピアが作成されると、私たちは、ローカルピアが作成します提供 それは、その地域の説明と、リモートピアのリモート説明として設定されています。
- 私たちは、その後、リモートピアが作成できます 回答 これはその地域の説明とローカルピアのリモート説明として設定されています。
我々はまた、持っているonIceCandidateReceived()その作品別のピアにあるピアから受け取った氷の候補を設定することである方法を。
あなたはのステップ-2フォルダを見てとることができGitリポジトリをループバックpeerconnectionの完全な作業コードのために。
すべては今うまくいっています。私たちのアプリは、私たちがやったように、あなたにあなたの顔を表示することができるようになります最初の部分。ことを除いて、この時間は、我々のアプリは、ピア接続を介して顔を見せています。
私たちは働いて基礎を得ました。私たちは、候補者と一緒に提供し、答えのSDPを転送し、今彼らは両方のデータを転送することができる2つのピアを持っています。しかし、これは実際のケースではありません。実質の人々は自分たち同士ではなくを呼び出します。実際のユースケースにこのソリューションを拡張することは簡単です。私達はちょうどのSDPを転送するための媒体といくつかのSTUNやTURNセットアップを必要としています。
これは、単一のポストのためにあまりにも大きくなりました。あなたが働いてピア・ツー・ピアアプリをビデオ通話を有効にしていますグランドフィナーレで(願わくば!)すぐに参照してください。