#ifndef AoqClientEngine_h
#define AoqClientEngine_h

#include "engine_define.h"

#include <cstddef>
#include <cstdint>

namespace AoqClientSdk {

/* 按模块划分 */
enum AoqErrorCode {
    /* 公共错误码 */
    AoqECOK = 0,
    AoqECParamInvalid = 1,
    AoqECStateInvalid = 2,
    /* 接口被调但当前平台/模式未实现（如 iOS 屏幕共享 ViaExtension 模式 v1 未实现） */
    AoqECUnSupport = 3,

    AoqECAudio = 100,
    AoqECAudioExternalBufferFull = 110,
    AoqECAudioDevice = 120,
    AoqECAudioDeviceRecordingAuthFailed = 121,
    AoqECAudioDeviceRecordingOccupied = 122,
    AoqECAudioDeviceRecordingBackgroundStart = 123,
    AoqECAudioDeviceRecordingStartFail = 124,
    AoqECAudioDevicePlayoutOccupied = 125,
    AoqECAudioDevicePlayoutBackgroundStart = 126,
    AoqECAudioDevicePlayoutStartFail = 127,
    AoqECAudioDeviceEarpieceRequiresVoipMode = 128,

    AoqECVideo = 200,
    AoqECVideoExternalBufferFull = 210,
    AoqECVideoExternalCaptureNotEnabled = 211,
    AoqECVideoExternalEncoderNotEnabled = 212,
    /* 视频设备/采集 */
    AoqECVideoDevice = 220,
    AoqECVideoDeviceCameraOpenFail = 221,
    AoqECVideoDeviceCameraAuthFailed = 222,
    AoqECVideoDeviceCameraOccupied = 223,
    AoqECVideoDeviceCameraRunningError = 224,
    /* 视频编解码 */
    AoqECVideoCodec = 230,
    AoqECVideoCodecEncoderInitFail = 231,
    /* 视频渲染 */
    AoqECVideoRender = 240,
    AoqECVideoRenderCreateFail = 241,
    AoqECVideoRenderDrawError = 242,
};

/* 按模块划分 */
enum AoqWarningCode {
    /* 公共警告码 */
    AoqWCOK = 0,

    AoqWCAudio = 100,
    AoqWCAudioHowling = 101,
    AoqWCAudioDevice = 120,
    AoqWCAudioDeviceMicEnumerateError = 121,
    AoqWCAudioDeviceMicStartTimeout = 122,
    AoqWCAudioDeviceRecordingError = 123,
    AoqWCAudioDeviceSpeakerEnumerateError = 124,
    AoqWCAudioDeviceSpeakerStartTimeout = 125,
    AoqWCAudioDevicePlayoutError = 126,

    AoqWCVideo = 200,
    AoqWCVideoCameraEnumerateError = 201,
    AoqWCVideoEncoderSwitched = 202,
    AoqWCVideoRenderDowngrade = 203,
};

/**
 * @brief 引擎创建配置
 * 所有 const char* 字符串由调用方负责生命周期，
 * 至少需保持到 createEngine 调用返回后，引擎内部将按需拷贝。
 */
struct AOQ_API AoqCreateConfig {
    /* SDK 工作目录 */
    const char* workDir = nullptr;
#if defined(__ANDROID__)
    /* true-蓝牙SCO模式 false-蓝牙A2DP模式，仅 Android 有效 */
    bool        isBTScoMode = false;
#endif
    /* 是否开启音频数据保存 */
    bool        enableDumpAudio = false;
    /* 扩展参数 (JSON 字符串) */
    const char* extras = nullptr;
};

/**
 * @brief track 媒体类型枚举值
 */
enum AoqTrackType {
    AoqTrackTypeAudio  = 0,
    AoqTrackTypeVideo  = 1,
    AoqTrackTypeData   = 2,
};

/**
 * @brief track 媒体格式枚举值
 */
enum AoqEncoderType {
    AoqEncoderTypeUnknown   = 0,
    AoqEncoderTypeAudioPCM  = 1,
    /* 插件化，需要联系SDK提供内置或动态下载到APP下 */
    AoqEncoderTypeAudioOpus = 2,
    AoqEncoderTypeVideoH264 = 3,
    AoqEncoderTypeVideoJpeg = 4,
    AoqEncoderTypeDataText  = 5,
};

/**
 * @brief 流式与非流式模式，仅对音频下行生效；
 */
enum AoqTrackMode {
    /* 分段：数据按语义片段（如一句话）打包送达 */
    AoqTrackModeSegment = 0,
    /* 流式：数据持续、连续地送达 */
    AoqTrackModeStream  = 1,
};

/**
 * @brief track 属性
 */
struct AOQ_API AoqTrackParam {
    AoqTrackType   trackType   = AoqTrackTypeAudio;
    /* 流式与非流式模式，仅对音频下行生效； */
    AoqTrackMode    trackMode    = AoqTrackModeSegment;
};

/**
 * @brief relay endpoint
 */
struct AoqRelayEndpoint {
    int         route_index = -1;
    const char* endpoint = nullptr;
    int         port     = 0;
};

/**
 * @brief 引擎连接状态
 */
enum AoqConnectionStatus {
    AoqConnectionStatusDisconnected = 0,
    AoqConnectionStatusConnecting   = 1,
    AoqConnectionStatusConnected    = 2,
    AoqConnectionStatusFailed       = 3,
};

/**
 * @brief 链接鉴权配置
 * publishTracks / subscribeTracks 为 C 风格数组 + 长度，
 * 数组内存由调用方持有，仅需在 connect 调用期间保持有效。
 */
struct AOQ_API AoqConnectConfig {
    const char* token            = nullptr;
    const char* sid              = nullptr;
    const char* certFingerprint  = nullptr;
    const char* workspaceIdHash  = nullptr;
    const AoqRelayEndpoint* relayEndpoints      = nullptr;
    size_t                  relayEndpointsCount = 0;

