Cのポインター型のサイズをgolangで取得する

TL;DR

0をuintptrにキャストして使用する

unsafe.Sizeof(uintptr(0))

mallocに渡す

ptr := (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(uintptr(0)))))

モチベーション

cgoでコードを書いていて、ポインターのサイズ分、マロックしたい。

公式 cgoによると、C.sizeof_Tにてサイズが取得できるとあるが、 ポインター型に対する記述がない。

ポインター型であるため以下のようにuintptrで確保すればいいと考えたが、コンパイルエラーになる

# compile error
ptr := (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(uintptr))))

# type uintptr is not an expression

解決策

0をuintptrにキャストして使用する

他の解決策

事前に変数を宣言することによっても可能。

ただcで書かれたライブラリにより変数が確保されている場合は、 mallocのサイズ取得のためにしか使用せず冗長になるため避けたいところ。

var charptr *C.char
charptr2 := (**C.char)(C.malloc(C.size_t(unsafe.Sizeof(charptr))))

cgoでvoid **(ダブルポインター)を使う

TL;DR;

void **はどうやって表現する? *unsafe.Pointer .

void *は? unsafe.Pointer .

example

var u8v **C.uint8_t
var doubleVoidPointer *unsafe.Pointer
u8v = (**C.uint8_t)(C.malloc(size))
doubleVoidPointer = (*unsafe.Pointer)(unsafe.Pointer(u8v))
free(unsafe.Pointer(u8v))

failure example

// `()`をつけ忘れている
u8v = (**C.uint8_t)C.malloc(size)

キャスト時にカッコ()をつけ忘れると、expected ';', found C といったエラーが出る

Ref

理解できない場合は以下2つの公式ドキュメントを読むと良い ただしここでは翻訳したurlとする

xn--go-hh0g6u.com

kawawa.hatenablog.com

以下ポエム

GCがあることにより困るのは以下の2点

  • GCによる変数の格納場所の入れ替え(以降moveとする)
  • 未参照時にfreeされる

この2つの特徴を混ぜていて、理解するのに時間がかかった。 そのため明確に分けると良い。

uintptr

uintptrはポインタを格納することのできる大きさを持った整数

unsafe.Pointerはポインタ

  • moveによる影響

uintptrが指す先はあくまで整数であるため、moveが発生しても変化しない

(int型の値はgcが発生してもint型の値)

  • 未参照時のfree

ただし参照されなくなると、freeされてしまう

uintptr型の数値は解放されないが、uintptrが示すアドレスの先に、Goポインターがあると、解放される

GCはCPointerを勝手に解放しない=GCはCPinterをGC対象としない

CはCのプログラム設計者の意図でメモリ管理されているため、そこにGCが介入すると破綻するため

move時のpointer

move時はアドレスが変わる。

そのためポインターを保持していると、ポインターの書き換えが起こる。

疑問

Cポインターのアドレスが書き換えられると困らない?

こまる。そのためCポインターは書き換えが発生しない(GC管理対象外)。

CポインターをGoポインターに保持する場合は困らない?

例えば以下のような場合

stuct Piyo {
    int pon
}

struct Piyo* piyo_open(void) {
    p = (* struct Piyo)malloc(sizeof(struct Piyo))
    p.pon = 0
    return p
}

stuct Fuga {
    struct Piyo *piyo
}

struct Fuga* fuga_open(void) {
    f = (* struct Fuga)malloc(sizeof(struct Fuga))
    f.piyo = piyo_open(void)
    return f
}
type Hoge struct {
   fuga *C.Fuga
}

func main() {
    hoge := &Hoge{}

    hoge.fuga = C.fuga_open()
}

hogeはGoPointer GC対象 hoge.fugaはCPointer GC対象外

hogaのアドレスが変わっても、hogeの中身のfugaはGC対象外だから、そのままの値が維持される

つまり困らない

unsafe.Pointer

こちらはあくまでポインター

ダブルポインターの要素の参照。後で書く

[] indexは使えないため、uintptrに変換してアクセスする

struct Hoge {
   char *x
};
    for i := 0; i < int(num); i++ {
        p := *(**C.Hoge)(unsafe.Pointer(uintptr(unsafe.Pointer(xxx)) + uintptr(i)*unsafe.Sizeof(uintptr(0))))
        fmt.Println(i, C.GoString(p.x))
    }

unsafe 翻訳

pkg.go.dev

Ver1.17

概要

unsafeパッケージには、Goプログラムの型安全性を回避する操作が含まれています。

unsafeをインポートしたパッケージは移植不可能な場合があり、Go 1の互換性ガイドラインでは保護されません。

関数

type Pointer

type Pointer *ArbitraryType

Pointerは、任意の型へのポインタを表します。Pointer型では、他の型にはない4つの特別な操作が可能です。

- 任意の型のポインタ値をPointerに変換することができます。
- Pointerは、任意の型のポインタ値に変換できます。
- uintptrはPointerに変換できます。
- Pointerはuintptrに変換できます。

そのため、ポインターを使うと、プログラムが型システムを破り、任意のメモリを読み書きできるようになります。使用には細心の注意が必要です。

Pointer を含む以下のパターンは有効です。これらのパターンを使用していないコードは、現在無効であるか、将来的に無効になる可能性があります。以下の有効なパターンにも重要な注意点があります。

"go vet"を実行すると、これらのパターンに適合しないPointerの使い方を見つけることができますが、"go vet "が沈黙していても、コードが有効であることを保証するものではありません。

