Error passing an UnsafeMutablePointer to a function

624 Views Asked by At

i am an ios developer who uses swift. I am using an sdk in order to communicate with an ip camera retrieve data from it, and show it to the user. I ve a project shipped with the sdk, which is written in obj c, and is working without problems. The problem is i am not able to pass the parameter to the expected parameter type from the function. The SDK is very old, i integrated it using static libraries and a bridging header and i was able to connect successfully to the camera. I think the sdk is written in c or c++, and i am basically having problems with the parameter conversion.

Fos.h

typedef struct   
{ 
int                       channel;
long long                 time;
unsigned int              index;
FOSMEDIATYPE              type;     //Is video or audio
FOSDECFMT                 fmt;      //The format of video or audio.
short                     isKey;
short                     multiAudioPage;
int                       frameTag; 
union{ 
    FOSVIDEO_INFO      video;       //Media type is video.
    FOSAUDIO_INFO      audio;       //Media type is audio.
} media;
unsigned long long      pts;        //Pts.
unsigned int              len;      //Size of data.
char                      data[0];  //Just data.
}ATTRIBUTE_PACKED FOSDEC_DATA;    //Save the video or audio data,include video or audio information.

.

Objective C function, currently working

- (IBAction)login:(id)sender {
//[self._imageView add]
NSString* str = [self._textField text];
const char* url = [str UTF8String];
char ip[128];
int port;
char usr[64];
char pwd[64];
int count = sscanf(url, "%[^:]:%d/%[^:]:%s", ip, &port, usr, pwd);
if (count != 4) {
    return;
}

mHandle = FosSdk_Create(ip, "", usr, pwd, port, port, FOSIPC_H264, FOSCNTYPE_IP);
//mHandle = FosSdk_Create(ip, "FASDFDSAFDASFASD", usr, pwd, port, port, FOSIPC_H264, FOSCNTYPE_P2P);
if (mHandle == FOSHANDLE_INVALID) {
    return;
}
[self._Login setEnabled:NO];
[self._Logout setEnabled:YES];

[NSThread detachNewThreadSelector:@selector(RunInThread:) toTarget:self withObject:^(void *param){

    int usrPrivilege = 0;
    FOSCMD_RESULT ret = FosSdk_Login(mHandle, &usrPrivilege, 500);
    if (FOSCMDRET_OK != ret) {
        return;
    }

    ret = FosSdk_OpenVideo(mHandle, FOSSTREAM_SUB, 500);
    if (FOSCMDRET_OK != ret) {
        return;
    }
    //char* framebuf = (char*)malloc(512*1024);
    FOSDEC_DATA* data = NULL;

    int outlen = 0;
    while (mHandle) {
// FosSdk_RetainHandle(mHandle, &usrPrivilege);
        if ( FOSCMDRET_OK == FosSdk_GetVideoData(mHandle, (char**)&data, &outlen, FOSDECTYPE_RGB24) && outlen>0)
        {

            if (data->type == FOSMEDIATYPE_VIDEO) {
                //int a=0;
                //a++;
                [self imageFromAVPicture:data->data width:data->media.video.picWidth height:data->media.video.picHeight];
            }
            else{
            }
        }
        usleep(20*1000);
    }
 }];
}

.

The method signature in fos.h

    FOSSDK FOSCMD_RESULT FOSAPI FosSdk_GetVideoData(FOSHANDLE handle, char **data, int *outLen, FOSDECFMT videoFmt);

.

When calling it from objective-c

FosSdk_GetVideoData(<#unsigned int handle#>, <#char **data#>, <#int *outLen#>, <#FOSDECFMT videoFmt#>)
}];

.

When calling it from swift

FosSdk_GetVideoData(handle: UInt32,
                   data: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!,
                   outLen: UnsafeMutablePointer<Int32>!,
                   videoFmt: FOSDECFMT)

.

The error is data parameter, exactly:

Cannot convert value of type 'UnsafeMutablePointer<FOSDEC_DATA>' to expected argument type 'UnsafeMutablePointer<Int8>?'

.

I ve tried changing the declaration of data to this, but it gives me this error

let data = UnsafeMutablePointer<Int8>(&fosdecData)
Ambiguous use of 'init'

.

This pointer stuff is a bit complicated but i was able to pair my device with the camera, using methods which used unsafeMutablePointers too. However, i cannot figure out how to pass this empy data object to the function.

The SDK is outdated and bad documented, which does not help. Anyway, i need to make it working. Hope to get some help, thanks coders! .

EDIT, THANKS TO OOPer . Here is the updated version in swift, which actually produces an unexpected error. The result returned from the function is now 0, so it s correct. However, trying to access original FosdedData struct, i have a Command failed due to signal: Segmentation fault: 11. How could i solve this? Sorry for long question, i wanted to be accurate.