    /* 推流 track 属性数组 */
    const AoqTrackParam* publishTracks      = nullptr;
    size_t               publishTracksCount = 0;
    /* 拉流 track 属性数组 */
    const AoqTrackParam* subscribeTracks      = nullptr;
    size_t               subscribeTracksCount = 0;
};

/**
 * @brief 音频流统计信息
 */
struct AoqAudioPublishStats {
    AoqTrackType   trackType   = AoqTrackTypeAudio;
    /* 码率 bps */
    unsigned int   bitrate     = 0;
    /* 字节数（累计） */
    uint64_t       bytes       = 0;
    /* 推流编码音量 */
    unsigned int   encodeVolume   = 0;
};

/**
 * @brief 视频流统计信息
 */
struct AoqVideoPublishStats {
    AoqTrackType   trackType   = AoqTrackTypeVideo;
    /* 码率 bps */
    unsigned int   bitrate     = 0;
    /* 字节数（累计） */
    uint64_t       bytes       = 0;
    /* 编码 fps */
    unsigned int   encodeFps   = 0;
};

/**
 * @brief 数据消息流统计信息
 */
struct AoqDataMsgPublishStats {
    AoqTrackType   trackType   = AoqTrackTypeData;
    /* 码率 bps */
    unsigned int   bitrate     = 0;
    /* 字节数（累计） */
    uint64_t       bytes       = 0;
};

/**
 * @brief 音频流统计信息
 */
struct AoqAudioSubscribeStats {
    AoqTrackType   trackType   = AoqTrackTypeAudio;
    /* 码率 bps */
    unsigned int   bitrate     = 0;
    /* 字节数（累计） */
    uint64_t       bytes       = 0;
    /* 播放音量 */
    unsigned int   playVolume   = 0;
};

/**
 * @brief 视频流统计信息
 */
struct AoqVideoSubscribeStats {
    AoqTrackType   trackType   = AoqTrackTypeVideo;
    /* 码率 bps */
    unsigned int   bitrate     = 0;
    /* 字节数（累计） */
    uint64_t       bytes       = 0;
    /* 解码 fps */
    unsigned int   decodeFps   = 0;
    /* 渲染 fps */
    unsigned int   renderFps   = 0;
};

/**
 * @brief 数据消息统计信息
 */
struct AoqDataMsgSubscribeStats {
    AoqTrackType   trackType   = AoqTrackTypeData;
    /* 码率 bps */
    unsigned int   bitrate     = 0;
    /* 字节数（累计） */
    uint64_t       bytes       = 0;
};

/**
 * @brief 网络统计信息
 */
struct AoqNetworkStats {
    /* 发送码率 bps */
    unsigned int   sendBitrate     = 0;
    /* 发送字节数（累计） */
    uint64_t       sendBytes       = 0;
    /* 接收码率 bps */
    unsigned int   recvBitrate     = 0;
    /* 接收字节数（累计） */
    uint64_t       recvBytes       = 0;
    /* 丢包率 0-100 */
    unsigned int   loss            = 0;
    /* rtt ms */
    unsigned int   rtt             = 0;
};

/**
 * @brief 引擎统计信息
 */
struct AoqStats {
    const AoqAudioPublishStats* audioPublishStats      = nullptr;
    unsigned int                audioPublishStatsCount = 0;
    const AoqVideoPublishStats* videoPublishStats      = nullptr;
    unsigned int                videoPublishStatsCount = 0;
    const AoqDataMsgPublishStats* dataMsgPublishStats  = nullptr;
    unsigned int                  dataMsgPublishStatsCount = 0;

    const AoqAudioSubscribeStats* audioSubscribeStats  = nullptr;
    unsigned int                  audioSubscribeStatsCount = 0;
    const AoqVideoSubscribeStats* videoSubscribeStats  = nullptr;
    unsigned int                  videoSubscribeStatsCount = 0;
    const AoqDataMsgSubscribeStats* dataMsgSubscribeStats  = nullptr;
    unsigned int                    dataMsgSubscribeStatsCount = 0;

    const AoqNetworkStats* networkStats      = nullptr;
};

/********************************************************************** Auido */

/**
 * @brief 音频采集配置
 */

struct AoqAudioCaptureConfig {
    /** 是否外部采集 */
    bool isExternal = false;
#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined (__OHOS__)
    /** 是否启用 VoIP 模式（硬件AEC） 移动端有效，采集播放参数先到为准，都停止后可变化 */
    bool isVoipMode = false;
#endif

    /** 音频采集通道数 支持1/2 */
    int channel = 1;
};

/**
 * @brief 音频播放配置
 */

struct AoqAudioPlaybackConfig {
#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined (__OHOS__)
    /** 是否启用 VoIP 模式（硬件AEC） 移动端有效，采集播放参数先到为准，都停止后可变化 */
    bool isVoipMode = false;
    /** 是否默认扬声器 移动端有效，非Voip时无效 */
    bool isDefaultSpeaker = true;
#endif
    /** 是否外部播放 */
    bool isExternal = false;

    /** 音频播放通道数 支持1/2 */
    int channel = 1;
};

/**
 * @brief 音频设备状态码
 */
enum AoqAudioDeviceStateCode {
    AoqAudioDeviceNone = 0,
    /* 打开采集通知 Starting */
    AoqAudioDeviceRecordStarting = 1,
    /* 打开采集成功 Recording */
    AoqAudioDeviceRecordStarted = 2,
    /* 关闭采集通知 Stopping */
    AoqAudioDeviceRecordStopping = 3,
    /* 关闭采集成功 Stopped */
    AoqAudioDeviceRecordStopped = 4,
    /* 采集失败通知 */
    AoqAudioDeviceRecordFail = 5,

