You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/pkg/rtmp/amf0.go

386 lines
9.0 KiB
Go

// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package rtmp
// amf0.go
6 years ago
// @pure
// 提供amf0格式的编码与解码的操作
import (
"bytes"
"errors"
"io"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
var (
ErrAMFInvalidType = errors.New("lal.rtmp: invalid amf0 type")
ErrAMFTooShort = errors.New("lal.rtmp: too short to unmarshal amf0 data")
ErrAMFNotExist = errors.New("lal.rtmp: not exist")
)
const (
AMF0TypeMarkerNumber = uint8(0x00)
AMF0TypeMarkerBoolean = uint8(0x01)
AMF0TypeMarkerString = uint8(0x02)
AMF0TypeMarkerObject = uint8(0x03)
AMF0TypeMarkerNull = uint8(0x05)
AMF0TypeMarkerEcmaArray = uint8(0x08)
AMF0TypeMarkerObjectEnd = uint8(0x09)
AMF0TypeMarkerLongString = uint8(0x0c)
// 还没用到的类型
//AMF0TypeMarkerMovieclip = uint8(0x04)
//AMF0TypeMarkerUndefined = uint8(0x06)
//AMF0TypeMarkerReference = uint8(0x07)
//AMF0TypeMarkerStrictArray = uint8(0x0a)
//AMF0TypeMarkerData = uint8(0x0b)
//AMF0TypeMarkerUnsupported = uint8(0x0d)
//AMF0TypeMarkerRecordset = uint8(0x0e)
//AMF0TypeMarkerXmlDocument = uint8(0x0f)
//AMF0TypeMarkerTypedObject = uint8(0x10)
)
var AMF0TypeMarkerObjectEndBytes = []byte{0, 0, AMF0TypeMarkerObjectEnd}
type ObjectPair struct {
Key string
Value interface{}
}
type ObjectPairArray []ObjectPair
func (o ObjectPairArray) Find(key string) interface{} {
for _, op := range o {
if op.Key == key {
return op.Value
}
}
return nil
}
func (o ObjectPairArray) FindString(key string) (string, error) {
for _, op := range o {
if op.Key == key {
if s, ok := op.Value.(string); ok {
return s, nil
}
}
}
return "", ErrAMFNotExist
}
4 years ago
func (o ObjectPairArray) FindNumber(key string) (int, error) {
for _, op := range o {
if op.Key == key {
if s, ok := op.Value.(float64); ok {
return int(s), nil
}
}
}
return -1, ErrAMFNotExist
}
type amf0 struct{}
var AMF0 amf0
// ----------------------------------------------------------------------------
func (amf0) WriteNumber(writer io.Writer, val float64) error {
if _, err := writer.Write([]byte{AMF0TypeMarkerNumber}); err != nil {
return err
}
return bele.WriteBE(writer, val)
}
func (amf0) WriteString(writer io.Writer, val string) error {
if len(val) < 65536 {
if _, err := writer.Write([]byte{AMF0TypeMarkerString}); err != nil {
return err
}
if err := bele.WriteBE(writer, uint16(len(val))); err != nil {
return err
}
} else {
if _, err := writer.Write([]byte{AMF0TypeMarkerLongString}); err != nil {
return err
}
if err := bele.WriteBE(writer, uint32(len(val))); err != nil {
return err
}
}
_, err := writer.Write([]byte(val))
return err
}
func (amf0) WriteNull(writer io.Writer) error {
_, err := writer.Write([]byte{AMF0TypeMarkerNull})
return err
}
func (amf0) WriteBoolean(writer io.Writer, b bool) error {
if _, err := writer.Write([]byte{AMF0TypeMarkerBoolean}); err != nil {
return err
}
v := uint8(0)
if b {
v = 1
}
_, err := writer.Write([]byte{v})
return err
}
func (amf0) WriteObject(writer io.Writer, opa ObjectPairArray) error {
if _, err := writer.Write([]byte{AMF0TypeMarkerObject}); err != nil {
return err
}
for i := 0; i < len(opa); i++ {
if err := bele.WriteBE(writer, uint16(len(opa[i].Key))); err != nil {
return err
}
if _, err := writer.Write([]byte(opa[i].Key)); err != nil {
return err
}
switch opa[i].Value.(type) {
case string:
if err := AMF0.WriteString(writer, opa[i].Value.(string)); err != nil {
return err
}
case int:
if err := AMF0.WriteNumber(writer, float64(opa[i].Value.(int))); err != nil {
return err
}
case bool:
if err := AMF0.WriteBoolean(writer, opa[i].Value.(bool)); err != nil {
return err
}
default:
nazalog.Panicf("unknown value type. i=%d, v=%+v", i, opa[i].Value)
}
}
_, err := writer.Write(AMF0TypeMarkerObjectEndBytes)
return err
}
// ----------------------------------------------------------------------------
// read类型的方法集合
//
// 从输入参数<b>切片中读取函数名所指定的amf类型数据
// 注意,方法内部不会修改输入参数<b>切片的内容
//
// 返回值如无特殊说明,则
// 第1个参数为读取出的所指定类型的数据
// 第2个参数为读取时从<b>消耗的字节大小
// 第3个参数error如果不等于nil表示读取失败
func (amf0) ReadStringWithoutType(b []byte) (string, int, error) {
if len(b) < 2 {
return "", 0, ErrAMFTooShort
}
l := int(bele.BEUint16(b))
if l > len(b)-2 {
return "", 0, ErrAMFTooShort
}
return string(b[2 : 2+l]), 2 + l, nil
}
func (amf0) ReadLongStringWithoutType(b []byte) (string, int, error) {
if len(b) < 4 {
return "", 0, ErrAMFTooShort
}
l := int(bele.BEUint32(b))
if l > len(b)-4 {
return "", 0, ErrAMFTooShort
}
return string(b[4 : 4+l]), 4 + l, nil
}
func (amf0) ReadString(b []byte) (val string, l int, err error) {
if len(b) < 1 {
return "", 0, ErrAMFTooShort
}
switch b[0] {
case AMF0TypeMarkerString:
val, l, err = AMF0.ReadStringWithoutType(b[1:])
l++
case AMF0TypeMarkerLongString:
val, l, err = AMF0.ReadLongStringWithoutType(b[1:])
l++
default:
err = ErrAMFInvalidType
}
return
}
func (amf0) ReadNumber(b []byte) (float64, int, error) {
if len(b) < 9 {
return 0, 0, ErrAMFTooShort
}
if b[0] != AMF0TypeMarkerNumber {
return 0, 0, ErrAMFInvalidType
}
return bele.BEFloat64(b[1:]), 9, nil
}
func (amf0) ReadBoolean(b []byte) (bool, int, error) {
if len(b) < 2 {
return false, 0, ErrAMFTooShort
}
if b[0] != AMF0TypeMarkerBoolean {
return false, 0, ErrAMFInvalidType
}
return b[1] != 0x0, 2, nil
}
func (amf0) ReadNull(b []byte) (int, error) {
if len(b) < 1 {
return 0, ErrAMFTooShort
}
if b[0] != AMF0TypeMarkerNull {
return 0, ErrAMFInvalidType
}
return 1, nil
}
func (amf0) ReadObject(b []byte) (ObjectPairArray, int, error) {
if len(b) < 1 {
return nil, 0, ErrAMFTooShort
}
if b[0] != AMF0TypeMarkerObject {
return nil, 0, ErrAMFInvalidType
}
index := 1
var ops ObjectPairArray
for {
if len(b)-index >= 3 && bytes.Equal(b[index:index+3], AMF0TypeMarkerObjectEndBytes) {
return ops, index + 3, nil
}
k, l, err := AMF0.ReadStringWithoutType(b[index:])
if err != nil {
return nil, 0, err
}
index += l
if len(b)-index < 1 {
return nil, 0, ErrAMFTooShort
}
vt := b[index]
switch vt {
case AMF0TypeMarkerString:
v, l, err := AMF0.ReadString(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
case AMF0TypeMarkerBoolean:
v, l, err := AMF0.ReadBoolean(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
case AMF0TypeMarkerNumber:
v, l, err := AMF0.ReadNumber(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
case AMF0TypeMarkerEcmaArray:
v, l, err := AMF0.ReadArray(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
default:
nazalog.Panicf("unknown type. vt=%d", vt)
}
}
}
// TODO chef:
// - 实现WriteArray
// - ReadArray和ReadObject有些代码重复
func (amf0) ReadArray(b []byte) (ObjectPairArray, int, error) {
if len(b) < 5 {
return nil, 0, ErrAMFTooShort
}
if b[0] != AMF0TypeMarkerEcmaArray {
return nil, 0, ErrAMFInvalidType
}
count := int(bele.BEUint32(b[1:]))
index := 5
var ops ObjectPairArray
for i := 0; i < count; i++ {
k, l, err := AMF0.ReadStringWithoutType(b[index:])
if err != nil {
return nil, 0, err
}
index += l
if len(b)-index < 1 {
return nil, 0, ErrAMFTooShort
}
vt := b[index]
switch vt {
case AMF0TypeMarkerString:
v, l, err := AMF0.ReadString(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
case AMF0TypeMarkerBoolean:
v, l, err := AMF0.ReadBoolean(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
case AMF0TypeMarkerNumber:
v, l, err := AMF0.ReadNumber(b[index:])
if err != nil {
return nil, 0, err
}
ops = append(ops, ObjectPair{k, v})
index += l
default:
nazalog.Panicf("unknown type. vt=%d", vt)
}
}
if len(b)-index >= 3 && bytes.Equal(b[index:index+3], AMF0TypeMarkerObjectEndBytes) {
index += 3
} else {
// 测试时发现Array最后也是以00 00 09结束不确定是否是标准规定的加个日志在这
nazalog.Warn("amf ReadArray without suffix AMF0TypeMarkerObjectEndBytes.")
}
return ops, index, nil
}
func (amf0) ReadObjectOrArray(b []byte) (ObjectPairArray, int, error) {
if len(b) < 1 {
return nil, 0, ErrAMFTooShort
}
switch b[0] {
case AMF0TypeMarkerObject:
return AMF0.ReadObject(b)
case AMF0TypeMarkerEcmaArray:
return AMF0.ReadArray(b)
}
return nil, 0, ErrAMFInvalidType
}