Move packet parsing code into its own package.

This commit is contained in:
Juliusz Chroboczek
2021-07-29 21:30:12 +02:00
parent 8178275164
commit 89780b866b
5 changed files with 101 additions and 99 deletions
-294
View File
@@ -1,294 +0,0 @@
package rtpconn
import (
"errors"
"strings"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
)
// isKeyframe determines if packet is the start of a keyframe.
// It returns (true, true) if that is the case, (false, true) if that is
// definitely not the case, and (false, false) if the information cannot
// be determined.
func isKeyframe(codec string, packet *rtp.Packet) (bool, bool) {
if strings.EqualFold(codec, "video/vp8") {
var vp8 codecs.VP8Packet
_, err := vp8.Unmarshal(packet.Payload)
if err != nil || len(vp8.Payload) < 1 {
return false, false
}
if vp8.S != 0 && vp8.PID == 0 && (vp8.Payload[0]&0x1) == 0 {
return true, true
}
return false, true
} else if strings.EqualFold(codec, "video/vp9") {
var vp9 codecs.VP9Packet
_, err := vp9.Unmarshal(packet.Payload)
if err != nil || len(vp9.Payload) < 1 {
return false, false
}
if !vp9.B {
return false, true
}
if (vp9.Payload[0] & 0xc0) != 0x80 {
return false, false
}
profile := (vp9.Payload[0] >> 4) & 0x3
if profile != 3 {
return (vp9.Payload[0] & 0xC) == 0, true
}
return (vp9.Payload[0] & 0x6) == 0, true
} else if strings.EqualFold(codec, "video/av1x") {
if len(packet.Payload) < 2 {
return false, true
}
// Z=0, N=1
if (packet.Payload[0] & 0x88) != 0x08 {
return false, true
}
w := (packet.Payload[0] & 0x30) >> 4
getObu := func(data []byte, last bool) ([]byte, int, bool) {
if last {
return data, len(data), false
}
offset := 0
length := 0
for {
if len(data) <= offset {
return nil, offset, offset > 0
}
l := data[offset]
length |= int(l&0x7f) << (offset * 7)
offset++
if (l & 0x80) == 0 {
break
}
}
if len(data) < offset+length {
return data[offset:], len(data), true
}
return data[offset : offset+length],
offset + length, false
}
offset := 1
i := 0
for {
obu, length, truncated :=
getObu(packet.Payload[offset:], int(w) == i+1)
if len(obu) < 1 {
return false, false
}
tpe := (obu[0] & 0x38) >> 3
switch i {
case 0:
// OBU_SEQUENCE_HEADER
if tpe != 1 {
return false, true
}
default:
// OBU_FRAME_HEADER or OBU_FRAME
if tpe == 3 || tpe == 6 {
if len(obu) < 2 {
return false, false
}
// show_existing_frame == 0
if (obu[1] & 0x80) != 0 {
return false, true
}
// frame_type == KEY_FRAME
return (obu[1] & 0x60) == 0, true
}
}
if truncated || i >= int(w) {
// the first frame header is in a second
// packet, give up.
return false, false
}
offset += length
i++
}
} else if strings.EqualFold(codec, "video/h264") {
if len(packet.Payload) < 1 {
return false, false
}
nalu := packet.Payload[0] & 0x1F
if nalu == 0 {
// reserved
return false, false
} else if nalu <= 23 {
// simple NALU
return nalu == 5, true
} else if nalu == 24 || nalu == 25 || nalu == 26 || nalu == 27 {
// STAP-A, STAP-B, MTAP16 or MTAP24
i := 1
if nalu == 25 || nalu == 26 || nalu == 27 {
// skip DON
i += 2
}
for i < len(packet.Payload) {
if i+2 > len(packet.Payload) {
return false, false
}
length := uint16(packet.Payload[i])<<8 |
uint16(packet.Payload[i+1])
i += 2
if i+int(length) > len(packet.Payload) {
return false, false
}
offset := 0
if nalu == 26 {
offset = 3
} else if nalu == 27 {
offset = 4
}
if offset >= int(length) {
return false, false
}
n := packet.Payload[i+offset] & 0x1F
if n == 7 {
return true, true
} else if n >= 24 {
// is this legal?
return false, false
}
i += int(length)
}
if i == len(packet.Payload) {
return false, true
}
return false, false
} else if nalu == 28 || nalu == 29 {
// FU-A or FU-B
if len(packet.Payload) < 2 {
return false, false
}
if (packet.Payload[1] & 0x80) == 0 {
// not a starting fragment
return false, true
}
return (packet.Payload[1]&0x1F == 7), true
}
return false, false
}
return false, false
}
var errTruncated = errors.New("truncated packet")
var errUnsupportedCodec = errors.New("unsupported codec")
type packetFlags struct {
seqno uint16
start bool
pid uint16 // only if it needs rewriting
tid uint8
sid uint8
tidupsync bool
sidsync bool
sidnonreference bool
discardable bool
}
func getPacketFlags(codec string, buf []byte) (packetFlags, error) {
if len(buf) < 12 {
return packetFlags{}, errTruncated
}
var flags packetFlags
flags.seqno = (uint16(buf[2]) << 8) | uint16(buf[3])
if strings.EqualFold(codec, "video/vp8") {
var packet rtp.Packet
err := packet.Unmarshal(buf)
if err != nil {
return flags, err
}
var vp8 codecs.VP8Packet
_, err = vp8.Unmarshal(packet.Payload)
if err != nil {
return flags, err
}
flags.start = vp8.S == 1 && vp8.PID == 0
flags.pid = vp8.PictureID
flags.tid = vp8.TID
flags.tidupsync = vp8.Y == 1
flags.discardable = vp8.N == 1
return flags, nil
} else if strings.EqualFold(codec, "video/vp9") {
var packet rtp.Packet
err := packet.Unmarshal(buf)
if err != nil {
return flags, err
}
var vp9 codecs.VP9Packet
_, err = vp9.Unmarshal(packet.Payload)
if err != nil {
return flags, err
}
flags.start = vp9.B
flags.tid = vp9.TID
flags.sid = vp9.SID
flags.tidupsync = vp9.U
flags.sidsync = vp9.P
// not yet in pion/rtp
flags.sidnonreference = (packet.Payload[0] & 0x01) != 0
return flags, nil
}
return flags, nil
}
func rewritePacket(codec string, data []byte, seqno uint16, delta uint16) error {
if len(data) < 12 {
return errTruncated
}
data[2] = uint8(seqno >> 8)
data[3] = uint8(seqno)
if delta == 0 {
return nil
}
offset := 12
offset += int(data[0]&0x0F) * 4
if len(data) < offset+4 {
return errTruncated
}
if (data[0] & 0x10) != 0 {
length := uint16(data[offset+2])<<8 | uint16(data[offset+3])
offset += 4 + int(length)*4
if len(data) < offset+4 {
return errTruncated
}
}
if strings.EqualFold(codec, "video/vp8") {
x := (data[offset] & 0x80) != 0
if !x {
return nil
}
i := (data[offset+1] & 0x80) != 0
if !i {
return nil
}
m := (data[offset+2] & 0x80) != 0
if m {
pid := (uint16(data[offset+2]&0x7F) << 8) |
uint16(data[offset+3])
pid = (pid + delta) & 0x7FFF
data[offset+2] = 0x80 | byte((pid>>8)&0x7F)
data[offset+3] = byte(pid & 0xFF)
} else {
data[offset+2] = (data[offset+2] + uint8(delta)) & 0x7F
}
return nil
}
return errUnsupportedCodec
}
-46
View File
@@ -1,46 +0,0 @@
package rtpconn
import (
"testing"
)
var vp8 = []byte{
0x80, 0, 0, 42,
0, 0, 0, 0,
0, 0, 0, 0,
0x90, 0x80, 0x80, 57,
0, 0, 0, 0,
}
func TestPacketFlags(t *testing.T) {
buf := append([]byte{}, vp8...)
flags, err := getPacketFlags("video/vp8", buf)
if flags.seqno != 42 || !flags.start || flags.pid != 57 ||
flags.sid != 0 || flags.tid != 0 ||
flags.tidupsync || flags.discardable || err != nil {
t.Errorf("Got %v, %v, %v, %v, %v, %v (%v)",
flags.seqno, flags.start, flags.pid, flags.sid,
flags.tidupsync, flags.discardable, err,
)
}
}
func TestRewrite(t *testing.T) {
for i := uint16(0); i < 0x7fff; i++ {
buf := append([]byte{}, vp8...)
err := rewritePacket("video/vp8", buf, i, i)
if err != nil {
t.Errorf("rewrite: %v", err)
continue
}
flags, err := getPacketFlags("video/vp8", buf)
if err != nil || flags.seqno != i ||
flags.pid != (57 + i) & 0x7FFF {
t.Errorf("Expected %v %v, got %v %v (%v)",
i, (57 + i) & 0x7FFF,
flags.seqno, flags.pid, err)
}
}
}
+21 -20
View File
@@ -14,6 +14,7 @@ import (
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3"
"github.com/jech/galene/codecs"
"github.com/jech/galene/conn"
"github.com/jech/galene/estimator"
"github.com/jech/galene/group"
@@ -213,59 +214,59 @@ var packetBufPool = sync.Pool{
func (down *rtpDownTrack) Write(buf []byte) (int, error) {
codec := down.remote.Codec().MimeType
flags, err := getPacketFlags(codec, buf)
flags, err := codecs.PacketFlags(codec, buf)
if err != nil {
return 0, err
}
layer := down.getLayerInfo()
if flags.tid > layer.maxTid || flags.sid > layer.maxSid {
if flags.tid > layer.maxTid {
if flags.Tid > layer.maxTid || flags.Sid > layer.maxSid {
if flags.Tid > layer.maxTid {
if layer.tid == layer.maxTid {
layer.wantedTid = flags.tid
layer.tid = flags.tid
layer.wantedTid = flags.Tid
layer.tid = flags.Tid
}
layer.maxTid = flags.tid
layer.maxTid = flags.Tid
}
if flags.sid > layer.maxSid {
if flags.Sid > layer.maxSid {
if layer.sid == layer.maxSid {
layer.wantedSid = flags.sid
layer.sid = flags.sid
layer.wantedSid = flags.Sid
layer.sid = flags.Sid
}
layer.maxSid = flags.sid
layer.maxSid = flags.Sid
}
down.setLayerInfo(layer)
down.adjustLayer()
}
if flags.start && (layer.tid != layer.wantedTid) {
if layer.wantedTid < layer.tid || flags.tidupsync {
if flags.Start && (layer.tid != layer.wantedTid) {
if layer.wantedTid < layer.tid || flags.TidUpSync {
layer.tid = layer.wantedTid
down.setLayerInfo(layer)
}
}
if flags.start && (layer.sid != layer.wantedSid) {
if flags.sidsync {
if flags.Start && (layer.sid != layer.wantedSid) {
if flags.SidSync {
layer.sid = layer.wantedTid
down.setLayerInfo(layer)
}
}
if flags.tid > layer.tid || flags.sid > layer.sid ||
(flags.sid < layer.sid && flags.sidnonreference) {
ok := down.packetmap.Drop(flags.seqno, flags.pid)
if flags.Tid > layer.tid || flags.Sid > layer.sid ||
(flags.Sid < layer.sid && flags.SidNonReference) {
ok := down.packetmap.Drop(flags.Seqno, flags.Pid)
if ok {
return 0, nil
}
}
ok, newseqno, piddelta := down.packetmap.Map(flags.seqno, flags.pid)
ok, newseqno, piddelta := down.packetmap.Map(flags.Seqno, flags.Pid)
if !ok {
return 0, nil
}
if newseqno == flags.seqno && piddelta == 0 {
if newseqno == flags.Seqno && piddelta == 0 {
return down.write(buf)
}
@@ -274,7 +275,7 @@ func (down *rtpDownTrack) Write(buf []byte) (int, error) {
buf2 := ibuf2.([]byte)
n := copy(buf2, buf)
err = rewritePacket(codec, buf2[:n], newseqno, piddelta)
err = codecs.RewritePacket(codec, buf2[:n], newseqno, piddelta)
if err != nil {
return 0, err
}
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/jech/galene/codecs"
"github.com/jech/galene/packetcache"
"github.com/jech/galene/rtptime"
)
@@ -74,7 +75,7 @@ func readLoop(track *rtpUpTrack) {
track.jitter.Accumulate(packet.Timestamp)
kf, kfKnown := isKeyframe(codec.MimeType, &packet)
kf, kfKnown := codecs.Keyframe(codec.MimeType, &packet)
if kf || !kfKnown {
kfNeeded = false
}