declare namespace OT {
  type MediaProcessorConnector = {
    setTrack: (track: MediaStreamTrack) => Promise<MediaStreamTrack>;
    destroy: () => Promise<void>;
  };

  export type OTError = {
    name: string;
    message: string;
  };

  export type Dimensions = {
    width: number;
    height: number;
  }

  export type ScreenSharingCapabilityResponse = {
    extensionInstalled?: boolean;
    supported: boolean;
    supportedSources: {
      application?: boolean;
      screen?: boolean;
      window?: boolean;
    };
    extensionRequired?: string;
    extensionRegistered?: boolean;
  };

  export function checkScreenSharingCapability(
    callback: (response: ScreenSharingCapabilityResponse) => void
  ): void;

  export function checkSystemRequirements(): number;

  export type Device = {
    kind: 'audioInput' | 'videoInput';
    deviceId: string;
    label: string;
  };

  export type AudioOutputDevice = {
    deviceId: string | null | undefined;
    label: string | null;
  };

  export type AudioInputDevice = {
    deviceId: string | null | undefined;
    label: string | null;
  }

  export type IceConfig = {
    includeServers: 'all' | 'custom';
    transportPolicy: 'all' | 'relay';
    customServers: Array<{
      urls: string | string[];
      username?: string;
      credential?: string;
    }>;
  };

  export type VideoCodec = 'vp8' | 'vp9' | 'h264';

  export type PreferredVideoCodecs = 'automatic' | [VideoCodec, ...VideoCodec[]];

  export type VideoBitratePresetInput = 'default' | 'bw_saver' | 'extra_bw_saver';
  export type VideoBitratePreset = VideoBitratePresetInput | 'custom';

  export function getDevices(
    callback: (error: OTError | undefined, devices?: Device[]) => void
  ): void;

  export function getAudioOutputDevices(): Promise<AudioOutputDevice[]>;

  export function setProxyUrl(proxyUrl: string): void;

  export function getSupportedCodecs(): Promise<{ videoEncoders: ('H264' | 'VP8' | 'VP9')[], videoDecoders: ('H264' | 'VP8' | 'VP9')[] }>;

  export function hasMediaProcessorSupport(mediaType?: 'audio' | 'video' | 'both'): boolean;

  export function hasEndToEndEncryptionSupport(): boolean;

  export type WidgetStyle = {
    audioLevelDisplayMode: 'auto' | 'on' | 'off';
    backgroundImageURI: string;
    buttonDisplayMode: 'auto' | 'on' | 'off';
    nameDisplayMode: 'auto' | 'on' | 'off';
    videoDisabledDisplayMode: 'auto' | 'on' | 'off';
  };

  export type WidgetProperties = {
    fitMode?: 'cover' | 'contain';
    insertDefaultUI?: boolean;
    insertMode?: 'replace' | 'after' | 'before' | 'append';
    showControls?: boolean;
    width?: string | number;
    height?: string | number;
  };

  export type PublisherStyle = WidgetStyle & {
    archiveStatusDisplayMode: 'auto' | 'off';
  };

  export type GetUserMediaProperties = {
    audioSource?: string | null | boolean | MediaStreamTrack;
    disableAudioProcessing?: boolean;
    echoCancellation?: boolean;
    noiseSuppression?: boolean;
    autoGainControl?: boolean;
    facingMode?: 'user' | 'environment' | 'left' | 'right';
    frameRate?: 30 | 15 | 7 | 1;
    maxResolution?: Dimensions;
    resolution?: (
      '1920x1080' |
      '1280x960' |
      '1280x720' |
      '640x480' |
      '640x360' |
      '320x240' |
      '320x180'
    );
    videoSource?: string | null | boolean | MediaStreamTrack;
  };

  export type VideoContentHint = "text" | "detail" | "motion" | "";

  export type BackgroundBlurFilter = {
    type: 'backgroundBlur';
    blurStrength?: 'low' | 'high';
  };

  export type BackgroundReplacementFilter = {
    type: 'backgroundReplacement';
    backgroundImgUrl: string;
  };

  export type AdvancedNoiseSuppressionFilter = {
    type: 'advancedNoiseSuppression';
  };

  export type VideoFilter = BackgroundBlurFilter | BackgroundReplacementFilter;

  export type AudioFilter = AdvancedNoiseSuppressionFilter;

