/*---------------------------------------------------------------------------*/
/* media.h                                                                   */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#define DEFAULT_MTU            1292
#define ETH_HEADER_LEN         14
#define IP_HEADER_LEN          20
#define UDP_HEADER_LEN          8
#define RTP_HEADER_LEN         12
#define RTCP_HEADER_LEN         8
#define RTP_H264_HEADER_LEN     2
#define RTP_VP8_HEADER_LEN      2
#define RTP_VP9_HEADER_LEN     16
#define TURN_HEADER_LEN         4
#define TURN_PADDING_LEN        3
#define SRTP_PADDING_LEN        3
#define SRTP_AUTH_LEN          10
#define TURN_HEADER_LEN         4
#define TURN_PADDING_LEN        3
#define DTLS_HEADER_DATA_LEN   13
#define SCTP_HEADER_DATA_LEN   28

enum MediaType {
    AUDIO = 0,
    VIDEO,
    VIDEO_APPSHARING,
    APPSHARING,
    MP4_CONTAINER,
    WEBM_VP8_CONTAINER,
    WEBM_VP9_CONTAINER,
    APPSHARING_CONTAINER,
};

class IMediaProvider * CreateMediaProvider();

class IMediaProvider {
public:
    virtual ~IMediaProvider() {}
    virtual class IMedia * CreateMedia(class IIoMux * const iomux, class UMedia * const user, class IInstanceLog * log, enum MediaType mediaType = MediaType::AUDIO) = 0;
};

enum AudioCoder {
    CODER_UNDEFINED = 0,
    G711_A,
    G711_U,
    G723_53,
    G723_63,
    G729,
    G729A,
    G729B,
    G729AB,
    G726_40,
    G726_32,
    G726_24,
    G726_16,
    GSM,
    DVI4,
    LPC,
    L16,
    ILBC,
    SPEEX,
    DTMF,
    CN,
    RED,
    G722,
    G7221,
    CLEARCHANNEL,
    OPUS_NB,
    OPUS_WB,
    AMR_WB,
};

enum VideoCoder {
    H264 = 128,
    VP8,
    VP9,
    AV1
};

enum AppSharingCoder {
    JRFB = 192,
    JRFB2 = 193,
    VIDEO_CODER = 194,
};

enum ConnectionType {
    REMOTE_RTP = 0,
    REMOTE_VIDEO_STREAM,
    LOCAL_VIDEO_STREAM,
    LOCAL_SHARING_STREAM,
    LOCAL_WEBCAM_STREAM
};

#define CoderMediaType(c) (((c) < ((int) VideoCoder::H264)) ? MediaType::AUDIO : (((c) < ((int) AppSharingCoder::JRFB)) ? MediaType::VIDEO : MediaType::APPSHARING))

#define DEFAULT_PAYLOAD_TYPE_G711_U 0
#define DEFAULT_PAYLOAD_TYPE_G726   2
#define DEFAULT_PAYLOAD_TYPE_GSM    3
#define DEFAULT_PAYLOAD_TYPE_G723   4
#define DEFAULT_PAYLOAD_TYPE_DVI4   5
#define DEFAULT_PAYLOAD_TYPE_LPC    7
#define DEFAULT_PAYLOAD_TYPE_G711_A 8
#define DEFAULT_PAYLOAD_TYPE_G722   9
#define DEFAULT_PAYLOAD_TYPE_L16    10
#define DEFAULT_PAYLOAD_TYPE_G729   18
#define DEFAULT_PAYLOAD_TYPE_CN     19
#define DEFAULT_PAYLOAD_TYPE_AV1    35
#define DEFAULT_PAYLOAD_TYPE_DTMF   101
#define DEFAULT_PAYLOAD_TYPE_H264   102
#define DEFAULT_PAYLOAD_TYPE_VP8    107
#define DEFAULT_PAYLOAD_TYPE_VP9    108
#define DEFAULT_PAYLOAD_TYPE_OPUS   109
#define DEFAULT_PAYLOAD_TYPE_AMR    110

#define DEFAULT_SCTP_CHANNEL_NUMBER 5000

#define CHANNEL_CANDIDATE_HOST              0
#define CHANNEL_CANDIDATE_SRFLX             1
#define CHANNEL_CANDIDATE_PRFLX             2
#define CHANNEL_CANDIDATE_RELAY             3
#define CHANNEL_CANDIDATE_HOST_TCP_PASSIVE  4
#define CHANNEL_CANDIDATE_HOST_TCP_ACTIVE   5
#define CHANNEL_CANDIDATE_HOST_TCP_SO       6 // simultaneous-open
#define CHANNEL_CANDIDATE_SRFLX_TCP_PASSIVE 7
#define CHANNEL_CANDIDATE_SRFLX_TCP_ACTIVE  8
#define CHANNEL_CANDIDATE_SRFLX_TCP_SO      9 // simultaneous-open
#define CHANNEL_CANDIDATE_RELAY_TCP_PASSIVE 10
#define CHANNEL_CANDIDATE_RELAY_TCP_ACTIVE  11
#define CHANNEL_CANDIDATE_RELAY_TCP_SO      12 // simultaneous-open

#define ChannelCandidateIsTcp(c)         ((c) > CHANNEL_CANDIDATE_RELAY)
#define ChannelCandidateIsHost(c)        (((c) == CHANNEL_CANDIDATE_HOST) || ((c) == CHANNEL_CANDIDATE_HOST_TCP_PASSIVE) || ((c) == CHANNEL_CANDIDATE_HOST_TCP_ACTIVE) || ((c) == CHANNEL_CANDIDATE_HOST_TCP_SO))
#define ChannelCandidateIsSrflx(c)       (((c) == CHANNEL_CANDIDATE_SRFLX) || ((c) == CHANNEL_CANDIDATE_SRFLX_TCP_PASSIVE) || ((c) == CHANNEL_CANDIDATE_SRFLX_TCP_ACTIVE) || ((c) == CHANNEL_CANDIDATE_SRFLX_TCP_SO))
#define ChannelCandidateIsRelay(c)       (((c) == CHANNEL_CANDIDATE_RELAY) || ((c) == CHANNEL_CANDIDATE_RELAY_TCP_PASSIVE) || ((c) == CHANNEL_CANDIDATE_RELAY_TCP_ACTIVE) || ((c) == CHANNEL_CANDIDATE_RELAY_TCP_SO))
#define ChannelCandidateIsTcpPassive(c)  (((c) == CHANNEL_CANDIDATE_HOST_TCP_PASSIVE) || ((c) == CHANNEL_CANDIDATE_SRFLX_TCP_PASSIVE) || ((c) == CHANNEL_CANDIDATE_RELAY_TCP_PASSIVE))
#define ChannelCandidateIsTcpActive(c)   (((c) == CHANNEL_CANDIDATE_HOST_TCP_ACTIVE) || ((c) == CHANNEL_CANDIDATE_SRFLX_TCP_ACTIVE) || ((c) == CHANNEL_CANDIDATE_RELAY_TCP_ACTIVE))
#define ChannelCandidateIsTcpSO(c)       (((c) == CHANNEL_CANDIDATE_HOST_TCP_SO) || ((c) == CHANNEL_CANDIDATE_SRFLX_TCP_SO) || ((c) == CHANNEL_CANDIDATE_RELAY_TCP_SO))