    /* 打开播放通知 */
    AoqAudioDevicePlayStarting = 6,
    /* 打开播放成功 */
    AoqAudioDevicePlayStarted = 7,
    /* 关闭播放通知 */
    AoqAudioDevicePlayStopping = 8,
    /* 关闭播放成功 */
    AoqAudioDevicePlayStopped = 9,
    /* 播放失败通知 */
    AoqAudioDevicePlayFail = 10,
};

/**
 * @brief 音频设备状态
 */
struct AoqAudioDeviceState {
    /** 音频设备状态码 */
    AoqAudioDeviceStateCode state = AoqAudioDeviceNone;
    /* AoqErrorCode */
    int reason = 0;
};

/**
 * @brief 音频编解码配置
 */
struct AoqAudioCodecConfig {
    /* 轨道类型，默认音频 */
    AoqTrackType   trackType  = AoqTrackTypeAudio;
    /* 编码类型，默认 PCM */
    AoqEncoderType codecType  = AoqEncoderTypeAudioPCM;
    /* 采样率 默认 48K；编码支持 Opus 8/16/48K、PCM 8/16/32/48K；
       解码额外支持 24K，但 24K 仅限 Segment 模式（Stream 模式不支持 24K，
       非法组合在 connect 时通过 onError 回调 AoqECParamInvalid） */
    int            sampleRate = 48000;
    /* 声道数 默认 1，支持1/2 */
    int            channel    = 1;
    /* 码率 (bps)，默认 32kbps */
    int            bitrate    = 32000;
};

/**
 * @brief 音频设备路由类型
 */
enum AoqAudioDeviceRouteType {
  AoqAudioDeviceRouteDefault = 0,     /* 默认 */
  AoqAudioDeviceRouteHeadset = 1,     /* 耳机 */
  AoqAudioDeviceRouteEarpiece = 2,    /* 听筒 */
  AoqAudioDeviceRouteHeadsetNoMic = 3,/* 无麦耳机 */
  AoqAudioDeviceRouteSpeakerPhone = 4,/* 内置扬声器 */
  AoqAudioDeviceRouteUsb = 5,         /* USB */
  AoqAudioDeviceRouteBluetooth = 6,   /* 蓝牙 */
  AoqAudioDeviceRouteBluetoothA2dp = 7, /* 蓝牙 */
};

#if (defined(__APPLE__) && TARGET_OS_IOS)
enum AoqAudioSessionRestriction {
    AoqAudioSessionNone               = 0,
    AoqAudioSessionSetCategory        = 1 << 0,
    AoqAudioSessionConfigureSession   = 1 << 1,
    AoqAudioSessionDeactivateSession  = 1 << 2,
    AoqAudioSessionActivateSession    = 1 << 3,
};
#endif

/****************************************************************** AuidoFile */

/**
 * @brief 音频文件推流播放配置
 */
struct AoqAudioFileMixConfig {
    /** 文件名（含路径） */
    const char* fileName = nullptr;
    /** 循环次数，-1表示无限循环 */
    int cycles = -1;
    /** 起始播放位置(ms) */
    long startPosMs = 0;
    /** 推流音量，取值范围[0-100]，默认值：100 */
    int publishVolume = 100;
    /** 播放音量，取值范围[0-100]，默认值：100 */
    int playoutVolume = 100;
};


/**
 * @brief 音频文件状态
 */
enum AoqAudioFileStateCode {
    AoqAudioFileNone = 0,
    /** 开始播放 */
    AoqAudioFileStarted = 1,
    /** 停止播放 */
    AoqAudioFileStopped = 2,
    /** 暂停播放 */
    AoqAudioFilePaused = 3,
    /** 恢复播放 */
    AoqAudioFileResumed = 4,
    /** 播放完毕 */
    AoqAudioFileEnded = 5,
    /** 正在缓冲 */
    AoqAudioFileBuffering = 6,
    /** 缓冲结束 */
    AoqAudioFileBufferingEnd = 7,
    /** 播放失败 */
    AoqAudioFileFailed = 8,
};

/**
 * @brief 音频文件播放错误码
 */
enum AoqAudioFileErrorCode {
    /** 没有错误 */
    AoqAudioFileNoError = 0,
    /** 打开文件失败 */
    AoqAudioFileOpenFailed = 1,
    /** 解码文件失败 */
    AoqAudioFileDecodeFailed = 2,
};

/**
 * @brief 音频文件状态
 */
struct AoqAudioFileState {
    /** 文件ID */
    const char* fileId = nullptr;
    /** 文件状态码 */
    AoqAudioFileStateCode stateCode = AoqAudioFileNone;
    /** 文件错误码 */
    AoqAudioFileErrorCode errorCode = AoqAudioFileNoError;
};

/************************************************************** AuidoExteranl */

/**
 * @brief 外部音频流配置
 */
struct AoqAudioExternalStreamConfig {
    /** 音频轨道类型 */
    AoqTrackType   trackType = AoqTrackTypeAudio;
    /** 音频流格式，支持PCM */
    AoqEncoderType codecType = AoqEncoderTypeAudioPCM;
    /** 声道数，默认值：1（受限推流音频Codec支持1/2，超过的声道忽略 */
    int channels = 1;
    /** 采样率，默认值：48K，支持8/12/16/24/32/44.1/48/64/88.2/96/176.4/192K */
    int sampleRate = 48000;
    /** 播放音量，取值范围[0-100]，默认值：100 */
    int playoutVolume = 100;
    /** 推流音量，取值范围[0-100]，默认值：100 */
    int publishVolume = 100;
    /** 最大缓冲时长，默认值： 10 * 60 * 1000 ms，取值范围[100, ~]， SDK缓存时长超过时Push会失败 */
    int maxBufferDuration = 600000;
    /** 输入PCM做3A处理 */
    bool enable3A = false;
};

/**
 * @brief 音频数据
 */
struct AoqAudioFrameData {
    /** 音频数据 */
    void* dataPtr       = 0;
    /** 采样点数（单个声道） */
    int numOfSamples    = 0;
    /** 每个采样点的字节数 */
    int bytesPerSample  = 0;
    /** 声道数 */
    int numOfChannels   = 0;
    /** 每秒采样点数 */
    int samplesPerSec   = 0;
    /** PCM输入轮次（用于 PushAudioExternalStreamData 消费完通知） */
    int pushSequence = 0;
    /** 时间戳 */
    int64_t timeStamp = 0;
    /** 数据长度 */
    int dataSize = 0;
    /** 数据回调有效，true-SDK生成的静音数据 */
    bool autoGenMute = false;
};

/**
 * @brief 音频流方向
 */
enum AoqAudioStreamDirection {
    /** 推流音频 */
    AoqAudioStreamPublish = 0,
    /** 播放音频 */
    AoqAudioStreamPlayout = 1,
};

/**
 * @brief 外部音频流控制
 */
enum AoqAudioExternalStreamToggle {
    /** 恢复正常 */
    AoqAudioExternalStreamToggleNormal = 0,
    /** 暂停 */
    AoqAudioExternalStreamTogglePause = 1,
};

/********************************************************* AuidoFrameObserver */

/**
 * @brief 音频数据源类型
 */
enum AoqAudioSource {
    /** 采集的音频数据 */
    AoqAudioSourceCaptured = 0,
    /** 3A后的音频数据 */
    AoqAudioSourceProcessCaptured = 1,
    /** 推流的音频数据，需要Connect成功 */
    AoqAudioSourcePublish = 2,
    /** 播放的音频数据 */
    AoqAudioSourcePlayback = 3,
    /** 占位 */
    AoqAudioSourceMax,
};

enum AoqAudioObserverMode {
    AoqAudioObserverModeReadOnly = 0,
    AoqAudioObserverModeReadWrite = 1
};

/**
 * @brief 音频数据回调参数设置
 *
 */
struct AoqAudioObserverConfig{
    /* 回调音频采样率，受限Source源 不一致时重采样，支持8/12/16/24/32/44.1/48/64/88.2/96/176.4/192K */
    int sampleRate = 48000;
    /* 回调音频声道数，支持1/2，受限拉流Codec参数 */
    int channels = 1;
    /* 回调模式 */
    AoqAudioObserverMode mode = AoqAudioObserverModeReadOnly;
};

/**************************************************** AudioVolumeIndication */

/**
 * @brief 本地音量提示配置
 */
struct AoqAudioVolumeIndicationConfig {
    /** 回调间隔，单位毫秒；<=0 表示关闭回调，>0 且 <10 时按 10 处理 */
    int interval = 0;
    /** 音量平滑系数，取值越大越平滑，取值范围[0-10]，默认值：3 */
    int smooth = 3;
};

/**
 * @brief 本地音量信息
 */
struct AoqAudioVolume {
    /** 平滑后的瞬时音量，取值范围[0-255] */
    int volume = 0;
};

/********************************************************************** Auido */

/**************************************************************** DataMessage */

/**
 * @brief 消息数据结构
 * 回调中使用时仅保证回调期间有效，如需异步使用请自行拷贝。
 */
struct AOQ_API AoqDataMsg {
    /** 指向调用方持有的内存 */
    const uint8_t* data = nullptr;
    /** 字节数 */
    size_t dataSize = 0;
};

/**************************************************************** DataMessage */

/********************************************************************** Video */
/* 渲染显示模式枚举值 */
enum AoqRenderMode {
    AoqRenderModeAuto    = 0,
    AoqRenderModeStretch = 1,
    AoqRenderModeFill    = 2,
    AoqRenderModeCrop    = 3,
};

/**
 * @brief 视频渲染数据结构
 * view 为平台原生视图句柄的不透明指针：
 *   - Apple : NSView*
 *   - Windows: HWND
 *   - Android: SDK 内部渲染视图适配对象
 * 调用方需保证 view 生命周期长于 setLocalView/setRemoteView 设置期间。
 */
struct AOQ_API AoqVideoCanvas {
    void*         view       = nullptr;
    AoqRenderMode renderMode = AoqRenderModeAuto;
};

#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined(__OHOS__)
/**
 * @brief 摄像头方向（移动端适用；桌面端无前后置概念，本枚举不暴露）
 */
enum AoqCameraDirection {
    /** 前置摄像头 */
    AoqCameraDirectionFront = 0,
    /** 后置摄像头 */
    AoqCameraDirectionBack  = 1,
};
#endif

/**
 * @brief 视频采集配置
 */
struct AoqVideoCaptureConfig {
    /** 采集宽度（像素），isExternal=true 时无效 */
    int width  = 1280;
    /** 采集高度（像素），isExternal=true 时无效 */
    int height = 720;
    /** 采集帧率，isExternal=true 时无效（节奏由送帧决定） */
    int fps    = 15;
    /** 是否外部采集；true 时不打开摄像头，由 pushExternalVideoCapturedFrame 喂帧 */
    bool isExternal = false;
#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined(__OHOS__)
    /** 摄像头方向；isExternal=true 时无效。桌面端无此字段 */
    AoqCameraDirection cameraDirection = AoqCameraDirectionFront;
#endif
};

/**
 * @brief 视频像素格式
 */
enum AoqVideoPixelFormat {
    AoqVideoPixelFormatUnknown       = 0,
    AoqVideoPixelFormatI420          = 1,
    AoqVideoPixelFormatNV12          = 2,
    AoqVideoPixelFormatNV21          = 3,
    AoqVideoPixelFormatBGRA          = 4,
    AoqVideoPixelFormatRGBA          = 5,
    /** 仅 Apple 平台有效；nativePixelBuffer 字段填 CVPixelBufferRef */
    AoqVideoPixelFormatCVPixelBuffer = 6,
    /** Android 外部 OES 纹理；textureId / transformMatrix 字段有效 */
    AoqVideoPixelFormatTextureOES    = 7,
    /** Android 普通 2D 纹理；textureId / transformMatrix 字段有效 */
    AoqVideoPixelFormatTexture2D     = 8,
};

/**
 * @brief 外部视频数据
 */
struct AoqVideoFrame {
    /** 像素格式 */
    AoqVideoPixelFormat format    = AoqVideoPixelFormatUnknown;

