Skip to content

Commit 44d538e

Browse files
Added test for MPEG-TS passthrough writing to recorder.test.go
1 parent 640b7bb commit 44d538e

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

internal/recorder/recorder_test.go

Lines changed: 161 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,164 @@ 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+
589+
// Create a recorder with MPEG-TS format
590+
r := &Recorder{
591+
PathFormat: recordPath,
592+
Format: conf.RecordFormatMPEGTS,
593+
PartDuration: 1 * time.Second,
594+
SegmentDuration: 2 * time.Second,
595+
PathName: "mypath",
596+
Stream: strm,
597+
Parent: test.NilLogger,
598+
}
599+
600+
// Create a buffered channel to receive data from the stream
601+
602+
// Buffer channel to prevent blocking on data send
603+
dataChan := make(chan []byte, 100)
604+
605+
// Add reader before initializing to avoid race with recorder's own reader
606+
rtpReader := func(u unit.Unit) error {
607+
rtpPackets := u.GetRTPPackets()
608+
609+
for _, pkt := range rtpPackets {
610+
if pkt == nil {
611+
continue
612+
}
613+
614+
// Send the payload to the channel
615+
if len(pkt.Payload) > 0 {
616+
select {
617+
case dataChan <- pkt.Payload:
618+
default:
619+
// Drop if channel is full
620+
}
621+
}
622+
}
623+
624+
return nil
625+
}
626+
627+
strm.AddReader(r, desc.Medias[0], mpegtsFormat, rtpReader)
628+
629+
// Initialize the recorder
630+
r.Initialize()
631+
632+
// Verify the recorder instance was created
633+
require.NotNil(t, r.currentInstance, "Recorder instance is nil")
634+
635+
// Start the reader after the recorder is fully initialized
636+
strm.StartReader(r)
637+
strm.WaitRunningReader()
638+
639+
// Ensure cleanup happens in the correct order
640+
t.Cleanup(func() {
641+
time.Sleep(100 * time.Millisecond) // Allow time for writes to complete
642+
r.Close()
643+
strm.Close()
644+
})
645+
646+
// Write multiple RTP packets to ensure we have enough data
647+
for i := 0; i < 10; i++ {
648+
// Create a new RTP packet for each iteration with updated timestamp and sequence number
649+
pkt := &rtp.Packet{
650+
Header: rtp.Header{
651+
Version: 2,
652+
PayloadType: 33, // Standard MPEG-TS payload type (RFC 2250)
653+
SequenceNumber: 100 + uint16(i),
654+
Timestamp: 123456 + uint32(i*90000), // 1 second apart
655+
SSRC: 0x9D8F,
656+
},
657+
Payload: testData,
658+
}
659+
660+
// Use the MPEG-TS format directly
661+
internalFormat := mpegtsFormat
662+
663+
// Write the RTP packet to the stream
664+
strm.WriteRTPPacket(desc.Medias[0], internalFormat, pkt, time.Now(), 0)
665+
}
666+
667+
// Check if the recorder instance was properly initialized
668+
if r.currentInstance == nil {
669+
t.Fatal("Recorder currentInstance is nil")
670+
}
671+
672+
// Give some time for all writes to complete
673+
time.Sleep(200 * time.Millisecond)
674+
675+
// Verify the recording was created
676+
entries, err := os.ReadDir(filepath.Join(dir, "mypath"))
677+
require.NoError(t, err)
678+
679+
// Find the .ts file
680+
var tsFile string
681+
for _, entry := range entries {
682+
if filepath.Ext(entry.Name()) == ".ts" {
683+
tsFile = filepath.Join(dir, "mypath", entry.Name())
684+
break
685+
}
686+
}
687+
688+
if tsFile == "" {
689+
t.Fatalf("No .ts file found in %s. Directory contents: %v", filepath.Join(dir, "mypath"), entries)
690+
}
691+
692+
// Verify the file contains our test data
693+
data, err := os.ReadFile(tsFile)
694+
require.NoError(t, err)
695+
require.Greater(t, len(data), 0, "Recorded file is empty")
696+
require.True(t, bytes.Contains(data, testData), "Test data not found in output")
697+
698+
// Give some time for all writes to complete before test ends
699+
time.Sleep(200 * time.Millisecond)
700+
})
701+
}
702+
542703
func TestRecorderFMP4SegmentSwitch(t *testing.T) {
543704
desc := &description.Session{Medias: []*description.Media{
544705
{

0 commit comments

Comments
 (0)