Go
golang
cgo

golangでffmpegやx264つかって変換するプログラムを書いてみる 2日目

概要

http://qiita.com/taktod/items/0d87e511388fde7ffe2d
これの続き2日目

建設地はこちら
https://github.com/taktod/ttLibGo

使い方は

$ go get github.com/taktod/ttLibGo

で取得できて

func TestAvcodecVideoDecode(t *testing.T) {
    {
        targetCodec := "h264"
        t.Log(targetCodec)
        in, err := os.Open(os.Getenv("HOME") + "/tools/data/source/test.h264.aac.mkv")
        if err != nil {
            panic(err)
        }
        var count uint32
        var reader ttLibGo.Reader
        if !reader.Init("mkv") {
            t.Errorf("reader初期化失敗")
        }
        defer reader.Close()

        var decoder ttLibGoFfmpeg.AvcodecDecoder
        defer decoder.Close()

        for {
            buffer := make([]byte, 65536)
            length, err := in.Read(buffer)
            if err != nil {
                panic(err)
            }
            if !reader.ReadFrame(
                buffer,
                uint64(length),
                func(frame *ttLibGo.Frame) bool {
                    if frame.CodecType == targetCodec {
                        decoder.InitVideo(targetCodec, frame.Width, frame.Height)
                        return decoder.Decode(
                            frame,
                            func(frame *ttLibGo.Frame) bool {
                                count++
                                return true
                            })
                    }
                    return true
                }) {
                t.Errorf("コンテナ読み込み失敗")
                return
            }
            if length < 65536 {
                break
            }
        }
        if count == 0 {
            t.Errorf("デコードされませんでした")
            return
        }
        t.Logf("デコードされた数:%d", count)
    }
}

こんな感じでフレームを取得して変換できます。

今日はひたすら実装してました。

注意:とりあえず書く動作のメモ書き書いておきますが、かならずしも正しいとは限りませんので、鵜呑みにしないでください。

ttLibGo

こちらは自分で全部書いた動作

Reader

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGo/test/reader_test.go

コンテナ読み込み動作
flv mpegts mp4 matroska webmの動作書いてあります。
ライブストリーミングで利用することを前提にしてます

Writer

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGo/test/writer_test.go

コンテナ書き出し動作
こっちもflv mpegts mp4 matroska webmの書き出し書いてあります。

hlsのための書き出しを実施したり、mp4データを吐き出してh264 / aacのデータをMediaSource extensionに流してvideoタグで動画を再生したりしてます。

AudioResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGo/testEx/audioResampler_test.go

signed int16で実装されているpcmS16とsigned float32で実装されているpcmF32の相互変換するリサンプラ

ImageResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGo/testEx/imageResampler_test.go

bgrとyuvの相互変換を実施するリサンプラ

ImageResizeResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGo/testEx/imageResizeResampler_test.go

yuvイメージデータの拡大縮小を実施するリサンプラ
3つとも質はよくないので、普通にswscaleResamplerとかswresampleResamplerとか使う方がいいと自分でも思います。

ttLibGoFaac

faacEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoFaac/test/faacEncoder_test.go

faacを使ってencodeするaacのencoder
とりあえずLowプロファイルで96kbpsで動作させれば、どこでも使えるaacができると思います。

ttLibGoFdkaac

fdkaacEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoFdkaac/test/fdkaacEncoder_test.go

なかなか質の良いaacのencoderらしいです。
利用できるプロファイルもたくさんあったはず。
とりあえずステレオとモノラルでそれっぽいaacができたら問題ないので・・・というコード書いてあります。
"AOT_AAC_LC"をプロファイルに設定して、やっぱりLowプロファイルでエンコードしておけば、間違いないかなと思います。
こちらは、SetBitrate関数を実装してあるので、中途でbitrateを変えたい場合は変更できるようにしてます。

ttLibGoFfmpeg

avcodecDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoFfmpeg/test/avcodecDecoder_test.go