enum SetupRole {
    SETUP_ROLE_NONE = 0x00,
    SETUP_ROLE_ACTIVE = 0x01,
    SETUP_ROLE_PASSIVE = 0x02,
    SETUP_ROLE_ACTPASS = 0x03,
    SETUP_ROLE_HOLDCONN = 0x04,
};

enum IceAbortReason {
    ICE_PATH_FOUND = 0x00,
    CALL_TERMINATED = 0x01,
    RESET_ICE_PATH = 0x2,
    UNKNOWN_REASON = 0x03,
};

// Codecs: h264, vp8, FB
class CodecAssignments : public istd::listElement<CodecAssignments> {
public:
    CodecAssignments(int coder, int pt) {
        this->coder = coder;
        this->pt = pt;
    };
    ~CodecAssignments() {};

public:
    int coder;
    int pt;
};

class MediaConfiguration {
public:
    MediaConfiguration(bool turnOnly, bool iceNoHost, int appSharingNumUpdates, int appSharingCaptureTimer, int appSharingBitrate, int appSharingJpegQuality, int appSharingUpdateSum, int appSharingWaitMsForAck, int mediaDropPacketsTx, int mediaDropPacketsRx, bool phoneLoad, bool hdVideo,
                       int execInterval = 160, bool recordRtpStream = false, byte * cert = NULL, size_t certLen = 0, bool testMode = false, int remoteControl = 0) {
        this->turnOnly = turnOnly;
        this->iceNoHost = iceNoHost;
        this->phoneLoad = phoneLoad;
        this->appSharingNumUpdates = appSharingNumUpdates;
        this->appSharingCaptureTimer = appSharingCaptureTimer;
        this->appSharingBitrate = appSharingBitrate;
        this->appSharingJpegQuality = appSharingJpegQuality;
        this->appSharingUpdateSum = appSharingUpdateSum;
        this->appSharingWaitMsForAck = appSharingWaitMsForAck;
        this->appSharingDesktopDuplication = false;
        this->mediaDropPacketsTx = mediaDropPacketsTx;
        this->mediaDropPacketsRx = mediaDropPacketsRx;
        this->certLen = certLen;
        if (cert) {
            this->cert = (byte *)(malloc(certLen));
            memcpy(this->cert, cert, certLen);
        }
        else {
            this->cert = NULL;
        }
        this->hdVideo = hdVideo;
        this->noVpnAddresses = false;
        this->currentUserSip = NULL;
        this->recordingUrl = NULL;
        this->langId = 0;
        this->execInterval = execInterval;
        this->recordRtpStream = recordRtpStream;
        this->isRunningTS = false;
        this->isCapturingApplications = false;
        this->ringerVolume = 700;
        this->handsetVolume = 700;
        this->handsfreeVolume = 700;
        this->headsetVolume = 700;
        this->excludeInterfaces = NULL;
        this->adminCapture = false;
        this->testMode = testMode;
        this->remoteControl = remoteControl;
        this->appSharingVideo = false;
        this->systemProc = false;
    }
    ~MediaConfiguration() {
        if (cert) free(cert);
        if (currentUserSip) free(currentUserSip);
        if (recordingUrl) free(recordingUrl);
        if (excludeInterfaces) free(excludeInterfaces);
    };

    bool turnOnly;
    bool iceNoHost;
    int appSharingNumUpdates;
    int appSharingCaptureTimer;
    int appSharingBitrate;
    int appSharingJpegQuality;
    int appSharingUpdateSum;
    int appSharingWaitMsForAck;
    int mediaDropPacketsTx;
    int mediaDropPacketsRx;
    bool phoneLoad;
    bool hdVideo;
    bool noVpnAddresses;
    byte * cert;
    size_t certLen;
    bool appSharingVideo;
    char* currentUserSip;
    char* recordingUrl;
    int langId;
    int execInterval;
    bool recordRtpStream;
    bool isRunningTS;
    bool appSharingDesktopDuplication;
    bool isCapturingApplications;
    unsigned ringerVolume;
    unsigned handsetVolume;
    unsigned handsfreeVolume;
    unsigned headsetVolume;
    char * excludeInterfaces;
    bool adminCapture;
    bool testMode;
    int remoteControl;
    bool systemProc;
};

// Codecs: h264, vp8, FB
class Codec: public istd::listElement<Codec> {
public:
    Codec() {
        coder = AudioCoder::CODER_UNDEFINED;
        this->addr = NULL; 
        this->mcAddr = NULL; 
    };
    ~Codec() { 
        if(addr) free(addr); 
        if(mcAddr) free(mcAddr); 
    };
public:
    int coder;
    int number;
    int xmitPacket;
    int recvPacket;
    int rate;
    char * addr;
    int port;
    char * mcAddr;
    int mcPort;
    int flags;
    int pt;
};

class IceCandidate : public istd::listElement<IceCandidate> {
public:
    IceCandidate() { 
        this->addr = NULL;
        this->relatedAddr = NULL;
        this->foundation = NULL;
        this->rtpPort = 0;
        this->rtcpPort = 0;
        this->relatedRtpPort = 0;
        this->relatedRtcpPort = 0;
        this->rtpPriority = 0;
        this->rtcpPriority = 0;
        this->type = 0;
    };
    ~IceCandidate() { 
        if(addr) free(addr);
        if(relatedAddr) free(relatedAddr);
        if(foundation) free(foundation);
    };
public:
    char * addr;
    char * relatedAddr;
    word rtpPort;
    word rtcpPort;
    word relatedRtpPort;
    word relatedRtcpPort;
    dword rtpPriority;
    dword rtcpPriority;
    dword type;
    char * foundation;
};

