Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

115 wiersze
2.3KB

  1. package aoiprxkit
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "sync"
  7. "time"
  8. )
  9. type StreamReceiverConfig struct {
  10. SourceLabel string
  11. }
  12. type StreamReceiver struct {
  13. cfg StreamReceiverConfig
  14. opener func(context.Context) (io.ReadCloser, error)
  15. onFrame FrameHandler
  16. mu sync.Mutex
  17. rc io.ReadCloser
  18. cancel context.CancelFunc
  19. done chan struct{}
  20. }
  21. func NewStreamReceiver(cfg StreamReceiverConfig, opener func(context.Context) (io.ReadCloser, error), onFrame FrameHandler) (*StreamReceiver, error) {
  22. if opener == nil {
  23. return nil, fmt.Errorf("opener must not be nil")
  24. }
  25. if onFrame == nil {
  26. return nil, fmt.Errorf("onFrame must not be nil")
  27. }
  28. return &StreamReceiver{cfg: cfg, opener: opener, onFrame: onFrame, done: make(chan struct{})}, nil
  29. }
  30. func (r *StreamReceiver) Start(ctx context.Context) error {
  31. r.mu.Lock()
  32. defer r.mu.Unlock()
  33. if r.rc != nil {
  34. return fmt.Errorf("stream receiver already started")
  35. }
  36. cctx, cancel := context.WithCancel(ctx)
  37. rc, err := r.opener(cctx)
  38. if err != nil {
  39. cancel()
  40. return err
  41. }
  42. r.rc = rc
  43. r.cancel = cancel
  44. r.done = make(chan struct{})
  45. go r.loop(cctx, rc)
  46. return nil
  47. }
  48. func (r *StreamReceiver) Stop() error {
  49. r.mu.Lock()
  50. rc := r.rc
  51. cancel := r.cancel
  52. done := r.done
  53. r.rc = nil
  54. r.cancel = nil
  55. r.mu.Unlock()
  56. if cancel != nil {
  57. cancel()
  58. }
  59. if rc != nil {
  60. _ = rc.Close()
  61. }
  62. if done != nil {
  63. <-done
  64. }
  65. return nil
  66. }
  67. func (r *StreamReceiver) loop(ctx context.Context, rc io.ReadCloser) {
  68. defer close(r.done)
  69. for {
  70. select {
  71. case <-ctx.Done():
  72. return
  73. default:
  74. }
  75. hdr, err := ReadStreamHeader(rc)
  76. if err != nil {
  77. return
  78. }
  79. payload := make([]byte, hdr.PayloadBytes)
  80. if _, err := io.ReadFull(rc, payload); err != nil {
  81. return
  82. }
  83. switch hdr.Codec {
  84. case StreamCodecPCM_S32LE:
  85. samples, err := DecodeS32LE(payload, int(hdr.Channels))
  86. if err != nil {
  87. continue
  88. }
  89. r.onFrame(PCMFrame{
  90. SequenceNumber: uint16(hdr.Sequence & 0xffff),
  91. Timestamp: uint32(hdr.Timestamp & 0xffffffff),
  92. SampleRateHz: int(hdr.SampleRateHz),
  93. Channels: int(hdr.Channels),
  94. Samples: samples,
  95. ReceivedAt: time.Now(),
  96. Source: r.cfg.SourceLabel,
  97. })
  98. case StreamCodecOpus:
  99. // Reserved for later phase. Not decoded in this module revision.
  100. continue
  101. default:
  102. continue
  103. }
  104. }
  105. }