cgo, ffmpeg: fixing cgo argument has Go pointer to Go Pointer

284 views
Skip to first unread message

Steven Edwards

unread,
Feb 22, 2016, 12:40:52 PM2/22/16
to golang-nuts
I'm having trouble calling sws_scale because I'm passing a reference to a Go pointer on line 143:


---
	var input_data [3]*C.uint8_t
	var input_linesize [3]C.int

	switch im := m.(type) {
	case *image.RGBA:
		bpp := 4
		input_data = [3]*C.uint8_t{ptr(im.Pix)}
		input_linesize = [3]C.int{C.int(im.Bounds().Dx() * bpp)}
	case *image.NRGBA:
		bpp := 4
		input_data = [3]*C.uint8_t{ptr(im.Pix)}
		input_linesize = [3]C.int{C.int(im.Bounds().Dx() * bpp)}
	default:
		panic("Unknown input image type")
	}

	// Perform scaling from input type to output type
	C.sws_scale(e._swscontext, &input_data[0], &input_linesize[0],
		0, e._context.height,
		&e._frame.data[0], &e._frame.linesize[0])
---

In the switch statement, I have tried several options to change input_data to avoid having the Go pointer to Go pointer.  The closest I have come, I think, is this:

---
       var input_data [3]*uint8                                                                                                                                                                                                   
        var input_linesize [3]C.int                                                                                                                                                                                                
                                                                                                                                                                                                                                   
        switch im := m.(type) {                                                                                                                                                                                                    
        case *image.RGBA:                                                                                                                                                                                                          
                bpp := 4                                                                                                                                                                                                           
                for i, _ := range im.Pix[:3] {                                                                                                                                                                                     
                        input_data[i] = &im.Pix[i]                                                                                                                                                                                 
                }                                                                                                                                                                                                                  
                input_linesize = [3]C.int{C.int(im.Bounds().Dx() * bpp)}                                                                                                                                                           
        case *image.NRGBA:                                                                                                                                                                                                         
                bpp := 4                                                                                                                                                                                                           
                for i, _ := range im.Pix[:3] {                                                                                                                                                                                     
                        input_data[i] = &im.Pix[i]                                                                                                                                                                                 
                }                                                                                                                                                                                                                  
                input_linesize = [3]C.int{C.int(im.Bounds().Dx() * bpp)}                                                                                                                                                           
        default:                                                                                                                                                                                                                   
                panic("Unknown input image type")                                                                                                                                                                                  
        }                                                                                                                                                                                                                          
                                                                                                                                                                                                                                   
        // Perform scaling from input type to output type                                                                                                                                                                          
        C.sws_scale(e._swscontext, (**C.uint8_t)(&input_data[0]), &input_linesize[0],                                                                                                                                              
                0, e._context.height,                                                                                                                                                                                              
                &e._frame.data[0], &e._frame.linesize[0])                                                                                                                                                                          
 ---

...but then go build gives the following error:

ffmpeg/ffmpeg.go:147: cannot convert &input_data[0] (type **uint8) to type **C.uint8_t

Am I approaching this the right way?

Ian Lance Taylor

unread,
Feb 22, 2016, 1:48:04 PM2/22/16
to Steven Edwards, golang-nuts
You can avoid that specific error easily enough by writing
(**C.uint8_t)(unsafe.Pointer(&input_data[0])).

But I don't understand what you are trying to do here. What Go 1.6
enforces is that you can not pass a Go pointer that contains a Go
pointer. In this case input_data is a Go array, so &input_data[0] is
a Go pointer. Passing &input_data[0] to C is only permitted if
input_data does not itself contain any Go pointers. If im.Pix is also
Go memory, then &im.Pix[i] is a Go pointer, and then you are passing a
Go pointer to memory that contains Go pointers, which is not
permitted. This is only OK if im.Pix is a C pointer.

I think the most likely way to make this pointer- and data-structure
heavy C code work with the Go garbage collector is to allocate more of
the memory in C.

Ian

Steven Edwards

unread,
Feb 22, 2016, 3:07:58 PM2/22/16
to golang-nuts, curead...@gmail.com
My goal is to pass an image's data, im.Pix, to sws_scale, which has the following function signature:

int sws_scale (struct SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[])

I can't figure out how to pass a go array (or slice) as the correct type.

Similarly, I could rewrite most of the code in C, but how would I reference the im.Pix data?

Best,

Steven 

Ian Lance Taylor

unread,
Feb 22, 2016, 4:12:37 PM2/22/16
to Steven Edwards, golang-nuts
On Mon, Feb 22, 2016 at 12:07 PM, Steven Edwards <curead...@gmail.com> wrote:
> My goal is to pass an image's data, im.Pix, to sws_scale, which has the
> following function signature:
>
> int sws_scale (struct SwsContext *c, const uint8_t *const srcSlice[], const
> int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const
> int dstStride[])
>
> I can't figure out how to pass a go array (or slice) as the correct type.

The C function expects a pointer to pointers for the srcSlice
argument. The inner pointers must be C pointers. There is no valid
way for them to be Go pointers.

Therefore, you must either copy im.Pix into C memory for this specific
call, or you must allocate it in C memory in the first place. I would
guess that the latter will give better performance.

Ian
Reply all
Reply to author
Forward
0 new messages