123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- package core
- import (
- "encoding/json"
- "fmt"
- "strings"
- "github.com/pion/sdp/v3"
- )
- // Media take best from:
- // - deepch/vdk/format/rtsp/sdp.Media
- // - pion/sdp.MediaDescription
- type Media struct {
- Kind string `json:"kind,omitempty"` // video or audio
- Direction string `json:"direction,omitempty"` // sendonly, recvonly
- Codecs []*Codec `json:"codecs,omitempty"`
- ID string `json:"id,omitempty"` // MID for WebRTC, Control for RTSP
- }
- func (m *Media) String() string {
- s := fmt.Sprintf("%s, %s", m.Kind, m.Direction)
- for _, codec := range m.Codecs {
- name := codec.String()
- if strings.Contains(s, name) {
- continue
- }
- s += ", " + name
- }
- return s
- }
- func (m *Media) MarshalJSON() ([]byte, error) {
- return json.Marshal(m.String())
- }
- func (m *Media) Clone() *Media {
- clone := *m
- clone.Codecs = make([]*Codec, len(m.Codecs))
- for i, codec := range m.Codecs {
- clone.Codecs[i] = codec.Clone()
- }
- return &clone
- }
- func (m *Media) MatchMedia(remote *Media) (codec, remoteCodec *Codec) {
- // check same kind and opposite dirrection
- if m.Kind != remote.Kind ||
- m.Direction == DirectionSendonly && remote.Direction != DirectionRecvonly ||
- m.Direction == DirectionRecvonly && remote.Direction != DirectionSendonly {
- return nil, nil
- }
- for _, codec = range m.Codecs {
- for _, remoteCodec = range remote.Codecs {
- if codec.Match(remoteCodec) {
- return
- }
- }
- }
- return nil, nil
- }
- func (m *Media) MatchCodec(remote *Codec) *Codec {
- for _, codec := range m.Codecs {
- if codec.Match(remote) {
- return codec
- }
- }
- return nil
- }
- func (m *Media) MatchAll() bool {
- for _, codec := range m.Codecs {
- if codec.Name == CodecAll {
- return true
- }
- }
- return false
- }
- func (m *Media) Equal(media *Media) bool {
- if media.ID != "" {
- return m.ID == media.ID
- }
- return m.String() == media.String()
- }
- func GetKind(name string) string {
- switch name {
- case CodecH264, CodecH265, CodecVP8, CodecVP9, CodecAV1, CodecJPEG, CodecRAW:
- return KindVideo
- case CodecPCMU, CodecPCMA, CodecAAC, CodecOpus, CodecG722, CodecMP3, CodecPCM, CodecPCML, CodecELD, CodecFLAC:
- return KindAudio
- }
- return ""
- }
- func MarshalSDP(name string, medias []*Media) ([]byte, error) {
- sd := &sdp.SessionDescription{
- Origin: sdp.Origin{
- Username: "-", SessionID: 1, SessionVersion: 1,
- NetworkType: "IN", AddressType: "IP4", UnicastAddress: "0.0.0.0",
- },
- SessionName: sdp.SessionName(name),
- ConnectionInformation: &sdp.ConnectionInformation{
- NetworkType: "IN", AddressType: "IP4", Address: &sdp.Address{
- Address: "0.0.0.0",
- },
- },
- TimeDescriptions: []sdp.TimeDescription{
- {Timing: sdp.Timing{}},
- },
- }
- for _, media := range medias {
- if media.Codecs == nil {
- continue
- }
- codec := media.Codecs[0]
- switch codec.Name {
- case CodecELD:
- name = CodecAAC
- case CodecPCML:
- name = CodecPCM // beacuse we using pcm.LittleToBig for RTSP server
- default:
- name = codec.Name
- }
- md := &sdp.MediaDescription{
- MediaName: sdp.MediaName{
- Media: media.Kind,
- Protos: []string{"RTP", "AVP"},
- },
- }
- md.WithCodec(codec.PayloadType, name, codec.ClockRate, codec.Channels, codec.FmtpLine)
- if media.ID != "" {
- md.WithValueAttribute("control", media.ID)
- }
- sd.MediaDescriptions = append(sd.MediaDescriptions, md)
- }
- return sd.Marshal()
- }
- func UnmarshalMedia(md *sdp.MediaDescription) *Media {
- m := &Media{
- Kind: md.MediaName.Media,
- }
- for _, attr := range md.Attributes {
- switch attr.Key {
- case DirectionSendonly, DirectionRecvonly, DirectionSendRecv:
- m.Direction = attr.Key
- case "control", "mid":
- m.ID = attr.Value
- }
- }
- for _, format := range md.MediaName.Formats {
- m.Codecs = append(m.Codecs, UnmarshalCodec(md, format))
- }
- return m
- }
- func ParseQuery(query map[string][]string) (medias []*Media) {
- // set media candidates from query list
- for key, values := range query {
- switch key {
- case KindVideo, KindAudio:
- for _, value := range values {
- media := &Media{Kind: key, Direction: DirectionSendonly}
- for _, name := range strings.Split(value, ",") {
- name = strings.ToUpper(name)
- // check aliases
- switch name {
- case "", "COPY":
- name = CodecAny
- case "MJPEG":
- name = CodecJPEG
- case "AAC":
- name = CodecAAC
- case "MP3":
- name = CodecMP3
- }
- media.Codecs = append(media.Codecs, &Codec{Name: name})
- }
- medias = append(medias, media)
- }
- }
- }
- return
- }
|