(1) T1をT2へのPointerに変換する。

T2がT1よりも大きくなく、2つのメモリレイアウトが同等であることを条件に、この変換により、ある型のデータを別の型のデータとして再解釈することができます。例として,math.Float64bitsの実装があります。

func Float64bits(f float64) uint64 {
    return *(*uint64)(unsafe.Pointer(&f))
}

(2)Pointerからuintptrへの変換(Pointerには戻らない)。

Pointerをuintptrに変換すると、指し示された値のメモリアドレスが整数として生成されます。このようなuintptrの通常の用途は、それを表示(プリント)することです。

uintptrをPointerに戻す変換は、一般的には有効ではありません。

uintptrは整数であり、参照ではありません。Pointerをuintptrに変換すると、ポインタのセマンティクスを持たない整数値が作成されます。uintptrがあるオブジェクトのアドレスを保持していても、オブジェクトが移動してもガベージコレクタはuintptrの値を更新しませんし、uintptrはオブジェクトの再取得を防ぎません。

残りのパターンは,uintptrからPointerへの唯一の有効な変換を列挙したものです。

(3) Pointerからuintptrへの変換と、その逆の変換(算術付き)。

p が割り当てられたオブジェクトを指している場合,uintptr への変換,オフセットの追加,Pointer への変換で,オブジェクトを進めることができます。

p = unsafe.Pointer(uintptr(p) + offset)

このパターンの最も一般的な使い方は、構造体のフィールドや配列の要素にアクセスすることです。

// equivalent to f := unsafe.Pointer(&s.f)
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))

// equivalent to e := unsafe.Pointer(&x[i])
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

この方法では、ポインタにオフセットを加えることも、ポインタからオフセットを引くことも可能です。また、通常はアライメントのためにポインタを丸めるために&^を使用することも有効です。いずれの場合も、結果は割り当てられた元のオブジェクトを指し続けなければなりません。

C言語とは異なり、ポインターを元の割り当ての終わりを超えて進めることはできません。

// INVALID: end points outside allocated space.
var s thing
end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))

// INVALID: end points outside allocated space.
b := make([]byte, n)
end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))

両方の変換は,それらの間に算術を介在させるだけで,同じ式の中に現れなければならないことに注意してください。

// INVALID: uintptr cannot be stored in variable
// before conversion back to Pointer.
u := uintptr(p)
p = unsafe.Pointer(u + offset)

なお、ポインタは割り当てられたオブジェクトを指していなければならないので、nilではないかもしれません。

// INVALID: conversion of nil pointer
u := unsafe.Pointer(nil)
p := unsafe.Pointer(uintptr(u) + offset)

(4) syscall.Syscallを呼び出す際のPointerからuintptrへの変換。

パッケージsyscallに含まれるSyscall関数は、そのuintptr引数をオペレーティングシステムに直接渡しますが、オペレーティングシステムは、呼び出しの詳細に応じて、そのうちのいくつかをポインタとして再解釈することがあります。つまり、システムコールの実装は、暗黙のうちに特定の引数をuintptrからポインタに変換し直しているのです。

ポインタの引数を引数として使うためにuintptrに変換しなければならない場合は、その変換は呼び出し式自体に現れなければなりません。

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

コンパイラは、アセンブリで実装された関数の呼び出しの引数リストにuintptrに変換されたPointerがある場合、型だけを見るとオブジェクトが呼び出し中に不要になったように見えても、参照された割り当てオブジェクトがあれば、呼び出しが完了するまで保持して移動しないように手配することで処理します。

コンパイラがこのパターンを認識するためには,引数リストに変換が現れなければならない。

// INVALID: uintptr cannot be stored in variable
// before implicit conversion back to Pointer during system call.
u := uintptr(unsafe.Pointer(p))
syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

(5) reflect.Value.Pointerまたはreflect.Value.UnsafeAddrの結果のuintptrからPointerへの変換。

パッケージreflectのValueメソッドであるPointerとUnsafeAddrは、unsafe.Pointerの代わりにuintptr型を返すことで、呼び出し側が最初に "unsafe "をインポートせずに結果を任意の型に変更できないようにしています。しかし、これは結果が壊れやすいことを意味し、呼び出した直後に同じ式の中でPointerに変換する必要があります。

p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))

上記の場合と同様に,変換前の結果を保存することは無効です。

// INVALID: uintptr cannot be stored in variable
// before conversion back to Pointer.
u := reflect.ValueOf(new(int)).Pointer()
p := (*int)(unsafe.Pointer(u))

(6) reflect.SliceHeaderまたはreflect.StringHeader DataフィールドのPointerへの変換、またはPointerからの変換。

前述のケースと同様に、reflectのデータ構造であるSliceHeaderとStringHeaderは、フィールドDataをuintptrとして宣言し、呼び出し側が最初に "unsafe "をインポートすることなく、結果を任意の型に変更できないようにしています。しかし、これはSliceHeaderとStringHeaderが実際のスライスや文字列の値の内容を解釈する場合にのみ有効であることを意味します。

var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)
hdr.Len = n

この使い方では、hdr.Dataは、実際には、文字列ヘッダ内の基礎となるポインタを参照するための代替手段であり、uintptr変数そのものではありません。

一般的に、reflect.SliceHeaderとreflect.StringHeaderは、実際のスライスや文字列を指すreflect.SliceHeaderとreflect.StringHeaderとしてのみ使用されるべきであり、決して単純な構造体として使用されるべきではありません。プログラムでは、これらの構造体タイプの変数を宣言したり、割り当てたりしてはいけません。