    /** 宽度（像素） */
    int width  = 0;
    /** 高度（像素） */
    int height = 0;

    /** 打包格式数据 (NV12/NV21/BGRA/RGBA) */
    void* dataPtr  = 0;
    /** 打包数据字节数 */
    int   dataSize = 0;

    /** I420 三平面 */
    void* dataY   = 0;
    void* dataU   = 0;
    void* dataV   = 0;
    int   strideY = 0;
    int   strideU = 0;
    int   strideV = 0;

    /** Apple 零拷贝；C++ 不解释，平台壳填 CVPixelBufferRef */
    void* nativePixelBuffer = 0;

    /** Android 纹理 ID（format=TextureOES/Texture2D 时有效） */
    int   textureId = 0;
    /** 纹理变换矩阵（format=TextureOES/Texture2D 时有效，行优先 4×4） */
    float transformMatrix[16] = {0};

    /** 时间戳，单位 ms；0 时 SDK 用本地时钟补 */
    int64_t timeStamp = 0;
};

/**
 * @brief 外部编码帧 codec 类型
 */
enum AoqVideoCodecType {
    AoqVideoCodecTypeJPEG = 0,
};

/**
 * @brief 外部已编码视频帧
 * 调用方自行完成编码，SDK 不做二次编码，直接打包发送。
 */
struct AoqVideoEncodedFrame {
    /** 编码格式 */
    AoqVideoCodecType codec = AoqVideoCodecTypeJPEG;
    /** 编码后数据指针 */
    void* data     = nullptr;
    /** 编码后数据字节数 */
    int   dataSize = 0;
    /** 宽度（像素） */
    int   width    = 0;
    /** 高度（像素） */
    int   height   = 0;
    /** 时间戳，单位 ms；0 时 SDK 用本地时钟补 */
    int64_t timeStamp = 0;
};

/**
 * @brief 视频设备状态码
 */
enum AoqVideoDeviceStateCode {
    AoqVideoDeviceNone = 0,
    /* 摄像头正在启动 */
    AoqVideoDeviceCaptureStarting = 1,
    /* 摄像头已启动 */
    AoqVideoDeviceCaptureStarted = 2,
    /* 摄像头正在停止 */
    AoqVideoDeviceCaptureStopping = 3,
    /* 摄像头已停止 */
    AoqVideoDeviceCaptureStopped = 4,
    /* 摄像头启动失败（权限拒绝、设备不可用等） */
    AoqVideoDeviceCaptureFail = 5,
};

/**
 * @brief 视频设备状态
 */
struct AoqVideoDeviceState {
    AoqVideoDeviceStateCode state = AoqVideoDeviceNone;
    /* AoqErrorCode */
    int reason = 0;
};

/**
 * @brief 镜像模式枚举值
 */
enum AoqMirrorMode {
    AoqMirrorModeDisabled = 0,
    AoqMirrorModeEnabled  = 1,
};

/**
 * @brief 方向模式枚举值
 */
enum AoqOrientationMode {
    AoqOrientationModeAuto      = 0,
    AoqOrientationModePortrait  = 1,
    AoqOrientationModeLandscape = 2,
};

/**
 * @brief 视频编解码参数（编解码共用）
 * @note 用于解码时仅 trackType/codecType/width/height/fps/bitrate 生效（作为订阅提议参与协商），
 *       minBitrate/keyframeInterval/mirrorMode/orientationMode/isExternal 仅编码使用
 */
struct AoqVideoCodecConfig {
    /* 外部编码模式：true 时由客户推送已编码帧，SDK 不做采集和编码 */
    bool               isExternal       = false;
    /* 轨道类型，默认视频 */
    AoqTrackType       trackType        = AoqTrackTypeVideo;
    /* 编码类型，默认 H264 */
    AoqEncoderType     codecType        = AoqEncoderTypeVideoH264;
    /* 分辨率宽 */
    int                width            = 540;
    /* 分辨率高 */
    int                height           = 960;
    /* 帧率 */
    int                fps              = 5;
    /* 起始码率 (bps) */
    int                bitrate          = 500000;
    /* 最小码率 (bps) */
    int                minBitrate       = 128000;
    /* 关键帧间隔 (秒) */
    int                keyframeInterval = 2;
    /* 镜像模式 */
    AoqMirrorMode      mirrorMode       = AoqMirrorModeDisabled;
    /* 方向模式 */
    AoqOrientationMode orientationMode  = AoqOrientationModeAuto;
};

/********************************************************* VideoFrameObserver */
/**
 * @brief 视频帧回调数据源（管线位置）
 */
enum AoqVideoSource {
    /** 采集后的视频数据（前处理前） */
    AoqVideoSourceCaptured  = 0,
    /** 编码前的视频数据（前处理后） */
    AoqVideoSourcePreEncode = 1,
    /** 远端解码后、渲染前的视频数据 */
    AoqVideoSourceRemote    = 2,
    /** 占位 */
    AoqVideoSourceMax,
};

/** 视频帧回调模式 */
enum AoqVideoObserverMode {
    AoqVideoObserverModeReadOnly  = 0,
    AoqVideoObserverModeReadWrite = 1,
};

/** 视频输出宽度对齐方式 */
enum AoqVideoObserverAlignment {
    AoqVideoObserverAlignmentDefault = 0,
    AoqVideoObserverAlignmentEven    = 1,
    AoqVideoObserverAlignment4       = 2,
    AoqVideoObserverAlignment8       = 3,
    AoqVideoObserverAlignment16      = 4,
};

/**
 * @brief 视频数据回调参数设置
 */
struct AoqVideoObserverConfig {
    /** 期望回调像素格式，默认 I420 */
    AoqVideoPixelFormat       format        = AoqVideoPixelFormatI420;
    /** 宽度对齐策略 */
    AoqVideoObserverAlignment alignment     = AoqVideoObserverAlignmentDefault;
    /** 回调模式，仅 I420 / CVPixelBuffer 支持 ReadWrite */
    AoqVideoObserverMode      mode          = AoqVideoObserverModeReadOnly;
    /** 是否对回调数据应用镜像 */
    bool                      mirrorApplied = false;
};

/********************************************************************** Video */

/**
 * @brief 引擎回调接口
 * 由调用方继承实现，并在 createEngine 时传入；
 * 所有回调可能在内部线程触发，实现者需自行处理线程安全。
 */
class AOQ_API AoqEngineEventListener {
public:
    AoqEngineEventListener();
    virtual ~AoqEngineEventListener();

