Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

195 рядки
3.8KB

  1. package aoiprxkit
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "sync"
  7. "time"
  8. )
  9. type PCMFrame struct {
  10. SequenceNumber uint16
  11. Timestamp uint32
  12. SampleRateHz int
  13. Channels int
  14. Samples []int32 // interleaved
  15. ReceivedAt time.Time
  16. Source string
  17. }
  18. type FrameHandler func(frame PCMFrame)
  19. type Receiver struct {
  20. cfg Config
  21. onFrame FrameHandler
  22. mu sync.Mutex
  23. conn *net.UDPConn
  24. cancel context.CancelFunc
  25. done chan struct{}
  26. doneOnce sync.Once
  27. stats statsAtomic
  28. }
  29. func NewReceiver(cfg Config, onFrame FrameHandler) (*Receiver, error) {
  30. if err := cfg.Validate(); err != nil {
  31. return nil, err
  32. }
  33. if onFrame == nil {
  34. return nil, fmt.Errorf("onFrame must not be nil")
  35. }
  36. return &Receiver{
  37. cfg: cfg,
  38. onFrame: onFrame,
  39. done: make(chan struct{}),
  40. }, nil
  41. }
  42. func (r *Receiver) Start(ctx context.Context) error {
  43. r.mu.Lock()
  44. defer r.mu.Unlock()
  45. if r.conn != nil {
  46. return fmt.Errorf("receiver already started")
  47. }
  48. group := net.ParseIP(r.cfg.MulticastGroup)
  49. ifi, err := resolveMulticastInterface(r.cfg.InterfaceName)
  50. if err != nil {
  51. return err
  52. }
  53. addr := &net.UDPAddr{IP: group, Port: r.cfg.Port}
  54. conn, err := net.ListenMulticastUDP("udp4", ifi, addr)
  55. if err != nil {
  56. return fmt.Errorf("listen multicast UDP: %w", err)
  57. }
  58. if r.cfg.ReadBufferBytes > 0 {
  59. _ = conn.SetReadBuffer(r.cfg.ReadBufferBytes)
  60. }
  61. cctx, cancel := context.WithCancel(ctx)
  62. r.conn = conn
  63. r.cancel = cancel
  64. r.done = make(chan struct{})
  65. r.doneOnce = sync.Once{}
  66. go r.loop(cctx)
  67. return nil
  68. }
  69. func (r *Receiver) Stop() error {
  70. r.mu.Lock()
  71. if r.conn == nil {
  72. r.mu.Unlock()
  73. return nil
  74. }
  75. conn := r.conn
  76. cancel := r.cancel
  77. done := r.done
  78. r.conn = nil
  79. r.cancel = nil
  80. r.mu.Unlock()
  81. if cancel != nil {
  82. cancel()
  83. }
  84. _ = conn.Close()
  85. <-done
  86. return nil
  87. }
  88. func (r *Receiver) Stats() Stats {
  89. return r.stats.snapshot()
  90. }
  91. func (r *Receiver) loop(ctx context.Context) {
  92. defer r.doneOnce.Do(func() { close(r.done) })
  93. jb := newJitterBuffer(r.cfg.JitterDepthPackets)
  94. buf := make([]byte, 64*1024)
  95. for {
  96. select {
  97. case <-ctx.Done():
  98. return
  99. default:
  100. }
  101. _ = r.conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
  102. n, _, err := r.conn.ReadFromUDP(buf)
  103. if err != nil {
  104. if ne, ok := err.(net.Error); ok && ne.Timeout() {
  105. continue
  106. }
  107. return
  108. }
  109. r.stats.packetsReceived.Add(1)
  110. if n < 12 {
  111. r.stats.packetsShort.Add(1)
  112. continue
  113. }
  114. pkt, err := ParseRTPPacket(buf[:n])
  115. if err != nil {
  116. r.stats.packetsShort.Add(1)
  117. continue
  118. }
  119. r.stats.packetsParsed.Add(1)
  120. if pkt.PayloadType != r.cfg.PayloadType {
  121. r.stats.packetsWrongPT.Add(1)
  122. continue
  123. }
  124. ready, lateDrop, gapLoss, reorder := jb.push(pkt)
  125. if lateDrop {
  126. r.stats.packetsLateDrop.Add(1)
  127. continue
  128. }
  129. if gapLoss > 0 {
  130. r.stats.packetsGapLoss.Add(gapLoss)
  131. }
  132. if reorder {
  133. r.stats.jitterReorders.Add(1)
  134. }
  135. for _, rp := range ready {
  136. samples, err := DecodeL24BE(rp.Payload, r.cfg.Channels)
  137. if err != nil {
  138. r.stats.decodeErrors.Add(1)
  139. continue
  140. }
  141. frame := PCMFrame{
  142. SequenceNumber: rp.SequenceNumber,
  143. Timestamp: rp.Timestamp,
  144. SampleRateHz: r.cfg.SampleRateHz,
  145. Channels: r.cfg.Channels,
  146. Samples: samples,
  147. ReceivedAt: time.Now(),
  148. Source: fmt.Sprintf("rtp://%s:%d", r.cfg.MulticastGroup, r.cfg.Port),
  149. }
  150. r.onFrame(frame)
  151. r.stats.packetsDelivered.Add(1)
  152. r.stats.samplesDelivered.Add(uint64(len(samples)))
  153. if r.cfg.Channels > 0 {
  154. r.stats.framesDelivered.Add(uint64(len(samples) / r.cfg.Channels))
  155. }
  156. r.stats.lastSequence.Store(uint32(rp.SequenceNumber))
  157. r.stats.sequenceValid.Store(1)
  158. }
  159. }
  160. }
  161. func resolveMulticastInterface(name string) (*net.Interface, error) {
  162. if name == "" {
  163. return nil, nil
  164. }
  165. ifi, err := net.InterfaceByName(name)
  166. if err != nil {
  167. return nil, fmt.Errorf("resolve interface %q: %w", name, err)
  168. }
  169. return ifi, nil
  170. }