如何使用指向C数据的指针在Golang中写入文件?

时间:2018-04-24 20:51:08

标签: c go ffmpeg cgo

我正在使用FFmpeg及其golang包装goav为Windows平台编写应用程序,但我无法理解如何使用C指针获取对数组的访问权限。< / p>

我试图将来自C的uint8指针指向的帧数据写入golang中的.ppm文件。

一旦我完成了这一点,为了证明FFmpeg正在做我期望的概念,我想在OpenGl中将帧设置为纹理,以使视频播放器具有很酷的过渡效果;任何指向做得好而有效的指针都会非常有帮助!我猜我需要写一些着色器代码来绘制ppm作为纹理......

PPM file structure看起来非常简单只是标题,然后是从左上角到右下角的框架中每个像素的每个红色,绿色和蓝色值的数据字节

我开始理解如何在C和Go类型之间转换指针,但是如何访问数据并在Go中写入与C相同的结果?在C中,我只需要为数据设置指针偏移量并说明要写入多少:

for (y = 0; y < height; y++) {
    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
}

我已经删除了C代码的所有相关部分,包装器和我的代码,如下所示:

C代码 - libavutil / frame.h

#include <stdint.h>

typedef struct AVFrame {
#define AV_NUM_DATA_POINTERS 8
    uint8_t *data[AV_NUM_DATA_POINTERS];
    int linesize[AV_NUM_DATA_POINTERS];
}

Golang goav wrapper

package avutil

/*
    #cgo pkg-config: libavutil
    #include <libavutil/frame.h>
    #include <stdlib.h>
*/
import "C"
import (
    "unsafe"
)

type Frame C.struct_AVFrame

func Data(f *Frame) *uint8 {
    return (*uint8)(unsafe.Pointer((*C.uint8_t)(unsafe.Pointer(&f.data))))
}
func Linesize(f *Frame) int {
    return int(*(*C.int)(unsafe.Pointer(&f.linesize)))
}

我的Golang代码

package main

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

func saveFrame(videoFrame *avutil.Frame, width int, height int, iFrame int) {
    var szFilename string
    var y int
    var file *os.File
    var err error

    szFilename = ""

    // Open file
    szFilename = fmt.Sprintf("frame%d.ppm", iFrame)

    if file, err = os.Create(szFilename); err != nil {
        log.Println("Error Reading")
    }

    // Write header
    fh := []byte(fmt.Sprintf("P6\n%d %d\n255\n", width, height))
    file.Write(fh)
    var b byte = 0
    // Write pixel data
    for y = 0; y < height; y++ {
        d := avutil.Data(videoFrame) // d should be a pointer to the first byte of data
        l := avutil.Linesize(videoFrame)

        // I'm basically lost trying to figure out how to correctly write
        // this to a file, the file is created, but when I open it in GIMP
        // the image is mostly black with glitchy fuzz - so it's not being
        // written properly; the header seems to be ok, it knows the height
        // and width at least.

        data := make([]byte, l*3)

        ptr := unsafe.Pointer(d)
        for i := 0; i < l; i++ {
            datum := (*uint8)(unsafe.Pointer(uintptr(ptr) + (uintptr(i)+(uintptr(y)*uintptr(l)))*unsafe.Sizeof(*d)))
            data = append(data, *datum)
            //fmt.Println(*datum)
        }

        n, err := file.Write(data)
        if err != nil {
            log.Println("Error Writing:", szFilename, "-", n)
        }
    }

    file.Close()
}

那么,如何使用指向数据的指针写入文件,就像在C中一样,并获得相同的结果?

第一帧应该是黑色的,所以0&0 39;但我得到一个毛躁的模糊,所以它必须访问一些随机数据

更新:使用C函数保存我的修复:

package avutil

/*
    #cgo pkg-config: libavutil
    #include <libavutil/frame.h>
    #include <stdlib.h>
    #include <stdio.h>

    void SaveFrame(const char* location, AVFrame *pFrame, int width, int height) {
        FILE *pFile;
        int  y;

        // Open file
        pFile=fopen(location, "wb");
        if(pFile==NULL)
            return;

        // Write header
        fprintf(pFile, "P6\n%d %d\n255\n", width, height);

        // Write pixel data
        for(y=0; y<height; y++)
            fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

        // Close file
        fclose(pFile);
    }
    uint8_t* GetData(AVFrame *pFrame) {
        return pFrame->data[0];
    }
*/
import "C"

我在goav包装器包中更新了avutil文件,顶部有这个save函数,然后将帧上下文传递给它,这样它就可以从中获取数据指针。我还将此Go函数添加到该avutil文件以调用C函数

func SaveFrame(location string, f *Frame, width int, height int) {
    csLoc := C.CString(location)
    C.SaveFrame(csLoc, (*C.struct_AVFrame)(unsafe.Pointer(f)), C.int(width), C.int(height))
    C.free(unsafe.Pointer(csLoc))
}

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题: C函数分配了空间,并提供了指向该空间的指针以及要读取的大小。 我通过以下方法解决了这个问题:

nodejs

这会将指针转换为内容地址处的Golang字节数组。 buffer := *(*[]byte)(unsafe.Pointer(&content)) 被来自buffer的数据填充,并且C函数处理了数据。