media.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package core
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. "github.com/pion/sdp/v3"
  7. )
  8. // Media take best from:
  9. // - deepch/vdk/format/rtsp/sdp.Media
  10. // - pion/sdp.MediaDescription
  11. type Media struct {
  12. Kind string `json:"kind,omitempty"` // video or audio
  13. Direction string `json:"direction,omitempty"` // sendonly, recvonly
  14. Codecs []*Codec `json:"codecs,omitempty"`
  15. ID string `json:"id,omitempty"` // MID for WebRTC, Control for RTSP
  16. }
  17. func (m *Media) String() string {
  18. s := fmt.Sprintf("%s, %s", m.Kind, m.Direction)
  19. for _, codec := range m.Codecs {
  20. name := codec.String()
  21. if strings.Contains(s, name) {
  22. continue
  23. }
  24. s += ", " + name
  25. }
  26. return s
  27. }
  28. func (m *Media) MarshalJSON() ([]byte, error) {
  29. return json.Marshal(m.String())
  30. }
  31. func (m *Media) Clone() *Media {
  32. clone := *m
  33. clone.Codecs = make([]*Codec, len(m.Codecs))
  34. for i, codec := range m.Codecs {
  35. clone.Codecs[i] = codec.Clone()
  36. }
  37. return &clone
  38. }
  39. func (m *Media) MatchMedia(remote *Media) (codec, remoteCodec *Codec) {
  40. // check same kind and opposite dirrection
  41. if m.Kind != remote.Kind ||
  42. m.Direction == DirectionSendonly && remote.Direction != DirectionRecvonly ||
  43. m.Direction == DirectionRecvonly && remote.Direction != DirectionSendonly {
  44. return nil, nil
  45. }
  46. for _, codec = range m.Codecs {
  47. for _, remoteCodec = range remote.Codecs {
  48. if codec.Match(remoteCodec) {
  49. return
  50. }
  51. }
  52. }
  53. return nil, nil
  54. }
  55. func (m *Media) MatchCodec(remote *Codec) *Codec {
  56. for _, codec := range m.Codecs {
  57. if codec.Match(remote) {
  58. return codec
  59. }
  60. }
  61. return nil
  62. }
  63. func (m *Media) MatchAll() bool {
  64. for _, codec := range m.Codecs {
  65. if codec.Name == CodecAll {
  66. return true
  67. }
  68. }
  69. return false
  70. }
  71. func (m *Media) Equal(media *Media) bool {
  72. if media.ID != "" {
  73. return m.ID == media.ID
  74. }
  75. return m.String() == media.String()
  76. }
  77. func GetKind(name string) string {
  78. switch name {
  79. case CodecH264, CodecH265, CodecVP8, CodecVP9, CodecAV1, CodecJPEG, CodecRAW:
  80. return KindVideo
  81. case CodecPCMU, CodecPCMA, CodecAAC, CodecOpus, CodecG722, CodecMP3, CodecPCM, CodecPCML, CodecELD, CodecFLAC:
  82. return KindAudio
  83. }
  84. return ""
  85. }
  86. func MarshalSDP(name string, medias []*Media) ([]byte, error) {
  87. sd := &sdp.SessionDescription{
  88. Origin: sdp.Origin{
  89. Username: "-", SessionID: 1, SessionVersion: 1,
  90. NetworkType: "IN", AddressType: "IP4", UnicastAddress: "0.0.0.0",
  91. },
  92. SessionName: sdp.SessionName(name),
  93. ConnectionInformation: &sdp.ConnectionInformation{
  94. NetworkType: "IN", AddressType: "IP4", Address: &sdp.Address{
  95. Address: "0.0.0.0",
  96. },
  97. },
  98. TimeDescriptions: []sdp.TimeDescription{
  99. {Timing: sdp.Timing{}},
  100. },
  101. }
  102. for _, media := range medias {
  103. if media.Codecs == nil {
  104. continue
  105. }
  106. codec := media.Codecs[0]
  107. switch codec.Name {
  108. case CodecELD:
  109. name = CodecAAC
  110. case CodecPCML:
  111. name = CodecPCM // beacuse we using pcm.LittleToBig for RTSP server
  112. default:
  113. name = codec.Name
  114. }
  115. md := &sdp.MediaDescription{
  116. MediaName: sdp.MediaName{
  117. Media: media.Kind,
  118. Protos: []string{"RTP", "AVP"},
  119. },
  120. }
  121. md.WithCodec(codec.PayloadType, name, codec.ClockRate, codec.Channels, codec.FmtpLine)
  122. if media.ID != "" {
  123. md.WithValueAttribute("control", media.ID)
  124. }
  125. sd.MediaDescriptions = append(sd.MediaDescriptions, md)
  126. }
  127. return sd.Marshal()
  128. }
  129. func UnmarshalMedia(md *sdp.MediaDescription) *Media {
  130. m := &Media{
  131. Kind: md.MediaName.Media,
  132. }
  133. for _, attr := range md.Attributes {
  134. switch attr.Key {
  135. case DirectionSendonly, DirectionRecvonly, DirectionSendRecv:
  136. m.Direction = attr.Key
  137. case "control", "mid":
  138. m.ID = attr.Value
  139. }
  140. }
  141. for _, format := range md.MediaName.Formats {
  142. m.Codecs = append(m.Codecs, UnmarshalCodec(md, format))
  143. }
  144. return m
  145. }
  146. func ParseQuery(query map[string][]string) (medias []*Media) {
  147. // set media candidates from query list
  148. for key, values := range query {
  149. switch key {
  150. case KindVideo, KindAudio:
  151. for _, value := range values {
  152. media := &Media{Kind: key, Direction: DirectionSendonly}
  153. for _, name := range strings.Split(value, ",") {
  154. name = strings.ToUpper(name)
  155. // check aliases
  156. switch name {
  157. case "", "COPY":
  158. name = CodecAny
  159. case "MJPEG":
  160. name = CodecJPEG
  161. case "AAC":
  162. name = CodecAAC
  163. case "MP3":
  164. name = CodecMP3
  165. }
  166. media.Codecs = append(media.Codecs, &Codec{Name: name})
  167. }
  168. medias = append(medias, media)
  169. }
  170. }
  171. }
  172. return
  173. }