Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

110 řádky
2.8KB

  1. package srt
  2. import (
  3. "bytes"
  4. "context"
  5. "io"
  6. "testing"
  7. "time"
  8. "aoiprxkit"
  9. "github.com/jan/fm-rds-tx/internal/ingest"
  10. )
  11. type readCloser struct{ io.Reader }
  12. func (r readCloser) Close() error { return nil }
  13. func TestSourceEmitsChunksFromSRTFrames(t *testing.T) {
  14. var stream bytes.Buffer
  15. if err := aoiprxkit.WritePCM32Packet(&stream, 2, 48000, 2, 10, 100, []int32{1, 2, 3, 4}); err != nil {
  16. t.Fatalf("write packet 1: %v", err)
  17. }
  18. if err := aoiprxkit.WritePCM32Packet(&stream, 2, 48000, 2, 12, 200, []int32{5, 6, 7, 8}); err != nil {
  19. t.Fatalf("write packet 2: %v", err)
  20. }
  21. src := New("srt-test", aoiprxkit.SRTConfig{
  22. URL: "srt://127.0.0.1:9000?mode=listener",
  23. Mode: "listener",
  24. SampleRateHz: 48000,
  25. Channels: 2,
  26. }, WithConnOpener(func(ctx context.Context, cfg aoiprxkit.SRTConfig) (io.ReadCloser, error) {
  27. _ = ctx
  28. _ = cfg
  29. return readCloser{Reader: bytes.NewReader(stream.Bytes())}, nil
  30. }))
  31. if err := src.Start(context.Background()); err != nil {
  32. t.Fatalf("start: %v", err)
  33. }
  34. defer src.Stop()
  35. chunk1 := readChunk(t, src.Chunks())
  36. if chunk1.SourceID != "srt-test" {
  37. t.Fatalf("source id=%q want srt-test", chunk1.SourceID)
  38. }
  39. if chunk1.Channels != 2 || chunk1.SampleRateHz != 48000 {
  40. t.Fatalf("shape=%d/%d", chunk1.Channels, chunk1.SampleRateHz)
  41. }
  42. if chunk1.Discontinuity {
  43. t.Fatalf("first chunk should not be discontinuity")
  44. }
  45. assertSamples(t, chunk1.Samples, []int32{1, 2, 3, 4})
  46. chunk2 := readChunk(t, src.Chunks())
  47. if !chunk2.Discontinuity {
  48. t.Fatalf("second chunk should be marked discontinuity on seq gap")
  49. }
  50. assertSamples(t, chunk2.Samples, []int32{5, 6, 7, 8})
  51. stats := src.Stats()
  52. if stats.State != "running" {
  53. t.Fatalf("state=%q want running", stats.State)
  54. }
  55. if !stats.Connected {
  56. t.Fatalf("connected=false want true")
  57. }
  58. if stats.ChunksIn != 2 {
  59. t.Fatalf("chunksIn=%d want 2", stats.ChunksIn)
  60. }
  61. if stats.SamplesIn != 8 {
  62. t.Fatalf("samplesIn=%d want 8", stats.SamplesIn)
  63. }
  64. if stats.TransportLoss != 1 {
  65. t.Fatalf("transportLoss=%d want 1", stats.TransportLoss)
  66. }
  67. if stats.Discontinuities < 1 {
  68. t.Fatalf("discontinuities=%d want >=1", stats.Discontinuities)
  69. }
  70. if stats.LastChunkAt.IsZero() {
  71. t.Fatalf("lastChunkAt should be set")
  72. }
  73. }
  74. func readChunk(t *testing.T, ch <-chan ingest.PCMChunk) ingest.PCMChunk {
  75. t.Helper()
  76. select {
  77. case chunk, ok := <-ch:
  78. if !ok {
  79. t.Fatal("chunk channel closed")
  80. }
  81. return chunk
  82. case <-time.After(500 * time.Millisecond):
  83. t.Fatal("timeout waiting for chunk")
  84. return ingest.PCMChunk{}
  85. }
  86. }
  87. func assertSamples(t *testing.T, got, want []int32) {
  88. t.Helper()
  89. if len(got) != len(want) {
  90. t.Fatalf("sample len=%d want %d", len(got), len(want))
  91. }
  92. for i := range want {
  93. if got[i] != want[i] {
  94. t.Fatalf("sample[%d]=%d want %d", i, got[i], want[i])
  95. }
  96. }
  97. }