ほぼなんでもエンコード・デコードできるffmpegのデコード動作を借りてデコードする動作
h264 flv1 vp6、aac mp3 nellymoserとrtmpの処理するのにとってもお世話になってます。

swresampleResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoFfmpeg/test/swresampleResampler_test.go

pcmのデータをいろいろ変換できるリサンプラ
ポテンシャルは高いのですが、まだ自分はそこまでお世話になってないリサンプラです。

swscaleResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoFfmpeg/test/swscaleResampler_test.go

映像データをいろいろ変換できるリサンプラ
僕の中でサーバーサイドで画像をいじるなら使えるんじゃないかな?と思ってるリサンプラ
少なくとも自分で書いたImageResizeResamplerよりは使えるはず。

ttLibGoJpeg

JpegDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoJpeg/test/jpegDecoder_test.go

libjpegをつかって動作するjpegのdecoder
まぁ、あまり使わない
あとyuv422やyuv444になるjpegデータは扱えません。
そのようなデータはffmpegのavcodecDecoderで処理してます。

JpegEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoJpeg/test/jpegEncoder_test.go

こっちは非常に有用
とにかく画像を保存したかったら、jpegにencodeしてframeからGetBinaryBufferを実施して[]byteデータをとってしまえば、あとはファイルに書き出すだけ
楽チン
ただ、コードを書いて実行すると大量の画像ができるのが玉に瑕。

ttLibGoMp3lame

Mp3lameDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoMp3lame/test/mp3lameDecoder_test.go

有名なmp3lameをつかって動作するmp3のデコーダー
ライセンス的にはこっちはGPLv3、エンコーダーだけだとLGPLv3
まぁ、v2の方かもしれない。
つかってもいいですが、avcodecで十分
なお、golangには実装してませんが、appleのAudioConverterやWindowsのMediaFoundationを使ってデコードするという選択肢もあります。
いずれにせよ、積極的にmp3lameでデコードする・・・ことはないんじゃないかな

Mp3lameEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoMp3lame/test/mp3lameEncoder_test.go

有名なmp3lameをつかって動作するmp3エンコーダー
mp3作りたかったら使う。
できたmp3のbinaryデータをそのまま順番にファイルに書き出せばmp3ファイルの一丁あがりとなります。

jpegと同じく単に音がどうなったか確認したいときに便利

ttLibGoOpenh264

Openh264Decoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoOpenh264/test/openh264Decoder_test.go

ciscoが出してくれたh264実装
ciscoが提供してるライブラリを直接DLして使わせればh264のパテントも対処してくれるという太っ腹なやつ。
あとsvcという規格のデータも積極的に対応していると思ってます。
bframe入りのh264が扱えなかったりと足りない部分もありますが、svcを使ったデータでffmpegのavcodecがデコードできなかったデータがデコードできたりということもありました。

まぁ、細かい動作したかったら今回のgoの実装では力不足なので、cでちゃんと書き直す必要があります。

Openh264Encoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoOpenh264/test/openh264Encoder_test.go

こちらはh264にencodeする実装
以前svcで複数の解像度のデータを同時に出力するとかであそんだことがあります。

まぁ、decoderと同じく、そういうことしたい場合は、このgoの実装では無理なので、それようにプログラムを書き直す必要があります。

なお、僕の実装として

  • 非常に細かい設定がmapを利用することで定義可能になっている
  • 画像をエンコードする前に該当画像をkeyFrameとして強制的に変換することが可能になっている
  • keyFrame間隔を任意のタイミングで変更できる。GOPが変更可能といった方がいいかもしれません。
  • RCモードの変更が可能(bitrate重視 -> quality重視)になっている

というのがあります。

ttLibGoOpus

OpusDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoOpus/test/opusDecoder_test.go

低遅延で高音質を実現する。すばらしい音声コーデック
ちょっと面白いコーデックでデコーダー側でサンプルレートとチャンネル数を指定して
どういうデータを出力させるか決めることができます。
48kHz 2chでエンコードしたけど、端末の処理能力が低いので
24kHz 1chでデコードして再生させるとかができます。

