Implement screen sharing

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

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

Start sharing a screen

Layout editing

Open the index.html file and add:

  • the start-screenshare-btn button as a child of the actions div
  • the screenshare-container div 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);
        ...

Layout modification

class ViewController: UIViewController {
    ...

    // Conference UI.
    var startScreenShareButton: UIButton!

    ...
}

Interface linking

...

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 */
        
    }
}

Logic implementation

    ...

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

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

Edit the main_activity.xml with the following items :

<LinearLayout ...>

    ...
    
    <!-- Step 7. Please put below the layout modification with the screen sharing step (start) -->

    <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 7.2 Please put below the layout modification with the screen sharing step (stop) -->
    </LinearLayout>

</LinearLayout>

Interface linking

Fields for MainActivity

Add the following into 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);
}

Methods for MainActivity

And create the following methods :

@OnClick(R.id.startScreenShare)
public void onStartScreenShare() {

}

Start implementation

Implementation for onStartScreenShare

public void onStartScreenShare() {
    VoxeetSDK.screenShare().sendRequestStartScreenShare();
}

Implementation for the permission event RequestScreenSharePermissionEvent

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(RequestScreenSharePermissionEvent event) {
        VoxeetSDK.screenShare().sendUserPermissionRequest(this);
    }

Implementation for the screen share result onActivityResult

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    boolean managed = VoxeetSDK.screenShare().onActivityResult(requestCode, resultCode, data);

    if (!managed) {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Implementation for the consuming the result

@Override
protected void onResume() {
    ...

    VoxeetSDK.screenShare().consumeRightsToScreenShare();
}

After the screenshare has been properly started, UserStreamUpdated will be triggered.

Stop sharing a screen

Layout editing

Open the index.html file and add the start-screenshare-btn button 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 the stopScreenshareBtn in the initUI:

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

Enable/disable the button

Edit the startScreenShareBtn onclick handler to enable the user 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 starts 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();
    ...

Layout modification

class ViewController: UIViewController {
    ...

    // Conference UI.
    var stopScreenShareButton: UIButton!

    ...
}

Interface linking

...

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 */

            }
        }
    }
}

Logic implementation

...

@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

Edit the main_activity.xml with the following items :

<LinearLayout ...>

    ...
    
    <!-- Step 7. Please put below the layout modification with the screen sharing step (start) -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        
        ...

        <!-- Step 7.2 Please put below the layout modification with the screen sharing step (stop) -->

        <Button
            android:id="@+id/stopScreenShare"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="stop screenshare" />
    </LinearLayout>

</LinearLayout>

Interface linking

Fields for MainActivity

Add the following into 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);
}

Methods for MainActivity

And create the following methods :

@OnClick(R.id.stopScreenShare)
public void onStopScreenShare() {
    VoxeetSDK.screenShare().stopScreenShare().then((result, solver) -> {
        //screenshare has been stopped locally and remotely
    }).error(error -> {
        //screenshare has been stopped locally but mostly network error occured
    });
}

Stop implementation

Event for onStartScreenShare

After the screenshare has been properly stopped, UserStreamUpdated will be triggered.

What's next ?

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