    /**
     * @brief 引擎错误回调
     * @param code 错误码 详见 {@link AoqErrorCode}
     * @param message 错误描述
     */
    virtual void onError(int code, const char* message) = 0;
    /**
     * @brief 引擎警告回调
     * @param code 警告码 详见 {@link AoqWarningCode}
     * @param message 警告描述
     */
    virtual void onWarning(int code, const char* message) = 0;

    /**
     * @brief 引擎连接状态变化回调
     * @param status 连接状态 详见 {@link AoqConnectionStatus}
     */
    virtual void onConnectionStatusChange(AoqConnectionStatus status) = 0;

    /**
     * @brief 引擎统计信息回调
     * @param stats 统计数据 详见 {@link AoqStats}
     */
    virtual void onStats(const AoqStats& stats) = 0;

/********************************************************************** Auido */
    /**
     * @brief 音频设备采集播放操作状态变化回调
     * @param state 音频设备状态 详见 {@link AoqAudioDeviceState}
     */
    virtual void onAudioDeviceStateChanged(const AoqAudioDeviceState& state) = 0;

    /**
     * @brief 音频设备路由变化回调
     * @param routeType 路由类型 详见 {@link AoqAudioDeviceRouteType}
     */
    virtual void onAudioDeviceRouteChanged(int routeType) = 0;

#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined (__OHOS__)
    /**
     * @brief 音频设备中断回调（仅移动端有效）
     * @param interrupt true 表示被中断，false 表示中断恢复
     */
    virtual void onAudioDeviceInterrupted(bool interrupt) = 0;
#endif

#if defined(__ANDROID__)
    /**
     * @brief 音频设备焦点变化回调（仅 Android 有效）
     * @param audioFocus 焦点状态 对应 AudioManager.AUDIOFOCUS_*
     */
    virtual void onAudioDeviceFocusChanged(int audioFocus) = 0;
#endif

/****************************************************************** AuidoFile */
    /**
     * @brief 音频文件播放状态回调
     * @param state 文件播放状态 详见 {@link AoqAudioFileState}
     */
    virtual void onAudioFileState(const AoqAudioFileState& state) = 0;

/**************************************************** AudioVolumeIndication */
    /**
     * @brief 本地采集音量提示回调
     * @param volume 本地音量信息 详见 {@link AoqAudioVolume}
     * @note 需调用 {@link AoqClientEngine::enableLocalAudioVolumeIndication} 开启
     */
    virtual void onLocalAudioVolumeIndication(const AoqAudioVolume& volume) = 0;

/********************************************************************** video */
    /**
     * @brief 视频设备采集操作状态变化回调
     * @param state 视频设备状态 详见 {@link AoqVideoDeviceState}
     */
    virtual void onVideoDeviceStateChanged(const AoqVideoDeviceState& state) = 0;

/******************************************************************** DataMsg */
    /**
     * @brief 接收消息回调
     * @param msg 数据消息 详见 {@link AoqDataMsg}
     */
    virtual void onDataMsg(const AoqDataMsg& msg) = 0;
};


/**
 * @brief 音频数据监听接口 请不要在此回调函数中做任何耗时操作，否则可能导致声音异常
 */
class AOQ_API IAudioFrameObserver {
public:
    virtual ~IAudioFrameObserver() {}
    /**
     * @brief 采集裸数据回调
     * @details enableAudioFrameObserver : true audioSource: AoqAudioSourceCaptured 开启
     *  - 该接口支持设置采样率、声道数
     *  - 该接口支持读写模式
     */
    virtual void onCapturedAudioFrame(const AoqAudioFrameData& data) = 0;
    