// INVALID: a directly-declared header will not hold Data as a reference.
var hdr reflect.StringHeader
hdr.Data = uintptr(unsafe.Pointer(p))
hdr.Len = n
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost

内部でasync を呼ぶ関数はなぜasync defと宣言する必要があるのか?

pythonでasync/awaitを使ってコードを書くと、

async def はコルーチンの定義と書いてある。 コルーチンは作りすぎると分岐予測に失敗しパフォーマンスが落ちるため、コルーチンを作るのは最小に抑えた方がいいよね、と考えた。

その結果まず以下の様なコードを考えた。 func_with_io_inside()にはasync defをつけず、非コルーチンとするもの。 しかしこのコードはエラーになる。

import asyncio


async def iofunc():
    print("iofunc")
    # exe io
    await asyncio.sleep(1)


def func_with_io_inside():
    print("func_with_io_inside")
    await iofunc()


async def main():
    await func_with_io_inside()


asyncio.run(main())

"await" allowed only within async functionとなってしまう。 動く様に書き直したものを以下に示す。 func_with_io_inside()にもasync defをつけ、その呼び出しもawaitする様に変更

import asyncio


async def iofunc():
    print("iofunc")
    # exe io
    await asyncio.sleep(1)


async def func_with_io_inside():
    print("func_with_io_inside")
    await iofunc()


async def main():
    await func_with_io_inside()


asyncio.run(main())

awaitでコルーチンを待つためには外側をasync defでコルーチンを定義する必要がある。 これではコルーチン増えてパフォーマンスでないと思った。

ただ、コルーチンが必要となる多くがIO待ちが発生する時で、その場合コルーチンの切り替えコストはIO待ちに比べて遥かに低いため大きな問題にはならないらしい。 妥当な選択でpythonらしいと思う。 ただ好きではない。

他にもasync defを必須とするのは呼び出し側でawait忘れを防止するためもある。

以下を参照した。

stackoverflow.com

lukasa.co.uk

Low-Latency HLSを有効化する

以下の翻訳 developer.apple.com

概要

HTTP Live Streaming (HLS) プロトコルは、ライブおよびオンデマンドのコンテンツストリームを世界中の視聴者に配信します。HLSはこれまで、レイテンシーよりもストリームの信頼性を重視してきました。低遅延HLSは、プロトコルを拡張し、スケーラビリティを維持しながら低遅延のビデオストリーミングを可能にします。新しい低遅延モードでは、公共ネットワーク上でのビデオの遅延が、標準的なテレビ放送の範囲内にまで低減されます。

バックエンドの制作ツールやコンテンツ配信システムは、低遅延ストリームの再生を可能にするために、新しいルールを実装する必要があります。ローレイテンシーHLSは、これらの分野で新たな機能を提供します。

  • Partial Segmentsの生成
  • PlaylistのDelta Updates
  • プレイリストの再読み込みのブロック
  • Preload hints とメディアダウンロードのブロック
  • Rendition Reports

HLSの仕様では、draft-pantos-hls-rfc8216bis revision 7以降でLow-Latency extensionsが定義されています。

Partial Media Segmentsを生成する

低遅延HLSは、メディアプレイリストのライブエッジでメディアを配信するためのパラレルチャンネルを提供し、メディアをCMAFチャンクのようなより小さなファイルに多数分割します。これらの小さいファイルをHLSのパーシャルセグメントと呼びます。各パーシャルセグメントの再生時間は短いので、親セグメントよりもずっと早くパッケージ化して公開し、メディアプレイリストに追加することができます。通常のメディアセグメントは1つ6秒ですが、パーシャルセグメントの例では200ミリ秒です。最初の部分セグメントは、前のセグメントがパブリッシュされてからわずか200ミリ秒後にパブリッシュされ、続いて29個の同一セグメントがパブリッシュされ、最後に30個の部分セグメントを連結したものと同じメディアを含む通常の長さの6秒のメディアセグメントがパブリッシュされます。プレイリストの肥大化を抑制するため、サーバーは、ライブエッジからターゲットデュレーションが3つ以上経過したパーシャルセグメントをメディアプレイリストから削除します。

パーシャルセグメントをメディアプレイリストに追加するには、新しいEXT-X-PARTタグを使用します。親セグメントの境界には、他のメディアセグメントタグ(EXT-X-DISCONTINUITYなど)を配置できます。

パーシャルセグメントは、HLS仕様(3.1~3.5項)で規定されているSupported Media Segment Formatsのいずれかでなければなりません。

PlaylistのDelta Updatesを提供する

低遅延HLSでは、クライアントがプレイリストを頻繁に転送します。クライアントは、この転送コストを削減するために、プレイリストのデルタアップデートを要求し、サーバーはそれを提供することができます。これらのアップデートは、クライアントが既に持っているプレイリストのかなりの部分を、新しいEXT-X-SKIPタグで置き換えます。

パーシャルセグメントの詳細については、HLSの仕様書の6.2.5.1項を参照してください。

Block Playlist Reload

