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.
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.