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