新しいメディアセグメントとパーシャルセグメントの効率的なクライアントへの通知をサポートするために、低遅延HLSはプレイリストのリロードリクエストをブロックする機能を導入しました。クライアントがメディアプレイリストの更新を要求するためにHTTP GETを発行する際、クライアントは配信指示子と呼ばれる特別なクエリパラメータを追加して、プレイリストの応答に将来のセグメントを含めることを指定できます。サーバーは、そのセグメントを含むプレイリストのバージョンが利用可能になるまで、リクエストを保留します(ブロック)。プレイリストのリロードをブロックすると、プレイリストのポーリングが不要になります。

プレイリストのリロードをブロックする機能の詳細は、HLS仕様書の6.2.5.2項を参照してください。

Preload Hints and Blocking of Media Downloads

低遅延のストリームをグローバルに配信する場合、不要なラウンドトリップをなくすことが重要です。サーバーは、新しいタグであるEXT-X-PRELOAD-HINTを使用して、パーシャルセグメントやメディア初期化セクションの予定をクライアントに通知します。クライアントは、ヒントとなるリソースに対して事前にGETリクエストを発行することができ、サーバーは、メディアが利用可能になるとすぐにリクエストに応答します。

プリロードヒントの詳細については、HLS仕様書の6.2.6項を参照してください。

Provide Rendition Reports

低遅延で再生する場合、ビットレート適応を行うためには、クライアントは最小限のラウンドトリップでレンディションを切り替えられる必要があります。これをサポートするために、サーバーはマスタープレイリスト内の他のレンディションに関するレンディションレポートを各メディアプレイリストに追加します。EXT-X-RENDITION-REPORTタグは、レンディションレポートを伝送し、そのレンディションのメディアプレイリストに含まれる最後のメディアシーケンス番号やパートなどの情報を提供します。

Rendition Report の詳細については、HLS 仕様書の 4.4.5.1.4 項を参照してください。

Add Low-Latency HLS Delivery Directives

HLSでは、プレイリストのGETリクエストのURLに追加できる特別なクエリパラメータであるDelivery Directivesが定義されています。これらの配信指示子には以下のものがあります。

  • _HLS_msn=<M>

メディアシーケンス番号が "M "以降のメディアセグメントがプレイリストに含まれるまで、サーバがリクエストを保留することを示します。_HLS_msnの詳細については、HLS仕様書の6.2.5.2項を参照してください。

  • _HLS_part=<N>

HLS_msnとの組み合わせで、プレイリストにメディアシーケンス番号M以降のパーシャルセグメントNが含まれるまで、サーバーがリクエストを保留することを示す。1つのセグメントの最初のPartial SegmentはHLS_part=0、2つ目はHLS_part=1、というようになります。また、HLS_partパラメータには、_HLS_msnパラメータが必要です。

  • _HLS_skip=YES|v2

プレイリストのデルタアップデートを要求します。プレイリストの前の部分がEXT-X-SKIPタグで置き換えられます。HLS_skipの詳細については、HLS仕様書の6.2.5.1項を参照してください。

New Media Playlist Tags for Low-Latency HLS

Low-Latency HLSに対応するため、以下のPlaylistタグが追加されました。

EXT-X-SERVER-CONTROL

EXT-X-SERVER-CONTROLタグは、サーバが「プレイリストの再読み込みの禁止」や「プレイリストの差分更新」などの機能をサポートしていることを示すためのものです。EXT-X-SERVER-CONTROLタグの詳細については、HLS仕様書の4.4.3.8項を参照してください。

EXT-X-PART-INF

EXT-X-PART-INFは、プレイリスト内のHLSパーシャルセグメントに関する情報を提供します。EXT-X-PART-INFタグの詳細については、HLS仕様書の4.4.3.7項を参照してください。

EXT-X-PART

EXT-X-PARTは、プレイリスト内のパーシャルセグメントを識別します。EXT-X-PARTタグの詳細については、HLS仕様書の4.4.4.9項を参照してください。

EXT-X-PRELOAD-HINT

EXT-X-PRELOAD-HINTタグは、プレゼンテーションの今後の部分を再生するために、リソースまたはリソースのバイト範囲が必要であることを示唆します。 ヒントとなったPartial Segmentが、最終的にEXT-X-PARTタグとしてプレイリストに表示される際には、以前のPartial Segmentとは異なる可能性があることに注意してください。不連続性シーケンス番号、メディア初期化セクション、暗号化構成などが異なる可能性があります。つまり、Partial Segmentの前には、前のParent Segmentの終わりを示すEXTINFタグと、EXT-X-DISCONTINUITY、EXT-X-MAP、EXT-X-KEYのいずれかのタグを付けることができます。

サーバーは、広告からの早期回収など、計画されたセグメンテーションが変更された場合には、以前にヒントとなったPartial Segmentを公開しないこともできます。

EXT-X-PARTタグの詳細については、HLS仕様書の4.4.5.1.3項を参照してください。

EXT-X-RENDITION-REPORT

EXT-X-RENDITION-REPORTは、関連するレンディションについて、それを含むプレイリストと同じくらい最新の情報を伝えます。EXT-X-RENDITION-REPORTタグの詳細については、HLS仕様書の4.4.5.1.4項を参照してください。

EXT-X-SKIP

サーバがプレイリストデルタアップデートを発行する際には、スキップバウンダリより前のメディアセグメン トとそれに関連するタグを EXT-X-SKIP タグで置き換えます。EXT-X-SKIPタグの詳細については、HLS仕様書のセクション4.4.5.1.2および6.2.5.1を参照してください。

Low-Latency Server Configuration Profileへの対応

