track_local_static.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //go:build !js
  2. // +build !js
  3. package webrtc
  4. import (
  5. "strings"
  6. "sync"
  7. "github.com/pion/rtp"
  8. "github.com/pion/webrtc/v3/internal/util"
  9. "github.com/pion/webrtc/v3/pkg/media"
  10. )
  11. // trackBinding is a single bind for a Track
  12. // Bind can be called multiple times, this stores the
  13. // result for a single bind call so that it can be used when writing
  14. type trackBinding struct {
  15. id string
  16. ssrc SSRC
  17. payloadType PayloadType
  18. writeStream TrackLocalWriter
  19. }
  20. // TrackLocalStaticRTP is a TrackLocal that has a pre-set codec and accepts RTP Packets.
  21. // If you wish to send a media.Sample use TrackLocalStaticSample
  22. type TrackLocalStaticRTP struct {
  23. mu sync.RWMutex
  24. bindings []trackBinding
  25. codec RTPCodecCapability
  26. id, rid, streamID string
  27. }
  28. // NewTrackLocalStaticRTP returns a TrackLocalStaticRTP.
  29. func NewTrackLocalStaticRTP(c RTPCodecCapability, id, streamID string, options ...func(*TrackLocalStaticRTP)) (*TrackLocalStaticRTP, error) {
  30. t := &TrackLocalStaticRTP{
  31. codec: c,
  32. bindings: []trackBinding{},
  33. id: id,
  34. streamID: streamID,
  35. }
  36. for _, option := range options {
  37. option(t)
  38. }
  39. return t, nil
  40. }
  41. // WithRTPStreamID sets the RTP stream ID for this TrackLocalStaticRTP.
  42. func WithRTPStreamID(rid string) func(*TrackLocalStaticRTP) {
  43. return func(t *TrackLocalStaticRTP) {
  44. t.rid = rid
  45. }
  46. }
  47. // Bind is called by the PeerConnection after negotiation is complete
  48. // This asserts that the code requested is supported by the remote peer.
  49. // If so it setups all the state (SSRC and PayloadType) to have a call
  50. func (s *TrackLocalStaticRTP) Bind(t TrackLocalContext) (RTPCodecParameters, error) {
  51. s.mu.Lock()
  52. defer s.mu.Unlock()
  53. parameters := RTPCodecParameters{RTPCodecCapability: s.codec}
  54. if codec, matchType := codecParametersFuzzySearch(parameters, t.CodecParameters()); matchType != codecMatchNone {
  55. s.bindings = append(s.bindings, trackBinding{
  56. ssrc: t.SSRC(),
  57. payloadType: codec.PayloadType,
  58. writeStream: t.WriteStream(),
  59. id: t.ID(),
  60. })
  61. return codec, nil
  62. }
  63. return RTPCodecParameters{}, ErrUnsupportedCodec
  64. }
  65. // Unbind implements the teardown logic when the track is no longer needed. This happens
  66. // because a track has been stopped.
  67. func (s *TrackLocalStaticRTP) Unbind(t TrackLocalContext) error {
  68. s.mu.Lock()
  69. defer s.mu.Unlock()
  70. for i := range s.bindings {
  71. if s.bindings[i].id == t.ID() {
  72. s.bindings[i] = s.bindings[len(s.bindings)-1]
  73. s.bindings = s.bindings[:len(s.bindings)-1]
  74. return nil
  75. }
  76. }
  77. return ErrUnbindFailed
  78. }
  79. // ID is the unique identifier for this Track. This should be unique for the
  80. // stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
  81. // and StreamID would be 'desktop' or 'webcam'
  82. func (s *TrackLocalStaticRTP) ID() string { return s.id }
  83. // StreamID is the group this track belongs too. This must be unique
  84. func (s *TrackLocalStaticRTP) StreamID() string { return s.streamID }
  85. // RID is the RTP stream identifier.
  86. func (s *TrackLocalStaticRTP) RID() string { return s.rid }
  87. // Kind controls if this TrackLocal is audio or video
  88. func (s *TrackLocalStaticRTP) Kind() RTPCodecType {
  89. switch {
  90. case strings.HasPrefix(s.codec.MimeType, "audio/"):
  91. return RTPCodecTypeAudio
  92. case strings.HasPrefix(s.codec.MimeType, "video/"):
  93. return RTPCodecTypeVideo
  94. default:
  95. return RTPCodecType(0)
  96. }
  97. }
  98. // Codec gets the Codec of the track
  99. func (s *TrackLocalStaticRTP) Codec() RTPCodecCapability {
  100. return s.codec
  101. }
  102. // packetPool is a pool of packets used by WriteRTP and Write below
  103. // nolint:gochecknoglobals
  104. var rtpPacketPool = sync.Pool{
  105. New: func() interface{} {
  106. return &rtp.Packet{}
  107. },
  108. }
  109. // WriteRTP writes a RTP Packet to the TrackLocalStaticRTP
  110. // If one PeerConnection fails the packets will still be sent to
  111. // all PeerConnections. The error message will contain the ID of the failed
  112. // PeerConnections so you can remove them
  113. func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error {
  114. ipacket := rtpPacketPool.Get()
  115. packet := ipacket.(*rtp.Packet) //nolint:forcetypeassert
  116. defer func() {
  117. *packet = rtp.Packet{}
  118. rtpPacketPool.Put(ipacket)
  119. }()
  120. *packet = *p
  121. return s.writeRTP(packet)
  122. }
  123. // writeRTP is like WriteRTP, except that it may modify the packet p
  124. func (s *TrackLocalStaticRTP) writeRTP(p *rtp.Packet) error {
  125. s.mu.RLock()
  126. defer s.mu.RUnlock()
  127. writeErrs := []error{}
  128. for _, b := range s.bindings {
  129. p.Header.SSRC = uint32(b.ssrc)
  130. p.Header.PayloadType = uint8(b.payloadType)
  131. if _, err := b.writeStream.WriteRTP(&p.Header, p.Payload); err != nil {
  132. writeErrs = append(writeErrs, err)
  133. }
  134. }
  135. return util.FlattenErrs(writeErrs)
  136. }
  137. // Write writes a RTP Packet as a buffer to the TrackLocalStaticRTP
  138. // If one PeerConnection fails the packets will still be sent to
  139. // all PeerConnections. The error message will contain the ID of the failed
  140. // PeerConnections so you can remove them
  141. func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) {
  142. ipacket := rtpPacketPool.Get()
  143. packet := ipacket.(*rtp.Packet) //nolint:forcetypeassert
  144. defer func() {
  145. *packet = rtp.Packet{}
  146. rtpPacketPool.Put(ipacket)
  147. }()
  148. if err = packet.Unmarshal(b); err != nil {
  149. return 0, err
  150. }
  151. return len(b), s.writeRTP(packet)
  152. }
  153. // TrackLocalStaticSample is a TrackLocal that has a pre-set codec and accepts Samples.
  154. // If you wish to send a RTP Packet use TrackLocalStaticRTP
  155. type TrackLocalStaticSample struct {
  156. packetizer rtp.Packetizer
  157. sequencer rtp.Sequencer
  158. rtpTrack *TrackLocalStaticRTP
  159. clockRate float64
  160. }
  161. // NewTrackLocalStaticSample returns a TrackLocalStaticSample
  162. func NewTrackLocalStaticSample(c RTPCodecCapability, id, streamID string, options ...func(*TrackLocalStaticRTP)) (*TrackLocalStaticSample, error) {
  163. rtpTrack, err := NewTrackLocalStaticRTP(c, id, streamID, options...)
  164. if err != nil {
  165. return nil, err
  166. }
  167. return &TrackLocalStaticSample{
  168. rtpTrack: rtpTrack,
  169. }, nil
  170. }
  171. // ID is the unique identifier for this Track. This should be unique for the
  172. // stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
  173. // and StreamID would be 'desktop' or 'webcam'
  174. func (s *TrackLocalStaticSample) ID() string { return s.rtpTrack.ID() }
  175. // StreamID is the group this track belongs too. This must be unique
  176. func (s *TrackLocalStaticSample) StreamID() string { return s.rtpTrack.StreamID() }
  177. // RID is the RTP stream identifier.
  178. func (s *TrackLocalStaticSample) RID() string { return s.rtpTrack.RID() }
  179. // Kind controls if this TrackLocal is audio or video
  180. func (s *TrackLocalStaticSample) Kind() RTPCodecType { return s.rtpTrack.Kind() }
  181. // Codec gets the Codec of the track
  182. func (s *TrackLocalStaticSample) Codec() RTPCodecCapability {
  183. return s.rtpTrack.Codec()
  184. }
  185. // Bind is called by the PeerConnection after negotiation is complete
  186. // This asserts that the code requested is supported by the remote peer.
  187. // If so it setups all the state (SSRC and PayloadType) to have a call
  188. func (s *TrackLocalStaticSample) Bind(t TrackLocalContext) (RTPCodecParameters, error) {
  189. codec, err := s.rtpTrack.Bind(t)
  190. if err != nil {
  191. return codec, err
  192. }
  193. s.rtpTrack.mu.Lock()
  194. defer s.rtpTrack.mu.Unlock()
  195. // We only need one packetizer
  196. if s.packetizer != nil {
  197. return codec, nil
  198. }
  199. payloader, err := payloaderForCodec(codec.RTPCodecCapability)
  200. if err != nil {
  201. return codec, err
  202. }
  203. s.sequencer = rtp.NewRandomSequencer()
  204. s.packetizer = rtp.NewPacketizer(
  205. rtpOutboundMTU,
  206. 0, // Value is handled when writing
  207. 0, // Value is handled when writing
  208. payloader,
  209. s.sequencer,
  210. codec.ClockRate,
  211. )
  212. s.clockRate = float64(codec.RTPCodecCapability.ClockRate)
  213. return codec, nil
  214. }
  215. // Unbind implements the teardown logic when the track is no longer needed. This happens
  216. // because a track has been stopped.
  217. func (s *TrackLocalStaticSample) Unbind(t TrackLocalContext) error {
  218. return s.rtpTrack.Unbind(t)
  219. }
  220. // WriteSample writes a Sample to the TrackLocalStaticSample
  221. // If one PeerConnection fails the packets will still be sent to
  222. // all PeerConnections. The error message will contain the ID of the failed
  223. // PeerConnections so you can remove them
  224. func (s *TrackLocalStaticSample) WriteSample(sample media.Sample) error {
  225. s.rtpTrack.mu.RLock()
  226. p := s.packetizer
  227. clockRate := s.clockRate
  228. s.rtpTrack.mu.RUnlock()
  229. if p == nil {
  230. return nil
  231. }
  232. // skip packets by the number of previously dropped packets
  233. for i := uint16(0); i < sample.PrevDroppedPackets; i++ {
  234. s.sequencer.NextSequenceNumber()
  235. }
  236. samples := uint32(sample.Duration.Seconds() * clockRate)
  237. if sample.PrevDroppedPackets > 0 {
  238. p.SkipSamples(samples * uint32(sample.PrevDroppedPackets))
  239. }
  240. packets := p.Packetize(sample.Data, samples)
  241. writeErrs := []error{}
  242. for _, p := range packets {
  243. if err := s.rtpTrack.WriteRTP(p); err != nil {
  244. writeErrs = append(writeErrs, err)
  245. }
  246. }
  247. return util.FlattenErrs(writeErrs)
  248. }