2015-10-04 18 views
22

WebRTC Native Code package for Android(つまりWebViewを使用しない)を使用してAndroidアプリ内で3方向ビデオチャットを実装しようとしています。私はnode.jsを使ってシグナリングサーバを作成し、クライアントアプリケーション内のGottox socket.io java clientライブラリを使ってサーバに接続し、SDPパケットを交換し、双方向ビデオチャット接続を確立しました。Android用のWebRTCネイティブコードで3者間会議通話ビデオチャットを実装する方法は?

しかし、今ではそれを超えて3方向通話に問題があります。 WebRTCネイティブコードパッケージに付属しているAppRTCDemoアプリは、双方向通話のみを実演しています(第三者が部屋に入ろうとした場合、「部屋いっぱい」のメッセージが返されます)。

this answer(特にAndroidには関係しない)によると、私は複数のPeerConnectionを作成することになっているので、各チャット参加者は2人の他の参加者に接続します。

しかし、複数のPeerConnectionClient(PeerConectionをラップするJavaクラス(libjingle_peerconnection_so.soのネイティブ側で実装されているJavaクラス)を作成すると、両方と競合してライブラリ内から例外がスローされますカメラにアクセスしようとする彼らの:でもそれはのNode.js、socket.ioまたはシグナリング・サーバーのもののいずれにも関係していないので、接続を確立しようとする前に、ローカルクライアントを初期化するとき

E/VideoCapturerAndroid(21170): startCapture failed 
E/VideoCapturerAndroid(21170): java.lang.RuntimeException: Fail to connect to camera service 
E/VideoCapturerAndroid(21170): at android.hardware.Camera.native_setup(Native Method) 
E/VideoCapturerAndroid(21170): at android.hardware.Camera.<init>(Camera.java:548) 
E/VideoCapturerAndroid(21170): at android.hardware.Camera.open(Camera.java:389) 
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid.startCaptureOnCameraThread(VideoCapturerAndroid.java:528) 
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid.access$11(VideoCapturerAndroid.java:520) 
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid$6.run(VideoCapturerAndroid.java:514) 
E/VideoCapturerAndroid(21170): at android.os.Handler.handleCallback(Handler.java:733) 
E/VideoCapturerAndroid(21170): at android.os.Handler.dispatchMessage(Handler.java:95) 
E/VideoCapturerAndroid(21170): at android.os.Looper.loop(Looper.java:136) 
E/VideoCapturerAndroid(21170): at org.webrtc.VideoCapturerAndroid$CameraThread.run(VideoCapturerAndroid.java:484) 

これが起こります。

同じビデオを複数のピアに送信できるように、カメラを共有する複数のPeerConnectionsを取得するにはどうすればよいですか?

複数の接続間で共有できるVideoCapturerAndroidを置き換えるために、ある種のシングルトンカメラクラスを実装するというアイデアはありましたが、それがうまくいくかどうかはわかりません。ライブラリーの内部をハッキングする前にAPIを使用して3方向の呼び出しを行います。

それは可能でしょうか?

更新:

Iは、複数PeerConnectionClients間VideoCapturerAndroidオブジェクトを共有するだけ最初の接続のためにそれを作成し、それに続くものに初期化関数にそれを通過しようとしたが、それは、この「捕獲をもたらしたのみ缶一度服用してください! "第2のピア接続のVideoCapturerオブジェクトから第VideoTrackを作成するときに例外が:

E/libjingle(19884): Local fingerprint does not match identity. 
E/libjingle(19884): P2PTransportChannel::Connect: The ice_ufrag_ and the ice_pwd_ are not set. 
E/libjingle(19884): Local fingerprint does not match identity. 
E/libjingle(19884): Failed to set local offer sdp: Failed to push down transport description: Local fingerprint does not match identity. 

MediaStreamの共有:PeerConnectionClients間VideoTrackオブジェクトを共有しようと

E/AndroidRuntime(18956): FATAL EXCEPTION: Thread-1397 
E/AndroidRuntime(18956): java.lang.RuntimeException: Capturer can only be taken once! 
E/AndroidRuntime(18956): at org.webrtc.VideoCapturer.takeNativeVideoCapturer(VideoCapturer.java:52) 
E/AndroidRuntime(18956): at org.webrtc.PeerConnectionFactory.createVideoSource(PeerConnectionFactory.java:113) 
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient.createVideoTrack(PeerConnectionClient.java:720) 
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient.createPeerConnectionInternal(PeerConnectionClient.java:482) 
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient.access$20(PeerConnectionClient.java:433) 
E/AndroidRuntime(18956): at com.example.rtcapp.PeerConnectionClient$2.run(PeerConnectionClient.java:280) 
E/AndroidRuntime(18956): at android.os.Handler.handleCallback(Handler.java:733) 
E/AndroidRuntime(18956): at android.os.Handler.dispatchMessage(Handler.java:95) 
E/AndroidRuntime(18956): at android.os.Looper.loop(Looper.java:136) 
E/AndroidRuntime(18956): at com.example.rtcapp.LooperExecutor.run(LooperExecutor.java:56) 

は、ネイティブコードから、このエラーが生じましたPeerConnectionClientsの間でアプリケーションが突然終了し、Logcatにエラーメッセージが表示されなくなります。

+0

"3-wayビデオ"とは何ですか? – SilentKnight

+0

@ SilentKnight 3つのパートを持つビデオ会議電話 – samgak

+0

@Samgakこんにちは。完全なソリューションを共有できますか?私はいくつかのオーディオを接続するのに問題がありました。 – GensaGames

答えて

15

あなたが持っている問題はPeerConnectionClientはそれが PeerConnectionが含まれていPeerConnection周りラッパーではないということです。

私はこの質問に答えなかったことに気づいたので、私はちょっと手伝うことができるかどうかを見たいと思っていました。私はソースコードを調べて、PeerConnectionClientは単一のリモートピアのために非常に多くのハードコーディングされています。あなたは、むしろ、このラインPeerConnectionオブジェクトのコレクションを作成する必要があります:

private PeerConnection peerConnection; 

をあなたはより多くのあなたが、それはもう少し複雑その後、これを取得することに気付くだろう少し周りを見てみると。

createPeerConnectionInternalでMediaStreamのロジックは一度だけ行われる必要があり、あなたがこのようなあなたのPeerConnectionオブジェクト間のストリームを共有する必要があります。

peerConnection.addStream(mediaStream); 

あなたはWebRTC specを参照するか確認するために、このstackoverflow質問を見てみることができますPeerConnectionタイプは、1つのピアだけを処理するように設計されています。それはやや漠然と暗示されているhereです。

ですから、一つだけMediaStreamのオブジェクトを維持する:あなたが接続したいピアを持っているよう

private MediaStream mediaStream; 

だから、再び主なアイデアは、1つのMediaStreamのオブジェクトとなど、多くのPeerConnectionオブジェクトです。したがって、複数のPeerConnectionClientオブジェクトを使用するのではなく、単一のPeerConnectionClientを変更して、マルチクライアント処理をカプセル化します。何らかの理由で複数のPeerConnectionClientオブジェクトの設計をしたいのであれば、メディアストリームロジック(および一度だけ作成する必要のあるサポートタイプ)を抽象化するだけで済みます。

また、既存の1というし、複数のリモートビデオトラックを維持する必要があります。

private VideoTrack remoteVideoTrack; 

あなたは明らかに一つだけのローカルカメラをレンダリングし、リモート接続のための複数のレンダラを作成するために、気になります。

私はこれがあなたを軌道に乗せるための十分な情報であることを望みます。私は、より詳細にビデオ会議通話をサポートするためのサンプルコードを適応する1つの方法を説明するつもりだこの答えではので、私は、それが働いて得ることができたマシュー・サンダース答えの助けを借りて

+0

あなたの答えをありがとう。私はすでに複数のPeerConnectionClients間でMediaStreamオブジェクトを共有しようとしましたが、複数のPeerConnectionsで単一のPeerConnectionClientを使用することをお勧めします – samgak

+0

受け取った例外はまさにそのものです。複数のPeerConnectionClientsを使用しています。これは、カメラを2度使用しようとします。私は、あなたがその部分をリファクタリングしようとしたと言ったことを理解しています。私はあなたが動かす必要があるまともな量の論理があるので、何かがリファクタに残されていなければならないと仮定しています。ストリームは1つしかありませんでしたか?これは、videoCapturerのmediaStream.addTrack(createVideoTrack(videoCapturer))を使用するためです。 –

+0

はい、私は1つのストリームしか持っていません。PeerConnectionClients間で共有しようとしました。最初のもので作成されたMediaStreamを残りの部分に渡すだけです(この場合、コメント内のコード行は実行されません) 。それはエレガントではありませんが、私は共有する必要のあるものと接続ごとのものを把握しようとしていました。実際にピアに接続する前にエラーなしでPeerConnectionClientsを初期化できる段階に到達しました。 – samgak

6

ほとんどの変更はPeerConnectionClientで行う必要がありますが、PeerConnectionClientを使用するクラスでも行われます。このクラスでは、シグナリングサーバーと通信して接続を設定します。 PeerConnectionClientインサイド

、以下のメンバ変数は、接続ごと保存する必要があります私のアプリケーションで

private VideoRenderer.Callbacks remoteRender; 
private final PCObserver pcObserver = new PCObserver(); 
private final SDPObserver sdpObserver = new SDPObserver(); 
private PeerConnection peerConnection; 
private LinkedList<IceCandidate> queuedRemoteCandidates; 
private boolean isInitiator; 
private SessionDescription localSdp; 
private VideoTrack remoteVideoTrack; 

私は(4ウェイチャット用)3つの最大接続を必要とするので、私はちょうど保存それぞれの配列ですが、それらをすべてオブジェクト内に配置し、オブジェクトの配列を持つことができます。

private static final int MAX_CONNECTIONS = 3; 
private VideoRenderer.Callbacks[] remoteRenders; 
private final PCObserver[] pcObservers = new PCObserver[MAX_CONNECTIONS]; 
private final SDPObserver[] sdpObservers = new SDPObserver[MAX_CONNECTIONS]; 
private PeerConnection[] peerConnections = new PeerConnection[MAX_CONNECTIONS]; 
private LinkedList<IceCandidate>[] queuedRemoteCandidateLists = new LinkedList[MAX_CONNECTIONS]; 
private boolean[] isConnectionInitiator = new boolean[MAX_CONNECTIONS]; 
private SessionDescription[] localSdps = new SessionDescription[MAX_CONNECTIONS]; 
private VideoTrack[] remoteVideoTracks = new VideoTrack[MAX_CONNECTIONS]; 

IはPCObserverSDPObserverクラスにconnectionIdフィールドを追加し、そしてPeerConnectionClientコンストラクタ内部Iは、アレイ内のオブザーバオブジェクトを割り当てられ、アレイ内のそのインデックスに対する各観察者オブジェクトのconnectionIdフィールドを設定します。上記のメンバ変数を参照するPCObserverおよびSDPObserverのすべてのメソッドは、connectionIdフィールドを使用して適切な配列にインデックスを付けるために変更する必要があります。

PeerConnectionClientのコールバックを変更する必要があります

public static interface PeerConnectionEvents { 
    public void onLocalDescription(final SessionDescription sdp, int connectionId); 
    public void onIceCandidate(final IceCandidate candidate, int connectionId); 
    public void onIceConnected(int connectionId); 
    public void onIceDisconnected(int connectionId); 
    public void onPeerConnectionClosed(int connectionId); 
    public void onPeerConnectionStatsReady(final StatsReport[] reports); 
    public void onPeerConnectionError(final String description); 
} 

そしてまた次PeerConnectionClient方法:

private void createPeerConnectionInternal(int connectionId) 
private void closeConnectionInternal(int connectionId) 
private void getStats(int connectionId) 
public void createOffer(final int connectionId) 
public void createAnswer(final int connectionId) 
public void addRemoteIceCandidate(final IceCandidate candidate, final int connectionId) 
public void setRemoteDescription(final SessionDescription sdp, final int connectionId) 
private void drainCandidates(int connectionId) 

をオブザーバクラスのメソッドと同じように、すべてのこれらの機能に変更する必要がありますconnectionIdを使用して、以前に存在した単一のオブジェクトを参照するのではなく、接続ごとのオブジェクトの適切な配列にインデックスを付けます。 connectionIdを返すようにコールバック関数の呼び出しを変更する必要があります。

私はcreateMultiPeerConnectionという新しい機能を置き換えました。この機能には、リモートビデオストリームを表示するためのオブジェクトが1つではなくVideoRenderer.Callbacksという配列に渡されています。この関数は、0からMAX_CONNECTIONS - 1にループして、のそれぞれに対して、createMediaConstraintsInternal()を1回、createPeerConnectionInternal()を呼び出します。 mediaStreamオブジェクトはcreatePeerConnectionInternal()への最初の呼び出し時にのみ作成され、if(mediaStream == null)チェックで初期化コードをラップするだけです。

アプリがシャットダウンし、PeerConnectionのインスタンスが閉じられ、MediaStreamが処分されたときに、1つの問題が発生しました。サンプルコードでmediaStreamがを使用してPeerConnectionに追加されますが、対応するremoveStream(mediaStream)関数が呼び出されることはありません(dispose()が代わりに呼び出されます)。 dispose()が最後PeerConnectionが閉じているときにのみ起こるべきMediaStreamを確定するのでMediaStreamオブジェクトを共有する複数のPeerConnectionがある場合ただし、これは(参照カウントがネイティブコードでMediaStreamInterfaceに主張する)問題を作成します。 removeStream()close()を呼び出すだけでは、PeerConnectionを完全にシャットダウンしないため、PeerConnectionFactoryオブジェクトを破棄するときにアサートクラッシュが発生するため、十分ではありません。

public void freeConnection() 
{ 
    localStreams.clear(); 
    freePeerConnection(nativePeerConnection); 
    freeObserver(nativeObserver); 
} 

そして最後以外の各PeerConnectionを最終決定する際、これらの関数を呼び出す:

peerConnections[connectionId].removeStream(mediaStream); 
peerConnections[connectionId].close(); 
peerConnections[connectionId].freeConnection(); 
peerConnections[connectionId] = null; 

をして、最後の1をシャットダウンし、私は見つけることができる唯一の修正はPeerConnectionクラスに次のコードを追加しましたこのように:

peerConnections[connectionId].dispose(); 
peerConnections[connectionId] = null; 

PeerConnectionClientを変更した後、それはCONNを設定するには、シグナリングコードを変更する必要がありますコールバックを適切に処理するために、適切な接続インデックスを関数のそれぞれに渡します。私はsocket.ioソケットIDと接続IDの間にハッシュを維持することでこれを行いました。新しいクライアントが会議室に参加すると、既存のメンバーのそれぞれが新しいクライアントにオファーを送信し、順番に回答を受け取ります。また、複数のVideoRenderer.Callbacksオブジェクトを初期化してPeerConnectionClientインスタンスに渡し、会議通話を希望する場合は画面を分割する必要があります。

+0

私は助けてくれることができてうれしくて、さらにあなたの問題を解決した詳細な答えを見て幸せです! –

+0

@samgakいい仕事、これ。 freePeerConnection()とfreeObserver()に到達する方法を聞かせてもらえますか?これらの関数はプライベートであり、PeerConnectionはPeerConnectionFactoryによって作成されるため、パブリックコンストラクタを持ちません。だから私はそれを拡張することはできませんと私が見る唯一の方法は、ライブラリ全体をコピーし、それを変更することです。 –

+1

@OliverHauslerはい残念ながら、それは私がしなければならなかったことです。私は、ライブラリ内のPeerConnectionクラスを変更せずにクラッシュすることなく、PeerConnectionsを閉じる方法を見つけることができませんでした。 – samgak

関連する問題