class IceCandidates {
public:
    IceCandidates() { 
        this->fingerprint = NULL;
        this->iceUfrag = NULL;
        this->icePwd = NULL;
        this->lIceUfrag = NULL;
        this->lIcePwd = NULL;
        this->dtlsSetupRole = SETUP_ROLE_NONE;
        this->rtcpMux = false;
        this->count = 0;
    };
    ~IceCandidates() { 
        if(this->fingerprint) free(this->fingerprint);
        if(this->iceUfrag) free(this->iceUfrag);
        if(this->icePwd) free(this->icePwd);
        if(this->lIceUfrag) free(this->lIceUfrag);
        if(this->lIcePwd) free(this->lIcePwd);
        while(this->candidateList.front()) {
            delete this->candidateList.front();
        }
    };
public:
    char * fingerprint;
    char * iceUfrag;
    char * icePwd;
    char * lIceUfrag;
    char * lIcePwd;
    enum SetupRole dtlsSetupRole;
    bool rtcpMux;
    int count;
    istd::list<class IceCandidate> candidateList;
};

class MediaConfig {
public:
    MediaConfig() { this->ice = NULL; this->defAddr = NULL; this->defPort = 0; this->mcAddr = NULL; this->mcPort = 0; remoteDTMF = 0; localDTMF = DEFAULT_PAYLOAD_TYPE_DTMF; };
    ~MediaConfig() { 
        if(ice) delete ice;
        while(this->codecList.front()) {
            delete this->codecList.front();
        }
        if(defAddr) free(defAddr);
        if(mcAddr) free(mcAddr);
    };

public:
    enum MediaType type;
    char * defAddr;
    word defPort;
    class IceCandidates * ice;
    istd::list<class Codec> codecList;
    char * mcAddr;
    word mcPort;
    byte remoteDTMF;
    byte localDTMF;
};

class IMedia {
public:
    virtual ~IMedia() {};
    virtual void Initialize(ISocketProvider * udpSocketProvider, ISocketProvider * tcpSocketProvider, class ISocketContext * socketContext, word minPort, word maxPort, const char * stunServers, const char * turnServers, const char * turnUsername, const char * turnPassword, enum MediaType media, bool stunSlow, bool turnOnly, bool iceNoHost, bool rtpOverTcp, int dropMediaTx, int dropMediaRx, const char * hostPbx, bool noVpnAddresses, istd::list<class CodecAssignments> * assignmentsList, bool rtcpMux = true, ISocketProvider * tlsSocketProvider = nullptr, const char * excludeInterfaces = nullptr, const char * recordingUrl = nullptr, OS_GUID * guid = nullptr, bool recordOn = false, bool recordRtpStream = false, const char * currentUserSip = nullptr, const char * recordingUsername = nullptr, const char * recordingPassword = nullptr) = 0;
    virtual void Connect(class MediaConfig *remoteMediaConfig, bool iceRemoteRole, enum SetupRole remoteDtlsRole) = 0;
    virtual void RtpSend(const void * buf, size_t len, dword timestamp, enum MediaType mediaType, bool isConference = false) = 0;
    virtual void RtpForward(const void * buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker) = 0;
    virtual void RtcpSend(const void * buf, size_t len) = 0;
    virtual void RtcpSendFirRequest(bool retransmission) = 0;
    virtual void RtpDtmf(char digit, byte pt) = 0;
    virtual void RecordingOn(bool on) = 0;
    virtual void SendFIR() = 0;
    virtual void SctpSend(const void * buf, size_t len, unsigned num) = 0;
    virtual void Recv(void * buf, size_t len, bool recvPartial = false) = 0;
    virtual void Close() = 0;
    virtual bool IsClosing() = 0;
    virtual void RewriteIceCredentials(const char * usr, const char* pwd) = 0;
    virtual void CleanSrtpKeys() = 0;
};

enum MediaEndpointEvent {
    CHANNEL_FULL_INTRA_REQUEST = 0,
    CHANNEL_PACKET_LOST_INDICATION,
    CHANNEL_KEYFRAME_SEQUENCES,
    CHANNEL_NACK_MESSAGE,
    CHANNEL_MEDIA_STATISTICS,
    CHANNEL_MEDIA_ERROR,
    CHANNEL_MEDIA_WARNING
};

#define FAULT_CODE_RTP        0x00050000

#define FAULT_CODE_RTP_NO_MEDIA_DATA            (FAULT_CODE_RTP|1)
#define FAULT_CODE_RTP_EXCESSIVE_LOSS_OF_DATA   (FAULT_CODE_RTP|2)
#define FAULT_CODE_RTP_WRONG_PAYLOAD_TYPE       (FAULT_CODE_RTP|3)
#define FAULT_CODE_RTP_STUN_FAILED              (FAULT_CODE_RTP|4)
#define FAULT_CODE_RTP_SRTP_AUTH_FAILED         (FAULT_CODE_RTP|5)
#define FAULT_CODE_RTP_SRTCP_AUTH_FAILED        (FAULT_CODE_RTP|6)
#define FAULT_CODE_RTP_STUN_DNS_FAILED          (FAULT_CODE_RTP|7)
#define FAULT_CODE_RTP_ICE_FAILED               (FAULT_CODE_RTP|8)
#define FAULT_CODE_RTP_DTLS_SRTP_FAILED         (FAULT_CODE_RTP|9)
#define FAULT_CODE_RTP_RECORDING_FAILED         (FAULT_CODE_RTP|10)
#define FAULT_CODE_RTP_NO_MEDIA_CODEC           (FAULT_CODE_RTP|11)
#define FAULT_CODE_RTP_NO_MATCHING_MEDIA_CODEC  (FAULT_CODE_RTP|12)

#define FAULT_CODE_RTP_TURN_ERROR               (FAULT_CODE_RTP|0x1000)

class EventMediaError {
public:
    EventMediaError(int code, const char * errorText, const char * src) {
        this->code = code;
        this->errorText = _strdup(errorText);
        this->src = _strdup(src);
    }
    ~EventMediaError() {
        if(errorText) free(errorText);
        if(src) free(src);
    };

    int code;
    char * errorText;
    char * src;
};

