我可以使用nubomedia(kurento-room-client-android)实现一对一视频通话,但是无法实现多个视频通话。请指导我。我可以在onRemoteStreamAdded方法上获得多个视频流。我检查不同用户的onIceCandiate状态。只有一个本地和一个远程iceCandidate状态已连接并完成,其余的远程用户仍在检查中。出于测试目的,我编写了onRemoteStreamAdded代码。请不要判断。
public class PeerVideoActivity extends AppCompatActivity
implements NBMWebRTCPeer.Observer, RoomListener {
private static final String TAG = "PeerVideoActivity";
private String usrArr;
private NBMWebRTCPeer nbmWebRTCPeer;
private SurfaceViewRenderer masterViewFirst, masterViewSecond;
private boolean masterViewFirstAdded=false, masterViewSecondAdded=false;
private SurfaceViewRenderer localView;
private Map<Integer, String> videoRequestUserMapping;
private int publishVideoRequestId;
private TextView mCallStatus;
private String username;
private boolean isAdded = false;
private HorizontalVideoStreamAdapter horizontalAdapter;
private RecyclerView horizontal_recycler_view;
private boolean isRemoteStreamAdded = false;
private boolean backPressed = false;
private Thread backPressedThread = null;
private HashMap<String, Boolean> generateOfferMap, sendOfferMap, iceCandidtateMap;
private ArrayList<MediaStream> remoteStreamList;
private Handler mHandler = null;
private CallState callState;
private enum CallState {
IDLE,
PUBLISHING,
PUBLISHED,
WAITING_REMOTE_USER,
RECEIVING_REMOTE_USER
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conference);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mHandler = new Handler();
masterViewFirst = (SurfaceViewRenderer) findViewById(R.id.gl_surface_first);
masterViewSecond= (SurfaceViewRenderer) findViewById(R.id.gl_surface_second);
horizontal_recycler_view = (RecyclerView) findViewById(R.id.horizontal_recycler_view);
remoteStreamList = new ArrayList<>();
localView = (SurfaceViewRenderer) findViewById(R.id.gl_surface_local);
this.mCallStatus = (TextView) findViewById(R.id.call_status);
callState = CallState.IDLE;
MainActivity.getKurentoRoomAPIInstance().addObserver(this);
}
@Override
protected void onStart() {
super.onStart();
Bundle extras = getIntent().getExtras();
this.username = extras.getString(Constants.USER_NAME, "");
usrArr = extras.getString("usrArr");
Log.i(TAG, "username: " + username);
generateOfferMap = getUserMap();
iceCandidtateMap = getUserMap();
sendOfferMap = getUserMap();
EglBase rootEglBase = EglBase.create();
masterViewFirst.init(rootEglBase.getEglBaseContext(), null);
masterViewFirst.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
masterViewSecond.init(rootEglBase.getEglBaseContext(), null);
masterViewSecond.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
localView.init(rootEglBase.getEglBaseContext(), null);
localView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
NBMMediaConfiguration peerConnectionParameters =
new NBMMediaConfiguration(NBMMediaConfiguration.NBMRendererType.OPENGLES,
NBMMediaConfiguration.NBMAudioCodec.OPUS, 0, NBMMediaConfiguration.NBMVideoCodec.VP8, 0,
new NBMMediaConfiguration.NBMVideoFormat(320, 240, PixelFormat.RGB_888, 30),
NBMMediaConfiguration.NBMCameraPosition.FRONT);
videoRequestUserMapping = new HashMap<>();
nbmWebRTCPeer = new NBMWebRTCPeer(peerConnectionParameters, this, localView, this);
//nbmWebRTCPeer.registerMasterRenderer(masterViewFirst);
//nbmWebRTCPeer.registerMasterRenderer(masterViewSecond);
horizontalAdapter = new HorizontalVideoStreamAdapter(remoteStreamList, this, nbmWebRTCPeer,
masterViewFirst, masterViewSecond);
LinearLayoutManager horizontalLayoutManager =
new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
horizontal_recycler_view.setLayoutManager(horizontalLayoutManager);
horizontal_recycler_view.setAdapter(horizontalAdapter);
Log.i(TAG, "Initializing nbmWebRTCPeer...");
nbmWebRTCPeer.initialize();
callState = CallState.PUBLISHING;
mCallStatus.setText("Publishing...");
}
@Override
protected void onStop() {
//endCall();
super.onStop();
}
@Override
protected void onPause() {
//nbmWebRTCPeer.stopLocalMedia();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
nbmWebRTCPeer.startLocalMedia();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
/* @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_video_chat, menu);
return true;
}*/
/* @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}*/
@Override
public void onBackPressed() {
// If back button has not been pressed in a while then trigger thread and toast notification
if (!this.backPressed) {
this.backPressed = true;
Toast.makeText(this, "Press back again to end.", Toast.LENGTH_SHORT).show();
this.backPressedThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
backPressed = false;
} catch (InterruptedException e) {
Log.d("VCA-oBP", "Successfully interrupted");
}
}
});
this.backPressedThread.start();
}
// If button pressed the second time then call super back pressed
// (eventually calls onDestroy)
else {
if (this.backPressedThread != null) this.backPressedThread.interrupt();
super.onBackPressed();
}
}
public void hangup(View view) {
finish();
}
private void GenerateOfferForRemote(String remote_name) {
nbmWebRTCPeer.generateOffer(remote_name, false);
callState = CallState.WAITING_REMOTE_USER;
runOnUiThread(new Runnable() {
@Override
public void run() {
mCallStatus.setText(R.string.waiting_remote_stream);
}
});
}
public void receiveFromRemote(View view) {
//GenerateOfferForRemote();
}
/**
* Terminates the current call and ends activity
*/
/* private void endCall() {
callState = CallState.IDLE;
try
{
if (nbmWebRTCPeer != null) {
nbmWebRTCPeer.close();
nbmWebRTCPeer = null;
}
}
catch (Exception e){e.printStackTrace();}
}*/
@Override
public void onInitialize() {
if (nbmWebRTCPeer != null) nbmWebRTCPeer.generateOffer("local", true);
}
@Override
public void onLocalSdpOfferGenerated(final SessionDescription sessionDescription,
final NBMPeerConnection nbmPeerConnection) {
if (callState == CallState.PUBLISHING || callState == CallState.PUBLISHED) {
Log.d(TAG, "local " + sessionDescription.type);
//publishVideoRequestId = ++Constants.id;
Log.i("sendPublishVideo", username);
MainActivity.getKurentoRoomAPIInstance()
.sendPublishVideo(sessionDescription.description, username);
} else { // Asking for remote user video
Log.d(TAG, "remote " + sessionDescription.type);
if (sendOfferMap != null) {
for (Map.Entry<String, Boolean> entry : sendOfferMap.entrySet()) {
if (entry.getValue()) {
MainActivity.getKurentoRoomAPIInstance()
.sendPublishVideo(sessionDescription.description, entry.getKey());
entry.setValue(false);
}
}
}
}
}
@Override
public void onLocalSdpAnswerGenerated(SessionDescription sessionDescription,
NBMPeerConnection nbmPeerConnection) {
}
@Override
public void onIceCandidate(IceCandidate iceCandidate, NBMPeerConnection nbmPeerConnection) {
int sendIceCandidateRequestId = ++Constants.id;
if (callState == CallState.PUBLISHING || callState == CallState.PUBLISHED) {
MainActivity.getKurentoRoomAPIInstance()
.sendOnIceCandidate(this.username, iceCandidate.sdp, iceCandidate.sdpMid,
Integer.toString(iceCandidate.sdpMLineIndex));
} else {
if (iceCandidtateMap != null) {
for (Map.Entry<String, Boolean> entry : iceCandidtateMap.entrySet()) {
if (entry.getValue()) {
MainActivity.getKurentoRoomAPIInstance()
.sendOnIceCandidate(entry.getKey(), iceCandidate.sdp, iceCandidate.sdpMid,
Integer.toString(iceCandidate.sdpMLineIndex));
entry.setValue(false);
}
}
}
//MainActivity.getKurentoRoomAPIInstance().sendOnIceCandidate(nbmPeerConnection.getConnectionId(), iceCandidate.sdp,
// iceCandidate.sdpMid, Integer.toString(iceCandidate.sdpMLineIndex), sendIceCandidateRequestId);
}
}
@Override
public void onIceStatusChanged(PeerConnection.IceConnectionState iceConnectionState,
NBMPeerConnection nbmPeerConnection) {
Log.i(TAG, "onIceStatusChanged");
}
@Override
public void onRemoteStreamAdded(final MediaStream mediaStream,
final NBMPeerConnection nbmPeerConnection) {
Log.i(TAG, "Enter onRemoteStreamAdded");
if (!nbmPeerConnection.getConnectionId().equalsIgnoreCase("local")) {
remoteStreamList.add(mediaStream);
runOnUiThread(new Runnable() {
@Override
public void run() {
mCallStatus.setText("");
if(remoteStreamList != null) {
if (remoteStreamList.size() == 1) {
//nbmWebRTCPeer.attachRendererToRemoteStream(masterViewFirst, remoteStreamList.get(0));
remoteStreamList.get(0).videoTracks.get(0).addRenderer(new VideoRenderer(masterViewFirst));
} else if (remoteStreamList.size() ==2) {
remoteStreamList.get(1).videoTracks.get(0).addRenderer(new VideoRenderer(masterViewSecond));
}
}
}
});
}
Log.i(TAG, "onRemoteStreamAdded :" + remoteStreamList.size());
Log.i(TAG, "Exit onRemoteStreamAdded");
}
@Override public void onRemoteStreamRemoved (MediaStream mediaStream, NBMPeerConnection
nbmPeerConnection){
Log.i(TAG, "onRemoteStreamRemoved");
}
@Override public void onPeerConnectionError (String s){
Log.i(TAG, "onPeerConnectionError:" + s);
}
@Override public void onDataChannel (DataChannel dataChannel, NBMPeerConnection connection){
Log.i(TAG, "[datachannel] Peer opened data channel");
}
@Override public void onBufferedAmountChange ( long l, NBMPeerConnection connection, DataChannel
channel){
}
public void sendHelloMessage (DataChannel channel){
/*byte[] rawMessage = "Hello Peer!".getBytes(Charset.forName("UTF-8"));
ByteBuffer directData = ByteBuffer.allocateDirect(rawMessage.length);
directData.put(rawMessage);
directData.flip();
DataChannel.Buffer data = new DataChannel.Buffer(directData, false);
channel.send(data);*/
}
@Override public void onStateChange (NBMPeerConnection connection, DataChannel channel){
Log.i(TAG, "[datachannel] DataChannel onStateChange: " + channel.state());
/* if (channel.state() == DataChannel.State.OPEN) {
sendHelloMessage(channel);
Log.i(TAG, "[datachannel] Datachannel open, sending first hello");
}*/
}
@Override public void onMessage (DataChannel.Buffer buffer, NBMPeerConnection
connection, DataChannel channel){
Log.i(TAG, "[datachannel] Message received: " + buffer.toString());
//sendHelloMessage(channel);
}
/* private Runnable offerWhenReady = new Runnable() {
@Override
public void run() {
// Generate offers to receive video from all peers in the room
if (generateOfferMap != null) {
for (Map.Entry<String, Boolean> entry : generateOfferMap.entrySet()) {
if (entry.getValue()) {
GenerateOfferForRemote(entry.getKey());
Log.i(TAG, "I'm " + username + " DERP: Generating offer for peer " + entry.getKey());
// Set value to false so that if this function is called again we won't
// generate another offer for this user
entry.setValue(false);
}
}
}
}
};*/
private void offerWhenReady(){
if (generateOfferMap != null) {
for (Map.Entry<String, Boolean> entry : generateOfferMap.entrySet()) {
if (entry.getValue()) {
GenerateOfferForRemote(entry.getKey());
Log.i(TAG, "I'm " + username + " DERP: Generating offer for peer " + entry.getKey());
// Set value to false so that if this function is called again we won't
// generate another offer for this user
entry.setValue(false);
}
}
}
}
@Override public void onRoomResponse (/*RoomResponse*/ String response){
Log.d(TAG, "OnRoomResponse:" + response);
if (response != null) {
try {
JSONObject obj = new JSONObject(response);
if (obj.getString("rsp").equals("answer")) {
if (obj.getString("answer") != null) {
SessionDescription sd =
new SessionDescription(SessionDescription.Type.ANSWER, obj.getString("answer"));
// Check if we are waiting for publication of our own vide
if (callState == CallState.PUBLISHING) {
callState = CallState.PUBLISHED;
nbmWebRTCPeer.processAnswer(sd, "local");
//mHandler.postDelayed(offerWhenReady, 2000);
offerWhenReady();
// Check if we are waiting for the video publication of the other peer
} else if (callState == CallState.WAITING_REMOTE_USER) {
//String user_name = Integer.toString(publishVideoRequestId);
callState = CallState.RECEIVING_REMOTE_USER;
//String connectionId = videoRequestUserMapping.get(publishVideoRequestId);
String connectionId = obj.getString("userId");
nbmWebRTCPeer.processAnswer(sd, connectionId);
} else {
callState = CallState.RECEIVING_REMOTE_USER;
//String connectionId = videoRequestUserMapping.get(publishVideoRequestId);
String connectionId = obj.getString("userId");
nbmWebRTCPeer.processAnswer(sd, connectionId);
}
}
} else if (obj.getString("rsp") != null) {
if (obj.getString("rsp").equals(RoomListener.METHOD_ICE_CANDIDATE)) {
JSONObject childObj = obj.getJSONObject("candidate");
String sdpMid = childObj.getString("sdpMid");
int sdpMLineIndex = Integer.valueOf(childObj.get("sdpMLineIndex").toString());
String sdp = childObj.getString("candidate");
String userId = obj.getString("userId");
IceCandidate ic = new IceCandidate(sdpMid, sdpMLineIndex, sdp);
if (callState == CallState.PUBLISHING || callState == CallState.PUBLISHED) {
nbmWebRTCPeer.addRemoteIceCandidate(ic, "local");
} else {
nbmWebRTCPeer.addRemoteIceCandidate(ic, userId);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
/* int requestId =response.getId();
if (requestId == publishVideoRequestId){
SessionDescription sd = new SessionDescription(SessionDescription.Type.ANSWER,
response.getValue("sdpAnswer").get(0));
// Check if we are waiting for publication of our own vide
if (callState == CallState.PUBLISHING){
callState = CallState.PUBLISHED;
nbmWebRTCPeer.processAnswer(sd, "local");
mHandler.postDelayed(offerWhenReady, 2000);
// Check if we are waiting for the video publication of the other peer
} else if (callState == CallState.WAITING_REMOTE_USER){
//String user_name = Integer.toString(publishVideoRequestId);
callState = CallState.RECEIVING_REMOTE_USER;
String connectionId = videoRequestUserMapping.get(publishVideoRequestId);
nbmWebRTCPeer.processAnswer(sd, connectionId);
}
}*/
}
@Override public void onRoomError (RoomError error){
Log.e(TAG, "OnRoomError:" + error);
}
@Override public void onRoomNotification (RoomNotification notification){
Log.i(TAG, "OnRoomNotification (state=" + callState.toString() + "):" + notification);
/* Map<String, Object> map = notification.getParams();
if(notification.getMethod().equals(RoomListener.METHOD_ICE_CANDIDATE)) {
String sdpMid = map.get("sdpMid").toString();
int sdpMLineIndex = Integer.valueOf(map.get("sdpMLineIndex").toString());
String sdp = map.get("candidate").toString();
IceCandidate ic = new IceCandidate(sdpMid, sdpMLineIndex, sdp);
if (callState == CallState.PUBLISHING || callState == CallState.PUBLISHED) {
nbmWebRTCPeer.addRemoteIceCandidate(ic, "local");
} else {
nbmWebRTCPeer.addRemoteIceCandidate(ic, "remote");
}
}
// Somebody in the room published their video
else if(notification.getMethod().equals(RoomListener.METHOD_PARTICIPANT_PUBLISHED)) {
mHandler.postDelayed(offerWhenReady, 2000);
}*/
}
@Override public void onRoomConnected () {
}
@Override public void onRoomDisconnected () {
Utils.showFinishingError(this, "Disconnected", "You have been disconnected from room.");
}
private HashMap<String, Boolean> getUserMap () {
HashMap<String, Boolean> map = new HashMap<>();
if (usrArr != null) {
try {
JSONArray arr = new JSONArray(usrArr);
if (arr.length() > 0) {
map = new HashMap();
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = (JSONObject) arr.get(i);
map.put(obj.getString("userId"), true);
//GenerateOfferForRemote(obj.getString("userId"));
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return map;
}
}