diff --git a/protocol b/protocol index 8dbc929c..a187ed4f 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit 8dbc929ca430d661bc7385618ac3a49a09cedb8f +Subproject commit a187ed4f546b4f6881313ec4813268d53cb070f8 diff --git a/src/RoomServiceClient.ts b/src/RoomServiceClient.ts index 6415c407..37496ccf 100644 --- a/src/RoomServiceClient.ts +++ b/src/RoomServiceClient.ts @@ -53,6 +53,18 @@ export interface CreateOptions { */ minPlayoutDelay?: number; + /** + * maximum playout delay in milliseconds + */ + maxPlayoutDelay?: number; + + /** + * improves A/V sync when min_playout_delay set to a value larger than 200ms. + * It will disables transceiver re-use -- this option is not recommended + * for rooms with frequent subscription changes + */ + syncStreams?: boolean; + /** * override the node room is allocated to, for debugging * does not work with Cloud diff --git a/src/proto/livekit_egress.ts b/src/proto/livekit_egress.ts index d30b0d04..5e497e23 100644 --- a/src/proto/livekit_egress.ts +++ b/src/proto/livekit_egress.ts @@ -5,6 +5,9 @@ import { AudioCodec, audioCodecFromJSON, audioCodecToJSON, + ImageCodec, + imageCodecFromJSON, + imageCodecToJSON, VideoCodec, videoCodecFromJSON, videoCodecToJSON, @@ -118,6 +121,39 @@ export function segmentedFileSuffixToJSON(object: SegmentedFileSuffix): string { } } +export enum ImageFileSuffix { + IMAGE_SUFFIX_INDEX = 0, + IMAGE_SUFFIX_TIMESTAMP = 1, + UNRECOGNIZED = -1, +} + +export function imageFileSuffixFromJSON(object: any): ImageFileSuffix { + switch (object) { + case 0: + case "IMAGE_SUFFIX_INDEX": + return ImageFileSuffix.IMAGE_SUFFIX_INDEX; + case 1: + case "IMAGE_SUFFIX_TIMESTAMP": + return ImageFileSuffix.IMAGE_SUFFIX_TIMESTAMP; + case -1: + case "UNRECOGNIZED": + default: + return ImageFileSuffix.UNRECOGNIZED; + } +} + +export function imageFileSuffixToJSON(object: ImageFileSuffix): string { + switch (object) { + case ImageFileSuffix.IMAGE_SUFFIX_INDEX: + return "IMAGE_SUFFIX_INDEX"; + case ImageFileSuffix.IMAGE_SUFFIX_TIMESTAMP: + return "IMAGE_SUFFIX_TIMESTAMP"; + case ImageFileSuffix.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + export enum StreamProtocol { /** DEFAULT_PROTOCOL - protocol chosen based on urls */ DEFAULT_PROTOCOL = 0, @@ -325,6 +361,7 @@ export interface RoomCompositeEgressRequest { fileOutputs?: EncodedFileOutput[]; streamOutputs?: StreamOutput[]; segmentOutputs?: SegmentedFileOutput[]; + imageOutputs?: ImageOutput[]; } /** record any website */ @@ -348,6 +385,7 @@ export interface WebEgressRequest { fileOutputs?: EncodedFileOutput[]; streamOutputs?: StreamOutput[]; segmentOutputs?: SegmentedFileOutput[]; + imageOutputs?: ImageOutput[]; } /** record audio and video from a single participant */ @@ -367,6 +405,7 @@ export interface ParticipantEgressRequest { fileOutputs?: EncodedFileOutput[]; streamOutputs?: StreamOutput[]; segmentOutputs?: SegmentedFileOutput[]; + imageOutputs?: ImageOutput[]; } /** containerize up to one audio and one video track */ @@ -398,6 +437,7 @@ export interface TrackCompositeEgressRequest { fileOutputs?: EncodedFileOutput[]; streamOutputs?: StreamOutput[]; segmentOutputs?: SegmentedFileOutput[]; + imageOutputs?: ImageOutput[]; } /** record tracks individually, without transcoding */ @@ -456,6 +496,27 @@ export interface DirectFileOutput { aliOSS?: AliOSSUpload | undefined; } +export interface ImageOutput { + /** in seconds (required) */ + captureInterval?: number; + /** (optional, defaults to track width) */ + width?: number; + /** (optional, defaults to track height) */ + height?: number; + /** (optional) */ + filenamePrefix?: string; + /** (optional, default INDEX) */ + filenameSuffix?: ImageFileSuffix; + /** (optional) */ + imageCodec?: ImageCodec; + /** disable upload of manifest file (default false) */ + disableManifest?: boolean; + s3?: S3Upload | undefined; + gcp?: GCPUpload | undefined; + azure?: AzureBlobUpload | undefined; + aliOSS?: AliOSSUpload | undefined; +} + export interface S3Upload { accessKey?: string; secret?: string; @@ -532,6 +593,12 @@ export interface UpdateStreamRequest { removeOutputUrls?: string[]; } +export interface UpdateOutputsRequest { + egressId?: string; + addImageOutputs?: ImageOutput[]; + removeImageOutputs?: ImageOutput[]; +} + export interface ListEgressRequest { /** (optional, filter by room name) */ roomName?: string; @@ -578,6 +645,7 @@ export interface EgressInfo { streamResults?: StreamInfo[]; fileResults?: FileInfo[]; segmentResults?: SegmentsInfo[]; + imageResults?: ImagesInfo[]; } /** @deprecated */ @@ -654,6 +722,12 @@ export interface SegmentsInfo { endedAt?: number; } +export interface ImagesInfo { + imageCount?: number; + startedAt?: number; + endedAt?: number; +} + export interface AutoParticipantEgress { /** (default H264_720P_30) */ preset?: @@ -690,6 +764,7 @@ function createBaseRoomCompositeEgressRequest(): RoomCompositeEgressRequest { fileOutputs: [], streamOutputs: [], segmentOutputs: [], + imageOutputs: [], }; } @@ -740,6 +815,11 @@ export const RoomCompositeEgressRequest = { SegmentedFileOutput.encode(v!, writer.uint32(106).fork()).ldelim(); } } + if (message.imageOutputs !== undefined && message.imageOutputs.length !== 0) { + for (const v of message.imageOutputs) { + ImageOutput.encode(v!, writer.uint32(114).fork()).ldelim(); + } + } return writer; }, @@ -789,6 +869,9 @@ export const RoomCompositeEgressRequest = { case 13: message.segmentOutputs!.push(SegmentedFileOutput.decode(reader, reader.uint32())); break; + case 14: + message.imageOutputs!.push(ImageOutput.decode(reader, reader.uint32())); + break; default: reader.skipType(tag & 7); break; @@ -818,6 +901,9 @@ export const RoomCompositeEgressRequest = { segmentOutputs: Array.isArray(object?.segmentOutputs) ? object.segmentOutputs.map((e: any) => SegmentedFileOutput.fromJSON(e)) : [], + imageOutputs: Array.isArray(object?.imageOutputs) + ? object.imageOutputs.map((e: any) => ImageOutput.fromJSON(e)) + : [], }; }, @@ -851,6 +937,11 @@ export const RoomCompositeEgressRequest = { } else { obj.segmentOutputs = []; } + if (message.imageOutputs) { + obj.imageOutputs = message.imageOutputs.map((e) => e ? ImageOutput.toJSON(e) : undefined); + } else { + obj.imageOutputs = []; + } return obj; }, @@ -877,6 +968,7 @@ export const RoomCompositeEgressRequest = { message.fileOutputs = object.fileOutputs?.map((e) => EncodedFileOutput.fromPartial(e)) || []; message.streamOutputs = object.streamOutputs?.map((e) => StreamOutput.fromPartial(e)) || []; message.segmentOutputs = object.segmentOutputs?.map((e) => SegmentedFileOutput.fromPartial(e)) || []; + message.imageOutputs = object.imageOutputs?.map((e) => ImageOutput.fromPartial(e)) || []; return message; }, }; @@ -895,6 +987,7 @@ function createBaseWebEgressRequest(): WebEgressRequest { fileOutputs: [], streamOutputs: [], segmentOutputs: [], + imageOutputs: [], }; } @@ -942,6 +1035,11 @@ export const WebEgressRequest = { SegmentedFileOutput.encode(v!, writer.uint32(90).fork()).ldelim(); } } + if (message.imageOutputs !== undefined && message.imageOutputs.length !== 0) { + for (const v of message.imageOutputs) { + ImageOutput.encode(v!, writer.uint32(106).fork()).ldelim(); + } + } return writer; }, @@ -988,6 +1086,9 @@ export const WebEgressRequest = { case 11: message.segmentOutputs!.push(SegmentedFileOutput.decode(reader, reader.uint32())); break; + case 13: + message.imageOutputs!.push(ImageOutput.decode(reader, reader.uint32())); + break; default: reader.skipType(tag & 7); break; @@ -1016,6 +1117,9 @@ export const WebEgressRequest = { segmentOutputs: Array.isArray(object?.segmentOutputs) ? object.segmentOutputs.map((e: any) => SegmentedFileOutput.fromJSON(e)) : [], + imageOutputs: Array.isArray(object?.imageOutputs) + ? object.imageOutputs.map((e: any) => ImageOutput.fromJSON(e)) + : [], }; }, @@ -1048,6 +1152,11 @@ export const WebEgressRequest = { } else { obj.segmentOutputs = []; } + if (message.imageOutputs) { + obj.imageOutputs = message.imageOutputs.map((e) => e ? ImageOutput.toJSON(e) : undefined); + } else { + obj.imageOutputs = []; + } return obj; }, @@ -1073,6 +1182,7 @@ export const WebEgressRequest = { message.fileOutputs = object.fileOutputs?.map((e) => EncodedFileOutput.fromPartial(e)) || []; message.streamOutputs = object.streamOutputs?.map((e) => StreamOutput.fromPartial(e)) || []; message.segmentOutputs = object.segmentOutputs?.map((e) => SegmentedFileOutput.fromPartial(e)) || []; + message.imageOutputs = object.imageOutputs?.map((e) => ImageOutput.fromPartial(e)) || []; return message; }, }; @@ -1087,6 +1197,7 @@ function createBaseParticipantEgressRequest(): ParticipantEgressRequest { fileOutputs: [], streamOutputs: [], segmentOutputs: [], + imageOutputs: [], }; } @@ -1122,6 +1233,11 @@ export const ParticipantEgressRequest = { SegmentedFileOutput.encode(v!, writer.uint32(66).fork()).ldelim(); } } + if (message.imageOutputs !== undefined && message.imageOutputs.length !== 0) { + for (const v of message.imageOutputs) { + ImageOutput.encode(v!, writer.uint32(74).fork()).ldelim(); + } + } return writer; }, @@ -1156,6 +1272,9 @@ export const ParticipantEgressRequest = { case 8: message.segmentOutputs!.push(SegmentedFileOutput.decode(reader, reader.uint32())); break; + case 9: + message.imageOutputs!.push(ImageOutput.decode(reader, reader.uint32())); + break; default: reader.skipType(tag & 7); break; @@ -1180,6 +1299,9 @@ export const ParticipantEgressRequest = { segmentOutputs: Array.isArray(object?.segmentOutputs) ? object.segmentOutputs.map((e: any) => SegmentedFileOutput.fromJSON(e)) : [], + imageOutputs: Array.isArray(object?.imageOutputs) + ? object.imageOutputs.map((e: any) => ImageOutput.fromJSON(e)) + : [], }; }, @@ -1207,6 +1329,11 @@ export const ParticipantEgressRequest = { } else { obj.segmentOutputs = []; } + if (message.imageOutputs) { + obj.imageOutputs = message.imageOutputs.map((e) => e ? ImageOutput.toJSON(e) : undefined); + } else { + obj.imageOutputs = []; + } return obj; }, @@ -1222,6 +1349,7 @@ export const ParticipantEgressRequest = { message.fileOutputs = object.fileOutputs?.map((e) => EncodedFileOutput.fromPartial(e)) || []; message.streamOutputs = object.streamOutputs?.map((e) => StreamOutput.fromPartial(e)) || []; message.segmentOutputs = object.segmentOutputs?.map((e) => SegmentedFileOutput.fromPartial(e)) || []; + message.imageOutputs = object.imageOutputs?.map((e) => ImageOutput.fromPartial(e)) || []; return message; }, }; @@ -1239,6 +1367,7 @@ function createBaseTrackCompositeEgressRequest(): TrackCompositeEgressRequest { fileOutputs: [], streamOutputs: [], segmentOutputs: [], + imageOutputs: [], }; } @@ -1283,6 +1412,11 @@ export const TrackCompositeEgressRequest = { SegmentedFileOutput.encode(v!, writer.uint32(106).fork()).ldelim(); } } + if (message.imageOutputs !== undefined && message.imageOutputs.length !== 0) { + for (const v of message.imageOutputs) { + ImageOutput.encode(v!, writer.uint32(114).fork()).ldelim(); + } + } return writer; }, @@ -1326,6 +1460,9 @@ export const TrackCompositeEgressRequest = { case 13: message.segmentOutputs!.push(SegmentedFileOutput.decode(reader, reader.uint32())); break; + case 14: + message.imageOutputs!.push(ImageOutput.decode(reader, reader.uint32())); + break; default: reader.skipType(tag & 7); break; @@ -1353,6 +1490,9 @@ export const TrackCompositeEgressRequest = { segmentOutputs: Array.isArray(object?.segmentOutputs) ? object.segmentOutputs.map((e: any) => SegmentedFileOutput.fromJSON(e)) : [], + imageOutputs: Array.isArray(object?.imageOutputs) + ? object.imageOutputs.map((e: any) => ImageOutput.fromJSON(e)) + : [], }; }, @@ -1384,6 +1524,11 @@ export const TrackCompositeEgressRequest = { } else { obj.segmentOutputs = []; } + if (message.imageOutputs) { + obj.imageOutputs = message.imageOutputs.map((e) => e ? ImageOutput.toJSON(e) : undefined); + } else { + obj.imageOutputs = []; + } return obj; }, @@ -1408,6 +1553,7 @@ export const TrackCompositeEgressRequest = { message.fileOutputs = object.fileOutputs?.map((e) => EncodedFileOutput.fromPartial(e)) || []; message.streamOutputs = object.streamOutputs?.map((e) => StreamOutput.fromPartial(e)) || []; message.segmentOutputs = object.segmentOutputs?.map((e) => SegmentedFileOutput.fromPartial(e)) || []; + message.imageOutputs = object.imageOutputs?.map((e) => ImageOutput.fromPartial(e)) || []; return message; }, }; @@ -1858,6 +2004,161 @@ export const DirectFileOutput = { }, }; +function createBaseImageOutput(): ImageOutput { + return { + captureInterval: 0, + width: 0, + height: 0, + filenamePrefix: "", + filenameSuffix: 0, + imageCodec: 0, + disableManifest: false, + s3: undefined, + gcp: undefined, + azure: undefined, + aliOSS: undefined, + }; +} + +export const ImageOutput = { + encode(message: ImageOutput, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.captureInterval !== undefined && message.captureInterval !== 0) { + writer.uint32(8).uint32(message.captureInterval); + } + if (message.width !== undefined && message.width !== 0) { + writer.uint32(16).int32(message.width); + } + if (message.height !== undefined && message.height !== 0) { + writer.uint32(24).int32(message.height); + } + if (message.filenamePrefix !== undefined && message.filenamePrefix !== "") { + writer.uint32(34).string(message.filenamePrefix); + } + if (message.filenameSuffix !== undefined && message.filenameSuffix !== 0) { + writer.uint32(40).int32(message.filenameSuffix); + } + if (message.imageCodec !== undefined && message.imageCodec !== 0) { + writer.uint32(48).int32(message.imageCodec); + } + if (message.disableManifest === true) { + writer.uint32(56).bool(message.disableManifest); + } + if (message.s3 !== undefined) { + S3Upload.encode(message.s3, writer.uint32(66).fork()).ldelim(); + } + if (message.gcp !== undefined) { + GCPUpload.encode(message.gcp, writer.uint32(74).fork()).ldelim(); + } + if (message.azure !== undefined) { + AzureBlobUpload.encode(message.azure, writer.uint32(82).fork()).ldelim(); + } + if (message.aliOSS !== undefined) { + AliOSSUpload.encode(message.aliOSS, writer.uint32(90).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ImageOutput { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseImageOutput(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.captureInterval = reader.uint32(); + break; + case 2: + message.width = reader.int32(); + break; + case 3: + message.height = reader.int32(); + break; + case 4: + message.filenamePrefix = reader.string(); + break; + case 5: + message.filenameSuffix = reader.int32() as any; + break; + case 6: + message.imageCodec = reader.int32() as any; + break; + case 7: + message.disableManifest = reader.bool(); + break; + case 8: + message.s3 = S3Upload.decode(reader, reader.uint32()); + break; + case 9: + message.gcp = GCPUpload.decode(reader, reader.uint32()); + break; + case 10: + message.azure = AzureBlobUpload.decode(reader, reader.uint32()); + break; + case 11: + message.aliOSS = AliOSSUpload.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ImageOutput { + return { + captureInterval: isSet(object.captureInterval) ? Number(object.captureInterval) : 0, + width: isSet(object.width) ? Number(object.width) : 0, + height: isSet(object.height) ? Number(object.height) : 0, + filenamePrefix: isSet(object.filenamePrefix) ? String(object.filenamePrefix) : "", + filenameSuffix: isSet(object.filenameSuffix) ? imageFileSuffixFromJSON(object.filenameSuffix) : 0, + imageCodec: isSet(object.imageCodec) ? imageCodecFromJSON(object.imageCodec) : 0, + disableManifest: isSet(object.disableManifest) ? Boolean(object.disableManifest) : false, + s3: isSet(object.s3) ? S3Upload.fromJSON(object.s3) : undefined, + gcp: isSet(object.gcp) ? GCPUpload.fromJSON(object.gcp) : undefined, + azure: isSet(object.azure) ? AzureBlobUpload.fromJSON(object.azure) : undefined, + aliOSS: isSet(object.aliOSS) ? AliOSSUpload.fromJSON(object.aliOSS) : undefined, + }; + }, + + toJSON(message: ImageOutput): unknown { + const obj: any = {}; + message.captureInterval !== undefined && (obj.captureInterval = Math.round(message.captureInterval)); + message.width !== undefined && (obj.width = Math.round(message.width)); + message.height !== undefined && (obj.height = Math.round(message.height)); + message.filenamePrefix !== undefined && (obj.filenamePrefix = message.filenamePrefix); + message.filenameSuffix !== undefined && (obj.filenameSuffix = imageFileSuffixToJSON(message.filenameSuffix)); + message.imageCodec !== undefined && (obj.imageCodec = imageCodecToJSON(message.imageCodec)); + message.disableManifest !== undefined && (obj.disableManifest = message.disableManifest); + message.s3 !== undefined && (obj.s3 = message.s3 ? S3Upload.toJSON(message.s3) : undefined); + message.gcp !== undefined && (obj.gcp = message.gcp ? GCPUpload.toJSON(message.gcp) : undefined); + message.azure !== undefined && (obj.azure = message.azure ? AzureBlobUpload.toJSON(message.azure) : undefined); + message.aliOSS !== undefined && (obj.aliOSS = message.aliOSS ? AliOSSUpload.toJSON(message.aliOSS) : undefined); + return obj; + }, + + fromPartial, I>>(object: I): ImageOutput { + const message = createBaseImageOutput(); + message.captureInterval = object.captureInterval ?? 0; + message.width = object.width ?? 0; + message.height = object.height ?? 0; + message.filenamePrefix = object.filenamePrefix ?? ""; + message.filenameSuffix = object.filenameSuffix ?? 0; + message.imageCodec = object.imageCodec ?? 0; + message.disableManifest = object.disableManifest ?? false; + message.s3 = (object.s3 !== undefined && object.s3 !== null) ? S3Upload.fromPartial(object.s3) : undefined; + message.gcp = (object.gcp !== undefined && object.gcp !== null) ? GCPUpload.fromPartial(object.gcp) : undefined; + message.azure = (object.azure !== undefined && object.azure !== null) + ? AzureBlobUpload.fromPartial(object.azure) + : undefined; + message.aliOSS = (object.aliOSS !== undefined && object.aliOSS !== null) + ? AliOSSUpload.fromPartial(object.aliOSS) + : undefined; + return message; + }, +}; + function createBaseS3Upload(): S3Upload { return { accessKey: "", @@ -2606,6 +2907,89 @@ export const UpdateStreamRequest = { }, }; +function createBaseUpdateOutputsRequest(): UpdateOutputsRequest { + return { egressId: "", addImageOutputs: [], removeImageOutputs: [] }; +} + +export const UpdateOutputsRequest = { + encode(message: UpdateOutputsRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.egressId !== undefined && message.egressId !== "") { + writer.uint32(10).string(message.egressId); + } + if (message.addImageOutputs !== undefined && message.addImageOutputs.length !== 0) { + for (const v of message.addImageOutputs) { + ImageOutput.encode(v!, writer.uint32(18).fork()).ldelim(); + } + } + if (message.removeImageOutputs !== undefined && message.removeImageOutputs.length !== 0) { + for (const v of message.removeImageOutputs) { + ImageOutput.encode(v!, writer.uint32(26).fork()).ldelim(); + } + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UpdateOutputsRequest { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUpdateOutputsRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.egressId = reader.string(); + break; + case 2: + message.addImageOutputs!.push(ImageOutput.decode(reader, reader.uint32())); + break; + case 3: + message.removeImageOutputs!.push(ImageOutput.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): UpdateOutputsRequest { + return { + egressId: isSet(object.egressId) ? String(object.egressId) : "", + addImageOutputs: Array.isArray(object?.addImageOutputs) + ? object.addImageOutputs.map((e: any) => ImageOutput.fromJSON(e)) + : [], + removeImageOutputs: Array.isArray(object?.removeImageOutputs) + ? object.removeImageOutputs.map((e: any) => ImageOutput.fromJSON(e)) + : [], + }; + }, + + toJSON(message: UpdateOutputsRequest): unknown { + const obj: any = {}; + message.egressId !== undefined && (obj.egressId = message.egressId); + if (message.addImageOutputs) { + obj.addImageOutputs = message.addImageOutputs.map((e) => e ? ImageOutput.toJSON(e) : undefined); + } else { + obj.addImageOutputs = []; + } + if (message.removeImageOutputs) { + obj.removeImageOutputs = message.removeImageOutputs.map((e) => e ? ImageOutput.toJSON(e) : undefined); + } else { + obj.removeImageOutputs = []; + } + return obj; + }, + + fromPartial, I>>(object: I): UpdateOutputsRequest { + const message = createBaseUpdateOutputsRequest(); + message.egressId = object.egressId ?? ""; + message.addImageOutputs = object.addImageOutputs?.map((e) => ImageOutput.fromPartial(e)) || []; + message.removeImageOutputs = object.removeImageOutputs?.map((e) => ImageOutput.fromPartial(e)) || []; + return message; + }, +}; + function createBaseListEgressRequest(): ListEgressRequest { return { roomName: "", egressId: "", active: false }; } @@ -2794,6 +3178,7 @@ function createBaseEgressInfo(): EgressInfo { streamResults: [], fileResults: [], segmentResults: [], + imageResults: [], }; } @@ -2862,6 +3247,11 @@ export const EgressInfo = { SegmentsInfo.encode(v!, writer.uint32(138).fork()).ldelim(); } } + if (message.imageResults !== undefined && message.imageResults.length !== 0) { + for (const v of message.imageResults) { + ImagesInfo.encode(v!, writer.uint32(162).fork()).ldelim(); + } + } return writer; }, @@ -2929,6 +3319,9 @@ export const EgressInfo = { case 17: message.segmentResults!.push(SegmentsInfo.decode(reader, reader.uint32())); break; + case 20: + message.imageResults!.push(ImagesInfo.decode(reader, reader.uint32())); + break; default: reader.skipType(tag & 7); break; @@ -2966,6 +3359,9 @@ export const EgressInfo = { segmentResults: Array.isArray(object?.segmentResults) ? object.segmentResults.map((e: any) => SegmentsInfo.fromJSON(e)) : [], + imageResults: Array.isArray(object?.imageResults) + ? object.imageResults.map((e: any) => ImagesInfo.fromJSON(e)) + : [], }; }, @@ -3009,6 +3405,11 @@ export const EgressInfo = { } else { obj.segmentResults = []; } + if (message.imageResults) { + obj.imageResults = message.imageResults.map((e) => e ? ImagesInfo.toJSON(e) : undefined); + } else { + obj.imageResults = []; + } return obj; }, @@ -3047,6 +3448,7 @@ export const EgressInfo = { message.streamResults = object.streamResults?.map((e) => StreamInfo.fromPartial(e)) || []; message.fileResults = object.fileResults?.map((e) => FileInfo.fromPartial(e)) || []; message.segmentResults = object.segmentResults?.map((e) => SegmentsInfo.fromPartial(e)) || []; + message.imageResults = object.imageResults?.map((e) => ImagesInfo.fromPartial(e)) || []; return message; }, }; @@ -3423,6 +3825,73 @@ export const SegmentsInfo = { }, }; +function createBaseImagesInfo(): ImagesInfo { + return { imageCount: 0, startedAt: 0, endedAt: 0 }; +} + +export const ImagesInfo = { + encode(message: ImagesInfo, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.imageCount !== undefined && message.imageCount !== 0) { + writer.uint32(8).int64(message.imageCount); + } + if (message.startedAt !== undefined && message.startedAt !== 0) { + writer.uint32(16).int64(message.startedAt); + } + if (message.endedAt !== undefined && message.endedAt !== 0) { + writer.uint32(24).int64(message.endedAt); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ImagesInfo { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseImagesInfo(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.imageCount = longToNumber(reader.int64() as Long); + break; + case 2: + message.startedAt = longToNumber(reader.int64() as Long); + break; + case 3: + message.endedAt = longToNumber(reader.int64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ImagesInfo { + return { + imageCount: isSet(object.imageCount) ? Number(object.imageCount) : 0, + startedAt: isSet(object.startedAt) ? Number(object.startedAt) : 0, + endedAt: isSet(object.endedAt) ? Number(object.endedAt) : 0, + }; + }, + + toJSON(message: ImagesInfo): unknown { + const obj: any = {}; + message.imageCount !== undefined && (obj.imageCount = Math.round(message.imageCount)); + message.startedAt !== undefined && (obj.startedAt = Math.round(message.startedAt)); + message.endedAt !== undefined && (obj.endedAt = Math.round(message.endedAt)); + return obj; + }, + + fromPartial, I>>(object: I): ImagesInfo { + const message = createBaseImagesInfo(); + message.imageCount = object.imageCount ?? 0; + message.startedAt = object.startedAt ?? 0; + message.endedAt = object.endedAt ?? 0; + return message; + }, +}; + function createBaseAutoParticipantEgress(): AutoParticipantEgress { return { preset: undefined, advanced: undefined, fileOutputs: [], segmentOutputs: [] }; } @@ -3617,6 +4086,8 @@ export interface Egress { UpdateLayout(request: UpdateLayoutRequest): Promise; /** add or remove stream endpoints */ UpdateStream(request: UpdateStreamRequest): Promise; + /** add or remove outputs */ + UpdateOutputs(request: UpdateOutputsRequest): Promise; /** list available egress */ ListEgress(request: ListEgressRequest): Promise; /** stop a recording or stream */ diff --git a/src/proto/livekit_ingress.ts b/src/proto/livekit_ingress.ts index f64a4804..f217febd 100644 --- a/src/proto/livekit_ingress.ts +++ b/src/proto/livekit_ingress.ts @@ -93,16 +93,26 @@ export function ingressAudioEncodingPresetToJSON(object: IngressAudioEncodingPre } export enum IngressVideoEncodingPreset { - /** H264_720P_30FPS_3_LAYERS - 1280x720, 30fps, 1700kbps main layer, 3 layers total */ + /** H264_720P_30FPS_3_LAYERS - 1280x720, 30fps, 1900kbps main layer, 3 layers total */ H264_720P_30FPS_3_LAYERS = 0, - /** H264_1080P_30FPS_3_LAYERS - 1980x1080, 30fps, 3000kbps main layer, 3 layers total */ + /** H264_1080P_30FPS_3_LAYERS - 1980x1080, 30fps, 3500kbps main layer, 3 layers total */ H264_1080P_30FPS_3_LAYERS = 1, - /** H264_540P_25FPS_2_LAYERS - 960x540, 25fps, 600kbps main layer, 2 layers total */ + /** H264_540P_25FPS_2_LAYERS - 960x540, 25fps, 1000kbps main layer, 2 layers total */ H264_540P_25FPS_2_LAYERS = 2, - /** H264_720P_30FPS_1_LAYER - 1280x720, 30fps, 1700kbps, no simulcast */ + /** H264_720P_30FPS_1_LAYER - 1280x720, 30fps, 1900kbps, no simulcast */ H264_720P_30FPS_1_LAYER = 3, - /** H264_1080P_30FPS_1_LAYER - 1980x1080, 30fps, 3000kbps, no simulcast */ + /** H264_1080P_30FPS_1_LAYER - 1980x1080, 30fps, 3500kbps, no simulcast */ H264_1080P_30FPS_1_LAYER = 4, + /** H264_720P_30FPS_3_LAYERS_HIGH_MOTION - 1280x720, 30fps, 2500kbps main layer, 3 layers total, higher bitrate for high motion, harder to encode content */ + H264_720P_30FPS_3_LAYERS_HIGH_MOTION = 5, + /** H264_1080P_30FPS_3_LAYERS_HIGH_MOTION - 1980x1080, 30fps, 4500kbps main layer, 3 layers total, higher bitrate for high motion, harder to encode content */ + H264_1080P_30FPS_3_LAYERS_HIGH_MOTION = 6, + /** H264_540P_25FPS_2_LAYERS_HIGH_MOTION - 960x540, 25fps, 1300kbps main layer, 2 layers total, higher bitrate for high motion, harder to encode content */ + H264_540P_25FPS_2_LAYERS_HIGH_MOTION = 7, + /** H264_720P_30FPS_1_LAYER_HIGH_MOTION - 1280x720, 30fps, 2500kbps, no simulcast, higher bitrate for high motion, harder to encode content */ + H264_720P_30FPS_1_LAYER_HIGH_MOTION = 8, + /** H264_1080P_30FPS_1_LAYER_HIGH_MOTION - 1980x1080, 30fps, 4500kbps, no simulcast, higher bitrate for high motion, harder to encode content */ + H264_1080P_30FPS_1_LAYER_HIGH_MOTION = 9, UNRECOGNIZED = -1, } @@ -123,6 +133,21 @@ export function ingressVideoEncodingPresetFromJSON(object: any): IngressVideoEnc case 4: case "H264_1080P_30FPS_1_LAYER": return IngressVideoEncodingPreset.H264_1080P_30FPS_1_LAYER; + case 5: + case "H264_720P_30FPS_3_LAYERS_HIGH_MOTION": + return IngressVideoEncodingPreset.H264_720P_30FPS_3_LAYERS_HIGH_MOTION; + case 6: + case "H264_1080P_30FPS_3_LAYERS_HIGH_MOTION": + return IngressVideoEncodingPreset.H264_1080P_30FPS_3_LAYERS_HIGH_MOTION; + case 7: + case "H264_540P_25FPS_2_LAYERS_HIGH_MOTION": + return IngressVideoEncodingPreset.H264_540P_25FPS_2_LAYERS_HIGH_MOTION; + case 8: + case "H264_720P_30FPS_1_LAYER_HIGH_MOTION": + return IngressVideoEncodingPreset.H264_720P_30FPS_1_LAYER_HIGH_MOTION; + case 9: + case "H264_1080P_30FPS_1_LAYER_HIGH_MOTION": + return IngressVideoEncodingPreset.H264_1080P_30FPS_1_LAYER_HIGH_MOTION; case -1: case "UNRECOGNIZED": default: @@ -142,6 +167,16 @@ export function ingressVideoEncodingPresetToJSON(object: IngressVideoEncodingPre return "H264_720P_30FPS_1_LAYER"; case IngressVideoEncodingPreset.H264_1080P_30FPS_1_LAYER: return "H264_1080P_30FPS_1_LAYER"; + case IngressVideoEncodingPreset.H264_720P_30FPS_3_LAYERS_HIGH_MOTION: + return "H264_720P_30FPS_3_LAYERS_HIGH_MOTION"; + case IngressVideoEncodingPreset.H264_1080P_30FPS_3_LAYERS_HIGH_MOTION: + return "H264_1080P_30FPS_3_LAYERS_HIGH_MOTION"; + case IngressVideoEncodingPreset.H264_540P_25FPS_2_LAYERS_HIGH_MOTION: + return "H264_540P_25FPS_2_LAYERS_HIGH_MOTION"; + case IngressVideoEncodingPreset.H264_720P_30FPS_1_LAYER_HIGH_MOTION: + return "H264_720P_30FPS_1_LAYER_HIGH_MOTION"; + case IngressVideoEncodingPreset.H264_1080P_30FPS_1_LAYER_HIGH_MOTION: + return "H264_1080P_30FPS_1_LAYER_HIGH_MOTION"; case IngressVideoEncodingPreset.UNRECOGNIZED: default: return "UNRECOGNIZED"; diff --git a/src/proto/livekit_models.ts b/src/proto/livekit_models.ts index 942fcf59..820542fc 100644 --- a/src/proto/livekit_models.ts +++ b/src/proto/livekit_models.ts @@ -95,6 +95,39 @@ export function videoCodecToJSON(object: VideoCodec): string { } } +export enum ImageCodec { + IC_DEFAULT = 0, + IC_JPEG = 1, + UNRECOGNIZED = -1, +} + +export function imageCodecFromJSON(object: any): ImageCodec { + switch (object) { + case 0: + case "IC_DEFAULT": + return ImageCodec.IC_DEFAULT; + case 1: + case "IC_JPEG": + return ImageCodec.IC_JPEG; + case -1: + case "UNRECOGNIZED": + default: + return ImageCodec.UNRECOGNIZED; + } +} + +export function imageCodecToJSON(object: ImageCodec): string { + switch (object) { + case ImageCodec.IC_DEFAULT: + return "IC_DEFAULT"; + case ImageCodec.IC_JPEG: + return "IC_JPEG"; + case ImageCodec.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + export enum TrackType { AUDIO = 0, VIDEO = 1, @@ -479,7 +512,6 @@ export interface Room { numParticipants: number; numPublishers: number; activeRecording: boolean; - playoutDelay?: PlayoutDelay; } export interface Codec { @@ -490,6 +522,7 @@ export interface Codec { export interface PlayoutDelay { enabled: boolean; min: number; + max: number; } export interface ParticipantPermission { @@ -987,7 +1020,6 @@ function createBaseRoom(): Room { numParticipants: 0, numPublishers: 0, activeRecording: false, - playoutDelay: undefined, }; } @@ -1026,9 +1058,6 @@ export const Room = { if (message.activeRecording === true) { writer.uint32(80).bool(message.activeRecording); } - if (message.playoutDelay !== undefined) { - PlayoutDelay.encode(message.playoutDelay, writer.uint32(98).fork()).ldelim(); - } return writer; }, @@ -1072,9 +1101,6 @@ export const Room = { case 10: message.activeRecording = reader.bool(); break; - case 12: - message.playoutDelay = PlayoutDelay.decode(reader, reader.uint32()); - break; default: reader.skipType(tag & 7); break; @@ -1098,7 +1124,6 @@ export const Room = { numParticipants: isSet(object.numParticipants) ? Number(object.numParticipants) : 0, numPublishers: isSet(object.numPublishers) ? Number(object.numPublishers) : 0, activeRecording: isSet(object.activeRecording) ? Boolean(object.activeRecording) : false, - playoutDelay: isSet(object.playoutDelay) ? PlayoutDelay.fromJSON(object.playoutDelay) : undefined, }; }, @@ -1119,8 +1144,6 @@ export const Room = { message.numParticipants !== undefined && (obj.numParticipants = Math.round(message.numParticipants)); message.numPublishers !== undefined && (obj.numPublishers = Math.round(message.numPublishers)); message.activeRecording !== undefined && (obj.activeRecording = message.activeRecording); - message.playoutDelay !== undefined && - (obj.playoutDelay = message.playoutDelay ? PlayoutDelay.toJSON(message.playoutDelay) : undefined); return obj; }, @@ -1137,9 +1160,6 @@ export const Room = { message.numParticipants = object.numParticipants ?? 0; message.numPublishers = object.numPublishers ?? 0; message.activeRecording = object.activeRecording ?? false; - message.playoutDelay = (object.playoutDelay !== undefined && object.playoutDelay !== null) - ? PlayoutDelay.fromPartial(object.playoutDelay) - : undefined; return message; }, }; @@ -1203,7 +1223,7 @@ export const Codec = { }; function createBasePlayoutDelay(): PlayoutDelay { - return { enabled: false, min: 0 }; + return { enabled: false, min: 0, max: 0 }; } export const PlayoutDelay = { @@ -1214,6 +1234,9 @@ export const PlayoutDelay = { if (message.min !== 0) { writer.uint32(16).uint32(message.min); } + if (message.max !== 0) { + writer.uint32(24).uint32(message.max); + } return writer; }, @@ -1230,6 +1253,9 @@ export const PlayoutDelay = { case 2: message.min = reader.uint32(); break; + case 3: + message.max = reader.uint32(); + break; default: reader.skipType(tag & 7); break; @@ -1242,6 +1268,7 @@ export const PlayoutDelay = { return { enabled: isSet(object.enabled) ? Boolean(object.enabled) : false, min: isSet(object.min) ? Number(object.min) : 0, + max: isSet(object.max) ? Number(object.max) : 0, }; }, @@ -1249,6 +1276,7 @@ export const PlayoutDelay = { const obj: any = {}; message.enabled !== undefined && (obj.enabled = message.enabled); message.min !== undefined && (obj.min = Math.round(message.min)); + message.max !== undefined && (obj.max = Math.round(message.max)); return obj; }, @@ -1256,6 +1284,7 @@ export const PlayoutDelay = { const message = createBasePlayoutDelay(); message.enabled = object.enabled ?? false; message.min = object.min ?? 0; + message.max = object.max ?? 0; return message; }, }; diff --git a/src/proto/livekit_room.ts b/src/proto/livekit_room.ts index 6c1b9250..43aa1489 100644 --- a/src/proto/livekit_room.ts +++ b/src/proto/livekit_room.ts @@ -27,8 +27,14 @@ export interface CreateRoomRequest { metadata?: string; /** egress */ egress?: RoomEgress; - /** minimum playout delay of subscriber */ + /** playout delay of subscriber */ minPlayoutDelay?: number; + maxPlayoutDelay?: number; + /** + * improves A/V sync when playout_delay set to a value larger than 200ms. It will disables transceiver re-use + * so not recommended for rooms with frequent subscription changes + */ + syncStreams?: boolean; } export interface RoomEgress { @@ -147,6 +153,8 @@ function createBaseCreateRoomRequest(): CreateRoomRequest { metadata: "", egress: undefined, minPlayoutDelay: 0, + maxPlayoutDelay: 0, + syncStreams: false, }; } @@ -173,6 +181,12 @@ export const CreateRoomRequest = { if (message.minPlayoutDelay !== undefined && message.minPlayoutDelay !== 0) { writer.uint32(56).uint32(message.minPlayoutDelay); } + if (message.maxPlayoutDelay !== undefined && message.maxPlayoutDelay !== 0) { + writer.uint32(64).uint32(message.maxPlayoutDelay); + } + if (message.syncStreams === true) { + writer.uint32(72).bool(message.syncStreams); + } return writer; }, @@ -204,6 +218,12 @@ export const CreateRoomRequest = { case 7: message.minPlayoutDelay = reader.uint32(); break; + case 8: + message.maxPlayoutDelay = reader.uint32(); + break; + case 9: + message.syncStreams = reader.bool(); + break; default: reader.skipType(tag & 7); break; @@ -221,6 +241,8 @@ export const CreateRoomRequest = { metadata: isSet(object.metadata) ? String(object.metadata) : "", egress: isSet(object.egress) ? RoomEgress.fromJSON(object.egress) : undefined, minPlayoutDelay: isSet(object.minPlayoutDelay) ? Number(object.minPlayoutDelay) : 0, + maxPlayoutDelay: isSet(object.maxPlayoutDelay) ? Number(object.maxPlayoutDelay) : 0, + syncStreams: isSet(object.syncStreams) ? Boolean(object.syncStreams) : false, }; }, @@ -233,6 +255,8 @@ export const CreateRoomRequest = { message.metadata !== undefined && (obj.metadata = message.metadata); message.egress !== undefined && (obj.egress = message.egress ? RoomEgress.toJSON(message.egress) : undefined); message.minPlayoutDelay !== undefined && (obj.minPlayoutDelay = Math.round(message.minPlayoutDelay)); + message.maxPlayoutDelay !== undefined && (obj.maxPlayoutDelay = Math.round(message.maxPlayoutDelay)); + message.syncStreams !== undefined && (obj.syncStreams = message.syncStreams); return obj; }, @@ -247,6 +271,8 @@ export const CreateRoomRequest = { ? RoomEgress.fromPartial(object.egress) : undefined; message.minPlayoutDelay = object.minPlayoutDelay ?? 0; + message.maxPlayoutDelay = object.maxPlayoutDelay ?? 0; + message.syncStreams = object.syncStreams ?? false; return message; }, };