    /**
     * @brief 3A后数据回调
     * @details enableAudioFrameObserver : true audioSource: AoqAudioSourceProcessCaptured 开启
     *  - 该接口支持设置采样率、声道数
     *  - 该接口支持读写模式
    */
    virtual void onProcessCapturedAudioFrame(const AoqAudioFrameData& data) = 0;

    /**
     * @brief 推流数据回调
     * @details enableAudioFrameObserver : true audioSource: AoqAudioSourcePublish 开启
     *  - 该接口支持设置采样率、声道数
     *  - 该接口只支持只读模式
     */
    virtual void onPublishAudioFrame(AoqTrackType trackType, const AoqAudioFrameData& data) = 0;

    /**
     * @brief 播放数据回调
     * @details enableAudioFrameObserver : true audioSource: AoqAudioSourcePlayback 开启
     *  - 该接口支持设置采样率、声道数
     *  - 该接口支持读写模式
     */
    virtual void onPlaybackAudioFrame(const AoqAudioFrameData& data) = 0;

};


/**
 * @brief 视频数据监听接口 请不要在此回调函数中做任何耗时操作，否则可能导致画面卡顿
 */
class AOQ_API IVideoFrameObserver {
public:
    virtual ~IVideoFrameObserver() {}

    /**
     * @brief 本地采集后裸数据回调（前处理前）
     * @details enableVideoFrameObserver: true videoSource: AoqVideoSourceCaptured 开启
     * @return
     *  - true : 数据已修改、需写回 SDK（仅 I420 / CVPixelBuffer 生效）
     *  - false: 只读
     */
    virtual bool onCapturedVideoFrame(AoqVideoFrame& frame) = 0;

    /**
     * @brief 本地编码前裸数据回调（前处理后）
     * @details enableVideoFrameObserver: true videoSource: AoqVideoSourcePreEncode 开启
     */
    virtual bool onPreEncodeVideoFrame(AoqTrackType trackType, AoqVideoFrame& frame) = 0;

    /**
     * @brief 远端解码后、渲染前裸数据回调
     * @details enableVideoFrameObserver: true videoSource: AoqVideoSourceRemote 开启
     */
    virtual bool onRemoteVideoFrame(AoqTrackType trackType, AoqVideoFrame& frame) = 0;
};


/**
 * @brief 引擎对外接口 (基类)
 * 全局单例。createEngine 重复调用返回同一实例，
 * destroy 后需重新 createEngine 方可继续使用。
 */
class AOQ_API AoqClientEngine {
public:
    /**
     * @brief 创建引擎实例（单例）
     * @param config 引擎创建配置 详见 {@link AoqCreateConfig}
     * @param listener 事件回调监听器 详见 {@link AoqEngineEventListener}
     * @return 引擎实例指针，失败返回 nullptr
     */
    static AoqClientEngine* createEngine(const AoqCreateConfig& config,
                                         AoqEngineEventListener*     listener);
    /**
     * @brief 销毁引擎实例
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    static int destroy();
    /**
     * @brief 获取 SDK 的版本号
     * @return 版本号字符串
     */
    static const char* getVersion();

    AoqClientEngine();
    virtual ~AoqClientEngine();

    /**
     * @brief 连接服务器
     * @param config 连接鉴权配置 详见 {@link AoqConnectConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int connect(const AoqConnectConfig& config);
    /**
     * @brief 断开服务器
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int disconnect();

/********************************************************************** Auido */

