Problems with C Pointers

I try to use the ffmpeg libraries in Swift, but I am struggling with the pointers. Actually I try to use the av_image_copy_to_buffer function (defined here). The function is defined in imgutils.h as:

int av_image_copy_to_buffer(uint8_t *dst, int dst_size,
                            const uint8_t * const src_data[4], const int src_linesize[4],
                            enum AVPixelFormat pix_fmt, int width, int height, int align);

I don't know a lot about C, but is my assumption correct, that const uint8_t * const src_data[4] means, that this function excepts an array with the size 4, pointing to four UInt8-Arrays?

I would like to pass the frame data like in this example. There they use this C code:

ret = av_image_copy_to_buffer(buffer, size,
        (const uint8_t * const *)tmp_frame->data,
        (const int *)tmp_frame->linesize, tmp_frame->format,
        tmp_frame->width, tmp_frame->height, 1);

If I now access myFrame.pointee.data, this will lead to data: (UnsafeMutablePointer<UInt8>?, UnsafeMutablePointer<UInt8>?, UnsafeMutablePointer<UInt8>?, UnsafeMutablePointer<UInt8>?, ....).

I could access with myFrame.pointee.data.0 up to myFrame.pointee.data.7. It seems to be a tuple?!

In frame.h, the data property of the AVFrame struct is defined as uint8_t *data[AV_NUM_DATA_POINTERS];

How can I now pass the data pointers to the function?

EDIT:

According to the swift docu (here), Type * const * should be converted to UnsafePointer<Type>. Why is then uint8_t *data[AV_NUM_DATA_POINTERS]; converted to a Tuple of (UnsafeMutablePointer<UInt8>?, ...)?

int *p and int p[N] (using int and N as stand-ins for any type and integer) are imported differently, due to how they work in structs. When the latter exists as a field in a struct in C, that field is an inline array rather than a pointer. As such, Swift imports fixed-size C arrays as tuples.

1 Like

Thanks for that information. I just also found this article.

AV_NUM_DATA_POINTERS is indeed defined as 8 in my example, so this is the reason, why it is actually imported as a tuple from .0 to .7.

But I actually don't understand this decision. Can someone explain, why this would be useful to import as a tuple? And reading the swift docu here again: why is it a mutable pointer? And more important: how can I convert this tuple to a UnsafePointer<UnsafePointer<UInt8>?>!?

Arrays with known size are not pointers, and while C programmers will often tell you that they reduce to pointers that's not quite true. Swift acknowledges this distinction: UnsafePointer doesn't carry size information, but tuples do.

As to why is the pointer mutable: the way to make a pointer non-mutable in Swift is to have that pointer be pointing to const. In your example (uint8_t *data[AV_NUM_DATA_POINTERS]) the pointer is not pointing to const uint8, but instead pointing to uint8, so the pointer is mutable.

As to how to convert to the pointer you want, use withUnsafePointer(to:) and withMemoryRebound:

withUnsafePointer(to: &yourValue) { tuplePtr in
    tuplePtr.withMemoryRebound(to: UnsafePointer<UInt8>?.self) { innerPtr in
        // compute on innerPtr
    }
}
2 Likes