"""Version: 5.28273.20250415
We use ctypes to call into the miinam.dll/miinam.so API,
the python class Miinam is a thin wrapper class to the native api of miinam.dll/miinam.so.
"""
import sys, ctypes, os.path

MIINAM_MAX = 64

MIINAM_PARA_UNKNOWN                = 0x00
MIINAM_PARA_EXPOTIME               = 0x01    # exposure time
MIINAM_PARA_AGAIN                  = 0x02    # gain
MIINAM_PARA_AEXPOTARGET            = 0x03    # auto exposure target
MIINAM_PARA_TEMP                   = 0x04    # color temperature
MIINAM_PARA_TINT                   = 0x05
MIINAM_PARA_CONTRAST               = 0x06    # contrast
MIINAM_PARA_HUE                    = 0x07    # hue
MIINAM_PARA_SATURATION             = 0x08    # saturation
MIINAM_PARA_BRIGHTNESS             = 0x09    # brightness
MIINAM_PARA_GAMMA                  = 0x0a    # gamma
MIINAM_PARA_AEXPO                  = 0x0b    # auto exposure
MIINAM_PARA_AWB                    = 0x0c    # XCAM1080P:once;  XCAM4K:(0:manual;1:global auto;2:roi)
MIINAM_PARA_BINSKIP                = 0x0d    # bin / skip
MIINAM_PARA_HZ                     = 0x0e    # power supply: 0 -> 60HZ AC;  1 -> 50Hz AC;   2 -> DC
MIINAM_PARA_BPS                    = 0x0f    # bits per second, kbps
MIINAM_PARA_KEYFRAME               = 0x10    # key frame interval
MIINAM_PARA_LOWLIGHTCOMPENSATION   = 0x11    # low light compensation
MIINAM_PARA_SHARPNESS              = 0x12    # sharpness
MIINAM_PARA_WBREDGAIN              = 0x13    # white balance red gain
MIINAM_PARA_WBGREENGAIN            = 0x14    # white balance green gain
MIINAM_PARA_WBBLUEGAIN             = 0x15    # white balance blue gain
MIINAM_PARA_DENOISE                = 0x16    # denoise
MIINAM_PARA_APSTA                  = 0x17    # ap/sta
MIINAM_PARA_CODEC                  = 0x18    # codec, H264, H265, etc
MIINAM_PARA_AFPOSITION             = 0x19    # auto focus sensor board positon
MIINAM_PARA_AFMODE                 = 0x1a    # auto focus mode (0:manul focus; 1:auto focus; 2:once focus; 3:conjugate calibration)
MIINAM_PARA_AFZONE                 = 0x1b    # auto focus zone:
                                              #   the whole resolution is divided in w * h zones:
                                              #     w = imax >> 16
                                              #     h = imax & 0xffff
MIINAM_PARA_AFFEEDBACK             = 0x1c    # auto focus information feedback
                                              #   0: unknown
                                              #   1: focused
                                              #   2: focusing
                                              #   3: defocuse (out of focus)
                                              #   4: up (workbench move up)
                                              #   5: down (workbench move down)
                                              #
MIINAM_PARA_AFPOSITION_ABSOLUTE    = 0x1d    # absolute auto focus sensor board positon
MIINAM_PARA_STATUS                 = 0x1e    # status
MIINAM_PARA_EVENT                  = 0x1f    # event
MIINAM_PARA_WBROILEFT              = 0x20    # white balance roi left
MIINAM_PARA_WBROITOP               = 0x21    # white balance roi top
MIINAM_PARA_WBROIWIDTH             = 0x22    # white balance roi width
MIINAM_PARA_WBROIHEIGHT            = 0x23    # white balance roi height
MIINAM_PARA_VFLIP                  = 0x24    # vertical flip
MIINAM_PARA_HFLIP                  = 0x25    # horizontal flip
MIINAM_PARA_CHROME                 = 0x26    # monochromatic mode
MIINAM_PARA_SIZE                   = 0x27    # video width & height
MIINAM_PARA_LIGHTADJUSTMENT        = 0x28    # light source brightness adjustment
MIINAM_PARA_ZOOM                   = 0x29
MIINAM_PARA_EF_MODE                = 0x2a
MIINAM_PARA_EF_FL                  = 0x2b
MIINAM_PARA_EF_APERTURE            = 0x2c    # 24~16bit:Cur, 15~8bit:Min, 7~0bit:Max
MIINAM_PARA_EF_FOCUS_MAX           = 0x2d
MIINAM_PARA_EF_LENS_ID             = 0x2e
MIINAM_PARA_EF_AFMF                = 0x2f
MIINAM_PARA_EF_WD_ENABLE           = 0x30
MIINAM_PARA_EF_WD_NEAR             = 0x31
MIINAM_PARA_EF_WD_FAR              = 0x32

