goodbye.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package rtcp
  4. import (
  5. "encoding/binary"
  6. "fmt"
  7. )
  8. // The Goodbye packet indicates that one or more sources are no longer active.
  9. type Goodbye struct {
  10. // The SSRC/CSRC identifiers that are no longer active
  11. Sources []uint32
  12. // Optional text indicating the reason for leaving, e.g., "camera malfunction" or "RTP loop detected"
  13. Reason string
  14. }
  15. // Marshal encodes the Goodbye packet in binary
  16. func (g Goodbye) Marshal() ([]byte, error) {
  17. /*
  18. * 0 1 2 3
  19. * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  20. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  21. * |V=2|P| SC | PT=BYE=203 | length |
  22. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  23. * | SSRC/CSRC |
  24. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  25. * : ... :
  26. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  27. * (opt) | length | reason for leaving ...
  28. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  29. */
  30. rawPacket := make([]byte, g.len())
  31. packetBody := rawPacket[headerLength:]
  32. if len(g.Sources) > countMax {
  33. return nil, errTooManySources
  34. }
  35. for i, s := range g.Sources {
  36. binary.BigEndian.PutUint32(packetBody[i*ssrcLength:], s)
  37. }
  38. if g.Reason != "" {
  39. reason := []byte(g.Reason)
  40. if len(reason) > sdesMaxOctetCount {
  41. return nil, errReasonTooLong
  42. }
  43. reasonOffset := len(g.Sources) * ssrcLength
  44. packetBody[reasonOffset] = uint8(len(reason))
  45. copy(packetBody[reasonOffset+1:], reason)
  46. }
  47. hData, err := g.Header().Marshal()
  48. if err != nil {
  49. return nil, err
  50. }
  51. copy(rawPacket, hData)
  52. return rawPacket, nil
  53. }
  54. // Unmarshal decodes the Goodbye packet from binary
  55. func (g *Goodbye) Unmarshal(rawPacket []byte) error {
  56. /*
  57. * 0 1 2 3
  58. * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  59. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  60. * |V=2|P| SC | PT=BYE=203 | length |
  61. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  62. * | SSRC/CSRC |
  63. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  64. * : ... :
  65. * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  66. * (opt) | length | reason for leaving ...
  67. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  68. */
  69. var header Header
  70. if err := header.Unmarshal(rawPacket); err != nil {
  71. return err
  72. }
  73. if header.Type != TypeGoodbye {
  74. return errWrongType
  75. }
  76. if getPadding(len(rawPacket)) != 0 {
  77. return errPacketTooShort
  78. }
  79. g.Sources = make([]uint32, header.Count)
  80. reasonOffset := int(headerLength + header.Count*ssrcLength)
  81. if reasonOffset > len(rawPacket) {
  82. return errPacketTooShort
  83. }
  84. for i := 0; i < int(header.Count); i++ {
  85. offset := headerLength + i*ssrcLength
  86. g.Sources[i] = binary.BigEndian.Uint32(rawPacket[offset:])
  87. }
  88. if reasonOffset < len(rawPacket) {
  89. reasonLen := int(rawPacket[reasonOffset])
  90. reasonEnd := reasonOffset + 1 + reasonLen
  91. if reasonEnd > len(rawPacket) {
  92. return errPacketTooShort
  93. }
  94. g.Reason = string(rawPacket[reasonOffset+1 : reasonEnd])
  95. }
  96. return nil
  97. }
  98. // Header returns the Header associated with this packet.
  99. func (g *Goodbye) Header() Header {
  100. return Header{
  101. Padding: false,
  102. Count: uint8(len(g.Sources)),
  103. Type: TypeGoodbye,
  104. Length: uint16((g.len() / 4) - 1),
  105. }
  106. }
  107. func (g *Goodbye) len() int {
  108. srcsLength := len(g.Sources) * ssrcLength
  109. reasonLength := len(g.Reason) + 1
  110. l := headerLength + srcsLength + reasonLength
  111. // align to 32-bit boundary
  112. return l + getPadding(l)
  113. }
  114. // DestinationSSRC returns an array of SSRC values that this packet refers to.
  115. func (g *Goodbye) DestinationSSRC() []uint32 {
  116. out := make([]uint32, len(g.Sources))
  117. copy(out, g.Sources)
  118. return out
  119. }
  120. func (g Goodbye) String() string {
  121. out := "Goodbye\n"
  122. for i, s := range g.Sources {
  123. out += fmt.Sprintf("\tSource %d: %x\n", i, s)
  124. }
  125. out += fmt.Sprintf("\tReason: %s\n", g.Reason)
  126. return out
  127. }