Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.

...

Volume control is located between the video elements.
Below are located controls for muting/unmuting audio and video for the published stream, input field for a text message and messages log.

Work with code of the example

To analyze the code, let's take class ConferenceActivity.java of  of the conference example version with hash 4ed4c6d77, which can be downloaded with corresponding build build 1.0.1.338.

Unlike direct connection to server with method createSession(), , 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 "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.

Code Block
languagejsjava
themeRDark
room = roomManager.join(roomOptions);
room.on(new RoomEvent() {
    public void onState(final Room room) {
        for (final Participant participant : room.getParticipants()) {
    ...

ParticipantView (SurfaceViewRenderer + TextView - line 613) 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. line 98

...

...

Flashphoner.init(

...

)

...

 code

For initialization, object Сontext is passed to the init() method.

Code Block
languagejava
themeRDark
Flashphoner.init(this);

2. Connection to the server.RoomManager object and session for connection to server are created when Connect button is clicked. line 145

Flashphoner.createRoomManager() code

RoomManagerOptions object with the following parameters is passed to createRoomManager() method()

  • URL of WCS server
  • username
Code Block
languagejsjava
themeRDark
roomManagerRoomManagerOptions roomManagerOptions = Flashphoner.createRoomManager(roomManagerOptions);

RoomManager object is created with method createRoomManager(), to which RoomManagerOptions object (line 139) with the following parameters is passed

  • URL of WCS server
  • username

Callback functions, which make appropriate changes in controls of the interface, are added for session events (line 150)

  • onConnected() - will be called when connection is successfully established
  • onDisconnection() - will be called when connection is closed
Code Block
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

Code Block
languagejs
themeRDark
roomManager.on(new RoomManagerEvent() {
    @Override
public void onConnected(final Connection connection) {
    runOnUiThread(new Runnable() {
    .....    @Override
    }
    public void onDisconnectionrun(final Connection connection) {
            mConnectButton.setText(R....string.action_disconnect);
    }
});

3. Joining a conference. line 145

After establishing connection to the server, method connection.join() is used to join the conference.
To join, name of the conference room is passed to the method. (The name can be specified as parameter in the URL of the client page; otherwise, random name will be generated.)

...

        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.

Code Block
languagejs
themeRDark
roomRoomOptions roomOptions = roomManager.join(roomOptions);

Callback functions for conference room events are added (line 254)

  • onState() - will be called when successfully joined the conference
  • onJoined() - will be called when a new participants joins the conference: the interface is changed to display the new participant and playback of video stream published by that participant is started
  • onLeft() - will be called when one of the participants leaves the conference: appropriate changes in the interface are done
  • onPublished() - will be called when one of the participants starts publishing: playback of video stream published by that participant is started
  • onFailed() - will be called in case of failure: the user leaves the conference
  • onMessage() - will be called when one of the participants sends text message: the message is added to the messages log

In callback function onState(),

  • the size of the collection of Participant objects returned by method Room.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" (line 264)
  • otherwise, appropriate changes in the interface are done and playback of video stream published by the other participants is started
Code Block
languagejs
themeRDark
room.on(new RoomEvent() {
    public void onState(final Room room) {
        .....
    }
    public void onJoined(final Participant participant) {
        .....
    }
    public void onLeft(final Participant participant) {
        .....
    }
    public void onPublished(final Participant participant) {
        .....
    }
    public void onFailed(Room room, final String info) {
        .....
    }
    public void onMessage(final Message message) {
        .....
    }
});

4. Video streaming. line 479

Video stream publication is started when Publish button is clicked.

Code Block
languagejs
themeRDark
stream = room.publish(localRenderer);

Method Room.publish() is called to publish a stream. SurfaceViewRenderer to be used to display video from the camera is passed to the method.

Callback function for processing stream statuses is added. (line 484)

Code Block
languagejs
themeRDark
publishStream.on(new StreamStatusEvent() {
    @Override
    public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
        runOnUiThread(new Runnable() { 
            @Override 
            public void run() { 
                if (StreamStatus.PUBLISHING.equals(streamStatus)) {
                    .....
                }
        }
    }
});

5. Playback of video stream. line 376

When one of the conference participants starts publishing, method Participant.play() is used to start playback of that stream.
SurfaceViewRenderer, in which the video will be displayed, is passed to the method.

Code Block
languagejs
themeRDark
participant.play(participantView.surfaceViewRenderer);

6. Stop of streaming. line 518

The following method is called to stop video streaming when Unpublish button is clicked.

Code Block
languagejs
themeRDark
room.unpublish();

7. Leaving conference room. line 416

Method room.leave() is called for leaving conference room when Leave button is clicked.
Handler for processing response from server-side REST application is passed to the method.

Code Block
languagejs
themeRDark
room.leave(new RestAppCommunicator.Handlernew 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 method Room.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


Code Block
languagejava
themeRDark
@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);
            ...
        }
    }
    ...
}

4. Video streaming when permissions to use camera and microphone are granted.

Room.publish() code

SurfaceViewRenderer to be used to display video from the camera is passed to the publish() method

Code Block
languagejava
themeRDark
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

Code Block
languagejava
themeRDark
@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, in which the video will be displayed, is passed to the method.

Code Block
languagejava
themeRDark
@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

Code Block
languagejava
themeRDark
@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

Code Block
languagejava
themeRDark
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

Code Block
languagejava
themeRDark
    @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

Методу передается обработчик ответа REST-приложения WCS-сервера.

Code Block
languagejava
themeRDark
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

Code Block
languagejava
themeRDark
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

Code Block
languagejava
themeRDark
/**
  * 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 onAccepted(Data dataonCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        if (isChecked) {
            ......stream.muteAudio();
        } else {
        public   void onRejected(Data data) {stream.unmuteAudio();
        ......}
    }
});

8. Disconnection. line 213

Method RoomManager.disconnect() is called to close connection to the server.

Code Block
languagejs
themeRDark
roomManager.disconnect();

9. Mute/unmute audio and video of the published stream. line 533, line 548

The following methods are used to mute/unmute audio and video

  • Stream.unmuteAudio();
  • Stream.muteAudio();
  • Stream.unmuteVideo();
  • Stream.muteVideo();

10. Sending text message. line 599

Method Participant.sendMessage() is used for sending text message to other participants. Message text is passed to the method.

When Send button is clicked,

  • method Room.getParticipants() is used to get the collection of connected participants
  • the message is sent to each participant
Code Block
languagejs
themeRDark
for (Participant participant : room.getParticipants()) {
    participant.sendMessage(text);
}

11. Adjusting the volume line 100

The following methods are used to adjust the volume

...



/**
  * 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();
        }
    }
});