  export type PublisherProperties = WidgetProperties & GetUserMediaProperties & {
    audioBitrate?: number;
    audioFallback?: {
      publisher?: boolean;
      subscriber?: boolean;
    };
    initials?: string;
    mirror?: boolean;
    name?: string;
    publishAudio?: boolean;
    publishVideo?: boolean;
    publishCaptions?: boolean;
    publishSenderStats?: boolean;
    scalableScreenshare?: boolean;
    scalableVideo?: boolean;
    style?: Partial<PublisherStyle>;
    videoContentHint?: VideoContentHint;
    enableDtx?: boolean;
    enableStereo?: boolean;
    videoFilter?: VideoFilter;
    audioFilter?: AudioFilter;
    disableAudioInputDeviceManagement?: boolean;
    preferredVideoCodecs?: PreferredVideoCodecs;
  };

  export type SubscriberStyle = WidgetStyle & {
    audioBlockedDisplayMode: 'auto' | 'on' | 'off';
  };

  export type CaptionsLanguageCode =
      | 'af-ZA' | 'ar-AE' | 'ar-SA' | 'eu-ES' | 'ca-ES' | 'zh-HK' | 'zh-CN' | 'zh-TW'
      | 'hr-HR' | 'cs-CZ' | 'da-DK' | 'nl-NL' | 'en-AU' | 'en-GB' | 'en-IN' | 'en-IE'
      | 'en-NZ' | 'en-AB' | 'en-ZA' | 'en-US' | 'en-WL' | 'fa-IR' | 'fi-FI' | 'fr-FR'
      | 'fr-CA' | 'gl-ES' | 'de-DE' | 'de-CH' | 'el-GR' | 'he-IL' | 'hi-IN' | 'id-ID'
      | 'it-IT' | 'ja-JP' | 'ko-KR' | 'lv-LV' | 'ms-MY' | 'no-NO' | 'pl-PL' | 'pt-PT'
      | 'pt-BR' | 'ro-RO' | 'ru-RU' | 'sr-RS' | 'sk-SK' | 'so-SO' | 'es-ES' | 'es-US'
      | 'sv-SE' | 'tl-PH' | 'th-TH' | 'uk-UA' | 'vi-VN' | 'zu-ZA';

  export type SubscriberProperties = WidgetProperties & {
    audioVolume?: number;
    preferredFrameRate?: number;
    preferredResolution?: 'auto' | Dimensions;
    style?: Partial<SubscriberStyle>;
    subscribeToAudio?: boolean;
    subscribeToCaptions?: boolean;
    subscribeToVideo?: boolean;
    testNetwork?: boolean;
    captionsTranslationLanguage?: CaptionsLanguageCode;
  };

  export class Connection {
    connectionId: string;
    creationTime: number;
    data: string;
  }

  export class Stream {
    connection: Connection;
    creationTime: number;
    frameRate: number;
    hasAudio: boolean;
    hasCaptions: boolean;
    hasVideo: boolean;
    initials: string;
    name: string;
    streamId: string;
    videoDimensions: {
      width: number;
      height: number;
    };
    videoType: 'camera' | 'screen' | 'custom' | undefined;
  }

  export type Event<Type, Target> = {
    type: Type;
    cancelable: boolean;
    target: Target;

    isDefaultPrevented(): boolean;
    preventDefault(): void;
  };

  export type ExceptionEvent = Event<'exception', {}> & {
    code: number;
    message: string;
    title: string;
  };

  export type VideoDimensionsChangedEvent<Target> = Event<'videoDimensionsChanged', Target> & {
    oldValue: Dimensions;
    newValue: Dimensions;
  };

  export type CpuPerformanceState = 'nominal' | 'fair' | 'serious' | 'critical';

  export type QualityScore = {
    audioQualityScore?: number;
    videoQualityScore?: number;
}

  class OTEventEmitter<EventMap> {
    on<EventName extends keyof EventMap>(
      eventName: EventName,
      callback: (event: EventMap[EventName]) => void,
      context?: object
    ): void;

    on(
      eventName: string,
      callback: (event: Event<string, any>) => void,
      context?: object
    ): void;

    on(
      eventMap: object,
      context?: object
    ): void;

    once<EventName extends keyof EventMap>(
      eventName: EventName,
      callback: (event: EventMap[EventName]) => void,
      context?: object
    ): void;

    once(
      eventName: string,
      callback: (event: Event<string, any>) => void,
      context?: object
    ): void;

    once(
      eventMap: object,
      context?: object
    ): void;

