add AV1 format (#252)

This commit is contained in:
Alessandro Ros
2023-04-15 13:10:56 +02:00
committed by GitHub
parent f5aedf9b7f
commit fee147222e
6 changed files with 155 additions and 0 deletions

View File

@@ -99,6 +99,7 @@ https://pkg.go.dev/github.com/bluenviron/gortsplib/v3#pkg-index
* [RFC2326, RTSP 1.0](https://datatracker.ietf.org/doc/html/rfc2326) * [RFC2326, RTSP 1.0](https://datatracker.ietf.org/doc/html/rfc2326)
* [RFC7826, RTSP 2.0](https://datatracker.ietf.org/doc/html/rfc7826) * [RFC7826, RTSP 2.0](https://datatracker.ietf.org/doc/html/rfc7826)
* [RFC8866, SDP: Session Description Protocol](https://datatracker.ietf.org/doc/html/rfc8866)
* [RFC3551, RTP Profile for Audio and Video Conferences with Minimal Control](https://datatracker.ietf.org/doc/html/rfc3551) * [RFC3551, RTP Profile for Audio and Video Conferences with Minimal Control](https://datatracker.ietf.org/doc/html/rfc3551)
* [RFC2250, RTP Payload Format for MPEG1/MPEG2 Video](https://datatracker.ietf.org/doc/html/rfc2250) * [RFC2250, RTP Payload Format for MPEG1/MPEG2 Video](https://datatracker.ietf.org/doc/html/rfc2250)
* [RFC2435, RTP Payload Format for JPEG-compressed Video](https://datatracker.ietf.org/doc/html/rfc2435) * [RFC2435, RTP Payload Format for JPEG-compressed Video](https://datatracker.ietf.org/doc/html/rfc2435)
@@ -111,6 +112,7 @@ https://pkg.go.dev/github.com/bluenviron/gortsplib/v3#pkg-index
* [RFC5215, RTP Payload Format for Vorbis Encoded Audio](https://datatracker.ietf.org/doc/html/rfc5215) * [RFC5215, RTP Payload Format for Vorbis Encoded Audio](https://datatracker.ietf.org/doc/html/rfc5215)
* [RFC7587, RTP Payload Format for the Opus Speech and Audio Codec](https://datatracker.ietf.org/doc/html/rfc7587) * [RFC7587, RTP Payload Format for the Opus Speech and Audio Codec](https://datatracker.ietf.org/doc/html/rfc7587)
* [RFC3640, RTP Payload Format for Transport of MPEG-4 Elementary Streams](https://datatracker.ietf.org/doc/html/rfc3640) * [RFC3640, RTP Payload Format for Transport of MPEG-4 Elementary Streams](https://datatracker.ietf.org/doc/html/rfc3640)
* [RTP Payload Format For AV1 (v1.0)](https://aomediacodec.github.io/av1-rtp-spec/)
* [ITU-T Rec. H.264 (08/2021)](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-202108-I!!PDF-E&type=items) * [ITU-T Rec. H.264 (08/2021)](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-202108-I!!PDF-E&type=items)
* [ITU-T Rec. H.265 (08/2021)](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.265-202108-I!!PDF-E&type=items) * [ITU-T Rec. H.265 (08/2021)](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.265-202108-I!!PDF-E&type=items)
* ISO 14496-3, Coding of audio-visual objects, part 3, Audio * ISO 14496-3, Coding of audio-visual objects, part 3, Audio

91
pkg/formats/av1.go Normal file
View File

@@ -0,0 +1,91 @@
package formats
import (
"fmt"
"strconv"
"github.com/pion/rtp"
)
// AV1 is a RTP format that uses the AV1 codec.
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
type AV1 struct {
PayloadTyp uint8
LevelIdx *int
Profile *int
Tier *int
}
// String implements Format.
func (f *AV1) String() string {
return "AV1"
}
// ClockRate implements Format.
func (f *AV1) ClockRate() int {
return 90000
}
// PayloadType implements Format.
func (f *AV1) PayloadType() uint8 {
return f.PayloadTyp
}
func (f *AV1) unmarshal(payloadType uint8, clock string, codec string, rtpmap string, fmtp map[string]string) error {
f.PayloadTyp = payloadType
for key, val := range fmtp {
switch key {
case "level-idx":
n, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return fmt.Errorf("invalid level-idx: %v", val)
}
v2 := int(n)
f.LevelIdx = &v2
case "profile":
n, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return fmt.Errorf("invalid profile: %v", val)
}
v2 := int(n)
f.Profile = &v2
case "tier":
n, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return fmt.Errorf("invalid tier: %v", val)
}
v2 := int(n)
f.Tier = &v2
}
}
return nil
}
// Marshal implements Format.
func (f *AV1) Marshal() (string, map[string]string) {
fmtp := make(map[string]string)
if f.LevelIdx != nil {
fmtp["level-idx"] = strconv.FormatInt(int64(*f.LevelIdx), 10)
}
if f.Profile != nil {
fmtp["profile"] = strconv.FormatInt(int64(*f.Profile), 10)
}
if f.Tier != nil {
fmtp["tier"] = strconv.FormatInt(int64(*f.Tier), 10)
}
return "AV1/90000", fmtp
}
// PTSEqualsDTS implements Format.
func (f *AV1) PTSEqualsDTS(*rtp.Packet) bool {
return true
}

18
pkg/formats/av1_test.go Normal file
View File

@@ -0,0 +1,18 @@
package formats //nolint:dupl
import (
"testing"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestAV1Attributes(t *testing.T) {
format := &AV1{
PayloadTyp: 100,
}
require.Equal(t, "AV1", format.String())
require.Equal(t, 90000, format.ClockRate())
require.Equal(t, uint8(100), format.PayloadType())
require.Equal(t, true, format.PTSEqualsDTS(&rtp.Packet{}))
}

View File

@@ -68,6 +68,9 @@ func Unmarshal(mediaType string, payloadType uint8, rtpMap string, fmtp map[stri
case codec == "vp9" && clock == "90000": case codec == "vp9" && clock == "90000":
return &VP9{} return &VP9{}
case codec == "av1" && clock == "90000":
return &AV1{}
} }
case mediaType == "audio": case mediaType == "audio":

View File

@@ -578,6 +578,29 @@ var casesFormat = []struct {
"profile-id": "789", "profile-id": "789",
}, },
}, },
{
"video av1",
"video",
96,
"AV1/90000",
map[string]string{
"profile": "2",
"level-idx": "8",
"tier": "1",
},
&AV1{
PayloadTyp: 96,
Profile: intPtr(2),
LevelIdx: intPtr(8),
Tier: intPtr(1),
},
"AV1/90000",
map[string]string{
"profile": "2",
"level-idx": "8",
"tier": "1",
},
},
{ {
"application", "application",
"application", "application",
@@ -764,6 +787,23 @@ func TestUnmarshalMPEG4AudioLATMErrors(t *testing.T) {
require.Error(t, err) require.Error(t, err)
} }
func TestUnmarshalAV1Errors(t *testing.T) {
_, err := Unmarshal("video", 96, "AV1/90000", map[string]string{
"level-idx": "aaa",
})
require.Error(t, err)
_, err = Unmarshal("video", 96, "AV1/90000", map[string]string{
"profile": "aaa",
})
require.Error(t, err)
_, err = Unmarshal("video", 96, "AV1/90000", map[string]string{
"tier": "aaa",
})
require.Error(t, err)
}
func FuzzUnmarshalH264(f *testing.F) { func FuzzUnmarshalH264(f *testing.F) {
f.Fuzz(func(t *testing.T, sps string, pktMode string) { f.Fuzz(func(t *testing.T, sps string, pktMode string) {
Unmarshal("video", 96, "H264/90000", map[string]string{ Unmarshal("video", 96, "H264/90000", map[string]string{

View File

@@ -73,6 +73,7 @@ func (f *VP9) unmarshal(payloadType uint8, clock string, codec string, rtpmap st
// Marshal implements Format. // Marshal implements Format.
func (f *VP9) Marshal() (string, map[string]string) { func (f *VP9) Marshal() (string, map[string]string) {
fmtp := make(map[string]string) fmtp := make(map[string]string)
if f.MaxFR != nil { if f.MaxFR != nil {
fmtp["max-fr"] = strconv.FormatInt(int64(*f.MaxFR), 10) fmtp["max-fr"] = strconv.FormatInt(int64(*f.MaxFR), 10)
} }