141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
/*
|
|
Copyright 2024 Josh Deprez
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package rtmp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/sfiera/multitalk/pkg/ddp"
|
|
)
|
|
|
|
// DataPacket represents an RTMP Data packet.
|
|
type DataPacket struct {
|
|
RouterAddr ddp.Addr
|
|
Extended bool
|
|
NetworkTuples []NetworkTuple
|
|
}
|
|
|
|
// NetworkTuple represents routing information.
|
|
type NetworkTuple struct {
|
|
Extended bool
|
|
RangeStart ddp.Network
|
|
RangeEnd ddp.Network
|
|
Distance uint8
|
|
}
|
|
|
|
func (nt *NetworkTuple) Size() int {
|
|
if nt.Extended {
|
|
return 6
|
|
} else {
|
|
return 3
|
|
}
|
|
}
|
|
|
|
// Marshal marshals an RTMP Data packet.
|
|
func (dp *DataPacket) Marshal() ([]byte, error) {
|
|
b := bytes.NewBuffer(nil)
|
|
write16(b, dp.RouterAddr.Network)
|
|
b.WriteByte(8)
|
|
b.WriteByte(byte(dp.RouterAddr.Node))
|
|
if !dp.Extended {
|
|
write16(b, uint16(0))
|
|
b.WriteByte(0x82)
|
|
}
|
|
for _, nt := range dp.NetworkTuples {
|
|
write16(b, nt.RangeStart)
|
|
if !nt.Extended {
|
|
b.WriteByte(nt.Distance)
|
|
continue
|
|
}
|
|
b.WriteByte(nt.Distance | 0x80)
|
|
write16(b, nt.RangeEnd)
|
|
b.WriteByte(0x82)
|
|
}
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
// 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]),
|
|
},
|
|
Extended: true,
|
|
}
|
|
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)
|
|
}
|
|
dp.Extended = false
|
|
first = false
|
|
continue
|
|
}
|
|
nt.Extended = nt.Distance&0x80 != 0
|
|
if !nt.Extended {
|
|
// ordinary non-extended tuple
|
|
nt.RangeEnd = nt.RangeStart
|
|
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
|
|
}
|