mirror of
https://github.com/aler9/gortsplib
synced 2025-11-02 21:14:07 +08:00
add TrackVP9
This commit is contained in:
3
track.go
3
track.go
@@ -74,6 +74,9 @@ func newTrackFromMediaDescription(md *psdp.MediaDescription) (Track, error) {
|
||||
|
||||
case rtpmapPart1 == "H265/90000":
|
||||
return newTrackH265FromMediaDescription(control, payloadType, md)
|
||||
|
||||
case rtpmapPart1 == "VP9/90000":
|
||||
return newTrackVP9FromMediaDescription(control, payloadType, md)
|
||||
}
|
||||
|
||||
case md.MediaName.Media == "audio":
|
||||
|
||||
@@ -381,6 +381,25 @@ func TestTrackNewFromMediaDescription(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"vp9",
|
||||
&psdp.MediaDescription{
|
||||
MediaName: psdp.MediaName{
|
||||
Media: "video",
|
||||
Protos: []string{"RTP", "AVP"},
|
||||
Formats: []string{"96"},
|
||||
},
|
||||
Attributes: []psdp.Attribute{
|
||||
{
|
||||
Key: "rtpmap",
|
||||
Value: "96 VP9/90000",
|
||||
},
|
||||
},
|
||||
},
|
||||
&TrackVP9{
|
||||
payloadType: 96,
|
||||
},
|
||||
},
|
||||
{
|
||||
"multiple formats",
|
||||
&psdp.MediaDescription{
|
||||
|
||||
171
track_vp9.go
Normal file
171
track_vp9.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package gortsplib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
psdp "github.com/pion/sdp/v3"
|
||||
)
|
||||
|
||||
// TrackVP9 is a VP9 track.
|
||||
type TrackVP9 struct {
|
||||
trackBase
|
||||
payloadType uint8
|
||||
maxFR *int
|
||||
maxFS *int
|
||||
profileID *int
|
||||
}
|
||||
|
||||
// NewTrackVP9 allocates a TrackVP9.
|
||||
func NewTrackVP9(payloadType uint8, maxFR *int, maxFS *int, profileID *int) *TrackVP9 {
|
||||
return &TrackVP9{
|
||||
payloadType: payloadType,
|
||||
maxFR: maxFR,
|
||||
maxFS: maxFS,
|
||||
profileID: profileID,
|
||||
}
|
||||
}
|
||||
|
||||
func newTrackVP9FromMediaDescription(
|
||||
control string,
|
||||
payloadType uint8,
|
||||
md *psdp.MediaDescription,
|
||||
) (*TrackVP9, error) {
|
||||
t := &TrackVP9{
|
||||
trackBase: trackBase{
|
||||
control: control,
|
||||
},
|
||||
payloadType: payloadType,
|
||||
}
|
||||
|
||||
t.fillParamsFromMediaDescription(md)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *TrackVP9) fillParamsFromMediaDescription(md *psdp.MediaDescription) error {
|
||||
v, ok := md.Attribute("fmtp")
|
||||
if !ok {
|
||||
return fmt.Errorf("fmtp attribute is missing")
|
||||
}
|
||||
|
||||
tmp := strings.SplitN(v, " ", 2)
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("invalid fmtp attribute (%v)", v)
|
||||
}
|
||||
|
||||
for _, kv := range strings.Split(tmp[1], ";") {
|
||||
kv = strings.Trim(kv, " ")
|
||||
|
||||
if len(kv) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tmp := strings.SplitN(kv, "=", 2)
|
||||
if len(tmp) != 2 {
|
||||
return fmt.Errorf("invalid fmtp attribute (%v)", v)
|
||||
}
|
||||
|
||||
switch tmp[0] {
|
||||
case "max-fr":
|
||||
val, err := strconv.ParseUint(tmp[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid max-fr (%v)", tmp[1])
|
||||
}
|
||||
v2 := int(val)
|
||||
t.maxFR = &v2
|
||||
|
||||
case "max-fs":
|
||||
val, err := strconv.ParseUint(tmp[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid max-fs (%v)", tmp[1])
|
||||
}
|
||||
v2 := int(val)
|
||||
t.maxFS = &v2
|
||||
|
||||
case "profile-id":
|
||||
val, err := strconv.ParseUint(tmp[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid profile-id (%v)", tmp[1])
|
||||
}
|
||||
v2 := int(val)
|
||||
t.profileID = &v2
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClockRate returns the track clock rate.
|
||||
func (t *TrackVP9) ClockRate() int {
|
||||
return 90000
|
||||
}
|
||||
|
||||
func (t *TrackVP9) clone() Track {
|
||||
return &TrackVP9{
|
||||
trackBase: t.trackBase,
|
||||
payloadType: t.payloadType,
|
||||
maxFR: t.maxFR,
|
||||
maxFS: t.maxFS,
|
||||
profileID: t.profileID,
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFR returns the track max-fr.
|
||||
func (t *TrackVP9) MaxFR() *int {
|
||||
return t.maxFR
|
||||
}
|
||||
|
||||
// MaxFS returns the track max-fs.
|
||||
func (t *TrackVP9) MaxFS() *int {
|
||||
return t.maxFS
|
||||
}
|
||||
|
||||
// ProfileID returns the track profile-id.
|
||||
func (t *TrackVP9) ProfileID() *int {
|
||||
return t.profileID
|
||||
}
|
||||
|
||||
// MediaDescription returns the track media description in SDP format.
|
||||
func (t *TrackVP9) MediaDescription() *psdp.MediaDescription {
|
||||
typ := strconv.FormatInt(int64(t.payloadType), 10)
|
||||
|
||||
fmtp := typ
|
||||
|
||||
var tmp []string
|
||||
if t.maxFR != nil {
|
||||
tmp = append(tmp, "max-fr="+strconv.FormatInt(int64(*t.maxFR), 10))
|
||||
}
|
||||
if t.maxFS != nil {
|
||||
tmp = append(tmp, "max-fs="+strconv.FormatInt(int64(*t.maxFS), 10))
|
||||
}
|
||||
if t.profileID != nil {
|
||||
tmp = append(tmp, "profile-id="+strconv.FormatInt(int64(*t.profileID), 10))
|
||||
}
|
||||
if tmp != nil {
|
||||
fmtp += " " + strings.Join(tmp, ";")
|
||||
}
|
||||
|
||||
return &psdp.MediaDescription{
|
||||
MediaName: psdp.MediaName{
|
||||
Media: "video",
|
||||
Protos: []string{"RTP", "AVP"},
|
||||
Formats: []string{typ},
|
||||
},
|
||||
Attributes: []psdp.Attribute{
|
||||
{
|
||||
Key: "rtpmap",
|
||||
Value: typ + " VP9/90000",
|
||||
},
|
||||
{
|
||||
Key: "fmtp",
|
||||
Value: fmtp,
|
||||
},
|
||||
{
|
||||
Key: "control",
|
||||
Value: t.control,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
59
track_vp9_test.go
Normal file
59
track_vp9_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package gortsplib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
psdp "github.com/pion/sdp/v3"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTrackVP9New(t *testing.T) {
|
||||
maxFR := 123
|
||||
maxFS := 456
|
||||
profileID := 789
|
||||
track := NewTrackVP9(96, &maxFR, &maxFS, &profileID)
|
||||
require.Equal(t, "", track.GetControl())
|
||||
require.Equal(t, 123, *track.MaxFR())
|
||||
require.Equal(t, 456, *track.MaxFS())
|
||||
require.Equal(t, 789, *track.ProfileID())
|
||||
}
|
||||
|
||||
func TestTracVP9Clone(t *testing.T) {
|
||||
maxFR := 123
|
||||
maxFS := 456
|
||||
profileID := 789
|
||||
track := NewTrackVP9(96, &maxFR, &maxFS, &profileID)
|
||||
|
||||
clone := track.clone()
|
||||
require.NotSame(t, track, clone)
|
||||
require.Equal(t, track, clone)
|
||||
}
|
||||
|
||||
func TestTrackVP9MediaDescription(t *testing.T) {
|
||||
maxFR := 123
|
||||
maxFS := 456
|
||||
profileID := 789
|
||||
track := NewTrackVP9(96, &maxFR, &maxFS, &profileID)
|
||||
|
||||
require.Equal(t, &psdp.MediaDescription{
|
||||
MediaName: psdp.MediaName{
|
||||
Media: "video",
|
||||
Protos: []string{"RTP", "AVP"},
|
||||
Formats: []string{"96"},
|
||||
},
|
||||
Attributes: []psdp.Attribute{
|
||||
{
|
||||
Key: "rtpmap",
|
||||
Value: "96 VP9/90000",
|
||||
},
|
||||
{
|
||||
Key: "fmtp",
|
||||
Value: "96 max-fr=123;max-fs=456;profile-id=789",
|
||||
},
|
||||
{
|
||||
Key: "control",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
}, track.MediaDescription())
|
||||
}
|
||||
Reference in New Issue
Block a user