class EventMediaStatistics {
public:
    EventMediaStatistics(bool xmit = false, dword loss = 0, dword jitter = 0, dword roundTrip = 0) {
        this->xmit = xmit;
        this->loss = loss;
        this->jitter = jitter;
        this->roundTrip = roundTrip;
    }
    ~EventMediaStatistics() {};

    bool xmit; // xmit==false: local recv statistics; xmit==true: remote recv statistics received via RTCP
    dword loss;
    dword jitter;
    dword roundTrip;
};

class MediaProviderContext {
public:
    void * session;
    char * src;
    char * app;
};

class USelectDeviceIo {
public:
    virtual void SelectVideoDevice(const char* app, const char* deviceId) = 0;
    virtual void SelectAudioDevice(class IAudioIo * audioIo, const char* app, const char* deviceId, int type) = 0;
    virtual void SelectRingerDevice(class IRingerIo* ringerIo, const char* app, const char* deviceId) = 0;
    virtual void SelectRingtone(class IRingerIo* ringerIo, const char* app, const char* ringtoneId) = 0;
    virtual void AudioDeviceList() = 0;
    virtual void VideoDeviceList() = 0;
    virtual void RingerDeviceList() = 0;
    virtual void RingtoneList() = 0;
    virtual void ChangeVideoDevice(const char* app, const char* deviceId) = 0;
    virtual void ChangeAudioDevice(class IAudioIo* audioIo, const char* app, const char* deviceId, int type) = 0;
    virtual void StartHookDevice(const char* deviceId, byte key, byte callId, const char* number, const char* name) = 0;
    virtual void AudioDeviceRemoved(const char* deviceId) = 0;
    virtual void VideoDeviceRemoved(const char* deviceId) = 0;
    virtual void RingerDeviceRemoved(const char* deviceId) = 0;
};

class SelectedDeviceIo : public istd::listElement<SelectedDeviceIo> {
public:
    SelectedDeviceIo(const char * app, const char * device, int type = 0, bool remote = false) {
        this->app = _strdup(app);
        this->device = _strdup(device);
        this->available = false;
        this->type = type;
        this->activeHookDevice = false;
        this->isRemote = remote;
    }
    ~SelectedDeviceIo() {
        if (this->app) free(this->app);
        if (this->device) free(this->device);
    }
    char * app;
    char * device;
    bool available;
    int type;
    bool activeHookDevice;
    bool isRemote;
};

class UMedia {
public:
    virtual void MediaInitializeComplete(IMedia * const media, class MediaConfig *localMediaConfig) {};
    virtual void MediaConnectResult(IMedia * const media, const char * error) {};
    virtual void MediaRtpSendResult(IMedia * const media) {};
    virtual void MediaSctpSendResult(IMedia * const media) {};
    virtual void MediaRtpRecvResult(IMedia * const media, void * buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker) {};
    virtual void MediaSctpRecvResult(IMedia * const media, void * buf, size_t len) {};
    virtual void MediaSctpRecvAck(IMedia * const media, unsigned num) {};
    virtual void MediaCloseComplete(IMedia * const media) {};
    virtual void MediaEventReceived(IMedia * const media, enum MediaEndpointEvent event, void * ctx) {};
    virtual void MediaRtpRecv(IMedia * const media, const char * src_addr, word src_port, dword ssrc, word pt) {};
    virtual void MediaRtpDtmfNearStart(char digit) {};
    virtual void MediaRtpDtmfNear(char digit) {};
    virtual enum AudioCoder AvailableAudioCoder(unsigned coderNumber) { return AudioCoder::CODER_UNDEFINED; };
    virtual enum VideoCoder AvailableVideoCoder(unsigned coderNumber) { return VideoCoder::H264; };
    virtual enum AppSharingCoder AvailableAppSharingCoder(unsigned coderNumber) { return AppSharingCoder::JRFB; };
    virtual unsigned AvailableAudioCoderCount() { return 0; };
    virtual unsigned AvailableVideoCoderCount() { return 0; };
    virtual unsigned AvailableAppSharingCoderCount() { return 0; };
    virtual void RecordingStopped(const char * error) {};
    virtual void RecordingStarted() {};
};

class IMediaEndpoint {
public:
    virtual ~IMediaEndpoint() {};
    virtual void Recv(char * buf, int len, dword timestamp, short sequenceNumberDiff, bool marker) = 0;
    virtual void Send(char * buf, int len, dword timestamp, unsigned num, enum MediaType mediaType, bool isConference = false) = 0;
};

class UMediaEndpoint {
public:
    virtual void RtpRecvResult(char * buf, int len, dword timestamp, short sequenceNumberDiff, bool marker) {};
    virtual void RtpSendResult() {};
    virtual void RtpSend(char * buf, int len, dword timestamp, short sequenceNumberDiff = 0, bool marker = false) = 0;
    virtual void RtcpSend(char * buf, int len) = 0;
    virtual void SctpRecvResult(char * buf, int len) {};
    virtual void SctpRecvAck(unsigned num) {};
    virtual void SctpSendResult() {};
    virtual void SctpSend(char * buf, int len, unsigned num) = 0;
    virtual word GetRtpSequence() { return 0; };
    virtual void RtcpSendFirRequest(bool retransmission) {};
    virtual void EventReceived(enum MediaEndpointEvent event, void* ctx) {};
};

class IMediaIoChannel {
public:
    virtual ~IMediaIoChannel() {};
    virtual void Open() = 0;
    virtual void RtpRecv(void * buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker) = 0;
    virtual void RtpSendResult() {};
    virtual void SctpRecv(void * buf, size_t len) = 0;
    virtual void SctpRecvAck(unsigned num) {};
    virtual void SctpSendResult() {};
    virtual void Close() = 0;
    virtual void EventRecv(enum MediaEndpointEvent event, void* ctx) = 0;
};

class UMediaIoChannel {
public:
    virtual void MediaIoRtpSend(const void * buf, size_t len, dword timestamp, enum MediaType mediaType, bool isConference = false) = 0;
    virtual void MediaIoRtpForward(const void * buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker, enum MediaType mediaType) = 0;
    virtual void MediaIoRtcpSend(const void * buf, size_t len, enum MediaType mediaType) = 0;
    virtual void MediaIoSctpSend(const void * buf, size_t len, unsigned num) = 0;
    virtual void MediaIoCloseComplete(class IMediaIoChannel * const mediaIoChannel) = 0;
    virtual void MediaIoSendFIR() = 0;
};