メディアのタイムリーな配信をサポートするために、低遅延HLSでは、通常のHLSで必要とされる以上の特定のトランスポート機能が必要となります。これらの要件は、低遅延サーバー構成プロファイルで定義されています。低遅延HLSの構文は、既存のHLSとの下位互換性があるため、クライアントは、サーバが必要な構成の一部をサポートしていないことが判明した場合、通常の遅延HLSの再生にフォールバックします。HLS仕様では、Appendix Bで低遅延サーバ構成プロファイルを定義しています。

Expect Delivery from CDN Tune-In

低遅延HLSのプレーヤーは、CDNやその他のHTTPキャッシュを介した低遅延ストリームの配信を期待する必要があります。低遅延で再生を開始するためには、クライアントはまず、メディアプレイリストの適度な最新版を取得する必要があります。HLS仕様の付録Cには、HTTPキャッシュを通じて提供される最新のメディアプレイリストを取得するための効率的な方法が記載されています。

Example: Low-Latency HLS Playlist

#EXTM3U
# This Playlist is a response to: GET https://example.com/2M/waitForMSN.php?_HLS_msn=273&_HLS_part=2
#EXT-X-TARGETDURATION:4
#EXT-X-VERSION:6
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.0,CAN-SKIP-UNTIL=12.0
#EXT-X-PART-INF:PART-TARGET=0.33334
#EXT-X-MEDIA-SEQUENCE:266
#EXT-X-PROGRAM-DATE-TIME:2019-02-14T02:13:36.106Z
#EXT-X-MAP:URI="init.mp4"
#EXTINF:4.00008,
fileSequence266.mp4
#EXTINF:4.00008,
fileSequence267.mp4
#EXTINF:4.00008,
fileSequence268.mp4
#EXTINF:4.00008,
fileSequence269.mp4
#EXTINF:4.00008,
fileSequence270.mp4
#EXT-X-PART:DURATION=0.33334,URI="filePart271.0.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.1.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.2.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.3.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.4.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart271.5.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.6.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.7.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.8.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart271.9.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.10.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.11.mp4"
#EXTINF:4.00008,
fileSequence271.mp4
#EXT-X-PROGRAM-DATE-TIME:2019-02-14T02:14:00.106Z
#EXT-X-PART:DURATION=0.33334,URI="filePart272.a.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.b.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.c.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.d.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.e.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.f.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart272.g.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.h.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.i.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.j.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.k.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.l.mp4"
#EXTINF:4.00008,
fileSequence272.mp4
#EXT-X-PART:DURATION=0.33334,URI="filePart273.0.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart273.1.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart273.2.mp4"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="filePart273.3.mp4"


#EXT-X-RENDITION-REPORT:URI="../1M/waitForMSN.php",LAST-MSN=273,LAST-PART=2
#EXT-X-RENDITION-REPORT:URI="../4M/waitForMSN.php",LAST-MSN=273,LAST-PART=1

Example: Playlist Delta Update

#EXTM3U
# Following the example above, this Playlist is a response to: GET https://example.com/2M/waitForMSN.php?_HLS_msn=273&_HLS_part=3 &_HLS_skip=YES
#EXT-X-TARGETDURATION:4
#EXT-X-VERSION:9
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.0,CAN-SKIP-UNTIL=12.0
#EXT-X-PART-INF:PART-TARGET=0.33334
#EXT-X-MEDIA-SEQUENCE:266
#EXT-X-SKIP:SKIPPED-SEGMENTS=3
#EXTINF:4.00008,
fileSequence269.mp4
#EXTINF:4.00008,
fileSequence270.mp4
#EXT-X-PART:DURATION=0.33334,URI="filePart271.0.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.1.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.2.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.3.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.4.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart271.5.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.6.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.7.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.8.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart271.9.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.10.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart271.11.mp4"
#EXTINF:4.00008,
fileSequence271.mp4
#EXT-X-PROGRAM-DATE-TIME:2019-02-14T02:14:00.106Z
#EXT-X-PART:DURATION=0.33334,URI="filePart272.a.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.b.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.c.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.d.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.e.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.f.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart272.g.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.h.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.i.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.j.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.k.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart272.l.mp4"
#EXTINF:4.00008,
fileSequence272.mp4
#EXT-X-PART:DURATION=0.33334,URI="filePart273.0.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="filePart273.1.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart273.2.mp4"
#EXT-X-PART:DURATION=0.33334,URI="filePart273.3.mp4"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="filePart273.4.mp4"


#EXT-X-RENDITION-REPORT:URI="../1M/waitForMSN.php",LAST-MSN=273,LAST-PART=3
#EXT-X-RENDITION-REPORT:URI="../4M/waitForMSN.php",LAST-MSN=273,LAST-PART=3

Example: Byterange-addressed Parts

# In these examples only the end of the Playlist is shown.
# This is Playlist update 1
#EXTINF:4.08,
fs270.mp4
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="20000@0"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="23000@20000"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="18000@43000"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fs271.mp4",BYTERANGE-START=61000


# This is Playlist update 2
#EXTINF:4.08,
fs270.mp4
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="20000@0"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="23000@20000"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="18000@43000"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="19000@61000"
#EXTINF:4.08,
fs271.mp4
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fs272.mp4",BYTERANGE-START=0


