Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(video-v2): add video v2 class and stream #930

Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
16fa265
feat(video-v2): add highlevel video class
madhur-push Dec 18, 2023
9816bab
fix: merge main
madhur-push Dec 20, 2023
ee8f2f9
feat(video): add video stream
madhur-push Dec 21, 2023
c8b2a07
fix(sendnotification): modify rules.access.data to be an object & cod…
madhur-push Dec 21, 2023
0fff331
fix(video): remove signer from input, throw err if signer, decrypted …
madhur-push Dec 22, 2023
277da90
Merge branch 'main' into 926-improvement-proposal-add-class-based-cha…
madhur-push Dec 22, 2023
e96a9b7
feat(video): add sendNotification calls in connect, disconnect methods
madhur-push Dec 22, 2023
3cf48a4
fix(videonotificationrules): typo in VideoNotificationRules interface
madhur-push Jan 2, 2024
10d05a9
feat(video stream): handle connect, retry internally from the SDK
madhur-push Jan 5, 2024
3dcace4
feat(video connect): remove the connect method from the videov2 SDK
madhur-push Jan 5, 2024
d39592c
fix: merge main
madhur-push Jan 8, 2024
194e064
fix(videov2): create push signer instance to get chain id in initiali…
madhur-push Jan 8, 2024
924ce7d
fix: merge main
madhur-push Jan 9, 2024
da7fee5
fix: merge main
madhur-push Jan 9, 2024
df4b6f8
fix(video stream): add backwards compatibilty to rules object for old…
madhur-push Jan 10, 2024
272667b
fix(video stream): fix bug in rules object creation
madhur-push Jan 10, 2024
e27725e
feat(video): update param names for video.initialize()
madhur-push Jan 11, 2024
407559b
fix: merge main
madhur-push Jan 12, 2024
9bd3ac8
feat(video): add internal event handlers for stream in video SDK
madhur-push Jan 15, 2024
095d213
fix: merge alpha
mohammeds1992 Jan 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/restapi/src/lib/payloads/helpers.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import {
ISendNotificationInputOptions,
INotificationPayload,
walletType,
VideNotificationRules,
VideoNotificationRules,
} from '../types';
import {
IDENTITY_TYPE,
@@ -212,7 +212,7 @@ export async function getVerificationProof({
wallet?: walletType;
pgpPrivateKey?: string;
env?: ENV;
rules?:VideNotificationRules;
rules?:VideoNotificationRules;
}) {
let message = null;
let verificationProof = null;
4 changes: 2 additions & 2 deletions packages/restapi/src/lib/payloads/sendNotifications.ts
Original file line number Diff line number Diff line change
@@ -185,7 +185,7 @@ export async function sendNotification(options: ISendNotificationInputOptions) {
uuid,
// for the pgpv2 verfication proof
chatId:
rules?.access.data ?? // for backwards compatibilty with 'chatId' param
rules?.access.data.chatId ?? // for backwards compatibilty with 'chatId' param
chatId,
pgpPrivateKey,
});
@@ -231,7 +231,7 @@ export async function sendNotification(options: ISendNotificationInputOptions) {
? {
rules: rules ?? {
access: {
data: chatId,
data: { chatId },
type: VIDEO_NOTIFICATION_ACCESS_TYPE.PUSH_CHAT,
},
},
10 changes: 10 additions & 0 deletions packages/restapi/src/lib/pushapi/PushAPI.ts
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import {
STREAM,
} from '../pushstream/pushStreamTypes';
import { ALPHA_FEATURE_CONFIG } from '../config';
import { Video } from './video';
import { isValidCAIP10NFTAddress } from '../helpers';

export class PushAPI {
@@ -29,6 +30,8 @@ export class PushAPI {
private progressHook?: (progress: ProgressHookType) => void;

public chat: Chat; // Public instances to be accessed from outside the class
public video: Video;

public profile: Profile;
public encryption: Encryption;
private user: User;
@@ -86,6 +89,13 @@ export class PushAPI {
this.progressHook
);
this.user = new User(this.account, this.env);

this.video = new Video(this.account,
this.env,
this.decryptedPgpPvtKey,
this.signer
);

this.errors = initializationErrors || [];
}
// Overloaded initialize method signatures
10 changes: 10 additions & 0 deletions packages/restapi/src/lib/pushapi/pushAPITypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Constants, { ENV } from '../constants';
import type { PushStream } from '../pushstream/PushStream';
import { ChatStatus, ProgressHookType, Rules } from '../types';

export enum ChatListType {
@@ -66,3 +67,12 @@ export interface ParticipantStatus {
role: 'admin' | 'member';
participant: boolean;
}

export interface VideoInitializeOptions {
stream: PushStream;
config: {
video?: boolean;
audio?: boolean;
};
media?: MediaStream;
}
146 changes: 146 additions & 0 deletions packages/restapi/src/lib/pushapi/video.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { ENV } from '../constants';
import CONSTANTS from '../constantsV2';
import { SignerType, VideoCallData, VideoCallStatus } from '../types';
import { Signer as PushSigner } from '../helpers';

import { Video as VideoV1, initVideoCallData } from '../video/Video';
import { VideoV2 } from '../video/VideoV2';
import { VideoInitializeOptions } from './pushAPITypes';
import { VideoEvent, VideoEventType } from '../pushstream/pushStreamTypes';
import { produce } from 'immer';
import { endStream } from '../video/helpers/mediaToggle';

export class Video {
constructor(
private account: string,
private env: ENV,
private decryptedPgpPvtKey?: string,
private signer?: SignerType
) {}

async initialize(
onChange: (fn: (data: VideoCallData) => VideoCallData) => void,
options: VideoInitializeOptions
) {
const { stream, config, media } = options;

if (!this.signer) {
throw new Error('Signer is required for push video');
}

if (!this.decryptedPgpPvtKey) {
throw new Error(
'PushSDK was initialized in readonly mode. Video functionality is not available.'
);
}

const chainId = await new PushSigner(this.signer).getChainId();

if (!chainId) {
throw new Error('Chain Id not retrievable from signer');
}

// Initialize the video instance with the provided options
const videoV1Instance = new VideoV1({
signer: this.signer!,
chainId,
pgpPrivateKey: this.decryptedPgpPvtKey!,
env: this.env,
setData: onChange,
});

// Create the media stream with the provided options
await videoV1Instance.create({
...(media && {
stream: media,
}),
...(config?.audio && {
audio: config.audio,
}),
...(config?.video && {
video: config.video,
}),
});

// Setup video event handlers
stream.on(CONSTANTS.STREAM.VIDEO, (data: VideoEvent) => {
const {
address,
signal,
meta: { rules },
} = data.peerInfo;

const chatId = rules.access.data.chatId;

// If the event is RequestVideo, update the video call 'data' state with the incoming call data
if (data.event === VideoEventType.RequestVideo) {
videoV1Instance.setData((oldData) => {
return produce(oldData, (draft) => {
draft.local.address = this.account;
draft.incoming[0].address = address;
draft.incoming[0].status = VideoCallStatus.RECEIVED;
draft.meta.chatId = chatId!;
draft.meta.initiator.address = address;
draft.meta.initiator.signal = signal;
});
});
}

// Check if the chatId from the incoming video event matches the chatId of the current video instance
if (chatId && chatId === videoV1Instance.data.meta.chatId) {
// If the event is DenyVideo, destroy the local stream & reset the video call data
if (data.event === VideoEventType.DenyVideo) {
// destroy the local stream
if (videoV1Instance.data.local.stream) {
endStream(videoV1Instance.data.local.stream);
}

videoV1Instance.setData(() => initVideoCallData);
}

// If the event is ApproveVideo or RetryApproveVideo, connect to the video
if (
data.event === VideoEventType.ApproveVideo ||
data.event === VideoEventType.RetryApproveVideo
) {
videoV1Instance.connect({ peerAddress: address, signalData: signal });
}

// If the event is RetryRequestVideo and the current instance is the initiator, send a request
if (
data.event === VideoEventType.RetryRequestVideo &&
videoV1Instance.isInitiator()
) {
videoV1Instance.request({
senderAddress: this.account,
recipientAddress: address,
rules,
retry: true,
});
}

// If the event is RetryRequestVideo and the current instance is not the initiator, accept the request
if (
data.event === VideoEventType.RetryRequestVideo &&
!videoV1Instance.isInitiator()
) {
videoV1Instance.acceptRequest({
signalData: signal,
senderAddress: this.account,
recipientAddress: address,
rules,
retry: true,
});
}
}
});

// Return an instance of the video v2 class
return new VideoV2({
videoV1Instance,
account: this.account,
decryptedPgpPvtKey: this.decryptedPgpPvtKey!,
env: this.env,
});
}
}
75 changes: 75 additions & 0 deletions packages/restapi/src/lib/pushstream/DataModifier.ts
Original file line number Diff line number Diff line change
@@ -17,7 +17,13 @@ import {
NotificationType,
NOTIFICATION,
ProposedEventNames,
VideoEventType,
MessageOrigin,
VideoEvent,
} from './pushStreamTypes';
import { VideoCallStatus, VideoPeerInfo } from '../types';
import { VideoDataType } from '../video';
import { VIDEO_NOTIFICATION_ACCESS_TYPE } from '../payloads/constants';

export class DataModifier {
public static handleChatGroupEvent(data: any, includeRaw = false): any {
@@ -388,4 +394,73 @@ export class DataModifier {
break;
}
}

public static convertToProposedNameForVideo(
currentVideoStatus: VideoCallStatus
): VideoEventType {
switch (currentVideoStatus) {
case VideoCallStatus.INITIALIZED:
return VideoEventType.RequestVideo;
case VideoCallStatus.RECEIVED:
return VideoEventType.ApproveVideo;
case VideoCallStatus.CONNECTED:
return VideoEventType.ConnectVideo;
case VideoCallStatus.ENDED:
return VideoEventType.DisconnectVideo;
case VideoCallStatus.DISCONNECTED:
return VideoEventType.DenyVideo;
case VideoCallStatus.RETRY_INITIALIZED:
return VideoEventType.RetryRequestVideo;
case VideoCallStatus.RETRY_RECEIVED:
return VideoEventType.RetryApproveVideo;
default:
throw new Error(`Unknown video call status: ${currentVideoStatus}`);
}
}

public static mapToVideoEvent(
data: any,
origin: MessageOrigin,
includeRaw = false
): VideoEvent {
const { senderAddress, signalData, status, chatId }: VideoDataType =
JSON.parse(data.payload.data.additionalMeta?.data);

// To maintain backward compatibility, if the rules object is not present in the payload,
// we create a new rules object with chatId from additionalMeta.data
const rules = data.payload.rules ?? {
access: {
type: VIDEO_NOTIFICATION_ACCESS_TYPE.PUSH_CHAT,
data: {
chatId,
},
},
};

const peerInfo: VideoPeerInfo = {
address: senderAddress,
signal: signalData,
meta: {
rules,
},
};

const videoEventType: VideoEventType =
DataModifier.convertToProposedNameForVideo(status);

const videoEvent: VideoEvent = {
event: videoEventType,
origin: origin,
timestamp: data.epoch,
peerInfo,
};

if (includeRaw) {
videoEvent.raw = {
verificationProof: data.payload.verificationProof,
};
}

return videoEvent;
}
}
Loading