class UAppSharingIo;
class UDeviceIo;
class IDeviceIo {
public:
    virtual void QueryDevices(void * context) = 0; // Calls DeviceAdded() for all present devices.
    virtual void CleanRemoteDevices() {};
    virtual const char * StartDevice(void * context, void * src, const char *deviceId, int deviceMode) = 0;
    virtual void StopDevice(void * context, const char *deviceId) = 0;
    virtual void SelectDevice(const char *app, const char *deviceId) = 0;
    virtual void ChangeDevice(const char *app, const char *deviceId) {};
    virtual const char * GetDeviceIo(const char * app) = 0;
    virtual int GetDeviceIoType(const char* deviceId) { return 0; };
    virtual const char * GetDeviceId(unsigned index) { return 0; };
    virtual const char * GetDeviceByType(int type) { return 0; };
    virtual void SelectRingtone(const char *app, const char *ringtone) {};
    virtual const char * GetRingtone(const char * app) { return NULL; };
    virtual void SubscribeApplications() {};
    virtual void UnsubscribeApplications() {};
    virtual void ShareApplication(void* context, unsigned int id, bool admin = false) {};
    virtual void UnshareApplication(void* context, unsigned int id) {};
    virtual void RemoteShareApplication() {};
    virtual void RemoteUnshareApplication() {};
    virtual void RemoteVideoPriority(void* context, const char * ch, bool on) {};
    virtual void UnshareAllApplications(void* context) {};
    virtual void RemoteMediaIoDeviceAdded(const char* deviceId, int deviceType, unsigned deviceCapabilities, const char* deviceName, bool selected = false) {};
    virtual void RemoteMediaIoDeviceRemoved(const char* deviceId) {};
    virtual void RemoteMediaIoDeviceFailed(const char* deviceId) {};
    virtual void RemoteMediaIoQueryDevicesResult() {};
    virtual void AddRemoteUDeviceIoUser(UDeviceIo* user) {};
    virtual void RemoveRemoteUDeviceIoUser(UDeviceIo* user) {};
    virtual void AddRemoteUAppSharingIoUser(UAppSharingIo* user) {};
    virtual void RemoveRemoteUAppSharingIoUser(UAppSharingIo* user) {};
    virtual void RemoteAddAppSharingApplication(unsigned int id, const char * name, const char * desc, const char * icon, const char * thumbnail, bool isDesktop) {};
    virtual void RemoteRemoveAppSharingApplication(unsigned int id) {};
};

class UDeviceIo {
public:
    virtual void MediaIoDeviceAdded(IDeviceIo * deviceIo, void * context, const char * deviceId, int deviceType, unsigned deviceCapabilities, const char * deviceName, bool selected = false) = 0;
    virtual void MediaIoDeviceRemoved(IDeviceIo * deviceIo, void * context, const char * deviceId) = 0;
    virtual void MediaIoDeviceFailed(IDeviceIo * deviceIo, void * context, const char * deviceId) = 0;
    virtual void MediaIoQueryDevicesResult(IDeviceIo * deviceIo, void * context) = 0;
    virtual void MediaIoDeviceStarted(IDeviceIo * deviceIo, const char * deviceId) {};
    virtual void MediaIoDeviceStopped(IDeviceIo * deviceIo, const char * deviceId) {};
};

class UDeviceIoHelper : public istd::listElement<UDeviceIoHelper> {
public:
    UDeviceIoHelper(UDeviceIo* user) {
        this->user = user;
    }
    ~UDeviceIoHelper() {};

    UDeviceIo* user;
};

class UAppSharingIoHelper : public istd::listElement<UAppSharingIoHelper> {
public:
    UAppSharingIoHelper(UAppSharingIo* user) {
        this->user = user;
    }
    ~UAppSharingIoHelper() {};

    UAppSharingIo* user;
};

class UHookDevice {
public:
    virtual void HookDeviceMsg(IAudioIo* audioIo, const char* deviceId, const char* msg) = 0;
    virtual void HookKeyReceived(IAudioIo* audioIo, const char* deviceId, byte key) = 0;
    virtual void AudioDeviceDelayed(IAudioIo* audioIo, const char* deviceIn, const char* deviceOut) {};
    virtual void AudioDeviceOpened(IAudioIo* audioIo, const char* deviceIn, const char* deviceOut) {};
};

class IAudioIoChannel : public IMediaIoChannel {
public:
    static class IAudioIoChannel * Create(class IAudioIo * audioIo, class UMediaIoChannel * const user);
    static unsigned AvailableCoderCount();
    static enum AudioCoder AvailableCoder(unsigned coderNumber);
    virtual ~IAudioIoChannel() {};
    virtual void Initialize(enum AudioCoder coder, bool sc, unsigned mediaPacketizationMs, unsigned execInterval8khz,
        unsigned execJitter8khz, unsigned pullSampleRate, unsigned putSampleRate, bool recordRtpStream = false) = 0;
    virtual bool Pull() = 0;
    virtual const short *PulledSamples() = 0;
    virtual const short *FeedbackSamples() = 0;
    virtual void PutSamples(const short *buffer) = 0;
};

#define AUDIO_IO_DUAL_TONE_MODULATE     0x00000001
#define AUDIO_IO_DUAL_TONE_LOOP         0x00000002
#define AUDIO_IO_DUAL_TONE_AUTOOFF      0x00000004
#define AUDIO_IO_DUAL_TONE_PASSTHROUGH  0x00000008
#define AUDIO_IO_DUAL_TONE_PEER         0x00000010 /* local / peer */
#define AUDIO_IO_DUAL_TONE_BOTH         0x00000020 /* local + full peer / peer + attenuated local */

struct AudioIoDualTone {
    word onTimeMs;
    word offTimeMs;
    word frequency0; /* Hz */
    short level0; /* (0x8000:-infinity, 0x8001:-127.996 dBm ... 0x7fff:127.996 dBm) */
    word frequency1; /* Hz */
    short level1; /* (0x8000:-infinity, 0x8001:-127.996 dBm ... 0x7fff:127.996 dBm) */
};