    /**
     * @brief 打开音频采集设备
     * @param config 音频采集配置 详见 {@link AoqAudioCaptureConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int startAudioCapture(const AoqAudioCaptureConfig& config);

    /**
     * @brief 关闭音频采集设备
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int stopAudioCapture();

    /**
     * @brief 静音音频采集
     * @param mute true表示静音，false表示取消静音
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int muteAudioCapture(bool mute);

    /**
     * @brief 开始音频渲染
     * @param config 音频播放配置 详见 {@link AoqAudioPlaybackConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int startAudioPlayer(const AoqAudioPlaybackConfig& config);

    /**
     * @brief 停止音频渲染
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int stopAudioPlayer();

    /**
     * @brief 暂停音频渲染
     * @param fadeMs 淡出时长，单位毫秒
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int pauseAudioPlayer(int fadeMs);

    /**
     * @brief 恢复音频渲染
     * @param fadeMs 淡入时长，单位毫秒
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int resumeAudioPlayer(int fadeMs);

    /**
     * @brief 打断本轮音频播放
     * @param trackType 音轨类型 详见 {@link AoqTrackType}
     * @param fadeMs 淡出时长，单位毫秒
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int interruptAudioPlayer(AoqTrackType trackType, int fadeMs);

#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined (__OHOS__)
    /**
     * @brief 扬声器和听筒切换
     * @param enable true切换到扬声器，false切换到听筒，只有在Voip模型下可切换
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int enableSpeakerphone(bool enable);

    /**
     * @brief 查询当前是否为扬声器模式
     * @return true表示扬声器模式，false表示听筒模式
     */
    virtual bool isSpeakerphoneEnabled();
#endif

#if (defined(__APPLE__) && TARGET_OS_IOS)
    /**
     * @brief 设置AVAudioSession的控制权限
     * @param restriction 权限类型 详见 {@link AoqAudioSessionRestriction}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setAudioSessionRestriction(int restriction);
#endif

    /**
     * @brief 设置音频编码参数
     * @param config 音频编解码配置 详见 {@link AoqAudioCodecConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setAudioEncoderConfig(const AoqAudioCodecConfig& config);

    /**
     * @brief 设置音频解码参数
     * @param config 音频编解码配置 详见 {@link AoqAudioCodecConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setAudioDecoderConfig(const AoqAudioCodecConfig& config);

/****************************************************************** AuidoFile */

    /**
     * @brief 开始推流播放音频文件
     * @param fileId 音频文件Id，业务层需保证唯一性
     * @param config 音频文件混音配置 详见 {@link AoqAudioFileMixConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int startAudioFile(const char* fileId, const AoqAudioFileMixConfig& config);

    /**
     * @brief 停止音频文件
     * @param fileId 音频文件Id
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int stopAudioFile(const char* fileId);

    /**
     * @brief 暂停音频文件
     * @param fileId 音频文件Id
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int pauseAudioFile(const char* fileId);

    /**
     * @brief 恢复音频文件
     * @param fileId 音频文件Id
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int resumeAudioFile(const char* fileId);

    /**
     * @brief 获取音频文件时长
     * @param fileId 音频文件Id
     * @return
     * - >=0 音频文件时长，单位毫秒
     * - <0: 失败 -AoqErrorCode
     */
    virtual int64_t getAudioFileDuration(const char* fileId);

    /**
     * @brief 获取音频文件当前播放位置
     * @param fileId 音频文件Id
     * @return
     * - >=0 当前播放位置，单位毫秒
     * - <0: 失败 -AoqErrorCode
     */
    virtual int64_t getAudioFileCurrentPosition(const char* fileId);

    /**
     * @brief 设置音频文件播放位置
     * @param fileId 音频文件Id
     * @param positionMillis 播放位置，单位毫秒
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setAudioFilePositionMillis(const char* fileId, int64_t positionMillis);

    /**
     * @brief 设置音频文件音量
     * @param fileId 音频文件Id
     * @param type 音频流方向 详见 {@link AoqAudioStreamDirection}
     * @param volume 音量值
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setAudioFileVolume(const char* fileId, AoqAudioStreamDirection type, int volume);

    /**
     * @brief 获取音频文件音量
     * @param fileId 音频文件Id
     * @param type 音频流方向 详见 {@link AoqAudioStreamDirection}
     * @return
     * - [0, 100]: 音量值
     * - <0: 失败 -AoqErrorCode
     */
    virtual int getAudioFileVolume(const char* fileId, AoqAudioStreamDirection type);

/************************************************************** AuidoExteranl */

    /**
     * @brief 新增外部音频流
     * @param streamId 外部音频流Id，业务层需保证唯一性
     * @param config 外部音频流配置 详见 {@link AoqAudioExternalStreamConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int addAudioExternalStream(const char* streamId, const AoqAudioExternalStreamConfig& config);
    
    /**
     * @brief 输入外部音频数据
     * @param streamId 外部音频流Id
     * @param data 外部音频裸数据 详见 {@link AoqAudioFrameData}
     * @return
     * - 0: 成功;
     * - 非0: 失败;
     * - 返回值为 {@link AoqECAudioExternalBufferFull} 时表示SDK缓存满
     * @note
     * 如果返回{@link AoqECAudioExternalBufferFull}时，说明内部缓冲区已满，建议等待20ms后再重新送当前数据帧
     * 最佳实践 实时采集，一次Push 10ms数据长度，有数据就Push；来源自文件，一次Push 40ms数据长度，间隔30ms给一次，处理AoqECAudioExternalBufferFull返回
     */
    virtual int pushAudioExternalStreamData(const char* streamId, AoqAudioFrameData& data);

    /**
     * @brief 设置外部音频流推流音量
     * @param streamId 外部音频流Id
     * @param type 外部音频流类型
     * @param vol 音量 0-100
     * @return
     * - 0: 成功
     * - 非0: 失败
     */
    virtual int setAudioExternalStreamVolume(const char* streamId, AoqAudioStreamDirection type, int vol);
    
    /**
     * @brief 获取外部音频流推流音量
     * @param streamId 外部音频流Id
     * @param type 外部音频流类型
     * @return
     * - [0, 100]: 推流音量
     * - <0: 失败 -AoqErrorCode
     */
    virtual int getAudioExternalStreamVolume(const char* streamId, AoqAudioStreamDirection type);

