package cn.tee3.avd;
import android.content.Context;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import cn.tee3.avd.Device.DeviceStatus;

/**
 * 房间中音频模块接口.
 *
 * 房间音频模块定义房间音频相关如麦克风、扬声器相关操作功能接口。
 */
public class MAudio extends Module {
    private static final String TAG = "MAudio";

    /**
     * 语音激励信息结构.
     *
     * 语音激励信息信息定义了当前房间中本地语音输入和输出能量，以及远端用户的语音能量信息。
     * 语音能量幅度：0-9，其中0为无能量，9为最大能量
     */
    public static class AudioInfo {
        private int input_level;					/**< 本地语音输入能量 */
        private int output_level;				    /**< 本地语音输出能量 */
        private Map<String, Integer> active_streams;/**< 远端用户的语音能量对应表，只包含能量大于0的用户 */

        @SuppressWarnings("unchecked")
        protected AudioInfo(HashMap streams, int il, int ol) {
            if (null == streams) {
                this.active_streams =  new HashMap();
            }
            else {
                this.active_streams = streams;
            }
            this.input_level = il;
            this.output_level = ol;
        }
        @SuppressWarnings("unchecked")
        protected AudioInfo() {
            this.input_level = 0;
            this.output_level = 0;
            this.active_streams = new HashMap();
        }

        public int getInputLevel() { return this.input_level; }
        public int getOutputLevel() { return this.output_level; }
        public Map<String, Integer> getActiveStreams() { return this.active_streams; }

        @Override
        public String toString() {
            return "AudioInfo: input_level=" + this.input_level + ",output_level="+this.output_level;
        }
    }

    /**
     * 房间中音频模块回调接口.
     *
     * 音频模块回调接口定义了房间中音频相关如麦克风、扬声器的通知和异步操作返回。
     */
    public static interface Listener {
        /// 通知
        /** 麦克风状态更改通知
         *
         * @param[in] status 麦克风状态。
         * @param[in] fromUserId 设备关联用户Id。
         * @note 当麦克风状态更改后，房间内所有用户接收到此通知。
         * @sa openMicrophone
         * @sa closeMicrophone
         */
        public void onMicrophoneStatusNotify(DeviceStatus status, String fromUserId);
        /** 语音激励通知
         *
         * @param[in] info 语音激励信息。
         * @note 语音激励通知，只有启用语音激励后才会有语音激励通知。启用语音激励接口为：monitorAudioLevel。
         * @sa monitorAudioLevel
         */
        public void onAudioLevelMonitorNotify(AudioInfo info);

        /// 异步返回
        /** 本用户打开麦克风异步返回
         *
         * @param[in] result 错误代码。
         * @sa openMicrophone
         */
        public void onOpenMicrophoneResult(int result);
        /** 本用户关闭麦克风异步返回
         *
         * @param[in] result 错误代码。
         * @sa closeMicrophone
         */
        public void onCloseMicrophoneResult(int result);

        /**
         * 音频数据回调接口
         *
         * @param[in] timestamp 时间戳, mi second。
         * @param[in] sampleRate 采样率。
         * @param[in] channels 通道数。
         * @param[in] buf 数据存储指针。
         * @param[in] len 数据长度。
         */
        public void onAudioData(String userId, long timestamp, int sampleRate, int channels, byte[] data, int len);

        /**
         * TTS媒体播放/停止回调通知
         * @param roomId 房间号
         * @param playingMediaId 媒体播放器id
         * @param userId 用户id
         * @param playEvent 0：播放； 1：停止
         */
        public void onMediaPlayNotify(String roomId, String playingMediaId, String userId, int playEvent);
    }