あと44.1kHzとか設定できません。

OpusEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoOpus/test/opusEncoder_test.go

こっちはそのopusのencoder

こっちが面白いのは処理単位のサンプル数を指定できるところですね。
サンプル数を小さくするとそれだけリアルタイムに処理できる能力があることになります。

普通は48kHzで、処理単位480でやるイメージがあります。

こちらにも特殊処理があり

  • SetBitrateで出力のbitrateを途中で変更する
  • SetComplexityで、内部の設定を変更できる

ようになってます。

ttLibGoSoundtouch

SoundtouchResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoSoundtouch/test/soundtouchResampler_test.go

soundtouchという音声の処理ライブラリを利用したリサンプラ
これを利用すると音の高さはそのままで早送りしたり
動作速度はそのままで音のpitchだけ上げ下げしたりとなかなか面白いことができます。

ttLibGoSpeex

SpeexDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoSpeex/test/speexDecoder_test.go

古いVoIPの処理でよく使われてたイメージのあるspeex
公式でもspeexよりopus使いましょうっていわれるようなコーデックです。
中身が3つのbandから構成されています。
narrow - wide - superwide
このコーデックで面白いところは、3つのbandから構成される32kHzの音声データのうち、narrowbandのところだけでもなんとか手に入れることができたら、(音質は悪くなりますし8kHzになりますが)ちゃんと音にデコードできるところが面白いです。

なお、モノラルのみ対応です。
ステレオのspeexも存在しますが、そちらはavcodecDecoderでデコードしてほしいところ

SpeexEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoSpeex/test/speexEncoder_test.go

そのspeexのencoder

ttLibGoSpeexdsp

SpeexdspResampler

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoSpeexdsp/test/speexdspResampler_test.go

そのspeexのおまけ実装にある音声のリサンプラ動作
他にエコーキャンセルとかもありますね。

これはお世話になりました。ライセンスもちょうどいいですからね。

ttLibGoTheora

TheoraDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoTheora/test/theoraDecoder_test.go

theoraによるdecoder実装
これはどちらかというとここで使うより、emscriptenでjavascript化してiOSのsafariとかで動作させると面白い。
まぁ、もうすぐiOSのsafariでwebrtc実装はいるので、そうなったらいらないか

TheoraEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoTheora/test/theoraEncoder_test.go

そのtheoraによるencode実装
これでencodeしてやって、できたフレームをwebsocketでiOSに送信、iOS側でdecodeしてcanvasに描画

するとめちゃくちゃ低遅延なライブ配信できたりします。
音声はopusかspeexでやれば良い感じにできました。

ttLibGoVorbis

VorbisDecoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoVorbis/test/vorbisDecoder_test.go

昔のwebmの音声をになってたやつ。
初めに送らないといけないデータが非常に大きいので、あまり好きではない。
まぁ、theoraもそういう点では同じだけど

VorbisEncoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoVorbis/test/vorbisEncoder_test.go

そのvorbisのencoder実装
まぁ、初期化実装にsampleRateとチャンネル数しかない時点で察してください状態です。

ttLibGoX264

X264Encoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoX264/test/x264Encoder_test.go

まぁ有名なやつですね。
このx264にはparam_parseという動作が実装されていて、文字列を割り当てることで任意のパラメーターの設定を更新することができるようになってます。
それに影響されて、openh264等の処理に同じようなものを書いてみました。

ttLibGoX265

X265Encoder

https://github.com/taktod/ttLibGo/blob/0b4f25daba09e171ea36d7a9df4525e3e0f39da3/ttLibGoX265/test/x265Encoder_test.go

こっちは最新のやつですね。
ただライセンスやパテントがアレなんで流行るのかな・・・

 まとめ

という感じで今日は書きまくってました。
明日はnode-gypの実装でも書きなおしておこうかな。