# This is Playlist update 3
#EXTINF:4.08,
fs270.mp4
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="20000@0"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="23000@20000"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="18000@43000"
#EXT-X-PART:DURATION=1.02,URI="fs271.mp4",BYTERANGE="19000@61000"
#EXTINF:4.08,
fs271.mp4
#EXT-X-PART:DURATION=1.02,URI="fs272.mp4",BYTERANGE="21000@0"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fs272.mp4",BYTERANGE-START=21000

Revision History

Ref

datatracker.ietf.org

cmd/cgoの翻訳

pkg.go.dev

go1.17時点

Goの参照からC

Go ファイル内では、Go のキーワードである C の構造体のフィールド名にアンダースコアを前置することでアクセスできます。x が type という名前のフィールドを持つ C 構造体を指している場合、x._type はそのフィールドにアクセスします。ビットフィールドやミスアラインドデータなど、Go で表現できない C 構造体のフィールドは、Go 構造体では省略され、次のフィールドや構造体の最後に到達するための適切なパディングで置き換えられます。

C.char、C.schar(符号付き char)、C.uchar(符号なし char)、C.short、C.ushort(符号なし short)、C.int、C.uint(符号なし int)、C.long、C.ulong(符号なし long)、C.longlong(long)、C.ulonglong(符号なし long long)、C.float、C.double、C.complexfloat(複素数 float)、C.complexdouble(複素数 double)という名前で、標準的な C の数値型が用意されています。C型のvoid*は、Goのunsafe.Pointerで表されます。C型のint128_tおよびuint128_tは、[16]byte.Pointerで表されます。

いくつかの特殊なC言語の型は、通常、Goではポインタ型で表現されますが、代わりにuintptrで表現されます。以下の「特殊なケース」を参照してください。

構造体(struct)、組合(union)、列挙型(enum)に直接アクセスするには、C.struct_statのように、構造体(struct)、組合(union)、列挙型(enum_)を前置します。

任意のC型Tのサイズは、C.sizeof_struct_statのように、C.sizeof_Tとして利用できます。

C関数は、特別な名前GoStringのパラメータ型を持つGoファイルで宣言することができます。この関数は、通常の Go の文字列値で呼び出すことができます。文字列の長さと文字列の内容へのポインタにアクセスするには、以下のC関数を呼び出す必要があります。

size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);

これらの関数はプリアンブルでのみ使用でき、他の C ファイルでは使用できません。C コードは、_GoStringPtr が返すポインタの内容を変更してはいけません。文字列の内容は、末尾に NUL バイトがあってはならないことに注意してください。

Go は一般的なケースでは C の union 型をサポートしていないので、C の union 型は同じ長さの Go のバイト配列として表されます。

Goの構造体は、Cの型を持つフィールドを埋め込むことができません。

Goのコードは、空ではないCの構造体の最後に発生するゼロサイズのフィールドを参照できません。このようなフィールドのアドレスを取得するには(ゼロサイズのフィールドでできる唯一の操作です)、構造体のアドレスを取り、構造体のサイズを加える必要があります。

Cgo は C 言語の型を同等の非移植の Go 言語の型に変換します。ある Go パッケージで使用されている C 型は、別の Go パッケージで使用されている同じ C 型とは異なります。

任意の C 関数 (void 関数も含む) を多重代入コンテキストで呼び出して、戻り値 (もしあれば) とエラーとしての C errno 変数の両方を取得することができます (関数が void を返す場合は _ を使用して結果値をスキップします)。例えば、以下のようになります。

n, err = C.sqrt(-1)
_, err := C.voidFunc()
var n, err = C.sqrt(1)

現在、C言語の関数ポインタの呼び出しはサポートされていませんが、C言語の関数ポインタを保持するGo変数を宣言し、GoとCの間でそれらをやり取りすることができます。Cコードは、Goから受け取った関数ポインタを呼び出すことができます。例えば、以下のようになります。

package main

// typedef int (*intFunc) ();
//
// int
// bridge_int_func(intFunc f)
// {
//     return f();
// }
//
// int fortytwo()
// {
//     return 42;
// }
import "C"
import "fmt"

func main() {
    f := C.intFunc(C.fortytwo)
    fmt.Println(int(C.bridge_int_func(f)))
    // Output: 42
}

Cでは、固定サイズの配列として書かれた関数の引数は、実際には配列の最初の要素へのポインタを必要とします。C言語コンパイラはこの呼び出し規則を認識しており、それに応じて呼び出しを調整しますが、Goではそれができません。Goでは、最初の要素へのポインタを明示的に渡さなければなりません。C.f(&C.x[0]).

可変長のC関数を呼び出すことはサポートされていません。C関数のラッパーを使用することで、この問題を回避することができます。例えば,以下のようになります.

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import "unsafe"

func main() {
    cs := C.CString("Hello from stdio")
    C.myprint(cs)
    C.free(unsafe.Pointer(cs))
}

いくつかの特殊な関数は、データのコピーを作成することで、Go型とC型の間で変換します。擬似的なGoの定義で。

// Go stringからC stringへ
// Cの文字列は、mallocを使ってCのヒープに割り当てられます。
// C.free(C.freeが必要な場合はstdlib.hを必ずインクルードしてください)を呼び出すなどして、
// 解放されるように手配するのは呼び出し側の責任です。
func C.CString(string) *C.char

// Cの配列は、mallocを使ってCのヒープに割り当てられます。
// C.free(C.freeが必要な場合はstdlib.hを必ずインクルードしてください)を呼び出すなど、
// 呼び出し側の責任で解放されるように手配します。
func C.CBytes([]byte) unsafe.Pointer

