1
1
mirror of https://github.com/wader/fq.git synced 2024-11-26 21:55:57 +03:00
fq/format/flv/flv.go
2022-07-19 18:33:50 +02:00

187 lines
4.1 KiB
Go

//go:build ignore
package flv
// TODO: make it useful
// https://rtmp.veriskope.com/pdf/video_file_format_spec_v10.pdf
import (
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/registry"
"github.com/wader/fq/pkg/scalar"
)
func init() {
registry.MustRegister(decode.Format{
Name: format.FLV,
Description: "Flash video",
Groups: []string{format.PROBE},
DecodeFn: flvDecode,
})
}
const (
audioData = 8
videoData = 9
scriptDataObject = 18
)
var tagTypeNames = scalar.UToSymStr{
audioData: "audioData",
videoData: "videoData",
scriptDataObject: "scriptDataObject",
}
const (
typeNumber = 0
typeBoolean = 1
typeString = 2
typeObject = 3
typeMovieClip = 4
typeNull = 5
typeUndefined = 6
typeReference = 7
typeECMAArray = 8
typeObjectEnd = 9
typeStrictArray = 10
typeDate = 11
typeLongString = 12
)
var typeNames = scalar.UToSymStr{
typeNumber: "Number",
typeBoolean: "Boolean",
typeString: "String",
typeObject: "Object",
typeMovieClip: "MovieClip",
typeNull: "Null",
typeUndefined: "Undefined",
typeReference: "Reference",
typeECMAArray: "ECMAArray",
typeObjectEnd: "ObjectEnd",
typeStrictArray: "StrictArray",
typeDate: "Date",
typeLongString: "LongString",
}
func flvDecode(d *decode.D, _ any) any {
var fieldScriptDataObject func()
var fieldScriptDataVariable func(d *decode.D, name string)
fieldScriptDataString := func(d *decode.D, name string) {
d.FieldStrFn(name, func(d *decode.D) string {
l := d.U16()
return d.UTF8(int(l))
})
}
fieldScriptDataStringLong := func(d *decode.D, name string) {
d.FieldStrFn(name, func(d *decode.D) string {
l := d.U32()
return d.UTF8(int(l))
})
}
fieldScriptDataVariable = func(d *decode.D, name string) {
d.FieldStruct(name, func(d *decode.D) {
fieldScriptDataString(d, "name")
fieldScriptDataString(d, "data")
})
}
fieldScriptDataValue := func(d *decode.D, _ string) uint64 {
typ := d.FieldU8("type", typeNames)
if typ == typeECMAArray {
d.FieldU32("ecma_array_length")
}
switch typ {
case typeNumber:
d.FieldF64("number")
case typeBoolean:
d.FieldU8("boolean")
case typeString:
fieldScriptDataString(d, "string")
case typeObject:
fieldScriptDataObject()
case typeMovieClip:
fieldScriptDataString(d, "path")
case typeNull:
case typeUndefined:
case typeReference:
d.FieldU16("reference")
case typeECMAArray:
d.FieldArray("array", func(d *decode.D) {
for {
if d.PeekBits(24) == typeObjectEnd { // variableEnd?
d.FieldU24("end")
break
}
fieldScriptDataVariable(d, "sasdadas")
}
})
case typeStrictArray:
length := d.FieldU32("length")
for i := uint64(0); i < length; i++ {
fieldScriptDataVariable(d, "sasdadas")
}
case typeDate:
d.FieldF64("date_time")
d.FieldS16("local_data_time_offset")
case typeLongString:
fieldScriptDataStringLong(d, "asdsad")
case typeObjectEnd: // variableEnd also?
}
return typ
}
fieldScriptDataObject = func() {
d.FieldStruct("object", func(d *decode.D) {
fieldScriptDataString(d, "name")
fieldScriptDataValue(d, "data")
})
}
d.FieldUTF8("signature", 3, d.AssertStr("FLV"))
d.FieldU8("version")
d.FieldU5("type_flags_reserved", d.AssertU(0))
d.FieldU1("type_flags_audio")
d.FieldU1("type_flags_reserved", d.AssertU(0))
d.FieldU1("type_flags_video")
dataOffset := d.FieldU32("data_offset")
d.SeekAbs(int64(dataOffset) * 8)
d.FieldArray("tags", func(d *decode.D) {
for !d.End() {
d.FieldStruct("tag", func(d *decode.D) {
d.FieldU32("previous_tag_size")
tagType := d.FieldU8("tag_type", tagTypeNames)
dataSize := d.FieldU24("data_size")
d.FieldU24("timestamp")
d.FieldU8("timestamp_extended")
d.FieldU24("stream_id")
switch tagType {
case audioData, videoData:
d.SeekRel(int64(dataSize) * 8)
case scriptDataObject:
for {
if d.PeekBits(24) == typeObjectEnd {
d.FieldU24("end")
break
}
fieldScriptDataObject()
}
}
})
}
})
return nil
}