class IAudioIo : public IDeviceIo {
public:
    static class IAudioIo * Create(class IIoMux * const iomux, class IAudioPhone * const audioPhone, class IInstanceLog * const log, unsigned execInterval8khz, class MediaConfiguration* mediaConfiguration);
    virtual ~IAudioIo() {};
    virtual class IAudioPhone *Initialize(class UAudioIo * const user, class UDeviceIo * const deviceIoUser) = 0;
    virtual void AudioExec(class IAudioExec *audioExec) = 0;

    virtual void StartMonitoring(const char *deviceId, int deviceMode) = 0;
    virtual void StopMonitoring(const char *deviceId) = 0;

    virtual void StartHookDevice(const char *deviceId) = 0;
    virtual void StopHookDevice(const char *deviceId) = 0;
    virtual void SendHookKey(const char *deviceId, byte key, byte callId, const char * number, const char * name) = 0;
    virtual void AcquireHookDevice(const char* deviceId) = 0;
    virtual unsigned DeviceCapabilities(const char* deviceId) = 0;

    virtual void Mute(bool microphone, bool speaker) = 0;

    virtual void StartDualTones(dword toneFlags, unsigned toneCount, const struct AudioIoDualTone *tones) = 0;
    virtual void StopDualTones() = 0;

    virtual void LoopTest(bool on) = 0;

    virtual class IAudioIoChannel *CreateChannel(class UMediaIoChannel * const user) = 0;
    virtual void InitializeChannel(class IAudioIoChannel *audioIoChannel, enum AudioCoder coder, bool sc, unsigned mediaPacketizationMs) = 0;

    virtual void ChannelOpened(class IAudioIoChannel *audioIoChannel) = 0;
    virtual void ChannelClosed(class IAudioIoChannel *audioIoChannel) = 0;
    virtual void ChannelDestroyed(class IAudioIoChannel *audioIoChannel) = 0;

    virtual void Close(class UAudioIo * user) = 0;

    virtual void SetUSelectDeviceIoUser(USelectDeviceIo* user) = 0;
    virtual void AddUHookDeviceUser(UHookDevice* user) = 0;
    virtual void RemoveUHookDeviceUser(UHookDevice* user) = 0;
    virtual void RemoteHookKeyReceived(const char* deviceId, byte key) {};
    virtual void RemoteHookDeviceMsg(const char* deviceId, const char* msg) {};
};

class UAudioIo {
public:
    virtual void CloseAudioIoComplete() = 0;
};

class URingerIo;
class URingtone;
class IRingerIo : public IDeviceIo {
public:
    static class IRingerIo * Create(class IIoMux * const iomux, class IRinger * const ringer, class IInstanceLog * log, class MediaConfiguration* mediaConfiguration);
    virtual ~IRingerIo() {};

    virtual void Initialize(class URingerIo * const user, class UDeviceIo * const deviceUser, class URingtone * const ringtoneUser) = 0;

    virtual void QueryRingtones(void * context) = 0;
    virtual void RingtoneStart(const char *deviceId, int deviceMode, const char *ident) = 0;
    virtual void RingtoneStop(const char *deviceId) = 0;

    virtual void QueryNotificationTones(void * context) = 0;
    virtual void NotificationTonePlay(const char *deviceId, int deviceMode, const char *ident) = 0;

    virtual void SetUSelectDeviceIoUser(USelectDeviceIo* user) = 0;
    virtual void Close(class URingerIo * user) = 0;

    virtual void RemoteRingerIoRingtoneAdded(const char* ident, const char* title, bool selected) {};
    virtual void RemoteRingerIoRingtoneRemoved(const char* ident, const char* title) {};
    virtual void RemoteRingerIoQueryRingtonesResult() {};

    virtual void RemoteRingerIoNotificationToneAdded(const char* ident, const char* title, bool selected) {};
    virtual void RemoteRingerIoNotificationToneRemoved(const char* ident, const char* title) {};
    virtual void RemoteRingerIoQueryNotificationTonesResult() {};

    virtual void AddRemoteURingtoneUser(URingtone* user) {};
    virtual void RemoveRemoteURingtoneUser(URingtone* user) {};
};

class URingerIo {
public:
    virtual void CloseRingerIoComplete() = 0;
};

class URingtone {
public:
    virtual void RingerIoRingtoneAdded(IRingerIo* ringerIo, void* context, const char* ident, const char* title, bool selected) = 0;
    virtual void RingerIoRingtoneRemoved(IRingerIo* ringerIo, void* context, const char* ident, const char* title) = 0;
    virtual void RingerIoQueryRingtonesResult(IRingerIo* ringerIo, void* context) = 0;

    virtual void RingerIoNotificationToneAdded(IRingerIo* ringerIo, void* context, const char* ident, const char* title, bool selected) = 0;
    virtual void RingerIoNotificationToneRemoved(IRingerIo* ringerIo, void* context, const char* ident, const char* title) = 0;
    virtual void RingerIoQueryNotificationTonesResult(IRingerIo* ringerIo, void* context) = 0;
};

class URingtoneHelper : public istd::listElement<URingtoneHelper> {
public:
    URingtoneHelper(URingtone* user) {
        this->user = user;
    }
    ~URingtoneHelper() {};

    URingtone* user;
};

enum VideoDeviceType {
    Webcam = 128,
    VideoScreen,
};

#define RTP_STAP_A             24
#define RTP_STAP_B             25
#define RTP_MTAP16             26
#define RTP_MTAP24             27
#define RTP_FU_A               28
#define RTP_FU_B               29

#define NAL_UNIT_NON_IDR       1
#define NAL_UNIT_IDR           5
#define NAL_UNIT_SEI           6
#define NAL_UNIT_SPS           7
#define NAL_UNIT_PPS           8
#define NAL_UNIT_DELIMITER     9

class IVideoIoChannel : public IMediaIoChannel {
public:
    static class IVideoIoChannel * Create(class IIoMux * const iomux, class IVideoIo * videoIo, class UMediaIoChannel * const user, class IInstanceLog * log);
    static unsigned AvailableCoderCount();
    static enum VideoCoder AvailableCoder(unsigned coderNumber);
    virtual ~IVideoIoChannel() {};
    virtual void Initialize(void * context, const char * channelId, enum VideoCoder coder, ConnectionType connType, bool remoteServices = false) = 0;
};

