Skip to content

Commit 68ac82b

Browse files
Added test for MPEG-TS passthrough writing to recorder.test.go
1 parent 17264c2 commit 68ac82b

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

internal/recorder/recorder_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package recorder
22

33
import (
4+
"bytes"
45
"fmt"
56
"os"
67
"path/filepath"
78
"testing"
89
"time"
910

11+
"github.com/pion/rtp"
12+
1013
"github.com/bluenviron/gortsplib/v4/pkg/description"
1114
rtspformat "github.com/bluenviron/gortsplib/v4/pkg/format"
1215
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
@@ -539,6 +542,159 @@ func TestRecorderSkipTracksFull(t *testing.T) {
539542
}
540543
}
541544

545+
func TestRecorderMPEGTSPassthrough(t *testing.T) {
546+
// Create a test MPEG-TS packet
547+
testData := []byte{
548+
0x47, 0x40, 0x00, 0x10, 0x00, // TS header with PID 0x1000 (video)
549+
// Add some dummy payload - this is a PAT (Program Association Table)
550+
0x00, 0x00, 0xB0, 0x0D, 0x00, 0x00, 0xC1, 0x00,
551+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
552+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
553+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
554+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
555+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
556+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
557+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
558+
}
559+
560+
t.Run("basic", func(t *testing.T) {
561+
// Create a temporary directory for the test
562+
dir, err := os.MkdirTemp("", "mediamtx-agent")
563+
require.NoError(t, err)
564+
defer os.RemoveAll(dir)
565+
566+
// Create the output directory
567+
recordPath := filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f")
568+
569+
// Create a stream with MPEG-TS format
570+
mpegtsFormat := &rtspformat.MPEGTS{}
571+
572+
desc := &description.Session{Medias: []*description.Media{
573+
{
574+
Type: description.MediaTypeVideo,
575+
Formats: []rtspformat.Format{mpegtsFormat},
576+
},
577+
}}
578+
579+
strm := &stream.Stream{
580+
WriteQueueSize: 512,
581+
UDPMaxPayloadSize: 1472,
582+
Desc: desc,
583+
GenerateRTPPackets: false, // Disable RTP packet generation
584+
Parent: test.NilLogger,
585+
}
586+
err = strm.Initialize()
587+
require.NoError(t, err)
588+
defer strm.Close()
589+
590+
// Create a recorder with MPEG-TS format
591+
r := &Recorder{
592+
PathFormat: recordPath,
593+
Format: conf.RecordFormatMPEGTS,
594+
PartDuration: 1 * time.Second,
595+
SegmentDuration: 2 * time.Second,
596+
PathName: "mypath",
597+
Stream: strm,
598+
Parent: test.NilLogger,
599+
}
600+
601+
r.Initialize()
602+
603+
// Verify the recorder instance was created
604+
require.NotNil(t, r.currentInstance, "Recorder instance is nil")
605+
606+
// Create a channel to receive data from the stream
607+
dataChan := make(chan []byte, 10)
608+
609+
strm.AddReader(r, desc.Medias[0], mpegtsFormat, func(u unit.Unit) error {
610+
rtpPackets := u.GetRTPPackets()
611+
612+
for _, pkt := range rtpPackets {
613+
if pkt == nil {
614+
continue
615+
}
616+
617+
// Send the payload to the channel
618+
if len(pkt.Payload) > 0 {
619+
dataChan <- pkt.Payload
620+
}
621+
}
622+
623+
return nil
624+
})
625+
626+
strm.StartReader(r)
627+
strm.WaitRunningReader()
628+
629+
// Write multiple RTP packets to ensure we have enough data
630+
for i := 0; i < 10; i++ {
631+
// Create a new RTP packet for each iteration with updated timestamp and sequence number
632+
pkt := &rtp.Packet{
633+
Header: rtp.Header{
634+
Version: 2,
635+
PayloadType: 33, // Standard MPEG-TS payload type (RFC 2250)
636+
SequenceNumber: 100 + uint16(i),
637+
Timestamp: 123456 + uint32(i*90000), // 1 second apart
638+
SSRC: 0x9D8F,
639+
},
640+
Payload: testData,
641+
}
642+
643+
// Use the MPEG-TS format directly
644+
internalFormat := mpegtsFormat
645+
646+
// Write the RTP packet to the stream
647+
strm.WriteRTPPacket(desc.Medias[0], internalFormat, pkt, time.Now(), 0)
648+
}
649+
650+
// Check if the recorder instance was properly initialized
651+
if r.currentInstance == nil {
652+
t.Fatal("Recorder currentInstance is nil")
653+
}
654+
655+
// Manually flush the buffer writer to ensure all data is written
656+
if r.currentInstance != nil && r.currentInstance.format2 != nil {
657+
if mpegtsFormat, ok := r.currentInstance.format2.(*formatMPEGTS); ok && mpegtsFormat.bw != nil {
658+
err = mpegtsFormat.bw.Flush()
659+
require.NoError(t, err, "Failed to flush buffer writer")
660+
661+
// Close the current segment if it exists
662+
if mpegtsFormat.currentSegment != nil {
663+
err = mpegtsFormat.currentSegment.close()
664+
require.NoError(t, err, "Failed to close segment")
665+
mpegtsFormat.currentSegment = nil
666+
}
667+
}
668+
}
669+
670+
// Close the recorder to flush any remaining data
671+
r.Close()
672+
673+
// Verify the recording was created
674+
entries, err := os.ReadDir(filepath.Join(dir, "mypath"))
675+
require.NoError(t, err)
676+
677+
// Find the .ts file
678+
var tsFile string
679+
for _, entry := range entries {
680+
if filepath.Ext(entry.Name()) == ".ts" {
681+
tsFile = filepath.Join(dir, "mypath", entry.Name())
682+
break
683+
}
684+
}
685+
686+
if tsFile == "" {
687+
t.Fatalf("No .ts file found in %s. Directory contents: %v", recordPath, entries)
688+
}
689+
690+
// Verify the file contains our test data
691+
data, err := os.ReadFile(tsFile)
692+
require.NoError(t, err)
693+
require.Greater(t, len(data), 0, "Recorded file is empty")
694+
require.True(t, bytes.Contains(data, testData), "Test data not found in output")
695+
})
696+
}
697+
542698
func TestRecorderFMP4SegmentSwitch(t *testing.T) {
543699
desc := &description.Session{Medias: []*description.Media{
544700
{

0 commit comments

Comments
 (0)