Android Video Conference¶
Example of video conference client for Android¶
This example can be used to participate in video conference for three participants on Web Call Server and allows to publish WebRTC stream.
On the screenshot below the participant is connected, publishing a stream and playing streams from the other two participants.
Input fields required for connecting to WCS server and joining conference
WCS URL
, where192.168.2.104
is the address of the WCS serverLogin
, whereAlice
is the usernameRoom
, whereroom1
is the name of conference room
Three videos are played
- video from the camera of this participant - the lower one
- videos from the other participants (Bob and Cindy)
Volume control is located between the video elements. Controls for muting/unmuting audio and video for the published stream, input field for a text message and messages log are located below the video elements.
Analyzing the example code¶
To analyze the code, let's take class ConferenceActivity.java of the conference
example, which can be downloaded with corresponding build 1.0.1.38.
Unlike direct connection to server with method createSession()
, the RoomManager
object is used for managing connection to server and conference. Connection to server is established when RoomManager
object
is created, and method RoomManager.join()
is called for joining a conference.
When joining, Room
object is created for work with the chat room. Participant
objects are used for work with conference participants.
All events occurring in the room (a user joined/left, or sent a message), are sent to all users connected to the room.
For example, in the following code, a user joins to a room and gets the list of already connected users:
room = roomManager.join(roomOptions);
room.on(new RoomEvent() {
public void onState(final Room room) {
for (final Participant participant : room.getParticipants()) {
...
}
...
}
...
});
ParticipantView
(SurfaceViewRenderer
+ TextView
) is assigned for each of the other participants, to display the name and video stream of that participant (Bob and Cindy on the screenshot above).
1. Initialization of the API¶
Flashphoner.init()
code
For initialization, Сontext
object is passed to the init()
method.
2. Connection to the server¶
Flashphoner.createRoomManager()
code
RoomManagerOptions
object with the following parameters is passed to createRoomManager()
method:
- URL of WCS server
- username
RoomManagerOptions roomManagerOptions = new RoomManagerOptions(mWcsUrlView.getText().toString(), mLoginView.getText().toString());
/**
* RoomManager object is created with method createRoomManager().
* Connection session is created when RoomManager object is created.
*/
roomManager = Flashphoner.createRoomManager(roomManagerOptions);
3. Receiving the event confirming successful connection¶
RoomManager.onConnected()
code
@Override
public void onConnected(final Connection connection) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mConnectButton.setText(R.string.action_disconnect);
mConnectButton.setTag(R.string.action_disconnect);
mConnectButton.setEnabled(true);
mConnectStatus.setText(connection.getStatus());
mJoinButton.setEnabled(true);
}
});
}
4. Joining a conference¶
RoomManager.join()
code
RoomOptions
object with the name of the conference room is passed to the join()
method
RoomOptions roomOptions = new RoomOptions();
roomOptions.setName(mJoinRoomView.getText().toString());
/**
* The participant joins a conference room with method RoomManager.join().
* RoomOptions object is passed to the method.
* Room object is created and returned by the method.
*/
room = roomManager.join(roomOptions);
5. Receiving the event describing chat room state¶
Room.onState()
code
On this event:
- the size of the collection of
Participant
objects returned by methodwRoom.getParticipants()
is determined to get the number of already connected participants - if the maximum allowed number of participants had already been reached, the user leaves the room
- otherwise, appropriate changes in the interface are done and playback of video stream published by the other participants is started
@Override
public void onState(final Room room) {
/**
* After joining, Room object with data of the room is received.
* Method Room.getParticipants() is used to check the number of already connected participants.
* The method returns collection of Participant objects.
* The collection size is determined, and, if the maximum allowed number (in this case, three) has already been reached, the user leaves the room with method Room.leave().
*/
if (room.getParticipants().size() >= 3) {
room.leave(null);
runOnUiThread(
new Runnable() {
@Override
public void run() {
mJoinStatus.setText("Room is full");
mJoinButton.setEnabled(true);
}
}
);
return;
}
final StringBuffer chatState = new StringBuffer("participants: ");
/**
* Iterating through the collection of the other participants returned by method Room.getParticipants().
* There is corresponding Participant object for each participant.
*/
for (final Participant participant : room.getParticipants()) {
/**
* A player view is assigned to each of the other participants in the room.
*/
final ParticipantView participantView = freeViews.poll();
if (participantView != null) {
chatState.append(participant.getName()).append(",");
busyViews.put(participant.getName(), participantView);
/**
* Playback of the stream being published by the other participant is started with method Participant.play().
* SurfaceViewRenderer to be used to display the video stream is passed when the method is called.
*/
participant.play(participantView.surfaceViewRenderer);
...
}
}
...
}
6. Video streaming when permissions to use camera and microphone are granted¶
Room.publish()
code
SurfaceViewRenderer
object to be used to display video from the camera is passed to the publish()
method
case PUBLISH_REQUEST_CODE: {
if (grantResults.length == 0 ||
grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "Permission has been denied by user");
} else {
mPublishButton.setEnabled(false);
/**
* Stream is created and published with method Room.publish().
* SurfaceViewRenderer to be used to display video from the camera is passed to the method.
*/
boolean record = mRecord.isChecked();
StreamOptions streamOptions = new StreamOptions();
streamOptions.setRecord(record);
stream = room.publish(localRenderer, streamOptions);
...
Log.i(TAG, "Permission has been granted by user");
}
}
7. Receiving the event notifying that other participant joined to the room¶
Room.onJoined()
code
@Override
public void onJoined(final Participant participant) {
/**
* When a new participant joins the room, a player view is assigned to that participant.
*/
final ParticipantView participantView = freeViews.poll();
if (participantView != null) {
runOnUiThread(
new Runnable() {
@Override
public void run() {
participantView.login.setText(participant.getName());
addMessageHistory(participant.getName(), "joined");
}
}
);
busyViews.put(participant.getName(), participantView);
}
}
8. Receiving the event notifying that other participant published video stream¶
Room.onPublished()
код
When one of the conference participants starts publishing, method Participant.play()
is used to start playback of that stream. SurfaceViewRenderer
object, in which the video will be displayed, is passed to the method
@Override
public void onPublished(final Participant participant) {
/**
* When one of the other participants starts publishing, playback of the stream published by that participant is started.
*/
final ParticipantView participantView = busyViews.get(participant.getName());
if (participantView != null) {
participant.play(participantView.surfaceViewRenderer);
}
}
9. Receiving the event notifying that other participant sent a message¶
Room.onMessage()
code
@Override
public void onMessage(final Message message) {
/**
* When one of the participants sends a text message, the received message is added to the messages log.
*/
runOnUiThread(
new Runnable() {
@Override
public void run() {
addMessageHistory(message.getFrom(), message.getText());
}
});
}
10. Sending a message to other room participants¶
Participant.sendMessage()
code
mSendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
String text = mMessage.getText().toString();
if (!"".equals(text)) {
for (Participant participant : room.getParticipants()) {
participant.sendMessage(text);
}
addMessageHistory(mLoginView.getText().toString(), text);
mMessage.setText("");
}
}
});
11. Streaming stop on Unpublish
button pressing¶
Room.unpublish()
code
@Override
public void onClick(View view) {
if (mPublishButton.getTag() == null || Integer.valueOf(R.string.action_publish).equals(mPublishButton.getTag())) {
ActivityCompat.requestPermissions(ConferenceActivity.this,
new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA},
PUBLISH_REQUEST_CODE);
} else {
mPublishButton.setEnabled(false);
/**
* Stream is unpublished with method Room.unpublish().
*/
room.unpublish();
}
View currentFocus = getCurrentFocus();
if (currentFocus != null) {
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(currentFocus.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
12. Leaving chat room¶
Room.leave()
code
Server REST hook application response handler function is passed to the metod
room.leave(new RestAppCommunicator.Handler() {
@Override
public void onAccepted(Data data) {
runOnUiThread(action);
}
@Override
public void onRejected(Data data) {
runOnUiThread(action);
}
});
13. Disconnection¶
RoomManager.disconnect()
code
mConnectButton.setEnabled(false);
/**
* Connection to WCS server is closed with method RoomManager.disconnect().
*/
roomManager.disconnect();
14. Mute/unmute audio and video for stream published¶
Stream.unmuteAudio()
, Stream.muteAudio()
, Stream.unmuteVideo()
, Stream.muteVideo()
code
/**
* MuteAudio switch is used to mute/unmute audio of the published stream.
* Audio is muted with method Stream.muteAudio() and unmuted with method Stream.unmuteAudio().
*/
mMuteAudio = (Switch) findViewById(R.id.mute_audio);
mMuteAudio.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
stream.muteAudio();
} else {
stream.unmuteAudio();
}
}
});
/**
* MuteVideo switch is used to mute/unmute video of the published stream.
* Video is muted with method Stream.muteVideo() and unmuted with method Stream.unmuteVideo().
*/
mMuteVideo = (Switch) findViewById(R.id.mute_video);
mMuteVideo.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
stream.muteVideo();
} else {
stream.unmuteVideo();
}
}
});