2020 API AWARDS WINNER - BEST IN MEDIA APIs LEARN MORE

Implement Screen Sharing

This tutorial is a part of a series; it comes after Manage participants.

This tutorial guides how to implement a screen sharing feature to the conference application.

Start sharing a screen

  • JavaScript
  • Swift
  • Java

Layout editing

Open the index.html file and add:

  • a button with the id start-screenshare-btn as a child of the actions div
  • a div with the id screenshare-container after the video-container div
<div id="actions">
  ...
  <button id="start-screenshare-btn" disabled>Start screen share</button>
</div>
...
<div id="video-container"></div>
<!-- add the screenshare-container here -->
<div id="screenshare-container"></div>

Interface linking

Get element from the document

Open the ui.js file and declare the start-screenshare-btn in the initUI:

const initUI = () => {
  const startScreenShareBtn = document.getElementById("start-screenshare-btn")
}

Disable/enable button

Then, edit the joinButton onclick handler to enable the startScreenShareBtn when the conference is joined:

... => VoxeetSDK.conference.join(conference, {}))
        .then(() => {
            ...
            startScreenShareBtn.disabled = false

And edit the leaveButton onclick handler to disable the startScreenShareBtn when the conference is left:

leaveButton.onclick = () => {
    VoxeetSDK.conference.leave()
        .then(() => {
            startScreenShareBtn.disabled = true
    ...

Logic implementation

Onclick handler

const initUI = () => {
    ...
    startScreenShareBtn.onclick = () => {
        VoxeetSDK.conference.startScreenShare()
            .then(() => {
                startScreenShareBtn.disabled = true
            })
            .catch((e) => console.log(e))
    }

Event handler

Note: When a user starts sharing a screen, a streamAdded event is triggered.

Open the ui.js file and write a new function called addScreenShareNode that creates a video node and attaches the screen stream to it:

const addScreenShareNode = stream => {
  const screenShareContainer = document.getElementById("screenshare-container")
  let screenShareNode = document.getElementById("screenshare")

  if (screenShareNode)
    return alert("There is already a participant sharing his screen !")

  screenShareNode = document.createElement("video")
  screenShareNode.autoplay = "autoplay"
  navigator.attachMediaStream(screenShareNode, stream)

  screenShareContainer.appendChild(screenShareNode)
}

Open the client.js file and add a condition upon receiving the streamAdded event to handle the case where the stream.type is ScreenShare:

    VoxeetSDK.conference.on("streamAdded", (participant, stream) => {
        if (stream.type === 'ScreenShare') return addScreenShareNode(stream)
        ...

Add UI for starting sharing the application’s screen in ViewController.swift.

1. In ViewController.swift, add a variable to the ViewController class to refer to the user interface element that will be created in step 2.

class ViewController: UIViewController {
    ...

    // Conference UI.
    var startScreenShareButton: UIButton!

    ...
}

2. Modify initConferenceUI to extend the user interface and enable and disable the startScreenShareButton as appropriate.

...

func initConferenceUI() {
    ...

    // Start screen share button.
    startScreenShareButton = UIButton(type: .system) as UIButton
    startScreenShareButton.frame = CGRect(x: 100, y: participantsLabel.frame.origin.y + participantsLabel.frame.height + 16, width: 100, height: 30)
    startScreenShareButton.isEnabled = false
    startScreenShareButton.isSelected = true
    startScreenShareButton.setTitle("STARTSCREEN", for: .normal)
    startScreenShareButton.addTarget(self, action: #selector(startScreenShareAction), for: .touchUpInside)
    self.view.addSubview(startScreenShareButton)
}

...

@objc func startButtonAction(sender: UIButton!) {
    ...
        // Join the conference with its id.
        VoxeetSDK.shared.conference.join(conference: conference, success: { response in

            ...
            self.startScreenShareButton.isEnabled = true /* Update startScreenShare button state */

        }, fail: { error in })
    }, fail: { error in })
}

@objc func leaveButtonAction(sender: UIButton!) {
    VoxeetSDK.shared.conference.leave { error in

        ...
        self.startScreenShareButton.isEnabled = false /* Update startScreenShare button state */

    }
}

3. Add a method to start sharing the application’s screen.

...

@objc func startScreenShareAction(sender: UIButton!) {
    if #available(iOS 11.0, *) {
        VoxeetSDK.shared.conference.startScreenShare { error in
            if error == nil {
                self.startScreenShareButton.isEnabled = false
            }
        }
    }
}

4. In ViewController.streamUpdated modify the .ScreenShare case to handle screen share streams.

extension ViewController: VTConferenceDelegate {
    ...

    func streamUpdated(participant: VTParticipant, stream: MediaStream) {
        switch stream.type {
        case .Camera:
            ...
        case .ScreenShare:
            if participant.id == VoxeetSDK.shared.session.participant?.id {
                if !stream.videoTracks.isEmpty {
                    videosView1.attach(participant: participant, stream: stream)
                }
            } else {
                if !stream.videoTracks.isEmpty {
                    videosView2.attach(participant: participant, stream: stream)
                }
            }
        default: break
        }
    }

    ...
}

Layout modification

1. To modify the layout, edit the main_activity.xml file, adding the following content for Step 5:

<LinearLayout ...>

    ...

    <!-- Step 5. Put the layout changes for the screen sharing step here -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/startScreenShare"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="start screenshare" />

        <!-- Step 5.2. This layout will be upgraded in the stop screen sharing step -->
    </LinearLayout>

    <!-- Step 6. ...

</LinearLayout>

2. Modify the interface linking in the MainActivity class in MainActivity.java:

  • New method for MainActivity:
@OnClick(R.id.startScreenShare)
public void onStartScreenShare() {

}
  • Add the following code to the onCreate method:
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    //adding the startScreenShare in the flow
    add(views, R.id.startScreenShare);
    add(buttonsInConference, R.id.startScreenShare);
    add(buttonsNotInOwnScreenShare, R.id.startScreenShare);
}

3. Add the following logic to the application:

  • In updateViews, enable and disable buttons based on the own video state.
private void updateViews() {
    ...

    if (null != current) {
        if (current.isScreenShareOn()) {
            setEnabled(buttonsInOwnScreenShare, true);
            setEnabled(buttonsNotInOwnScreenShare, false);
        } else {
            setEnabled(buttonsInOwnScreenShare, false);
            setEnabled(buttonsNotInOwnScreenShare, true);
        }
    }
}
  • In updateStreams, use a screen share stream in preference to a video stream:
private void updateStreams() {
    ...

    // Screen shares take precedence over videos
    for (Participant user : VoxeetSDK.conference().getParticipants()) {
        boolean isLocal = user.getId().equals(VoxeetSDK.session().getParticipantId());
        MediaStream stream = user.streamsHandler().getFirst(MediaStreamType.ScreenShare);

        VideoView video = isLocal ? this.video : this.videoOther;

        if (null != stream && !stream.videoTracks().isEmpty()) {
            video.setVisibility(View.VISIBLE);
            video.attach(user.getId(), stream);
        }
    }
}
  • Use the following implementation onStartScreenShare:
public void onStartScreenShare() {
    VoxeetSDK.screenShare().sendRequestStartScreenShare();
}
  • Add a handler for the RequestScreenSharePermissionEvent.
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(RequestScreenSharePermissionEvent event) {
        VoxeetSDK.screenShare().sendUserPermissionRequest(this);
    }
  • Add a handler for activity results with an override of onActivityResult.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    boolean managed = false;

    if (null != VoxeetSDK.screenShare()) {
        managed = VoxeetSDK.screenShare().onActivityResult(requestCode, resultCode, data);
    }

    if (!managed) {
        super.onActivityResult(requestCode, resultCode, data);
    }
}
  • Modify onResume to consuming the result of the screen sharing request.
@Override
protected void onResume() {
    ...

    VoxeetSDK.screenShare().consumeRightsToScreenShare();
}

After the screenshare is started, UserStreamUpdated is triggered.

Stop sharing a screen

  • JavaScript
  • Swift
  • Java

Layout editing

Open the index.html file and add a button with the id stop-screenshare-btn as a child of the actions div:

<div id="actions">
  ...
  <button id="stop-screenshare-btn" disabled>Stop screen share</button>
</div>
...

Interface linking

Get element from document

Open the ui.js file and declare stopScreenshareBtn in initUI:

const initUI = () => {
  const stopScreenShareBtn = document.getElementById("stop-screenshare-btn")
}

Enable/disable the button

Edit the startScreenShareBtn onclick handler to enable the button to stop sharing the screen:

startScreenShareBtn.onclick = () => {
    VoxeetSDK.conference.startScreenShare()
        .then(() =>
            ...
            stopScreenShareBtn.disabled = false
        ...

Then, edit the leaveButton onclick handler to disable the button when the conference is left:

leaveButton.onclick = () => {
    VoxeetSDK.conference.leave()
        .then(() => {
            ...
            stopScreenShareBtn.disabled = true
    ...

Logic implementation

Onclick handler

Open the ui.js file and write the onclick function for the stopScreenShareBtn in the initUI function:

const initUI = () => {
    ...
    stopScreenShareBtn.onclick = () => {
        VoxeetSDK.conference.stopScreenShare()
            .then(() => {
                startScreenShareBtn.disabled = false
                stopScreenShareBtn.disabled = true
            })
            .catch((e) => console.log(e))
    }

Event handler

Note: When a user stops sharing a screen, a streamRemoved event is triggered.

Open the ui.js file and write a new function called removeScreenShareNode that creates a video node and attaches the screen related stream to it:

const removeScreenShareNode = () => {
  let screenShareNode = document.getElementById("screenshare")

  if (screenShareNode) {
    screenShareNode.parentNode.removeChild(screenShareNode)
  }
}

Open the client.js file and add a condition upon receiving a streamRemoved event to handle the case when the stream.type is ScreenShare:

VoxeetSDK.conference.on('streamRemoved', (participant, stream) => {
    if (stream.type === 'ScreenShare') return removeScreenShareNode()
    ...

Add UI for stopping the sharing of the application’s screen in ViewController.swift.

1. In ViewController.swift, add a variable to the ViewController class to refer to the user interface element that will be created in step 2.

class ViewController: UIViewController {
    ...

    // Conference UI.
    var stopScreenShareButton: UIButton!

    ...
}

2. Modify initConferenceUI to extend the user interface and enable & disable the stopScreenShareButton as appropriate.

...

func initConferenceUI() {
    ...

    // Stop screen share button.
    stopScreenShareButton = UIButton(type: .system) as UIButton
    stopScreenShareButton.frame = CGRect(x: 200, y: participantsLabel.frame.origin.y + participantsLabel.frame.height + 16, width: 100, height: 30)
    stopScreenShareButton.isEnabled = false
    stopScreenShareButton.isSelected = true
    stopScreenShareButton.setTitle("STOPSCREEN", for: .normal)
    stopScreenShareButton.addTarget(self, action: #selector(stopScreenShareAction), for: .touchUpInside)
    self.view.addSubview(stopScreenShareButton)
}

...

@objc func leaveButtonAction(sender: UIButton!) {
    VoxeetSDK.shared.conference.leave { error in

        ...
        self.stopScreenShareButton.isEnabled = false /* Update stopScreenShare button state */

    }
}

@objc func startScreenShareAction(sender: UIButton!) {
    if #available(iOS 11.0, *) {
        VoxeetSDK.shared.conference.startScreenShare { error in
            if error == nil {

                ...
                self.stopScreenShareButton.isEnabled = true /* Update stopScreenShare button state */

            }
        }
    }
}

3. Add a method to stop sharing the screen.

...

@objc func stopScreenShareAction(sender: UIButton!) {
    if #available(iOS 11.0, *) {
        VoxeetSDK.shared.conference.stopScreenShare { error in
            if error == nil {
                self.startScreenShareButton.isEnabled = true /* Update startScreenShare button state */
                self.stopScreenShareButton.isEnabled = false /* Update stopScreenShare button state */
            }
        }
    }
}

...

Layout modification

1. To modify the layout, edit the main_activity.xml file, adding the following content for Step 5.2:

<LinearLayout ...>
    ...
    <LinearLayout ...>
        ...

        <!-- Step 5.2. This layout will be upgraded in the stop screen sharing step -->
        <Button
            android:id="@+id/stopScreenShare"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="stop screenshare" />

    </LinearLayout>

    <!-- Step 6. ...

</LinearLayout>

2. Modify the interface linking in the MainActivity class in MainActivity.java:

  • New method for MainActivity:
@OnClick(R.id.stopScreenShare)
public void onStopScreenShare() {

}
  • Add the following code to the onCreate method:
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    //adding the stopScreenShare in the flow
    add(views, R.id.stopScreenShare);
    add(buttonsInConference, R.id.stopScreenShare);
    add(buttonsInOwnScreenShare, R.id.stopScreenShare);
}

3. Add the following logic to the application:

  • Use the following implementation for onStopScreenShare:
public void onStopScreenShare() {
    VoxeetSDK.screenShare().stopScreenShare().then((result, solver) -> {
        //screenshare has been stopped locally and remotely
        updateViews();
    }).error(error -> {
        //screenshare has been stopped locally but a network error occured
    });
}

After the screenshare is stopped, UserStreamUpdated is triggered.

What’s next?

If you want to learn more about creating conference applications, go to the Record the conference tutorial.