install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
package tlv8
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type errReader struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *errReader) Read([]byte) (int, error) {
|
||||
return 0, e.err
|
||||
}
|
||||
|
||||
func MarshalBase64(v any) (string, error) {
|
||||
b, err := Marshal(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func MarshalReader(v any) io.Reader {
|
||||
b, err := Marshal(v)
|
||||
if err != nil {
|
||||
return &errReader{err: err}
|
||||
}
|
||||
return bytes.NewReader(b)
|
||||
}
|
||||
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
value := reflect.ValueOf(v)
|
||||
kind := value.Type().Kind()
|
||||
|
||||
if kind == reflect.Pointer {
|
||||
value = value.Elem()
|
||||
kind = value.Type().Kind()
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return appendSlice(nil, value)
|
||||
case reflect.Struct:
|
||||
return appendStruct(nil, value)
|
||||
}
|
||||
|
||||
return nil, errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
// separator the most confusing meaning in the documentation.
|
||||
// It can have a value of 0x00 or 0xFF or even 0x05.
|
||||
const separator = 0xFF
|
||||
|
||||
func appendSlice(b []byte, value reflect.Value) ([]byte, error) {
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, separator, 0)
|
||||
}
|
||||
var err error
|
||||
if b, err = appendStruct(b, value.Index(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func appendStruct(b []byte, value reflect.Value) ([]byte, error) {
|
||||
valueType := value.Type()
|
||||
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
refField := value.Field(i)
|
||||
s, ok := valueType.Field(i).Tag.Lookup("tlv8")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
tag, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err = appendValue(b, byte(tag), refField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func appendValue(b []byte, tag byte, value reflect.Value) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Uint8:
|
||||
v := value.Uint()
|
||||
return append(b, tag, 1, byte(v)), nil
|
||||
|
||||
case reflect.Uint16:
|
||||
v := value.Uint()
|
||||
return append(b, tag, 2, byte(v), byte(v>>8)), nil
|
||||
|
||||
case reflect.Uint32:
|
||||
v := value.Uint()
|
||||
return append(b, tag, 4, byte(v), byte(v>>8), byte(v>>16), byte(v>>24)), nil
|
||||
|
||||
case reflect.Uint64:
|
||||
v := value.Uint()
|
||||
return binary.LittleEndian.AppendUint64(append(b, tag, 8), v), nil
|
||||
|
||||
case reflect.Float32:
|
||||
v := math.Float32bits(float32(value.Float()))
|
||||
return append(b, tag, 4, byte(v), byte(v>>8), byte(v>>16), byte(v>>24)), nil
|
||||
|
||||
case reflect.String:
|
||||
v := value.String()
|
||||
l := len(v) // support "big" string
|
||||
for ; l > 255; l -= 255 {
|
||||
b = append(b, tag, 255)
|
||||
b = append(b, v[:255]...)
|
||||
v = v[255:]
|
||||
}
|
||||
b = append(b, tag, byte(l))
|
||||
return append(b, v...), nil
|
||||
|
||||
case reflect.Array:
|
||||
if value.Type().Elem().Kind() == reflect.Uint8 {
|
||||
n := value.Len()
|
||||
b = append(b, tag, byte(n))
|
||||
for i := 0; i < n; i++ {
|
||||
b = append(b, byte(value.Index(i).Uint()))
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if i > 0 {
|
||||
b = append(b, separator, 0)
|
||||
}
|
||||
if b, err = appendValue(b, tag, value.Index(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
|
||||
case reflect.Struct:
|
||||
b = append(b, tag, 0)
|
||||
i := len(b)
|
||||
if b, err = appendStruct(b, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b[i-1] = byte(len(b) - i) // set struct size
|
||||
return b, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("tlv8: not implemented: " + value.Kind().String())
|
||||
}
|
||||
|
||||
func UnmarshalBase64(in any, out any) error {
|
||||
s, _ := in.(string) // protect from in == nil
|
||||
data, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Unmarshal(data, out)
|
||||
}
|
||||
|
||||
func UnmarshalReader(r io.Reader, n int64, v any) error {
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
if n > 0 {
|
||||
data = make([]byte, n)
|
||||
_, err = io.ReadFull(r, data)
|
||||
} else {
|
||||
data, err = io.ReadAll(r)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte, v any) error {
|
||||
if len(data) == 0 {
|
||||
return errors.New("tlv8: unmarshal zero data")
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(v)
|
||||
kind := value.Kind()
|
||||
|
||||
if kind != reflect.Pointer {
|
||||
return errors.New("tlv8: value should be pointer: " + kind.String())
|
||||
}
|
||||
|
||||
value = value.Elem()
|
||||
kind = value.Kind()
|
||||
|
||||
if kind == reflect.Interface {
|
||||
value = value.Elem()
|
||||
kind = value.Kind()
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return unmarshalSlice(data, value)
|
||||
case reflect.Struct:
|
||||
return unmarshalStruct(data, value)
|
||||
}
|
||||
|
||||
return errors.New("tlv8: not implemented: " + kind.String())
|
||||
}
|
||||
|
||||
// unmarshalTLV can return two types of errors:
|
||||
// - critical and then the value of []byte will be nil
|
||||
// - not critical and then []byte will contain the value
|
||||
func unmarshalTLV(b []byte, value reflect.Value) ([]byte, error) {
|
||||
if len(b) < 2 {
|
||||
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
|
||||
t := b[0]
|
||||
l := int(b[1])
|
||||
|
||||
// array item divider (t == 0x00 || t == 0xFF)
|
||||
if l == 0 {
|
||||
return b[2:], errors.New("tlv8: zero item")
|
||||
}
|
||||
|
||||
var v []byte
|
||||
|
||||
for {
|
||||
if len(b) < 2+l {
|
||||
return nil, errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
|
||||
v = append(v, b[2:2+l]...)
|
||||
b = b[2+l:]
|
||||
|
||||
// if size == 255 and same tag - continue read big payload
|
||||
if l < 255 || len(b) < 2 || b[0] != t {
|
||||
break
|
||||
}
|
||||
|
||||
l = int(b[1])
|
||||
}
|
||||
|
||||
tag := strconv.Itoa(int(t))
|
||||
|
||||
valueField, ok := getStructField(value, tag)
|
||||
if !ok {
|
||||
return b, fmt.Errorf("tlv8: can't find T=%d,L=%d,V=%x for: %s", t, l, v, value.Type().Name())
|
||||
}
|
||||
|
||||
if err := unmarshalValue(v, valueField); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func unmarshalSlice(b []byte, value reflect.Value) error {
|
||||
valueIndex := value.Index(growSlice(value))
|
||||
for len(b) > 0 {
|
||||
var err error
|
||||
if b, err = unmarshalTLV(b, valueIndex); err != nil {
|
||||
if b != nil {
|
||||
valueIndex = value.Index(growSlice(value))
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalStruct(b []byte, value reflect.Value) error {
|
||||
for len(b) > 0 {
|
||||
var err error
|
||||
if b, err = unmarshalTLV(b, value); b == nil && err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalValue(v []byte, value reflect.Value) error {
|
||||
switch value.Kind() {
|
||||
case reflect.Uint8:
|
||||
if len(v) != 1 {
|
||||
return errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
value.SetUint(uint64(v[0]))
|
||||
|
||||
case reflect.Uint16:
|
||||
if len(v) != 2 {
|
||||
return errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
value.SetUint(uint64(v[0]) | uint64(v[1])<<8)
|
||||
|
||||
case reflect.Uint32:
|
||||
if len(v) != 4 {
|
||||
return errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
value.SetUint(uint64(v[0]) | uint64(v[1])<<8 | uint64(v[2])<<16 | uint64(v[3])<<24)
|
||||
|
||||
case reflect.Uint64:
|
||||
if len(v) != 8 {
|
||||
return errors.New("tlv8: wrong size: " + value.Type().Name())
|
||||
}
|
||||
value.SetUint(binary.LittleEndian.Uint64(v))
|
||||
|
||||
case reflect.Float32:
|
||||
f := math.Float32frombits(binary.LittleEndian.Uint32(v))
|
||||
value.SetFloat(float64(f))
|
||||
|
||||
case reflect.String:
|
||||
value.SetString(string(v))
|
||||
|
||||
case reflect.Array:
|
||||
if kind := value.Type().Elem().Kind(); kind != reflect.Uint8 {
|
||||
return errors.New("tlv8: unsupported array: " + kind.String())
|
||||
}
|
||||
|
||||
for i, b := range v {
|
||||
value.Index(i).SetUint(uint64(b))
|
||||
}
|
||||
return nil
|
||||
|
||||
case reflect.Slice:
|
||||
i := growSlice(value)
|
||||
return unmarshalValue(v, value.Index(i))
|
||||
|
||||
case reflect.Struct:
|
||||
return unmarshalStruct(v, value)
|
||||
|
||||
default:
|
||||
return errors.New("tlv8: not implemented: " + value.Kind().String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStructField(value reflect.Value, tag string) (reflect.Value, bool) {
|
||||
valueType := value.Type()
|
||||
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
valueField := value.Field(i)
|
||||
|
||||
if s, ok := valueType.Field(i).Tag.Lookup("tlv8"); ok && s == tag {
|
||||
return valueField, true
|
||||
}
|
||||
}
|
||||
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
func growSlice(value reflect.Value) int {
|
||||
size := value.Len()
|
||||
|
||||
if size >= value.Cap() {
|
||||
newcap := value.Cap() + value.Cap()/2
|
||||
if newcap < 4 {
|
||||
newcap = 4
|
||||
}
|
||||
newValue := reflect.MakeSlice(value.Type(), value.Len(), newcap)
|
||||
reflect.Copy(newValue, value)
|
||||
value.Set(newValue)
|
||||
}
|
||||
|
||||
if size >= value.Len() {
|
||||
value.SetLen(size + 1)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package tlv8
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
type Struct struct {
|
||||
Byte byte `tlv8:"1"`
|
||||
Uint16 uint16 `tlv8:"2"`
|
||||
Uint32 uint32 `tlv8:"3"`
|
||||
Float32 float32 `tlv8:"4"`
|
||||
String string `tlv8:"5"`
|
||||
Slice []byte `tlv8:"6"`
|
||||
Array [4]byte `tlv8:"7"`
|
||||
}
|
||||
|
||||
src := Struct{
|
||||
Byte: 1,
|
||||
Uint16: 2,
|
||||
Uint32: 3,
|
||||
Float32: 1.23,
|
||||
String: "123",
|
||||
Slice: []byte{1, 2, 3},
|
||||
Array: [4]byte{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
b, err := Marshal(src)
|
||||
require.Nil(t, err)
|
||||
|
||||
var dst Struct
|
||||
err = Unmarshal(b, &dst)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, src, dst)
|
||||
}
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
bytes := make([]byte, 255)
|
||||
for i := 0; i < len(bytes); i++ {
|
||||
bytes[i] = byte(i)
|
||||
}
|
||||
|
||||
type Struct struct {
|
||||
String string `tlv8:"1"`
|
||||
}
|
||||
src := Struct{
|
||||
String: string(bytes),
|
||||
}
|
||||
|
||||
b, err := Marshal(src)
|
||||
require.Nil(t, err)
|
||||
|
||||
var dst Struct
|
||||
err = Unmarshal(b, &dst)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, src, dst)
|
||||
require.Equal(t, bytes, []byte(dst.String))
|
||||
}
|
||||
|
||||
func TestVideoCodecParams(t *testing.T) {
|
||||
type VideoCodecParams struct {
|
||||
ProfileID []byte `tlv8:"1"`
|
||||
Level []byte `tlv8:"2"`
|
||||
PacketizationMode byte `tlv8:"3"`
|
||||
CVOEnabled []byte `tlv8:"4"`
|
||||
CVOID []byte `tlv8:"5"`
|
||||
}
|
||||
|
||||
src, err := hex.DecodeString("0101010201000000020102030100040100")
|
||||
require.Nil(t, err)
|
||||
|
||||
var v VideoCodecParams
|
||||
err = Unmarshal(src, &v)
|
||||
require.Nil(t, err)
|
||||
|
||||
dst, err := Marshal(v)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, src, dst)
|
||||
}
|
||||
|
||||
func TestInterface(t *testing.T) {
|
||||
type Struct struct {
|
||||
Byte byte `tlv8:"1"`
|
||||
}
|
||||
|
||||
src := Struct{
|
||||
Byte: 1,
|
||||
}
|
||||
var v1 any = &src
|
||||
|
||||
b, err := Marshal(v1)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, []byte{1, 1, 1}, b)
|
||||
|
||||
var dst Struct
|
||||
var v2 any = &dst
|
||||
|
||||
err = Unmarshal(b, v2)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, src, dst)
|
||||
}
|
||||
|
||||
func TestSlice1(t *testing.T) {
|
||||
var v struct {
|
||||
VideoAttrs []struct {
|
||||
Width uint16 `tlv8:"1"`
|
||||
Height uint16 `tlv8:"2"`
|
||||
Framerate uint8 `tlv8:"3"`
|
||||
} `tlv8:"3"`
|
||||
}
|
||||
|
||||
s := `030b010280070202380403011e ff00 030b010200050202d00203011e`
|
||||
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Unmarshal(b1, &v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, v.VideoAttrs, 2)
|
||||
|
||||
b2, err := Marshal(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, b1, b2)
|
||||
}
|
||||
|
||||
func TestSlice2(t *testing.T) {
|
||||
var v []struct {
|
||||
Width uint16 `tlv8:"1"`
|
||||
Height uint16 `tlv8:"2"`
|
||||
Framerate uint8 `tlv8:"3"`
|
||||
}
|
||||
|
||||
s := `010280070202380403011e ff00 010200050202d00203011e`
|
||||
b1, err := hex.DecodeString(strings.ReplaceAll(s, " ", ""))
|
||||
require.NoError(t, err)
|
||||
|
||||
err = Unmarshal(b1, &v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, v, 2)
|
||||
|
||||
b2, err := Marshal(v)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, b1, b2)
|
||||
}
|
||||
Reference in New Issue
Block a user