cgo 調用練習, 簡單愚蠢命令行詞典

在go 的程序中調用 c 代碼, golang 提供了兩種方法:   cgo, swing 。gstreamer 是開源跨平臺的多媒體框架庫,主要是在gnome 基礎核心庫 glib 之上構建。下面有一個簡單的使用cgo 包裝 gstreamer playbin 插件的例子: gstuse.gophp

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  指定所需連接的庫的元信息, 頭文件,庫位置等,python

若是是平時跟c 代碼連接 通常這樣使用 pkg-config --cflags --libs  gstreamer-1.0, cgo 中只須要寫庫名字 。playbin 是gstreamer 的高層插件, 提供媒體的播放功能, 它會自動創建媒體處理管線, 尋找合適的demuxer,  decoder。git

gst_parse_launch() 函數, 提供相似gst-lanch-×xx 命令行工具語法組建處理pipeline。 使用github

gst_element_set_state

把pipeline 設置爲啓動播放狀態,而後獲取pipeline的總線, 在總線上等待直到遇到錯誤,或者媒體流的末尾。golang

C.CString  把 golang  string 類型轉換*C.char , 其是在heap 上分配, 須要調用C.free  釋放。下面是使用金山詞霸api 的例子:web

gciba.gochrome

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, 使得上一個單詞未發音以前,不得打印下一個單詞。json

編譯 :    go build -o gciba gciba.go gstuse.goapi

./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 調用耗時愈來愈長。

相關文章
相關標籤/搜索