This SDK is for developing frontend for FlipFlop Cloud Applications.
The SDK documentation can be found here.
npm install @jocoos/ffc-sdk-client-javascript
yarn add @jocoos/ffc-sdk-client-javascript
The SDK can be targeted to either the sandbox or production environment and assumes its use for a logged in user. Therefore, you must initialize it with the API endpoint of the targeted environment and member access token for your FlipFlop/Vicollo app in FlipFlop Cloud.
import { FlipFlopCloud } from '@jocoos/ffc-sdk-client-javascript'
// Constant indicating whether the targeted FlipFlop Cloud environment is sandbox or production
const FFC_ENV = 'sandbox'; // or 'produdction'
// Constant for the FlipFlop Cloud API endpoint
const FFC_ENDPOINT_BASE_URL = FFC_ENV === 'sandbox'
? 'https://api-sandbox.flipflop.cloud'
: 'https://api.flipflop.cloud';
// Constant for the user's member access token (must be obtained from your service backed)
const FFC_USER_MEMBER_ACCESS_TOKEN = '[REPLACE_THIS_WITH_ACTUAL_TOKEN_STRING]';
// Iniitialize SDK
FlipFlopCloud.init(FFC_ENDPOINT_BASE_URL, FFC_USER_MEMBER_ACCESS_TOKEN);
The following are the signatures of the static methods of FlipFlopCloud class that handles Rest API requests.
// Get information of myself(the user which the member access token was issued to)
getMe(): Promise<FFCMemberDto>
// Create video room
createVideoRoom(params: FFCCreateVideoRoomParams): Promise<FFCVideoRoomDto>
// List video rooms
listVideoRooms(query?: FFCListVideoRoomsQuery): Promise<FFCPagesDto<FFCVideoRoomDto>>
// Get video room
getVideoRoom(videoRoomId: number): Promise<FFCVideoRoomDto>
// Update video room
updateVideoRoom(videoRoomId: number, params: FFCUpdateVideoRoomParams): Promise<FFCVideoRoomDto>
// Delete video room
deleteVideoRoom(videoRoomId: number): Promise<void>
// Issue token to join video room
issueRtcVideoRoomToken(videoRoomId: number, params?: FFCIssueRtcVideoRoomTokenParams): Promise<FFCRtcVideoRoomTokenDto>
All operations for joining the video room and publising and subscribing streams work with the FlipFlop Cloud media server. The FFCRtcVideoRoom class provides the API for the operations.
The operations in the video room via FFCRtcVideoRoom can be summerized as the following.
FlipFlop Cloud app member the participant corresponds to.FFCRtcVideoRoom object does not mean joining the video room. To join, the object's connect function must be used.Video Room.FFCRtcVideoRoom object.Create FFCRtcVideoRoom object and prepare for connection.
const FFC_RTC_SERVER_URL = '[OBTAINED_WHEN_ISSUING_VIDEO_ROOM_TOKEN]';
const FFC_RTC_TOKEN = '[OBTAINED_WHEN_ISSUING_VIDEO_ROOM_TOKEN]';
const room = new FFCRtcVideoRoom();
room.prepareConnection(FFC_RTC_SERVER_URL, FFC_RTC_TOKEN);
Set up listeners for FFCRtcVideoRoomEvent.
room.on(/* ROOM EVENT */, /* CALLBACK */)
Connect to room to join.
await room.connect(FFC_RTC_SERVER_URL, FFC_RTC_TOKEN);
Publish by enabling camera and microphone using manually created FFCTrack or MediaStreamTrack.
// Prepare webcam and microphone and create track and track publication so it could be published
room.localParticipant.enableCameraAndMicrophone();
// Or you can create tracks and publish them manually
// Create tracks
const tracks = await createLocalTracks({
video: true,
audio: true,
});
// publish the created tracks
await room.localParticipant.publishTrack(tracks[0]);
await room.localParticipant.publishTrack(tracks[1]);
// Or if you have a MediaStreamTrack you can publish it too.
await room.localParticipant.publishTrack(mediaStreamTrack);
// If FFCRtcVideoRoom object was not created with autoSubscribe option set to true,
// The iniation of a subscription must happen manually.
const REMOTE_PARTICIPANT_IDENTITY = '[IDENTITY_STRING_OF_SOME_PARTICIPANT]';
const TRACK_PUBLICATION_SID = '[PUBLICATION_TRACK_STREAM_ID]';
const remoteParticipant = room.remoteParticipant.get(REMOTE_PARTICIPANT_IDENTITY);
const publication = remoteParticipant.trackPublications.get(TRACK_PUBLICATION_SID);
publication.setSubscribed(true);
// Add event listeners for subscription and unsubsciption
room
.on(FFCRtcVideoRoomEvent.TRACK_SUBSCRIBED, handleTrackSubscribed)
.on(FFCRtcVideoRoomEvent.TRACK_UNSUBSCRIBED, handleTrackUnsubscribed)
function handleTrackSubscribed(
track: FFCRemoteTrack,
publication: FFCRemoteTrackPublication,
participant: FFCRemoteParticipant,
) {
if (track.kind === FFCTrack.Kind.Video || track.kind === FFCTrack.Kind.Audio) {
// attach it to a new HTMLVideoElement or HTMLAudioElement
const element = track.attach();
parentElement.appendChild(element);
}
}
function handleTrackUnsubscribed(
track: FFCRemoteTrack,
publication: FFCRemoteTrackPublication,
participant: FFCRemoteParticipant,
) {
// remove tracks from all attached elements
track.detach();
}
import {
FFCLocalParticipant,
FFCLocalTrackPublication,
FFCParticipant,
FFCRemoteParticipant,
FFCRemoteTrack,
FFCRemoteTrackPublication,
FFCRtcVideoRoom,
FFCRtcVideoRoomEvent,
FFCTrack,
FFCVideoPresets,
} from 'ffc-sdk-client-javascript';
// Use FlipFlopCloud.issueRtcVideoRoomTicket() to retrieve following information
const url = '';
const token = '';
async function main() {
// creates a new room with options
const room = new FFCRtcVideoRoom({
// automatically manage subscribed video quality
adaptiveStream: true,
// optimize publishing bandwidth and CPU for published tracks
dynacast: true,
// default capture settings
videoCaptureDefaults: {
resolution: FFCVideoPresets.h720.resolution,
},
});
// pre-warm connection, this can be called as early as your page is loaded
room.prepareConnection(url, token);
// set up event listeners
room
.on(FFCRtcVideoRoomEvent.TRACK_SUBSCRIBED, handleTrackSubscribed)
.on(FFCRtcVideoRoomEvent.TRACK_UNSUBSCRIBED, handleTrackUnsubscribed)
.on(FFCRtcVideoRoomEvent.ACTIVE_SPEAKERS_CHANGED, handleActiveSpeakerChange)
.on(FFCRtcVideoRoomEvent.DISCONNECTED, handleDisconnect)
.on(FFCRtcVideoRoomEvent.LOCAL_TRACK_UNPUBLISHED, handleLocalTrackUnpublished);
// connect to room
await room.connect(url, token);
console.log('connected to room', room.name);
// publish local camera and mic tracks
await room.localParticipant.enableCameraAndMicrophone();
}
function handleTrackSubscribed(
track: FFCRemoteTrack,
publication: FFCRemoteTrackPublication,
participant: FFCRemoteParticipant,
) {
if (track.kind === FFCTrack.Kind.Video || track.kind === FFCTrack.Kind.Audio) {
const parentElement = document.getElementById('subscriptions')
if (parentElement === null) {
console.error('parent element not found');
return;
}
const element = track.attach();
parentElement.appendChild(element);
}
}
function handleTrackUnsubscribed(
track: FFCRemoteTrack,
publication: FFCRemoteTrackPublication,
participant: FFCRemoteParticipant,
) {
// remove tracks from all attached elements
track.detach();
}
function handleLocalTrackUnpublished(
publication: FFCLocalTrackPublication,
participant: FFCLocalParticipant,
) {
// when local tracks are ended, update UI to remove them from rendering
publication.track?.detach();
}
function handleActiveSpeakerChange(speakers: FFCParticipant[]) {
// show UI indicators when participant is speaking
}
function handleDisconnect() {
console.log('disconnected from room');
}
main().then(() => {
console.log('done');
}).catch((error) => {
console.error('error', error);
});