    public static interface SubscribeListener {
        /// 异步返回
        /** 本用户订阅音频异步返回
         *
         * @param[in] result 错误代码。
         * @param[in] fromId 用户Id，唯一标示一个用户。
         *
         * @sa subscribe
         */
        public void onSubscribeResult(int result, String fromId);
        /** 本用户取消订阅音频异步返回
         *
         * @param[in] result 错误代码。
         * @param[in] fromId 用户Id，唯一标示一个用户。
         *
         * @sa unsubscribe
         */
        public void onUnsubscribeResult(int result, String fromId);
    }
    ///
    private long nativeListener = 0;
    @Nullable private Listener listener4cb = null;
    @Nullable private Handler listenerHandler = null;
    @Nullable private SDKListener listener4native = null;
    @Nullable private MixerDataListener listener4datacb = null;
    ///
    /** 获取音频模块接口
     *
     * @param[in] room 房间对象。
     *
     * @return 返回音频模块接口。
     */
    @Nullable public static MAudio getAudio(Room room) {
        return (MAudio)room.getModule(Type.audio);
    }

    protected MAudio(Room room) {
        super(room, Type.audio, room.nativegetMAudio());
        Tlog.d(TAG, "MAudio, nativeAudio:" + this.nativeobj);
        initNativeListener();
        AudioRoute.instance().init();
    }
    @Override
    protected void dispose() {
        Tlog.d(TAG, "dispose, out:");
        AudioRoute.instance().uninit();
        if (0 != this.nativeListener) {
            nativeFreeListener(this.nativeListener);
        }
        this.nativeListener = 0;
        this.nativeobj = 0;
        Tlog.v(TAG, "dispose, out:");
    }
    /** 设置音频模块回调接口
     *
     * @param[in] listener 音频模块回调接口指针。
     * @return 返回错误代码。
     * @sa IListener
     */
    public boolean setListener(Listener listener) {
        Tlog.d(TAG, "setListener, listener:" + listener + ",listener4native=" + this.listener4native + ",listenerHandler=" + this.listenerHandler);
        this.listener4cb = listener;
        if (null != listener) {
            initNativeListener();
        }
        Tlog.v(TAG, "setListener, out:");
        return true;
    }

    public void initNativeListener(){
        if (0 != this.nativeListener) {
            return;
        }
        initHandler();
        this.nativeListener = nativeCreateListener(listener4native);
        Tlog.d(TAG, "initNativeListener, Create nativelistener:" + nativeListener);
    }

    @Nullable private SubscribeListener listener4subscribe = null;
    public boolean setSubscribeListener(SubscribeListener listener) {
        Tlog.d(TAG, "setSubscribeListener, listener:" + listener );
        this.listener4subscribe = listener;
        return true;
    }
    /** 打开本地麦克风发布到房间中
     *
     * @return 返回错误代码。
     * @note 为异步操作，操作结果在 onOpenMicrophoneResult 中返回。
     * @sa onOpenMicrophoneResult
     */
    public int  openMicrophone () {
        Tlog.d(TAG, "openMicrophone,");
        int ret = nativeopenMicrophone();
        Tlog.v(TAG, "openMicrophone, out:");
        return ret;
    }
    /** 关闭本地麦克风发布到房间中
     *
     * @return 返回错误代码。
     * @note 为异步操作，操作结果在 onCloseMicrophoneResult 中返回。
     * @sa onCloseMicrophoneResult
     */
    public int  closeMicrophone() {
        Tlog.d(TAG, "closeMicrophone,");
        int ret = nativecloseMicrophone();
        Tlog.v(TAG, "closeMicrophone, out:");
        return ret;
    }
    /** 获取本地麦克风状态
     * @return 返回麦克风状态。
     */
    public DeviceStatus getMicrophoneStatus() {
        return nativegetMicrophoneStatus();
    }

    /** 获取本地麦克风状态
     * @return 返回麦克风状态。
     */
    public int getMicrophoneStatus2() {
        return nativegetMicrophoneStatus2();
    }
    /** 判断本地麦克风是否已经打开
     * @return 返回麦克风是否打开。
     */
    public boolean isOpenMicrophone() {
        return getMicrophoneStatus2() == DeviceStatus.published.ordinal();
    }