class IVideoIo : public IDeviceIo {
public:
    static class IVideoIo * Create(class IIoMux * const iomux, class IInstanceLog * log, class MediaConfiguration* mediaConfiguration);
    virtual ~IVideoIo() {};
    virtual class IVideoIoChannel *CreateChannel(class UMediaIoChannel * const user) = 0;
    virtual void Initialize(class UVideoIo * const user, class UDeviceIo * const deviceIoUser, class UAppSharingIo * const appSharingIoUser) = 0;
    virtual const char* StartVideoDevice(void* context, void* src, const char* deviceId, int width, int height, int deviceMode) = 0;
    virtual void StartLocalVideoEncoder(void * context, const char * channelId, int coder, bool remoteServices = false) = 0;
    virtual void StartRemoteVideoEncoder(void * context, const char * channelId, int coder) = 0;
    virtual void StopLocalVideoEncoder(void * context, const char * channelId, int coder) = 0;
    virtual void Close() = 0;
    virtual void SetUSelectDeviceIoUser(USelectDeviceIo* user) = 0;
    virtual void SetRemoteMediaAppSharingUser(class URemoteMediaAppSharing* user) {};
};

class UVideoIo {
public:
    virtual void CloseVideoIoComplete() = 0;
};

class IAppClientMedia {
public:
    virtual void SetExcludeInterfaces(const char * excludeInf) = 0;
    virtual void SetNoVpnAddresses(bool noVpnAddresses) = 0;
    virtual void SetCurrentUserSip(const char * sip) = 0;
    virtual void SetRecordingUrl(const char * url, bool force) = 0;
    virtual void SetLangId(const char * langId) = 0;
    virtual bool IsCapturingApplications() { return false; };
    virtual class IVideoIo* GetVideoIo() = 0;
    virtual void QueryDevices(bool cleanDevices) {};
    virtual void MyAppsRunningRemote(bool remote) {};
    virtual void SetRingerVolume(unsigned volume) {};
    virtual void SetHandsetVolume(unsigned volume) {};
    virtual void SetHandsfreeVolume(unsigned volume) {};
    virtual void SetHeadsetVolume(unsigned volume) {};
};

enum AppSharingDeviceType {
    FrameBuffer = 192,
    Canvas,
};

class IAppSharingIoChannel : public IMediaIoChannel {
public:
    static class IAppSharingIoChannel * Create(class IIoMux * const iomux, class IAppSharingIo * appSharingIo, class UMediaIoChannel * const user, class IInstanceLog * log, const char * dn, class MediaConfiguration * mediaConfiguration);
    static unsigned AvailableCoderCount();
    static enum AppSharingCoder AvailableCoder(unsigned coderNumber);
    virtual ~IAppSharingIoChannel() {};
    virtual void Initialize(void * context, const char * channelId, enum AppSharingCoder coder) = 0;
};

class IAppSharingIo : public IDeviceIo {
public:
    static class IAppSharingIo * Create(class IIoMux * const iomux, class IInstanceLog * log, class MediaConfiguration * mediaConfiguration);
    virtual ~IAppSharingIo() {};
    virtual void Initialize(class UAppSharingIo * const user, class UDeviceIo * const deviceIoUser) = 0;
    virtual void SignalingMessage(void * context, void * buffer, int len) = 0;
    virtual void GiveControlToUser(void * context, unsigned int sessionId) = 0;
    virtual void RemoveControlFromUser(void * context, unsigned int sessionId) = 0;
    virtual void RequestControlFromUser(void * context, unsigned int sessionId) = 0;
    virtual void AddRemoteContainer(void * context, void * container, const char * channelId, enum MediaType coder) = 0;
    virtual void RemoveRemoteContainer(void * container, enum MediaType coder) = 0;
    virtual void Close() = 0;
};

class UAppSharingIo {
public:
    virtual void AddAppSharingApplication(unsigned int id, const char * name, const char * desc, const char * hwId, const char * icon,  const char * thumbnail, bool isDesktop = false) = 0;
    virtual void RemoveAppSharingApplication(unsigned int id) = 0;
    virtual void UpdateAppSharingApplication(unsigned int id, const char * name, const char * desc, const char * hwId, const char * icon,  const char * thumbnail) = 0;
    virtual void CloseAppSharingIoComplete() = 0;
    virtual void MediaIoAppSharingContainerSend(const void * buf, size_t len, dword timestamp, const void * context, bool local, enum MediaType type) = 0;
    virtual void RequestAppSharingControl(unsigned int userId) = 0;
};

class IJitterBuffer {
public:
    static IJitterBuffer * Create(class UJitterBuffer * user);
    virtual ~IJitterBuffer() {};

    virtual unsigned Configure(enum AudioCoder coder, unsigned mediaPacketizationMs, unsigned pullInterval8khz,
        unsigned minBufferMs, unsigned initialBufferMs, unsigned maxBufferMs) = 0;
    virtual void Adjust(unsigned initialBufferMs) = 0;
    virtual unsigned GetDelay() = 0;
    virtual void Push(const void * buf, size_t len, dword timestamp) = 0;
    virtual bool Pull(bool discard) = 0;
};

class UJitterBuffer {
public:
    virtual void JitterBufferPulled(IJitterBuffer * const jitterBuffer, const void * buf, size_t len) = 0;
};

class ISrtpSession {
public:
    static ISrtpSession * Create(byte * srtpMasterkey, byte * srtpMastersalt, const char * profile);
    virtual ~ISrtpSession() {};

    virtual byte * Protect(byte * header, int hLen, byte * payload, int pLen, int * olen) = 0;
    virtual int Unprotect(char * srtpPacket, int len) = 0;
    virtual byte * SrtcpProtect(byte * rtcpPacket, int pLen, int * olen) = 0;
    virtual int SrtcpUnprotect(char * srtcpPacket, int len) = 0;
};

class UMediaContainer {
public:
    virtual void ContainerSample(char * buf, int len) = 0;
};

class IMediaContainer {
public:
    virtual ~IMediaContainer() {};
    virtual void PutSample(char * buf, int len, dword timestamp) = 0;
    virtual void Initialize(void) = 0;
};

enum VideoFormat {
    VIDEO_FORMAT_UNKNOWN = 0,
    VIDEO_FORMAT_RGB24,
    VIDEO_FORMAT_RGB32,
    VIDEO_FORMAT_NV12,
    VIDEO_FORMAT_YUY2,
    VIDEO_FORMAT_I420,
    VIDEO_FORMAT_YV12,
    VIDEO_FORMAT_IYUV,
    VIDEO_FORMAT_ENCODED
};

