@ -106,6 +106,7 @@ func TestReadTracks(t *testing.T) {
@@ -106,6 +106,7 @@ func TestReadTracks(t *testing.T) {
name string
videoTrack format . Format
audioTrack format . Format
messages [ ] message . Message
} {
{
"video+audio" ,
@ -126,138 +127,8 @@ func TestReadTracks(t *testing.T) {
@@ -126,138 +127,8 @@ func TestReadTracks(t *testing.T) {
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
} ,
{
"video" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
nil ,
} ,
{
"metadata without codec id, video+audio" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
} ,
{
"metadata without codec id, video only" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
nil ,
} ,
{
"missing metadata, video+audio" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
} ,
{
"missing metadata, audio" ,
nil ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
} ,
{
"obs studio pre 29.1 h265" ,
& format . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
} ,
{
"xplit broadcaster" ,
& format . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
nil ,
} ,
{
"obs 30" ,
& format . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
nil ,
} ,
{
"ffmpeg av1" ,
& format . AV1 {
PayloadTyp : 96 ,
} ,
nil ,
} ,
} {
t . Run ( ca . name , func ( t * testing . T ) {
var buf bytes . Buffer
bc := bytecounter . NewReadWriter ( & buf )
mrw := message . NewReadWriter ( bc , bc , true )
switch ca . name {
case "video+audio" :
err := mrw . Write ( & message . DataAMF0 {
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
@ -282,32 +153,22 @@ func TestReadTracks(t *testing.T) {
@@ -282,32 +153,22 @@ func TestReadTracks(t *testing.T) {
} ,
} ,
} ,
} )
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
} ,
& message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeConfig ,
Payload : buf ,
} )
require . NoError ( t , err )
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
err = mrw . Write ( & message . Audio {
Payload : func ( ) [ ] byte {
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
return buf
} ( ) ,
} ,
& message . Audio {
ChunkStreamID : message . AudioChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecMPEG4Audio ,
@ -315,12 +176,29 @@ func TestReadTracks(t *testing.T) {
@@ -315,12 +176,29 @@ func TestReadTracks(t *testing.T) {
Depth : flvio . SOUND_16BIT ,
Channels : flvio . SOUND_STEREO ,
AACType : message . AudioAACTypeConfig ,
Payload : enc ,
} )
require . NoError ( t , err )
case "video" :
err := mrw . Write ( & message . DataAMF0 {
Payload : func ( ) [ ] byte {
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
return enc
} ( ) ,
} ,
} ,
} ,
{
"video" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
nil ,
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
@ -345,158 +223,72 @@ func TestReadTracks(t *testing.T) {
@@ -345,158 +223,72 @@ func TestReadTracks(t *testing.T) {
} ,
} ,
} ,
} )
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeConfig ,
Payload : buf ,
} )
require . NoError ( t , err )
case "metadata without codec id, video+audio" :
err := mrw . Write ( & message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
"@setDataFrame" ,
"onMetaData" ,
flvio . AMFMap {
{
K : "width" ,
V : float64 ( 2688 ) ,
} ,
{
K : "height" ,
V : float64 ( 1520 ) ,
} ,
{
K : "framerate" ,
V : float64 ( 0 o25 ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
} ,
& message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeConfig ,
Payload : buf ,
} )
require . NoError ( t , err )
enc , err := mpeg4audio . Config {
Payload : func ( ) [ ] byte {
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
return buf
} ( ) ,
} ,
} ,
} ,
{
"issue mediamtx/386 (missing metadata), video+audio" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : h264SPS ,
PPS : h264PPS ,
PacketizationMode : 1 ,
} ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
err = mrw . Write ( & message . Audio {
ChunkStreamID : message . AudioChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecMPEG4Audio ,
Rate : flvio . SOUND_44Khz ,
Depth : flvio . SOUND_16BIT ,
Channels : flvio . SOUND_STEREO ,
AACType : message . AudioAACTypeConfig ,
Payload : enc ,
} )
require . NoError ( t , err )
case "metadata without codec id, video only" :
err := mrw . Write ( & message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
"@setDataFrame" ,
"onMetaData" ,
flvio . AMFMap {
{
K : "width" ,
V : float64 ( 2688 ) ,
} ,
{
K : "height" ,
V : float64 ( 1520 ) ,
} ,
{
K : "framerate" ,
V : float64 ( 0 o25 ) ,
} ,
} ,
} ,
} )
require . NoError ( t , err )
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err = mrw . Write ( & message . Video {
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
[ ] message . Message {
& message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeConfig ,
Payload : buf ,
} )
require . NoError ( t , err )
err = mrw . Write ( & message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeAU ,
Payload : [ ] byte { 0x01 , 0x02 , 0x03 , 0x04 } ,
DTS : 1 * time . Second ,
} )
require . NoError ( t , err )
case "missing metadata, video+audio" :
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
err := mrw . Write ( & message . Video {
Payload : func ( ) [ ] byte {
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
return buf
} ( ) ,
} ,
& message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeConfig ,
Payload : buf ,
} )
require . NoError ( t , err )
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
err = mrw . Write ( & message . Audio {
Payload : func ( ) [ ] byte {
buf , _ := h264conf . Conf {
SPS : h264SPS ,
PPS : h264PPS ,
} . Marshal ( )
return buf
} ( ) ,
} ,
& message . Audio {
ChunkStreamID : message . AudioChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecMPEG4Audio ,
@ -504,19 +296,34 @@ func TestReadTracks(t *testing.T) {
@@ -504,19 +296,34 @@ func TestReadTracks(t *testing.T) {
Depth : flvio . SOUND_16BIT ,
Channels : flvio . SOUND_STEREO ,
AACType : message . AudioAACTypeConfig ,
Payload : enc ,
} )
require . NoError ( t , err )
case "missing metadata, audio" :
enc , err := mpeg4audio . Config {
Payload : func ( ) [ ] byte {
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
return enc
} ( ) ,
} ,
} ,
} ,
{
"issue mediamtx/386 (missing metadata), audio" ,
nil ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
err = mrw . Write ( & message . Audio {
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
[ ] message . Message {
& message . Audio {
ChunkStreamID : message . AudioChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecMPEG4Audio ,
@ -524,11 +331,17 @@ func TestReadTracks(t *testing.T) {
@@ -524,11 +331,17 @@ func TestReadTracks(t *testing.T) {
Depth : flvio . SOUND_16BIT ,
Channels : flvio . SOUND_STEREO ,
AACType : message . AudioAACTypeConfig ,
Payload : enc ,
} )
require . NoError ( t , err )
err = mrw . Write ( & message . Audio {
Payload : func ( ) [ ] byte {
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
return enc
} ( ) ,
} ,
& message . Audio {
ChunkStreamID : message . AudioChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecMPEG4Audio ,
@ -536,13 +349,40 @@ func TestReadTracks(t *testing.T) {
@@ -536,13 +349,40 @@ func TestReadTracks(t *testing.T) {
Depth : flvio . SOUND_16BIT ,
Channels : flvio . SOUND_STEREO ,
AACType : message . AudioAACTypeConfig ,
Payload : enc ,
DTS : 1 * time . Second ,
} )
require . NoError ( t , err )
case "obs studio pre 29.1 h265" :
err := mrw . Write ( & message . DataAMF0 {
Payload : func ( ) [ ] byte {
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
return enc
} ( ) ,
DTS : 1 * time . Second ,
} ,
} ,
} ,
{
"obs studio pre 29.1 h265" ,
& format . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
@ -567,34 +407,24 @@ func TestReadTracks(t *testing.T) {
@@ -567,34 +407,24 @@ func TestReadTracks(t *testing.T) {
} ,
} ,
} ,
} )
require . NoError ( t , err )
avcc , err := h264 . AVCCMarshal ( [ ] [ ] byte {
h265VPS ,
h265SPS ,
h265PPS ,
} )
require . NoError ( t , err )
err = mrw . Write ( & message . Video {
} ,
& message . Video {
ChunkStreamID : message . VideoChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecH264 ,
IsKeyFrame : true ,
Type : message . VideoTypeAU ,
Payload : avcc ,
} )
require . NoError ( t , err )
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
err = mrw . Write ( & message . Audio {
Payload : func ( ) [ ] byte {
avcc , err := h264 . AVCCMarshal ( [ ] [ ] byte {
h265VPS ,
h265SPS ,
h265PPS ,
} )
require . NoError ( t , err )
return avcc
} ( ) ,
} ,
& message . Audio {
ChunkStreamID : message . AudioChunkStreamID ,
MessageStreamID : 0x1000000 ,
Codec : message . CodecMPEG4Audio ,
@ -602,12 +432,29 @@ func TestReadTracks(t *testing.T) {
@@ -602,12 +432,29 @@ func TestReadTracks(t *testing.T) {
Depth : flvio . SOUND_16BIT ,
Channels : flvio . SOUND_STEREO ,
AACType : message . AudioAACTypeConfig ,
Payload : enc ,
} )
require . NoError ( t , err )
case "xplit broadcaster" :
err := mrw . Write ( & message . DataAMF0 {
Payload : func ( ) [ ] byte {
enc , err := mpeg4audio . Config {
Type : 2 ,
SampleRate : 44100 ,
ChannelCount : 2 ,
} . Marshal ( )
require . NoError ( t , err )
return enc
} ( ) ,
} ,
} ,
} ,
{
"issue mediamtx/2232 (xsplit broadcaster)" ,
& format . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
nil ,
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
@ -632,23 +479,31 @@ func TestReadTracks(t *testing.T) {
@@ -632,23 +479,31 @@ func TestReadTracks(t *testing.T) {
} ,
} ,
} ,
} )
require . NoError ( t , err )
var buf bytes . Buffer
_ , err = mp4 . Marshal ( & buf , hvcc , mp4 . Context { } )
require . NoError ( t , err )
err = mrw . Write ( & message . ExtendedSequenceStart {
} ,
& message . ExtendedSequenceStart {
ChunkStreamID : 4 ,
MessageStreamID : 0x1000000 ,
FourCC : message . FourCCHEVC ,
Config : buf . Bytes ( ) ,
} )
require . NoError ( t , err )
case "obs 30" :
err := mrw . Write ( & message . DataAMF0 {
Config : func ( ) [ ] byte {
var buf bytes . Buffer
_ , err = mp4 . Marshal ( & buf , hvcc , mp4 . Context { } )
require . NoError ( t , err )
return buf . Bytes ( )
} ( ) ,
} ,
} ,
} ,
{
"obs 30" ,
& format . H265 {
PayloadTyp : 96 ,
VPS : h265VPS ,
SPS : h265SPS ,
PPS : h265PPS ,
} ,
nil ,
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
@ -673,23 +528,28 @@ func TestReadTracks(t *testing.T) {
@@ -673,23 +528,28 @@ func TestReadTracks(t *testing.T) {
} ,
} ,
} ,
} )
require . NoError ( t , err )
var buf bytes . Buffer
_ , err = mp4 . Marshal ( & buf , hvcc , mp4 . Context { } )
require . NoError ( t , err )
err = mrw . Write ( & message . ExtendedSequenceStart {
} ,
& message . ExtendedSequenceStart {
ChunkStreamID : 4 ,
MessageStreamID : 0x1000000 ,
FourCC : message . FourCCHEVC ,
Config : buf . Bytes ( ) ,
} )
require . NoError ( t , err )
case "ffmpeg av1" :
err := mrw . Write ( & message . DataAMF0 {
Config : func ( ) [ ] byte {
var buf bytes . Buffer
_ , err = mp4 . Marshal ( & buf , hvcc , mp4 . Context { } )
require . NoError ( t , err )
return buf . Bytes ( )
} ( ) ,
} ,
} ,
} ,
{
"ffmpeg av1" ,
& format . AV1 {
PayloadTyp : 96 ,
} ,
nil ,
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
@ -730,14 +590,8 @@ func TestReadTracks(t *testing.T) {
@@ -730,14 +590,8 @@ func TestReadTracks(t *testing.T) {
} ,
} ,
} ,
} )
require . NoError ( t , err )
var buf bytes . Buffer
_ , err = mp4 . Marshal ( & buf , hvcc , mp4 . Context { } )
require . NoError ( t , err )
err = mrw . Write ( & message . ExtendedSequenceStart {
} ,
& message . ExtendedSequenceStart {
ChunkStreamID : 6 ,
MessageStreamID : 0x1000000 ,
FourCC : message . FourCCAV1 ,
@ -746,7 +600,93 @@ func TestReadTracks(t *testing.T) {
@@ -746,7 +600,93 @@ func TestReadTracks(t *testing.T) {
0x00 , 0x42 , 0xab , 0xbf , 0xc3 , 0x70 , 0x0b , 0xe0 ,
0x01 ,
} ,
} )
} ,
} ,
} ,
{
"issue mediamtx/2289 (missing videocodecid)" ,
& format . H264 {
PayloadTyp : 96 ,
SPS : [ ] byte {
0x67 , 0x64 , 0x00 , 0x1f , 0xac , 0x2c , 0x6a , 0x81 ,
0x40 , 0x16 , 0xe9 , 0xb8 , 0x28 , 0x08 , 0x2a , 0x00 ,
0x00 , 0x03 , 0x00 , 0x02 , 0x00 , 0x00 , 0x03 , 0x00 ,
0xc9 , 0x08 ,
} ,
PPS : [ ] byte { 0x68 , 0xee , 0x31 , 0xb2 , 0x1b } ,
PacketizationMode : 1 ,
} ,
& format . MPEG4Audio {
PayloadTyp : 96 ,
Config : & mpeg4audio . Config {
Type : 2 ,
SampleRate : 48000 ,
ChannelCount : 1 ,
} ,
SizeLength : 13 ,
IndexLength : 3 ,
IndexDeltaLength : 3 ,
} ,
[ ] message . Message {
& message . DataAMF0 {
ChunkStreamID : 4 ,
MessageStreamID : 1 ,
Payload : [ ] interface { } {
"@setDataFrame" ,
"onMetaData" ,
flvio . AMFMap {
{
K : "width" ,
V : float64 ( 1280 ) ,
} ,
{
K : "height" ,
V : float64 ( 720 ) ,
} ,
{
K : "framerate" ,
V : float64 ( 30 ) ,
} ,
{
K : "audiocodecid" ,
V : float64 ( 10 ) ,
} ,
} ,
} ,
} ,
& message . Video {
ChunkStreamID : 0x15 ,
MessageStreamID : 0x1000000 ,
Codec : 0x7 ,
IsKeyFrame : true ,
Payload : [ ] uint8 {
0x01 , 0x64 , 0x00 , 0x1f , 0xff , 0xe1 , 0x00 , 0x1a ,
0x67 , 0x64 , 0x00 , 0x1f , 0xac , 0x2c , 0x6a , 0x81 ,
0x40 , 0x16 , 0xe9 , 0xb8 , 0x28 , 0x08 , 0x2a , 0x00 ,
0x00 , 0x03 , 0x00 , 0x02 , 0x00 , 0x00 , 0x03 , 0x00 ,
0xc9 , 0x08 , 0x01 , 0x00 , 0x05 , 0x68 , 0xee , 0x31 ,
0xb2 , 0x1b ,
} ,
} ,
& message . Audio {
ChunkStreamID : 0x14 ,
MessageStreamID : 0x1000000 ,
Codec : 0xa ,
Rate : 0x3 ,
Depth : 0x1 ,
Channels : 0x1 ,
Payload : [ ] uint8 { 0x11 , 0x88 } ,
} ,
} ,
} ,
} {
t . Run ( ca . name , func ( t * testing . T ) {
var buf bytes . Buffer
bc := bytecounter . NewReadWriter ( & buf )
mrw := message . NewReadWriter ( bc , bc , true )
for _ , msg := range ca . messages {
err := mrw . Write ( msg )
require . NoError ( t , err )
}