    public static void enableAutoRoute(boolean enable) {
        AudioRoute.instance().enable(enable);
        Tlog.i(TAG, "enableAutoRoute " + enable);
    }
    /** 自动路由音频设备（麦克风和扬声器）
     * @return 返回是否自动路由
     * @note 麦克风设备有：BUILTIN_MIC 手机上的话筒;  WIRED_HEADSET 有线耳机上的话筒;  BLUETOOTH_SCO_HEADSET 蓝牙耳机上的话筒
     *        扬声器设备有：EARPIECE 听筒； SPEAKER 手机外放扬声器； (移动端内置)
     *                      WIRED_HEADSET 带话筒的耳机； WIRED_HEADPHONE 不带话筒的耳机；
     *                      BLUETOOTH_SCO 蓝牙.面向连接（SCO）方式：主要用于话音传输； BLUETOOTH_SCO_HEADSET（HFP）蓝牙耳机,带话筒；
     *                          BLUETOOTH_SCO_CARKIT 蓝牙车载设备； BLUETOOTH_A2DP 蓝牙立体声;  BLUETOOTH_A2DP_HEADPHONES 蓝牙立体声音耳机;  BLUETOOTH_A2DP_SPEAKER 带话筒的;
     *      自动路由音频策略：若有蓝牙设备，优先输出到蓝牙设备，优先采用蓝牙设备输入；
     *                        若有有线耳麦，优先输出到有线耳机，优先采用有线麦克风输入；
     *                        最后选择手机上设备，优先输出到手机外放；若有距离感应器，根据感应器来输出到外放和听筒转换；输入与输出适应。
     */
    public boolean isAutoRoute() {
        return AudioRoute.instance().isAutoRoute();
    }
    public void setAutoRoute(boolean isOn) {
        AudioRoute.instance().setAutoRoute(isOn);
    }
    /** 设置手机内置 听筒和扬声器切换
     * @note 默认采用手机内置扬声器做声音输出
     */
    public boolean isHandFree() {
        return AudioRoute.instance().isHandFree();
    }
    public void setHandFree(boolean isOn) {
        AudioRoute.instance().setHandFree(isOn);
    }

    // no input audio, anyone cann't listen my speak; like pause mic
    /** 本地麦克风静默
     *
     * @return 返回错误代码。
     * @note 麦克风静默表示本地没有声音发布到房间中，房间中用户不会听到本地的任何声音。
     */
    public int muteMicrophone() {
        Tlog.d(TAG, "muteMicrophone,");
        return nativemuteMicrophone();
    }
    /** 本地麦克风取消静默
     * @return 返回错误代码。
     */
    public int unmuteMicrophone() {
        Tlog.d(TAG, "unmuteMicrophone,");
        return nativeunmuteMicrophone();
    }
    /** 判断本地麦克风是否静默
     */
    public boolean ismuteMicrophone() {
        return nativeismuteMicrophone();
    }
    // no output audio, cann't listen anyone sound
    /** 本地扬声器静默
     *
     * @return 返回错误代码。
     * @note 扬声器静默表示房间中的声音不会在本地扬声器播放，本用户不会听到任何房间中的声音。
     */
    public int muteSpeaker() {
        Tlog.d(TAG, "muteSpeaker,");
        return nativemuteSpeaker();
    }
    /** 本地扬声器取消静默
     * @return 返回错误代码。
     */
    public int unmuteSpeaker() {
        Tlog.d(TAG, "unmuteSpeaker,");
        return nativeunmuteSpeaker();
    }
    /** 判断本地扬声器是否静默
     */
    public boolean ismuteSpeaker() {
        return nativeismuteSpeaker();
    }

    /**
     * 静默指定用户的音频，该用户音频将不会播放出来
     * @param userId 用户id
     * @return
     */
    public int muteAudio(String userId) {
        Tlog.d(TAG, "muteAudio: " + userId);
        return nativemuteAudio(userId);
    }

    /**
     * 取消静默指定用户的音频，该用户音频将会播放出来
     * @param userId 用户id
     * @return
     */
    public int unMuteAudio(String userId) {
        Tlog.d(TAG, "unMuteAudio: " + userId);
        return nativeunmuteAudio(userId);
    }

