Go / Cgo - 如何访问 Cstruct 的字段?

Go / Cgo - How to access a field of a Cstruct?

我用 Go 开发了一个应用程序,用于将音频文件从一种格式转码为另一种格式:

我使用使用 Cgo 绑定 FFmpeg C-libs 的 goav 库: https://github.com/giorgisio/goav/


goav 库; package avformat 有一个转换原始 FFmpeg lib C-Struct AVOutputFormat 的 typedef:

type ( 
   OutputFormat               C.struct_AVOutputFormat
)

在我的代码中,我有一个名为 outputFOutputFormat 类型的变量,它是 C.struct_AVOutputFormat.

C 真正的 AVOutputFormat 结构有字段:

name, long_name, mime_type, extensions, audio_codec, video_codec, subtitle_codec,..

还有更多领域。

参见:https://ffmpeg.org/doxygen/2.6/structAVOutputFormat.html


我通过fmt.Println(outputF)验证了情况,达到:

{0x7ffff7f23383 0x7ffff7f23907 0x7ffff7f13c33 0x7ffff7f23383 86017 61 0 128 <nil> 0x7ffff7f8cfa0 <nil> 3344 0x7ffff7e3ec10 0x7ffff7e3f410 0x7ffff7e3ecc0 <nil> 0x7ffff7e3dfc0 <nil> <nil> <nil> <nil> <nil> <nil> 0 0x7ffff7e3e070 0x7ffff7e3e020 <nil>}

音频编解码器字段位于 5 位置并包含 86017

我使用包 reflect:

验证了字段名称
val := reflect.Indirect(reflect.ValueOf(outputF))
fmt.Println(val)
fmt.Println("Fieldname: ", val.Type().Field(4).Name)

Output:
Fieldname:  audio_codec

我尝试使用以下方法访问原始 AVOutputFormat 的字段 audio_codec

fmt.Println(outputF.audio_codec)
ERROR: outputF.audio_codec undefined (cannot refer to unexported field or method audio_codec)


fmt.Println(outputF._audio_codec)
ERROR: outputF._audio_codec undefined (type *avformat.OutputFormat has no field or method _audio_codec)

As i read in the Cgo documentation: Within the Go file, C's struct field names that are keywords in Go can be accessed by prefixing them with an underscore: if x points at a C struct with a field named "type", x._type accesses the field. C struct fields that cannot be expressed in Go, such as bit fields or misaligned data, are omitted in the Go struct, replaced by appropriate padding to reach the next field or the end of the struct.

但我不知道我做错了什么。

编辑: 好的,可以肯定不需要下划线,因为 audio_codec 不是 Go 中的关键字。我现在明白了。但是仍然有一个问题,为什么我无法访问 CStruct 字段“audio_codec”。

您在这里遇到了 GO/CGO 的一些特质:

type OutputFormat C.struct_AVOutputFormat 是一个 go type declaration,不是别名。将其视为薄包装而不是别名可能会有所帮助。因此 OutputFormat != C.struct_AVOutputFormat 和 C.struct_AVOutputFormat 的字段不会导出,这就是为什么你得到 ERROR: outputF.audio_codec undefined (cannot refer to unexported field or method audio_codec)

如果该字段被称为 Audio_codec,它将符合 go 对 exported identifier 的定义,我们可以访问它,但事实并非如此。

有一种方法可以解决这个问题,但我建议在继续使用之前三思而后行,因为它使用不安全的指针,并且您的程序可能会在运行时失去可移植性 and/or 稳定性。 This is a good intro to unsafe pointers 如果您想了解更多。

现在,如果您真的确定要这样做,解决方案是将指向 OutputFormat 的指针转换为不安全指针,然后将其转换为指向 C.struct_AVOutputFormat 的指针。请注意,这需要您加载 FFMPEG headers 以获取 C.struct_AVOutputFormat

的定义
//#cgo pkg-config: libavformat
//#include <libavformat/avformat.h>
import "C"
import (
    "fmt"
    "unsafe"
    "github.com/giorgisio/goav/avformat"
)

func getOutput() *avformat.OutputFormat {
  // calls out to avformat and gets an OutputFormat back
}

func main() {
    outputF := getOutput()
    coutput := *(*C.struct_AVOutputFormat)(unsafe.Pointer(outputF))

    fmt.Println(coutput.audio_codec) // This should now work
}

警告:我没有测试过 cgo 包配置和 <libavformat/avformat.h> 导入是否正确,但这适用于我站起来尝试的简单 C 库。