本文主要实现编写 go 语言代码,去调用 mplayer 的 c 语言代码,将 .sub 字幕文件转成 .pgm 格式的图片文件。
相当于使用 go 来改写 这个代码 。
vobsub.go
内容如下:
package main
// mplayer 代码来源于 https://github.com/ruediger/VobSub2SRT/tree/master/mplayer
// 这个文件相当于用 go 改写示例 VobSub2SRT/src/vobsub2srt.c++
/*
#include <vobsub.h>
#include <stdlib.h>
#include <spudec.h>
#include <mp_msg.h>
*/
import "C"
import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"unsafe"
)
var subBaseNmae = flag.String("subName", "", "输入 .sub 字幕文件路径,需要去掉 .sub 和 .idx 文件后缀")
func main() {
flag.Parse()
if strings.TrimSpace(*subBaseNmae) == "" {
println("Please enter subfile")
os.Exit(1)
}
// C 中的 void * 对应 cgo 中的 unsafe.Pointer
var spu unsafe.Pointer
// 调用 C.free 时确保要引用 include <stdlib.h>
defer C.free(spu)
// void mp_msg_init(void);
C.mp_msg_init()
subBaseDir, _ := filepath.Abs(filepath.Dir(*subBaseNmae))
// char * 对应 cgo 中的 C.CString
subname := C.CString(*subBaseNmae)
// 对申请的非 go 类型,都要手动进行回收
defer C.free(unsafe.Pointer(subname))
// void *vobsub_open(const char *subname, const char *const ifo, const int force, unsigned int y_threshold, void** spu);
// void** 对应 cgo 中直接取 unsafe.Pointer 地址,即 &spu
vob := C.vobsub_open(subname, C.CString(""), C.int(1), C.uint(0), &spu)
defer C.free(vob)
var packet unsafe.Pointer
defer C.free(packet)
var timestamp, vlen C.int
var lastStartPts, width, height, stride, startPts, endPts C.uint
minWidth := 9
minHeight := 1
subCounter := 1
var image *C.uchar
defer C.free(unsafe.Pointer(image))
var imageSize C.size_t
for {
// int vobsub_get_packet(void *vobhandle, float pts, void** data, int* timestamp);
vlen = C.vobsub_get_next_packet(vob, &packet, ×tamp)
if vlen <= 0 {
break
}
if timestamp < 0 {
continue
}
// void spudec_assemble(void *self, unsigned char *packet, unsigned int len, int pts100);
// 将 unsafe.Pointer 类型的 packet 转成 unsigned char * --> (*C.uchar)(packet)
C.spudec_assemble(spu, (*C.uchar)(packet), C.uint(vlen), timestamp)
// void spudec_heartbeat(void *self, unsigned int pts100);
C.spudec_heartbeat(spu, C.uint(timestamp))
// void spudec_get_data(void *self, const unsigned char **image, size_t *image_size, unsigned *width, unsigned *height,
// unsigned *stride, unsigned *start_pts, unsigned *end_pts);
// image 上指向 C.uchar 的指针,所以 unsigned char **image 就只需要对 image 取地址操作
C.spudec_get_data(spu, &image, &imageSize, &width, &height, &stride, &startPts, &endPts)
if startPts == lastStartPts {
continue
}
lastStartPts = startPts
if int(width) < minWidth || int(height) < minHeight {
continue
}
// 这里如何将 *C.uchar 转成 go 的 []bytes
gobytes := C.GoBytes(unsafe.Pointer(image), C.int(imageSize))
fname := fmt.Sprintf("pgm_%d.pgm", subCounter)
fpath := filepath.Join(subBaseDir, fname)
f, _ := os.Create(fpath)
f.Write([]byte(fmt.Sprintf("P5\n%d %d %d\n", int(width), int(height), 255)))
f.Write(gobytes)
f.Close()
subCounter++
}
}
参考链接:
- https://karthikkaranth.me/blog/calling-c-code-from-go/
- http://litang.me/post/golang-cgo/
- https://golang.org/cmd/cgo/
- https://jamesadam.me/2016/03/26/c-and-go-dealing-with-void-parameters-in-cgo/
- https://github.com/golang/go/wiki/cgo