    /**
     * 用户音频是否已静默
     * @param userId 用户id
     * @return
     */
    public boolean isMutedAudio(String userId) {
        return nativeismuteAudio(userId);
    }

    /// remote microphone control
    /** 获取远端麦克风状态
     * @param[in] userId 关联用户Id。
     * @return 返回麦克风状态。
     */
    public DeviceStatus getRemoteMicrophoneStatus(String userId) {
        return nativegetRemoteMicrophoneStatus(userId);
    }
    /** 命令远端用户打开麦克风
     * @param[in] userId 关联用户Id。
     * @return 返回错误代码。
     */
    public int  remotecmdOpenMicrophone ( String userId) {
        Tlog.d(TAG, "remotecmdOpenMicrophone,userId:"+userId);
        return nativeremotecmdOpenMicrophone(userId);
    }
    /** 命令远端用户关闭麦克风
     * @param[in] userId 关联用户Id。
     * @return 返回错误代码。
     */
    public int  remotecmdCloseMicrophone( String userId) {
        Tlog.d(TAG, "remotecmdCloseMicrophone,userId:"+userId);
        return nativeremotecmdCloseMicrophone(userId);
    }

    /** 命令远端用户取消静默麦克风
     * @param[in] userId 关联用户Id。
     * @return 返回错误代码。
     */
    public int  remotecmdUnmuteMicrophone ( String userId) {
        Tlog.d(TAG, "remotecmdOpenMicrophone,userId:"+userId);
        return nativeremotecmdUnmuteMicrophone(userId);
    }

    /** 命令远端用户静默麦克风
     * @param[in] userId 关联用户Id。
     * @return 返回错误代码。
     */
    public int  remotecmdMuteMicrophone( String userId) {
        Tlog.d(TAG, "remotecmdCloseMicrophone,userId:"+userId);
        return nativeremotecmdMuteMicrophone(userId);
    }

    /** 开启语音激励功能
     * @return 返回错误代码。
     */
    public int monitorAudioLevel() {
        return nativemonitorAudioLevel();
    }
    /** 关闭语音激励
     * @return 返回错误代码。
     */
    public int unmonitorAudioLevel() {
        return nativeunmonitorAudioLevel();
    }
    /** 判断是否开启语音激励
     */
    public boolean ismonitorAudioLevel() {
        return nativeismonitorAudioLevel();
    }

    ///
    /**
     * 房间中音频混音数据回调接口.
     */
    public static interface MixerDataListener {
        /** 音频格式回调接口
         * @param[in] sampleRate 采样率。
         * @param[in] channels 通道数。
         */
        public void onAudioParam(int sampleRate, int channels);
        /** 音频数据回调接口
         * @param[in] timestamp_ns 时间戳, nanos second。
         * @param[in] buf 数据存储指针。
         * @param[in] len 数据长度。
         */
        public void onAudioData(long timestamp_ns, byte[] buf, int len);
    }

    /** 设置获取音频数据回调接口
     *
     * @return 返回错误代码。
     * @param[in] cb 混音数据回调指针。
     * @param[in] sampleRate 导出混音数据的采样率：16000,44100,48000。
     * @param[in] channels 导出混音数据的通道数：1，2。
     * @sa MixerDataListener
     * @sa Room.Option.ro_audio_mixerdata_callback_buffered 单次混音数据回调“10ms数据”还是“1024samples数据”
     * @note 如果音视频同时混合出流，建议先设置视频，再设置音频的回调接口，有助于音视频时间戳对齐。
     */
    public int setMixerDataListener(MixerDataListener cb)  {
        Tlog.i(TAG, "setMixerDataListener, cb:" + cb);
        this.listener4datacb = cb;
        return nativesetMixerDataListener((null == cb) ? 0 : this.nativeListener);
    }
    public int setMixerDataListener(MixerDataListener cb, int sampleRate, int channels)  {
        Tlog.i(TAG, "setMixerDataListener, cb:" + cb +",sampleRate:" + sampleRate +",channels:" + channels);
        this.listener4datacb = cb;
        return nativesetMixerDataListener2((null == cb) ? 0 : this.nativeListener, sampleRate, channels);
    }