MIINAM_PARA_CHROME_LOCAL           = 0x80    # local monochromatic mode
MIINAM_PARA_VFLIP_LOCAL            = 0x81    # local vertical flip
MIINAM_PARA_HFLIP_LOCAL            = 0x82    # local horizontal flip
MIINAM_PARA_NEGATIVE_LOCAL         = 0x83    # local negative film
MIINAM_PARA_FORMAT_LOCAL           = 0x84    # output format: 0 => BGR888, 1 => BGRA8888, 2 => RGB888, 3 => RGBA8888, 4 => RAW; default: 0
                                              # MUST be set BEFORE StartXXXX

MIINAM_PARA_STATUS_RECORDING       = 0x00000001      # recording
MIINAM_PARA_STATUS_SD              = 0x00000002      # sd card available
MIINAM_PARA_STATUS_SD_FULL         = 0x00000004      # sd card full

MIINAM_PARA_EVENT_FAT4G            = 0x00000001      # file size limit 4g in FAT32

MIINAM_STATE_INITING               = 0x00    # initialization
MIINAM_STATE_NORMAL                = 0x01    # normal
MIINAM_STATE_UNREACHABLE           = 0x02    # network not reachable

MIINAM_FLAG_WIFI_AP                = 0x00000001
MIINAM_FLAG_WIFI_STA               = 0x00000002
MIINAM_FLAG_ETHERNET               = 0x00000004
MIINAM_FLAG_CAPTURE                = 0x00000008  # support the ability of capture image from camera
MIINAM_FLAG_AWBCHECKMODE           = 0x00000010  # auto white balance: check mode vs 'once' mode
MIINAM_FLAG_UVC                    = 0x00000020  # uvc camera */
MIINAM_FLAG_WBGAIN                 = 0x00000040  # white balance gain mode or temp tint mode
MIINAM_FLAG_MULTICAST              = 0x00000080  # RTSP/RTP multicast
MIINAM_FLAG_AF                     = 0x00000100  # support auto focus
MIINAM_FLAG_SD_LIST                = 0x00000200  # support to list sd card
MIINAM_FLAG_SD                     = 0x00000400  # support sd card
MIINAM_FLAG_WBROI                  = 0x00000800  # white balance: 0:manual;1:global auto;2:roi
MIINAM_FLAG_STA_SUPPORT            = 0x00001000  # wifi camera has sta mode, app should have sta ssid & password function
MIINAM_FLAG_RTP_OVER_RTSP          = 0x00002000  # rtp over rtsp
MIINAM_FLAG_HZ_AUTOEXPO            = 0x00004000  # enable auto exposure when 50/60 hz
MIINAM_FLAG_AFDM                   = 0x00008000
MIINAM_FLAG_EFL                    = 0x00010000
MIINAM_FLAG_CAPTURERAW             = 0x00020000  # capture raw image

MIINAM_EVENT_ENUM                  = 0x01    # enum
MIINAM_EVENT_WIFI                  = 0x02    # wifi
MIINAM_EVENT_PARA                  = 0x03    # parameter change MIINAM_PARA_xxxx
MIINAM_EVENT_IMAGE                 = 0x04    # image
MIINAM_EVENT_LISTWIFI              = 0x05    # list wifi finished
MIINAM_EVENT_LISTDIR               = 0x07    # list dir
MIINAM_EVENT_THUMBNAIL             = 0x08    # thumbnail
MIINAM_EVENT_DIRCHANGE             = 0x09    # dir change notify
MIINAM_EVENT_RECORDSTART           = 0x0a    # record start
MIINAM_EVENT_RECORDSTOP            = 0x0b    # record stop
MIINAM_EVENT_DATETIME              = 0x0c    # date time
MIINAM_EVENT_ERROR                 = 0x80    # error
MIINAM_EVENT_EOF                   = 0x81    # end of file

