@ -5,8 +5,10 @@ import (
@@ -5,8 +5,10 @@ import (
"testing"
"time"
"github.com/abema/go-mp4"
"github.com/bluenviron/gortsplib/v3/pkg/formats"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/stretchr/testify/require"
@ -17,16 +19,37 @@ import (
@@ -17,16 +19,37 @@ import (
)
func TestReadTracks ( t * testing . T ) {
sps := [ ] byte {
h264SPS := [ ] byte {
0x67 , 0x64 , 0x00 , 0x0c , 0xac , 0x3b , 0x50 , 0xb0 ,
0x4b , 0x42 , 0x00 , 0x00 , 0x03 , 0x00 , 0x02 , 0x00 ,
0x00 , 0x03 , 0x00 , 0x3d , 0x08 ,
}
pps := [ ] byte {
h264PPS := [ ] byte {
0x68 , 0xee , 0x3c , 0x80 ,
}
h265VPS := [ ] byte {
0x40 , 0x01 , 0x0c , 0x01 , 0xff , 0xff , 0x01 , 0x40 ,
0x00 , 0x00 , 0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x7b , 0xac , 0x09 ,
}
h265SPS := [ ] byte {
0x42 , 0x01 , 0x01 , 0x01 , 0x40 , 0x00 , 0x00 , 0x03 ,
0x00 , 0x00 , 0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x7b , 0xa0 , 0x03 , 0xc0 , 0x80 , 0x11 ,
0x07 , 0xcb , 0x96 , 0xb4 , 0xa4 , 0x25 , 0x92 , 0xe3 ,
0x01 , 0x6a , 0x02 , 0x02 , 0x02 , 0x08 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x08 , 0x00 , 0x00 , 0x03 , 0x01 , 0xe3 ,
0x00 , 0x2e , 0xf2 , 0x88 , 0x00 , 0x09 , 0x89 , 0x60 ,
0x00 , 0x04 , 0xc4 , 0xb4 , 0x20 ,
}
h265PPS := [ ] byte {
0x44 , 0x01 , 0xc0 , 0xf7 , 0xc0 , 0xcc , 0x90 ,
}
for _ , ca := range [ ] struct {
name string
videoTrack formats . Format
@ -36,8 +59,8 @@ func TestReadTracks(t *testing.T) {
@@ -36,8 +59,8 @@ func TestReadTracks(t *testing.T) {
"video+audio" ,
& formats . H264 {
PayloadTyp : 96 ,
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
& formats . MPEG4Audio {
@ -56,8 +79,8 @@ func TestReadTracks(t *testing.T) {
@@ -56,8 +79,8 @@ func TestReadTracks(t *testing.T) {
"video" ,
& formats . H264 {
PayloadTyp : 96 ,
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
nil ,
@ -66,8 +89,8 @@ func TestReadTracks(t *testing.T) {
@@ -66,8 +89,8 @@ func TestReadTracks(t *testing.T) {
"metadata without codec id, video+audio" ,
& formats . H264 {
PayloadTyp : 96 ,
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
& formats . MPEG4Audio {
@ -86,8 +109,8 @@ func TestReadTracks(t *testing.T) {
@@ -86,8 +109,8 @@ func TestReadTracks(t *testing.T) {
"metadata without codec id, video only" ,
& formats . H264 {
PayloadTyp : 96 ,
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
nil ,
@ -96,8 +119,8 @@ func TestReadTracks(t *testing.T) {
@@ -96,8 +119,8 @@ func TestReadTracks(t *testing.T) {
"missing metadata, video+audio" ,
& formats . H264 {
PayloadTyp : 96 ,
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
& formats . MPEG4Audio {
@ -131,24 +154,9 @@ func TestReadTracks(t *testing.T) {
@@ -131,24 +154,9 @@ func TestReadTracks(t *testing.T) {
"obs studio pre 29.1 h265" ,
& formats . H265 {
PayloadTyp : 96 ,
VPS : [ ] byte {
0x40 , 0x01 , 0x0c , 0x01 , 0xff , 0xff , 0x01 , 0x40 ,
0x00 , 0x00 , 0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x7b , 0xac , 0x09 ,
} ,
SPS : [ ] byte {
0x42 , 0x01 , 0x01 , 0x01 , 0x40 , 0x00 , 0x00 , 0x03 ,
0x00 , 0x00 , 0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x7b , 0xa0 , 0x03 , 0xc0 , 0x80 , 0x11 ,
0x07 , 0xcb , 0x96 , 0xb4 , 0xa4 , 0x25 , 0x92 , 0xe3 ,
0x01 , 0x6a , 0x02 , 0x02 , 0x02 , 0x08 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x08 , 0x00 , 0x00 , 0x03 , 0x01 , 0xe3 ,
0x00 , 0x2e , 0xf2 , 0x88 , 0x00 , 0x09 , 0x89 , 0x60 ,
0x00 , 0x04 , 0xc4 , 0xb4 , 0x20 ,
} ,
PPS : [ ] byte {
0x44 , 0x01 , 0xc0 , 0xf7 , 0xc0 , 0xcc , 0x90 ,
} ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
& formats . MPEG4Audio {
PayloadTyp : 96 ,
@ -162,6 +170,16 @@ func TestReadTracks(t *testing.T) {
@@ -162,6 +170,16 @@ func TestReadTracks(t *testing.T) {
IndexDeltaLength : 3 ,
} ,
} ,
{
"xplit broadcaster" ,
& formats . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
nil ,
} ,
} {
t . Run ( ca . name , func ( t * testing . T ) {
var buf bytes . Buffer
@ -199,8 +217,8 @@ func TestReadTracks(t *testing.T) {
@@ -199,8 +217,8 @@ func TestReadTracks(t *testing.T) {
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
@ -262,8 +280,8 @@ func TestReadTracks(t *testing.T) {
@@ -262,8 +280,8 @@ func TestReadTracks(t *testing.T) {
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
@ -302,8 +320,8 @@ func TestReadTracks(t *testing.T) {
@@ -302,8 +320,8 @@ func TestReadTracks(t *testing.T) {
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
@ -361,8 +379,8 @@ func TestReadTracks(t *testing.T) {
@@ -361,8 +379,8 @@ func TestReadTracks(t *testing.T) {
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
@ -388,8 +406,8 @@ func TestReadTracks(t *testing.T) {
@@ -388,8 +406,8 @@ func TestReadTracks(t *testing.T) {
case "missing metadata, video+audio" :
buf , _ := h264conf . Conf {
SPS : sps ,
PPS : pps ,
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err := mrw . Write ( & message . Video {
@ -484,25 +502,9 @@ func TestReadTracks(t *testing.T) {
@@ -484,25 +502,9 @@ func TestReadTracks(t *testing.T) {
require . NoError ( t , err )
avcc , err := h264 . AVCCMarshal ( [ ] [ ] byte {
{ // VPS
0x40 , 0x01 , 0x0c , 0x01 , 0xff , 0xff , 0x01 , 0x40 ,
0x00 , 0x00 , 0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x7b , 0xac , 0x09 ,
} ,
{ // SPS
0x42 , 0x01 , 0x01 , 0x01 , 0x40 , 0x00 , 0x00 , 0x03 ,
0x00 , 0x00 , 0x03 , 0x00 , 0x00 , 0x03 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x7b , 0xa0 , 0x03 , 0xc0 , 0x80 , 0x11 ,
0x07 , 0xcb , 0x96 , 0xb4 , 0xa4 , 0x25 , 0x92 , 0xe3 ,
0x01 , 0x6a , 0x02 , 0x02 , 0x02 , 0x08 , 0x00 , 0x00 ,
0x03 , 0x00 , 0x08 , 0x00 , 0x00 , 0x03 , 0x01 , 0xe3 ,
0x00 , 0x2e , 0xf2 , 0x88 , 0x00 , 0x09 , 0x89 , 0x60 ,
0x00 , 0x04 , 0xc4 , 0xb4 , 0x20 ,
} ,
{
// PPS
0x44 , 0x01 , 0xc0 , 0xf7 , 0xc0 , 0xcc , 0x90 ,
} ,
h265VPS ,
h265SPS ,
h265PPS ,
} )
require . NoError ( t , err )
@ -534,6 +536,99 @@ func TestReadTracks(t *testing.T) {
@@ -534,6 +536,99 @@ func TestReadTracks(t *testing.T) {
Payload : enc ,
} )
require . NoError ( t , err )
case "xplit broadcaster" :
err := mrw . Write ( & message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
"@setDataFrame" ,
"onMetaData" ,
flvio . AMFMap {
{
K : "videodatarate" ,
V : float64 ( 0 ) ,
} ,
{
K : "videocodecid" ,
V : "hvc1" ,
} ,
{
K : "audiodatarate" ,
V : float64 ( 0 ) ,
} ,
{
K : "audiocodecid" ,
V : float64 ( 0 ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
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 ,
} } ,
} ,
} ,
}
var buf bytes . Buffer
_ , err = mp4 . Marshal ( & buf , hvcc , mp4 . Context { } )
require . NoError ( t , err )
err = mrw . Write ( & message . ExtendedSequenceStart {
ChunkStreamID : 4 ,
MessageStreamID : 0x1000000 ,
FourCC : message . FourCCHEVC ,
Config : buf . Bytes ( ) ,
} )
require . NoError ( t , err )
}
c := newNoHandshakeConn ( & buf )