// C string から Go stringへ
func C.GoString(*C.char) string

// 明示的な長さを持つC data からGo stringへ
func C.GoStringN(*C.char, C.int) string

// 明示的な長さを持つC data から Go []byteへ
func C.GoBytes(unsafe.Pointer, C.int) []byte

特殊なケースとして、C.mallocはCライブラリのmallocを直接呼び出すのではなく、Cライブラリのmallocをラップして、決してnilを返さないことを保証するGoのヘルパー関数を呼び出します。Cのmallocがメモリ不足を示した場合、Go自身がメモリ不足に陥ったときのように、ヘルパー関数がプログラムをクラッシュさせます。C.mallocは失敗することができないので、errnoを返す2つの結果の形式はありません。

Cの参照からGo

Go関数は、次のようにしてCコードで使用できるようにエクスポートできます。

//export MyFunction
func MyFunction(arg1, arg2 int, arg3 string) int64 {...}

//export MyFunction2
func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}

これらはCコードとして以下のように利用できます。

extern GoInt64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);

cgo_export.hの生成されたヘッダの中で、cgoの入力ファイルからコピーされたプリアンブルの後に記述されています。複数の戻り値を持つ関数は、構造体を返す関数にマッピングされます。

すべての Go の型が C の型に便利な方法でマッピングできるわけではありません。Go の構造体タイプはサポートされていないので、C の構造体タイプを使用してください。Go の配列型はサポートされていません。C のポインタを使用してください。

文字列型の引数を取るGo関数は、上述のCのGoString型で呼び出すことができます。このGoString型は、プリアンブルで自動的に定義されます。これは、文字列の値をGoからCに渡したり、Goに戻したりする場合にのみ有効です。

ファイル内で //export を使用すると、プリアンブルに制限がかかります。プリアンブルは 2 つの異なる C 出力ファイルにコピーされるため、定義を含んではならず、宣言のみが含まれます。ファイルに定義と宣言の両方が含まれていると、2つの出力ファイルでシンボルが重複してしまい、リンカが失敗してしまいます。これを避けるためには、他のファイルのプリアンブルやCソースファイルの中に定義を入れる必要があります。

Passing pointers

Goはガベージコレクション言語であり、ガベージコレクタはGoのメモリへのすべてのポインタの位置を知る必要があります。そのため、GoとCの間でのポインタの受け渡しには制限があります。

本節では、Goポインタとは、Goによって割り当てられたメモリへのポインタ(&演算子の使用や定義済みのnew関数の呼び出しなど)を意味し、Cポインタとは、Cによって割り当てられたメモリへのポインタ(C.mallocの呼び出しなど)を意味します。ポインターがGoポインターであるかCポインターであるかは、メモリがどのように割り当てられたかによって決まる動的な性質であり、ポインターの型とは関係ありません。

いくつかのGo型の値は、その型のゼロ値以外は、常にGoポインタを含んでいることに注意してください。これは、string、slice、interface、channel、map、functionの各型に当てはまります。ポインタ型は、Goポインタを保持していても、Cポインタを保持していてもかまいません。配列型と構造体型は、要素の型によって、Goポインタを含む場合と含まない場合があります。以下のGoポインターに関する説明は、ポインター型だけでなく、Goポインターを含む他の型にも適用されます。

Go コードは、Go ポインタが指す Go メモリに Go ポインタが含まれていない場合、C に Go ポインタを渡すことができます。Cコードはこの特性を維持しなければなりません。たとえ一時的であっても、GoポインタをGoメモリに格納してはなりません。構造体のフィールドへのポインタを渡す場合、問題となるGoメモリは構造体全体ではなく、フィールドが占有するメモリです。配列やスライスの要素へのポインタを渡す場合、問題となるGoメモリは、配列全体またはスライスの下位配列全体です。

Cコードは、呼び出しが戻った後に、Goポインタのコピーを保持することはできません。これには GoString 型も含まれます。上述のように、GoString 型は Go ポインタを含んでいます。

Cコードから呼び出されたGo関数は、Goポインタを返すことはできません(これは、文字列、スライス、チャンネルなどを返すことができないことを意味します)。Cコードによって呼び出されたGo関数は、引数としてCポインタを取ることができ、それらのポインタを通して非ポインタまたはCポインタのデータを格納することができますが、Cポインタによって指されるメモリにGoポインタを格納することはできません。C コードから呼び出される Go 関数は、引数として Go ポインタを取ることができますが、その関数が指す Go メモリには Go ポインタが含まれていないという特性を保持する必要があります。

Go コードは、C メモリに Go ポインタを格納することはできませんCコードはCメモリにGoポインタを格納することができますが、上記のルールに従い、C関数が戻るときにGoポインタの格納を停止しなければなりません。

これらの規則は、実行時に動的にチェックされます。このチェックは、環境変数 GODEBUG の cgocheck の設定によって制御されます。デフォルトの設定は GODEBUG=cgocheck=1 で、これは合理的に安価な動的チェックを実装しています。GODEBUG=cgocheck=0 を使用すると、これらのチェックを完全に無効にすることができます。 GODEBUG=cgocheck=2 を使用すると、実行時に多少のコストがかかりますが、ポインタ処理の完全なチェックを行うことができます。

unsafeパッケージを使用することで、この強制力を無効にすることは可能ですし、もちろんCコードが好きなことをするのを止めることはできません。しかし、これらのルールを破ったプログラムは、予期せぬ予期せぬ方法で失敗する可能性があります。