    public int getSpeakerVolume() {
        return AudioRoute.instance().getSpeakerVolume();
    }
    public void setSpeakerVolume(int value) {
        AudioRoute.instance().setSpeakerVolume(value);
    }

    /// subscribe
    /** 订阅一个用户的音频
     *
     * @param[in] userId 用户Id。
     * @return 返回错误代码。
     * @note 订阅操作为异步操作，操作结果在 onSubscribeResult 中返回。房间中发布的音频缺省已经订阅，所以一般不需要调用
     * 此函数，只有调用了unsubscribe后，若又需要此音频时才调用此函数。
     * @sa onSubscribeResult
     */
    public int subscribe(String userId) {
        Tlog.i(TAG, "subscribe, userId:" + userId);
        return nativesubscribe(userId);
    }
    /** 取消订阅一个用户的音频
     *
     * @param[in] userId 用户Id。
     * @return 返回错误代码。
     * @note 取消订阅操作为异步操作，操作结果在 onUnsubscribeResult 中返回。房间中发布的音频缺省已经订阅，如果不想听某
     * 用户的声音，可以调用此函数来取消订阅。
     * @sa onUnsubscribeResult
     */
    public int unsubscribe(String userId) {
        Tlog.i(TAG, "unsubscribe, userId:" + userId);
        return nativeunsubscribe(userId);
    }

    /** 获取已经publish的音频用户
     *
     * @return items 已经pub的用户列表。
     */
    public List<String> getPubAudioUsers() {
        return nativegetPubAudioUsers();
    }
    /** 判断用户音频是否已经发布
     * @param[in] userId 摄像头Id，唯一标示用户ID。
     * @return 返回是否是已发布的用户。
     */
    public boolean isAudioPublished(String userId) {
        return nativeisAudioPublished(userId);
    }
    /** 获取已经Subscrib的音频用户
     *
     * @return items 已经sub的用户列表。
     */
    public List<String> getSubAudioUsers() {
        return nativegetSubAudioUsers();
    }
    /** 判断用户音频是否已经订阅
     * @param[in] userId 摄像头Id，唯一标示用户ID。
     * @return 返回是否是已订阅的用户。
     */
    public boolean isAudioSubscribed(String userId) {
        return nativeisAudioSubscribed(userId);
    }

    //UsbManager
    @Nullable static UsbManager mUsbManager = null;
    @Nullable static Map<String, UsbDevice> mUsbDevices = null;

    /**
     * 获取USB中的音频设备，找了几个全向麦，发现有些并未安装规范定义class的值，目前是取列表的时候排除一些非audio设备
     *
     * @return
     */
    @Nullable public static Map<String, UsbDevice> getAudioUsbDeviceList(Context context) {
        if (mUsbManager == null) {
            mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        }
        if (mUsbDevices == null) {
            mUsbDevices = mUsbManager.getDeviceList();
        }

        if (mUsbDevices != null && mUsbDevices.size() > 0) {
            Set<Map.Entry<String, UsbDevice>> mapSet = mUsbDevices.entrySet();
            Iterator<Map.Entry<String, UsbDevice>> iterable = mapSet.iterator();
            while (iterable.hasNext()) {
                Map.Entry<String, UsbDevice> entry = iterable.next();
                UsbDevice usbDevice = entry.getValue();
                int classId = usbDevice.getDeviceClass();
                if (classId == UsbConstants.USB_CLASS_HID || classId == UsbConstants.USB_CLASS_STILL_IMAGE ||
                        classId == UsbConstants.USB_CLASS_PRINTER || classId == UsbConstants.USB_CLASS_MASS_STORAGE ||
                        classId == UsbConstants.USB_CLASS_VIDEO || classId == UsbConstants.USB_CLASS_MISC) {
                    iterable.remove();
                }
            }
        }

        // TODO: 2019/1/23 ava.util.ConcurrentModificationException
//        if (mUsbDevices != null && mUsbDevices.size() > 0) {
//            for (Map.Entry<String, UsbDevice> entry : mUsbDevices.entrySet()) {
//                UsbDevice usbDevice = entry.getValue();
//                int classId = usbDevice.getDeviceClass();
//                if (classId == UsbConstants.USB_CLASS_HID || classId == UsbConstants.USB_CLASS_STILL_IMAGE ||
//                        classId == UsbConstants.USB_CLASS_PRINTER || classId == UsbConstants.USB_CLASS_MASS_STORAGE ||
//                        classId == UsbConstants.USB_CLASS_VIDEO || classId == UsbConstants.USB_CLASS_MISC) {
//                    mUsbDevices.remove(entry.getKey());
//                }
//            }
//        }
        return mUsbDevices;
    }

