Как получить доступ к массиву указателей C из Golang

Я пишу приложение для платформы Windows, используя FFmpeg и его goav-оболочку golang, но у меня возникают проблемы с пониманием того, как использовать указатели C для получения доступа к массиву.

Я пытаюсь получить потоки, хранящиеся в классе AVFormatContext, для использования в движении и, в конечном итоге, добавить кадры в текстуру в OpenGl, чтобы сделать видеоплеер с классными переходами.

Я думаю, что понимание того, как преобразовать и получить доступ к данным C, значительно упростит кодирование.

Я удалил все соответствующие части кода C, оболочки и моего кода, как показано ниже:

Код C - libavformat/avformat.h

typedef struct AVFormatContext { 
    unsigned int nb_streams; 
    AVStream **streams; 
}

Обертка Golang Goav

package avutil

//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import (
    "unsafe"
)

type Context C.struct_AVFormatContext; 

func (ctxt *Context) StreamsGet(i uintptr) *Stream {
    streams := (**Stream)(unsafe.Pointer(ctxt.streams));
    // I think this is where it's going wrong, I'm brand new to this stuff 
    return (*Stream)(unsafe.Pointer(uintptr(unsafe.Pointer(streams)) + i*unsafe.Sizeof(*streams)));
}

Мой код Голанга

package main

import "github.com/giorgisio/goav/avformat"

func main() {
    ctx := &avformat.Context{} // the actual function to initiate this does an mallocz for the streams

    stream := ctx.StreamsGet(0)

    //do stuff with stream...
}

В C похоже, что мне просто нужно делать только streams[i], но это не сработает в go, поэтому я добавил функцию в оболочку, используя технику из моего вопроса здесь. Однако я не получаю данные; Похоже, я получаю указатель на случайное место в памяти. Итак, как я могу получить доступ к этим элементам из golang? Любые ресурсы тоже будут полезны; Я собираюсь инвестировать в это довольно много времени.


person nevernew    schedule 23.04.2018    source источник


Ответы (1)


Как вы заметили, проблема в следующем коде:

func (ctxt *Context) StreamsGet(i uintptr) *Stream {
    streams := (**Stream)(unsafe.Pointer(ctxt.streams));
    // I think this is where it's going wrong, I'm brand new to this stuff 
    return (*Stream)(unsafe.Pointer(uintptr(unsafe.Pointer(streams)) + i*unsafe.Sizeof(*streams)));
}

В коде переменная streams является двойным указателем, поэтому результатом добавления смещения к streams также является двойной указатель (т. е. тип **Stream). Но в ваших фрагментах оно приводится к *Stream, что неверно. Правильный код:

func (ctxt *Context) StreamsGet(i uintptr) *Stream {
    streams := (**Stream)(unsafe.Pointer(ctxt.streams))
    // Add offset i then cast it to **Stream
    ptrPtr := (**Stream)(unsafe.Pointer(uintptr(unsafe.Pointer(streams)) + i*unsafe.Sizeof(*streams)))
    return *ptrPtr
}

Дополнительное примечание.
Если вы хотите избежать арифметических действий с указателем на стороне Go, вы можете определить вспомогательную функцию для доступа к элементу указателя (т. е. потокам) в Сторона C следующим образом:

/*
void * ptr_at(void **ptr, int idx) {
    return ptr[idx];
}

struct AVStream * stream_at(struct AVFormatContext *c, int idx) {
    if (i >= 0 && i < c->nb_streams)
        return c->streams[idx];
    return NULL;
}
*/
import "C"
import (
    "unsafe"
)

type Context C.struct_AVFormatContext
type Stream C.struct_AVStream

func (ctx *Context) StreamAt(i int) *Stream {
    p := (*unsafe.Pointer)(unsafe.Pointer(ctx.streams))
    ret := C.ptr_at(p, C.int(i))

    return (*Stream)(ret)
}

func (ctx *Context) StreamAt2(i int) *Stream {
    ret := C.stream_at((*C.struct_AVFormatContext)(ctx), C.int(i))

    return (*Stream)(ret)
}

Вы можете выбрать либо функцию ptr_at, которая принимает общий (любой) двойной указатель в качестве аргумента, либо более конкретную функцию stream_at, которая принимает в качестве аргумента только указатель на AVFormatContext. Первый подход можно использовать для доступа к элементу из любого двойного указателя, такого как: AVProgram **, AVChapter ** и т. д. Более поздний подход предпочтительнее, если нам нужно реализовать дополнительную обработку, такую ​​как проверка границ.

person putu    schedule 24.04.2018
comment
Круто, функции ptr_at и stream_at выглядят так же. Как работает stream_at? В примере вы передаете ему AVFormatContext (ctx), а не AVStream (ctx.streams), он ищет потоки в этом классе или что-то в этом роде? - person nevernew; 24.04.2018
comment
@nevernew Извините, это была моя ошибка. Вы должны передать указатель на AVFormatContext в функцию stream_at. Я обновил объяснение. - person putu; 24.04.2018
comment
О, круто, я не знал о C.stream_at - person Thomas; 24.04.2018
comment
продолжение вопроса: stackoverflow.com/questions/50010555/ - person nevernew; 24.04.2018