    off<EventName extends keyof EventMap>(
      eventName?: EventName,
      callback?: (event: EventMap[EventName]) => void,
      context?: object
    ): void;

    off(
      eventName?: string,
      callback?: (event: Event<string, any>) => void,
      context?: object
    ): void;

    off(
      eventMap: object,
      context?: object
    ): void;
  }

  // This class is a modified copy of OTEventEmitter that was needed in order to support the template string used for the signal:type event
  class SessionOTEventEmitter<EventMap> {
    on<EventName extends keyof EventMap>(
      eventName: EventName,
      callback: (event: EventMap[EventName]) => void,
      context?: object
    ): void;

    on(
      eventName: `signal:${string}`,
      callback: (event: Event<`signal:${string}`, Session> & {
        type?: string;
        data?: string;
        from: Connection | null;
      }) => void,
      context?: object
    ): void;

    on(
      eventName: string,
      callback: (event: Event<string, any>) => void,
      context?: object
    ): void;

    on(
      eventMap: object,
      context?: object
    ): void;

    once<EventName extends keyof EventMap>(
      eventName: EventName,
      callback: (event: EventMap[EventName]) => void,
      context?: object
    ): void;

    once(
      eventName: `signal:${string}`,
      callback: (event: Event<`signal:${string}`, Session> & {
        type?: string;
        data?: string;
        from: Connection | null;
      }) => void,
      context?: object
    ): void;

    once(
      eventName: string,
      callback: (event: Event<string, any>) => void,
      context?: object
    ): void;

    once(
      eventMap: object,
      context?: object
    ): void;

    off<EventName extends keyof EventMap>(
      eventName?: EventName,
      callback?: (event: EventMap[EventName]) => void,
      context?: object
    ): void;

    off(
      eventName: `signal:${string}`,
      context?: object
    ): void;

    off(
      eventName?: string,
      callback?: (event: Event<string, any>) => void,
      context?: object
    ): void;

    off(
      eventMap: object,
      context?: object
    ): void;
  }

  export class Publisher extends OTEventEmitter<{
    accessAllowed: Event<'accessAllowed', Publisher>;
    accessDenied: Event<'accessDenied', Publisher>;
    accessDialogClosed: Event<'accessDialogClosed', Publisher>;
    accessDialogOpened: Event<'accessDialogOpened', Publisher>;

    audioInputDeviceChanged: Event<'audioInputDeviceChanged', Publisher> & {
      device: AudioInputDevice;
    };

    audioLevelUpdated: Event<'audioLevelUpdated', Publisher> & {
      audioLevel: number
    };

    destroyed: Event<'destroyed', Publisher>;

    mediaStopped: Event<'mediaStopped', Publisher> & {
      track: MediaStreamTrack | undefined
    };

    mediaStreamAvailable: Event<'mediaStreamAvailable', Publisher> & {
      mediaStream: MediaStream;
    };

    streamCreated: Event<'streamCreated', Publisher> & {
      stream: Stream;
    };

    streamDestroyed: Event<'streamDestroyed', Publisher> & {
      stream: Stream;
      reason: string;
    };

    videoDimensionsChanged: VideoDimensionsChangedEvent<Publisher>;

    videoElementCreated: Event<'videoElementCreated', Publisher> & {
      element: HTMLVideoElement | HTMLObjectElement;
    };

    videoEnabled: Event<'videoEnabled', Publisher> & {
      reason: 'quality';
    };
    videoDisabled: Event<'videoDisabled', Publisher> & {
      reason: 'quality';
    };

    videoDisableWarning: Event<'videoDisableWarning', Publisher>;
    videoDisableWarningLifted: Event<'videoDisableWarningLifted', Publisher>;

    videoQualityChanged: Event<'videoQualityChanged', Publisher> & {
      reason: VideoQualityReason;
      statsContainer: PublisherStatContainer;
    };

    networkConditionChanged: Event<'networkConditionChanged', Publisher> & {
      reason: 'networkConditionChanged';
      statsContainer: PublisherStatContainer;
    };

