Я пишу приложение для платформы Windows, использующее FFmpeg, и это gavang wrapper goav, но мне трудно понять, как использовать указатели C для получения доступа к массиву.
Я пытаюсь записать данные фрейма, на которые указывает указатель uint8 из C, в файл .ppm в golang.
Как только я это сделаю, для подтверждения концепции, что FFmpeg делает то, что я ожидаю, я хочу установить кадры на текстуру в OpenGl, чтобы сделать видеопроигрыватель с классными переходами; любые указатели, чтобы сделать это красиво и эффективно, были бы очень полезны! Я предполагаю, что мне нужно написать некоторый код шейдера, чтобы нарисовать ppm в качестве текстуры ...
Файловая структура PPM выглядит довольно просто, просто заголовок, а затем байт данных для каждого красного, зеленого и синего значения каждого пикселя в кадре сверху вниз слева направо
Я начинаю понимать, как приводить указатели между типами 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)))
}
Код моего Голанга
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, но я получаю сбой, поэтому он должен получить доступ к некоторым случайным данным
Обновление: Мое исправление с использованием функции 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"
Я обновил файл avutil в пакете goav с помощью этой функции сохранения вверху, затем передал ему контекст фрейма, чтобы он мог получить от него указатель данных. Я также добавил эту функцию 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))
}