runtime/cgo.Handle型は、GoとCの間でGoの値を安全に渡すために使用できます。詳細はruntime/cgoパッケージのドキュメントを参照してください。

注意: 現在の実装にはバグがあります。GoコードはCメモリにnilやCポインタ(Goポインタではない)を書き込むことが許可されていますが、現在の実装では、Cメモリの内容がGoポインタであるように見える場合、ランタイムエラーが発生することがあります。そのため、Goコードがポインタ値を格納する場合は、初期化されていないCメモリをGoコードに渡さないようにしてください。Goに渡す前にCでメモリをゼロにしてください。

Special cases

いくつかの特殊なC言語の型は、通常、Goではポインタ型で表現されますが、代わりにuintptrで表現されます。それらは以下の通りです。

  1. Darwinの*Ref型は、CoreFoundationのCFTypeRef型に根ざしています。

  2. JavaのJNIインターフェースのオブジェクト型。

jobject
jclass
jthrowable
jstring
jarray
jbooleanArray
jbyteArray
jcharArray
jshortArray
jintArray
jlongArray
jfloatArray
jdoubleArray
jobjectArray
jweak
  1. EGL API の EGLDisplay および EGLConfig 型です。

これらの型は、そうしないとGoのガベージコレクタを混乱させるため、Go側ではuintptrとなっています。これらは、実際にはポインタではなく、ポインタ型でエンコードされたデータ構造であることもあります。このような空の参照を初期化するための適切な定数はnilではなく0です。

これらの特殊なケースは、Go 1.10で導入されました。Go 1.9 以前のコードを自動更新するには、Go fix tool の cftype または jni rewrites を使用してください。

go tool fix -r cftype <pkg>
go tool fix -r jni <pkg>

適切な場所で nil を 0 に置き換えます。

EGLDisplayケースは、Go 1.12で導入されました。Go 1.11 以前のコードを自動更新するには、egl rewrite を使用してください。

go tool fix -r egl <pkg>

EGLConfigケースは、Go 1.15で導入されました。Go 1.14 以前のコードを自動更新するには、eglconf rewrite を使用してください。

go tool fix -r eglconf <pkg>

cgoを直接使用する

使い方:

go tool cgo [cgo options] [-- compiler options] gofiles...

Cgo は、指定された入力 Go ソースファイルを、複数の出力 Go および C ソースファイルに変換します。

C コンパイラオプションは、パッケージの C パートをコンパイルするために C コンパイラを起動する際に解釈されずに渡されます。

cgo を直接実行する場合、以下のオプションが利用できます。

-V
    Print cgo version and exit.
-debug-define
    Debugging option. Print #defines.
-debug-gcc
    Debugging option. Trace C compiler execution and output.
-dynimport file
    Write list of symbols imported by file. Write to
    -dynout argument or to standard output. Used by go
    build when building a cgo package.
-dynlinker
    Write dynamic linker as part of -dynimport output.
-dynout file
    Write -dynimport output to file.
-dynpackage package
    Set Go package for -dynimport output.
-exportheader file
    If there are any exported functions, write the
    generated export declarations to file.
    C code can #include this to see the declarations.
-importpath string
    The import path for the Go package. Optional; used for
    nicer comments in the generated files.
-import_runtime_cgo
    If set (which it is by default) import runtime/cgo in
    generated output.
-import_syscall
    If set (which it is by default) import syscall in
    generated output.
-gccgo
    Generate output for the gccgo compiler rather than the
    gc compiler.
-gccgoprefix prefix
    The -fgo-prefix option to be used with gccgo.
-gccgopkgpath path
    The -fgo-pkgpath option to be used with gccgo.
-godefs
    Write out input file in Go syntax replacing C package
    names with real values. Used to generate files in the
    syscall package when bootstrapping a new target.
-objdir directory
    Put all generated files in directory.
-srcdir directory

AACについて調べたリンク集

AAC概要

まずはwikipedia

ja.wikipedia.org

AACの特徴

bb.watch.impress.co.jp

機能面の改善がとてもわかりやすい

  • 単なる2chのステレオに留まらず、最大48chまでに対応(MP3はMPEG-2モードでも最大5.1ch)
  • 8kHz~96kHzまでのサンプリングレートに対応(MP3は16kHz~48kHz)
  • 符号化方式を「MDCT(Modified Discrete Cosine Transform)」から「DCT(Discrete Cosine Transform)」に切り替えることで、符号化効率を向上
  • エンコード時にブロックサイズを最適化
  • フィルタリングに「カイザー窓(Kaiser window)」と呼ばれる窓関数を使えるように変更
  • 16kHz以下の周波数の取り扱いを改善
  • Joint Stereoの取り扱いを柔軟にした
  • エラー検出・訂正を内蔵した

AACの圧縮方法について詳しく記載されている

https://www.ne.jp/asahi/shiga/home/MyRoom/Audiocompression.pdf

AAC では、一定の重なりを持ってある時間間隔ごとに音声入力信号を切り出し、DCT(離散コサイン変換)処理により周波数成分に分割します

高効率音楽符号化

mp3がメインだが基礎が詰まっている。

http://www.spcom.ecei.tohoku.ac.jp/~aito/kisosemi/slides2.pdf

サンプリング数に関して

googleにて'aac サンプリング 1024'にて検索

フレームサイズにてよくまとめられている

smile-jsp.hateblo.jp

AACの工程について記載されている

art-of-life.jp