    muteForced: Event<'muteForced', Publisher>;
  }> {
    accessAllowed: boolean;
    element?: HTMLElement | undefined;
    id?: string;
    stream?: Stream;
    session?: Session;

    destroy(): void;
    getImgData(): string | null;
    getStats(callback: (error?: OTError, stats?: PublisherStatsArr) => void): void;
    getRtcStatsReport(): Promise<PublisherRtcStatsReportArr>;
    getStyle(): PublisherStyle;
    applyVideoFilter(videoFilter: VideoFilter): Promise<void>;
    getVideoFilter(): VideoFilter | null;
    clearVideoFilter(): Promise<void>;
    applyAudioFilter(audioFilter: AudioFilter): Promise<void>;
    clearAudioFilter(): Promise<void>;
    getAudioFilter(): AudioFilter | null;
    publishAudio(value: boolean): void;
    publishVideo(value: boolean, callback?: (error?: OTError) => void): void;
    publishCaptions(value: boolean): void;
    cycleVideo(): Promise<{ deviceId: string }>;
    setAudioSource(audioSource:string | MediaStreamTrack): Promise<undefined>;
    getAudioSource(): MediaStreamTrack;
    setVideoSource(videoSourceId: string): Promise<undefined>;
    getVideoContentHint(): VideoContentHint;
    setVideoContentHint(hint: VideoContentHint): void;
    getVideoSource(): {deviceId: string | null, type: string | null, track: MediaStreamTrack | null};
    setStyle<Style extends keyof PublisherStyle>(style: Style, value: PublisherStyle[Style]): void;
    setStyle(styles: Partial<PublisherStyle>): void;
    videoWidth(): number | undefined;
    videoHeight(): number | undefined;
    setVideoMediaProcessorConnector(connector: MediaProcessorConnector | null): Promise<void>;
    setAudioMediaProcessorConnector(connector: MediaProcessorConnector | null): Promise<void>;
    setPreferredFrameRate(frameRate: number): Promise<void>;
    setPreferredResolution(resolution: Dimensions): Promise<void>;
    setMaxVideoBitrate(bitrateBps: number): Promise<void>;
    setVideoBitratePreset(preset: VideoBitratePresetInput): Promise<void>;
    getMaxVideoBitrate(): number;
    getVideoBitratePreset(): VideoBitratePreset;
  }

  export function getUserMedia(
    properties?: GetUserMediaProperties
  ): Promise<MediaStream>;

  export function initPublisher(
    targetElement?: HTMLElement | string,
    properties?: PublisherProperties,
    callback?: (error?: OTError) => void
  ): Publisher;

  export function log(message: string): void;

  export function off(
    eventName?: 'exception',
    callback?: (event: ExceptionEvent) => void,
    context?: object
  ): void;

  export function on(
    eventName: 'exception',
    callback: (event: ExceptionEvent) => void,
    context?: object
  ): void;

  export function once(
    eventName: 'exception',
    callback: (event: ExceptionEvent) => void,
    context?: object
  ): void;

  export class Session extends SessionOTEventEmitter<{
    archiveStarted: Event<'archiveStarted', Session> & {
      id: string;
      name: string;
    };

    archiveStopped: Event<'archiveStopped', Session> & {
      id: string;
      name: string;
    };

    connectionCreated: Event<'connectionCreated', Session> & {
      connection: Connection;
    };

    connectionDestroyed: Event<'connectionDestroyed', Session> & {
      connection: Connection;
      reason: string;
    };

    cpuPerformanceChanged: Event<'cpuPerformanceChanged', Session> & {
      cpuPerformanceState: CpuPerformanceState;
    };

    sessionConnected: Event<'sessionConnected', Session>;

    sessionDisconnected: Event<'sessionDisconnected', Session> & {
      reason: string;
    };

    sessionReconnected: Event<'sessionReconnected', Session>;
    sessionReconnecting: Event<'sessionReconnecting', Session>;

    signal: Event<'signal', Session> & {
      type?: string;
      data?: string;
      from: Connection | null;
    };

    streamCreated: Event<'streamCreated', Session> & {
      stream: Stream;
    };

    streamDestroyed: Event<'streamDestroyed', Session> & {
      stream: Stream;
      reason: string;
    };

    streamPropertyChanged: (
      Event<'streamPropertyChanged', Session> & {
        stream: Stream;
      } & (
        { changedProperty: 'hasAudio'; oldValue: boolean; newValue: boolean; } |
        { changedProperty: 'hasCaptions'; oldValue: boolean; newValue: boolean; } |
        { changedProperty: 'hasVideo'; oldValue: boolean; newValue: boolean; } |
        { changedProperty: 'videoDimensions'; oldValue: Dimensions; newValue: Dimensions; }
      )
    );

