Add RTMP packet formats
This commit is contained in:
parent
ce7b360dff
commit
3001499cae
3 changed files with 147 additions and 0 deletions
89
atalk/rtmp/data.go
Normal file
89
atalk/rtmp/data.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package rtmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/sfiera/multitalk/pkg/ddp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataPacket represents an RTMP Data packet.
|
||||||
|
type DataPacket struct {
|
||||||
|
RouterAddr ddp.Addr
|
||||||
|
NetworkTuples []NetworkTuple
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkTuple represents routing information.
|
||||||
|
type NetworkTuple struct {
|
||||||
|
Extended bool
|
||||||
|
RangeStart ddp.Network
|
||||||
|
RangeEnd ddp.Network
|
||||||
|
Distance uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalDataPacket unmarshals a DataPacket.
|
||||||
|
func UnmarshalDataPacket(data []byte) (*DataPacket, error) {
|
||||||
|
if len(data) < 7 || (len(data)-4)%3 != 0 {
|
||||||
|
return nil, fmt.Errorf("invalid input length %d for RTMP Data packet", len(data))
|
||||||
|
}
|
||||||
|
if data[2] != 8 {
|
||||||
|
return nil, fmt.Errorf("unsupported node ID length %d for RTMP Data packet", data[2])
|
||||||
|
}
|
||||||
|
dp := &DataPacket{
|
||||||
|
RouterAddr: ddp.Addr{
|
||||||
|
Network: ddp.Network(binary.BigEndian.Uint16(data[:2])),
|
||||||
|
Node: ddp.Node(data[3]),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
data = data[4:]
|
||||||
|
|
||||||
|
first := true
|
||||||
|
for len(data) > 0 {
|
||||||
|
if len(data) < 3 {
|
||||||
|
return nil, fmt.Errorf("insufficient remaining input length %d for RTMP Data network tuple", len(data))
|
||||||
|
}
|
||||||
|
nt := NetworkTuple{
|
||||||
|
RangeStart: ddp.Network(binary.BigEndian.Uint16(data[:2])),
|
||||||
|
Distance: data[2],
|
||||||
|
}
|
||||||
|
data = data[3:]
|
||||||
|
if nt.RangeStart == 0 {
|
||||||
|
// if non-extended, first tuple should contain version
|
||||||
|
if !first {
|
||||||
|
return nil, fmt.Errorf("invalid RTMP network tuple range start 0")
|
||||||
|
}
|
||||||
|
// initial non-extended tuple with Distance field containing version
|
||||||
|
if nt.Distance != 0x82 {
|
||||||
|
return nil, fmt.Errorf("unsupported RTMP version %x", nt.Distance)
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nt.Extended = nt.Distance&0x80 != 0
|
||||||
|
if !nt.Extended {
|
||||||
|
// ordinary non-extended tuple
|
||||||
|
if first && nt.RangeStart != 0 {
|
||||||
|
return nil, fmt.Errorf("first RTMP network tuple is not version tuple")
|
||||||
|
}
|
||||||
|
dp.NetworkTuples = append(dp.NetworkTuples, nt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// extended tuple
|
||||||
|
if len(data) < 3 {
|
||||||
|
return nil, fmt.Errorf("insufficient remaining input length %d for RTMP Data extended network tuple", len(data))
|
||||||
|
}
|
||||||
|
nt.Distance &^= 0x80
|
||||||
|
nt.RangeEnd = ddp.Network(binary.BigEndian.Uint16(data[:2]))
|
||||||
|
if first {
|
||||||
|
if data[2] != 0x82 {
|
||||||
|
return nil, fmt.Errorf("unsupported RTMP version %x", data[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
dp.NetworkTuples = append(dp.NetworkTuples, nt)
|
||||||
|
data = data[3:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp, nil
|
||||||
|
}
|
15
atalk/rtmp/request.go
Normal file
15
atalk/rtmp/request.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package rtmp
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// RequestPacket represents an RTMP Request or RTMP Route Data Request packet.
|
||||||
|
type RequestPacket struct {
|
||||||
|
Function uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnmarshalRequestPacket(data []byte) (*RequestPacket, error) {
|
||||||
|
if len(data) != 1 {
|
||||||
|
return nil, fmt.Errorf("invalid data length %d for RTMP Request or RTMP RDR packet", len(data))
|
||||||
|
}
|
||||||
|
return &RequestPacket{Function: data[0]}, nil
|
||||||
|
}
|
43
atalk/rtmp/response.go
Normal file
43
atalk/rtmp/response.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package rtmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/sfiera/multitalk/pkg/ddp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResponsePacket struct {
|
||||||
|
SenderAddr ddp.Addr
|
||||||
|
Extended bool
|
||||||
|
RangeStart ddp.Network
|
||||||
|
RangeEnd ddp.Network
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnmarshalResponsePacket(data []byte) (*ResponsePacket, error) {
|
||||||
|
if len(data) != 4 && len(data) != 10 {
|
||||||
|
return nil, fmt.Errorf("invalid input length %d for RTMP Response packet", len(data))
|
||||||
|
}
|
||||||
|
if data[2] != 8 {
|
||||||
|
return nil, fmt.Errorf("unsupported node ID length %d for RTMP Response packet", data[2])
|
||||||
|
}
|
||||||
|
rp := &ResponsePacket{
|
||||||
|
SenderAddr: ddp.Addr{
|
||||||
|
Network: ddp.Network(binary.BigEndian.Uint16(data[:2])),
|
||||||
|
Node: ddp.Node(data[3]),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if len(data) == 4 {
|
||||||
|
return rp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rp.RangeStart = ddp.Network(binary.BigEndian.Uint16(data[4:6]))
|
||||||
|
if data[6] != 0x80 {
|
||||||
|
return nil, fmt.Errorf("invalid intermediate byte %x for RTMP Response packet", data[6])
|
||||||
|
}
|
||||||
|
rp.RangeEnd = ddp.Network(binary.BigEndian.Uint16(data[7:9]))
|
||||||
|
if data[9] != 0x82 {
|
||||||
|
return nil, fmt.Errorf("unsupported version %x for RTMP Response packet", data[9])
|
||||||
|
}
|
||||||
|
return rp, nil
|
||||||
|
}
|
Loading…
Reference in a new issue