在go 的程序中调用 c 代码, golang 提供了两种方法: cgo, swing 。gstreamer 是开源跨平台的多媒体框架库,主要是在gnome 基础核心库 glib 之上构建。下面有一个简单的使用cgo 包装 gstreamer playbin 插件的例子: gstuse.go
package main
/*
#cgo pkg-config: gstreamer-1.0
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <gst/gst.h>
void play(const char *uri){
GstElement *pipeline;
GstBus *bus;
GstMessage *msg;
gst_init (NULL, NULL);
char *res;
if (-1 == asprintf(&res, "playbin uri=%s", uri)){
return;
}
pipeline = gst_parse_launch(res, NULL);
if(!pipeline){
g_print("create playbin pipeline error\n");
free(res);
return;
}
gst_element_set_state (pipeline, GST_STATE_PLAYING);
bus = gst_element_get_bus (pipeline);
msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_ERROR|GST_MESSAGE_EOS);
if (msg != NULL)
gst_message_unref (msg);
free(res);
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
}
*/
import "C"
import "unsafe"
func sndply(snd string){
if snd == ""{
return
}
cs := C.CString(snd)
defer C.free(unsafe.Pointer(cs))
C.play(cs)
}
把c 代码写在注释之中,很像python/c 之间的 cffi 工具, #cgo 是cgo 工具的编译指示标志, pkg-config 指定所需链接的库的元信息, 头文件,库位置等,
如果是平时跟c 代码链接 一般这样使用 pkg-config --cflags --libs gstreamer-1.0, cgo 中只需要写库名字 。playbin 是gstreamer 的高层插件, 提供媒体的播放功能, 它会自动建立媒体处理管线, 寻找合适的demuxer, decoder。
gst_parse_launch() 函数, 提供类似gst-lanch-×xx 命令行工具语法组建处理pipeline。 使用
gst_element_set_state
把pipeline 设置为启动播放状态,然后获取pipeline的总线, 在总线上等待直到遇到错误,或者媒体流的末尾。
C.CString 把 golang string 类型转换*C.char , 其是在heap 上分配, 需要调用C.free 释放。下面是使用金山词霸api 的例子:
gciba.go
package main
import (
"fmt"
"log"
"flag"
"sync"
"net/http"
"net/url"
"encoding/json"
)
const reqUri = "http://dict-co.iciba.com/api/dictionary.php?"
type Dict struct {
Exchange struct {
Word_done string `json:"word_done"`
Word_er []string `json:"word_er"`
Word_est []string `json:"word_est"`
Word_ing string `json:"word_ing"`
Word_past string `json:"word_past"`
Word_pl []string `json:"word_pl"`
Word_third string `json:"word_third"`
}`json:"exchange"`
Iscri int `json:"is_CRI"`
Items []string `json:"items"`
Symbols []struct{
Parts []struct{
Means []string `json:"means"`
Part string `json:"part"`
}`json:"parts"`
Ph_am string `json:"ph_am"`
Ph_am_mp3 string `json:"ph_am_mp3"`
Ph_en string `json:"ph_en"`
Ph_en_mp3 string `json:"ph_en_mp3"`
Ph_other string `json:"ph_other"`
Ph_tts_mp3 string `json:"ph_tts_mp3"`
}`json:"symbols"`
Word_name string `json:"word_name"`
}
func player(snds chan string, done chan bool){
for s := range snds {
sndply(s)
}
close(done)
}
func doTrans(word string, snds chan string) {
defer wg.Done()
reqArgs := url.Values{}
reqArgs.Add("w", word)
reqArgs.Add("type", "json")
reqArgs.Add("key", "E9E58402D44342EFB0B1ABC41E86BF8E")
resp, err := http.Get(reqUri + reqArgs.Encode())
if err != nil {
log.Println(err)
return
}
defer resp.Body.Close()
var res Dict
enc := json.NewDecoder(resp.Body)
enc.Decode(&res)
//fmt.Println(res)
var snd string
if len(res.Symbols) < 1 { return }
switch *phonetic {
case "am": snd = res.Symbols[0].Ph_am_mp3
case "en": snd = res.Symbols[0].Ph_en_mp3
default : snd = res.Symbols[0].Ph_tts_mp3
}
snds <- snd
fmt.Printf("am: %s, en: %s\n", res.Symbols[0].Ph_am,
res.Symbols[0].Ph_en )
for _, p := range res.Symbols[0].Parts {
fmt.Printf("%s %s\n", p.Part, p.Means)
}
fmt.Println()
}
var phonetic = flag.String("phon", "am", "use american or english phonetic")
var wg sync.WaitGroup
func main() {
flag.Parse()
snds := make(chan string, flag.NArg())
done := make(chan bool)
go player(snds, done)
for _, w := range flag.Args(){
wg.Add(1)
go doTrans(w, snds)
}
wg.Wait()
close(snds)
<-done
}
player 函数包裹了gstuse.go 中的 sndply ,使用单独的goroutine 调用 player函数,player 从 snds channel 读取声音uri,直到channel 被关闭,close done channel, 通知main goroutine, 自己要退出。doTrans 发起web request, 解析包含json的结果,提取声音uri 发到 snds channel。snds 是缓冲channel,也可改成无缓冲channel, 使得上一个单词未发音之前,不得打印下一个单词。
编译 : go build -o gciba gciba.go gstuse.go
./gciba clear some thing chrome
am: kroʊm, en: krəʊm
n. [谷歌浏览器 铬,铬合金]
vt. [镀以铬 用铬化合物印染]
am: θɪŋ, en: θɪŋ
n. [事件,形势 东西,事物 家伙 事业]
am: sʌm, en: səm
adj. [一些 某个 大约 相当多的]
pron. [一些 若干 其中的一部分 (数量不确切时用)有些人]
adv. [非常 相当 <美>稍微]
am: klɪr, en: klɪə(r)
adj. [清楚的,明白的 清晰的,明亮的 清澈的 明确的]
adv. [完全地 清晰地 整整]
vi. [变明朗 变清澈]
vt. [扫除,除去 消除(嫌疑) 使清楚 使干净]
n. [空隙,空间]
cgo 调用也存在以些问题,由于所调用的c 库由于设计上的问题, 使用 thread local storage或者某些api 不是thread safe ,导致不能并发使用,或者要锁在特定线程之上。 minux 指出从 golang1.0 到 1.4 cgo 调用耗时越来越长。