    muteForced: Event<'muteForced', Session>;
  }> {
    capabilities: {
      forceDisconnect: number;
      forceUnpublish: number;
      forceMute: number;
      publish: number;
      subscribe: number;
    };

    connection?: Connection;
    sessionId: string;

    connect(token: string, callback: (error?: OTError) => void): void;
    disconnect(): void;
    disableForceMute(): Promise<void>;
    forceDisconnect(connection: Connection, callback: (error?: OTError) => void): void;
    forceUnpublish(stream: Stream, callback: (error?: OTError) => void): void;
    forceMuteStream(stream: Stream): Promise<void>;
    forceMuteAll(excludedStreams?: Stream[]): Promise<void>;
    getPublisherForStream(stream: Stream): Publisher | undefined;
    getSubscribersForStream(stream: Stream): [Subscriber];
    publish(publisher: Publisher, callback?: (error?: OTError) => void): Publisher;
    publish(targetElement: string | HTMLElement, properties?: PublisherProperties, callback?: (error?: OTError) => void): Publisher;
    setEncryptionSecret(secret: string): Promise<void>;
    setIceConfig(iceConfig: IceConfig): Promise <void>;

    signal(
      signal: { type?: string, data?: string, to?: Connection, retryAfterReconnect?: boolean },
      callback?: (error?: OTError) => void
    ): void;

    subscribe(
      stream: Stream,
      targetElement?: HTMLElement | string,
      properties?: SubscriberProperties,
      callback?: (error?: OTError) => void
    ): Subscriber;

    unpublish(publisher: Publisher): void;
    unsubscribe(subscriber: Subscriber): void;
  }

  export function initSession(
    partnerId: string,
    sessionId: string,
    options?: {
      connectionEventsSuppressed?: boolean;
      iceConfig?: IceConfig;
      ipWhitelist?: boolean;
      encryptionSecret?: string;
      singlePeerConnection?: boolean;
      sessionMigration?: boolean;
    }
  ): Session;

  export type IncomingTrackStats = {
    bytesReceived: number;
    packetsLost: number;
    packetsReceived: number;
  };

  export type OutgoingTrackStats = {
    bytesSent: number;
    packetsLost: number;
    packetsSent: number;
  }

  export type VideoQualityReason =
    | 'none'
    | 'bandwidth'
    | 'cpu'
    | 'other'
    | 'resolutionChange'
    | 'codecChange'
    | 'videoInterruption';
  
  export type NetworkConditionReason =
    | 'none'
    | 'unknown'
    | 'bandwidth'
    | 'packetLoss';

  export type NetworkCondition =
    | 'unknown'
    | 'critical'
    | 'warning'
    | 'fair'
    | 'good'
    | 'excellent';

  export type NetworkDegradationSource =
    | 'none'
    | 'bothOrUnclear'
    | 'local'
    | 'remote';

  export type SenderStats = {
    connectionMaxAllocatedBitrate: number;
    connectionEstimatedBandwidth: number;
  }

  export type SubscriberVideoStats = IncomingTrackStats & {
    codec: string;
    width: number;
    height: number;
    bitrate: number;
    totalBitrate: number;
    frameRate: number;
    decodedFrameRate: number;
    pauseCount?: number;
    totalPausesDuration?: number;
    freezeCount?: number;
    totalFreezesDuration?: number;
  };

  export type SubscriberMediaLink = {
    transport: TransportStats;
    remotePublisherTransport: TransportStats;
    networkDegradationSource: NetworkDegradationSource;
  };

  export type SubscriberStats = {
    audio: IncomingTrackStats;
    video: SubscriberVideoStats;
    mediaLink: SubscriberMediaLink;
    senderStats?: SenderStats;
    timestamp: number;
  }

  export type VideoLayerStats = {
    codec: string;
    width: number;
    height: number;
    encodedFrameRate: number;
    bitrate: number;
    totalBitrate: number;
    scalabilityMode?: string;
    qualityLimitationReason?: VideoQualityReason;
  };

  export type PublisherVideoStats = OutgoingTrackStats & {
    frameRate: number;
    layers?: VideoLayerStats[];
  };

  export type TransportStats = {
    connectionEstimatedBandwidth: number;
    networkCondition: NetworkCondition;
    networkConditionReason: NetworkConditionReason;
  }

  export type PublisherMediaLink = {
    transport: TransportStats;
  };

  export type PublisherStats = {
    audio: OutgoingTrackStats;
    video: PublisherVideoStats;
    mediaLink: PublisherMediaLink;
    timestamp: number;
    transportStats: TransportStats;
  }

