openvidu/openvidu-testapp/src/app/services/room-api.service.ts

381 lines
11 KiB
TypeScript

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import {
AccessToken,
AgentDispatchClient,
CreateIngressOptions,
DirectFileOutput,
EgressClient,
EgressInfo,
EncodedOutputs,
EncodingOptions,
EncodingOptionsPreset,
IngressClient,
IngressInfo,
IngressInput,
IngressVideoEncodingPreset,
ParticipantInfo,
Room,
RoomCompositeOptions,
RoomServiceClient,
TrackCompositeOptions,
TrackInfo,
VideoGrant,
} from 'livekit-server-sdk';
import { LivekitParamsService } from './livekit-params.service';
import { VideoQuality } from 'livekit-client';
import {
AgentDispatch,
IngressVideoEncodingOptions,
JobRestartPolicy,
VideoCodec,
VideoLayer,
} from '@livekit/protocol';
export const DEFAULT_URI_HTTP_VIDEO_AUDIO =
'https://s3.eu-west-1.amazonaws.com/public.openvidu.io/bbb_sunflower_1080p_60fps_normal.mp4';
export const DEFAULT_URI_HTTP_ONLY_VIDEO =
'https://s3.eu-west-1.amazonaws.com/public.openvidu.io/bbb_sunflower_1080p_60fps_normal_noaudio.mp4';
export const DEFAULT_URI_HTTP_ONLY_AUDIO =
'https://s3.eu-west-1.amazonaws.com/public.openvidu.io/bbb_sunflower_1080p_60fps_normal_onlyaudio.mp3';
export const DEFAULT_URI_RTSP = 'rtsp://127.0.0.1:8554/live';
export const DEFAULT_URI_SRT = 'srt://127.0.0.1:8554/';
@Injectable({
providedIn: 'root',
})
export class RoomApiService {
private roomServiceClient: RoomServiceClient;
private egressClient: EgressClient;
private ingressClient: IngressClient;
private agentDispatchClient: AgentDispatchClient;
constructor(
private http: HttpClient,
private livekitParamsService: LivekitParamsService
) {
this.roomServiceClient = new RoomServiceClient(
this.getRestUrl(),
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret
);
this.egressClient = new EgressClient(
this.getRestUrl(),
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret
);
this.ingressClient = new IngressClient(
this.getRestUrl(),
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret
);
this.agentDispatchClient = new AgentDispatchClient(
this.getRestUrl(),
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret
);
}
async createToken(
permissions: VideoGrant,
participantName?: string,
roomName?: string
): Promise<string> {
const at = new AccessToken(
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret,
{ identity: participantName }
);
if (roomName) {
permissions.room = roomName;
}
at.addGrant(permissions);
return at.toJwt();
}
/*
* RoomService API
* https://docs.livekit.io/reference/server/server-apis/
*/
async listRooms(): Promise<Room[]> {
return this.roomServiceClient.listRooms();
}
async deleteRoom(roomName: string): Promise<void> {
return await this.roomServiceClient.deleteRoom(roomName);
}
async listParticipants(roomName: string): Promise<ParticipantInfo[]> {
return await this.roomServiceClient.listParticipants(roomName);
}
async getParticipant(
roomName: string,
participantIdentity: string
): Promise<ParticipantInfo> {
return await this.roomServiceClient.getParticipant(
roomName,
participantIdentity
);
}
async removeParticipant(
roomName: string,
participantIdentity: string
): Promise<void> {
return await this.roomServiceClient.removeParticipant(
roomName,
participantIdentity
);
}
async mutePublishedTrack(
roomName: string,
participantIdentity: string,
track_sid: string,
muted: boolean
): Promise<TrackInfo> {
return await this.roomServiceClient.mutePublishedTrack(
roomName,
participantIdentity,
track_sid,
muted
);
}
async listEgress(): Promise<EgressInfo[]> {
return await this.egressClient.listEgress({});
}
async startRoomCompositeEgress(
roomName: string,
roomCompositeOptions: RoomCompositeOptions,
encodedOutputs: EncodedOutputs,
encodingOptions?: EncodingOptionsPreset | EncodingOptions
): Promise<EgressInfo> {
if (encodedOutputs.file) {
encodedOutputs.file.filepath =
'RoomComposite-{room_id}-{room_name}-{time}';
}
if (encodingOptions) {
roomCompositeOptions.encodingOptions = encodingOptions;
}
return await this.egressClient.startRoomCompositeEgress(
roomName,
encodedOutputs,
roomCompositeOptions
);
}
async startTrackCompositeEgress(
roomName: string,
audioTrackId: string,
videoTrackId: string,
encodedOutputs: EncodedOutputs,
encodingOptions?: EncodingOptionsPreset | EncodingOptions
): Promise<EgressInfo> {
if (encodedOutputs.file) {
encodedOutputs.file.filepath =
'TrackComposite-{room_id}-{room_name}-{time}-{publisher_identity}';
}
const trackCompositeOptions: TrackCompositeOptions = {
audioTrackId,
videoTrackId,
};
if (encodingOptions) {
trackCompositeOptions.encodingOptions = encodingOptions;
}
return await this.egressClient.startTrackCompositeEgress(
roomName,
encodedOutputs,
trackCompositeOptions
);
}
async startTrackEgress(
roomName: string,
track_id: string,
output?: DirectFileOutput | string
): Promise<EgressInfo> {
if (!output) {
let outputAux = {
filepath:
'Track-{room_id}-{room_name}-{time}-{publisher_identity}-{track_id}-{track_type}-{track_source}',
};
output = outputAux as DirectFileOutput;
}
return await this.egressClient.startTrackEgress(roomName, output, track_id);
}
async stopEgress(egressId: string): Promise<EgressInfo> {
return await this.egressClient.stopEgress(egressId);
}
async listIngress(): Promise<IngressInfo[]> {
const ingressClient: IngressClient = new IngressClient(
this.getRestUrl(),
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret
);
return await ingressClient.listIngress({});
}
async createIngress(
room_name: string,
inputType: IngressInput,
urlInputType: string,
urlUri: string,
withAudio: boolean,
withVideo: boolean,
codec: VideoCodec,
simulcast: boolean,
enableTranscoding: boolean,
preset?: IngressVideoEncodingPreset
): Promise<IngressInfo> {
let url;
if (inputType === IngressInput.URL_INPUT) {
if (urlUri) {
url = urlUri;
} else {
switch (urlInputType) {
case 'HTTP':
if (withVideo && withAudio) {
url = DEFAULT_URI_HTTP_VIDEO_AUDIO;
} else {
if (withVideo) {
url = DEFAULT_URI_HTTP_ONLY_VIDEO;
} else {
url = DEFAULT_URI_HTTP_ONLY_AUDIO;
}
}
break;
case 'SRT':
url = DEFAULT_URI_SRT;
break;
case 'RTSP':
url = DEFAULT_URI_RTSP;
break;
default:
const errorMsg = 'Invalid URL type';
console.error(errorMsg);
window.alert(errorMsg);
throw new Error(errorMsg);
}
}
}
let options: CreateIngressOptions = {
name: inputType + '-' + room_name,
roomName: room_name,
participantIdentity: 'IngressParticipantIdentity',
participantName: 'MyIngress',
participantMetadata: 'IngressParticipantMetadata',
url,
};
if (inputType === IngressInput.WHIP_INPUT) {
options.enableTranscoding = enableTranscoding;
}
if (inputType != IngressInput.WHIP_INPUT || enableTranscoding) {
options.video = {
encodingOptions: {
case: preset != undefined ? 'preset' : 'options',
value: {},
},
} as any;
if (preset != undefined) {
options.video!.encodingOptions.value = preset;
} else {
const encodingOptions = options.video!.encodingOptions
.value! as IngressVideoEncodingOptions;
encodingOptions.videoCodec = codec;
encodingOptions.frameRate = 30;
let layers: VideoLayer[] = [];
if (simulcast) {
layers = [
{
quality: VideoQuality.HIGH,
width: 1920,
height: 1080,
},
{
quality: VideoQuality.MEDIUM,
width: 1280,
height: 720,
},
{
quality: VideoQuality.LOW,
width: 640,
height: 360,
},
] as VideoLayer[];
} else {
layers = [
{
quality: VideoQuality.HIGH,
width: 1920,
height: 1080,
} as VideoLayer,
];
}
encodingOptions.layers = layers;
}
}
const ingressInfo = await this.ingressClient.createIngress(
inputType,
options
);
return ingressInfo;
}
async deleteIngress(ingressId: string): Promise<IngressInfo> {
const ingressClient: IngressClient = new IngressClient(
this.getRestUrl(),
this.livekitParamsService.getParams().livekitApiKey,
this.livekitParamsService.getParams().livekitApiSecret
);
return await ingressClient.deleteIngress(ingressId);
}
/*
* Agent Dispatch API
* https://docs.livekit.io/reference/agents/agent-dispatch-service-api/
*/
async createDispatch(
roomName: string,
agentName: string,
metadata?: string,
restartPolicy?: JobRestartPolicy
): Promise<AgentDispatch> {
return await this.agentDispatchClient.createDispatch(roomName, agentName, {
metadata,
restartPolicy,
});
}
async getDispatch(
dispatchId: string,
roomName: string
): Promise<AgentDispatch | undefined> {
return await this.agentDispatchClient.getDispatch(dispatchId, roomName);
}
async listDispatch(roomName: string): Promise<AgentDispatch[]> {
return await this.agentDispatchClient.listDispatch(roomName);
}
async deleteDispatch(dispatchId: string, roomName: string): Promise<void> {
return await this.agentDispatchClient.deleteDispatch(dispatchId, roomName);
}
private getRestUrl() {
const wsUrl = this.livekitParamsService.getParams().livekitUrl;
const protocol =
wsUrl.startsWith('wss:') || wsUrl.startsWith('https:') ? 'https' : 'http';
return `${protocol}://${wsUrl
.substring(wsUrl.indexOf('//') + 2)
.replace(/\/$/, '')}`;
}
}