enum VideoInterlaceMode {
    VIDEO_INTERLACE_Unknown = 0,
    VIDEO_INTERLACE_Progressive = 1,
    VIDEO_INTERLACE_FieldInterleavedUpperFirst = 2,
    VIDEO_INTERLACE_FieldInterleavedLowerFirst = 3,
    VIDEO_INTERLACE_FieldSingleUpper = 4,
    VIDEO_INTERLACE_FieldSingleLower = 5,
    VIDEO_INTERLACE_MixedInterlaceOrProgressive	= 6
};

struct VideoRatio {
    int numerator;
    int denominator;
};

struct VideoFrameFormat {
    int captureWidth;
    int captureHeight;
    int width;
    int height;
    int stride;
    int averageBitrate;
    int targetBitrate;
    int sampleSize;
    bool hdVideo;
    bool appSharing;
    enum VideoFormat format;
    enum VideoInterlaceMode interlace;
    struct VideoRatio frameRate;
    struct VideoRatio aspectRatio;
};

#define WEBCAM_CAPABILITY_FACING_FRONT  0x00010000

class IWebcam {
public:
    virtual ~IWebcam() {};
    virtual struct VideoFrameFormat * Start(int width, int height, bool hdVideo) = 0;
    virtual void Stop() = 0;
    virtual void Close() = 0;
    virtual const char * GetWebcamId() = 0;
    virtual unsigned GetWebcamCapabilities() = 0;
    virtual const char * GetWebcamName() = 0;
    virtual void FullIntraRequest() {};
    virtual bool ReadSample() { return true; };
    virtual void WebcamFlushed() {};
    virtual bool IsFlushing() { return false; };
    virtual void * GetContext() = 0;
};

class UWebcam {
public:
    virtual void WebcamVideoFormat(class IWebcam * const webcam, const struct VideoFrameFormat * format) = 0;
    virtual void WebcamSample(class IWebcam * const webcam, const void * buf, int len, dword timestamp, dword duration, const struct VideoFrameFormat * format) = 0;
};

class IWebcamProvider {
public:
    static class IWebcamProvider * Create(class IIoMux * const iomux, class UWebcamProvider * const webcamProviderUser, class UWebcam * const webcamUser, class IInstanceLog * const log);
    virtual ~IWebcamProvider() {};
    virtual void QueryDevices(void * context) = 0;
    virtual void Close() = 0;
};

class UWebcamProvider {
public:
    virtual void WebcamLost(class IWebcam * const webcam, void * context) = 0;
    virtual void WebcamFailed(class IWebcam* const webcam, void* context) = 0;
    virtual void WebcamAdded(class IWebcam * const webcam, void * context) = 0;
    virtual void WebcamQueryResult(void * context) = 0;
    virtual void WebcamClosed(class IWebcam * const webcam) = 0;
    virtual void WebcamProviderCloseResult() = 0;
};

class IMediaEncoder
{
public:
    static class IMediaEncoder * Create(class IIoMux * const iomux, class UMediaEncoder * const user, class IInstanceLog * log, enum VideoCoder coder);
    virtual ~IMediaEncoder() {};
    virtual bool Initialize(const struct VideoFrameFormat * const format) = 0;
    virtual void Encode(const void * buf, int len, dword timestamp, dword duration, const struct VideoFrameFormat * f) = 0;
    virtual void PacketLostIndication() {};
    virtual bool FullIntraRequest() = 0;
    virtual void RtpNackMessage(void* ctx) {};
    virtual void KeyFrameSequences(void* ctx) {};
    virtual enum VideoCoder GetVideoCoder() = 0;
    virtual void Close() = 0;
};

class UMediaEncoder
{
public:
    virtual void EncodeResult(class IMediaEncoder * encoder, const void * buf, int len, dword timestamp, dword duration) = 0;
    virtual void MediaEncoderCloseResult(class IMediaEncoder * encoder) = 0;
};

class URemoteMediaAppSharing
{
public:
    virtual void EncodeResult(class IMediaEncoder* encoder, const void* buf, int len, dword timestamp, dword duration) = 0;
    virtual void RemoteShareApplication() = 0;
    virtual void RemoteUnshareApplication() = 0;
    virtual void ShareApplication(unsigned int id) = 0;
    virtual void UnshareApplication(unsigned int id) = 0;
};

class IMediaDecoder
{
public:
    static class IMediaDecoder * Create(class IIoMux * const iomux, class UMediaDecoder * const user, class IInstanceLog * log, enum VideoCoder coder);
    virtual ~IMediaDecoder() {};
    virtual void DecodeFrame(const void *buf, int len, dword timestamp) = 0;
    virtual void DecodeStream(const void *buf, int len, dword timestamp, short sequenceNumberDiff, bool marker) = 0;
    virtual enum VideoCoder GetVideoDecoder() = 0;
    virtual void Close() = 0;
};

class UMediaDecoder
{
public:
    virtual void DecodeResult(class IMediaDecoder * decoder, const void * buf, int len, dword timestamp, dword duration, struct VideoFrameFormat * f) = 0;
    virtual void MediaDecoderCloseResult(class IMediaDecoder * decoder) = 0;
};

class IRenderVideoProvider {
public:
    static class IRenderVideoProvider* Create(class IIoMux* const iomux, class URenderVideoProvider* const renderVideoProviderUser, class IInstanceLog* const log);
    virtual ~IRenderVideoProvider() {};

    virtual class IRenderVideoStream* OpenVideoRenderStream(class URenderVideoStream* const user, bool preview, enum VideoCoder decoder = VideoCoder::VP8, bool alwaysOnTop = false) = 0;

    virtual void Close() = 0;
};

class URenderVideoProvider {
public:
    virtual void RenderVideoProviderCloseResult() = 0;
};

class IRenderVideoStream {
public:
    virtual ~IRenderVideoStream() {};
    virtual void RtpRecv(void* buf, size_t len, unsigned int timestamp, short sequenceNumberDiff, bool marker) = 0;
    virtual void ChangeOutputDevice(int device) = 0;
    virtual void DisplayFrame(const void* buf, int len, struct VideoFrameFormat* f) = 0;
    virtual bool IsPreview() = 0;
    virtual void Close() = 0;
};

class URenderVideoStream {
public:
    virtual void RenderVideoStreamCloseResult(class IRenderVideoStream* renderer) = 0;
};