From f9ec0f9b43c2a311a0d2d9a155cc86dc7efdb7ab Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Fri, 6 Jan 2023 22:59:20 +0100 Subject: [PATCH] Implement empty VP8 headers. According to RFC 7741 Section 4.4, a packetizer may ignore VP8 partition boundaries. In that case, it might generate VP8 packets with S=0 and no data at all in the header. --- codecs/codecs.go | 35 ++++++++++++++++++++++++----------- codecs/codecs_test.go | 42 +++++++++++++++++++++++++++++++++++++++++- go.mod | 2 ++ go.sum | 6 ++---- 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/codecs/codecs.go b/codecs/codecs.go index 70628f8..8585ac7 100644 --- a/codecs/codecs.go +++ b/codecs/codecs.go @@ -233,7 +233,8 @@ type Flags struct { Start bool End bool Keyframe bool - Pid uint16 // only returned for VP8 + MissingPid bool // only returned for VP8 + Pid uint16 // only returned for VP8 Tid uint8 Sid uint8 TidUpSync bool @@ -243,7 +244,7 @@ type Flags struct { } func PacketFlags(codec string, buf []byte) (Flags, error) { - if len(buf) < 12 { + if len(buf) < 4 { return Flags{}, errTruncated } @@ -267,6 +268,7 @@ func PacketFlags(codec string, buf []byte) (Flags, error) { flags.Start = vp8.S != 0 && vp8.PID == 0 flags.End = packet.Marker flags.Keyframe = vp8.S != 0 && (vp8.Payload[0]&0x1) == 0 + flags.MissingPid = vp8.S == 0 && vp8.I == 0 flags.Pid = vp8.PictureID flags.Tid = vp8.TID flags.TidUpSync = flags.Keyframe || vp8.Y == 1 @@ -309,7 +311,7 @@ func RewritePacket(codec string, data []byte, setMarker bool, seqno uint16, delt return errTruncated } - if(setMarker) { + if setMarker { data[1] |= 0x80 } @@ -321,7 +323,7 @@ func RewritePacket(codec string, data []byte, setMarker bool, seqno uint16, delt offset := 12 offset += int(data[0]&0x0F) * 4 - if len(data) < offset+4 { + if len(data) <= offset { return errTruncated } @@ -339,19 +341,30 @@ func RewritePacket(codec string, data []byte, setMarker bool, seqno uint16, delt if !x { return nil } - i := (data[offset+1] & 0x80) != 0 + offset++ + if len(data) <= offset { + return errTruncated + } + i := (data[offset] & 0x80) != 0 if !i { return nil } - m := (data[offset+2] & 0x80) != 0 + offset++ + if len(data) <= offset { + return errTruncated + } + m := (data[offset] & 0x80) != 0 if m { - pid := (uint16(data[offset+2]&0x7F) << 8) | - uint16(data[offset+3]) + if len(data) <= offset+1 { + return errTruncated + } + pid := (uint16(data[offset]&0x7F) << 8) | + uint16(data[offset+1]) pid = (pid + delta) & 0x7FFF - data[offset+2] = 0x80 | byte((pid>>8)&0x7F) - data[offset+3] = byte(pid & 0xFF) + data[offset] = 0x80 | byte((pid>>8)&0x7F) + data[offset+1] = byte(pid & 0xFF) } else { - data[offset+2] = (data[offset+2] + uint8(delta)) & 0x7F + data[offset] = (data[offset] + uint8(delta)) & 0x7F } return nil } diff --git a/codecs/codecs_test.go b/codecs/codecs_test.go index c276e97..bbb5bd0 100644 --- a/codecs/codecs_test.go +++ b/codecs/codecs_test.go @@ -138,10 +138,19 @@ var vp8 = []byte{ 0, 0, 0, 0, } +var emptyVP8 = []byte{ + 0x80, 0, 0, 42, + 0, 0, 0, 0, + 0, 0, 0, 0, + + 0x00, +} + func TestPacketFlagsVP8(t *testing.T) { buf := append([]byte{}, vp8...) flags, err := PacketFlags("video/vp8", buf) - if flags.Seqno != 42 || !flags.Start || flags.Pid != 57 || + if flags.Seqno != 42 || !flags.Start || + flags.MissingPid || 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)", @@ -151,6 +160,19 @@ func TestPacketFlagsVP8(t *testing.T) { } } +func TestEmptyPacketFlagsVP8(t *testing.T) { + buf := append([]byte{}, emptyVP8...) + flags, err := PacketFlags("video/vp8", buf) + if flags.Seqno != 42 || flags.Start || !flags.MissingPid || + 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 TestRewriteVP8(t *testing.T) { for i := uint16(0); i < 0x7fff; i++ { buf := append([]byte{}, vp8...) @@ -169,6 +191,24 @@ func TestRewriteVP8(t *testing.T) { } } +func TestRewriteEmptyVP8(t *testing.T) { + for i := uint16(0); i < 0x7fff; i++ { + buf := append([]byte{}, emptyVP8...) + err := RewritePacket("video/vp8", buf, true, i, i) + if err != nil { + t.Errorf("rewrite: %v", err) + continue + } + flags, err := PacketFlags("video/vp8", buf) + if err != nil || flags.Seqno != i || + !flags.MissingPid || !flags.Marker { + t.Errorf("Expected %v %v, got %v %v (%v)", + i, (57+i)&0x7FFF, + flags.Seqno, flags.Pid, err) + } + } +} + var vp9 = []byte{ 0x80, 0, 0, 42, 0, 0, 0, 0, diff --git a/go.mod b/go.mod index e162d19..a2ed559 100644 --- a/go.mod +++ b/go.mod @@ -19,3 +19,5 @@ require ( golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f ) + +replace github.com/pion/rtp => github.com/jech/rtp v1.6.6-0.20230106211622-35cc0f7fad45 diff --git a/go.sum b/go.sum index 88b3440..6321834 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jech/cert v0.0.0-20210819231831-aca735647728 h1:tN+W1ll2oKuJGMCaO1CRK4rr+xSRjVSfWmnKlACdx38= github.com/jech/cert v0.0.0-20210819231831-aca735647728/go.mod h1:FXUA/zpiQfV4uBVN2kAwkf3X7pU7l1l2ovS45CsSYZs= +github.com/jech/rtp v1.6.6-0.20230106211622-35cc0f7fad45 h1:VqLy+KBoTQBYgd7dqr1WwJiKmTojw+rTcAnpV97SbXk= +github.com/jech/rtp v1.6.6-0.20230106211622-35cc0f7fad45/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/jech/samplebuilder v0.0.0-20220417174833-7353a593563a h1:yFkaguK4pmi0K33b8TA9T5Qoj0mZHpNPMCFdtru2yDg= github.com/jech/samplebuilder v0.0.0-20220417174833-7353a593563a/go.mod h1:U83y/Kl/5BrI9ceNw17+lmguIrlUlMVntxKmZmei5EA= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -71,10 +73,6 @@ github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqN github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= -github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= -github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= -github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= -github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=