  export type PublisherStatContainer = {
    subscriberId?: string,
    connectionId?: string,
    stats: PublisherStats
  }

  export type PublisherStatsArr = PublisherStatContainer[];

  export type PublisherRtcStatsReportContainer = {
    subscriberId?: string,
    connectionId?: string,
    rtcStatsReport: RTCStatsReport
  }

  export type PublisherRtcStatsReportArr = PublisherRtcStatsReportContainer[];

  export class Subscriber extends OTEventEmitter<{
    audioBlocked: Event<'audioBlocked', Subscriber>;

    audioLevelUpdated: Event<'audioLevelUpdated', Subscriber> & {
      audioLevel: number
    };

    audioUnblocked: Event<'audioUnblocked', Subscriber>;

    connected: Event<'connected', Subscriber>;

    captionReceived: Event<'captionReceived', Subscriber> & {
      streamId: string;
      caption: string;
      isFinal: boolean;
    };

    destroyed: Event<'destroyed', Subscriber> & {
      reason: string;
    };

    disconnected: Event<'disconnected', Subscriber>;

    encryptionSecretMismatch: Event<'encryptionSecretMismatch', Subscriber>;

    encryptionSecretMatch: Event<'encryptionSecretMatch', Subscriber>;

    mediaStreamAvailable: Event<'mediaStreamAvailable', Subscriber> & {
      mediaStream: MediaStream;
    };

    qualityScoreChanged: Event<'qualityScoreChanged', Subscriber> & {
      qualityScore : QualityScore;
    }

    videoDimensionsChanged: VideoDimensionsChangedEvent<Subscriber>;

    videoDisabled: Event<'videoDisabled', Subscriber> & {
      reason: string;
    };

    videoDisableWarning: Event<'videoDisableWarning', Subscriber>;
    videoDisableWarningLifted: Event<'videoDisableWarningLifted', Subscriber>;

    videoElementCreated: Event<'videoElementCreated', Subscriber> & {
      element: HTMLVideoElement | HTMLObjectElement;
    };

    videoEnabled: Event<'videoEnabled', Subscriber> & {
      reason: string;
    };

    videoQualityChanged: Event<'videoQualityChanged', Subscriber> & {
      reason: VideoQualityReason;
      stats: SubscriberStats;
    };

    networkConditionChanged: Event<'networkConditionChanged', Subscriber> & {
      reason: 'networkConditionChanged';
      stats: SubscriberStats;
    };
  }> {
    element?: HTMLElement;
    id?: string;
    stream?: Stream;

    getAudioVolume(): number;
    getImgData(): string | null;
    getStats(callback: (error?: OTError, stats?: SubscriberStats) => void): void;
    getRtcStatsReport(): Promise<Map<string, any>>;
    getStyle(): SubscriberStyle;
    subscribeToCaptions(value: boolean): Promise<void>;
    isSubscribedToCaptions(): boolean;
    isAudioBlocked(): boolean;
    restrictFrameRate(value: boolean): void;
    setAudioVolume(volume: number): void;
    setPreferredFrameRate(frameRate: number): void;
    setPreferredResolution(resolution: Dimensions): void;
    setAudioMediaProcessorConnector(connector: MediaProcessorConnector | null): Promise<void>;
    setVideoMediaProcessorConnector(connector: MediaProcessorConnector | null): Promise<void>;
    subscribeToAudio(value: boolean): void;
    subscribeToVideo(value: boolean): void;
    setCaptionsTranslationLanguage(langCode: CaptionsLanguageCode | null): Promise<void>;
    getCaptionsTranslationLanguage(): CaptionsLanguageCode | null;

    setStyle<Style extends keyof SubscriberStyle>(
      style: Style,
      value: SubscriberStyle[Style]
    ): void;
    setStyle(styles: Partial<SubscriberStyle>): void;

    videoHeight(): number | undefined;
    videoWidth(): number | undefined;
  }

  export function registerScreenSharingExtension(
    kind: string,
    id: string,
    version: number
  ): void;

  export function reportIssue(callback: (error?: OTError, reportId?: string) => void): void;

  export function setAudioOutputDevice(deviceId: string): Promise<void>;

  export function getActiveAudioOutputDevice(): Promise<AudioOutputDevice>;

  export function setLogLevel(level: number): void;

  export function upgradeSystemRequirements(): void;

  export function unblockAudio(): Promise<undefined>;
}

declare module "@vonage/client-sdk-video" {
  export = OT;
}
