Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

134 linhas
3.4KB

  1. package factory
  2. import (
  3. "bytes"
  4. "context"
  5. "io"
  6. "testing"
  7. "time"
  8. "aoiprxkit"
  9. "github.com/jan/fm-rds-tx/internal/audio"
  10. "github.com/jan/fm-rds-tx/internal/config"
  11. "github.com/jan/fm-rds-tx/internal/ingest"
  12. )
  13. type streamReadCloser struct{ io.Reader }
  14. func (r streamReadCloser) Close() error { return nil }
  15. func TestHTTPRawFactoryToRuntimeSmoke(t *testing.T) {
  16. cfg := config.Default()
  17. cfg.Ingest.Kind = "http-raw"
  18. cfg.Ingest.HTTPRaw.SampleRateHz = 44100
  19. cfg.Ingest.HTTPRaw.Channels = 2
  20. src, ingress, err := BuildSource(cfg, Deps{})
  21. if err != nil {
  22. t.Fatalf("build source: %v", err)
  23. }
  24. if src == nil || ingress == nil {
  25. t.Fatalf("expected source and ingress for kind=http-raw")
  26. }
  27. sink := audio.NewStreamSource(128, cfg.Ingest.HTTPRaw.SampleRateHz)
  28. rt := ingest.NewRuntime(sink, src)
  29. if err := rt.Start(context.Background()); err != nil {
  30. t.Fatalf("runtime start: %v", err)
  31. }
  32. defer rt.Stop()
  33. // Two stereo frames: L1,R1,L2,R2 (S16LE).
  34. frames, err := ingress.WritePCM16([]byte{
  35. 0xE8, 0x03, 0x18, 0xFC,
  36. 0xD0, 0x07, 0x30, 0xF8,
  37. })
  38. if err != nil {
  39. t.Fatalf("write pcm16: %v", err)
  40. }
  41. if frames != 2 {
  42. t.Fatalf("frames=%d want 2", frames)
  43. }
  44. waitForSinkFrames(t, sink, 2)
  45. stats := rt.Stats()
  46. if stats.Active.Kind != "http-raw" {
  47. t.Fatalf("active kind=%q want http-raw", stats.Active.Kind)
  48. }
  49. if stats.Source.ChunksIn != 1 {
  50. t.Fatalf("source chunksIn=%d want 1", stats.Source.ChunksIn)
  51. }
  52. if stats.Source.SamplesIn != 4 {
  53. t.Fatalf("source samplesIn=%d want 4", stats.Source.SamplesIn)
  54. }
  55. if stats.Runtime.State != "running" {
  56. t.Fatalf("runtime state=%q want running", stats.Runtime.State)
  57. }
  58. if stats.Runtime.LastChunkAt.IsZero() {
  59. t.Fatalf("runtime lastChunkAt should be set")
  60. }
  61. }
  62. func TestSRTFactoryToRuntimeSmoke(t *testing.T) {
  63. var stream bytes.Buffer
  64. if err := aoiprxkit.WritePCM32Packet(&stream, 2, 48000, 2, 1, 480, []int32{11, -11, 22, -22}); err != nil {
  65. t.Fatalf("write packet: %v", err)
  66. }
  67. cfg := config.Default()
  68. cfg.Ingest.Kind = "srt"
  69. cfg.Ingest.SRT.URL = "srt://127.0.0.1:9000?mode=listener"
  70. cfg.Ingest.SRT.SampleRateHz = 48000
  71. cfg.Ingest.SRT.Channels = 2
  72. src, ingress, err := BuildSource(cfg, Deps{
  73. SRTOpener: func(ctx context.Context, srtCfg aoiprxkit.SRTConfig) (io.ReadCloser, error) {
  74. _ = ctx
  75. _ = srtCfg
  76. return streamReadCloser{Reader: bytes.NewReader(stream.Bytes())}, nil
  77. },
  78. })
  79. if err != nil {
  80. t.Fatalf("build source: %v", err)
  81. }
  82. if src == nil {
  83. t.Fatalf("expected source for kind=srt")
  84. }
  85. if ingress != nil {
  86. t.Fatalf("expected no ingress for kind=srt")
  87. }
  88. sink := audio.NewStreamSource(128, cfg.Ingest.SRT.SampleRateHz)
  89. rt := ingest.NewRuntime(sink, src)
  90. if err := rt.Start(context.Background()); err != nil {
  91. t.Fatalf("runtime start: %v", err)
  92. }
  93. defer rt.Stop()
  94. waitForSinkFrames(t, sink, 2)
  95. stats := rt.Stats()
  96. if stats.Active.Kind != "srt" {
  97. t.Fatalf("active kind=%q want srt", stats.Active.Kind)
  98. }
  99. if stats.Source.ChunksIn != 1 {
  100. t.Fatalf("source chunksIn=%d want 1", stats.Source.ChunksIn)
  101. }
  102. if stats.Source.SamplesIn != 4 {
  103. t.Fatalf("source samplesIn=%d want 4", stats.Source.SamplesIn)
  104. }
  105. }
  106. func waitForSinkFrames(t *testing.T, sink *audio.StreamSource, minFrames int) {
  107. t.Helper()
  108. deadline := time.Now().Add(1 * time.Second)
  109. for time.Now().Before(deadline) {
  110. if sink.Available() >= minFrames {
  111. return
  112. }
  113. time.Sleep(10 * time.Millisecond)
  114. }
  115. t.Fatalf("timeout waiting for sink frames: have=%d want>=%d", sink.Available(), minFrames)
  116. }