var mhandle : UInt32 = FOS_HANDLE_INIT.rawValue

override func viewDidLoad() {
    super.viewDidLoad()

    //These functions basically do camera pairing, and write result on mhandle, which is equal to 0 , if functions work, otherwise 1
    initializeFOS()
    startEzLinkFOS()
    sdkCreateFOS()
    loginFOS()
    openVideoFOS()        

    var data: UnsafeMutablePointer<FOSDEC_DATA>? = nil
    var outlen : Int32 = 0
    while self.mhandle > 0 {
        let resultOOPer = withUnsafeMutablePointer(to: &data) { pointerToFosdecData in
            pointerToFosdecData.withMemoryRebound(to: UnsafeMutablePointer<Int8>?.self, capacity: 1, { (pointerToInt8Pointer) in

                FosSdk_GetVideoData(self.mhandle, pointerToInt8Pointer, &outlen, FOSDECTYPE_RGB24)
            })
        }

        if resultOOPer == FOSCMDRET_OK && outlen > 0{
            if let unwrData = data {

                //if unwrData.pointee.type == FOSMEDIATYPE_VIDEO{
                //Produced Output when i try access pointee, which is actually the fosdecData struct i need:
                //Command failed due to signal: Segmentation fault: 11
                //}
            }
        }
        usleep(20*1000)
    }


}
1

There are 1 best solutions below

10
On

First of all, using the initializer of UnsafeMutablePointer in this way may generate an unexpected result and you should never do:

let data = UnsafeMutablePointer<FOSDEC_DATA>(&fosdecData)

Swift compiler may prepare a temporary region for the inout parameter and pass the address of the region, which is released immediately after the call. Unfortunately, this code may work in some cases, but you know codes work-in-some-cases can be a hard-to-fix bug.


Second, in your Objective-C code, you declare data as FOSDEC_DATA* -- pointer to FOSDEC_DATA -- and initializing it to NULL.

Why are you trying to initialize your Swift version of data with &fosdecData? There's no fosdecData in your Objective-C code.

The equivalent Swift code for the declaration of data should be like this:

var data: UnsafeMutablePointer<FOSDEC_DATA>? = nil

You should declare data as UnsafeMutablePointer<FOSDEC_DATA>? -- pointer to FOSDEC_DATA -- and initialize it to nil.

In addition, it needs to be var, your FosSdk_GetVideoData would rewrite it to a valid pointer to FOSDEC_DATA.


Third, you need to pass UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>! to the second parameter of FosSdk_GetVideoData, but &data would generate a pointer of type UnsafeMutablePointer<UnsafeMutablePointer<FOSDEC_DATA>?>.

To make this sort of pointer type conversion, you need to use withMemoryRebound<T, Result>(to:capacity:_:). And you need to call withUnsafeMutablePointer<T, Result>(to:_:) to get a pointer to data.

let result = withUnsafeMutablePointer(to: &data) {
    pointerToFosdecDataPtr in
    //pointerToFosdecDataPtr: UnsafeMutablePointer<UnsafeMutablePointer<FOSDEC_DATA>?>
    pointerToFosdecDataPtr.withMemoryRebound(to: UnsafeMutablePointer<Int8>?.self, capacity: 1) {
        pointerToInt8Ptr in
        //pointerToInt8Ptr: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
        //...
    }
}

So, your Swift code equivalent to your Objective-C code would be something like this:

var data: UnsafeMutablePointer<FOSDEC_DATA>? = nil
var outlen: Int32 = 0

while mHandle != 0 {

    let result = withUnsafeMutablePointer(to: &data) {
        pointerToFosdecDataPtr in
        //pointerToFosdecDataPtr: UnsafeMutablePointer<UnsafeMutablePointer<FOSDEC_DATA>?>
        pointerToFosdecDataPtr.withMemoryRebound(to: UnsafeMutablePointer<Int8>?.self, capacity: 1) {
            pointerToInt8Ptr in
            //pointerToInt8Ptr: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>
            FosSdk_GetVideoData(mHandle, pointerToInt8Ptr, &outlen, FOSDECTYPE_RGB24)
            //Implicit `return` works here and the result is passed to `result`.
        }
    }
    if result == FOSCMDRET_OK && outlen > 0 {
        if let data = data, data.pointee.type == FOSMEDIATYPE_VIDEO {
            //...
            //self.imageFromAVPicture...
        } else {
        }
    }
    usleep(20*1000)
}

EDIT

Code updated to reflect whole behavior of the currently working Objective-C code. (Renamed mhandle to mHandle as in Objective-C code.)