mirror of
https://github.com/wader/fq.git
synced 2024-11-26 21:55:57 +03:00
137 lines
3.3 KiB
Go
137 lines
3.3 KiB
Go
|
package rtmp
|
||
|
|
||
|
// https://rtmp.veriskope.com/pdf/amf0-file-format-specification.pdf
|
||
|
|
||
|
import (
|
||
|
"github.com/wader/fq/format"
|
||
|
"github.com/wader/fq/format/registry"
|
||
|
"github.com/wader/fq/pkg/decode"
|
||
|
"github.com/wader/fq/pkg/scalar"
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
registry.MustRegister(decode.Format{
|
||
|
Name: format.AMF0,
|
||
|
Description: "Action Message Format 0",
|
||
|
DecodeFn: amf0Decode,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
typeNumber = 0x0
|
||
|
typeBoolean = 0x1
|
||
|
typeString = 0x2
|
||
|
typeObject = 0x3
|
||
|
typeMovieClip = 0x4
|
||
|
typeNull = 0x5
|
||
|
typeUndefined = 0x6
|
||
|
typeReference = 0x7
|
||
|
typeECMAArray = 0x8
|
||
|
typeObjectEnd = 0x9
|
||
|
typeStrictArray = 0xa
|
||
|
typeDate = 0xb
|
||
|
typeLongString = 0xc
|
||
|
typeUnsupported = 0xd
|
||
|
typeRecordSet = 0xe
|
||
|
typeXML = 0xf
|
||
|
typeTypedObject = 0x10
|
||
|
)
|
||
|
|
||
|
var typeNames = scalar.UToSymStr{
|
||
|
typeNumber: "number",
|
||
|
typeBoolean: "boolean",
|
||
|
typeString: "string",
|
||
|
typeObject: "object",
|
||
|
typeMovieClip: "movie_clip",
|
||
|
typeNull: "null",
|
||
|
typeUndefined: "undefined",
|
||
|
typeReference: "reference",
|
||
|
typeECMAArray: "ecma_array",
|
||
|
typeObjectEnd: "object_end",
|
||
|
typeStrictArray: "strict_array",
|
||
|
typeDate: "date",
|
||
|
typeLongString: "long_string",
|
||
|
typeUnsupported: "unsupported",
|
||
|
typeRecordSet: "record_set",
|
||
|
typeXML: "xml",
|
||
|
typeTypedObject: "typed_object",
|
||
|
}
|
||
|
|
||
|
func amf0DecodeString(d *decode.D) string { return d.UTF8(int(d.U16())) }
|
||
|
func amf0DecodeStringLong(d *decode.D) string { return d.UTF8(int(d.U32())) }
|
||
|
|
||
|
func amf0DecodeValue(d *decode.D) {
|
||
|
typ := d.FieldU8("type", typeNames)
|
||
|
|
||
|
switch typ {
|
||
|
case typeNumber:
|
||
|
d.FieldF64("value")
|
||
|
case typeBoolean:
|
||
|
d.FieldU8("value")
|
||
|
case typeString:
|
||
|
d.FieldStrFn("value", amf0DecodeString)
|
||
|
case typeObject:
|
||
|
d.FieldArray("value", func(d *decode.D) {
|
||
|
var typ uint64
|
||
|
for typ != typeObjectEnd {
|
||
|
d.FieldStruct("pair", func(d *decode.D) {
|
||
|
d.FieldStrFn("key", amf0DecodeString)
|
||
|
typ = d.PeekBits(8)
|
||
|
d.FieldStruct("value", amf0DecodeValue)
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
case typeNull:
|
||
|
d.FieldValueNil("value")
|
||
|
case typeUndefined:
|
||
|
d.FieldValueNil("value") // TODO: ?
|
||
|
case typeReference:
|
||
|
d.FieldU16("value") // TODO: index pointer
|
||
|
case typeECMAArray:
|
||
|
count := d.FieldU32("count")
|
||
|
d.FieldArray("value", func(d *decode.D) {
|
||
|
d.FieldStruct("entry", func(d *decode.D) {
|
||
|
for i := uint64(0); i < count; i++ {
|
||
|
d.FieldStrFn("key", amf0DecodeString)
|
||
|
d.FieldStruct("value", amf0DecodeValue)
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
case typeObjectEnd:
|
||
|
// nop
|
||
|
case typeStrictArray:
|
||
|
length := d.FieldU32("count")
|
||
|
d.FieldArray("value", func(d *decode.D) {
|
||
|
for i := uint64(0); i < length; i++ {
|
||
|
d.FieldStruct("entry", amf0DecodeValue)
|
||
|
}
|
||
|
})
|
||
|
case typeDate:
|
||
|
d.FieldF64("date_time")
|
||
|
d.FieldS16("local_data_time_offset")
|
||
|
case typeLongString:
|
||
|
d.FieldStrFn("value", amf0DecodeStringLong)
|
||
|
case typeXML:
|
||
|
d.FieldStrFn("value", amf0DecodeStringLong)
|
||
|
case typeTypedObject:
|
||
|
d.FieldStrFn("class_name", amf0DecodeString)
|
||
|
d.FieldArray("value", func(d *decode.D) {
|
||
|
var typ uint64
|
||
|
for typ != typeObjectEnd {
|
||
|
d.FieldStruct("pair", func(d *decode.D) {
|
||
|
d.FieldStrFn("key", amf0DecodeString)
|
||
|
typ = d.PeekBits(8)
|
||
|
d.FieldStruct("value", amf0DecodeValue)
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
default:
|
||
|
d.Fatalf("unknown type %d", typ)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func amf0Decode(d *decode.D, in interface{}) interface{} {
|
||
|
amf0DecodeValue(d)
|
||
|
return nil
|
||
|
}
|