    public static boolean isOpenUsb = false;

    /**
     * 打开USB音频设备
     *
     * @param device
     */
    public static void openAudioUsbDevice(UsbDevice device) {
        try {
            isOpenUsb = false;
            if (device != null && mUsbManager != null) {
                mUsbManager.openDevice(device);
                isOpenUsb = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /// android上此接口无效
//    /** 获取本机麦克风设备信息列表
//     * @return 麦克风设备集合存放列表。
//     */
//    public List<Device> getLocalMicrophones() {
//        return nativegetLocalMicrophones();
//    }
//    /** 获取当前默认使用的本机麦克风设备信息
//     * @return 返回麦克风设备信息。
//     */
//    public Device getRecordingMicrophone()  {
//        return nativegetRecordingMicrophone();
//    }
//    /** 设置当前默认使用的本机麦克风设备
//     * @param[in] id 设备Id。
//     * @return 返回错误代码。
//     */
//    public int setRecordingMicrophone(String deviceId) {
//        return nativesetRecordingMicrophone(deviceId);
//    }
//    /** 获取本机扬声器设备信息列表
//     * @return items 扬声器设备集合存放列表。
//     */
//    public List<Device> getLocalSpeakers() {
//        return nativegetLocalSpeakers();
//    }
//    /** 获取当前默认使用的本机扬声器设备信息
//     * @return 返回扬声器设备信息。
//     */
//    public Device getPlayoutSpeaker() {
//        return nativegetPlayoutSpeaker();
//    }
//    /** 设置当前默认使用的本机扬声器设备
//     * @param[in] id 设备Id。
//     * @return 返回错误代码。
//     */
//    public int setPlayoutSpeaker(String deviceId) {
//        return nativesetPlayoutSpeaker(deviceId);
//    }

    /**
     * 注册某一路音频数据回调接口
     *
     * @return 返回错误代码。
     * @param[in] userId 音频对应的用户ID。
     * @param[in] listener 混音数据回调指针。
     * @param[in] freq 采样频率 8000，16000 ，44100，48000
     * @param[in] channels 通道数 1，2
     * @sa IAudioDataListener
     */
    public int registerPCMDataListener(String userId, int freq, int channels) {
        return nativeregisterPCMDataListener(userId, nativeListener, freq, channels);
    }

    /**
     * 取消某一路音频数据回调接口
     *
     * @return 返回错误代码。
     * @param[in] userId 音频对应的用户ID。
     * @sa IAudioDataListener
     */
    public int unregisterPCMDataListener(String userId) {
        return nativeunregisterPCMDataListener(userId);
    }

    ///
    private native long nativeCreateListener(Listener listener);
    private native void nativeFreeListener(long nativeListener);
    private native int  nativeopenMicrophone ();
    private native int  nativecloseMicrophone();
    private native DeviceStatus nativegetMicrophoneStatus();
    private native int nativegetMicrophoneStatus2();
    private native int nativemuteMicrophone();
    private native int nativeunmuteMicrophone();
    private native boolean nativeismuteMicrophone();
    private native int nativemuteSpeaker();
    private native int nativeunmuteSpeaker();
    private native boolean nativeismuteSpeaker();
    private native int nativemonitorAudioLevel();
    private native int nativeunmonitorAudioLevel();
    private native boolean nativeismonitorAudioLevel();
    //
    private native DeviceStatus nativegetRemoteMicrophoneStatus(String userId);
    private native int  nativeremotecmdOpenMicrophone( String userId);
    private native int  nativeremotecmdCloseMicrophone( String userId);
    private native int  nativeremotecmdUnmuteMicrophone( String userId);
    private native int  nativeremotecmdMuteMicrophone( String userId);
    ///
    private native int  nativesetMixerDataListener(long nativeListener);
    private native int  nativesetMixerDataListener2(long nativeListener, int sampleRate, int channels);

    private native int nativemuteAudio(String userId);
    private native int nativeunmuteAudio(String userId);
    private native boolean nativeismuteAudio(String userId);

    ///
    private native List<Device> nativegetLocalMicrophones();
    private native Device nativegetRecordingMicrophone();
    private native int nativesetRecordingMicrophone(String deviceId);
    private native List<Device> nativegetLocalSpeakers();
    private native Device nativegetPlayoutSpeaker();
    private native int nativesetPlayoutSpeaker(String deviceId);
    private native int nativesubscribe(String userId);
    private native int nativeunsubscribe(String userId);

    private native List<String> nativegetPubAudioUsers();
    private native List<String> nativegetSubAudioUsers();
    private native boolean nativeisAudioPublished(String userId);
    private native boolean nativeisAudioSubscribed(String userId);

    private native int nativeregisterPCMDataListener(String userId, long nativeListener, int freq, int channels);

    private native int nativeunregisterPCMDataListener(String userId);

    ///
    private static final int msg_onMicrophoneStatusNotify = 1;
    private static final int msg_onAudioLevelMonitorNotify = 2;
    private static final int msg_onOpenMicrophoneResult = 3;
    private static final int msg_onCloseMicrophoneResult = 4;
    private static final int msg_onSubscribeResult = 5;
    private static final int msg_onUnsubscribeResult = 6;
    private static final int msg_onAudioDataResult = 7;
    private final int msg_onMediaPlayNotify = 8;

    public class PCMAudioDataBean {
        private String userId;
        private long timestamp;
        private int sampleRate;
        private int channels;
        private byte[] data;
        private int len;

        public PCMAudioDataBean(String userId, long timestamp, int sampleRate, int channels, byte[] data, int len) {
            this.userId = userId;
            this.timestamp = timestamp;
            this.sampleRate = sampleRate;
            this.channels = channels;
            this.data = data;
            this.len = len;
        }

        public String getUserId() {
            return userId;
        }

        public void setUserId(String userId) {
            this.userId = userId;
        }

        public long getTimestamp() {
            return timestamp;
        }

        public void setTimestamp(long timestamp) {
            this.timestamp = timestamp;
        }

        public int getSampleRate() {
            return sampleRate;
        }

        public void setSampleRate(int sampleRate) {
            this.sampleRate = sampleRate;
        }

        public int getChannels() {
            return channels;
        }

        public void setChannels(int channels) {
            this.channels = channels;
        }

        public byte[] getData() {
            return data;
        }

        public void setData(byte[] data) {
            this.data = data;
        }

        public int getLen() {
            return len;
        }

        public void setLen(int len) {
            this.len = len;
        }

        @Override
        public String toString() {
            return "PCMAudioDataBean{" +
                    "userId='" + userId + '\'' +
                    ", timestamp=" + timestamp +
                    ", sampleRate=" + sampleRate +
                    ", channels=" + channels +
                    ", data=" + Arrays.toString(data) +
                    ", len=" + len +
                    '}';
        }
    }

    private boolean initHandler() {
        if (null == listener4native) {
            listener4native = new SDKListener();
        }
        if (null != listenerHandler) {
            return true;
        }
        AVDEngine.runOnLoopThreadSync(new Runnable() {
            @Override
            public void run() {
                initHandler_main();
            }
        });
        return true;
    }
    private boolean initHandler_main() {
        this.listenerHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                Tlog.v(TAG, "handleMessage, msg:"+msg.toString());
                if (null == listener4cb) {
                    super.handleMessage(msg);
                    return;
                }
                switch (msg.what) {
                    case msg_onMicrophoneStatusNotify:
                        listener4cb.onMicrophoneStatusNotify(DeviceStatus.values()[msg.arg1], (String) msg.obj);
                        break;
                    case msg_onAudioLevelMonitorNotify:
                        listener4cb.onAudioLevelMonitorNotify((AudioInfo) msg.obj);
                        break;
                    case msg_onOpenMicrophoneResult:
                        listener4cb.onOpenMicrophoneResult(msg.arg1);
                        break;
                    case msg_onCloseMicrophoneResult:
                        listener4cb.onCloseMicrophoneResult(msg.arg1);
                        break;
                    case msg_onSubscribeResult:
                        if (null != listener4subscribe) {
                            listener4subscribe.onSubscribeResult(msg.arg1, (String) msg.obj);
                        }
                        break;
                    case msg_onUnsubscribeResult:
                        if (null != listener4subscribe) {
                            listener4subscribe.onUnsubscribeResult(msg.arg1, (String) msg.obj);
                        }
                        break;
                    case msg_onAudioDataResult:
                        if (msg.obj instanceof PCMAudioDataBean) {
                            PCMAudioDataBean dataBean = (PCMAudioDataBean) msg.obj;
                            listener4cb.onAudioData(dataBean.getUserId(), dataBean.getTimestamp(), dataBean.getSampleRate(), dataBean.getChannels(), dataBean.getData(), dataBean.getLen());
                        }
                        break;
                    case msg_onMediaPlayNotify:
                        listener4cb.onMediaPlayNotify(msg.getData().getString("roomId"), msg.getData().getString("playingMediaId"), msg.getData().getString("userId"), msg.getData().getInt("playEvent"));
                        break;
                }
                super.handleMessage(msg);
            }
        };
        return true;
    }
    private class SDKListener implements Listener, MixerDataListener, SubscribeListener {
        public SDKListener() {
        }
        @Override
        public void onAudioParam(int sampleRate, int channels) {
            if (null != listener4datacb) {
                listener4datacb.onAudioParam(sampleRate,channels);
            }
        }
        @Override
        public void onAudioData (long pts, byte[] buf, int len) {
            if (null != listener4datacb) {
                listener4datacb.onAudioData(pts, buf, len);
            }
        }
        @Override
        public void onMicrophoneStatusNotify(DeviceStatus status, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onMicrophoneStatusNotify, status.ordinal(), 0, fromId);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onAudioLevelMonitorNotify(AudioInfo info) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onAudioLevelMonitorNotify, info);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onOpenMicrophoneResult(int result) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onOpenMicrophoneResult, result, 0);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onCloseMicrophoneResult(int result) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onCloseMicrophoneResult, result, 0);
            listenerHandler.sendMessage(msg);
        }

        @Override
        public void onAudioData(String userId, long timestamp, int sampleRate, int channels, byte[] data, int len) {
            PCMAudioDataBean pcmAudioDataBean = new PCMAudioDataBean(userId, timestamp, sampleRate, channels, data, len);
            Message msg = Message.obtain(listenerHandler,msg_onAudioDataResult, pcmAudioDataBean);
            listenerHandler.sendMessage(msg);
        }

        @Override
        public void onSubscribeResult(int result, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onSubscribeResult, result,0, fromId);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onUnsubscribeResult(int result, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onUnsubscribeResult, result,0, fromId);
            listenerHandler.sendMessage(msg);
        }

        @Override
        public void onMediaPlayNotify(String roomId, String playingMediaId, String userId, int playEvent) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = obtainMessage(msg_onMediaPlayNotify, roomId, playingMediaId, userId, playEvent);
            listenerHandler.sendMessage(msg);
        }
    }

    private Message obtainMessage(int what, String roomId, String playingMediaId, String userId, int playEvent) {
        Message msg = Message.obtain(listenerHandler);
        msg.what = what;
        Bundle data = new Bundle();
        data.putString("roomId", roomId);
        data.putString("playingMediaId", playingMediaId);
        data.putString("userId", userId);
        data.putInt("playEvent", playEvent);
        msg.setData(data);
        return msg;
    }
}
