Search code examples
vala

vala: how to cast void * to uint[]?


I want to call Gdk.Pixbuf.from_data() constructor in order to wrap a video frame from GStreamer into a pixbuf. The constructor accepts the pointer to memory as uint8[]. But I have a void * variable (Gst.Video.Frame.data[0]) which points to the start of the data. I know uint8[] is not just a pointer but rather a type knowing about where memory starts and how far it spans. But for Gdk.Pixbuf.from_data() it doesn't really matter because C signature of the function accepts just const guchar * and the length is inferred from other parameters like stride and height.

So... How do I correctly wrap my void * pointer into a uint[] array?

Video.Frame frame = {};
...
var pixbuf = new Gdk.Pixbuf.from_data((uint8[])frame.data[0], ...);

The casting above doesn't work because vala generates C code which copies data from memory to the newly created array but with incorrect length. Firstly, it's obvious that vala itself cannot know the length to which void * points and secondly, I don't need any copying, I just need a simple casting.


Solution

  • I don't know where you got the void*, but for the most part if you have a pointer in Vala you're doing it wrong. There are exceptions, mainly if you're using libxml, but they are few and far between. You should take a look at the code which creates a void* and think about if there is a better way, like maybe new uint8[x] instead of malloc(x).

    Casting void* to uint8[] only requires a cast, but the length will be set to -1. Gdk.Pixbuf.from_data doesn't actually need length (it calculates it based on height and rowstride), though, so that shouldn't be a problem. However, in general you probably want to do something like

    unowned uint8[] data = (uint8[]) memory;
    data.length = whatever;
    

    Since some functions do require a length. Many also don't check the length argument for using it, which means an invalid length can easily trigger a segfault. Basically, it's a good habit to get into, and it's generally easier than checking every argument to see if the array_length = false CCode attribute is set.

    The reason Vala generates a copy is because Gdk.Pixbuf.from_data takes ownership of the data. You need to tell Vala to transfer ownership of the data (using (owned)).

    Putting it all together:

    unowned uint8[] data = (uint8[]) memory;
    data.length = whatever;
    Gdk.Pixbuf pb = new Gdk.Pixbuf.from_data(data, ...);
    

    But note that you'll still end up with a copy when the data is passed to Gdk.Pixbuf.from_data. There is just no way around the copy since Gdk.Pixbuf.from_data takes ownership, unless you can transfer ownership of the original data.