openvidu-toggle-hand 🔗

Check it on GitHub

openvidu-toggle-hand is an Angular app developed with openvidu-angular library and its powerful components for customization. It features a toggle hand button that allows participants to ask for a turn to speak. This tutorial uses the addition of a very simple feature to demonstrate the power of openvidu-angular library and how to customize and extend its pre-built components.


Running this tutorial 🔗

To run the tutorial you need the three components stated in OpenVidu application architecture: an OpenVidu deployment, your server application and your client application. In this order:

1. Run OpenVidu deployment 🔗

Using Docker Engine:

# WARNING: this container is not suitable for production deployments of OpenVidu
# Visit https://docs.openvidu.io/en/stable/deployment

docker run -p 4443:4443 --rm -e OPENVIDU_SECRET=MY_SECRET openvidu/openvidu-dev:2.23.0

2. Run your preferred server application sample 🔗

For more information visit Application server.

3. Run the client application tutorial 🔗

You need NPM and Angular CLI to serve the application. Check them with the following command:

npm -v
ng v

To serve the tutorial:

# Using the same repository openvidu-tutorials from step 2

cd openvidu-tutorials/openvidu-components/openvidu-toggle-hand
npm install
ng serve

Go to http://localhost:4200 to test the app once the server is running.

To test the application with other devices in your network, visit this FAQ


Understanding the code 🔗

This application is a simple Angular app using openvidu-angular library. It uses prebuilt components to

The files we are going to focus on are the following ones:

  • app/app.module.ts: app file where openvidu-angular library is initialized.
  • app/app.component.ts: component file with all the logic. It requests OpenVidu tokens and handles the toggle hand feature.
  • app/app.component.html: component template view where openvidu-angular components can be placed and customized.

First, we need to install the openvidu-angular library. You can check how to do that here. After that we are ready to include openvidu-angular components into our app.

Let's focus on the app.component.html template to see how to customize the VideoconferenceComponent to achieve the toggle hand feature.

For remote participants, we need to know when someone has requested the turn to speak and show an icon on top of their video stream, so everybody knows they want to take the floor. To achieve this we subscribe to the onSessionCreated output event of ov-videoconference element.

<ov-videoconference (onSessionCreated)="onSessionCreated($event)" [tokens]="tokens" [prejoin]="true">
    ...
</ov-videoconference>

The onSessionCreated method is invoked when the OpenVidu Session has been initialized. We use this event to subscribe to the handToggle signal so we know when a remote participant has raised their hand. To do this, we use the signal method provided by openvidu-browser SDK.

onSessionCreated(session: Session) {
    this.session = session;
    this.handleRemoteHand();
}

handleRemoteHand() {
    // Subscribe to hand toggling events from other participants
    this.session.on(`signal:${SignalApp.HAND_TOGGLE}`, (event: any) => {
        const connectionId = event.from.connectionId;
        const participant = <ParticipantAppModel>this.participantService.getRemoteParticipantByConnectionId(connectionId);
        if (participant) {
            participant.toggleHandRaised();
            this.participantService.updateRemoteParticipants();
        }
    });
}

For the local participant, we need to include a custom button in the ToolbarComponent. We do so by adding an element tagged with the ToolbarAdditionalButtonsDirective (*ovToolbarAdditionalButtons).

<ov-videoconference (onSessionCreated)="onSessionCreated($event)" [tokens]="tokens" [prejoin]="true">

    <div *ovToolbarAdditionalButtons>
        <button toolbar-btn mat-icon-button (click)="handleLocalHand()" [class.active-btn]="hasHandRaised">
            <mat-icon matTooltip="Toggle hand">front_hand</mat-icon>
        </button>
    </div>

    ...

</ov-videoconference>

This button allows the local user to raise their hand. Whenever it is clicked, method handleLocalHand is called.

handleLocalHand() {
    // Get local participant with ParticipantService
    const participant = <ParticipantAppModel>this.participantService.getLocalParticipant();

    // Toggle the participant hand with the method we wil add in our ParticipantAppModel
    participant.toggleHandRaised();

    // Refresh the local participant object for others component and services
    this.participantService.updateLocalParticipant();

    // Send a signal with the new value to others participant using the openvidu-browser signal
    const remoteConnections = this.openviduService.getRemoteConnections();
    if (remoteConnections.length > 0) {
        //Sending hand toggle signal to others
        const signalOptions: SignalOptions = {
            type: SignalApp.HAND_TOGGLE,
            to: remoteConnections
        };
        this.session.signal(signalOptions);
    }
}

This method shows an icon on the local video to let the user know they have requested the turn to speak, and send a handToggle signal to the rest of users. This signal will trigger the handleRemoteHand method as explained above.

We also need to customize the StreamComponent injecting a new element when a participant raises their hand:

<ov-videoconference (onSessionCreated)="onSessionCreated($event)" [tokens]="tokens" [prejoin]="true">

    ...

    <div *ovStream="let stream" style="height: 100%">
        <ov-stream [stream]="stream"></ov-stream>
        <button mat-icon-button @inOutHandAnimation id="hand-notification" *ngIf="stream.participant.hasHandRaised">
            <mat-icon>front_hand</mat-icon>
        </button>
    </div>

    ...

</ov-videoconference>

Now, using the StreamDirective (*ovStream) we can customize the StreamComponent and we can add whatever we want to it. In this case we simply add an icon button indicating the participant has raised their hand.

We also add the hand notification to the participants panel. We handle this using the ParticipantPanelItemElementsDirective (*ovParticipantPanelItemElements). In this case we simply add an icon button indicating the participant has raised their hand.

<ov-videoconference (onSessionCreated)="onSessionCreated($event)" [tokens]="tokens" [prejoin]="true">

    ...

    <div *ovParticipantPanelItemElements="let participant">
        <button mat-icon-button *ngIf="participant.hasHandRaised">
            <mat-icon>front_hand</mat-icon>
        </button>
    </div>

</ov-videoconference>

As you can see we assign a participant variable to the *ovParticipantPanelItemElements directive (let participant). In this way we are able access the participant's properties in our custom HTML template. In this case, the hasHandRaised property.

Following this line of reasoning, we need a new property (hasHandRaised) in the ParticipantAbstractModel. We have to extend the ParticipantAbstractModel from openvidu-angular library. See ParticipantAppModel class under app/models/ folder:

import { ParticipantAbstractModel } from 'openvidu-angular';

export class ParticipantAppModel extends ParticipantAbstractModel {

    hasHandRaised: boolean;

    toggleHandRaised() {
        this.hasHandRaised = !this.hasHandRaised;
    }

}

Once we have extended the ParticipantAbstractModel, we need to configure openvidu-angular library to use this new class. To achieve this we add a participantFactory in app.module.ts and assign it to the OpenViduAngularConfig:

const config: OpenViduAngularConfig = {
    production: environment.production,
    participantFactory: (props: ParticipantProperties, streamModel: StreamModel) => new ParticipantAppModel(props, streamModel)
};

Now we can get our custom hasHandRaised participant property in the ParticipantsPanelComponent and in the StreamComponent.