    /**
     * @brief 清空外部音频流缓存
     * @param streamId 外部音频流Id
     * @param fadeoutMs 淡出时长，-1 使用SDK内部默认淡出，0 表示全部清空无淡出，>0 保留ms淡出
     */
    virtual void clearAudioExternalStreamBuffer(const char* streamId, int fadeoutMs);

    /**
     * @brief 移除外部音频流
     * @param streamId 外部音频流Id
     * @return
     * - 0: 成功
     * - 非0: 失败
     */
    virtual int removeAudioExternalStream(const char* streamId);


/********************************************************* AuidoFrameObserver */
    /**
     * @brief 注册音频数据回调
     * @param observer nullptr 停止回调数据 {@link IAudioFrameObserver}
     * @note 调用 enableAudioFrameObserver 开启 AoqAudioSource 回调
     * @return
     * - 0: 成功
     * - 非0: 失败
     */
    virtual int setAudioFrameObserver(IAudioFrameObserver* observer) = 0;

    /**
     * @brief 订阅音频数据
     * @param enabled 是否允许数据回调
     * @param audioSource 音频裸数据源类型，详见 {@link AoqAudioSource}
     * @param config 回调参数设置，详见{@link AoqAudioObserverConfig}
     * @return
     * - 0: 成功
     * - 非0: 失败
     */
    virtual int enableAudioFrameObserver(bool enabled, AoqAudioSource audioSource, const AoqAudioObserverConfig& config) = 0;

/**************************************************** AudioVolumeIndication */
    /**
     * @brief 开启/关闭本地采集音量提示
     * @param config 音量提示配置，详见 {@link AoqAudioVolumeIndicationConfig}
     * @note 开启后按 config.interval 周期触发 {@link AoqEngineEventListener::onLocalAudioVolumeIndication}；
     *       config.interval <= 0 时关闭回调。需在 startAudioCapture 之后调用才有音量数据。
     * @return
     * - 0: 成功
     * - 非0: 失败
     */
    virtual int enableLocalAudioVolumeIndication(const AoqAudioVolumeIndicationConfig& config);

/********************************************************************** Auido */

/********************************************************************** Video */

    /**
     * @brief 打开视频采集设备
     * @param config 视频采集配置，详见 {@link AoqVideoCaptureConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int startVideoCapture(const AoqVideoCaptureConfig& config);
    /**
     * @brief 关闭视频采集设备
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int stopVideoCapture();
#if defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS) || defined (__OHOS__)
    /**
     * @brief 切换前后置摄像头（仅移动端有效）
     * @param direction 摄像头方向，详见 #AoqCameraDirection
     * @return
     * - 0: 调用已下发（异步执行）
     * - <0: 失败
     */
    virtual int switchCamera(AoqCameraDirection direction);
#endif
    /**
     * @brief 设置本地渲染窗口
     * @param trackType 视频轨道，详见 {@link AoqTrackType}
     * @param canvas 渲染画布；canvas.view==nullptr 表示解绑
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setLocalView(AoqTrackType trackType, const AoqVideoCanvas& canvas);
    /**
     * @brief 设置远端渲染窗口
     * @param trackType 视频轨道，详见 {@link AoqTrackType}
     * @param canvas 渲染画布；canvas.view==nullptr 表示解绑
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setRemoteView(AoqTrackType trackType, const AoqVideoCanvas& canvas);
    /**
     * @brief 设置视频编码参数
     * @param config 编码配置，按 config.trackType 路由到 Video，详见 {@link AoqVideoCodecConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setVideoEncoderConfig(const AoqVideoCodecConfig& config);
    /**
     * @brief 设置视频解码参数（订阅侧 codec 提议，需在 connect 之前调用）
     * @param config 解码配置，按 config.trackType 路由到 Video，详见 {@link AoqVideoCodecConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int setVideoDecoderConfig(const AoqVideoCodecConfig& config);

    /**
     * @brief 推送外部视频帧
     * @param trackType 路由目标：AoqTrackTypeVideo
     * 未启动外部视频采集或格式不支持时返回 AoqECParamInvalid
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int pushExternalVideoCapturedFrame(AoqTrackType trackType, const AoqVideoFrame& frame);

    /**
     * @brief 推送外部已编码视频帧（bypass 编码器直推）
     * @param trackType 路由目标：AoqTrackTypeVideo
     * @param frame 已编码帧数据（当前仅支持 JPEG）
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int pushExternalVideoEncodedFrame(AoqTrackType trackType, const AoqVideoEncodedFrame& frame);

    /**
     * @brief 控制本地媒体流的发送
     * @param trackType 路由目标：AoqTrackTypeAudio / AoqTrackTypeVideo
     * @param enable true 启用发送，false 停用发送
     * @return
     * - =0: 调用已下发（异步执行）
     * - 非0: 失败
     */
    virtual int enableSendMediaStream(AoqTrackType trackType, bool enable);

/********************************************************* VideoFrameObserver */
    /**
     * @brief 注册视频数据回调
     * @param observer 设为 nullptr 时停止回调（{@link IVideoFrameObserver}）
     * @note 调用 enableVideoFrameObserver 开启 AoqVideoSource 回调
     * @return
     *  - 0: 成功
     *  - 非0: 失败
     */
    virtual int setVideoFrameObserver(IVideoFrameObserver* observer) = 0;

    /**
     * @brief 订阅视频数据
     * @param enabled 是否启用某位置数据回调
     * @param videoSource 视频帧回调数据源（管线位置），详见 {@link AoqVideoSource}
     * @param config 回调参数设置，详见 {@link AoqVideoObserverConfig}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int enableVideoFrameObserver(bool enabled, AoqVideoSource videoSource, const AoqVideoObserverConfig& config) = 0;

/**************************************************************** DataMessage */

    /**
     * @brief 发送消息
     * @param msg 消息内容，详见 {@link AoqDataMsg}
     * @return
     * - =0: 成功
     * - 非0: 失败
     */
    virtual int sendDataMsg(const AoqDataMsg& msg);

/**************************************************************** DataMessage */
};

} // namespace AoqClientSdk

#endif // AoqClientEngine_h
