mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-10-24 16:20:23 +08:00
support publishing AV1/H265 with OBS 30 (#2217) (#2234)
Some checks reported warnings
lint / code (push) Has been cancelled
lint / mod-tidy (push) Has been cancelled
lint / apidocs (push) Has been cancelled
test / test64 (push) Has been cancelled
test / test32 (push) Has been cancelled
test / test_highlevel (push) Has been cancelled
Some checks reported warnings
lint / code (push) Has been cancelled
lint / mod-tidy (push) Has been cancelled
lint / apidocs (push) Has been cancelled
test / test64 (push) Has been cancelled
test / test32 (push) Has been cancelled
test / test_highlevel (push) Has been cancelled
This commit is contained in:
@@ -77,15 +77,19 @@ func (m *Audio) Unmarshal(raw *rawmessage.Message) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal implements Message.
|
func (m Audio) marshalBodySize() int {
|
||||||
func (m Audio) Marshal() (*rawmessage.Message, error) {
|
|
||||||
var l int
|
var l int
|
||||||
if m.Codec == CodecMPEG1Audio {
|
if m.Codec == CodecMPEG1Audio {
|
||||||
l = 1 + len(m.Payload)
|
l = 1 + len(m.Payload)
|
||||||
} else {
|
} else {
|
||||||
l = 2 + len(m.Payload)
|
l = 2 + len(m.Payload)
|
||||||
}
|
}
|
||||||
body := make([]byte, l)
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal implements Message.
|
||||||
|
func (m Audio) Marshal() (*rawmessage.Message, error) {
|
||||||
|
body := make([]byte, m.marshalBodySize())
|
||||||
|
|
||||||
body[0] = m.Codec<<4 | m.Rate<<2 | m.Depth<<1 | m.Channels
|
body[0] = m.Codec<<4 | m.Rate<<2 | m.Depth<<1 | m.Channels
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type ExtendedCodedFrames struct {
|
|||||||
ChunkStreamID byte
|
ChunkStreamID byte
|
||||||
DTS time.Duration
|
DTS time.Duration
|
||||||
MessageStreamID uint32
|
MessageStreamID uint32
|
||||||
FourCC [4]byte
|
FourCC FourCC
|
||||||
PTSDelta time.Duration
|
PTSDelta time.Duration
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
@@ -26,7 +26,7 @@ func (m *ExtendedCodedFrames) Unmarshal(raw *rawmessage.Message) error {
|
|||||||
m.ChunkStreamID = raw.ChunkStreamID
|
m.ChunkStreamID = raw.ChunkStreamID
|
||||||
m.DTS = raw.Timestamp
|
m.DTS = raw.Timestamp
|
||||||
m.MessageStreamID = raw.MessageStreamID
|
m.MessageStreamID = raw.MessageStreamID
|
||||||
copy(m.FourCC[:], raw.Body[1:5])
|
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
|
|
||||||
if m.FourCC == FourCCHEVC {
|
if m.FourCC == FourCCHEVC {
|
||||||
m.PTSDelta = time.Duration(uint32(raw.Body[5])<<16|uint32(raw.Body[6])<<8|uint32(raw.Body[7])) * time.Millisecond
|
m.PTSDelta = time.Duration(uint32(raw.Body[5])<<16|uint32(raw.Body[6])<<8|uint32(raw.Body[7])) * time.Millisecond
|
||||||
@@ -38,18 +38,25 @@ func (m *ExtendedCodedFrames) Unmarshal(raw *rawmessage.Message) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal implements Message.
|
func (m ExtendedCodedFrames) marshalBodySize() int {
|
||||||
func (m ExtendedCodedFrames) Marshal() (*rawmessage.Message, error) {
|
|
||||||
var l int
|
var l int
|
||||||
if m.FourCC == FourCCHEVC {
|
if m.FourCC == FourCCHEVC {
|
||||||
l = 8 + len(m.Payload)
|
l = 8 + len(m.Payload)
|
||||||
} else {
|
} else {
|
||||||
l = 5 + len(m.Payload)
|
l = 5 + len(m.Payload)
|
||||||
}
|
}
|
||||||
body := make([]byte, l)
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal implements Message.
|
||||||
|
func (m ExtendedCodedFrames) Marshal() (*rawmessage.Message, error) {
|
||||||
|
body := make([]byte, m.marshalBodySize())
|
||||||
|
|
||||||
body[0] = 0b10000000 | byte(ExtendedTypeCodedFrames)
|
body[0] = 0b10000000 | byte(ExtendedTypeCodedFrames)
|
||||||
copy(body[1:5], m.FourCC[:])
|
body[1] = uint8(m.FourCC >> 24)
|
||||||
|
body[2] = uint8(m.FourCC >> 16)
|
||||||
|
body[3] = uint8(m.FourCC >> 8)
|
||||||
|
body[4] = uint8(m.FourCC)
|
||||||
|
|
||||||
if m.FourCC == FourCCHEVC {
|
if m.FourCC == FourCCHEVC {
|
||||||
tmp := uint32(m.PTSDelta / time.Millisecond)
|
tmp := uint32(m.PTSDelta / time.Millisecond)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bluenviron/mediamtx/internal/rtmp/rawmessage"
|
"github.com/bluenviron/mediamtx/internal/rtmp/rawmessage"
|
||||||
@@ -11,27 +12,38 @@ type ExtendedFramesX struct {
|
|||||||
ChunkStreamID byte
|
ChunkStreamID byte
|
||||||
DTS time.Duration
|
DTS time.Duration
|
||||||
MessageStreamID uint32
|
MessageStreamID uint32
|
||||||
FourCC [4]byte
|
FourCC FourCC
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal implements Message.
|
// Unmarshal implements Message.
|
||||||
func (m *ExtendedFramesX) Unmarshal(raw *rawmessage.Message) error {
|
func (m *ExtendedFramesX) Unmarshal(raw *rawmessage.Message) error {
|
||||||
|
if len(raw.Body) < 6 {
|
||||||
|
return fmt.Errorf("not enough bytes")
|
||||||
|
}
|
||||||
|
|
||||||
m.ChunkStreamID = raw.ChunkStreamID
|
m.ChunkStreamID = raw.ChunkStreamID
|
||||||
m.DTS = raw.Timestamp
|
m.DTS = raw.Timestamp
|
||||||
m.MessageStreamID = raw.MessageStreamID
|
m.MessageStreamID = raw.MessageStreamID
|
||||||
copy(m.FourCC[:], raw.Body[1:5])
|
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
m.Payload = raw.Body[5:]
|
m.Payload = raw.Body[5:]
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m ExtendedFramesX) marshalBodySize() int {
|
||||||
|
return 5 + len(m.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
// Marshal implements Message.
|
// Marshal implements Message.
|
||||||
func (m ExtendedFramesX) Marshal() (*rawmessage.Message, error) {
|
func (m ExtendedFramesX) Marshal() (*rawmessage.Message, error) {
|
||||||
body := make([]byte, 5+len(m.Payload))
|
body := make([]byte, m.marshalBodySize())
|
||||||
|
|
||||||
body[0] = 0b10000000 | byte(ExtendedTypeFramesX)
|
body[0] = 0b10000000 | byte(ExtendedTypeFramesX)
|
||||||
copy(body[1:5], m.FourCC[:])
|
body[1] = uint8(m.FourCC >> 24)
|
||||||
|
body[2] = uint8(m.FourCC >> 16)
|
||||||
|
body[3] = uint8(m.FourCC >> 8)
|
||||||
|
body[4] = uint8(m.FourCC)
|
||||||
copy(body[5:], m.Payload)
|
copy(body[5:], m.Payload)
|
||||||
|
|
||||||
return &rawmessage.Message{
|
return &rawmessage.Message{
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ import (
|
|||||||
|
|
||||||
// ExtendedMetadata is a metadata extended message.
|
// ExtendedMetadata is a metadata extended message.
|
||||||
type ExtendedMetadata struct {
|
type ExtendedMetadata struct {
|
||||||
FourCC [4]byte
|
FourCC FourCC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal implements Message.
|
// Unmarshal implements Message.
|
||||||
func (m *ExtendedMetadata) Unmarshal(raw *rawmessage.Message) error {
|
func (m *ExtendedMetadata) Unmarshal(raw *rawmessage.Message) error {
|
||||||
copy(m.FourCC[:], raw.Body[1:5])
|
if len(raw.Body) != 5 {
|
||||||
|
return fmt.Errorf("invalid body size")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
|
|
||||||
return fmt.Errorf("ExtendedMetadata is not implemented yet")
|
return fmt.Errorf("ExtendedMetadata is not implemented yet")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ import (
|
|||||||
|
|
||||||
// ExtendedMPEG2TSSequenceStart is a MPEG2-TS sequence start extended message.
|
// ExtendedMPEG2TSSequenceStart is a MPEG2-TS sequence start extended message.
|
||||||
type ExtendedMPEG2TSSequenceStart struct {
|
type ExtendedMPEG2TSSequenceStart struct {
|
||||||
FourCC [4]byte
|
FourCC FourCC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal implements Message.
|
// Unmarshal implements Message.
|
||||||
func (m *ExtendedMPEG2TSSequenceStart) Unmarshal(raw *rawmessage.Message) error {
|
func (m *ExtendedMPEG2TSSequenceStart) Unmarshal(raw *rawmessage.Message) error {
|
||||||
copy(m.FourCC[:], raw.Body[1:5])
|
if len(raw.Body) != 5 {
|
||||||
|
return fmt.Errorf("invalid body size")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
|
|
||||||
return fmt.Errorf("ExtendedMPEG2TSSequenceStart is not implemented yet")
|
return fmt.Errorf("ExtendedMPEG2TSSequenceStart is not implemented yet")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
// ExtendedSequenceEnd is a sequence end extended message.
|
// ExtendedSequenceEnd is a sequence end extended message.
|
||||||
type ExtendedSequenceEnd struct {
|
type ExtendedSequenceEnd struct {
|
||||||
FourCC [4]byte
|
FourCC FourCC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal implements Message.
|
// Unmarshal implements Message.
|
||||||
@@ -17,7 +17,7 @@ func (m *ExtendedSequenceEnd) Unmarshal(raw *rawmessage.Message) error {
|
|||||||
return fmt.Errorf("invalid body size")
|
return fmt.Errorf("invalid body size")
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(m.FourCC[:], raw.Body[1:5])
|
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/bluenviron/mediamtx/internal/rtmp/rawmessage"
|
"github.com/bluenviron/mediamtx/internal/rtmp/rawmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -8,26 +10,37 @@ import (
|
|||||||
type ExtendedSequenceStart struct {
|
type ExtendedSequenceStart struct {
|
||||||
ChunkStreamID byte
|
ChunkStreamID byte
|
||||||
MessageStreamID uint32
|
MessageStreamID uint32
|
||||||
FourCC [4]byte
|
FourCC FourCC
|
||||||
Config []byte
|
Config []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal implements Message.
|
// Unmarshal implements Message.
|
||||||
func (m *ExtendedSequenceStart) Unmarshal(raw *rawmessage.Message) error {
|
func (m *ExtendedSequenceStart) Unmarshal(raw *rawmessage.Message) error {
|
||||||
|
if len(raw.Body) < 6 {
|
||||||
|
return fmt.Errorf("not enough bytes")
|
||||||
|
}
|
||||||
|
|
||||||
m.ChunkStreamID = raw.ChunkStreamID
|
m.ChunkStreamID = raw.ChunkStreamID
|
||||||
m.MessageStreamID = raw.MessageStreamID
|
m.MessageStreamID = raw.MessageStreamID
|
||||||
copy(m.FourCC[:], raw.Body[1:5])
|
m.FourCC = FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
m.Config = raw.Body[5:]
|
m.Config = raw.Body[5:]
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m ExtendedSequenceStart) marshalBodySize() int {
|
||||||
|
return 5 + len(m.Config)
|
||||||
|
}
|
||||||
|
|
||||||
// Marshal implements Message.
|
// Marshal implements Message.
|
||||||
func (m ExtendedSequenceStart) Marshal() (*rawmessage.Message, error) {
|
func (m ExtendedSequenceStart) Marshal() (*rawmessage.Message, error) {
|
||||||
body := make([]byte, 5+len(m.Config))
|
body := make([]byte, m.marshalBodySize())
|
||||||
|
|
||||||
body[0] = 0b10000000 | byte(ExtendedTypeSequenceStart)
|
body[0] = 0b10000000 | byte(ExtendedTypeSequenceStart)
|
||||||
copy(body[1:5], m.FourCC[:])
|
body[1] = uint8(m.FourCC >> 24)
|
||||||
|
body[2] = uint8(m.FourCC >> 16)
|
||||||
|
body[3] = uint8(m.FourCC >> 8)
|
||||||
|
body[4] = uint8(m.FourCC)
|
||||||
copy(body[5:], m.Config)
|
copy(body[5:], m.Config)
|
||||||
|
|
||||||
return &rawmessage.Message{
|
return &rawmessage.Message{
|
||||||
|
|||||||
@@ -61,13 +61,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// FourCC is an identifier of a video codec.
|
// FourCC is an identifier of a video codec.
|
||||||
type FourCC [4]byte
|
type FourCC uint32
|
||||||
|
|
||||||
// video codec identifiers.
|
// video codec identifiers.
|
||||||
var (
|
var (
|
||||||
FourCCAV1 FourCC = [4]byte{'a', 'v', '0', '1'}
|
FourCCAV1 FourCC = 'a'<<24 | 'v'<<16 | '0'<<8 | '1'
|
||||||
FourCCVP9 FourCC = [4]byte{'v', 'p', '0', '9'}
|
FourCCVP9 FourCC = 'v'<<24 | 'p'<<16 | '0'<<8 | '9'
|
||||||
FourCCHEVC FourCC = [4]byte{'h', 'v', 'c', '1'}
|
FourCCHEVC FourCC = 'h'<<24 | 'v'<<16 | 'c'<<8 | '1'
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message is a message.
|
// Message is a message.
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ func allocateMessage(raw *rawmessage.Message) (Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (raw.Body[0] & 0b10000000) != 0 {
|
if (raw.Body[0] & 0b10000000) != 0 {
|
||||||
var fourCC [4]byte
|
fourCC := FourCC(raw.Body[1])<<24 | FourCC(raw.Body[2])<<16 | FourCC(raw.Body[3])<<8 | FourCC(raw.Body[4])
|
||||||
copy(fourCC[:], raw.Body[1:5])
|
|
||||||
|
|
||||||
switch fourCC {
|
switch fourCC {
|
||||||
case FourCCAV1, FourCCVP9, FourCCHEVC:
|
case FourCCAV1, FourCCVP9, FourCCHEVC:
|
||||||
|
|||||||
@@ -232,6 +232,20 @@ var readWriterCases = []struct {
|
|||||||
0x0a, 0x01, 0x02, 0x03,
|
0x0a, 0x01, 0x02, 0x03,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"extended sequence start",
|
||||||
|
&ExtendedSequenceStart{
|
||||||
|
ChunkStreamID: 4,
|
||||||
|
MessageStreamID: 0x1000000,
|
||||||
|
FourCC: FourCCHEVC,
|
||||||
|
Config: []byte{0x01, 0x02, 0x03},
|
||||||
|
},
|
||||||
|
[]byte{
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x80, 0x68, 0x76, 0x63,
|
||||||
|
0x31, 0x01, 0x02, 0x03,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"extended coded frames",
|
"extended coded frames",
|
||||||
&ExtendedCodedFrames{
|
&ExtendedCodedFrames{
|
||||||
|
|||||||
@@ -74,9 +74,13 @@ func (m *Video) Unmarshal(raw *rawmessage.Message) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m Video) marshalBodySize() int {
|
||||||
|
return 5 + len(m.Payload)
|
||||||
|
}
|
||||||
|
|
||||||
// Marshal implements Message.
|
// Marshal implements Message.
|
||||||
func (m Video) Marshal() (*rawmessage.Message, error) {
|
func (m Video) Marshal() (*rawmessage.Message, error) {
|
||||||
body := make([]byte, 5+len(m.Payload))
|
body := make([]byte, m.marshalBodySize())
|
||||||
|
|
||||||
if m.IsKeyFrame {
|
if m.IsKeyFrame {
|
||||||
body[0] = flvio.FRAME_KEY << 4
|
body[0] = flvio.FRAME_KEY << 4
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form
|
|||||||
case 0:
|
case 0:
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
||||||
case message.CodecH264:
|
case message.CodecH264, float64(message.FourCCAV1),
|
||||||
|
float64(message.FourCCVP9), float64(message.FourCCHEVC):
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,58 @@ func TestReadTracks(t *testing.T) {
|
|||||||
0x44, 0x01, 0xc0, 0xf7, 0xc0, 0xcc, 0x90,
|
0x44, 0x01, 0xc0, 0xf7, 0xc0, 0xcc, 0x90,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var spsp h265.SPS
|
||||||
|
err := spsp.Unmarshal(h265SPS)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
hvcc := &mp4.HvcC{
|
||||||
|
ConfigurationVersion: 1,
|
||||||
|
GeneralProfileIdc: spsp.ProfileTierLevel.GeneralProfileIdc,
|
||||||
|
GeneralProfileCompatibility: spsp.ProfileTierLevel.GeneralProfileCompatibilityFlag,
|
||||||
|
GeneralConstraintIndicator: [6]uint8{
|
||||||
|
h265SPS[7], h265SPS[8], h265SPS[9],
|
||||||
|
h265SPS[10], h265SPS[11], h265SPS[12],
|
||||||
|
},
|
||||||
|
GeneralLevelIdc: spsp.ProfileTierLevel.GeneralLevelIdc,
|
||||||
|
// MinSpatialSegmentationIdc
|
||||||
|
// ParallelismType
|
||||||
|
ChromaFormatIdc: uint8(spsp.ChromaFormatIdc),
|
||||||
|
BitDepthLumaMinus8: uint8(spsp.BitDepthLumaMinus8),
|
||||||
|
BitDepthChromaMinus8: uint8(spsp.BitDepthChromaMinus8),
|
||||||
|
// AvgFrameRate
|
||||||
|
// ConstantFrameRate
|
||||||
|
NumTemporalLayers: 1,
|
||||||
|
// TemporalIdNested
|
||||||
|
LengthSizeMinusOne: 3,
|
||||||
|
NumOfNaluArrays: 3,
|
||||||
|
NaluArrays: []mp4.HEVCNaluArray{
|
||||||
|
{
|
||||||
|
NaluType: byte(h265.NALUType_VPS_NUT),
|
||||||
|
NumNalus: 1,
|
||||||
|
Nalus: []mp4.HEVCNalu{{
|
||||||
|
Length: uint16(len(h265VPS)),
|
||||||
|
NALUnit: h265VPS,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NaluType: byte(h265.NALUType_SPS_NUT),
|
||||||
|
NumNalus: 1,
|
||||||
|
Nalus: []mp4.HEVCNalu{{
|
||||||
|
Length: uint16(len(h265SPS)),
|
||||||
|
NALUnit: h265SPS,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NaluType: byte(h265.NALUType_PPS_NUT),
|
||||||
|
NumNalus: 1,
|
||||||
|
Nalus: []mp4.HEVCNalu{{
|
||||||
|
Length: uint16(len(h265PPS)),
|
||||||
|
NALUnit: h265PPS,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
for _, ca := range []struct {
|
for _, ca := range []struct {
|
||||||
name string
|
name string
|
||||||
videoTrack formats.Format
|
videoTrack formats.Format
|
||||||
@@ -180,6 +232,16 @@ func TestReadTracks(t *testing.T) {
|
|||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"obs 30",
|
||||||
|
&formats.H265{
|
||||||
|
PayloadTyp: 96,
|
||||||
|
VPS: h265VPS,
|
||||||
|
SPS: h265SPS,
|
||||||
|
PPS: h265PPS,
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
@@ -566,57 +628,46 @@ func TestReadTracks(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var spsp h265.SPS
|
var buf bytes.Buffer
|
||||||
err = spsp.Unmarshal(h265SPS)
|
_, err = mp4.Marshal(&buf, hvcc, mp4.Context{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
hvcc := &mp4.HvcC{
|
err = mrw.Write(&message.ExtendedSequenceStart{
|
||||||
ConfigurationVersion: 1,
|
ChunkStreamID: 4,
|
||||||
GeneralProfileIdc: spsp.ProfileTierLevel.GeneralProfileIdc,
|
MessageStreamID: 0x1000000,
|
||||||
GeneralProfileCompatibility: spsp.ProfileTierLevel.GeneralProfileCompatibilityFlag,
|
FourCC: message.FourCCHEVC,
|
||||||
GeneralConstraintIndicator: [6]uint8{
|
Config: buf.Bytes(),
|
||||||
h265SPS[7], h265SPS[8], h265SPS[9],
|
})
|
||||||
h265SPS[10], h265SPS[11], h265SPS[12],
|
require.NoError(t, err)
|
||||||
},
|
|
||||||
GeneralLevelIdc: spsp.ProfileTierLevel.GeneralLevelIdc,
|
case "obs 30":
|
||||||
// MinSpatialSegmentationIdc
|
err := mrw.Write(&message.DataAMF0{
|
||||||
// ParallelismType
|
ChunkStreamID: 4,
|
||||||
ChromaFormatIdc: uint8(spsp.ChromaFormatIdc),
|
MessageStreamID: 1,
|
||||||
BitDepthLumaMinus8: uint8(spsp.BitDepthLumaMinus8),
|
Payload: []interface{}{
|
||||||
BitDepthChromaMinus8: uint8(spsp.BitDepthChromaMinus8),
|
"@setDataFrame",
|
||||||
// AvgFrameRate
|
"onMetaData",
|
||||||
// ConstantFrameRate
|
flvio.AMFMap{
|
||||||
NumTemporalLayers: 1,
|
{
|
||||||
// TemporalIdNested
|
K: "videodatarate",
|
||||||
LengthSizeMinusOne: 3,
|
V: float64(0),
|
||||||
NumOfNaluArrays: 3,
|
},
|
||||||
NaluArrays: []mp4.HEVCNaluArray{
|
{
|
||||||
{
|
K: "videocodecid",
|
||||||
NaluType: byte(h265.NALUType_VPS_NUT),
|
V: float64(message.FourCCHEVC),
|
||||||
NumNalus: 1,
|
},
|
||||||
Nalus: []mp4.HEVCNalu{{
|
{
|
||||||
Length: uint16(len(h265VPS)),
|
K: "audiodatarate",
|
||||||
NALUnit: h265VPS,
|
V: float64(0),
|
||||||
}},
|
},
|
||||||
},
|
{
|
||||||
{
|
K: "audiocodecid",
|
||||||
NaluType: byte(h265.NALUType_SPS_NUT),
|
V: float64(0),
|
||||||
NumNalus: 1,
|
},
|
||||||
Nalus: []mp4.HEVCNalu{{
|
|
||||||
Length: uint16(len(h265SPS)),
|
|
||||||
NALUnit: h265SPS,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NaluType: byte(h265.NALUType_PPS_NUT),
|
|
||||||
NumNalus: 1,
|
|
||||||
Nalus: []mp4.HEVCNalu{{
|
|
||||||
Length: uint16(len(h265PPS)),
|
|
||||||
NALUnit: h265PPS,
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
_, err = mp4.Marshal(&buf, hvcc, mp4.Context{})
|
_, err = mp4.Marshal(&buf, hvcc, mp4.Context{})
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ readTimeout: 10s
|
|||||||
# Timeout of write operations.
|
# Timeout of write operations.
|
||||||
writeTimeout: 10s
|
writeTimeout: 10s
|
||||||
# Number of read buffers.
|
# Number of read buffers.
|
||||||
# A higher value allows a wider throughput, a lower value allows to save RAM.
|
# A higher value allows to increase throughput, a lower value allows to save RAM.
|
||||||
readBufferCount: 512
|
readBufferCount: 512
|
||||||
# Maximum size of payload of outgoing UDP packets.
|
# Maximum size of outgoing UDP packets.
|
||||||
# This can be decreased to avoid fragmentation on networks with a low UDP MTU.
|
# This can be decreased to avoid fragmentation on networks with a low UDP MTU.
|
||||||
udpMaxPayloadSize: 1472
|
udpMaxPayloadSize: 1472
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user