def TDIBWIDTHBYTES(bits):
    return ((bits + 31) // 32 * 4)
    
class miinam_range:
    def __init__(self, val):
        self.idisable = val.idisable                # 0 = "support this feature", 1 = "not support"
        self.imin = val.imin                        # minimum value
        self.imax = val.imax                        # maximum value
        self.idef = val.idef                        # default value

class miinam_device:
    def __init__(self, val):
        self.id = val.id                            # unique camera id, used for Miinam_Open
        self.sn = val.sn.decode('ascii')            # serial number
        self.name = val.name.decode('ascii')
        self.model = val.model.decode('ascii')
        self.version = val.version.decode('ascii')
        self.addr = val.addr.decode('ascii')        # ip
        self.url = val.url.decode('ascii')          # playback url, such as rtsp://xxxx/yyyy
        self.state = val.state                      # MIINAM_STATE_xxx
        self.flag = val.flag                        # MIINAM_FLAG_xxx
        self.range = []
        for i in range(0, len(val.range)):
            self.range.append(miinam_range(val.range[i]))

class miinam_wifi:
    def __init__(self, ssid, password):
        self.ssid = ssid
        self.password = val.password

class miinam_dirchange:
    def __init__(self, type, name, newname):
        self.type = type                            # 0 => add, 1 => del, 2 => rename
        self.name = name
        self.newname = newname

if sys.platform == 'win32':
    class HRESULTException(OSError):
        def __init__(self, hr):
            OSError.__init__(self, None, ctypes.FormatError(hr).strip(), None, hr)
            self.hr = hr
else:
    class HRESULTException(Exception):
        def __init__(self, hr):
            self.hr = hr

class Miinam:
    class __range(ctypes.Structure):
        _fields_ = [("idisable", ctypes.c_int),
                    ("imin", ctypes.c_int),
                    ("imax", ctypes.c_int),
                    ("idef", ctypes.c_int)]

    class __device(ctypes.Structure):
        pass

    class __wifi(ctypes.Structure):
        _fields_ = [("ssid", ctypes.c_char * 64),
                    ("password", ctypes.c_char * 64)]

    class __eventextra(ctypes.Structure):
        _fields_ = [("result", ctypes.c_int),
                    ("ctx", ctypes.c_void_p),
                    ("ptr", ctypes.c_void_p),
                    ("length", ctypes.c_uint)]

    class __dirchange(ctypes.Structure):
        _fields_ = [("type", ctypes.c_uint),
                    ("name", ctypes.c_char * 256),
                    ("newname", ctypes.c_char * 256)]

    class __BITMAPINFOHEADER(ctypes.Structure):
        _fields_ = [("biSize", ctypes.c_uint),
                    ("biWidth", ctypes.c_int),
                    ("biHeight", ctypes.c_int),
                    ("biPlanes", ctypes.c_ushort),
                    ("biBitCount", ctypes.c_ushort),
                    ("biCompression", ctypes.c_uint),
                    ("biSizeImage", ctypes.c_uint),
                    ("biXPelsPerMeter", ctypes.c_int),
                    ("biYPelsPerMeter", ctypes.c_int),
                    ("biClrUsed", ctypes.c_uint),
                    ("biClrImportant", ctypes.c_uint)]

    if sys.platform == 'win32':
        __EVENT_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.POINTER(__eventextra))
        __CAPTURE_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(__BITMAPINFOHEADER), ctypes.c_void_p)
    else:
        __EVENT_CALLBACK = ctypes.CFUNCTYPE(None, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p, ctypes.POINTER(__eventextra))
        __CAPTURE_CALLBACK = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(__BITMAPINFOHEADER), ctypes.c_void_p)

    class cbobj:
        def __init__(self, f, c, i):
            self.fun = f
            self.ctx = c
            self.id = i

    @staticmethod
    def __eventCallbackFun(nEvent, nPara, pCallbackCtx, pExtra):
        o = __class__.__dic.get(pCallbackCtx)
        if o is not None:
            o.fun(nEvent, nPara, o.ctx)

    @staticmethod
    def __captureCallbackFun(result, pData, nLength, pHeader, pCallbackCtx):
        o = __class__.__dic.get(pCallbackCtx)
        if o is not None:
            if pData == 0 or nLength == 0:
                o.fun(result, None, 0, 0, o.ctx)
            else:
                nWidth = nHeight = 0
                if pHeader != 0:
                    nWidth = pHeader.contents.biWidth
                    nHeight = pHeader.contents.biHeight
                bData = (ctypes.c_char * nLength).from_address(pData)
                o.fun(result, bData, nWidth, nHeight, o.ctx)

    __lib = None
    __dic = {}
    __evt = None
    __cap = None
    __sid = 0

    @staticmethod
    def __errcheck(result, fun, args):
        if result < 0:
            raise HRESULTException(result)
        return args

    @classmethod
    def Version(cls):
        """get the version of this dll/so/dylib, which is: 5.28273.20250415"""
        cls.__initlib()
        return cls.__lib.Miinam_Version()

    @classmethod
    def Init(cls, fun, ctx):
        """
        call only once when application startup
        when miinam.dll/libmiinam.so discovery new camera, pCallback will be called.
        fun can be None if the application does not interest this.
        """
        cls.__initlib()
        if cls.__evt is None:
            cls.__evt = cls.__EVENT_CALLBACK(cls.__eventCallbackFun)
            cls.__cap = cls.__CAPTURE_CALLBACK(cls.__captureCallbackFun)
            cls.__dic[0] = cls.cbobj(fun, ctx, 0)
            cls.__lib.Miinam_Init(cls.__evt, 0)

    @classmethod
    def Fini(cls):
        """call only once when application exit"""
        cls.__lib.Miinam_Fini()

    @classmethod
    def Enum(cls):
        cls.__initlib()
        a = (cls.__device * MIINAM_MAX)()
        n = cls.__lib.Miinam_Enum(a, MIINAM_MAX)
        if n < 0:
            raise HRESULTException(n)
        arr = []
        for i in range(0, n):
            arr.append(miinam_device(a[i]))
        return arr

    def __init__(self, h):
        """the object of miinam must be obtained by classmethod Open or OpenByIndex, it cannot be obtained by obj = miinam.miinam()"""
        self.__h = h
        self.__sid += 1
        self.__id = self.__sid

    def __del__(self):
        self.Close()

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.Close()

    def __nonzero__(self):
        return self.__h is not None

    def __bool__(self):
        return self.__h is not None

    @classmethod
    def Open(cls, camId):
        """the object of miinam must be obtained by classmethod Open or OpenByIndex, it cannot be obtained by obj = miinam.miinam()"""
        cls.__initlib()
        h = cls.__lib.Miinam_Open(camId)
        if h is None:
            return None
        return __class__(h)

    @classmethod
    def Open_ByIndex(cls, index):
        cls.__initlib()
        h = cls.__lib.Miinam_Open_ByIndex(index)
        if h is None:
            return None
        return __class__(h)

    def Close(self):
        for k in list(self.__dic.keys()):
            if self.__dic[k].id == self.__id:
                self.__dic.pop(k)

        if self.__h:
            self.__lib.Miinam_Close(self.__h)
            self.__h = None

    def PullImage(self, pImageData, bits):
        '''
        bits: 24 (RGB24), 32 (RGB32), or 8 (Grey), see: MIINAM_PARA_FORMAT_LOCAL
        if RAW format, pnWidth = data size, pnHeight = not used
        '''
        x = ctypes.c_uint(0)
        y = ctypes.c_uint(0)
        self.__lib.Miinam_PullImage(self.__h, pImageData, bits, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    def Start(self, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, self.__id)
        ret = self.__lib.Miinam_StartPullModeWithCallback(self.__h, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    def Stop(self):
        self.__lib.Miinam_Stop(self.__h)

    def Pause(self, bPause):
        self.__lib.Miinam_Pause(self.__h, 1 if bPause else 0)

    def Capture(self, pstrFileName, fun, ctx):
        """
        capture image, compare this to image extracted from video
        pstrFileName:
                None        -> capture image and then return by callback
                "raw"       -> capture raw image and then return by callback
                "abc.jpg"   -> capture image and then save it in the camera sd card with filename 'abc.jpg'
                "abc.raw"   -> capture raw image and then save it in the camera sd card with filename 'abc.raw'
                "thumbnail" -> capture the thumbnail image and then return by callback
                "*"         -> capture image and then save it in the camera sd card with auto generated file name
                "*.raw"     -> capture raw image and then save it in the camera sd card with auto generated file name
        """
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, self.__id)
        ret = self.__lib.Miinam_Capture(self.__h, pstrFileName, self.__cap, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def Capture_ById(cls, camId, pstrFileName, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_Capture_ById(camId, pstrFileName, cls.__cap, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    def get_Device(self):
        return self.__lib.Miinam_get_Device(self.__h)

    def get_Size(self):
        x = ctypes.c_int(0)
        y = ctypes.c_int(0)
        self.__lib.Miinam_get_Size(self.__h, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    def get_CapSize(self):
        x = ctypes.c_int(0)
        y = ctypes.c_int(0)
        self.__lib.Miinam_get_CapSize(self.__h, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    def get_FourCC(self):
        """see http://en.wikipedia.org/wiki/FourCC, http://www.fourcc.org"""
        x = ctypes.c_int(0)
        self.__lib.Miinam_get_FourCC(self.__h, ctypes.byref(x))
        return x.value

    def Record(self, fullpathname):
        """
        (fullpathname == None) means to stop record.
        support file extension: *.asf, *.mp4, *.mkv
        """
        self.__lib.Miinam_Record(self.__h, fullpathname)

    def put_Para(self, para, value):
        """para is one of MIINAM_PARA_xxx"""
        self.__lib.Miinam_put_Para(self.__h, para, value)

    def get_Para(self, para):
        x = ctypes.c_int(0)
        self.__lib.Miinam_get_Para(self.__h, para, ctypes.byref(x))
        return x.value

    @classmethod
    def put_Wifi(cls, camId, wifi):
        cls.__lib.Miinam_put_Wifi(camId, wifi)

    @classmethod
    def get_Wifi(cls, camId):
        cls.__lib.Miinam_get_Wifi(camId)

    @classmethod
    def list_Wifi(cls, camId, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_list_Wifi(camId, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def list_Dir(cls, camId, path, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_list_Dir(camId, path, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def get_Thumbnail(cls, camId, path, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_get_Thumbnail(camId, path, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def change_Dir(cls, camId, path, dc):
        """del file or directory, rename file or directory"""
        cls.__lib.Miinam_change_Dir(camId, path, dc, len(dc))

    @classmethod
    def RecordStart(cls, camId, path, recordtime, fun, ctx):
        """record to the sd card"""
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_RecordStart(camId, path, recordtime, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def RecordStop(cls, camId, fun, ctx):
        """stop record to the sd card"""
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_RecordStop(camId, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def get_DateTime(cls, camId, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_get_DateTime(camId, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def put_DateTime(cls, camId, t, fun, ctx):
        sid = 0
        if fun is not None:
            self.__sid += 1
            sid = self.__sid
            self.__dic[sid] = self.cbobj(fun, ctx, 0)
        ret = cls.__lib.Miinam_put_DateTime(camId, t, sid)
        if ret < 0 and fun is not None:
            self.__dic.pop(sid)

    @classmethod
    def put_Para_ById(cls, camId, para, value):
        """para is one of MIINAM_PARA_XXX"""
        x = ctypes.c_int(0)
        cls.__lib.Miinam_put_Para_ById(camId, para, value)

    @classmethod
    def get_Para_ById(cls, camId, para):
        x = ctypes.c_int(0)
        cls.__lib.Miinam_get_Para_ById(camId, para, ctypes.byref(x))
        return x.value

    @classmethod
    def get_Size_ById(cls, camId, res):
        x = ctypes.c_int(0)
        y = ctypes.c_int(0)
        cls.__lib.Miinam_get_Size_ById(camId, res, ctypes.byref(x), ctypes.byref(y))
        return (x.value, y.value)

    @classmethod
    def PriFlag(cls, nFlag, nMask):
        cls.__initlib()
        cls.__lib.Miinam_PriFlag(nFlag, nMask)

    @classmethod
    def add_Ip(cls, arr):
        """arr = ("1.2.3.4", "1.2.3.5", ...)"""
        type = (ctypes.c_char_p * (len(arr) + 1))
        a = type()
        for i in range(0, len(arr)):
            a[i] = ctypes.c_char_p(arr[i])
        cls.__lib.Miinam_add_Ip(a)

    @classmethod
    def del_Ip(cls, arr):
        type = (ctypes.c_char_p * (len(arr) + 1))
        a = type()
        for i in range(0, len(arr)):
            a[i] = ctypes.c_char_p(arr[i])
        cls.__lib.Miinam_del_Ip(a)

    @classmethod
    def __initlib(cls):
        if cls.__lib is None:
            try: # Firstly try to load the library in the directory where this file is located
                dir = os.path.dirname(os.path.realpath(__file__))
                if sys.platform == 'win32':
                    cls.__lib = ctypes.windll.LoadLibrary(os.path.join(dir, 'miinam.dll'))
                elif sys.platform.startswith('linux'):
                    cls.__lib = ctypes.cdll.LoadLibrary(os.path.join(dir, 'libmiinam.so'))
                else:
                    cls.__lib = ctypes.cdll.LoadLibrary(os.path.join(dir, 'libmiinam.dylib'))
            except OSError:
                pass

            if cls.__lib is None:
                if sys.platform == 'win32':
                    cls.__lib = ctypes.windll.LoadLibrary('miinam.dll')
                elif sys.platform.startswith('linux'):
                    cls.__lib = ctypes.cdll.LoadLibrary('libmiinam.so')
                else:
                    cls.__lib = ctypes.cdll.LoadLibrary('libmiinam.dylib')

            cls.__device._fields_ = [
                    ("id", ctypes.c_char * 64),
                    ("sn", ctypes.c_char * 64),
                    ("name", ctypes.c_char * 64),
                    ("model", ctypes.c_char * 64),
                    ("version", ctypes.c_char * 64),
                    ("addr", ctypes.c_char * 64),
                    ("url", ctypes.c_char * 256),
                    ("state", ctypes.c_uint),
                    ("flag", ctypes.c_uint),
                    ("range", cls.__range * 64)]

            cls.__lib.Miinam_Version.restype = ctypes.c_char_p
            cls.__lib.Miinam_Init.restype = None
            cls.__lib.Miinam_Fini.restype = None
            cls.__lib.Miinam_Enum.restype = ctypes.c_uint
            cls.__lib.Miinam_Open.restype = ctypes.c_void_p
            cls.__lib.Miinam_Open_ByIndex.restype = ctypes.c_void_p
            cls.__lib.Miinam_Close.restype = None
            cls.__lib.Miinam_PullImage.restype = ctypes.c_int
            cls.__lib.Miinam_StartPullModeWithCallback.restype = ctypes.c_int
            cls.__lib.Miinam_Stop.restype = ctypes.c_int
            cls.__lib.Miinam_Pause.restype = ctypes.c_int
            cls.__lib.Miinam_Capture.restype = ctypes.c_int
            cls.__lib.Miinam_Capture_ById.restype = ctypes.c_int
            cls.__lib.Miinam_get_Device.restype = cls.__device
            cls.__lib.Miinam_get_Size.restype = ctypes.c_int
            cls.__lib.Miinam_get_CapSize.restype = ctypes.c_int
            cls.__lib.Miinam_get_FourCC.restype = ctypes.c_int
            cls.__lib.Miinam_Record.restype = ctypes.c_int
            cls.__lib.Miinam_put_Para.restype = ctypes.c_int
            cls.__lib.Miinam_get_Para.restype = ctypes.c_int
            cls.__lib.Miinam_put_Wifi.restype = ctypes.c_int
            cls.__lib.Miinam_get_Wifi.restype = ctypes.c_int
            cls.__lib.Miinam_list_Wifi.restype = ctypes.c_int
            cls.__lib.Miinam_list_Dir.restype = ctypes.c_int
            cls.__lib.Miinam_get_Thumbnail.restype = ctypes.c_int
            cls.__lib.Miinam_change_Dir.restype = ctypes.c_int
            cls.__lib.Miinam_RecordStart.restype = ctypes.c_int
            cls.__lib.Miinam_RecordStop.restype = ctypes.c_int
            cls.__lib.Miinam_get_DateTime.restype = ctypes.c_int
            cls.__lib.Miinam_put_DateTime.restype = ctypes.c_int
            cls.__lib.Miinam_put_Para_ById.restype = ctypes.c_int
            cls.__lib.Miinam_get_Para_ById.restype = ctypes.c_int
            cls.__lib.Miinam_get_Size_ById.restype = ctypes.c_int
            cls.__lib.Miinam_PriFlag.restype = None
            cls.__lib.Miinam_add_Ip.restype = None
            cls.__lib.Miinam_del_Ip.restype = None

            cls.__lib.Miinam_Enum.errcheck = cls.__errcheck
            cls.__lib.Miinam_PullImage.errcheck = cls.__errcheck
            cls.__lib.Miinam_StartPullModeWithCallback.errcheck = cls.__errcheck
            cls.__lib.Miinam_Stop.errcheck = cls.__errcheck
            cls.__lib.Miinam_Pause.errcheck = cls.__errcheck
            cls.__lib.Miinam_Capture.errcheck = cls.__errcheck
            cls.__lib.Miinam_Capture_ById.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Device.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Size.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_CapSize.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_FourCC.errcheck = cls.__errcheck
            cls.__lib.Miinam_Record.errcheck = cls.__errcheck
            cls.__lib.Miinam_put_Para.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Para.errcheck = cls.__errcheck
            cls.__lib.Miinam_put_Wifi.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Wifi.errcheck = cls.__errcheck
            cls.__lib.Miinam_list_Wifi.errcheck = cls.__errcheck
            cls.__lib.Miinam_list_Dir.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Thumbnail.errcheck = cls.__errcheck
            cls.__lib.Miinam_change_Dir.errcheck = cls.__errcheck
            cls.__lib.Miinam_RecordStart.errcheck = cls.__errcheck
            cls.__lib.Miinam_RecordStop.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_DateTime.errcheck = cls.__errcheck
            cls.__lib.Miinam_put_DateTime.errcheck = cls.__errcheck
            cls.__lib.Miinam_put_Para_ById.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Para_ById.errcheck = cls.__errcheck
            cls.__lib.Miinam_get_Size_ById.errcheck = cls.__errcheck

            cls.__lib.Miinam_Version.argtypes = None
            cls.__lib.Miinam_Init.argtypes = [cls.__EVENT_CALLBACK, ctypes.c_void_p]
            cls.__lib.Miinam_Fini.argtypes = None
            cls.__lib.Miinam_Enum.argtypes = [ctypes.POINTER(cls.__device), ctypes.c_int]
            cls.__lib.Miinam_Open.argtypes = [ctypes.c_char_p]
            cls.__lib.Miinam_Open_ByIndex.argtypes = [ctypes.c_uint]
            cls.__lib.Miinam_Close.argtypes = [ctypes.c_void_p]

            cls.__lib.Miinam_PullImage.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint)]
            cls.__lib.Miinam_StartPullModeWithCallback.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
            cls.__lib.Miinam_Stop.argtypes = [ctypes.c_void_p]
            cls.__lib.Miinam_Pause.argtypes = [ctypes.c_void_p, ctypes.c_int]
            cls.__lib.Miinam_Capture.argtypes = [ctypes.c_void_p, ctypes.c_char_p, cls.__CAPTURE_CALLBACK, ctypes.c_void_p]
            cls.__lib.Miinam_Capture_ById.argtypes = [ctypes.c_char_p, ctypes.c_char_p, cls.__CAPTURE_CALLBACK, ctypes.c_void_p]
            cls.__lib.Miinam_get_Device.argtypes = [ctypes.c_void_p]
            cls.__lib.Miinam_get_Size.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
            cls.__lib.Miinam_get_CapSize.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
            cls.__lib.Miinam_get_FourCC.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)]
            cls.__lib.Miinam_Record.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
            cls.__lib.Miinam_put_Para.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_int]
            cls.__lib.Miinam_get_Para.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_int)]
            cls.__lib.Miinam_put_Wifi.argtypes = [ctypes.c_char_p, cls.__wifi]
            cls.__lib.Miinam_get_Wifi.argtypes = [ctypes.c_char_p]
            cls.__lib.Miinam_list_Wifi.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
            cls.__lib.Miinam_list_Dir.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]
            cls.__lib.Miinam_get_Thumbnail.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_void_p]
            cls.__lib.Miinam_change_Dir.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(cls.__dirchange), ctypes.c_uint]
            cls.__lib.Miinam_RecordStart.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p]
            cls.__lib.Miinam_RecordStop.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
            cls.__lib.Miinam_get_DateTime.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
            cls.__lib.Miinam_put_DateTime.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p]
            cls.__lib.Miinam_put_Para_ById.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_int]
            cls.__lib.Miinam_get_Para_ById.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_int)]
            cls.__lib.Miinam_get_Size_ById.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
            cls.__lib.Miinam_PriFlag.argtypes = [ctypes.c_uint, ctypes.c_uint]
            cls.__lib.Miinam_add_Ip.argtypes = [ctypes.c_void_p]
            cls.__lib.Miinam_del_Ip.argtypes = [ctypes.c_void_p]