mirror of
				https://github.com/aler9/gortsplib
				synced 2025-11-01 02:52:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			165 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package description
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	psdp "github.com/pion/sdp/v3"
 | |
| 
 | |
| 	"github.com/bluenviron/gortsplib/v4/pkg/sdp"
 | |
| 	"github.com/bluenviron/gortsplib/v4/pkg/url"
 | |
| )
 | |
| 
 | |
| func atLeastOneHasMID(medias []*Media) bool {
 | |
| 	for _, media := range medias {
 | |
| 		if media.ID != "" {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func atLeastOneDoesntHaveMID(medias []*Media) bool {
 | |
| 	for _, media := range medias {
 | |
| 		if media.ID == "" {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func hasMediaWithID(medias []*Media, id string) bool {
 | |
| 	for _, media := range medias {
 | |
| 		if media.ID == id {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // SessionFECGroup is a FEC group.
 | |
| type SessionFECGroup []string
 | |
| 
 | |
| // Session is the description of a RTSP stream.
 | |
| type Session struct {
 | |
| 	// Base URL of the stream (read only).
 | |
| 	BaseURL *url.URL
 | |
| 
 | |
| 	// Title of the stream (optional).
 | |
| 	Title string
 | |
| 
 | |
| 	// FEC groups (RFC5109).
 | |
| 	FECGroups []SessionFECGroup
 | |
| 
 | |
| 	// Media streams.
 | |
| 	Medias []*Media
 | |
| }
 | |
| 
 | |
| // FindFormat finds a certain format among all the formats in all the medias of the stream.
 | |
| // If the format is found, it is inserted into forma, and its media is returned.
 | |
| func (d *Session) FindFormat(forma interface{}) *Media {
 | |
| 	for _, media := range d.Medias {
 | |
| 		ok := media.FindFormat(forma)
 | |
| 		if ok {
 | |
| 			return media
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Unmarshal decodes the description from SDP.
 | |
| func (d *Session) Unmarshal(ssd *sdp.SessionDescription) error {
 | |
| 	d.Title = string(ssd.SessionName)
 | |
| 	if d.Title == " " {
 | |
| 		d.Title = ""
 | |
| 	}
 | |
| 
 | |
| 	d.Medias = make([]*Media, len(ssd.MediaDescriptions))
 | |
| 
 | |
| 	for i, md := range ssd.MediaDescriptions {
 | |
| 		var m Media
 | |
| 		err := m.Unmarshal(md)
 | |
| 		if err != nil {
 | |
| 			return fmt.Errorf("media %d is invalid: %v", i+1, err)
 | |
| 		}
 | |
| 
 | |
| 		if m.ID != "" && hasMediaWithID(d.Medias[:i], m.ID) {
 | |
| 			return fmt.Errorf("duplicate media IDs")
 | |
| 		}
 | |
| 
 | |
| 		d.Medias[i] = &m
 | |
| 	}
 | |
| 
 | |
| 	if atLeastOneHasMID(d.Medias) && atLeastOneDoesntHaveMID(d.Medias) {
 | |
| 		return fmt.Errorf("media IDs sent partially")
 | |
| 	}
 | |
| 
 | |
| 	for _, attr := range ssd.Attributes {
 | |
| 		if attr.Key == "group" && strings.HasPrefix(attr.Value, "FEC ") {
 | |
| 			group := SessionFECGroup(strings.Split(attr.Value[len("FEC "):], " "))
 | |
| 
 | |
| 			for _, id := range group {
 | |
| 				if !hasMediaWithID(d.Medias, id) {
 | |
| 					return fmt.Errorf("FEC group points to an invalid media ID: %v", id)
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			d.FECGroups = append(d.FECGroups, group)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Marshal encodes the description in SDP.
 | |
| func (d Session) Marshal(multicast bool) ([]byte, error) {
 | |
| 	var sessionName psdp.SessionName
 | |
| 	if d.Title != "" {
 | |
| 		sessionName = psdp.SessionName(d.Title)
 | |
| 	} else {
 | |
| 		// RFC 4566: If a session has no meaningful name, the
 | |
| 		// value "s= " SHOULD be used (i.e., a single space as the session name).
 | |
| 		sessionName = psdp.SessionName(" ")
 | |
| 	}
 | |
| 
 | |
| 	var address string
 | |
| 	if multicast {
 | |
| 		address = "224.1.0.0"
 | |
| 	} else {
 | |
| 		address = "0.0.0.0"
 | |
| 	}
 | |
| 
 | |
| 	sout := &sdp.SessionDescription{
 | |
| 		SessionName: sessionName,
 | |
| 		Origin: psdp.Origin{
 | |
| 			Username:       "-",
 | |
| 			NetworkType:    "IN",
 | |
| 			AddressType:    "IP4",
 | |
| 			UnicastAddress: "127.0.0.1",
 | |
| 		},
 | |
| 		// required by Darwin Sessioning Server
 | |
| 		ConnectionInformation: &psdp.ConnectionInformation{
 | |
| 			NetworkType: "IN",
 | |
| 			AddressType: "IP4",
 | |
| 			Address:     &psdp.Address{Address: address},
 | |
| 		},
 | |
| 		TimeDescriptions: []psdp.TimeDescription{
 | |
| 			{Timing: psdp.Timing{StartTime: 0, StopTime: 0}},
 | |
| 		},
 | |
| 		MediaDescriptions: make([]*psdp.MediaDescription, len(d.Medias)),
 | |
| 	}
 | |
| 
 | |
| 	for i, media := range d.Medias {
 | |
| 		sout.MediaDescriptions[i] = media.Marshal()
 | |
| 	}
 | |
| 
 | |
| 	for _, group := range d.FECGroups {
 | |
| 		sout.Attributes = append(sout.Attributes, psdp.Attribute{
 | |
| 			Key:   "group",
 | |
| 			Value: "FEC " + strings.Join(group, " "),
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return sout.Marshal()
 | |
| }
 | 
