├── .gitignore ├── AUTHORS ├── rtspclient ├── .travis.yml ├── version.go ├── stream_state.go ├── README.md ├── dummy_sink.go └── callback.go ├── examples ├── test.264 ├── run.sh └── dor_media_player.go ├── livemedia ├── version.go ├── framed_filter.go ├── framed_file_source.go ├── file_server_media_subsession.go ├── utils.go ├── video_rtp_sink.go ├── basic_udp_sink.go ├── simple_rtp_sink.go ├── basic_udp_source.go ├── rtp_source.go ├── media_session_test.go ├── binary.go ├── m2ts_file_media_subsession.go ├── rtcp_from_spec.go ├── h264_file_media_subsession.go ├── mpeg_video_stream_parser.go ├── framed_source.go ├── common_test.go ├── byte_stream_file_source_linux_386.go ├── byte_stream_file_source_linux_amd64.go ├── byte_stream_file_source_darwin_amd64.go ├── server_media_subsession.go ├── server_media_session.go ├── bit_vector.go ├── m2ts_video_stream_framer.go ├── mpeg_video_stream_framer_darwin_amd64.go ├── mpeg_video_stream_framer_linux_386.go ├── mpeg_video_stream_framer_linux_amd64.go ├── stream_state.go ├── rtp_transmission_stats.go ├── rtp_sink.go ├── h264_video_rtp_source.go ├── stream_parser.go ├── h264_video_rtp_sink.go ├── on_demand_server_media_subsession.go ├── rtp_interface.go ├── media_sink.go ├── rtp_reception_stats_linux_amd64.go ├── rtp_reception_stats_darwin_amd64.go ├── rtp_reception_stats_linux_386.go ├── rtcp_linux_386.go ├── common.go ├── multi_framed_rtp_sink_linux_386.go ├── multi_framed_rtp_sink_linux_amd64.go └── multi_framed_rtp_sink_darwin_amd64.go ├── makefile ├── .travis.yml ├── rtspserver ├── stream_state.go └── server.go ├── make.sh ├── groupsock ├── inet_test.go ├── inet.go ├── helper.go └── groupsock.go ├── auth ├── db.go └── digest.go ├── main.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | dorsvr 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | yanfei 2 | -------------------------------------------------------------------------------- /rtspclient/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.6 4 | - tip 5 | 6 | -------------------------------------------------------------------------------- /examples/test.264: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djwackey/dorsvr/HEAD/examples/test.264 -------------------------------------------------------------------------------- /livemedia/version.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | const mediaServerVersion = "1.0.0.3" 4 | -------------------------------------------------------------------------------- /rtspclient/version.go: -------------------------------------------------------------------------------- 1 | package rtspclient 2 | 3 | const MEDIA_CLIENT_VERSION = "1.0.0.3" 4 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @./make.sh 3 | 4 | test: 5 | @./make.sh test 6 | 7 | fmt: 8 | @./make.sh fmt 9 | -------------------------------------------------------------------------------- /examples/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | go run dor_media_player.go rtsp://192.168.1.101:8554/test.264 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.6 5 | 6 | sudo: false 7 | 8 | script: 9 | - make 10 | -------------------------------------------------------------------------------- /rtspserver/stream_state.go: -------------------------------------------------------------------------------- 1 | package rtspserver 2 | 3 | import "github.com/djwackey/dorsvr/livemedia" 4 | 5 | type StreamServerState struct { 6 | subsession livemedia.IServerMediaSubsession 7 | streamToken *livemedia.StreamState 8 | } 9 | -------------------------------------------------------------------------------- /livemedia/framed_filter.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | type FramedFilter struct { 4 | FramedSource 5 | inputSource IFramedSource 6 | } 7 | 8 | func (f *FramedFilter) initFramedFilter(inputSource IFramedSource) { 9 | f.inputSource = inputSource 10 | } 11 | -------------------------------------------------------------------------------- /livemedia/framed_file_source.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import "os" 4 | 5 | type FramedFileSource struct { 6 | FramedSource 7 | fid *os.File 8 | } 9 | 10 | func (s *FramedFileSource) initFramedFileSource(source IFramedSource) { 11 | s.initFramedSource(source) 12 | } 13 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ ! -f make.sh ]; then 4 | echo 'make.sh must be run within its container folder' 1>&2 5 | exit 1 6 | fi 7 | 8 | if [ "$1" = "fmt" ]; then 9 | gofmt -w . 10 | fi 11 | 12 | if [ "$1" = "test" ]; then 13 | go test ./groupsock 14 | go test ./livemedia 15 | go test ./rtspclient 16 | go test ./rtspserver 17 | fi 18 | -------------------------------------------------------------------------------- /livemedia/file_server_media_subsession.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | type FileServerMediaSubsession struct { 4 | OnDemandServerMediaSubsession 5 | fileName string 6 | fileSize int64 7 | } 8 | 9 | func (s *FileServerMediaSubsession) initFileServerMediaSubsession(isubsession IServerMediaSubsession, fileName string) { 10 | s.fileName = fileName 11 | s.initOnDemandServerMediaSubsession(isubsession) 12 | } 13 | -------------------------------------------------------------------------------- /livemedia/utils.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | func ADVANCE(data []byte, size, n uint) ([]byte, uint) { 4 | data = data[n:] 5 | size -= n 6 | return data, size 7 | } 8 | 9 | func seqNumLT(s1, s2 int) bool { 10 | // a 'less-than' on 16-bit sequence numbers 11 | diff := s2 - s1 12 | if diff > 0 { 13 | return (diff < 0x8000) 14 | } else if diff < 0 { 15 | return (diff < -0x8000) 16 | } else { // diff == 0 17 | return false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rtspclient/stream_state.go: -------------------------------------------------------------------------------- 1 | package rtspclient 2 | 3 | import "github.com/djwackey/dorsvr/livemedia" 4 | 5 | type StreamClientState struct { 6 | Session *livemedia.MediaSession 7 | Subsession *livemedia.MediaSubsession 8 | } 9 | 10 | func newStreamClientState() *StreamClientState { 11 | return new(StreamClientState) 12 | } 13 | 14 | func (s *StreamClientState) Next() *livemedia.MediaSubsession { 15 | return s.Session.Subsession() 16 | } 17 | -------------------------------------------------------------------------------- /rtspclient/README.md: -------------------------------------------------------------------------------- 1 | # rtspclient 2 | It 's a RTSP client implemented by golang 3 | 4 | ## Install 5 | ```bash 6 | go get github.com/djwackey/dorsvr/rtspclient 7 | ``` 8 | ## Examples 9 | ```go 10 | // define a rtsp url 11 | rtsp_url := "rtsp://192.168.1.105:8554/demo.264" 12 | 13 | // create a rtspclient instance 14 | client := rtspclient.New() 15 | 16 | // connect rtsp server and send request 17 | client.DialRTSP(rtsp_url) 18 | 19 | // waiting for the response, and print the output frame data 20 | client.Waiting() 21 | 22 | ``` 23 | -------------------------------------------------------------------------------- /examples/dor_media_player.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/djwackey/dorsvr/rtspclient" 9 | ) 10 | 11 | func main() { 12 | flag.Parse() 13 | if flag.NArg() < 1 { 14 | fmt.Println("Please input rtsp url.") 15 | return 16 | } 17 | 18 | rtsp_url := os.Args[1] 19 | 20 | client := rtspclient.New() 21 | 22 | // to connect rtsp server 23 | if !client.DialRTSP(rtsp_url) { 24 | return 25 | } 26 | 27 | // send the options/describe request 28 | client.SendRequest() 29 | 30 | select {} 31 | } 32 | -------------------------------------------------------------------------------- /livemedia/video_rtp_sink.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import gs "github.com/djwackey/dorsvr/groupsock" 4 | 5 | type VideoRTPSink struct { 6 | MultiFramedRTPSink 7 | } 8 | 9 | func (s *VideoRTPSink) initVideoRTPSink(rtpSink IMediaSink, rtpGroupSock *gs.GroupSock, 10 | rtpPayloadType, rtpTimestampFrequency uint32, rtpPayloadFormatName string) { 11 | s.InitMultiFramedRTPSink(rtpSink, rtpGroupSock, rtpPayloadType, 12 | rtpTimestampFrequency, rtpPayloadFormatName) 13 | } 14 | 15 | func (s *VideoRTPSink) sdpMediaType() string { 16 | return "video" 17 | } 18 | -------------------------------------------------------------------------------- /livemedia/basic_udp_sink.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | sys "syscall" 5 | 6 | gs "github.com/djwackey/dorsvr/groupsock" 7 | ) 8 | 9 | const maxPayloadSize = 1450 10 | 11 | type BasicUDPSink struct { 12 | MediaSink 13 | gs *gs.GroupSock 14 | maxPayloadSize uint 15 | outputBuffer []byte 16 | nextSendTime sys.Timeval 17 | } 18 | 19 | func NewBasicUDPSink(gs *gs.GroupSock) *BasicUDPSink { 20 | return &BasicUDPSink{ 21 | gs: gs, 22 | maxPayloadSize: maxPayloadSize, 23 | outputBuffer: make([]byte, maxPayloadSize), 24 | } 25 | } 26 | 27 | func (s *BasicUDPSink) ContinuePlaying() { 28 | } 29 | -------------------------------------------------------------------------------- /groupsock/inet_test.go: -------------------------------------------------------------------------------- 1 | package groupsock 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestOurRandom(t *testing.T) { 9 | sessionID := fmt.Sprintf("%010d", OurRandom()) 10 | if len(sessionID) == 10 { 11 | t.Log("success") 12 | } else { 13 | t.Error("failed") 14 | } 15 | } 16 | 17 | func TestOurRandom32(t *testing.T) { 18 | sessionID := fmt.Sprintf("%010d", OurRandom32()) 19 | if len(sessionID) == 10 { 20 | t.Log("success") 21 | } else { 22 | t.Error("failed") 23 | } 24 | } 25 | 26 | func TestOurIPAddress(t *testing.T) { 27 | ip, _ := OurIPAddress() 28 | if ip != "" { 29 | println(ip) 30 | t.Log("success") 31 | } else { 32 | t.Error("failed") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /livemedia/simple_rtp_sink.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import gs "github.com/djwackey/dorsvr/groupsock" 4 | 5 | type SimpleRTPSink struct { 6 | MultiFramedRTPSink 7 | allowMultipleFramesPerPacket bool 8 | } 9 | 10 | func newSimpleRTPSink(rtpGS *gs.GroupSock, rtpPayloadFormat, 11 | rtpTimestampFrequency, numChannels uint32, 12 | sdpMediaTypeString, rtpPayloadFormatName string, 13 | allowMultipleFramesPerPacket, doNormalMBitRule bool) *SimpleRTPSink { 14 | sink := new(SimpleRTPSink) 15 | sink.InitMultiFramedRTPSink(sink, rtpGS, rtpPayloadFormat, rtpTimestampFrequency, rtpPayloadFormatName) 16 | sink.allowMultipleFramesPerPacket = allowMultipleFramesPerPacket 17 | return sink 18 | } 19 | 20 | func (s *SimpleRTPSink) AuxSDPLine() string { 21 | return "" 22 | } 23 | 24 | func (s *SimpleRTPSink) ContinuePlaying() { 25 | } 26 | -------------------------------------------------------------------------------- /livemedia/basic_udp_source.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | 6 | gs "github.com/djwackey/dorsvr/groupsock" 7 | ) 8 | 9 | type BasicUDPSource struct { 10 | FramedSource 11 | inputSocket *gs.GroupSock 12 | haveStartedReading bool 13 | } 14 | 15 | func newBasicUDPSource(inputSocket *gs.GroupSock) *BasicUDPSource { 16 | source := new(BasicUDPSource) 17 | source.inputSocket = inputSocket 18 | source.initFramedSource(source) 19 | return source 20 | } 21 | 22 | func (s *BasicUDPSource) doGetNextFrame() error { 23 | go s.incomingPacketHandler() 24 | return nil 25 | } 26 | 27 | func (s *BasicUDPSource) doStopGettingFrames() error { 28 | s.haveStartedReading = false 29 | return nil 30 | } 31 | 32 | func (s *BasicUDPSource) incomingPacketHandler() { 33 | for { 34 | numBytes, err := s.inputSocket.HandleRead(s.buffTo) 35 | if err != nil { 36 | fmt.Println("Failed to read from input socket.", err.Error()) 37 | break 38 | } 39 | 40 | s.frameSize = uint(numBytes) 41 | 42 | s.afterGetting() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /livemedia/rtp_source.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import gs "github.com/djwackey/dorsvr/groupsock" 4 | 5 | type RTPSource struct { 6 | FramedSource 7 | ssrc uint32 8 | lastReceivedSSRC uint32 9 | rtpPayloadFormat uint32 10 | timestampFrequency uint32 11 | curPacketRTPSeqNum uint32 12 | curPacketRTPTimestamp uint32 13 | curPacketSyncUsingRTCP bool 14 | curPacketMarkerBit bool 15 | receptionStatsDB *RTPReceptionStatsDB 16 | rtpInterface *RTPInterface 17 | } 18 | 19 | func newRTPSource() *RTPSource { 20 | return &RTPSource{} 21 | } 22 | 23 | func (s *RTPSource) initRTPSouce(isource IFramedSource, RTPgs *gs.GroupSock, 24 | rtpPayloadFormat, rtpTimestampFrequency uint32) { 25 | s.rtpInterface = newRTPInterface(s, RTPgs) 26 | s.lastReceivedSSRC = 0 27 | s.rtpPayloadFormat = rtpPayloadFormat 28 | s.timestampFrequency = rtpTimestampFrequency 29 | s.ssrc = gs.OurRandom32() 30 | s.curPacketSyncUsingRTCP = false 31 | s.receptionStatsDB = newRTPReceptionStatsDB() 32 | s.initFramedSource(isource) 33 | } 34 | 35 | func (s *RTPSource) SetStreamSocket() { 36 | } 37 | -------------------------------------------------------------------------------- /auth/db.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | // Database stores username and password to implement access control 4 | type Database struct { 5 | Realm string 6 | username string 7 | password string 8 | records map[string]string 9 | } 10 | 11 | // NewAuthDatabase returns a pointer to a new instance of authorization database 12 | func NewAuthDatabase(realm string) *Database { 13 | if realm == "" { 14 | realm = "dorsvr streaming server" 15 | } 16 | return &Database{ 17 | Realm: realm, 18 | } 19 | } 20 | 21 | // InsertUserRecord inserts user record, it contains username and password fields 22 | func (d *Database) InsertUserRecord(username, password string) { 23 | if username == "" || password == "" { 24 | return 25 | } 26 | 27 | _, existed := d.records[username] 28 | if !existed { 29 | d.records[username] = password 30 | } 31 | } 32 | 33 | // RemoveUserRecord removes user record 34 | func (d *Database) RemoveUserRecord(username string) { 35 | _, existed := d.records[username] 36 | if existed { 37 | delete(d.records, username) 38 | } 39 | } 40 | 41 | // LookupPassword lookups the password by username 42 | func (d *Database) LookupPassword(username string) (password string) { 43 | password, _ = d.records[username] 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /rtspclient/dummy_sink.go: -------------------------------------------------------------------------------- 1 | package rtspclient 2 | 3 | import ( 4 | "fmt" 5 | sys "syscall" 6 | 7 | "github.com/djwackey/dorsvr/livemedia" 8 | ) 9 | 10 | type DummySink struct { 11 | livemedia.MediaSink 12 | streamID string 13 | receiveBuffer []byte 14 | subsession *livemedia.MediaSubsession 15 | } 16 | 17 | // Implementation of "DummySink": 18 | 19 | var dummySinkReceiveBufferSize uint = 100000 20 | 21 | func NewDummySink(subsession *livemedia.MediaSubsession, streamID string) *DummySink { 22 | sink := new(DummySink) 23 | sink.streamID = streamID 24 | sink.subsession = subsession 25 | sink.receiveBuffer = make([]byte, dummySinkReceiveBufferSize) 26 | sink.InitMediaSink(sink) 27 | return sink 28 | } 29 | 30 | func (s *DummySink) AfterGettingFrame(frameSize, durationInMicroseconds uint, 31 | presentationTime sys.Timeval) { 32 | //return 33 | fmt.Printf("Stream \"%s\"; %s/%s:\tReceived %d bytes.\tPresentation Time: %f\n", 34 | s.streamID, s.subsession.MediumName(), s.subsession.CodecName(), frameSize, 35 | float32(presentationTime.Sec/1000/1000+int64(presentationTime.Usec))) 36 | } 37 | 38 | func (s *DummySink) ContinuePlaying() { 39 | s.Source.GetNextFrame(s.receiveBuffer, dummySinkReceiveBufferSize, 40 | s.AfterGettingFrame, s.OnSourceClosure) 41 | } 42 | -------------------------------------------------------------------------------- /livemedia/media_session_test.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var sdpDesc = "v=0\r\n" + 9 | "o=- 1464450493310666 1 IN IP4 192.168.1.105\r\n" + 10 | "s=H.264 Video, streamed by the Dor Media Server\r\n" + 11 | "i=test.264\r\n" + 12 | "t=0 0\r\n" + 13 | "a=tool:Dor Streaming Media v2012.10.01\r\n" + 14 | "a=type:broadcast\r\n" + 15 | "a=control:*\r\n" + 16 | "a=range:npt=0-\r\n" + 17 | "a=x-qt-text-nam:H.264 Video, streamed by the Dor Media Server\r\n" + 18 | "a=x-qt-text-inf:test.264\r\n" + 19 | "m=video 0 RTP/AVP 96\r\n" + 20 | "c=IN IP4 0.0.0.0\r\n" + 21 | "b=AS:500\r\n" + 22 | "a=rtpmap:96 H264/90000\r\n" + 23 | "a=control:track1\r\n\r\n" 24 | 25 | func TestInitWithSDP(t *testing.T) { 26 | session := NewMediaSession(sdpDesc) 27 | if session != nil { 28 | t.Log("success") 29 | } else { 30 | t.Error("failed") 31 | } 32 | } 33 | 34 | func TestConnectionEndpointName(t *testing.T) { 35 | session := NewMediaSession(sdpDesc) 36 | if session == nil { 37 | fmt.Println("Unable to create new MediaSession") 38 | t.Error("failed") 39 | } 40 | 41 | subsession := NewMediaSubsession(session) 42 | if subsession == nil { 43 | fmt.Println("Unable to create new MediaSubsession") 44 | t.Error("failed") 45 | } 46 | 47 | //endPointName := subsession.ConnectionEndpointName() 48 | //fmt.Println("Connection Endpoint Name:", endPointName) 49 | t.Log("success") 50 | } 51 | -------------------------------------------------------------------------------- /groupsock/inet.go: -------------------------------------------------------------------------------- 1 | package groupsock 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "math/rand" 9 | "net" 10 | "time" 11 | ) 12 | 13 | func OurRandom() int32 { 14 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 15 | return r.Int31() 16 | } 17 | 18 | func OurRandom32() uint32 { 19 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 20 | random16_1 := r.Int31() & 0x00FFFF00 21 | random16_2 := r.Int31() & 0x00FFFF00 22 | return uint32((random16_1 << 8) | (random16_2 >> 8)) 23 | } 24 | 25 | func OurRandom16() uint32 { 26 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 27 | return uint32(r.Int31() >> 16) 28 | } 29 | 30 | func Ntohl(packet []byte) (uint32, error) { 31 | var value uint32 32 | buffer := bytes.NewReader(packet) 33 | 34 | err := binary.Read(buffer, binary.BigEndian, &value) 35 | if err != nil { 36 | fmt.Println("failed to read binary.", err.Error()) 37 | return value, err 38 | } 39 | 40 | return value, nil 41 | } 42 | 43 | func OurIPAddress() (string, error) { 44 | addrs, err := net.InterfaceAddrs() 45 | if err != nil { 46 | fmt.Printf("Failed to get InterfaceAddrs.%s\n", err.Error()) 47 | return "", err 48 | } 49 | 50 | var ip string 51 | err = errors.New("ip address not found") 52 | for _, address := range addrs { 53 | if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 54 | if ipnet.IP.To4() != nil { 55 | ip, err = ipnet.IP.String(), nil 56 | //break 57 | } 58 | } 59 | } 60 | return ip, err 61 | } 62 | -------------------------------------------------------------------------------- /livemedia/binary.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | var ( 9 | DefaultEndianness = binary.BigEndian 10 | ) 11 | 12 | func Uint16(b []byte) uint16 { 13 | return DefaultEndianness.Uint16(pad(b, 2)) 14 | } 15 | 16 | func Uint32(b []byte) uint32 { 17 | return DefaultEndianness.Uint32(pad(b, 4)) 18 | } 19 | 20 | func LittleEndianUint32(b []byte) uint32 { 21 | return binary.LittleEndian.Uint32(padl(b, 4)) 22 | } 23 | 24 | func Uint64(b []byte) uint64 { 25 | return DefaultEndianness.Uint64(pad(b, 8)) 26 | } 27 | 28 | func PutUint8(n byte, w io.Writer) (int, error) { 29 | return w.Write([]byte{n}) 30 | } 31 | 32 | func PutUint16(n uint16, w io.Writer) (int, error) { 33 | buffer := make([]byte, 2) 34 | DefaultEndianness.PutUint16(buffer, n) 35 | 36 | return w.Write(buffer) 37 | } 38 | 39 | func PutUint24(n uint32, w io.Writer) (int, error) { 40 | buf := []byte{ 41 | byte((n >> 16) & 0xff), 42 | byte((n >> 8) & 0xff), 43 | byte((n >> 0) & 0xff), 44 | } 45 | 46 | return w.Write(buf) 47 | } 48 | 49 | func PutUint32(n uint32, w io.Writer) (int, error) { 50 | buf := make([]byte, 4) 51 | DefaultEndianness.PutUint32(buf, n) 52 | 53 | return w.Write(buf) 54 | } 55 | 56 | func LittleEndianPutUint32(n uint32, w io.Writer) (int, error) { 57 | buf := make([]byte, 4) 58 | binary.LittleEndian.PutUint32(buf, n) 59 | 60 | return w.Write(buf) 61 | } 62 | 63 | func pad(b []byte, n int) []byte { 64 | return append(make([]byte, n-len(b)), b...) 65 | } 66 | 67 | func padl(b []byte, n int) []byte { 68 | return append(b, make([]byte, n-len(b))...) 69 | } 70 | -------------------------------------------------------------------------------- /groupsock/helper.go: -------------------------------------------------------------------------------- 1 | package groupsock 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | // SetupDatagramSocket returns a udp connection of Listening to the specified port. 9 | func SetupDatagramSocket(address string, port uint) *net.UDPConn { 10 | addr := fmt.Sprintf("%s:%d", address, port) 11 | udpAddr, err := net.ResolveUDPAddr("udp", addr) 12 | if err != nil { 13 | fmt.Println("Failed to resolve UDP address.", err) 14 | return nil 15 | } 16 | 17 | udpConn, err := net.ListenUDP("udp", udpAddr) 18 | if err != nil { 19 | fmt.Println("Failed to listen UDP address.", err) 20 | return nil 21 | } 22 | 23 | //go func() { 24 | // data := make([]byte, 32) 25 | // for { 26 | // n, remoteAddr, err := socketNum.ReadFromUDP(data) 27 | // if err != nil { 28 | // fmt.Printf("error during read: %v\n", err) 29 | // break 30 | // } 31 | 32 | // fmt.Printf("<%s> %s\n", remoteAddr, data[:n]) 33 | // } 34 | //}() 35 | 36 | return udpConn 37 | } 38 | 39 | func setupStreamSocket(address string, port uint) *net.TCPConn { 40 | addr := fmt.Sprintf("%s:%d", address, port) 41 | tcpAddr, err := net.ResolveTCPAddr("tcp", addr) 42 | if err != nil { 43 | fmt.Println("Failed to resolve TCP address.", err) 44 | return nil 45 | } 46 | 47 | tcpConn, err := net.DialTCP("tcp", nil, tcpAddr) 48 | if err != nil { 49 | return nil 50 | } 51 | return tcpConn 52 | } 53 | 54 | // ReadSocket reads data from the connection. 55 | func ReadSocket(conn net.Conn, buffer []byte) (int, error) { 56 | return conn.Read(buffer) 57 | } 58 | 59 | func writeSocket(conn net.Conn, buffer []byte) (int, error) { 60 | return conn.Write(buffer) 61 | } 62 | -------------------------------------------------------------------------------- /livemedia/m2ts_file_media_subsession.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import gs "github.com/djwackey/dorsvr/groupsock" 4 | 5 | var TRANSPORT_PACKET_SIZE uint = 188 6 | var TRANSPORT_PACKETS_PER_NETWORK_PACKET uint = 7 7 | 8 | type M2TSFileMediaSubsession struct { 9 | FileServerMediaSubsession 10 | duration float32 11 | } 12 | 13 | func NewM2TSFileMediaSubsession(fileName string) *M2TSFileMediaSubsession { 14 | subsession := new(M2TSFileMediaSubsession) 15 | subsession.initFileServerMediaSubsession(subsession, fileName) 16 | return subsession 17 | } 18 | 19 | func (s *M2TSFileMediaSubsession) createNewStreamSource() IFramedSource { 20 | //inputDataChunkSize := TRANSPORT_PACKETS_PER_NETWORK_PACKET * TRANSPORT_PACKET_SIZE 21 | 22 | // Create the video source: 23 | fileSource := newByteStreamFileSource(s.fileName) 24 | if fileSource == nil { 25 | return nil 26 | } 27 | s.fileSize = fileSource.FileSize() 28 | 29 | // Use the file size and the duration to estimate the stream's bitrate: 30 | //var estBitrate float32 = 5000 // kbps, estimate 31 | //if this.fileSize > 0 && this.duration > 0.0 { 32 | // estBitrate = float32(this.fileSize) / (125 * this.duration) + 0.5 // kbps, rounded 33 | //} 34 | 35 | // Create a framer for the Transport Stream: 36 | framer := NewM2TSVideoStreamFramer(fileSource) 37 | return framer 38 | } 39 | 40 | func (s *M2TSFileMediaSubsession) createNewRTPSink(rtpGroupSock *gs.GroupSock, rtpPayloadType uint) IMediaSink { 41 | return newSimpleRTPSink(rtpGroupSock, 33, 90000, 1, "video", "MP2T", true, false) 42 | } 43 | 44 | func (s *M2TSFileMediaSubsession) Duration() float32 { 45 | return s.duration 46 | } 47 | -------------------------------------------------------------------------------- /livemedia/rtcp_from_spec.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import gs "github.com/djwackey/dorsvr/groupsock" 4 | 5 | const ( 6 | eventUnknown = 0 7 | eventReport = 1 8 | eventBye = 2 9 | ) 10 | 11 | func drand48() int32 { 12 | return gs.OurRandom() 13 | } 14 | 15 | func rtcpInterval(members, senders, weSent, rtcpBW, avgRtcpSize float64) float64 { 16 | RTCP_MIN_TIME := 5. 17 | RTCP_SENDER_BW_FRACTION := 0.25 18 | RTCP_RCVR_BW_FRACTION := (1 - RTCP_SENDER_BW_FRACTION) 19 | COMPENSATION := 2.71828 - 1.5 20 | 21 | rtcpMinTime := RTCP_MIN_TIME 22 | 23 | n := members 24 | if senders > 0 && senders < members*RTCP_SENDER_BW_FRACTION { 25 | if weSent != 0 { 26 | rtcpBW *= RTCP_SENDER_BW_FRACTION 27 | n = senders 28 | } else { 29 | rtcpBW *= RTCP_RCVR_BW_FRACTION 30 | n -= senders 31 | } 32 | } 33 | 34 | t := avgRtcpSize * n / rtcpBW 35 | if t < rtcpMinTime { 36 | t = rtcpMinTime 37 | } 38 | 39 | t = t * (float64(drand48()) + 0.5) 40 | t = t / COMPENSATION 41 | return t 42 | } 43 | 44 | func OnExpire(instance *RTCPInstance, members, senders, weSent, rtcpBW, avgRTCPSize, tc, tp float64) { 45 | if instance == nil { 46 | return 47 | } 48 | if instance.typeOfEvent == eventBye { 49 | } else if instance.typeOfEvent == eventReport { 50 | t := rtcpInterval(members, senders, weSent, rtcpBW, avgRTCPSize) 51 | tn := tp + t 52 | 53 | if tn <= tc { 54 | instance.sendReport() 55 | avgRTCPSize = (1./16.)*float64(instance.sentPacketSize()) + (15./16.)*avgRTCPSize 56 | 57 | t := rtcpInterval(members, senders, weSent, rtcpBW, avgRTCPSize) 58 | instance.schedule(int64(t + tc)) 59 | } else { 60 | instance.schedule(int64(tn)) 61 | } 62 | } 63 | } 64 | 65 | func OnReceive() { 66 | } 67 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | //"github.com/djwackey/dorsvr/auth" 7 | "github.com/djwackey/dorsvr/rtspserver" 8 | "github.com/djwackey/gitea/log" 9 | ) 10 | 11 | func main() { 12 | // open a logger writer of console or file mode. 13 | mode := "console" 14 | config := `{"level":1,"filename":"test.log"}` 15 | log.NewLogger(0, mode, config) 16 | 17 | // to implement client access control to the RTSP server, do the following: 18 | // var realm string 19 | // authdb = auth.NewAuthDatabase(realm) 20 | // authdb.InsertUserRecord("username1", "password1") 21 | // repeat the above with each , that you wish to allow 22 | // access to the server. 23 | 24 | // create a rtsp server 25 | server := rtspserver.New(nil) 26 | 27 | portNum := 8554 28 | err := server.Listen(portNum) 29 | if err != nil { 30 | fmt.Printf("Failed to bind port: %d\n", portNum) 31 | return 32 | } 33 | 34 | // also, attempt to create a HTTP server for RTSP-over-HTTP tunneling. 35 | // Try first with the default HTTP port (80), and then with the alternative HTTP 36 | // port numbers (8000 and 8080). 37 | if !server.SetupTunnelingOverHTTP(80) || 38 | !server.SetupTunnelingOverHTTP(8000) || 39 | !server.SetupTunnelingOverHTTP(8080) { 40 | fmt.Printf("We use port %d for optional RTSP-over-HTTP tunneling, "+ 41 | "or for HTTP live streaming (for indexed Transport Stream files only).\n", 42 | server.HTTPServerPortNum()) 43 | } else { 44 | fmt.Println("(RTSP-over-HTTP tunneling is not available.)") 45 | } 46 | 47 | urlPrefix := server.RtspURLPrefix() 48 | fmt.Println("This server's URL: " + urlPrefix + ".") 49 | 50 | server.Start() 51 | 52 | // do event loop 53 | select {} 54 | } 55 | -------------------------------------------------------------------------------- /livemedia/h264_file_media_subsession.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "time" 5 | 6 | gs "github.com/djwackey/dorsvr/groupsock" 7 | //"github.com/djwackey/dorsvr/log" 8 | ) 9 | 10 | type H264FileMediaSubsession struct { 11 | FileServerMediaSubsession 12 | dummyRTPSink IMediaSink 13 | auxSDPLine string 14 | } 15 | 16 | func NewH264FileMediaSubsession(fileName string) *H264FileMediaSubsession { 17 | subsession := new(H264FileMediaSubsession) 18 | subsession.initFileServerMediaSubsession(subsession, fileName) 19 | return subsession 20 | } 21 | 22 | func (s *H264FileMediaSubsession) createNewStreamSource() IFramedSource { 23 | //estBitrate = 500 // kbps, estimate 24 | 25 | // Create the video source: 26 | fileSource := newByteStreamFileSource(s.fileName) 27 | if fileSource == nil { 28 | return nil 29 | } 30 | s.fileSize = fileSource.FileSize() 31 | 32 | // Create a framer for the Video Elementary Stream: 33 | return newH264VideoStreamFramer(fileSource) 34 | } 35 | 36 | func (s *H264FileMediaSubsession) createNewRTPSink(rtpGroupSock *gs.GroupSock, rtpPayloadType uint) IMediaSink { 37 | return newH264VideoRTPSink(rtpGroupSock, uint32(rtpPayloadType)) 38 | } 39 | 40 | func (s *H264FileMediaSubsession) getAuxSDPLine(rtpSink IMediaSink, inputSource IFramedSource) string { 41 | if s.auxSDPLine != "" { 42 | return s.auxSDPLine 43 | } 44 | 45 | if s.dummyRTPSink == nil { 46 | s.dummyRTPSink = rtpSink 47 | 48 | // start reading the file 49 | go s.dummyRTPSink.StartPlaying(inputSource, s.afterPlayingDummy) 50 | 51 | s.checkForAuxSDPLine() 52 | } 53 | return s.auxSDPLine 54 | } 55 | 56 | func (s *H264FileMediaSubsession) checkForAuxSDPLine() { 57 | var auxSDPLine string 58 | for s.auxSDPLine == "" { 59 | if s.dummyRTPSink == nil { 60 | break 61 | } 62 | 63 | auxSDPLine = s.dummyRTPSink.AuxSDPLine() 64 | if auxSDPLine != "" { 65 | s.auxSDPLine = auxSDPLine 66 | break 67 | } 68 | 69 | // delay 100ms 70 | uSecsToDelay := 100000 71 | time.Sleep(time.Duration(uSecsToDelay) * time.Microsecond) 72 | } 73 | } 74 | 75 | func (s *H264FileMediaSubsession) afterPlayingDummy() { 76 | } 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dorsvr Streaming Server 2 | ======================= 3 | 4 | [![Build Status](https://travis-ci.org/djwackey/dorsvr.svg?branch=master)](https://travis-ci.org/djwackey/dorsvr) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/djwackey/dorsvr)](https://goreportcard.com/report/github.com/djwackey/dorsvr) 6 | [![GitHub issues](https://img.shields.io/github/issues/djwackey/dorsvr.svg)](https://github.com/djwackey/dorsvr/issues) 7 | ## Modules 8 | * rtspserver - rtsp server 9 | * rtspclient - rtsp client 10 | * groupsock - group socket 11 | * livemedia - media library 12 | 13 | ## Feature 14 | * Streaming Video (H264, M2TS) 15 | * Streaming Audio (MP3) 16 | * Protocols: RTP, RTCP, RTSP 17 | * Access Control 18 | 19 | ## Install 20 | go get github.com/djwackey/dorsvr 21 | 22 | ## Format 23 | $ make fmt 24 | 25 | ## Testing 26 | $ make test 27 | 28 | ## Example 29 | ```golang 30 | import ( 31 | "fmt" 32 | 33 | "github.com/djwackey/dorsvr/rtspserver" 34 | ) 35 | 36 | func main() { 37 | server := rtspserver.New(nil) 38 | 39 | portNum := 8554 40 | err := server.Listen(portNum) 41 | if err != nil { 42 | fmt.Printf("Failed to bind port: %d\n", portNum) 43 | return 44 | } 45 | 46 | if !server.SetupTunnelingOverHTTP(80) || 47 | !server.SetupTunnelingOverHTTP(8000) || 48 | !server.SetupTunnelingOverHTTP(8080) { 49 | fmt.Printf("We use port %d for optional RTSP-over-HTTP tunneling, "+ 50 | "or for HTTP live streaming (for indexed Transport Stream files only).\n", server.HTTPServerPortNum()) 51 | } else { 52 | fmt.Println("(RTSP-over-HTTP tunneling is not available.)") 53 | } 54 | 55 | urlPrefix := server.RtspURLPrefix() 56 | fmt.Println("This server's URL: " + urlPrefix + ".") 57 | 58 | server.Start() 59 | 60 | select {} 61 | } 62 | ``` 63 | ## Author 64 | djwackey, worcy_kiddy@126.com 65 | 66 | ## LICENSE 67 | dorsvr is licensed under the GNU Lesser General Public License, Version 2.1. See [LICENSE](https://github.com/djwackey/dorsvr/blob/master/LICENSE) for the full license text. 68 | -------------------------------------------------------------------------------- /livemedia/mpeg_video_stream_parser.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | type MPEGVideoStreamParser struct { 4 | StreamParser 5 | buffTo []byte 6 | saveTo []byte 7 | startOfFrame []byte 8 | numLimitBytes uint 9 | numSavedBytes uint 10 | numTruncatedBytes uint 11 | savedCurFrameSize uint 12 | savedNumTruncatedBytes uint 13 | usingSource IFramedSource 14 | } 15 | 16 | func (p *MPEGVideoStreamParser) initMPEGVideoStreamParser(usingSource, inputSource IFramedSource, 17 | clientOnInputCloseFunc, clientContinueFunc interface{}) { 18 | p.usingSource = usingSource 19 | 20 | p.initStreamParser(inputSource, clientOnInputCloseFunc, clientContinueFunc, p.restoreSavedParserState) 21 | } 22 | 23 | func (p *MPEGVideoStreamParser) restoreSavedParserState() { 24 | p.restore() 25 | p.buffTo = p.saveTo 26 | p.numSavedBytes = p.savedCurFrameSize 27 | p.numTruncatedBytes = p.savedNumTruncatedBytes 28 | } 29 | 30 | func (p *MPEGVideoStreamParser) registerReadInterest(buffTo []byte, maxSize uint) { 31 | p.buffTo = buffTo 32 | p.saveTo = buffTo 33 | p.numLimitBytes = maxSize 34 | p.numSavedBytes = 0 35 | p.numTruncatedBytes = 0 36 | p.savedCurFrameSize = 0 37 | p.savedNumTruncatedBytes = 0 38 | p.startOfFrame = p.buffTo 39 | } 40 | 41 | func (p *MPEGVideoStreamParser) saveByte(b uint) { 42 | if p.numSavedBytes >= p.numLimitBytes { 43 | p.numTruncatedBytes += 1 44 | return 45 | } 46 | p.buffTo[p.numSavedBytes:][0] = byte(b) 47 | p.numSavedBytes += 1 48 | } 49 | 50 | func (p *MPEGVideoStreamParser) save4Bytes(word uint) { 51 | if p.numSavedBytes+4 > p.numLimitBytes { 52 | p.numTruncatedBytes += 4 53 | return 54 | } 55 | 56 | p.buffTo[p.numSavedBytes:][0] = byte(word >> 24) 57 | p.buffTo[p.numSavedBytes:][1] = byte(word >> 16) 58 | p.buffTo[p.numSavedBytes:][2] = byte(word >> 8) 59 | p.buffTo[p.numSavedBytes:][3] = byte(word) 60 | p.numSavedBytes += 4 61 | } 62 | 63 | func (p *MPEGVideoStreamParser) curFrameSize() uint { 64 | return p.numSavedBytes 65 | } 66 | 67 | func (p *MPEGVideoStreamParser) setParseState() { 68 | p.saveTo = p.buffTo 69 | p.savedCurFrameSize = p.numSavedBytes 70 | p.savedNumTruncatedBytes = p.numTruncatedBytes 71 | p.saveParserState() 72 | } 73 | -------------------------------------------------------------------------------- /livemedia/framed_source.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import sys "syscall" 4 | 5 | type IFramedSource interface { 6 | GetNextFrame(buffTo []byte, maxSize uint, afterGettingFunc, onCloseFunc interface{}) error 7 | doGetNextFrame() error 8 | isAwaitingData() bool 9 | maxFrameSize() uint 10 | afterGetting() 11 | handleClosure() 12 | stopGettingFrames() 13 | doStopGettingFrames() error 14 | destroy() 15 | } 16 | 17 | type FramedSource struct { 18 | afterGettingFunc interface{} 19 | onCloseFunc interface{} 20 | source IFramedSource 21 | buffTo []byte 22 | maxSize uint 23 | frameSize uint 24 | numTruncatedBytes uint 25 | durationInMicroseconds uint 26 | isCurrentlyAwaitingData bool 27 | presentationTime sys.Timeval 28 | } 29 | 30 | func (f *FramedSource) initFramedSource(source IFramedSource) { 31 | f.source = source 32 | } 33 | 34 | func (f *FramedSource) GetNextFrame(buffTo []byte, maxSize uint, 35 | afterGettingFunc, onCloseFunc interface{}) error { 36 | if f.isCurrentlyAwaitingData { 37 | panic("FramedSource::GetNextFrame(): attempting to read more than once at the same time!") 38 | } 39 | 40 | f.buffTo = buffTo 41 | f.maxSize = maxSize 42 | f.onCloseFunc = onCloseFunc 43 | f.afterGettingFunc = afterGettingFunc 44 | f.isCurrentlyAwaitingData = true 45 | 46 | return f.source.doGetNextFrame() 47 | } 48 | 49 | func (f *FramedSource) afterGetting() { 50 | f.isCurrentlyAwaitingData = false 51 | 52 | if f.afterGettingFunc != nil { 53 | f.afterGettingFunc.(func(frameSize, durationInMicroseconds uint, 54 | presentationTime sys.Timeval))(f.frameSize, 55 | f.durationInMicroseconds, f.presentationTime) 56 | } 57 | } 58 | 59 | func (f *FramedSource) handleClosure() { 60 | f.stopGettingFrames() 61 | //f.isCurrentlyAwaitingData = false 62 | 63 | if f.onCloseFunc != nil { 64 | f.onCloseFunc.(func())() 65 | } 66 | } 67 | 68 | func (f *FramedSource) stopGettingFrames() { 69 | f.isCurrentlyAwaitingData = false 70 | 71 | // perform any specialized action 72 | f.source.doStopGettingFrames() 73 | } 74 | 75 | // default implementation: do nothing 76 | func (f *FramedSource) doStopGettingFrames() error { 77 | return nil 78 | } 79 | 80 | // default implementation: do nothing 81 | func (f *FramedSource) maxFrameSize() uint { 82 | return 0 83 | } 84 | 85 | func (f *FramedSource) isAwaitingData() bool { 86 | return f.isCurrentlyAwaitingData 87 | } 88 | 89 | // default implementation: do nothing 90 | func (f *FramedSource) destroy() { 91 | } 92 | -------------------------------------------------------------------------------- /livemedia/common_test.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | optionsRequest = "OPTIONS rtsp://172.22.0.172/123.ts RTSP/1.0\r\n" + 10 | "CSeq: 1\r\n" + 11 | "User-Agent: LibVLC/2.1.2 (Dor Streaming Media v1.0.0.3))\r\n\r\n" 12 | 13 | descriptionRequest = "DESCRIBE rtsp://192.168.1.103/live1.264 RTSP/1.0\r\n" + 14 | "CSeq: 2\r\n" + 15 | "User-Agent: LibVLC/2.1.5 (Dor Streaming Media v1.0.0.3))\r\n" + 16 | "Accept: application/sdp\r\n\r\n" 17 | 18 | setupRequest = "SETUP rtsp://192.168.1.105:8554/test.264/track1 RTSP/1.0\r\n" + 19 | "CSeq: 3\r\n" + 20 | "User-Agent: dorsvr (Dor Streaming Media v1.0.0.3)\r\n" + 21 | "Transport: RTP/AVP;unicast;client_port=37175-37176\r\n\r\n" 22 | 23 | playRequest = "PLAY rtsp://192.168.1.105:8554/test.264/ RTSP/1.0\r\n" + 24 | "CSeq: 4\r\n" + 25 | "User-Agent: dorsvr (Dor Streaming Media v1.0.0.3)\r\n" + 26 | "Session: E1155C20\r\n" + 27 | "Range: npt=0.000-\r\n" 28 | 29 | teardownRequest = "TEARDOWN rtsp://192.168.1.105:8554/test.264 RTSP/1.0\r\n" + 30 | "CSeq: 5\r\n" + 31 | "Session: E1155C20\r\n" + 32 | "User-Agent: VLC media player (Dor Streaming Media v1.0.0.3))" 33 | ) 34 | 35 | func TestParseRTSPRequestString(t *testing.T) { 36 | var verify bool = true 37 | 38 | cmdList := []string{"OPTIONS", "DESCRIBE", "SETUP", "PLAY", "TEARDOWN"} 39 | reqList := []string{optionsRequest, descriptionRequest, setupRequest, playRequest, teardownRequest} 40 | for i, req := range reqList { 41 | reqStr, ok := ParseRTSPRequestString(req, len(req)) 42 | if !ok { 43 | break 44 | } 45 | 46 | // check request command 47 | if reqStr.CmdName != cmdList[i] { 48 | verify = false 49 | break 50 | } 51 | 52 | // check cseq 53 | if reqStr.Cseq != fmt.Sprintf("%d", i+1) { 54 | fmt.Println("parse cseq error", reqStr.Cseq, i+1) 55 | verify = false 56 | break 57 | } 58 | 59 | // check session id 60 | if reqStr.CmdName == "PLAY" || reqStr.CmdName == "TEARDOWN" { 61 | var sessionID string = "E1155C20" 62 | if reqStr.SessionIDStr != sessionID { 63 | fmt.Println("parse session id error", reqStr.Cseq, i+1) 64 | verify = false 65 | break 66 | } 67 | } 68 | 69 | // check content length 70 | fmt.Printf("CommandName: %s\n", reqStr.CmdName) 71 | fmt.Printf("ContentLength: %s\n", reqStr.ContentLength) 72 | // check url presuffix and suffix 73 | fmt.Printf("UrlPreSuffix: %s\n", reqStr.UrlPreSuffix) 74 | fmt.Printf("UrlSuffix: %s\n\n", reqStr.UrlSuffix) 75 | } 76 | if verify { 77 | t.Log("success") 78 | } else { 79 | t.Error("failed") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /livemedia/byte_stream_file_source_linux_386.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | sys "syscall" 8 | 9 | "github.com/djwackey/gitea/log" 10 | ) 11 | 12 | type ByteStreamFileSource struct { 13 | FramedFileSource 14 | presentationTime sys.Timeval 15 | fileSize int64 16 | lastPlayTime uint 17 | playTimePerFrame uint 18 | preferredFrameSize uint 19 | } 20 | 21 | func newByteStreamFileSource(fileName string) *ByteStreamFileSource { 22 | fid, err := os.Open(fileName) 23 | if err != nil { 24 | fmt.Println(err, fileName) 25 | return nil 26 | } 27 | stat, _ := fid.Stat() 28 | 29 | fileSource := new(ByteStreamFileSource) 30 | fileSource.fid = fid 31 | fileSource.fileSize = stat.Size() 32 | fileSource.initFramedFileSource(fileSource) 33 | return fileSource 34 | } 35 | 36 | func (s *ByteStreamFileSource) destroy() { 37 | s.stopGettingFrames() 38 | } 39 | 40 | func (s *ByteStreamFileSource) doGetNextFrame() error { 41 | if !s.source.isAwaitingData() { 42 | s.doStopGettingFrames() 43 | return errors.New("file source is not awaiting data.") 44 | } 45 | 46 | if err := s.doReadFromFile(); err != nil { 47 | return err 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func (s *ByteStreamFileSource) doStopGettingFrames() error { 54 | return s.fid.Close() 55 | } 56 | 57 | func (s *ByteStreamFileSource) doReadFromFile() error { 58 | frameSize, err := s.fid.Read(s.buffTo) 59 | if err != nil { 60 | log.Trace("[ByteStreamFileSource::doReadFromFile] Failed to read bytes from file.%s", err.Error()) 61 | s.handleClosure() 62 | return err 63 | } 64 | s.frameSize = uint(frameSize) 65 | 66 | // Set the 'presentation time': 67 | if s.playTimePerFrame > 0 && s.preferredFrameSize > 0 { 68 | if s.presentationTime.Sec == 0 && s.presentationTime.Usec == 0 { 69 | // This is the first frame, so use the current time: 70 | sys.Gettimeofday(&s.presentationTime) 71 | } else { 72 | // Increment by the play time of the previous data: 73 | uSeconds := s.presentationTime.Usec + int32(s.lastPlayTime) 74 | s.presentationTime.Sec += uSeconds / 1000000 75 | s.presentationTime.Usec = uSeconds % 1000000 76 | } 77 | 78 | // Remember the play time of this data: 79 | s.lastPlayTime = (s.playTimePerFrame * s.frameSize) / s.preferredFrameSize 80 | s.durationInMicroseconds = s.lastPlayTime 81 | } else { 82 | // We don't know a specific play time duration for this data, 83 | // so just record the current time as being the 'presentation time': 84 | sys.Gettimeofday(&s.presentationTime) 85 | } 86 | 87 | s.afterGetting() 88 | return nil 89 | } 90 | 91 | func (s *ByteStreamFileSource) FileSize() int64 { 92 | return s.fileSize 93 | } 94 | -------------------------------------------------------------------------------- /livemedia/byte_stream_file_source_linux_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | sys "syscall" 8 | 9 | "github.com/djwackey/gitea/log" 10 | ) 11 | 12 | type ByteStreamFileSource struct { 13 | FramedFileSource 14 | presentationTime sys.Timeval 15 | fileSize int64 16 | lastPlayTime uint 17 | playTimePerFrame uint 18 | preferredFrameSize uint 19 | } 20 | 21 | func newByteStreamFileSource(fileName string) *ByteStreamFileSource { 22 | fid, err := os.Open(fileName) 23 | if err != nil { 24 | fmt.Println(err, fileName) 25 | return nil 26 | } 27 | stat, _ := fid.Stat() 28 | 29 | fileSource := new(ByteStreamFileSource) 30 | fileSource.fid = fid 31 | fileSource.fileSize = stat.Size() 32 | fileSource.initFramedFileSource(fileSource) 33 | return fileSource 34 | } 35 | 36 | func (s *ByteStreamFileSource) destroy() { 37 | s.stopGettingFrames() 38 | } 39 | 40 | func (s *ByteStreamFileSource) doGetNextFrame() error { 41 | if !s.source.isAwaitingData() { 42 | s.doStopGettingFrames() 43 | return errors.New("file source is not awaiting data.") 44 | } 45 | 46 | if err := s.doReadFromFile(); err != nil { 47 | return err 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func (s *ByteStreamFileSource) doStopGettingFrames() error { 54 | return s.fid.Close() 55 | } 56 | 57 | func (s *ByteStreamFileSource) doReadFromFile() error { 58 | frameSize, err := s.fid.Read(s.buffTo) 59 | if err != nil { 60 | log.Trace("[ByteStreamFileSource::doReadFromFile] Failed to read bytes from file.%s", err.Error()) 61 | s.handleClosure() 62 | return err 63 | } 64 | s.frameSize = uint(frameSize) 65 | 66 | // Set the 'presentation time': 67 | if s.playTimePerFrame > 0 && s.preferredFrameSize > 0 { 68 | if s.presentationTime.Sec == 0 && s.presentationTime.Usec == 0 { 69 | // This is the first frame, so use the current time: 70 | sys.Gettimeofday(&s.presentationTime) 71 | } else { 72 | // Increment by the play time of the previous data: 73 | uSeconds := s.presentationTime.Usec + int64(s.lastPlayTime) 74 | s.presentationTime.Sec += uSeconds / 1000000 75 | s.presentationTime.Usec = uSeconds % 1000000 76 | } 77 | 78 | // Remember the play time of this data: 79 | s.lastPlayTime = (s.playTimePerFrame * s.frameSize) / s.preferredFrameSize 80 | s.durationInMicroseconds = s.lastPlayTime 81 | } else { 82 | // We don't know a specific play time duration for this data, 83 | // so just record the current time as being the 'presentation time': 84 | sys.Gettimeofday(&s.presentationTime) 85 | } 86 | 87 | s.afterGetting() 88 | return nil 89 | } 90 | 91 | func (s *ByteStreamFileSource) FileSize() int64 { 92 | return s.fileSize 93 | } 94 | -------------------------------------------------------------------------------- /livemedia/byte_stream_file_source_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | sys "syscall" 8 | 9 | "github.com/djwackey/gitea/log" 10 | ) 11 | 12 | type ByteStreamFileSource struct { 13 | FramedFileSource 14 | presentationTime sys.Timeval 15 | fileSize int64 16 | lastPlayTime uint 17 | playTimePerFrame uint 18 | preferredFrameSize uint 19 | } 20 | 21 | func newByteStreamFileSource(fileName string) *ByteStreamFileSource { 22 | fid, err := os.Open(fileName) 23 | if err != nil { 24 | fmt.Println(err, fileName) 25 | return nil 26 | } 27 | stat, _ := fid.Stat() 28 | 29 | fileSource := new(ByteStreamFileSource) 30 | fileSource.fid = fid 31 | fileSource.fileSize = stat.Size() 32 | fileSource.initFramedFileSource(fileSource) 33 | return fileSource 34 | } 35 | 36 | func (s *ByteStreamFileSource) destroy() { 37 | s.stopGettingFrames() 38 | } 39 | 40 | func (s *ByteStreamFileSource) doGetNextFrame() error { 41 | if !s.source.isAwaitingData() { 42 | s.doStopGettingFrames() 43 | return errors.New("file source is not awaiting data.") 44 | } 45 | 46 | if err := s.doReadFromFile(); err != nil { 47 | return err 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func (s *ByteStreamFileSource) doStopGettingFrames() error { 54 | return s.fid.Close() 55 | } 56 | 57 | func (s *ByteStreamFileSource) doReadFromFile() error { 58 | frameSize, err := s.fid.Read(s.buffTo) 59 | if err != nil { 60 | log.Trace("[ByteStreamFileSource::doReadFromFile] Failed to read bytes from file.%s", err.Error()) 61 | s.handleClosure() 62 | return err 63 | } 64 | s.frameSize = uint(frameSize) 65 | 66 | // Set the 'presentation time': 67 | if s.playTimePerFrame > 0 && s.preferredFrameSize > 0 { 68 | if s.presentationTime.Sec == 0 && s.presentationTime.Usec == 0 { 69 | // This is the first frame, so use the current time: 70 | sys.Gettimeofday(&s.presentationTime) 71 | } else { 72 | // Increment by the play time of the previous data: 73 | uSeconds := int32(s.presentationTime.Usec) + int32(s.lastPlayTime) 74 | s.presentationTime.Sec += int64(uSeconds) / 1000000 75 | s.presentationTime.Usec = int32(uSeconds) % 1000000 76 | } 77 | 78 | // Remember the play time of this data: 79 | s.lastPlayTime = (s.playTimePerFrame * s.frameSize) / s.preferredFrameSize 80 | s.durationInMicroseconds = s.lastPlayTime 81 | } else { 82 | // We don't know a specific play time duration for this data, 83 | // so just record the current time as being the 'presentation time': 84 | sys.Gettimeofday(&s.presentationTime) 85 | } 86 | 87 | s.afterGetting() 88 | return nil 89 | } 90 | 91 | func (s *ByteStreamFileSource) FileSize() int64 { 92 | return s.fileSize 93 | } 94 | -------------------------------------------------------------------------------- /groupsock/groupsock.go: -------------------------------------------------------------------------------- 1 | package groupsock 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // GroupSock is used to both send and receive packets. 11 | // As the name suggests, it was originally designed to send/receive 12 | // multicast, but it can send/receive unicast as well. 13 | type GroupSock struct { 14 | portNum uint 15 | udpConn *net.UDPConn 16 | dests []*destRecord 17 | } 18 | 19 | // NewGroupSock returns a source-independent multicast group 20 | func NewGroupSock(addrStr string, portNum uint) *GroupSock { 21 | udpConn := SetupDatagramSocket(addrStr, portNum) 22 | if udpConn == nil { 23 | return nil 24 | } 25 | return &GroupSock{ 26 | portNum: portNum, 27 | udpConn: udpConn, 28 | } 29 | } 30 | 31 | // Output does the datagram send, to each destination. 32 | func (g *GroupSock) Output(buffer []byte, bufferSize uint) bool { 33 | var err error 34 | var writeSuccess bool 35 | for _, dest := range g.dests { 36 | if _, err = g.write(dest.addrStr, dest.portNum, buffer, bufferSize); err == nil { 37 | writeSuccess = true 38 | } 39 | } 40 | return writeSuccess 41 | } 42 | 43 | func (g *GroupSock) write(destAddr string, portNum uint, buffer []byte, bufferSize uint) (int, error) { 44 | addr := fmt.Sprintf("%s:%d", destAddr, portNum) 45 | udpAddr, err := net.ResolveUDPAddr("udp", addr) 46 | if err != nil { 47 | return 0, err 48 | } 49 | 50 | return g.udpConn.WriteToUDP(buffer[:bufferSize], udpAddr) 51 | } 52 | 53 | // Close is responsible for disconnection from client. 54 | func (g *GroupSock) Close() { 55 | g.udpConn.Close() 56 | } 57 | 58 | // HandleRead reads data from client connection. 59 | func (g *GroupSock) HandleRead(buffer []byte) (int, error) { 60 | numBytes, err := ReadSocket(g.udpConn, buffer) 61 | if err != nil { 62 | return numBytes, err 63 | } 64 | 65 | return numBytes, err 66 | } 67 | 68 | // GetSourcePort returns the source port of system allocation. 69 | func (g *GroupSock) GetSourcePort() uint { 70 | if g.udpConn != nil { 71 | localAddr := strings.Split(g.udpConn.LocalAddr().String(), ":") 72 | sourcePort, err := strconv.Atoi(localAddr[len(localAddr)-1]) 73 | if err == nil { 74 | return uint(sourcePort) 75 | } 76 | } 77 | return 0 78 | } 79 | 80 | // AddDestination can add multiple destinations (addresses & ports) 81 | // This can be used to implement multi-unicast. 82 | func (g *GroupSock) AddDestination(addr string, port uint) { 83 | g.dests = append(g.dests, newDestRecord(addr, port)) 84 | } 85 | 86 | // DelDestination can remove the destinations. 87 | func (g *GroupSock) DelDestination() { 88 | } 89 | 90 | type destRecord struct { 91 | addrStr string 92 | portNum uint 93 | } 94 | 95 | func newDestRecord(addr string, port uint) *destRecord { 96 | return &destRecord{ 97 | addrStr: addr, 98 | portNum: port, 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /livemedia/server_media_subsession.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | gs "github.com/djwackey/dorsvr/groupsock" 8 | ) 9 | 10 | type IServerMediaSubsession interface { 11 | getAuxSDPLine(rtpSink IMediaSink, inputSource IFramedSource) string 12 | setParentSession(parentSession *ServerMediaSession) 13 | createNewStreamSource() IFramedSource 14 | createNewRTPSink(rtpGroupSock *gs.GroupSock, rtpPayloadType uint) IMediaSink 15 | GetStreamParameters(tcpSocketNum net.Conn, destAddr, clientSessionID string, 16 | clientRTPPort, clientRTCPPort, rtpChannelID, rtcpChannelID uint) *StreamParameter 17 | TestScaleFactor(scale float32) float32 18 | //Duration() float32 19 | IncrTrackNumber() 20 | TrackID() string 21 | SDPLines() string 22 | CNAME() string 23 | StartStream(clientSessionID string, streamState *StreamState, 24 | rtcpRRHandler, serverRequestAlternativeByteHandler interface{}) (uint32, uint32) 25 | PauseStream(streamState *StreamState) 26 | DeleteStream(sessionID string, streamState *StreamState) 27 | SeekStream(sessionID string, streamState *StreamState, streamDuration float32) 28 | } 29 | 30 | type ServerMediaSubsession struct { 31 | trackNumber uint 32 | trackID string 33 | parentSession *ServerMediaSession 34 | isubsession IServerMediaSubsession 35 | } 36 | 37 | func (s *ServerMediaSubsession) initBaseClass(isubsession IServerMediaSubsession) { 38 | s.isubsession = isubsession 39 | } 40 | 41 | func (s *ServerMediaSubsession) setParentSession(parentSession *ServerMediaSession) { 42 | s.parentSession = parentSession 43 | } 44 | 45 | func (s *ServerMediaSubsession) TrackID() string { 46 | if s.trackID == "" { 47 | s.trackID = fmt.Sprintf("track%d", s.trackNumber) 48 | } 49 | return s.trackID 50 | } 51 | 52 | func (s *ServerMediaSubsession) TrackNumber() uint { 53 | return s.trackNumber 54 | } 55 | 56 | func (s *ServerMediaSubsession) IncrTrackNumber() { 57 | s.trackNumber++ 58 | } 59 | 60 | func (s *ServerMediaSubsession) getAbsoluteTimeRange(absStartTime, absEndTime *string) { 61 | //absStartTime = nil 62 | //absEndTime = nil 63 | } 64 | 65 | func (s *ServerMediaSubsession) rangeSDPLine() string { 66 | var absStart, absEnd *string 67 | s.getAbsoluteTimeRange(absStart, absEnd) 68 | if absStart != nil { 69 | } 70 | 71 | if s.parentSession == nil { 72 | return "" 73 | } 74 | 75 | if s.parentSession.Duration() >= 0.0 { 76 | return "" 77 | } 78 | 79 | ourDuration := s.Duration() 80 | if ourDuration == 0.0 { 81 | return "a=range:npt=0-\r\n" 82 | } else { 83 | return fmt.Sprintf("a=range:npt=0-%.3f\r\n", ourDuration) 84 | } 85 | } 86 | 87 | // default implementation: Support scale = 1 only 88 | func (s *ServerMediaSubsession) TestScaleFactor(scale float32) float32 { 89 | scale = 1.0 90 | return scale 91 | } 92 | 93 | // default implementation: assume an unbounded session 94 | func (s *ServerMediaSubsession) Duration() float32 { 95 | return 0.0 96 | } 97 | -------------------------------------------------------------------------------- /livemedia/server_media_session.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | sys "syscall" 6 | 7 | gs "github.com/djwackey/dorsvr/groupsock" 8 | ) 9 | 10 | var libNameStr string = "Dor Streaming Media v" 11 | var libVersionStr string = mediaServerVersion 12 | 13 | type ServerMediaSession struct { 14 | isSSM bool 15 | ipAddr string 16 | streamName string 17 | descSDPStr string 18 | infoSDPStr string 19 | miscSDPLines string 20 | referenceCount int 21 | SubsessionCounter int 22 | creationTime sys.Timeval 23 | Subsessions []IServerMediaSubsession 24 | } 25 | 26 | func NewServerMediaSession(description, streamName string) *ServerMediaSession { 27 | session := new(ServerMediaSession) 28 | session.descSDPStr = description + ", streamed by the Dor Media Server" 29 | session.infoSDPStr = streamName 30 | session.streamName = streamName 31 | session.Subsessions = make([]IServerMediaSubsession, 1024) 32 | session.ipAddr, _ = gs.OurIPAddress() 33 | 34 | sys.Gettimeofday(&session.creationTime) 35 | return session 36 | } 37 | 38 | func (s *ServerMediaSession) GenerateSDPDescription() string { 39 | var sourceFilterLine string 40 | if s.isSSM { 41 | sourceFilterLine = fmt.Sprintf("a=source-filter: incl IN IP4 * %s\r\n"+ 42 | "a=rtcp-unicast: reflection\r\n", s.ipAddr) 43 | } else { 44 | sourceFilterLine = "" 45 | } 46 | 47 | var rangeLine string 48 | dur := s.Duration() 49 | if dur == 0.0 { 50 | rangeLine = "a=range:npt=0-\r\n" 51 | } else if dur > 0.0 { 52 | rangeLine = fmt.Sprintf("a=range:npt=0-%.3f\r\n", dur) 53 | } 54 | 55 | sdpPrefixFmt := "v=0\r\n" + 56 | "o=- %d%06d %d IN IP4 %s\r\n" + 57 | "s=%s\r\n" + 58 | "i=%s\r\n" + 59 | "t=0 0\r\n" + 60 | "a=tool:%s%s\r\n" + 61 | "a=type:broadcast\r\n" + 62 | "a=control:*\r\n" + 63 | "%s" + 64 | "%s" + 65 | "a=x-qt-text-nam:%s\r\n" + 66 | "a=x-qt-text-inf:%s\r\n" + 67 | "%s" 68 | 69 | sdp := fmt.Sprintf(sdpPrefixFmt, 70 | s.creationTime.Sec, 71 | s.creationTime.Usec, 72 | 1, 73 | s.ipAddr, 74 | s.descSDPStr, 75 | s.infoSDPStr, 76 | libNameStr, libVersionStr, 77 | sourceFilterLine, 78 | rangeLine, 79 | s.descSDPStr, 80 | s.infoSDPStr, 81 | s.miscSDPLines) 82 | 83 | // Then, add the (media-level) lines for each subsession: 84 | for i := 0; i < s.SubsessionCounter; i++ { 85 | sdpLines := s.Subsessions[i].SDPLines() 86 | sdp += sdpLines 87 | } 88 | 89 | return sdp 90 | } 91 | 92 | func (s *ServerMediaSession) StreamName() string { 93 | return s.streamName 94 | } 95 | 96 | func (s *ServerMediaSession) AddSubsession(subsession IServerMediaSubsession) { 97 | s.Subsessions[s.SubsessionCounter] = subsession 98 | s.SubsessionCounter++ 99 | subsession.setParentSession(s) 100 | subsession.IncrTrackNumber() 101 | } 102 | 103 | func (s *ServerMediaSession) Duration() float32 { 104 | return 0.0 105 | } 106 | 107 | func (s *ServerMediaSession) TestScaleFactor() float32 { 108 | return 1.0 109 | } 110 | -------------------------------------------------------------------------------- /livemedia/bit_vector.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | var maxLength uint = 32 8 | var singleBitMask = [8]byte{0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01} 9 | 10 | type BitVector struct { 11 | totNumBits uint 12 | curBitIndex uint 13 | baseBitOffset uint 14 | baseByte []byte 15 | } 16 | 17 | func newBitVector(baseByte []byte, baseBitOffset, totNumBits uint) *BitVector { 18 | return &BitVector{ 19 | baseByte: baseByte, 20 | baseBitOffset: baseBitOffset, 21 | totNumBits: totNumBits, 22 | } 23 | } 24 | 25 | func (v *BitVector) init(baseByte []byte, baseBitOffset, totNumBits uint) { 26 | v.baseByte = baseByte 27 | v.baseBitOffset = baseBitOffset 28 | v.totNumBits = totNumBits 29 | } 30 | 31 | func (v *BitVector) getBits(numBits uint) uint { 32 | if numBits == 0 { 33 | return 0 34 | } 35 | 36 | tmpBuf := make([]byte, 4) 37 | 38 | if numBits > maxLength { 39 | numBits = maxLength 40 | } 41 | 42 | var overflowingBits uint 43 | if numBits > v.totNumBits-v.curBitIndex { 44 | overflowingBits = numBits - (v.totNumBits - v.curBitIndex) 45 | } 46 | 47 | v.shiftBits(tmpBuf, v.baseByte, 0, v.baseBitOffset+v.curBitIndex, numBits-overflowingBits) 48 | v.curBitIndex += (numBits - overflowingBits) 49 | 50 | result := uint(binary.BigEndian.Uint32(tmpBuf)) 51 | result >>= (maxLength - numBits) // move into low-order part of word 52 | result &= (0xFFFFFFFF << overflowingBits) // so any overflow bits are 0 53 | return result 54 | } 55 | 56 | // The following is equivalent to "getBits(1)", except faster: 57 | func (v *BitVector) get1Bit() uint { 58 | if v.curBitIndex >= v.totNumBits { /* overflow */ 59 | return 0 60 | } else { 61 | totBitOffset := v.baseBitOffset + v.curBitIndex 62 | v.curBitIndex++ 63 | curFromByte := v.baseByte[totBitOffset/8] 64 | result := (curFromByte >> (7 - (totBitOffset % 8))) & 0x01 65 | return uint(result) 66 | } 67 | } 68 | 69 | func (v *BitVector) get1BitBoolean() bool { 70 | return (v.get1Bit() != 0) 71 | } 72 | 73 | func (v *BitVector) shiftBits(toBaseByte, fromBaseByte []byte, toBitOffset, fromBitOffset, numBits uint) { 74 | if numBits == 0 { 75 | return 76 | } 77 | 78 | /* Note that from and to may overlap, if from>to */ 79 | fromBytePtr := fromBaseByte[fromBitOffset/8:] 80 | fromBitRem := fromBitOffset % 8 81 | toBytePtr := toBaseByte[toBitOffset/8:] 82 | toBitRem := toBitOffset % 8 83 | 84 | for numBits > 0 { 85 | fromBitMask := singleBitMask[fromBitRem] 86 | fromBit := fromBytePtr[0] & fromBitMask 87 | toBitMask := singleBitMask[toBitRem] 88 | 89 | if fromBit != 0 { 90 | toBytePtr[0] |= toBitMask 91 | } else { 92 | toBytePtr[0] &= ^toBitMask 93 | } 94 | 95 | fromBitRem++ 96 | if fromBitRem == 8 { 97 | fromBytePtr = fromBytePtr[1:] 98 | fromBitRem = 0 99 | } 100 | toBitRem++ 101 | if toBitRem == 8 { 102 | toBytePtr = toBytePtr[1:] 103 | toBitRem = 0 104 | } 105 | numBits-- 106 | } 107 | } 108 | 109 | func (v *BitVector) skipBits(numBits uint) { 110 | if numBits > v.totNumBits-v.curBitIndex { /* overflow */ 111 | v.curBitIndex = v.totNumBits 112 | } else { 113 | v.curBitIndex += numBits 114 | } 115 | } 116 | 117 | func (v *BitVector) getExpGolomb() uint { 118 | var numLeadingZeroBits uint 119 | var codeStart uint = 1 120 | 121 | for v.get1Bit() == 0 && v.curBitIndex < v.totNumBits { 122 | numLeadingZeroBits++ 123 | codeStart *= 2 124 | } 125 | 126 | return codeStart - 1 + v.getBits(numLeadingZeroBits) 127 | } 128 | -------------------------------------------------------------------------------- /livemedia/m2ts_video_stream_framer.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | var transportSyncByte byte = 0x47 8 | 9 | type PIDStatus struct { 10 | firstClock, lastClock, firstRealTime, lastRealTime float32 11 | lastPacketNum uint 12 | } 13 | 14 | func NewPIDStatus() *PIDStatus { 15 | return new(PIDStatus) 16 | } 17 | 18 | type M2TSVideoStreamFramer struct { 19 | FramedFilter 20 | pcrLimit float32 21 | tsPCRCount uint 22 | tsPacketCount uint 23 | numTSPacketsToStream uint 24 | limitNumTSPacketsToStream bool 25 | limitTSPacketsToStreamByPCR bool 26 | pidStatusDict map[byte]*PIDStatus 27 | } 28 | 29 | func NewM2TSVideoStreamFramer(inputSource IFramedSource) *M2TSVideoStreamFramer { 30 | return new(M2TSVideoStreamFramer) 31 | } 32 | 33 | func (f *M2TSVideoStreamFramer) doGetNextFrame() error { 34 | if f.limitNumTSPacketsToStream { 35 | if f.numTSPacketsToStream == 0 { 36 | //f.handleClosure(this) 37 | return nil 38 | } 39 | if f.numTSPacketsToStream*TRANSPORT_PACKET_SIZE < f.maxSize { 40 | f.maxSize = f.numTSPacketsToStream * TRANSPORT_PACKET_SIZE 41 | } 42 | } 43 | return nil 44 | } 45 | 46 | func (f *M2TSVideoStreamFramer) doStopGettingFrames() error { 47 | f.tsPacketCount = 0 48 | f.tsPCRCount = 0 49 | 50 | return f.clearPIDStatusTable() 51 | } 52 | 53 | func (f *M2TSVideoStreamFramer) afterGettingFrame() { 54 | } 55 | 56 | func (f *M2TSVideoStreamFramer) setNumTSPacketsToStream(numTSRecordsToStream uint) { 57 | f.numTSPacketsToStream = numTSRecordsToStream 58 | if numTSRecordsToStream > 0 { 59 | f.limitNumTSPacketsToStream = true 60 | } else { 61 | f.limitNumTSPacketsToStream = false 62 | } 63 | } 64 | 65 | func (f *M2TSVideoStreamFramer) clearPIDStatusTable() error { 66 | return nil 67 | } 68 | 69 | func (f *M2TSVideoStreamFramer) updateTSPacketDurationEstimate(pkt []byte, timeNow float32) bool { 70 | if pkt[0] == transportSyncByte { 71 | fmt.Println("Missing sync byte!") 72 | return false 73 | } 74 | f.tsPacketCount++ 75 | 76 | // If this packet doesn't contain a PCR, then we're not interested in it: 77 | adaptation_field_control := (pkt[3] & 0x30) >> 4 78 | if adaptation_field_control != 2 && adaptation_field_control != 3 { 79 | // there's no adaptation_field 80 | return false 81 | } 82 | 83 | adaptation_field_length := pkt[4] 84 | if adaptation_field_length == 0 { 85 | return false 86 | } 87 | 88 | //discontinuity_indicator := pkt[5]&0x80 89 | pcrFlag := pkt[5] & 0x10 90 | if pcrFlag == 0 { 91 | // no PCR 92 | return false 93 | } 94 | 95 | // There's a PCR. Get it, and the PID: 96 | f.tsPCRCount++ 97 | pcrBaseHigh := float32(uint(pkt[6])<<24 | uint(pkt[7])<<16 | uint(pkt[8])<<8 | uint(pkt[9])) 98 | clock := pcrBaseHigh / 45000.0 99 | if (pkt[10] & 0x80) != 0 { 100 | clock += 1 / 90000.0 // add in low-bit (if set) 101 | } 102 | pcrExt := float32(((uint(pkt[10]) & 0x01) << 8) | uint(pkt[11])) 103 | clock += pcrExt / 27000000.0 104 | if f.limitTSPacketsToStreamByPCR { 105 | if clock > f.pcrLimit { 106 | // We've hit a preset limit within the stream: 107 | return false 108 | } 109 | } 110 | 111 | pid := (uint(pkt[1])&0x1F)<<8 | uint(pkt[2]) 112 | pidStatus := f.pidStatusDict[byte(pid)] 113 | if pidStatus == nil { 114 | pidStatus = NewPIDStatus() 115 | } 116 | 117 | pidStatus.lastClock = clock 118 | pidStatus.lastRealTime = timeNow 119 | pidStatus.lastPacketNum = f.tsPacketCount 120 | return true 121 | } 122 | -------------------------------------------------------------------------------- /livemedia/mpeg_video_stream_framer_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | //"github.com/djwackey/dorsvr/log" 5 | sys "syscall" 6 | ) 7 | 8 | type TimeCode struct { 9 | days uint 10 | hours uint 11 | minutes uint 12 | seconds uint 13 | pictures uint 14 | } 15 | 16 | func NewTimeCode() *TimeCode { 17 | return new(TimeCode) 18 | } 19 | 20 | type MPEGVideoStreamFramer struct { 21 | FramedFilter 22 | frameRate uint 23 | tcSecsBase uint 24 | pictureCount uint 25 | pictureTimeBase uint 26 | picturesAdjustment uint 27 | pictureEndMarker bool 28 | curGOPTimeCode TimeCode 29 | preGOPTimeCode TimeCode 30 | presentationTimeBase sys.Timeval 31 | parser *H264VideoStreamParser 32 | } 33 | 34 | func (f *MPEGVideoStreamFramer) initMPEGVideoStreamFramer(parser *H264VideoStreamParser) { 35 | f.parser = parser 36 | f.reset() 37 | } 38 | 39 | func (f *MPEGVideoStreamFramer) reset() { 40 | sys.Gettimeofday(&f.presentationTimeBase) 41 | } 42 | 43 | // Computes "presentationTime" from the most recent GOP's 44 | // time_code, along with the "numAdditionalPictures" parameter: 45 | func (f *MPEGVideoStreamFramer) computePresentationTime(numAdditionalPictures uint) { 46 | tc := f.curGOPTimeCode 47 | 48 | var pictureTime uint 49 | tcSecs := (((tc.days*24)+tc.hours)*60+tc.minutes)*60 + tc.seconds - f.tcSecsBase 50 | if f.frameRate == 0.0 { 51 | pictureTime = 0.0 52 | } else { 53 | pictureTime = (tc.pictures + f.picturesAdjustment + numAdditionalPictures) / f.frameRate 54 | } 55 | 56 | for pictureTime < f.pictureTimeBase { // "if" should be enough, but just in case 57 | if tcSecs > 0 { 58 | tcSecs -= 1 59 | } 60 | pictureTime += 1.0 61 | } 62 | pictureTime -= f.pictureTimeBase 63 | if pictureTime < 0.0 { 64 | pictureTime = 0.0 // sanity check 65 | } 66 | pictureSeconds := pictureTime 67 | pictureFractionOfSecond := pictureTime - pictureSeconds 68 | 69 | f.presentationTime = f.presentationTimeBase 70 | f.presentationTime.Sec += int64(tcSecs + pictureSeconds) 71 | f.presentationTime.Usec += int32(pictureFractionOfSecond * 1000000.0) 72 | if f.presentationTime.Usec >= 1000000 { 73 | f.presentationTime.Usec -= 1000000 74 | f.presentationTime.Sec++ 75 | } 76 | } 77 | 78 | func (f *MPEGVideoStreamFramer) doGetNextFrame() error { 79 | f.parser.registerReadInterest(f.buffTo, f.maxSize) 80 | f.continueReadProcessing() 81 | return nil 82 | } 83 | 84 | func (f *MPEGVideoStreamFramer) continueReadProcessing() { 85 | acquiredFrameSize, err := f.parser.parse() 86 | if err == nil { 87 | // We were able to acquire a frame from the input. 88 | // It has already been copied to the reader's space. 89 | f.frameSize = acquiredFrameSize 90 | f.numTruncatedBytes = f.parser.numTruncatedBytes 91 | 92 | // "presentationTime" should have already been computed. 93 | 94 | // Compute "durationInMicroseconds" now: 95 | if f.frameRate == 0.0 || f.pictureCount < 0 { 96 | f.durationInMicroseconds = 0 97 | } else { 98 | f.durationInMicroseconds = f.pictureCount * 1000000 / f.frameRate 99 | } 100 | f.pictureCount = 0 101 | 102 | // Call our own 'after getting' function. Because we're not a 'leaf' 103 | // source, we can call this directly, without risking infinite recursion. 104 | f.afterGetting() 105 | } else { 106 | if err.Error() == "EOF" { 107 | // We were unable to parse a complete frame from the input, because: 108 | // - we had to read more data from the source stream, or 109 | // - the source stream has ended. 110 | } else { 111 | f.continueReadProcessing() 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /livemedia/mpeg_video_stream_framer_linux_386.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | //"github.com/djwackey/dorsvr/log" 5 | sys "syscall" 6 | ) 7 | 8 | type TimeCode struct { 9 | days uint 10 | hours uint 11 | minutes uint 12 | seconds uint 13 | pictures uint 14 | } 15 | 16 | func NewTimeCode() *TimeCode { 17 | return new(TimeCode) 18 | } 19 | 20 | type MPEGVideoStreamFramer struct { 21 | FramedFilter 22 | frameRate uint 23 | tcSecsBase uint 24 | pictureCount uint 25 | pictureTimeBase uint 26 | picturesAdjustment uint 27 | pictureEndMarker bool 28 | curGOPTimeCode TimeCode 29 | preGOPTimeCode TimeCode 30 | presentationTimeBase sys.Timeval 31 | parser *H264VideoStreamParser 32 | } 33 | 34 | func (f *MPEGVideoStreamFramer) initMPEGVideoStreamFramer(parser *H264VideoStreamParser) { 35 | f.parser = parser 36 | f.reset() 37 | } 38 | 39 | func (f *MPEGVideoStreamFramer) reset() { 40 | sys.Gettimeofday(&f.presentationTimeBase) 41 | } 42 | 43 | // Computes "presentationTime" from the most recent GOP's 44 | // time_code, along with the "numAdditionalPictures" parameter: 45 | func (f *MPEGVideoStreamFramer) computePresentationTime(numAdditionalPictures uint) { 46 | tc := f.curGOPTimeCode 47 | 48 | var pictureTime uint 49 | tcSecs := (((tc.days*24)+tc.hours)*60+tc.minutes)*60 + tc.seconds - f.tcSecsBase 50 | if f.frameRate == 0.0 { 51 | pictureTime = 0.0 52 | } else { 53 | pictureTime = (tc.pictures + f.picturesAdjustment + numAdditionalPictures) / f.frameRate 54 | } 55 | 56 | for pictureTime < f.pictureTimeBase { // "if" should be enough, but just in case 57 | if tcSecs > 0 { 58 | tcSecs -= 1 59 | } 60 | pictureTime += 1.0 61 | } 62 | pictureTime -= f.pictureTimeBase 63 | if pictureTime < 0.0 { 64 | pictureTime = 0.0 // sanity check 65 | } 66 | pictureSeconds := pictureTime 67 | pictureFractionOfSecond := pictureTime - pictureSeconds 68 | 69 | f.presentationTime = f.presentationTimeBase 70 | f.presentationTime.Sec += int32(tcSecs + pictureSeconds) 71 | f.presentationTime.Usec += int32(pictureFractionOfSecond * 1000000.0) 72 | if f.presentationTime.Usec >= 1000000 { 73 | f.presentationTime.Usec -= 1000000 74 | f.presentationTime.Sec++ 75 | } 76 | } 77 | 78 | func (f *MPEGVideoStreamFramer) doGetNextFrame() error { 79 | f.parser.registerReadInterest(f.buffTo, f.maxSize) 80 | f.continueReadProcessing() 81 | return nil 82 | } 83 | 84 | func (f *MPEGVideoStreamFramer) continueReadProcessing() { 85 | acquiredFrameSize, err := f.parser.parse() 86 | if err == nil { 87 | // We were able to acquire a frame from the input. 88 | // It has already been copied to the reader's space. 89 | f.frameSize = acquiredFrameSize 90 | f.numTruncatedBytes = f.parser.numTruncatedBytes 91 | 92 | // "presentationTime" should have already been computed. 93 | 94 | // Compute "durationInMicroseconds" now: 95 | if f.frameRate == 0.0 || f.pictureCount < 0 { 96 | f.durationInMicroseconds = 0 97 | } else { 98 | f.durationInMicroseconds = f.pictureCount * 1000000 / f.frameRate 99 | } 100 | f.pictureCount = 0 101 | 102 | // Call our own 'after getting' function. Because we're not a 'leaf' 103 | // source, we can call this directly, without risking infinite recursion. 104 | f.afterGetting() 105 | } else { 106 | if err.Error() == "EOF" { 107 | // We were unable to parse a complete frame from the input, because: 108 | // - we had to read more data from the source stream, or 109 | // - the source stream has ended. 110 | } else { 111 | f.continueReadProcessing() 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /livemedia/mpeg_video_stream_framer_linux_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | //"github.com/djwackey/dorsvr/log" 5 | sys "syscall" 6 | ) 7 | 8 | type TimeCode struct { 9 | days uint 10 | hours uint 11 | minutes uint 12 | seconds uint 13 | pictures uint 14 | } 15 | 16 | func NewTimeCode() *TimeCode { 17 | return new(TimeCode) 18 | } 19 | 20 | type MPEGVideoStreamFramer struct { 21 | FramedFilter 22 | frameRate uint 23 | tcSecsBase uint 24 | pictureCount uint 25 | pictureTimeBase uint 26 | picturesAdjustment uint 27 | pictureEndMarker bool 28 | curGOPTimeCode TimeCode 29 | preGOPTimeCode TimeCode 30 | presentationTimeBase sys.Timeval 31 | parser *H264VideoStreamParser 32 | } 33 | 34 | func (f *MPEGVideoStreamFramer) initMPEGVideoStreamFramer(parser *H264VideoStreamParser) { 35 | f.parser = parser 36 | f.reset() 37 | } 38 | 39 | func (f *MPEGVideoStreamFramer) reset() { 40 | sys.Gettimeofday(&f.presentationTimeBase) 41 | } 42 | 43 | // Computes "presentationTime" from the most recent GOP's 44 | // time_code, along with the "numAdditionalPictures" parameter: 45 | func (f *MPEGVideoStreamFramer) computePresentationTime(numAdditionalPictures uint) { 46 | tc := f.curGOPTimeCode 47 | 48 | var pictureTime uint 49 | tcSecs := (((tc.days*24)+tc.hours)*60+tc.minutes)*60 + tc.seconds - f.tcSecsBase 50 | if f.frameRate == 0.0 { 51 | pictureTime = 0.0 52 | } else { 53 | pictureTime = (tc.pictures + f.picturesAdjustment + numAdditionalPictures) / f.frameRate 54 | } 55 | 56 | for pictureTime < f.pictureTimeBase { // "if" should be enough, but just in case 57 | if tcSecs > 0 { 58 | tcSecs -= 1 59 | } 60 | pictureTime += 1.0 61 | } 62 | pictureTime -= f.pictureTimeBase 63 | if pictureTime < 0.0 { 64 | pictureTime = 0.0 // sanity check 65 | } 66 | pictureSeconds := pictureTime 67 | pictureFractionOfSecond := pictureTime - pictureSeconds 68 | 69 | f.presentationTime = f.presentationTimeBase 70 | f.presentationTime.Sec += int64(tcSecs + pictureSeconds) 71 | f.presentationTime.Usec += int64(pictureFractionOfSecond * 1000000.0) 72 | if f.presentationTime.Usec >= 1000000 { 73 | f.presentationTime.Usec -= 1000000 74 | f.presentationTime.Sec++ 75 | } 76 | } 77 | 78 | func (f *MPEGVideoStreamFramer) doGetNextFrame() error { 79 | f.parser.registerReadInterest(f.buffTo, f.maxSize) 80 | f.continueReadProcessing() 81 | return nil 82 | } 83 | 84 | func (f *MPEGVideoStreamFramer) continueReadProcessing() { 85 | acquiredFrameSize, err := f.parser.parse() 86 | if err == nil { 87 | // We were able to acquire a frame from the input. 88 | // It has already been copied to the reader's space. 89 | f.frameSize = acquiredFrameSize 90 | f.numTruncatedBytes = f.parser.numTruncatedBytes 91 | 92 | // "presentationTime" should have already been computed. 93 | 94 | // Compute "durationInMicroseconds" now: 95 | if f.frameRate == 0.0 || f.pictureCount < 0 { 96 | f.durationInMicroseconds = 0 97 | } else { 98 | f.durationInMicroseconds = f.pictureCount * 1000000 / f.frameRate 99 | } 100 | f.pictureCount = 0 101 | 102 | // Call our own 'after getting' function. Because we're not a 'leaf' 103 | // source, we can call this directly, without risking infinite recursion. 104 | f.afterGetting() 105 | } else { 106 | if err.Error() == "EOF" { 107 | // We were unable to parse a complete frame from the input, because: 108 | // - we had to read more data from the source stream, or 109 | // - the source stream has ended. 110 | } else { 111 | f.continueReadProcessing() 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /auth/digest.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "strings" 9 | sys "syscall" 10 | ) 11 | 12 | var counter = 0 13 | 14 | // Digest is a struct used for digest authentication. 15 | // The "realm", and "nonce" fields are supplied by the server 16 | // (in a "401 Unauthorized" response). 17 | // The "username" and "password" fields are supplied by the client. 18 | type Digest struct { 19 | Realm string 20 | Nonce string 21 | Username string 22 | Password string 23 | } 24 | 25 | // NewDigest returns a pointer to a new instance of authorization digest 26 | func NewDigest() *Digest { 27 | return &Digest{} 28 | } 29 | 30 | // RandomNonce returns a random nonce 31 | func (d *Digest) RandomNonce() { 32 | var timeNow sys.Timeval 33 | sys.Gettimeofday(&timeNow) 34 | 35 | counter++ 36 | seedData := fmt.Sprintf("%d.%06d%d", timeNow.Sec, timeNow.Usec, counter) 37 | 38 | // Use MD5 to compute a 'random' nonce from this seed data: 39 | h := md5.New() 40 | io.WriteString(h, seedData) 41 | d.Nonce = hex.EncodeToString(h.Sum(nil)) 42 | } 43 | 44 | // ComputeResponse represents generating the response using cmd and url value 45 | func (d *Digest) ComputeResponse(cmd, url string) string { 46 | ha1Data := fmt.Sprintf("%s:%s:%s", d.Username, d.Realm, d.Password) 47 | ha2Data := fmt.Sprintf("%s:%s", cmd, url) 48 | 49 | h1 := md5.New() 50 | h2 := md5.New() 51 | io.WriteString(h1, ha1Data) 52 | io.WriteString(h2, ha2Data) 53 | 54 | digestData := fmt.Sprintf("%s:%s:%s", hex.EncodeToString(h1.Sum(nil)), d.Nonce, hex.EncodeToString(h2.Sum(nil))) 55 | 56 | h3 := md5.New() 57 | io.WriteString(h3, digestData) 58 | 59 | return hex.EncodeToString(h3.Sum(nil)) 60 | } 61 | 62 | // AuthorizationHeader is a struct stored the infomation of parsing "Authorization:" line 63 | type AuthorizationHeader struct { 64 | URI string 65 | Realm string 66 | Nonce string 67 | Username string 68 | Response string 69 | } 70 | 71 | // ParseAuthorizationHeader represents the parsing of "Authorization:" line, 72 | // Authorization Header contains uri, realm, nonce, Username, response fields 73 | func ParseAuthorizationHeader(buf string) *AuthorizationHeader { 74 | if buf == "" { 75 | return nil 76 | } 77 | 78 | // First, find "Authorization:" 79 | index := strings.Index(buf, "Authorization: Digest ") 80 | if -1 == index { 81 | return nil 82 | } 83 | 84 | // Then, run through each of the fields, looking for ones we handle: 85 | var n1, n2 int 86 | var parameter, value, username, realm, nonce, uri, response string 87 | fields := buf[index+22:] 88 | for { 89 | n1, _ = fmt.Sscanf(fields, "%[^=]=\"%[^\"]\"", ¶meter, &value) 90 | n2, _ = fmt.Sscanf(fields, "%[^=]=\"\"", ¶meter) 91 | if n1 != 2 && n2 != 1 { 92 | break 93 | } 94 | if strings.EqualFold(parameter, "username") { 95 | username = value 96 | } else if strings.EqualFold(parameter, "realm") { 97 | realm = value 98 | } else if strings.EqualFold(parameter, "nonce") { 99 | nonce = value 100 | } else if strings.EqualFold(parameter, "uri") { 101 | uri = value 102 | } else if strings.EqualFold(parameter, "response") { 103 | response = value 104 | } 105 | fields = fields[len(parameter)+2+len(value)+1:] 106 | for fields[0] == ' ' || fields[0] == ',' { 107 | fields = fields[1:] 108 | } 109 | if fields == "" || fields[0] == '\r' || fields[0] == '\n' { 110 | break 111 | } 112 | } 113 | 114 | return &AuthorizationHeader{ 115 | URI: uri, 116 | Realm: realm, 117 | Nonce: nonce, 118 | Username: username, 119 | Response: response, 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /rtspclient/callback.go: -------------------------------------------------------------------------------- 1 | package rtspclient 2 | 3 | import ( 4 | "github.com/djwackey/dorsvr/livemedia" 5 | "github.com/djwackey/gitea/log" 6 | ) 7 | 8 | func continueAfterDESCRIBE(c *RTSPClient, resultCode int, resultStr string) { 9 | for { 10 | if resultCode != 0 { 11 | log.Error(4, "Failed to get a SDP description: %s", resultStr) 12 | break 13 | } 14 | 15 | sdpDesc := resultStr 16 | 17 | scs := c.scs 18 | // Create a media session object from this SDP description 19 | scs.Session = livemedia.NewMediaSession(sdpDesc) 20 | if scs.Session == nil { 21 | log.Error(4, "Failed to create a MediaSession object from the sdp Description.") 22 | break 23 | } else if !scs.Session.HasSubsessions() { 24 | log.Error(4, "This session has no media subsessions (i.e., no \"-m\" lines)") 25 | break 26 | } 27 | 28 | // Then, create and set up our data source objects for the session. 29 | setupNextSubSession(c) 30 | return 31 | } 32 | 33 | // An error occurred with this stream. 34 | shutdownStream(c) 35 | } 36 | 37 | func continueAfterSETUP(c *RTSPClient, resultCode int, resultStr string) { 38 | for { 39 | if resultCode != 0 { 40 | log.Error(4, "Failed to set up the subsession") 41 | break 42 | } 43 | 44 | scs := c.scs 45 | scs.Subsession.Sink = NewDummySink(scs.Subsession, c.baseURL) 46 | if scs.Subsession.Sink == nil { 47 | log.Error(4, "Failed to create a data sink for the subsession.") 48 | break 49 | } 50 | 51 | log.Info("Created a data sink for the \"%s/%s\" subsession.", 52 | scs.Subsession.MediumName(), scs.Subsession.CodecName()) 53 | 54 | scs.Subsession.MiscPtr = c 55 | scs.Subsession.Sink.StartPlaying(scs.Subsession.ReadSource(), nil) 56 | if scs.Subsession.RtcpInstance() != nil { 57 | scs.Subsession.RtcpInstance().SetByeHandler(subsessionByeHandler, scs.Subsession) 58 | } 59 | break 60 | } 61 | 62 | // Set up the next subsession, if any: 63 | setupNextSubSession(c) 64 | } 65 | 66 | func continueAfterPLAY(c *RTSPClient, resultCode int, resultStr string) { 67 | for { 68 | if resultCode != 0 { 69 | log.Error(4, "Failed to start playing session: %s", resultStr) 70 | break 71 | } 72 | 73 | log.Info("Started playing session") 74 | return 75 | } 76 | 77 | // An unrecoverable error occurred with this stream. 78 | shutdownStream(c) 79 | } 80 | 81 | func subsessionByeHandler(subsession *livemedia.MediaSubsession) { 82 | log.Info("Received RTCP BYE on subsession.") 83 | 84 | // Now act as if the subsession had closed: 85 | subsessionAfterPlaying(subsession) 86 | } 87 | 88 | func subsessionAfterPlaying(subsession *livemedia.MediaSubsession) { 89 | rtspClient := subsession.MiscPtr.(*RTSPClient) 90 | shutdownStream(rtspClient) 91 | } 92 | 93 | func shutdownStream(c *RTSPClient) { 94 | if c != nil { 95 | c.sendTeardownCommand(c.scs.Session, nil) 96 | } 97 | 98 | log.Info("Closing the Stream.") 99 | } 100 | 101 | func setupNextSubSession(c *RTSPClient) { 102 | scs := c.scs 103 | scs.Subsession = scs.Next() 104 | if scs.Subsession != nil { 105 | if !scs.Subsession.Initiate() { 106 | log.Error(4, "Failed to initiate the subsession.") 107 | setupNextSubSession(c) 108 | } else { 109 | log.Info("Initiated the \"%s/%s\" subsession (client ports %d-%d)", 110 | scs.Subsession.MediumName(), scs.Subsession.CodecName(), 111 | scs.Subsession.ClientPortNum(), scs.Subsession.ClientPortNum()+1) 112 | c.sendSetupCommand(scs.Subsession, continueAfterSETUP) 113 | } 114 | return 115 | } 116 | 117 | if scs.Session.AbsStartTime() != "" { 118 | c.sendPlayCommand(scs.Session, continueAfterPLAY) 119 | } else { 120 | c.sendPlayCommand(scs.Session, continueAfterPLAY) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /livemedia/stream_state.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | gs "github.com/djwackey/dorsvr/groupsock" 5 | ) 6 | 7 | //////// StreamState //////// 8 | type StreamState struct { 9 | master IServerMediaSubsession 10 | rtpSink IMediaSink 11 | udpSink *BasicUDPSink 12 | rtpGS *gs.GroupSock 13 | rtcpGS *gs.GroupSock 14 | rtcpInstance *RTCPInstance 15 | mediaSource IFramedSource 16 | serverRTPPort uint 17 | serverRTCPPort uint 18 | totalBW uint 19 | areCurrentlyPlaying bool 20 | } 21 | 22 | func newStreamState(master IServerMediaSubsession, serverRTPPort, serverRTCPPort uint, 23 | rtpSink IMediaSink, udpSink *BasicUDPSink, totalBW uint, 24 | mediaSource IFramedSource, rtpGS, rtcpGS *gs.GroupSock) *StreamState { 25 | return &StreamState{ 26 | rtpGS: rtpGS, 27 | rtcpGS: rtcpGS, 28 | master: master, 29 | rtpSink: rtpSink, 30 | udpSink: udpSink, 31 | totalBW: totalBW, 32 | mediaSource: mediaSource, 33 | serverRTPPort: serverRTPPort, 34 | serverRTCPPort: serverRTCPPort, 35 | } 36 | } 37 | 38 | func (s *StreamState) startPlaying(dests *Destinations, 39 | rtcpRRHandler, serverRequestAlternativeByteHandler interface{}) { 40 | if dests == nil { 41 | return 42 | } 43 | 44 | if s.rtcpInstance == nil && s.rtpSink != nil { 45 | // Note: This starts RTCP running automatically 46 | // Create (and start) a 'RTCP instance' for this RTP sink: 47 | s.rtcpInstance = newRTCPInstance(s.rtcpGS, s.totalBW, s.master.CNAME(), s.rtpSink, nil) 48 | } 49 | 50 | if dests.isTCP { 51 | if s.rtpSink != nil { 52 | s.rtpSink.addStreamSocket(dests.tcpSocketNum, dests.rtpChannelID) 53 | s.rtpSink.setServerRequestAlternativeByteHandler(dests.tcpSocketNum, serverRequestAlternativeByteHandler) 54 | } 55 | if s.rtcpInstance != nil { 56 | s.rtcpInstance.setSpecificRRHandler(rtcpRRHandler) 57 | } 58 | } else { 59 | // Tell the RTP and RTCP 'groupsocks' about this destination 60 | // (in case they don't already have it): 61 | if s.rtpGS != nil { 62 | s.rtpGS.AddDestination(dests.addrStr, dests.rtpPort) 63 | } 64 | if s.rtcpGS != nil { 65 | s.rtcpGS.AddDestination(dests.addrStr, dests.rtcpPort) 66 | } 67 | if s.rtcpInstance != nil { 68 | s.rtcpInstance.setSpecificRRHandler(rtcpRRHandler) 69 | } 70 | } 71 | 72 | if s.rtcpInstance != nil { 73 | // Hack: Send an initial RTCP "SR" packet, before the initial RTP packet, so that receivers will (likely) be able to 74 | // get RTCP-synchronized presentation times immediately: 75 | s.rtcpInstance.sendReport() 76 | } 77 | 78 | if !s.areCurrentlyPlaying && s.mediaSource != nil { 79 | if s.rtpSink != nil { 80 | s.rtpSink.StartPlaying(s.mediaSource, s.afterPlayingStreamState) 81 | s.areCurrentlyPlaying = true 82 | } else if s.udpSink != nil { 83 | s.areCurrentlyPlaying = true 84 | s.udpSink.StartPlaying(s.mediaSource, s.afterPlayingStreamState) 85 | } 86 | } 87 | } 88 | 89 | func (s *StreamState) pause() { 90 | if s.rtpSink != nil { 91 | s.rtpSink.StopPlaying() 92 | } 93 | if s.udpSink != nil { 94 | s.udpSink.StopPlaying() 95 | } 96 | s.areCurrentlyPlaying = false 97 | } 98 | 99 | func (s *StreamState) endPlaying(dests *Destinations) { 100 | if dests == nil { 101 | return 102 | } 103 | 104 | if dests.isTCP { 105 | if s.rtpSink != nil { 106 | s.rtpSink.delStreamSocket(dests.tcpSocketNum, dests.rtpChannelID) 107 | } 108 | if s.rtcpInstance != nil { 109 | s.rtcpInstance.unsetSpecificRRHandler() 110 | } 111 | } else { 112 | } 113 | } 114 | 115 | func (s *StreamState) ServerRTPPort() uint { 116 | return s.serverRTPPort 117 | } 118 | 119 | func (s *StreamState) ServerRTCPPort() uint { 120 | return s.serverRTCPPort 121 | } 122 | 123 | func (s *StreamState) afterPlayingStreamState() { 124 | s.reclaim() 125 | } 126 | 127 | func (s *StreamState) reclaim() { 128 | s.rtcpInstance.destroy() 129 | } 130 | 131 | func (s *StreamState) RtpSink() IMediaSink { 132 | return s.rtpSink 133 | } 134 | -------------------------------------------------------------------------------- /livemedia/rtp_transmission_stats.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | sys "syscall" 5 | ) 6 | 7 | //////// RTPTransmissionStatsDB //////// 8 | type RTPTransmissionStatsDB struct { 9 | sink *RTPSink 10 | table map[uint32]*RTPTransmissionStats 11 | } 12 | 13 | func newRTPTransmissionStatsDB(sink *RTPSink) *RTPTransmissionStatsDB { 14 | return &RTPTransmissionStatsDB{ 15 | sink: sink, 16 | table: make(map[uint32]*RTPTransmissionStats), 17 | } 18 | } 19 | 20 | func (d *RTPTransmissionStatsDB) add(ssrc uint32, s *RTPTransmissionStats) { 21 | d.table[ssrc] = s 22 | } 23 | 24 | func (d *RTPTransmissionStatsDB) lookup(ssrc uint32) *RTPTransmissionStats { 25 | s, _ := d.table[ssrc] 26 | return s 27 | } 28 | 29 | func (d *RTPTransmissionStatsDB) noteIncomingRR(lastFromAddress string, 30 | ssrc, lossStats, lastPacketNumReceived, jitter, lastSRTime, diffSRRRTime uint32) { 31 | stats := d.lookup(ssrc) 32 | if stats == nil { 33 | // This is the first time we've heard of this SSRC. 34 | // Create a new record for it: 35 | stats = newRTPTransmissionStats(d.sink, ssrc) 36 | if stats == nil { 37 | return 38 | } 39 | d.add(ssrc, stats) 40 | } 41 | 42 | stats.noteIncomingRR(lastFromAddress, 43 | lossStats, lastPacketNumReceived, jitter, 44 | lastSRTime, diffSRRRTime) 45 | } 46 | 47 | //////// RTPTransmissionStats //////// 48 | type RTPTransmissionStats struct { 49 | sink *RTPSink 50 | ssrc uint32 51 | jitter uint32 52 | lastSRTime uint32 53 | diffSRRRTime uint32 54 | lastOctetCount uint32 55 | lastPacketCount uint32 56 | packetLossRatio uint32 57 | totNumPacketsLost uint32 58 | totalOctetCountLo uint32 59 | totalOctetCountHi uint32 60 | totalPacketCountLo uint32 61 | totalPacketCountHi uint32 62 | lastPacketNumReceived uint32 63 | firstPacketNumReported uint32 64 | oldLastPacketNumReceived uint32 65 | oldTotNumPacketsLost uint32 66 | lastFromAddress string 67 | atLeastTwoRRsHaveBeenReceived bool 68 | firstPacket bool 69 | timeCreated sys.Timeval 70 | timeReceived sys.Timeval 71 | } 72 | 73 | func newRTPTransmissionStats(sink *RTPSink, ssrc uint32) *RTPTransmissionStats { 74 | return &RTPTransmissionStats{ 75 | ssrc: ssrc, 76 | sink: sink, 77 | } 78 | } 79 | 80 | func (s *RTPTransmissionStats) noteIncomingRR(lastFromAddress string, 81 | lossStats, lastPacketNumReceived, jitter, lastSRTime, diffSRRRTime uint32) { 82 | if s.firstPacket { 83 | s.firstPacket = false 84 | s.firstPacketNumReported = lastPacketNumReceived 85 | } else { 86 | s.atLeastTwoRRsHaveBeenReceived = true 87 | s.oldLastPacketNumReceived = s.lastPacketNumReceived 88 | s.oldTotNumPacketsLost = s.totNumPacketsLost 89 | } 90 | sys.Gettimeofday(&s.timeReceived) 91 | 92 | s.lastFromAddress = lastFromAddress 93 | s.packetLossRatio = lossStats >> 24 94 | s.totNumPacketsLost = lossStats & 0xFFFFFF 95 | s.lastPacketNumReceived = lastPacketNumReceived 96 | s.jitter = jitter 97 | s.lastSRTime = lastSRTime 98 | s.diffSRRRTime = diffSRRRTime 99 | 100 | // Update our counts of the total number of octets and packets sent towards 101 | // this receiver: 102 | newOctetCount := uint32(s.sink.octetCount()) 103 | octetCountDiff := newOctetCount - s.lastOctetCount 104 | s.lastOctetCount = newOctetCount 105 | prevTotalOctetCountLo := s.totalOctetCountLo 106 | s.totalOctetCountLo += octetCountDiff 107 | if s.totalOctetCountLo < prevTotalOctetCountLo { // wrap around 108 | s.totalOctetCountHi++ 109 | } 110 | 111 | newPacketCount := uint32(s.sink.packetCount()) 112 | packetCountDiff := newPacketCount - s.lastPacketCount 113 | s.lastPacketCount = newPacketCount 114 | prevTotalPacketCountLo := s.totalPacketCountLo 115 | s.totalPacketCountLo += packetCountDiff 116 | if s.totalPacketCountLo < prevTotalPacketCountLo { // wrap around 117 | s.totalPacketCountHi++ 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /livemedia/rtp_sink.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | sys "syscall" 7 | 8 | gs "github.com/djwackey/dorsvr/groupsock" 9 | //"github.com/djwackey/dorsvr/log" 10 | ) 11 | 12 | //////// RTPSink //////// 13 | type RTPSink struct { 14 | MediaSink 15 | seqNo uint32 16 | _ssrc uint32 17 | _octetCount uint 18 | _packetCount uint // incl RTP hdr 19 | totalOctetCount uint 20 | timestampBase uint32 21 | _rtpPayloadType uint32 22 | rtpTimestampFrequency uint32 23 | rtpPayloadFormatName string 24 | _enableRTCPReports bool 25 | _nextTimestampHasBeenPreset bool 26 | _transmissionStatsDB *RTPTransmissionStatsDB 27 | rtpInterface *RTPInterface 28 | } 29 | 30 | func (s *RTPSink) InitRTPSink(rtpSink IMediaSink, g *gs.GroupSock, rtpPayloadType, 31 | rtpTimestampFrequency uint32, rtpPayloadFormatName string) { 32 | s.InitMediaSink(rtpSink) 33 | s.rtpInterface = newRTPInterface(s, g) 34 | s.rtpPayloadFormatName = rtpPayloadFormatName 35 | s.rtpTimestampFrequency = rtpTimestampFrequency 36 | s._rtpPayloadType = rtpPayloadType 37 | s._nextTimestampHasBeenPreset = false 38 | s._transmissionStatsDB = newRTPTransmissionStatsDB(s) 39 | s._enableRTCPReports = true 40 | 41 | s.seqNo = gs.OurRandom16() 42 | s._ssrc = gs.OurRandom32() 43 | s.timestampBase = gs.OurRandom32() 44 | } 45 | 46 | func (s *RTPSink) addStreamSocket(socketNum net.Conn, streamChannelID uint) { 47 | s.rtpInterface.addStreamSocket(socketNum, streamChannelID) 48 | } 49 | 50 | func (s *RTPSink) delStreamSocket(socketNum net.Conn, streamChannelID uint) { 51 | s.rtpInterface.delStreamSocket(socketNum, streamChannelID) 52 | } 53 | 54 | func (s *RTPSink) currentSeqNo() uint32 { 55 | return s.seqNo 56 | } 57 | 58 | func (s *RTPSink) sdpMediaType() string { 59 | return "data" 60 | } 61 | 62 | func (s *RTPSink) rtpPayloadType() uint32 { 63 | return s._rtpPayloadType 64 | } 65 | 66 | func (s *RTPSink) rtpmapLine() (line string) { 67 | var encodingParamsPart string 68 | if s._rtpPayloadType >= 96 { 69 | line = fmt.Sprintf("a=rtpmap:%d %s/%d%s\r\n", 70 | s._rtpPayloadType, 71 | s.rtpPayloadFormatName, 72 | s.rtpTimestampFrequency, encodingParamsPart) 73 | } 74 | return 75 | } 76 | 77 | func (s *RTPSink) ssrc() uint32 { 78 | return s._ssrc 79 | } 80 | 81 | func (s *RTPSink) octetCount() uint { 82 | return s._octetCount 83 | } 84 | 85 | func (s *RTPSink) packetCount() uint { 86 | return s._packetCount 87 | } 88 | 89 | func (s *RTPSink) enableRTCPReports() bool { 90 | return s._enableRTCPReports 91 | } 92 | 93 | func (s *RTPSink) nextTimestampHasBeenPreset() bool { 94 | return s._nextTimestampHasBeenPreset 95 | } 96 | 97 | func (s *RTPSink) transmissionStatsDB() *RTPTransmissionStatsDB { 98 | return s._transmissionStatsDB 99 | } 100 | 101 | func (s *RTPSink) presetNextTimestamp() uint32 { 102 | var timeNow sys.Timeval 103 | sys.Gettimeofday(&timeNow) 104 | 105 | tsNow := s.convertToRTPTimestamp(timeNow) 106 | s.timestampBase = tsNow 107 | s._nextTimestampHasBeenPreset = true 108 | 109 | return tsNow 110 | } 111 | 112 | func (s *RTPSink) convertToRTPTimestamp(tv sys.Timeval) uint32 { 113 | // Begin by converting from "struct timeval" units to RTP timestamp units: 114 | timestampIncrement := s.rtpTimestampFrequency * uint32(tv.Sec) 115 | timestampIncrement += (2.0*s.rtpTimestampFrequency*uint32(tv.Usec) + 1000000.0) / 2000000 116 | 117 | // Then add this to our 'timestamp base': 118 | if s._nextTimestampHasBeenPreset { 119 | // Make the returned timestamp the same as the current "fTimestampBase", 120 | // so that timestamps begin with the value that was previously preset: 121 | s.timestampBase -= timestampIncrement 122 | s._nextTimestampHasBeenPreset = false 123 | } 124 | 125 | // return RTP Timestamp 126 | return s.timestampBase + timestampIncrement 127 | } 128 | 129 | func (s *RTPSink) setServerRequestAlternativeByteHandler(socketNum net.Conn, handler interface{}) { 130 | s.rtpInterface.setServerRequestAlternativeByteHandler(socketNum, handler) 131 | } 132 | -------------------------------------------------------------------------------- /livemedia/h264_video_rtp_source.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import gs "github.com/djwackey/dorsvr/groupsock" 4 | 5 | type H264VideoRTPSource struct { 6 | MultiFramedRTPSource 7 | curPacketNALUnitType uint 8 | currentPacketBeginsFrame bool 9 | currentPacketCompletesFrame bool 10 | } 11 | 12 | func newH264VideoRTPSource(RTPgs *gs.GroupSock, 13 | rtpPayloadFormat, rtpTimestampFrequency uint32) *H264VideoRTPSource { 14 | source := new(H264VideoRTPSource) 15 | 16 | source.initMultiFramedRTPSource(source, RTPgs, 17 | rtpPayloadFormat, rtpTimestampFrequency, newH264BufferedPacketFactory()) 18 | source.setSpecialHeaderHandler(source.processSpecialHeader) 19 | return source 20 | } 21 | 22 | func (s *H264VideoRTPSource) processSpecialHeader(packet IBufferedPacket) ( 23 | resultSpecialHeaderSize uint32, processOK bool) { 24 | headerStart, packetSize := packet.data(), packet.dataSize() 25 | 26 | var expectedHeaderSize uint32 27 | 28 | // Check if the type field is 28 (FU-A) or 29 (FU-B) 29 | s.curPacketNALUnitType = uint(headerStart[0]) & 0x1F 30 | 31 | switch s.curPacketNALUnitType { 32 | case 24: // STAP-A 33 | expectedHeaderSize = 1 34 | case 25, 26, 27: // STAP-B, MTAP16, or MTAP24 35 | expectedHeaderSize = 3 36 | case 28, 29: // FU-A or FU-B 37 | startBit := (headerStart[1] & 0x80) != 0 38 | endBit := headerStart[1] & 0x40 39 | 40 | if startBit { 41 | expectedHeaderSize = 1 42 | 43 | if packetSize < expectedHeaderSize { 44 | return 45 | } 46 | 47 | headerStart[1] = (headerStart[0] & 0xE0) + (headerStart[1] & 0x1F) 48 | s.currentPacketBeginsFrame = true 49 | } else { 50 | expectedHeaderSize = 2 51 | 52 | if packetSize < expectedHeaderSize { 53 | return 54 | } 55 | 56 | s.currentPacketBeginsFrame = false 57 | } 58 | 59 | s.currentPacketCompletesFrame = (endBit != 0) 60 | default: 61 | s.currentPacketBeginsFrame = true 62 | s.currentPacketCompletesFrame = true 63 | } 64 | 65 | resultSpecialHeaderSize, processOK = expectedHeaderSize, true 66 | return 67 | } 68 | 69 | type SPropRecord struct { 70 | sPropLength uint 71 | sPropBytes []byte 72 | } 73 | 74 | func parseSPropParameterSets(sPropParameterSetsStr string) ([]*SPropRecord, uint) { 75 | return nil, 0 76 | } 77 | 78 | type H264BufferedPacket struct { 79 | BufferedPacket 80 | source *H264VideoRTPSource 81 | } 82 | 83 | type H264BufferedPacketFactory struct { 84 | BufferedPacketFactory 85 | } 86 | 87 | func newH264BufferedPacket(source *H264VideoRTPSource) *H264BufferedPacket { 88 | packet := new(H264BufferedPacket) 89 | packet.initBufferedPacket() 90 | packet.source = source 91 | packet.nextEnclosedFrameProc = packet.nextEnclosedFrameSize 92 | return packet 93 | } 94 | 95 | func (p *H264BufferedPacket) nextEnclosedFrameSize(buff []byte, size uint32) uint32 { 96 | framePtr, dataSize := buff[p.head:], p.tail-p.head 97 | 98 | var resultNALUSize, frameSize uint32 99 | 100 | switch p.source.curPacketNALUnitType { 101 | case 24, 25: // STAP-A or STAP-B 102 | // The first two bytes are NALU size: 103 | if dataSize >= 2 { 104 | resultNALUSize = (uint32(framePtr[0]) << 8) | uint32(framePtr[1]) 105 | framePtr = framePtr[2:] 106 | } 107 | case 26: // MTAP16 108 | // The first two bytes are NALU size. 109 | // The next three are the DOND and TS offset: 110 | if dataSize >= 5 { 111 | resultNALUSize = (uint32(framePtr[0]) << 8) | uint32(framePtr[1]) 112 | framePtr = framePtr[5:] 113 | } 114 | case 27: // MTAP24 115 | // The first two bytes are NALU size. 116 | // The next four are the DOND and TS offset: 117 | if dataSize >= 6 { 118 | resultNALUSize = (uint32(framePtr[0]) << 8) | uint32(framePtr[1]) 119 | framePtr = framePtr[6:] 120 | } 121 | default: 122 | // Common case: We use the entire packet data: 123 | return dataSize 124 | } 125 | 126 | if resultNALUSize <= dataSize { 127 | frameSize = resultNALUSize 128 | } else { 129 | frameSize = dataSize 130 | } 131 | 132 | return frameSize 133 | } 134 | 135 | func newH264BufferedPacketFactory() IBufferedPacketFactory { 136 | return new(H264BufferedPacketFactory) 137 | } 138 | 139 | func (f *H264BufferedPacketFactory) createNewPacket(source interface{}) IBufferedPacket { 140 | var h264VideoRTPSource *H264VideoRTPSource 141 | if source != nil { 142 | h264VideoRTPSource = source.(*H264VideoRTPSource) 143 | } 144 | return newH264BufferedPacket(h264VideoRTPSource) 145 | } 146 | -------------------------------------------------------------------------------- /livemedia/stream_parser.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "errors" 5 | sys "syscall" 6 | 7 | "github.com/djwackey/gitea/log" 8 | ) 9 | 10 | const bankSize uint = 150000 11 | 12 | type StreamParser struct { 13 | curBankNum uint 14 | curParserIndex uint 15 | saveParserIndex uint 16 | totNumValidBytes uint 17 | savedParserIndex uint 18 | remainingUnparsedBits uint 19 | savedRemainingUnparsedBits uint 20 | haveSeenEOF bool 21 | inputSource IFramedSource 22 | bank [2][]byte 23 | curBank []byte 24 | clientContinueFunc interface{} 25 | clientOnInputCloseFunc interface{} 26 | restoreParserStateFunc interface{} 27 | lastSeenPresentationTime sys.Timeval 28 | } 29 | 30 | func (p *StreamParser) initStreamParser(inputSource IFramedSource, 31 | clientOnInputCloseFunc, clientContinueFunc, restoreParserStateFunc interface{}) { 32 | p.inputSource = inputSource 33 | p.clientContinueFunc = clientContinueFunc 34 | p.clientOnInputCloseFunc = clientOnInputCloseFunc 35 | p.restoreParserStateFunc = restoreParserStateFunc 36 | 37 | p.bank[0] = make([]byte, bankSize) 38 | p.bank[1] = make([]byte, bankSize) 39 | 40 | p.curBank = p.bank[p.curBankNum] 41 | } 42 | 43 | func (p *StreamParser) restore() { 44 | p.curParserIndex = p.savedParserIndex 45 | p.remainingUnparsedBits = p.savedRemainingUnparsedBits 46 | } 47 | 48 | func (p *StreamParser) get4Bytes() (n uint, err error) { 49 | if n, err = p.test4Bytes(); err != nil { 50 | return 51 | } 52 | 53 | p.curParserIndex += 4 54 | p.remainingUnparsedBits = 0 55 | return 56 | } 57 | 58 | func (p *StreamParser) get2Bytes() (n uint, err error) { 59 | if err = p.ensureValidBytes(2); err != nil { 60 | return 61 | } 62 | 63 | ptr := p.nextToParse() 64 | n = uint(ptr[0])<<8 | uint(ptr[1]) 65 | 66 | p.curParserIndex += 2 67 | p.remainingUnparsedBits = 0 68 | return 69 | } 70 | 71 | func (p *StreamParser) get1Byte() (uint, error) { 72 | if err := p.ensureValidBytes(1); err != nil { 73 | return 0, err 74 | } 75 | 76 | p.curParserIndex++ 77 | return uint(p.curBank[p.curParserIndex]), nil 78 | } 79 | 80 | func (p *StreamParser) test4Bytes() (uint, error) { 81 | if err := p.ensureValidBytes(4); err != nil { 82 | return 0, err 83 | } 84 | 85 | ptr := p.nextToParse() 86 | return uint(Uint32(ptr[:4])), nil 87 | } 88 | 89 | func (p *StreamParser) testBytes(to []byte, numBytes uint) error { 90 | if err := p.ensureValidBytes(numBytes); err != nil { 91 | return err 92 | } 93 | 94 | copy(to, p.nextToParse()[:numBytes]) 95 | return nil 96 | } 97 | 98 | func (p *StreamParser) skipBytes(numBytes uint) error { 99 | if err := p.ensureValidBytes(numBytes); err != nil { 100 | return err 101 | } 102 | 103 | p.curParserIndex += numBytes 104 | return nil 105 | } 106 | 107 | func (p *StreamParser) nextToParse() []byte { 108 | return p.curBank[p.curParserIndex:] 109 | } 110 | 111 | func (p *StreamParser) curOffset() uint { 112 | return p.curParserIndex 113 | } 114 | 115 | func (p *StreamParser) saveParserState() { 116 | p.savedParserIndex = p.curParserIndex 117 | p.savedRemainingUnparsedBits = p.remainingUnparsedBits 118 | } 119 | 120 | func (p *StreamParser) ensureValidBytes(numBytesNeeded uint) error { 121 | if p.curParserIndex+numBytesNeeded <= p.totNumValidBytes { 122 | return nil 123 | } 124 | 125 | return p.ensureValidBytes1(numBytesNeeded) 126 | } 127 | 128 | func (p *StreamParser) ensureValidBytes1(numBytesNeeded uint) error { 129 | maxInputFrameSize := p.inputSource.maxFrameSize() 130 | if maxInputFrameSize > numBytesNeeded { 131 | numBytesNeeded = maxInputFrameSize 132 | } 133 | 134 | // First, check whether these new bytes would overflow the current 135 | // bank. If so, start using a new bank now. 136 | if p.curParserIndex+numBytesNeeded > bankSize { 137 | numBytesToSave := p.totNumValidBytes - p.savedParserIndex 138 | from := p.curBank[p.saveParserIndex:] 139 | 140 | p.curBankNum = (p.curBankNum + 1) % 2 141 | p.curBank = p.bank[p.curBankNum] 142 | copy(p.curBank, from[:numBytesToSave]) 143 | //p.curBank = p.curBank[p.saveParserIndex : p.saveParserIndex+numBytesToSave] 144 | 145 | p.curParserIndex -= p.savedParserIndex 146 | p.savedParserIndex = 0 147 | p.totNumValidBytes = numBytesToSave 148 | } 149 | 150 | if p.curParserIndex+numBytesNeeded > bankSize { 151 | panic("StreamParser Internal error") 152 | } 153 | 154 | // Try to read as many new bytes as will fit in the current bank: 155 | maxNumBytesToRead := bankSize - p.totNumValidBytes 156 | err := p.inputSource.GetNextFrame(p.curBank[p.totNumValidBytes:], maxNumBytesToRead, 157 | p.afterGettingBytes, p.onInputClosure) 158 | if err != nil { 159 | // maybe haven't more data to read, reached file's end. 160 | return err 161 | } 162 | 163 | return errors.New("reading bytes from input source.") 164 | } 165 | 166 | func (p *StreamParser) afterGettingBytes(numBytesRead, numTruncatedBytes uint, presentationTime sys.Timeval) { 167 | if p.totNumValidBytes+numBytesRead > bankSize { 168 | log.Warn("StreamParser::afterGettingBytes() "+ 169 | "warning: read %d bytes; expected no more than %d\n", numBytesRead, bankSize-p.totNumValidBytes) 170 | } 171 | 172 | p.lastSeenPresentationTime = presentationTime 173 | 174 | p.totNumValidBytes += numBytesRead 175 | //log.Debug("[StreamParser::afterGettingBytes] totNumValidBytes: %d", p.totNumValidBytes) 176 | 177 | // Continue our original calling source where it left off: 178 | p.restoreParserStateFunc.(func())() 179 | p.clientContinueFunc.(func())() 180 | } 181 | 182 | func (p *StreamParser) onInputClosure() { 183 | if !p.haveSeenEOF { 184 | p.haveSeenEOF = true 185 | p.afterGettingBytes(0, 0, p.lastSeenPresentationTime) 186 | } else { 187 | // We're hitting EOF for the second time. Now, we handle the source input closure: 188 | if p.clientOnInputCloseFunc != nil { 189 | p.clientOnInputCloseFunc.(func())() 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /livemedia/h264_video_rtp_sink.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | sys "syscall" 7 | 8 | gs "github.com/djwackey/dorsvr/groupsock" 9 | "github.com/djwackey/gitea/log" 10 | ) 11 | 12 | //////// H264VideoRTPSink //////// 13 | type H264VideoRTPSink struct { 14 | VideoRTPSink 15 | ourFragmenter *H264FUAFragmenter 16 | sps []byte 17 | pps []byte 18 | spsSize uint 19 | ppsSize uint 20 | } 21 | 22 | func newH264VideoRTPSink(rtpGroupSock *gs.GroupSock, rtpPayloadType uint32) *H264VideoRTPSink { 23 | sink := new(H264VideoRTPSink) 24 | sink.initVideoRTPSink(sink, rtpGroupSock, rtpPayloadType, 90000, "H264") 25 | return sink 26 | } 27 | 28 | func (s *H264VideoRTPSink) destroy() { 29 | s.StopPlaying() 30 | } 31 | 32 | func (s *H264VideoRTPSink) ContinuePlaying() { 33 | if s.ourFragmenter == nil { 34 | s.ourFragmenter = newH264FUAFragmenter(s.Source, OutPacketBufferMaxSize, s.ourMaxPacketSize-12) 35 | } else { 36 | // reassign input source 37 | s.ourFragmenter.initFramedFilter(s.Source) 38 | } 39 | 40 | s.Source = s.ourFragmenter 41 | s.multiFramedPlaying() 42 | } 43 | 44 | func (s *H264VideoRTPSink) AuxSDPLine() string { 45 | if len(s.sps) == 0 || len(s.pps) == 0 { 46 | if s.ourFragmenter == nil { 47 | return "" 48 | } 49 | 50 | framerSource := s.ourFragmenter.inputSource.(*H264VideoStreamFramer) 51 | if framerSource == nil { 52 | return "" 53 | } 54 | 55 | s.sps, s.pps, s.spsSize, s.ppsSize = framerSource.getSPSandPPS() 56 | if len(s.sps) == 0 || len(s.pps) == 0 { 57 | return "" 58 | } 59 | } 60 | 61 | spsBase64 := base64.StdEncoding.EncodeToString(s.sps) 62 | ppsBase64 := base64.StdEncoding.EncodeToString(s.pps) 63 | 64 | var profileLevelID uint32 65 | if s.spsSize >= 4 { 66 | profileLevelID = Uint32(s.sps[1:4]) 67 | } 68 | 69 | return fmt.Sprintf("a=fmtp:%d packetization-mode=1;profile-level-id=%06X;sprop-parameter-sets=%s,%s\r\n", 70 | s._rtpPayloadType, profileLevelID, spsBase64, ppsBase64) 71 | } 72 | 73 | func (s *H264VideoRTPSink) doSpecialFrameHandling(fragmentationOffset, numBytesInFrame, numRemainingBytes uint, 74 | frameStart []byte, framePresentationTime sys.Timeval) { 75 | if s.ourFragmenter != nil { 76 | framerSource := s.ourFragmenter.inputSource.(*H264VideoStreamFramer) 77 | if s.ourFragmenter.lastFragmentCompletedNALUnit && framerSource != nil && framerSource.pictureEndMarker { 78 | s.setMarkerBit() 79 | framerSource.pictureEndMarker = false 80 | } 81 | } 82 | s.setTimestamp(framePresentationTime) 83 | } 84 | 85 | func (s *H264VideoRTPSink) frameCanAppearAfterPacketStart(frameStart []byte, numBytesInFrame uint) bool { 86 | return false 87 | } 88 | 89 | //////// H264FUAFragmenter //////// 90 | type H264FUAFragmenter struct { 91 | FramedFilter 92 | saveNumTruncatedBytes uint 93 | maxOutputPacketSize uint 94 | numValidDataBytes uint 95 | inputBufferSize uint 96 | curDataOffset uint 97 | inputBuffer []byte 98 | lastFragmentCompletedNALUnit bool 99 | } 100 | 101 | func newH264FUAFragmenter(inputSource IFramedSource, 102 | inputBufferMax, maxOutputPacketSize uint) *H264FUAFragmenter { 103 | fragment := new(H264FUAFragmenter) 104 | fragment.curDataOffset = 1 105 | fragment.numValidDataBytes = 1 106 | fragment.inputBufferSize = inputBufferMax + 1 107 | fragment.maxOutputPacketSize = maxOutputPacketSize 108 | fragment.inputBuffer = make([]byte, fragment.inputBufferSize) 109 | fragment.initFramedFilter(inputSource) 110 | fragment.initFramedSource(fragment) 111 | return fragment 112 | } 113 | 114 | func (f *H264FUAFragmenter) doGetNextFrame() error { 115 | if f.numValidDataBytes == 1 { 116 | // We have no NAL unit data currently in the buffer. Read a new one: 117 | return f.inputSource.GetNextFrame(f.inputBuffer[1:], f.inputBufferSize-1, 118 | f.afterGettingFrame, f.handleClosure) // H264VideoStreamFramer 119 | } else { 120 | if f.maxSize < f.maxOutputPacketSize { 121 | log.Warn("H264FUAFragmenter::doGetNextFrame(): maxSize (%d) is smaller than expected\n", f.maxSize) 122 | } else { 123 | f.maxSize = f.maxOutputPacketSize 124 | } 125 | 126 | f.lastFragmentCompletedNALUnit = true 127 | if f.curDataOffset == 1 { 128 | if f.numValidDataBytes-1 <= f.maxSize { // case 1 129 | copy(f.buffTo, f.inputBuffer[1:f.numValidDataBytes]) 130 | f.frameSize = f.numValidDataBytes - 1 131 | f.curDataOffset = f.numValidDataBytes 132 | } else { // case 2 133 | // We need to send the NAL unit data as FU-A packets. Deliver the first 134 | // packet now. Note that we add FU indicator and FU header bytes to the front 135 | // of the packet (reusing the existing NAL header byte for the FU header). 136 | f.inputBuffer[0] = (f.inputBuffer[1] & 0xE0) | 28 // FU indicator 137 | f.inputBuffer[1] = 0x80 | (f.inputBuffer[1] & 0x1F) // FU header (with S bit) 138 | copy(f.buffTo, f.inputBuffer[:f.maxSize]) 139 | f.frameSize = f.maxSize 140 | f.curDataOffset += f.maxSize - 1 141 | f.lastFragmentCompletedNALUnit = false 142 | } 143 | } else { 144 | f.inputBuffer[f.curDataOffset-2] = f.inputBuffer[0] // FU indicator 145 | f.inputBuffer[f.curDataOffset-1] = f.inputBuffer[1] &^ 0x80 // FU header (no S bit) 146 | numBytesToSend := 2 + f.numValidDataBytes - f.curDataOffset 147 | if numBytesToSend > f.maxSize { 148 | // We can't send all of the remaining data this time: 149 | numBytesToSend = f.maxSize 150 | f.lastFragmentCompletedNALUnit = false 151 | } else { 152 | // This is the last fragment: 153 | f.inputBuffer[f.curDataOffset-1] |= 0x40 // set the E bit in the FU header 154 | f.numTruncatedBytes = f.saveNumTruncatedBytes 155 | } 156 | copy(f.buffTo, f.inputBuffer[f.curDataOffset-2:f.curDataOffset-2+numBytesToSend]) 157 | f.frameSize = numBytesToSend 158 | f.curDataOffset += numBytesToSend - 2 159 | } 160 | 161 | if f.curDataOffset >= f.numValidDataBytes { 162 | // We're done with this data. Reset the pointers for receiving new data: 163 | f.numValidDataBytes = 1 164 | f.curDataOffset = 1 165 | } 166 | 167 | // Complete delivery to the client: 168 | f.afterGetting() 169 | } 170 | return nil 171 | } 172 | 173 | func (f *H264FUAFragmenter) afterGettingFrame(frameSize, durationInMicroseconds uint, presentationTime sys.Timeval) { 174 | f.numValidDataBytes += frameSize 175 | f.presentationTime = presentationTime 176 | f.durationInMicroseconds = durationInMicroseconds 177 | 178 | f.doGetNextFrame() 179 | } 180 | -------------------------------------------------------------------------------- /rtspserver/server.go: -------------------------------------------------------------------------------- 1 | package rtspserver 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "os" 10 | "runtime" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "github.com/djwackey/dorsvr/auth" 16 | gs "github.com/djwackey/dorsvr/groupsock" 17 | "github.com/djwackey/dorsvr/livemedia" 18 | lg "github.com/djwackey/gitea/log" 19 | ) 20 | 21 | type RTSPServer struct { 22 | urlPrefix string 23 | rtspPort int 24 | httpPort int 25 | rtspListen *net.TCPListener 26 | httpListen *net.TCPListener 27 | clientSessions map[string]*RTSPClientSession 28 | clientHTTPConnections map[string]*RTSPClientConnection 29 | serverMediaSessions map[string]*livemedia.ServerMediaSession 30 | reclamationTestSeconds time.Duration 31 | authDatabase *auth.Database 32 | smsMutex sync.Mutex 33 | sessionMutex sync.Mutex 34 | httpConnectionMutex sync.Mutex 35 | rtspConnectionMutex sync.Mutex 36 | } 37 | 38 | func New(authDatabase *auth.Database) *RTSPServer { 39 | runtime.GOMAXPROCS(runtime.NumCPU()) 40 | 41 | return &RTSPServer{ 42 | authDatabase: authDatabase, 43 | reclamationTestSeconds: 65, 44 | clientSessions: make(map[string]*RTSPClientSession), 45 | clientHTTPConnections: make(map[string]*RTSPClientConnection), 46 | serverMediaSessions: make(map[string]*livemedia.ServerMediaSession), 47 | } 48 | } 49 | 50 | func (s *RTSPServer) Destroy() { 51 | s.rtspListen.Close() 52 | s.httpListen.Close() 53 | } 54 | 55 | func (s *RTSPServer) Listen(portNum int) error { 56 | s.rtspPort = portNum 57 | 58 | var err error 59 | s.rtspListen, err = s.setupOurSocket(portNum) 60 | if err == nil { 61 | s.startMonitor() 62 | } 63 | 64 | return err 65 | } 66 | 67 | func (s *RTSPServer) Start() { 68 | go s.incomingConnectionHandler(s.rtspListen) 69 | } 70 | 71 | func (s *RTSPServer) startMonitor() { 72 | go s.monitorServe() 73 | } 74 | 75 | func (s *RTSPServer) monitorServe() { 76 | log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) 77 | } 78 | 79 | func (s *RTSPServer) setupOurSocket(portNum int) (*net.TCPListener, error) { 80 | tcpAddr := fmt.Sprintf("0.0.0.0:%d", portNum) 81 | addr, _ := net.ResolveTCPAddr("tcp", tcpAddr) 82 | 83 | return net.ListenTCP("tcp", addr) 84 | } 85 | 86 | func (s *RTSPServer) SetupTunnelingOverHTTP(httpPort int) bool { 87 | s.httpPort = httpPort 88 | 89 | var err error 90 | s.httpListen, err = s.setupOurSocket(httpPort) 91 | if err != nil { 92 | return false 93 | } 94 | 95 | go s.incomingConnectionHandler(s.httpListen) 96 | return true 97 | } 98 | 99 | func (s *RTSPServer) HTTPServerPortNum() int { 100 | return s.httpPort 101 | } 102 | 103 | func (s *RTSPServer) RtspURL(streamName string) string { 104 | urlPrefix := s.RtspURLPrefix() 105 | return fmt.Sprintf("%s%s", urlPrefix, streamName) 106 | } 107 | 108 | func (s *RTSPServer) RtspURLPrefix() string { 109 | s.urlPrefix, _ = gs.OurIPAddress() 110 | return fmt.Sprintf("rtsp://%s:%d/", s.urlPrefix, s.rtspPort) 111 | } 112 | 113 | func (s *RTSPServer) incomingConnectionHandler(l *net.TCPListener) { 114 | for { 115 | tcpConn, err := l.AcceptTCP() 116 | if err != nil { 117 | lg.Error(0, "failed to accept client.%s", err.Error()) 118 | continue 119 | } 120 | 121 | tcpConn.SetReadBuffer(50 * 1024) 122 | 123 | // Create a new object for handling server RTSP connection: 124 | go s.newClientConnection(tcpConn) 125 | } 126 | } 127 | 128 | func (s *RTSPServer) newClientConnection(conn net.Conn) { 129 | c := newRTSPClientConnection(s, conn) 130 | if c != nil { 131 | c.incomingRequestHandler() 132 | } 133 | } 134 | 135 | func (s *RTSPServer) getServerMediaSession(streamName string) (sms *livemedia.ServerMediaSession, existed bool) { 136 | s.smsMutex.Lock() 137 | defer s.smsMutex.Unlock() 138 | sms, existed = s.serverMediaSessions[streamName] 139 | return 140 | } 141 | 142 | func (s *RTSPServer) lookupServerMediaSession(streamName string) *livemedia.ServerMediaSession { 143 | // Next, check whether we already have a "ServerMediaSession" for server file: 144 | sms, existed := s.getServerMediaSession(streamName) 145 | 146 | fid, err := os.Open(streamName) 147 | if err != nil { 148 | if existed { 149 | s.removeServerMediaSession(streamName) 150 | } 151 | return nil 152 | } 153 | defer fid.Close() 154 | 155 | if !existed { 156 | sms = s.createNewSMS(streamName) 157 | s.addServerMediaSession(sms) 158 | } 159 | 160 | return sms 161 | } 162 | 163 | func (s *RTSPServer) addServerMediaSession(sms *livemedia.ServerMediaSession) { 164 | sessionName := sms.StreamName() 165 | 166 | s.smsMutex.Lock() 167 | defer s.smsMutex.Unlock() 168 | s.serverMediaSessions[sessionName] = sms 169 | } 170 | 171 | func (s *RTSPServer) removeServerMediaSession(sessionName string) { 172 | s.smsMutex.Lock() 173 | defer s.smsMutex.Unlock() 174 | delete(s.serverMediaSessions, sessionName) 175 | } 176 | 177 | func (s *RTSPServer) getClientSession(sessionID string) (clientSession *RTSPClientSession, existed bool) { 178 | s.sessionMutex.Lock() 179 | defer s.sessionMutex.Unlock() 180 | clientSession, existed = s.clientSessions[sessionID] 181 | return 182 | } 183 | 184 | func (s *RTSPServer) addClientSession(sessionID string, clientSession *RTSPClientSession) { 185 | s.sessionMutex.Lock() 186 | defer s.sessionMutex.Unlock() 187 | s.clientSessions[sessionID] = clientSession 188 | } 189 | 190 | func (s *RTSPServer) removeClientSession(sessionID string) { 191 | s.sessionMutex.Lock() 192 | defer s.sessionMutex.Unlock() 193 | delete(s.clientSessions, sessionID) 194 | } 195 | 196 | func (s *RTSPServer) createNewSMS(fileName string) (sms *livemedia.ServerMediaSession) { 197 | array := strings.Split(fileName, ".") 198 | if len(array) < 2 { 199 | return 200 | } 201 | 202 | extension := array[1] 203 | switch extension { 204 | case "264": 205 | // Assumed to be a H.264 Video Elementary Stream file: 206 | sms = livemedia.NewServerMediaSession("H.264 Video", fileName) 207 | // allow for some possibly large H.264 frames 208 | livemedia.OutPacketBufferMaxSize = 2000000 209 | sms.AddSubsession(livemedia.NewH264FileMediaSubsession(fileName)) 210 | case "ts": 211 | //indexFileName := fmt.Sprintf("%sx", fileName) 212 | sms = livemedia.NewServerMediaSession("MPEG Transport Stream", fileName) 213 | sms.AddSubsession(livemedia.NewM2TSFileMediaSubsession(fileName)) 214 | default: 215 | } 216 | return 217 | } 218 | 219 | func (s *RTSPServer) specialClientAccessCheck(clientSocket net.Conn, clientAddr, urlSuffix string) bool { 220 | return true 221 | } 222 | -------------------------------------------------------------------------------- /livemedia/on_demand_server_media_subsession.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | 8 | gs "github.com/djwackey/dorsvr/groupsock" 9 | ) 10 | 11 | type OnDemandServerMediaSubsession struct { 12 | ServerMediaSubsession 13 | cname string 14 | sdpLines string 15 | portNumForSDP int 16 | initialPortNum uint 17 | reuseFirstSource bool 18 | lastStreamToken *StreamState 19 | destinations map[string]*Destinations 20 | } 21 | 22 | type StreamParameter struct { 23 | IsMulticast bool 24 | ClientRTPPort uint 25 | ClientRTCPPort uint 26 | ServerRTPPort uint 27 | ServerRTCPPort uint 28 | DestinationTTL uint 29 | DestinationAddr string 30 | StreamToken *StreamState 31 | } 32 | 33 | func (s *OnDemandServerMediaSubsession) initOnDemandServerMediaSubsession(isubsession IServerMediaSubsession) { 34 | s.initialPortNum = 6970 35 | s.cname, _ = os.Hostname() 36 | s.destinations = make(map[string]*Destinations) 37 | s.initBaseClass(isubsession) 38 | } 39 | 40 | func (s *OnDemandServerMediaSubsession) SDPLines() string { 41 | if s.sdpLines == "" { 42 | rtpPayloadType := 96 + s.TrackNumber() - 1 43 | 44 | var dummyAddr string 45 | dummyGroupSock := gs.NewGroupSock(dummyAddr, 0) 46 | dummyRTPSink := s.isubsession.createNewRTPSink(dummyGroupSock, rtpPayloadType) 47 | inputSource := s.isubsession.createNewStreamSource() 48 | 49 | s.setSDPLinesFromRTPSink(dummyRTPSink, inputSource, 500) 50 | dummyRTPSink.destroy() 51 | inputSource.destroy() 52 | } 53 | 54 | return s.sdpLines 55 | } 56 | 57 | func (s *OnDemandServerMediaSubsession) GetStreamParameters(tcpSocketNum net.Conn, destAddr, 58 | clientSessionID string, clientRTPPort, clientRTCPPort, rtpChannelID, rtcpChannelID uint) *StreamParameter { 59 | var streamBitrate uint = 500 60 | 61 | sp := new(StreamParameter) 62 | 63 | if s.lastStreamToken != nil { 64 | streamState := s.lastStreamToken 65 | sp.ServerRTPPort = streamState.ServerRTPPort() 66 | sp.ServerRTCPPort = streamState.ServerRTCPPort() 67 | 68 | sp.StreamToken = s.lastStreamToken 69 | } else { 70 | mediaSource := s.isubsession.createNewStreamSource() 71 | 72 | var dummyAddr string 73 | var rtpSink IMediaSink 74 | var udpSink *BasicUDPSink 75 | var rtpGroupSock, rtcpGroupSock *gs.GroupSock 76 | 77 | sp.ServerRTPPort = s.initialPortNum 78 | if clientRTCPPort == 0 { 79 | // We're streaming raw UDP (not RTP). Create a single groupsock: 80 | for { 81 | rtpGroupSock = gs.NewGroupSock(dummyAddr, sp.ServerRTPPort) 82 | if rtpGroupSock != nil { 83 | break 84 | } 85 | sp.ServerRTPPort++ 86 | } 87 | udpSink = NewBasicUDPSink(rtpGroupSock) 88 | } else { 89 | // Normal case: We're streaming RTP (over UDP or TCP). Create a pair of 90 | // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even): 91 | for { 92 | rtpGroupSock = gs.NewGroupSock(dummyAddr, sp.ServerRTPPort) 93 | if rtpGroupSock == nil { 94 | sp.ServerRTPPort += 2 95 | continue 96 | } 97 | 98 | sp.ServerRTCPPort = sp.ServerRTPPort + 1 99 | rtcpGroupSock = gs.NewGroupSock(dummyAddr, sp.ServerRTCPPort) 100 | if rtcpGroupSock == nil { 101 | sp.ServerRTPPort += 2 102 | continue 103 | } 104 | break 105 | } 106 | rtpPayloadType := 96 + s.TrackNumber() - 1 107 | rtpSink = s.isubsession.createNewRTPSink(rtpGroupSock, rtpPayloadType) 108 | } 109 | 110 | // Set up the state of the stream. The stream will get started later: 111 | s.lastStreamToken = newStreamState(s.isubsession, 112 | sp.ServerRTPPort, 113 | sp.ServerRTCPPort, 114 | rtpSink, 115 | udpSink, 116 | streamBitrate, 117 | mediaSource, 118 | rtpGroupSock, 119 | rtcpGroupSock) 120 | sp.StreamToken = s.lastStreamToken 121 | } 122 | 123 | // Record these destinations as being for this client session id: 124 | dests := newDestinations(tcpSocketNum, destAddr, clientRTPPort, clientRTCPPort, rtpChannelID, rtcpChannelID) 125 | s.destinations[clientSessionID] = dests 126 | 127 | return sp 128 | } 129 | 130 | func (s *OnDemandServerMediaSubsession) getAuxSDPLine(rtpSink IMediaSink, inputSource IFramedSource) string { 131 | if rtpSink == nil { 132 | return "" 133 | } 134 | 135 | return rtpSink.AuxSDPLine() 136 | } 137 | 138 | func (s *OnDemandServerMediaSubsession) setSDPLinesFromRTPSink(rtpSink IMediaSink, inputSource IFramedSource, estBitrate uint) { 139 | if rtpSink == nil { 140 | return 141 | } 142 | 143 | mediaType := rtpSink.sdpMediaType() 144 | rtpmapLine := rtpSink.rtpmapLine() 145 | rtpPayloadType := rtpSink.rtpPayloadType() 146 | 147 | rangeLine := s.rangeSDPLine() 148 | auxSDPLine := s.isubsession.getAuxSDPLine(rtpSink, inputSource) 149 | if auxSDPLine == "" { 150 | auxSDPLine = "" 151 | } 152 | 153 | ipAddr := "0.0.0.0" 154 | sdpFmt := "m=%s %d RTP/AVP %d\r\n" + 155 | "c=IN IP4 %s\r\n" + 156 | "b=AS:%d\r\n" + 157 | "%s" + 158 | "%s" + 159 | "%s" + 160 | "a=control:%s\r\n" 161 | 162 | s.sdpLines = fmt.Sprintf(sdpFmt, 163 | mediaType, 164 | s.portNumForSDP, 165 | rtpPayloadType, 166 | ipAddr, 167 | estBitrate, 168 | rtpmapLine, 169 | rangeLine, 170 | auxSDPLine, 171 | s.TrackID()) 172 | } 173 | 174 | func (s *OnDemandServerMediaSubsession) CNAME() string { 175 | return s.cname 176 | } 177 | 178 | func (s *OnDemandServerMediaSubsession) StartStream(clientSessionID string, streamState *StreamState, 179 | rtcpRRHandler, serverRequestAlternativeByteHandler interface{}) (rtpSeqNum, rtpTimestamp uint32) { 180 | destinations, _ := s.destinations[clientSessionID] 181 | go streamState.startPlaying(destinations, rtcpRRHandler, serverRequestAlternativeByteHandler) 182 | 183 | if streamState.RtpSink() != nil { 184 | rtpSeqNum = streamState.RtpSink().currentSeqNo() 185 | rtpTimestamp = streamState.RtpSink().presetNextTimestamp() 186 | } 187 | return 188 | } 189 | 190 | func (s *OnDemandServerMediaSubsession) SeekStream(sessionID string, streamState *StreamState, streamDuration float32) { 191 | if s.reuseFirstSource { 192 | return 193 | } 194 | } 195 | 196 | func (s *OnDemandServerMediaSubsession) PauseStream(streamState *StreamState) { 197 | streamState.pause() 198 | } 199 | 200 | func (s *OnDemandServerMediaSubsession) DeleteStream(sessionID string, streamState *StreamState) { 201 | if dest, existed := s.destinations[sessionID]; existed { 202 | streamState.endPlaying(dest) 203 | delete(s.destinations, sessionID) 204 | } 205 | 206 | if streamState != nil { 207 | streamState.reclaim() 208 | } 209 | } 210 | 211 | //////// Destinations //////// 212 | type Destinations struct { 213 | isTCP bool 214 | addrStr string 215 | rtpPort uint 216 | rtcpPort uint 217 | rtpChannelID uint 218 | rtcpChannelID uint 219 | tcpSocketNum net.Conn 220 | } 221 | 222 | func newDestinations(tcpSocketNum net.Conn, destAddr string, 223 | clientRTPPort, clientRTCPPort, rtpChannelID, rtcpChannelID uint) *Destinations { 224 | var isTCP bool 225 | if tcpSocketNum != nil { 226 | isTCP = true 227 | } 228 | return &Destinations{ 229 | isTCP: isTCP, 230 | addrStr: destAddr, 231 | rtpPort: clientRTPPort, 232 | rtcpPort: clientRTCPPort, 233 | rtpChannelID: rtpChannelID, 234 | rtcpChannelID: rtcpChannelID, 235 | tcpSocketNum: tcpSocketNum, 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /livemedia/rtp_interface.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "net" 5 | 6 | gs "github.com/djwackey/dorsvr/groupsock" 7 | "github.com/djwackey/gitea/log" 8 | ) 9 | 10 | type RTPInterface struct { 11 | gs *gs.GroupSock 12 | owner interface{} 13 | auxReadHandlerFunc interface{} 14 | nextTCPReadStreamSocketNum net.Conn 15 | socketDescriptors map[net.Conn]*SocketDescriptor 16 | tcpStreams *tcpStreamRecord 17 | nextTCPReadSize uint 18 | nextTCPReadStreamChannelID uint 19 | } 20 | 21 | func newRTPInterface(owner interface{}, gs *gs.GroupSock) *RTPInterface { 22 | return &RTPInterface{ 23 | gs: gs, 24 | owner: owner, 25 | socketDescriptors: make(map[net.Conn]*SocketDescriptor), 26 | } 27 | } 28 | 29 | func (i *RTPInterface) startNetworkReading(handlerProc interface{}) { 30 | go handlerProc.(func())() 31 | } 32 | 33 | func (i *RTPInterface) stopNetworkReading() { 34 | i.gs.Close() 35 | } 36 | 37 | func (i *RTPInterface) setServerRequestAlternativeByteHandler(socketNum net.Conn, handler interface{}) { 38 | descriptor := i.lookupSocketDescriptor(socketNum) 39 | if descriptor != nil { 40 | descriptor.setServerRequestAlternativeByteHandler(handler) 41 | } 42 | } 43 | 44 | func (i *RTPInterface) addStreamSocket(socketNum net.Conn, streamChannelID uint) { 45 | if socketNum == nil { 46 | return 47 | } 48 | 49 | var streams *tcpStreamRecord 50 | for streams = i.tcpStreams; streams != nil; streams = streams.next { 51 | if streams.streamSocketNum == socketNum && streams.streamChannelID == streamChannelID { 52 | return 53 | } 54 | } 55 | 56 | i.tcpStreams = newTCPStreamRecord(socketNum, streamChannelID, i.tcpStreams) 57 | 58 | // Also, make sure this new socket is set up for receiving RTP/RTCP over TCP: 59 | descriptor := i.lookupSocketDescriptor(socketNum) 60 | descriptor.registerRTPInterface(streamChannelID, i) 61 | } 62 | 63 | func (i *RTPInterface) delStreamSocket(socketNum net.Conn, streamChannelID uint) { 64 | var streams *tcpStreamRecord 65 | for streams = i.tcpStreams; streams != nil; streams = streams.next { 66 | if streams.streamSocketNum == socketNum && streams.streamChannelID == streamChannelID { 67 | i.deregisterSocket(socketNum, streamChannelID) 68 | 69 | next := streams.next 70 | streams.next = nil 71 | streams = next 72 | } 73 | } 74 | } 75 | 76 | // normal case: send as a UDP packet, also, send over each of our TCP sockets 77 | func (i *RTPInterface) sendPacket(packet []byte, packetSize uint) bool { 78 | success := i.gs.Output(packet, packetSize) 79 | 80 | var streams *tcpStreamRecord 81 | for streams = i.tcpStreams; streams != nil; streams = streams.next { 82 | sendRTPOverTCP(streams.streamSocketNum, packet, packetSize, streams.streamChannelID) 83 | } 84 | 85 | return success 86 | } 87 | 88 | func (i *RTPInterface) handleRead(buffer []byte) (int, error) { 89 | return i.gs.HandleRead(buffer) 90 | } 91 | 92 | func (i *RTPInterface) lookupSocketDescriptor(socketNum net.Conn) *SocketDescriptor { 93 | var existed bool 94 | var descriptor *SocketDescriptor 95 | if descriptor, existed = i.socketDescriptors[socketNum]; existed { 96 | return descriptor 97 | } 98 | 99 | descriptor = newSocketDescriptor(socketNum) 100 | i.socketDescriptors[socketNum] = descriptor 101 | return descriptor 102 | } 103 | 104 | func (i *RTPInterface) deregisterSocket(socketNum net.Conn, streamChannelID uint) { 105 | descriptor := i.lookupSocketDescriptor(socketNum) 106 | if descriptor != nil { 107 | i.removeSocketDescriptor(socketNum) 108 | descriptor.deregisterRTPInterface(streamChannelID) 109 | } 110 | } 111 | 112 | func (i *RTPInterface) removeSocketDescriptor(socketNum net.Conn) { 113 | delete(i.socketDescriptors, socketNum) 114 | } 115 | 116 | type tcpStreamRecord struct { 117 | streamChannelID uint 118 | streamSocketNum net.Conn 119 | next *tcpStreamRecord 120 | } 121 | 122 | func newTCPStreamRecord(streamSocketNum net.Conn, streamChannelID uint, next *tcpStreamRecord) *tcpStreamRecord { 123 | return &tcpStreamRecord{ 124 | next: next, 125 | streamChannelID: streamChannelID, 126 | streamSocketNum: streamSocketNum, 127 | } 128 | } 129 | 130 | ///////////// Help Functions /////////////// 131 | 132 | // Send RTP over TCP, using the encoding defined RFC 2326, section 10.12: 133 | func sendRTPOverTCP(socketNum net.Conn, packet []byte, packetSize, streamChannelID uint) error { 134 | var err error 135 | 136 | dollar := []byte{'$'} 137 | _, err = socketNum.Write(dollar) 138 | if err != nil { 139 | return err 140 | } 141 | 142 | channelID := []byte{byte(streamChannelID)} 143 | _, err = socketNum.Write(channelID) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | netPacketSize := make([]byte, 2) 149 | netPacketSize[0] = byte((packetSize & 0xFF00) >> 8) 150 | netPacketSize[1] = byte(packetSize & 0xFF) 151 | _, err = socketNum.Write(netPacketSize) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | _, err = socketNum.Write(packet[:packetSize]) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | return nil 162 | } 163 | 164 | const ( 165 | awaitingDollar = iota 166 | awaitingStreamChannelID 167 | awaitingSize1 168 | awaitingSize2 169 | awaitingPacketData 170 | ) 171 | 172 | type SocketDescriptor struct { 173 | tcpReadingState int 174 | streamChannelID uint 175 | sizeByte1 uint 176 | socketNum net.Conn 177 | serverRequestAlternativeByteHandler interface{} 178 | subChannels map[uint]*RTPInterface 179 | } 180 | 181 | func newSocketDescriptor(socketNum net.Conn) *SocketDescriptor { 182 | return &SocketDescriptor{ 183 | socketNum: socketNum, 184 | tcpReadingState: awaitingDollar, 185 | subChannels: make(map[uint]*RTPInterface), 186 | } 187 | } 188 | 189 | func (s *SocketDescriptor) registerRTPInterface(streamChannelID uint, rtpInterface *RTPInterface) { 190 | s.subChannels[streamChannelID] = rtpInterface 191 | go s.tcpReadHandler(rtpInterface) 192 | } 193 | 194 | func (s *SocketDescriptor) lookupRTPInterface(streamChannelID uint) (rtpInterface *RTPInterface, existed bool) { 195 | rtpInterface, existed = s.subChannels[streamChannelID] 196 | return 197 | } 198 | 199 | func (s *SocketDescriptor) deregisterRTPInterface(streamChannelID uint) { 200 | defer s.socketNum.Close() 201 | if s.serverRequestAlternativeByteHandler != nil { 202 | s.serverRequestAlternativeByteHandler.(func(requestByte uint))(0xFE) 203 | } 204 | delete(s.subChannels, streamChannelID) 205 | } 206 | 207 | func (s *SocketDescriptor) tcpReadHandler(rtpInterface *RTPInterface) { 208 | defer s.socketNum.Close() 209 | buffer := make([]byte, 1) 210 | for { 211 | if s.tcpReadingState != awaitingPacketData { 212 | _, err := gs.ReadSocket(s.socketNum, buffer) 213 | if err != nil { 214 | if s.serverRequestAlternativeByteHandler != nil { 215 | s.serverRequestAlternativeByteHandler.(func(requestByte uint))(0xFF) 216 | } 217 | rtpInterface.removeSocketDescriptor(s.socketNum) 218 | break 219 | } 220 | } 221 | } 222 | 223 | switch s.tcpReadingState { 224 | case awaitingDollar: 225 | if buffer[0] == '$' { 226 | log.Debug("[SocketDescriptor::tcpReadHandler] Saw '$'") 227 | s.tcpReadingState = awaitingStreamChannelID 228 | } else { 229 | // This character is part of a RTSP request or command, which is handled separately: 230 | if s.serverRequestAlternativeByteHandler != nil && buffer[0] != 0xFF && buffer[0] != 0xFE { 231 | s.serverRequestAlternativeByteHandler.(func(requestByte uint))(uint(buffer[0])) 232 | } 233 | } 234 | case awaitingStreamChannelID: 235 | // The byte that we read is the stream channel id. 236 | if _, existed := s.lookupRTPInterface(uint(buffer[0])); existed { 237 | s.streamChannelID = uint(buffer[0]) 238 | s.tcpReadingState = awaitingSize1 239 | } else { 240 | s.tcpReadingState = awaitingDollar 241 | } 242 | case awaitingSize1: 243 | // The byte that we read is the first (high) byte of the 16-bit RTP or RTCP packet 'size'. 244 | s.sizeByte1 = uint(buffer[0]) 245 | s.tcpReadingState = awaitingSize2 246 | case awaitingSize2: 247 | // The byte that we read is the second (low) byte of the 16-bit RTP or RTCP packet 'size'. 248 | size := (s.sizeByte1 << 8) | uint(buffer[0]) 249 | 250 | // Record the information about the packet data that will be read next: 251 | rtpInterface, existed := s.lookupRTPInterface(s.streamChannelID) 252 | if existed { 253 | rtpInterface.nextTCPReadSize = size 254 | rtpInterface.nextTCPReadStreamSocketNum = s.socketNum 255 | rtpInterface.nextTCPReadStreamChannelID = s.streamChannelID 256 | } 257 | s.tcpReadingState = awaitingPacketData 258 | case awaitingPacketData: 259 | s.tcpReadingState = awaitingDollar 260 | } 261 | } 262 | 263 | func (s *SocketDescriptor) setServerRequestAlternativeByteHandler(handler interface{}) { 264 | s.serverRequestAlternativeByteHandler = handler 265 | } 266 | -------------------------------------------------------------------------------- /livemedia/media_sink.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "net" 7 | sys "syscall" 8 | 9 | "github.com/djwackey/gitea/log" 10 | ) 11 | 12 | // default 13 | var OutPacketBufferMaxSize uint = 2000000 14 | 15 | //////// OutPacketBuffer //////// 16 | type OutPacketBuffer struct { 17 | buff []byte 18 | limit uint 19 | preferred uint 20 | curOffset uint 21 | packetStart uint 22 | maxPacketSize uint 23 | overflowDataSize uint 24 | overflowDataOffset uint 25 | overflowDurationInMicroseconds uint 26 | overflowPresentationTime sys.Timeval 27 | } 28 | 29 | func newOutPacketBuffer(preferredPacketSize, maxPacketSize uint) *OutPacketBuffer { 30 | maxNumPackets := (OutPacketBufferMaxSize + (maxPacketSize - 1)) / maxPacketSize 31 | limit := maxNumPackets * maxPacketSize 32 | 33 | b := &OutPacketBuffer{ 34 | limit: limit, 35 | maxPacketSize: maxPacketSize, 36 | preferred: preferredPacketSize, 37 | buff: make([]byte, limit), 38 | } 39 | 40 | b.resetOffset() 41 | b.resetPacketStart() 42 | b.resetOverflowData() 43 | return b 44 | } 45 | 46 | func (b *OutPacketBuffer) packet() []byte { 47 | return b.buff[b.packetStart:] 48 | } 49 | 50 | func (b *OutPacketBuffer) curPtr() []byte { 51 | return b.buff[(b.packetStart + b.curOffset):] 52 | } 53 | 54 | func (b *OutPacketBuffer) curPacketSize() uint { 55 | return b.curOffset 56 | } 57 | 58 | func (b *OutPacketBuffer) totalBufferSize() uint { 59 | return b.limit 60 | } 61 | 62 | func (b *OutPacketBuffer) increment(numBytes uint) { 63 | b.curOffset += numBytes 64 | } 65 | 66 | func (b *OutPacketBuffer) haveOverflowData() bool { 67 | return b.overflowDataSize > 0 68 | } 69 | 70 | func (b *OutPacketBuffer) isPreferredSize() bool { 71 | return b.curOffset >= b.preferred 72 | } 73 | 74 | func (b *OutPacketBuffer) setOverflowData(overflowDataOffset, overflowDataSize, 75 | durationInMicroseconds uint, presentationTime sys.Timeval) { 76 | b.overflowDataSize = overflowDataSize 77 | b.overflowDataOffset = overflowDataOffset 78 | b.overflowPresentationTime = presentationTime 79 | b.overflowDurationInMicroseconds = durationInMicroseconds 80 | } 81 | 82 | func (b *OutPacketBuffer) useOverflowData() { 83 | b.enqueue(b.buff[(b.packetStart+b.overflowDataOffset):], b.overflowDataSize) 84 | } 85 | 86 | func (b *OutPacketBuffer) adjustPacketStart(numBytes uint) { 87 | b.packetStart += numBytes 88 | if b.overflowDataOffset >= numBytes { 89 | b.overflowDataOffset -= numBytes 90 | } else { 91 | b.overflowDataOffset = 0 92 | b.overflowDataSize = 0 93 | } 94 | } 95 | 96 | func (b *OutPacketBuffer) totalBytesAvailable() uint { 97 | return b.limit - (b.packetStart + b.curOffset) 98 | } 99 | 100 | func (b *OutPacketBuffer) enqueue(from []byte, numBytes uint) { 101 | if numBytes > b.totalBytesAvailable() { 102 | log.Warn("OutPacketBuffer::enqueue() warning: %d > %d\n", numBytes, b.totalBytesAvailable()) 103 | numBytes = b.totalBytesAvailable() 104 | } 105 | 106 | //log.Warn("OutPacketBuffer::enqueue() %v", from) 107 | copy(b.curPtr(), from) 108 | //if !bytes.Equal(b.curPtr(), from) { 109 | // copy(b.curPtr(), from) 110 | //} 111 | b.increment(numBytes) 112 | } 113 | 114 | func (b *OutPacketBuffer) enqueueWord(word uint32) { 115 | buff := new(bytes.Buffer) 116 | err := binary.Write(buff, binary.BigEndian, word) 117 | if err != nil { 118 | log.Error(0, "[OutPacketBuffer::enqueueWord] Failed to enqueueWord.%s", err.Error()) 119 | return 120 | } 121 | b.enqueue(buff.Bytes(), 4) 122 | } 123 | 124 | func (b *OutPacketBuffer) insert(from []byte, numBytes, toPosition uint) { 125 | realToPosition := b.packetStart + toPosition 126 | if realToPosition+numBytes > b.limit { 127 | if realToPosition > b.limit { 128 | return // we can't do this 129 | } 130 | numBytes = b.limit - realToPosition 131 | } 132 | 133 | copy(b.buff[realToPosition:], from[:numBytes]) 134 | if toPosition+numBytes > b.curOffset { 135 | b.curOffset = toPosition + numBytes 136 | } 137 | } 138 | 139 | func (b *OutPacketBuffer) insertWord(word uint32, toPosition uint) { 140 | buff := new(bytes.Buffer) 141 | err := binary.Write(buff, binary.BigEndian, word) 142 | if err == nil { 143 | b.insert(buff.Bytes(), 4, toPosition) 144 | } 145 | } 146 | 147 | func (b *OutPacketBuffer) wouldOverflow(numBytes uint) bool { 148 | return (b.curOffset + numBytes) > b.maxPacketSize 149 | } 150 | 151 | func (b *OutPacketBuffer) numOverflowBytes(numBytes uint) uint { 152 | return (b.curOffset + numBytes) - b.maxPacketSize 153 | } 154 | 155 | func (b *OutPacketBuffer) isTooBigForAPacket(numBytes uint) bool { 156 | return numBytes > b.maxPacketSize 157 | } 158 | 159 | func (b *OutPacketBuffer) extract(to []byte, numBytes, fromPosition uint) { 160 | realFromPosition := b.packetStart + fromPosition 161 | if realFromPosition+numBytes > b.limit { 162 | if realFromPosition > b.limit { 163 | return 164 | } 165 | numBytes = b.limit - realFromPosition 166 | } 167 | copy(to, b.buff[realFromPosition:realFromPosition+numBytes]) 168 | } 169 | 170 | func (b *OutPacketBuffer) extractWord(fromPosition uint) uint32 { 171 | word := make([]byte, 4) 172 | b.extract(word, 4, fromPosition) 173 | 174 | return binary.BigEndian.Uint32(word) 175 | } 176 | 177 | func (b *OutPacketBuffer) skipBytes(numBytes uint) { 178 | if numBytes > b.totalBytesAvailable() { 179 | numBytes = b.totalBytesAvailable() 180 | } 181 | 182 | b.increment(numBytes) 183 | } 184 | 185 | func (b *OutPacketBuffer) resetPacketStart() { 186 | if b.overflowDataSize > 0 { 187 | b.overflowDataOffset += b.packetStart 188 | } 189 | b.packetStart = 0 190 | } 191 | 192 | func (b *OutPacketBuffer) resetOffset() { 193 | b.curOffset = 0 194 | } 195 | 196 | func (b *OutPacketBuffer) resetOverflowData() { 197 | b.overflowDataSize = 0 198 | b.overflowDataOffset = 0 199 | } 200 | 201 | //////// MediaSink //////// 202 | type IMediaSink interface { 203 | AuxSDPLine() string 204 | rtpmapLine() string 205 | sdpMediaType() string 206 | enableRTCPReports() bool 207 | nextTimestampHasBeenPreset() bool 208 | StartPlaying(source IFramedSource, afterFunc interface{}) bool 209 | StopPlaying() 210 | ContinuePlaying() 211 | destroy() 212 | ssrc() uint32 213 | octetCount() uint 214 | packetCount() uint 215 | currentSeqNo() uint32 216 | rtpPayloadType() uint32 217 | presetNextTimestamp() uint32 218 | convertToRTPTimestamp(tv sys.Timeval) uint32 219 | transmissionStatsDB() *RTPTransmissionStatsDB 220 | addStreamSocket(socketNum net.Conn, streamChannelID uint) 221 | delStreamSocket(socketNum net.Conn, streamChannelID uint) 222 | setServerRequestAlternativeByteHandler(socketNum net.Conn, handler interface{}) 223 | frameCanAppearAfterPacketStart(frameStart []byte, numBytesInFrame uint) bool 224 | doSpecialFrameHandling(fragmentationOffset, numBytesInFrame, numRemainingBytes uint, 225 | frameStart []byte, framePresentationTime sys.Timeval) 226 | } 227 | 228 | type MediaSink struct { 229 | Source IFramedSource 230 | rtpSink IMediaSink 231 | afterFunc interface{} 232 | } 233 | 234 | func (s *MediaSink) InitMediaSink(rtpSink IMediaSink) { 235 | s.rtpSink = rtpSink 236 | } 237 | 238 | func (s *MediaSink) StartPlaying(source IFramedSource, afterFunc interface{}) bool { 239 | if s.Source != nil { 240 | log.Error(1, "This sink is already being played") 241 | return false 242 | } 243 | 244 | if s.rtpSink == nil { 245 | log.Error(1, "This RTP Sink is nil") 246 | return false 247 | } 248 | 249 | s.Source = source 250 | s.afterFunc = afterFunc 251 | s.rtpSink.ContinuePlaying() 252 | return true 253 | } 254 | 255 | func (s *MediaSink) StopPlaying() { 256 | // First, tell the source that we're no longer interested: 257 | if s.Source != nil { 258 | s.Source.stopGettingFrames() 259 | } 260 | 261 | s.Source = nil 262 | s.afterFunc = nil 263 | } 264 | 265 | func (s *MediaSink) OnSourceClosure() { 266 | if s.afterFunc != nil { 267 | s.afterFunc.(func())() 268 | } 269 | } 270 | 271 | func (s *MediaSink) addStreamSocket(socketNum net.Conn, streamChannelID uint) {} 272 | func (s *MediaSink) delStreamSocket(socketNum net.Conn, streamChannelID uint) {} 273 | func (s *MediaSink) frameCanAppearAfterPacketStart(frameStart []byte, numBytesInFrame uint) bool { 274 | return false 275 | } 276 | func (s *MediaSink) doSpecialFrameHandling(fragmentationOffset, numBytesInFrame, numRemainingBytes uint, frameStart []byte, framePresentationTime sys.Timeval) { 277 | } 278 | func (s *MediaSink) setServerRequestAlternativeByteHandler(socketNum net.Conn, handler interface{}) {} 279 | 280 | func (s *MediaSink) nextTimestampHasBeenPreset() bool { return true } 281 | func (s *MediaSink) enableRTCPReports() bool { return true } 282 | func (s *MediaSink) AuxSDPLine() string { return "" } 283 | func (s *MediaSink) rtpmapLine() string { return "" } 284 | func (s *MediaSink) sdpMediaType() string { return "" } 285 | func (s *MediaSink) presetNextTimestamp() uint32 { return 0 } 286 | func (s *MediaSink) convertToRTPTimestamp(tv sys.Timeval) uint32 { return 0 } 287 | func (s *MediaSink) transmissionStatsDB() *RTPTransmissionStatsDB { return nil } 288 | func (s *MediaSink) rtpPayloadType() uint32 { return 0 } 289 | func (s *MediaSink) currentSeqNo() uint32 { return 0 } 290 | func (s *MediaSink) packetCount() uint { return 0 } 291 | func (s *MediaSink) octetCount() uint { return 0 } 292 | func (s *MediaSink) ssrc() uint32 { return 0 } 293 | func (s *MediaSink) destroy() {} 294 | -------------------------------------------------------------------------------- /livemedia/rtp_reception_stats_linux_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import sys "syscall" 4 | 5 | ////////// RTPReceptionStats ////////// 6 | const Million = 1000000 7 | 8 | type RTPReceptionStats struct { 9 | ssrc uint32 10 | syncTimestamp uint32 11 | totBytesReceivedHi uint32 12 | totBytesReceivedLo uint32 13 | lastReceivedSRNTPmsw uint32 14 | lastReceivedSRNTPlsw uint32 15 | baseExtSeqNumReceived uint32 16 | totNumPacketsReceived uint32 // for all SSRCs 17 | highestExtSeqNumReceived uint32 18 | previousPacketRTPTimestamp uint32 19 | lastResetExtSeqNumReceived uint32 20 | numPacketsReceivedSinceLastReset uint32 21 | minInterPacketGapUS int64 22 | maxInterPacketGapUS int64 23 | lastTransit int64 24 | syncTime sys.Timeval 25 | lastReceivedSRTime sys.Timeval 26 | lastPacketReceptionTime sys.Timeval 27 | totalInterPacketGaps sys.Timeval 28 | hasBeenSynchronized bool 29 | haveSeenInitialSequenceNumber bool 30 | jitter float32 31 | } 32 | 33 | func newRTPReceptionStats(ssrc, seqNum uint32) *RTPReceptionStats { 34 | s := new(RTPReceptionStats) 35 | s.init(ssrc) 36 | s.initSeqNum(seqNum) 37 | return s 38 | } 39 | 40 | func (s *RTPReceptionStats) init(ssrc uint32) { 41 | s.ssrc = ssrc 42 | s.totNumPacketsReceived = 0 43 | s.totBytesReceivedHi = 0 44 | s.totBytesReceivedLo = 0 45 | s.haveSeenInitialSequenceNumber = false 46 | s.lastTransit = -1 47 | s.previousPacketRTPTimestamp = 0 48 | s.jitter = 0.0 49 | s.lastReceivedSRNTPmsw = 0 50 | s.lastReceivedSRNTPlsw = 0 51 | s.lastReceivedSRTime.Sec = 0 52 | s.lastReceivedSRTime.Usec = 0 53 | s.lastPacketReceptionTime.Sec = 0 54 | s.lastPacketReceptionTime.Usec = 0 55 | s.minInterPacketGapUS = 0x7FFFFFFF 56 | s.maxInterPacketGapUS = 0 57 | s.totalInterPacketGaps.Sec = 0 58 | s.totalInterPacketGaps.Usec = 0 59 | s.hasBeenSynchronized = false 60 | s.syncTime.Sec = 0 61 | s.syncTime.Usec = 0 62 | s.reset() 63 | } 64 | 65 | func (s *RTPReceptionStats) reset() { 66 | s.numPacketsReceivedSinceLastReset = 0 67 | s.lastResetExtSeqNumReceived = s.highestExtSeqNumReceived 68 | } 69 | 70 | func (s *RTPReceptionStats) initSeqNum(initialSeqNum uint32) { 71 | s.baseExtSeqNumReceived = 0x10000 | initialSeqNum 72 | s.highestExtSeqNumReceived = 0x10000 | initialSeqNum 73 | s.haveSeenInitialSequenceNumber = true 74 | } 75 | 76 | func (s *RTPReceptionStats) noteIncomingPacket(seqNum, rtpTimestamp, timestampFrequency, packetSize uint32, 77 | useForJitterCalculation bool) (resultPresentationTime sys.Timeval, resultHasBeenSyncedUsingRTCP bool) { 78 | if !s.haveSeenInitialSequenceNumber { 79 | s.initSeqNum(seqNum) 80 | } 81 | 82 | s.numPacketsReceivedSinceLastReset++ 83 | s.totNumPacketsReceived++ 84 | 85 | prevTotBytesReceivedLo := s.totBytesReceivedLo 86 | s.totBytesReceivedLo += packetSize 87 | if s.totBytesReceivedLo < prevTotBytesReceivedLo { // wrap-around 88 | s.totBytesReceivedHi++ 89 | } 90 | 91 | // Check whether the new sequence number is the highest yet seen: 92 | oldSeqNum := (s.highestExtSeqNumReceived & 0xFFFF) 93 | seqNumCycle := (s.highestExtSeqNumReceived & 0xFFFF0000) 94 | seqNumDifference := seqNum - oldSeqNum 95 | var newSeqNum uint32 96 | 97 | if seqNumLT(int(oldSeqNum), int(seqNum)) { 98 | // This packet was not an old packet received out of order, so check it: 99 | 100 | if seqNumDifference >= 0x8000 { 101 | // The sequence number wrapped around, so start a new cycle: 102 | seqNumCycle += 0x10000 103 | } 104 | 105 | newSeqNum = seqNumCycle | seqNum 106 | if newSeqNum > s.highestExtSeqNumReceived { 107 | s.highestExtSeqNumReceived = newSeqNum 108 | } 109 | } else if s.totNumPacketsReceived > 1 { 110 | // This packet was an old packet received out of order 111 | 112 | if seqNumDifference >= 0x8000 { 113 | // The sequence number wrapped around, so switch to an old cycle: 114 | seqNumCycle -= 0x10000 115 | } 116 | 117 | newSeqNum = seqNumCycle | seqNum 118 | if newSeqNum < s.baseExtSeqNumReceived { 119 | s.baseExtSeqNumReceived = newSeqNum 120 | } 121 | } 122 | 123 | // Record the inter-packet delay 124 | var timeNow sys.Timeval 125 | sys.Gettimeofday(&timeNow) 126 | if s.lastPacketReceptionTime.Sec != 0 || 127 | s.lastPacketReceptionTime.Usec != 0 { 128 | gap := (timeNow.Sec-s.lastPacketReceptionTime.Sec)*Million + 129 | timeNow.Usec - s.lastPacketReceptionTime.Usec 130 | if gap > s.maxInterPacketGapUS { 131 | s.maxInterPacketGapUS = gap 132 | } 133 | if gap < s.minInterPacketGapUS { 134 | s.minInterPacketGapUS = gap 135 | } 136 | s.totalInterPacketGaps.Usec += gap 137 | if s.totalInterPacketGaps.Usec >= Million { 138 | s.totalInterPacketGaps.Sec++ 139 | s.totalInterPacketGaps.Usec -= Million 140 | } 141 | } 142 | s.lastPacketReceptionTime = timeNow 143 | 144 | // Compute the current 'jitter' using the received packet's RTP timestamp, 145 | // and the RTP timestamp that would correspond to the current time. 146 | // (Use the code from appendix A.8 in the RTP spec.) 147 | // Note, however, that we don't use this packet if its timestamp is 148 | // the same as that of the previous packet (this indicates a multi-packet 149 | // fragment), or if we've been explicitly told not to use this packet. 150 | if useForJitterCalculation && rtpTimestamp != s.previousPacketRTPTimestamp { 151 | arrival := int64(timestampFrequency) * timeNow.Sec 152 | arrival += ((2.0*int64(timestampFrequency)*timeNow.Usec + 1000000.0) / 2000000) 153 | // note: rounding 154 | transit := arrival - int64(rtpTimestamp) 155 | if s.lastTransit == -1 { 156 | s.lastTransit = transit // hack for first time 157 | } 158 | d := transit - s.lastTransit 159 | s.lastTransit = transit 160 | if d < 0 { 161 | d = -d 162 | } 163 | s.jitter += (1.0 / 16.0) * (float32(d) - s.jitter) 164 | } 165 | 166 | // Return the 'presentation time' that corresponds to "rtpTimestamp": 167 | if s.syncTime.Sec == 0 && s.syncTime.Usec == 0 { 168 | // This is the first timestamp that we've seen, so use the current 169 | // 'wall clock' time as the synchronization time. (This will be 170 | // corrected later when we receive RTCP SRs.) 171 | s.syncTimestamp = rtpTimestamp 172 | s.syncTime = timeNow 173 | } 174 | 175 | timestampDiff := rtpTimestamp - s.syncTimestamp 176 | // Note: This works even if the timestamp wraps around 177 | // (as long as "int" is 32 bits) 178 | 179 | // Divide this by the timestamp frequency to get real time: 180 | timeDiff := float32(timestampDiff) / float32(timestampFrequency) 181 | 182 | // Add this to the 'sync time' to get our result: 183 | var million float32 = 1000000 184 | var seconds, uSeconds int64 185 | if timeDiff >= 0.0 { 186 | seconds = s.syncTime.Sec + int64(timeDiff) 187 | uSeconds = s.syncTime.Usec + int64((timeDiff-float32(int64(timeDiff)))*million) 188 | if uSeconds >= int64(million) { 189 | uSeconds -= int64(million) 190 | seconds++ 191 | } 192 | } else { 193 | timeDiff = -timeDiff 194 | seconds = s.syncTime.Sec - int64(timeDiff) 195 | uSeconds = s.syncTime.Usec - int64((timeDiff-float32(int64(timeDiff)))*million) 196 | if uSeconds < 0 { 197 | uSeconds += int64(million) 198 | seconds-- 199 | } 200 | } 201 | 202 | resultPresentationTime.Sec = int64(seconds) 203 | resultPresentationTime.Usec = int64(uSeconds) 204 | resultHasBeenSyncedUsingRTCP = s.hasBeenSynchronized 205 | 206 | // Save these as the new synchronization timestamp & time: 207 | s.syncTimestamp = rtpTimestamp 208 | s.syncTime = resultPresentationTime 209 | 210 | s.previousPacketRTPTimestamp = rtpTimestamp 211 | return 212 | } 213 | 214 | func (s *RTPReceptionStats) noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp uint32) { 215 | s.lastReceivedSRNTPmsw = ntpTimestampMSW 216 | s.lastReceivedSRNTPlsw = ntpTimestampLSW 217 | 218 | sys.Gettimeofday(&s.lastReceivedSRTime) 219 | 220 | // Use this SR to update time synchronization information: 221 | s.syncTimestamp = rtpTimestamp 222 | s.syncTime.Sec = int64(ntpTimestampMSW - 0x83AA7E80) // 1/1/1900 -> 1/1/1970 223 | microseconds := float32((ntpTimestampLSW * 15625.0) / 0x04000000) // 10^6/2^32 224 | s.syncTime.Usec = int64(microseconds + 0.5) 225 | s.hasBeenSynchronized = true 226 | } 227 | 228 | ////////// RTPReceptionStatsDB ////////// 229 | type RTPReceptionStatsDB struct { 230 | table map[uint32]*RTPReceptionStats 231 | totNumPacketsReceived uint32 232 | numActiveSourcesSinceLastReset uint32 233 | } 234 | 235 | func newRTPReceptionStatsDB() *RTPReceptionStatsDB { 236 | return &RTPReceptionStatsDB{ 237 | table: make(map[uint32]*RTPReceptionStats), 238 | } 239 | } 240 | 241 | func (d *RTPReceptionStatsDB) add(ssrc uint32, s *RTPReceptionStats) { 242 | d.table[ssrc] = s 243 | } 244 | 245 | func (d *RTPReceptionStatsDB) lookup(ssrc uint32) *RTPReceptionStats { 246 | s, _ := d.table[ssrc] 247 | return s 248 | } 249 | 250 | func (d *RTPReceptionStatsDB) noteIncomingPacket(ssrc, seqNum, 251 | rtpTimestamp, timestampFrequency, packetSize uint32, 252 | useForJitterCalculation bool) (presentationTime sys.Timeval, hasBeenSyncedUsingRTCP bool) { 253 | d.totNumPacketsReceived++ 254 | 255 | s := d.lookup(ssrc) 256 | if s == nil { 257 | s = newRTPReceptionStats(ssrc, seqNum) 258 | if s == nil { 259 | return 260 | } 261 | 262 | d.add(ssrc, s) 263 | } 264 | 265 | if s.numPacketsReceivedSinceLastReset == 0 { 266 | d.numActiveSourcesSinceLastReset++ 267 | } 268 | 269 | presentationTime, hasBeenSyncedUsingRTCP = s.noteIncomingPacket(seqNum, rtpTimestamp, 270 | timestampFrequency, packetSize, useForJitterCalculation) 271 | return 272 | } 273 | 274 | func (d *RTPReceptionStatsDB) noteIncomingSR(ssrc, ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp uint32) { 275 | s := d.lookup(ssrc) 276 | if s == nil { 277 | s = newRTPReceptionStats(ssrc, 0) 278 | if s == nil { 279 | return 280 | } 281 | d.table[ssrc] = s 282 | } 283 | s.noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp) 284 | } 285 | -------------------------------------------------------------------------------- /livemedia/rtp_reception_stats_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import sys "syscall" 4 | 5 | ////////// RTPReceptionStats ////////// 6 | const Million = 1000000 7 | 8 | type RTPReceptionStats struct { 9 | ssrc uint32 10 | syncTimestamp uint32 11 | totBytesReceivedHi uint32 12 | totBytesReceivedLo uint32 13 | lastReceivedSRNTPmsw uint32 14 | lastReceivedSRNTPlsw uint32 15 | baseExtSeqNumReceived uint32 16 | totNumPacketsReceived uint32 // for all SSRCs 17 | highestExtSeqNumReceived uint32 18 | previousPacketRTPTimestamp uint32 19 | lastResetExtSeqNumReceived uint32 20 | numPacketsReceivedSinceLastReset uint32 21 | minInterPacketGapUS int64 22 | maxInterPacketGapUS int64 23 | lastTransit int64 24 | syncTime sys.Timeval 25 | lastReceivedSRTime sys.Timeval 26 | lastPacketReceptionTime sys.Timeval 27 | totalInterPacketGaps sys.Timeval 28 | hasBeenSynchronized bool 29 | haveSeenInitialSequenceNumber bool 30 | jitter float32 31 | } 32 | 33 | func newRTPReceptionStats(ssrc, seqNum uint32) *RTPReceptionStats { 34 | s := new(RTPReceptionStats) 35 | s.init(ssrc) 36 | s.initSeqNum(seqNum) 37 | return s 38 | } 39 | 40 | func (s *RTPReceptionStats) init(ssrc uint32) { 41 | s.ssrc = ssrc 42 | s.totNumPacketsReceived = 0 43 | s.totBytesReceivedHi = 0 44 | s.totBytesReceivedLo = 0 45 | s.haveSeenInitialSequenceNumber = false 46 | s.lastTransit = -1 47 | s.previousPacketRTPTimestamp = 0 48 | s.jitter = 0.0 49 | s.lastReceivedSRNTPmsw = 0 50 | s.lastReceivedSRNTPlsw = 0 51 | s.lastReceivedSRTime.Sec = 0 52 | s.lastReceivedSRTime.Usec = 0 53 | s.lastPacketReceptionTime.Sec = 0 54 | s.lastPacketReceptionTime.Usec = 0 55 | s.minInterPacketGapUS = 0x7FFFFFFF 56 | s.maxInterPacketGapUS = 0 57 | s.totalInterPacketGaps.Sec = 0 58 | s.totalInterPacketGaps.Usec = 0 59 | s.hasBeenSynchronized = false 60 | s.syncTime.Sec = 0 61 | s.syncTime.Usec = 0 62 | s.reset() 63 | } 64 | 65 | func (s *RTPReceptionStats) reset() { 66 | s.numPacketsReceivedSinceLastReset = 0 67 | s.lastResetExtSeqNumReceived = s.highestExtSeqNumReceived 68 | } 69 | 70 | func (s *RTPReceptionStats) initSeqNum(initialSeqNum uint32) { 71 | s.baseExtSeqNumReceived = 0x10000 | initialSeqNum 72 | s.highestExtSeqNumReceived = 0x10000 | initialSeqNum 73 | s.haveSeenInitialSequenceNumber = true 74 | } 75 | 76 | func (s *RTPReceptionStats) noteIncomingPacket(seqNum, rtpTimestamp, timestampFrequency, packetSize uint32, 77 | useForJitterCalculation bool) (resultPresentationTime sys.Timeval, resultHasBeenSyncedUsingRTCP bool) { 78 | if !s.haveSeenInitialSequenceNumber { 79 | s.initSeqNum(seqNum) 80 | } 81 | 82 | s.numPacketsReceivedSinceLastReset++ 83 | s.totNumPacketsReceived++ 84 | 85 | prevTotBytesReceivedLo := s.totBytesReceivedLo 86 | s.totBytesReceivedLo += packetSize 87 | if s.totBytesReceivedLo < prevTotBytesReceivedLo { // wrap-around 88 | s.totBytesReceivedHi++ 89 | } 90 | 91 | // Check whether the new sequence number is the highest yet seen: 92 | oldSeqNum := (s.highestExtSeqNumReceived & 0xFFFF) 93 | seqNumCycle := (s.highestExtSeqNumReceived & 0xFFFF0000) 94 | seqNumDifference := seqNum - oldSeqNum 95 | var newSeqNum uint32 96 | 97 | if seqNumLT(int(oldSeqNum), int(seqNum)) { 98 | // This packet was not an old packet received out of order, so check it: 99 | 100 | if seqNumDifference >= 0x8000 { 101 | // The sequence number wrapped around, so start a new cycle: 102 | seqNumCycle += 0x10000 103 | } 104 | 105 | newSeqNum = seqNumCycle | seqNum 106 | if newSeqNum > s.highestExtSeqNumReceived { 107 | s.highestExtSeqNumReceived = newSeqNum 108 | } 109 | } else if s.totNumPacketsReceived > 1 { 110 | // This packet was an old packet received out of order 111 | 112 | if seqNumDifference >= 0x8000 { 113 | // The sequence number wrapped around, so switch to an old cycle: 114 | seqNumCycle -= 0x10000 115 | } 116 | 117 | newSeqNum = seqNumCycle | seqNum 118 | if newSeqNum < s.baseExtSeqNumReceived { 119 | s.baseExtSeqNumReceived = newSeqNum 120 | } 121 | } 122 | 123 | // Record the inter-packet delay 124 | var timeNow sys.Timeval 125 | sys.Gettimeofday(&timeNow) 126 | if s.lastPacketReceptionTime.Sec != 0 || 127 | s.lastPacketReceptionTime.Usec != 0 { 128 | gap := (timeNow.Sec-s.lastPacketReceptionTime.Sec)*Million + 129 | int64(timeNow.Usec) - int64(s.lastPacketReceptionTime.Usec) 130 | if gap > s.maxInterPacketGapUS { 131 | s.maxInterPacketGapUS = gap 132 | } 133 | if gap < s.minInterPacketGapUS { 134 | s.minInterPacketGapUS = gap 135 | } 136 | s.totalInterPacketGaps.Usec += int32(gap) 137 | if s.totalInterPacketGaps.Usec >= Million { 138 | s.totalInterPacketGaps.Sec++ 139 | s.totalInterPacketGaps.Usec -= Million 140 | } 141 | } 142 | s.lastPacketReceptionTime = timeNow 143 | 144 | // Compute the current 'jitter' using the received packet's RTP timestamp, 145 | // and the RTP timestamp that would correspond to the current time. 146 | // (Use the code from appendix A.8 in the RTP spec.) 147 | // Note, however, that we don't use this packet if its timestamp is 148 | // the same as that of the previous packet (this indicates a multi-packet 149 | // fragment), or if we've been explicitly told not to use this packet. 150 | if useForJitterCalculation && rtpTimestamp != s.previousPacketRTPTimestamp { 151 | arrival := int64(timestampFrequency) * timeNow.Sec 152 | arrival += ((2.0*int64(timestampFrequency)*int64(timeNow.Usec) + 1000000.0) / 2000000) 153 | // note: rounding 154 | transit := arrival - int64(rtpTimestamp) 155 | if s.lastTransit == -1 { 156 | s.lastTransit = transit // hack for first time 157 | } 158 | d := transit - s.lastTransit 159 | s.lastTransit = transit 160 | if d < 0 { 161 | d = -d 162 | } 163 | s.jitter += (1.0 / 16.0) * (float32(d) - s.jitter) 164 | } 165 | 166 | // Return the 'presentation time' that corresponds to "rtpTimestamp": 167 | if s.syncTime.Sec == 0 && s.syncTime.Usec == 0 { 168 | // This is the first timestamp that we've seen, so use the current 169 | // 'wall clock' time as the synchronization time. (This will be 170 | // corrected later when we receive RTCP SRs.) 171 | s.syncTimestamp = rtpTimestamp 172 | s.syncTime = timeNow 173 | } 174 | 175 | timestampDiff := rtpTimestamp - s.syncTimestamp 176 | // Note: This works even if the timestamp wraps around 177 | // (as long as "int" is 32 bits) 178 | 179 | // Divide this by the timestamp frequency to get real time: 180 | timeDiff := float32(timestampDiff) / float32(timestampFrequency) 181 | 182 | // Add this to the 'sync time' to get our result: 183 | var million float32 = 1000000 184 | var seconds, uSeconds int64 185 | if timeDiff >= 0.0 { 186 | seconds = s.syncTime.Sec + int64(timeDiff) 187 | uSeconds = int64(s.syncTime.Usec) + int64((timeDiff-float32(int64(timeDiff)))*million) 188 | if uSeconds >= int64(million) { 189 | uSeconds -= int64(million) 190 | seconds++ 191 | } 192 | } else { 193 | timeDiff = -timeDiff 194 | seconds = s.syncTime.Sec - int64(timeDiff) 195 | uSeconds = int64(s.syncTime.Usec) - int64((timeDiff-float32(int64(timeDiff)))*million) 196 | if uSeconds < 0 { 197 | uSeconds += int64(million) 198 | seconds-- 199 | } 200 | } 201 | 202 | resultPresentationTime.Sec = int64(seconds) 203 | resultPresentationTime.Usec = int32(uSeconds) 204 | resultHasBeenSyncedUsingRTCP = s.hasBeenSynchronized 205 | 206 | // Save these as the new synchronization timestamp & time: 207 | s.syncTimestamp = rtpTimestamp 208 | s.syncTime = resultPresentationTime 209 | 210 | s.previousPacketRTPTimestamp = rtpTimestamp 211 | return 212 | } 213 | 214 | func (s *RTPReceptionStats) noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp uint32) { 215 | s.lastReceivedSRNTPmsw = ntpTimestampMSW 216 | s.lastReceivedSRNTPlsw = ntpTimestampLSW 217 | 218 | sys.Gettimeofday(&s.lastReceivedSRTime) 219 | 220 | // Use this SR to update time synchronization information: 221 | s.syncTimestamp = rtpTimestamp 222 | s.syncTime.Sec = int64(ntpTimestampMSW - 0x83AA7E80) // 1/1/1900 -> 1/1/1970 223 | microseconds := float32((ntpTimestampLSW * 15625.0) / 0x04000000) // 10^6/2^32 224 | s.syncTime.Usec = int32(microseconds + 0.5) 225 | s.hasBeenSynchronized = true 226 | } 227 | 228 | ////////// RTPReceptionStatsDB ////////// 229 | type RTPReceptionStatsDB struct { 230 | table map[uint32]*RTPReceptionStats 231 | totNumPacketsReceived uint32 232 | numActiveSourcesSinceLastReset uint32 233 | } 234 | 235 | func newRTPReceptionStatsDB() *RTPReceptionStatsDB { 236 | return &RTPReceptionStatsDB{ 237 | table: make(map[uint32]*RTPReceptionStats), 238 | } 239 | } 240 | 241 | func (d *RTPReceptionStatsDB) add(ssrc uint32, s *RTPReceptionStats) { 242 | d.table[ssrc] = s 243 | } 244 | 245 | func (d *RTPReceptionStatsDB) lookup(ssrc uint32) *RTPReceptionStats { 246 | s, _ := d.table[ssrc] 247 | return s 248 | } 249 | 250 | func (d *RTPReceptionStatsDB) noteIncomingPacket(ssrc, seqNum, 251 | rtpTimestamp, timestampFrequency, packetSize uint32, 252 | useForJitterCalculation bool) (presentationTime sys.Timeval, hasBeenSyncedUsingRTCP bool) { 253 | d.totNumPacketsReceived++ 254 | 255 | s := d.lookup(ssrc) 256 | if s == nil { 257 | s = newRTPReceptionStats(ssrc, seqNum) 258 | if s == nil { 259 | return 260 | } 261 | 262 | d.add(ssrc, s) 263 | } 264 | 265 | if s.numPacketsReceivedSinceLastReset == 0 { 266 | d.numActiveSourcesSinceLastReset++ 267 | } 268 | 269 | presentationTime, hasBeenSyncedUsingRTCP = s.noteIncomingPacket(seqNum, rtpTimestamp, 270 | timestampFrequency, packetSize, useForJitterCalculation) 271 | return 272 | } 273 | 274 | func (d *RTPReceptionStatsDB) noteIncomingSR(ssrc, ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp uint32) { 275 | s := d.lookup(ssrc) 276 | if s == nil { 277 | s = newRTPReceptionStats(ssrc, 0) 278 | if s == nil { 279 | return 280 | } 281 | d.table[ssrc] = s 282 | } 283 | s.noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp) 284 | } 285 | -------------------------------------------------------------------------------- /livemedia/rtp_reception_stats_linux_386.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import sys "syscall" 4 | 5 | ////////// RTPReceptionStats ////////// 6 | const MILLION = 1000000 7 | 8 | type RTPReceptionStats struct { 9 | SSRC uint32 10 | syncTimestamp uint32 11 | totBytesReceived_hi uint32 12 | totBytesReceived_lo uint32 13 | minInterPacketGapUS uint32 14 | maxInterPacketGapUS uint32 15 | lastReceivedSR_NTPmsw uint32 16 | lastReceivedSR_NTPlsw uint32 17 | baseExtSeqNumReceived uint32 18 | totNumPacketsReceived uint32 // for all SSRCs 19 | highestExtSeqNumReceived uint32 20 | previousPacketRTPTimestamp uint32 21 | lastResetExtSeqNumReceived uint32 22 | numPacketsReceivedSinceLastReset uint32 23 | syncTime sys.Timeval 24 | lastReceivedSR_time sys.Timeval 25 | lastPacketReceptionTime sys.Timeval 26 | totalInterPacketGaps sys.Timeval 27 | hasBeenSynchronized bool 28 | haveSeenInitialSequenceNumber bool 29 | lastTransit int 30 | jitter float32 31 | } 32 | 33 | func NewRTPReceptionStats(SSRC, seqNum uint32) *RTPReceptionStats { 34 | stats := new(RTPReceptionStats) 35 | stats.init(SSRC) 36 | stats.initSeqNum(seqNum) 37 | return stats 38 | } 39 | 40 | func (stats *RTPReceptionStats) init(SSRC uint32) { 41 | stats.SSRC = SSRC 42 | stats.totNumPacketsReceived = 0 43 | stats.totBytesReceived_hi = 0 44 | stats.totBytesReceived_lo = 0 45 | stats.haveSeenInitialSequenceNumber = false 46 | stats.lastTransit = -1 47 | stats.previousPacketRTPTimestamp = 0 48 | stats.jitter = 0.0 49 | stats.lastReceivedSR_NTPmsw = 0 50 | stats.lastReceivedSR_NTPlsw = 0 51 | stats.lastReceivedSR_time.Sec = 0 52 | stats.lastReceivedSR_time.Usec = 0 53 | stats.lastPacketReceptionTime.Sec = 0 54 | stats.lastPacketReceptionTime.Usec = 0 55 | stats.minInterPacketGapUS = 0x7FFFFFFF 56 | stats.maxInterPacketGapUS = 0 57 | stats.totalInterPacketGaps.Sec = 0 58 | stats.totalInterPacketGaps.Usec = 0 59 | stats.hasBeenSynchronized = false 60 | stats.syncTime.Sec = 0 61 | stats.syncTime.Usec = 0 62 | stats.reset() 63 | } 64 | 65 | func (stats *RTPReceptionStats) reset() { 66 | stats.numPacketsReceivedSinceLastReset = 0 67 | stats.lastResetExtSeqNumReceived = stats.highestExtSeqNumReceived 68 | } 69 | 70 | func (stats *RTPReceptionStats) initSeqNum(initialSeqNum uint32) { 71 | stats.baseExtSeqNumReceived = 0x10000 | initialSeqNum 72 | stats.highestExtSeqNumReceived = 0x10000 | initialSeqNum 73 | stats.haveSeenInitialSequenceNumber = true 74 | } 75 | 76 | func (stats *RTPReceptionStats) noteIncomingPacket(seqNum, rtpTimestamp, timestampFrequency, packetSize uint32, 77 | useForJitterCalculation bool) (resultPresentationTime sys.Timeval, resultHasBeenSyncedUsingRTCP bool) { 78 | if !stats.haveSeenInitialSequenceNumber { 79 | stats.initSeqNum(seqNum) 80 | } 81 | 82 | stats.numPacketsReceivedSinceLastReset++ 83 | stats.totNumPacketsReceived++ 84 | 85 | prevTotBytesReceived_lo := stats.totBytesReceived_lo 86 | stats.totBytesReceived_lo += packetSize 87 | if stats.totBytesReceived_lo < prevTotBytesReceived_lo { // wrap-around 88 | stats.totBytesReceived_hi++ 89 | } 90 | 91 | // Check whether the new sequence number is the highest yet seen: 92 | oldSeqNum := (stats.highestExtSeqNumReceived & 0xFFFF) 93 | seqNumCycle := (stats.highestExtSeqNumReceived & 0xFFFF0000) 94 | seqNumDifference := seqNum - oldSeqNum 95 | var newSeqNum uint32 96 | 97 | if seqNumLT(int(oldSeqNum), int(seqNum)) { 98 | // This packet was not an old packet received out of order, so check it: 99 | 100 | if seqNumDifference >= 0x8000 { 101 | // The sequence number wrapped around, so start a new cycle: 102 | seqNumCycle += 0x10000 103 | } 104 | 105 | newSeqNum = seqNumCycle | seqNum 106 | if newSeqNum > stats.highestExtSeqNumReceived { 107 | stats.highestExtSeqNumReceived = newSeqNum 108 | } 109 | } else if stats.totNumPacketsReceived > 1 { 110 | // This packet was an old packet received out of order 111 | 112 | if seqNumDifference >= 0x8000 { 113 | // The sequence number wrapped around, so switch to an old cycle: 114 | seqNumCycle -= 0x10000 115 | } 116 | 117 | newSeqNum = seqNumCycle | seqNum 118 | if newSeqNum < stats.baseExtSeqNumReceived { 119 | stats.baseExtSeqNumReceived = newSeqNum 120 | } 121 | } 122 | 123 | // Record the inter-packet delay 124 | var timeNow sys.Timeval 125 | sys.Gettimeofday(&timeNow) 126 | if stats.lastPacketReceptionTime.Sec != 0 || 127 | stats.lastPacketReceptionTime.Usec != 0 { 128 | gap := (timeNow.Sec-stats.lastPacketReceptionTime.Sec)*MILLION + 129 | timeNow.Usec - stats.lastPacketReceptionTime.Usec 130 | if gap > int32(stats.maxInterPacketGapUS) { 131 | stats.maxInterPacketGapUS = uint32(gap) 132 | } 133 | if gap < int32(stats.minInterPacketGapUS) { 134 | stats.minInterPacketGapUS = uint32(gap) 135 | } 136 | stats.totalInterPacketGaps.Usec += gap 137 | if stats.totalInterPacketGaps.Usec >= MILLION { 138 | stats.totalInterPacketGaps.Sec++ 139 | stats.totalInterPacketGaps.Usec -= MILLION 140 | } 141 | } 142 | stats.lastPacketReceptionTime = timeNow 143 | 144 | // Compute the current 'jitter' using the received packet's RTP timestamp, 145 | // and the RTP timestamp that would correspond to the current time. 146 | // (Use the code from appendix A.8 in the RTP spec.) 147 | // Note, however, that we don't use this packet if its timestamp is 148 | // the same as that of the previous packet (this indicates a multi-packet 149 | // fragment), or if we've been explicitly told not to use this packet. 150 | if useForJitterCalculation && rtpTimestamp != stats.previousPacketRTPTimestamp { 151 | arrival := int32(timestampFrequency) * timeNow.Sec 152 | arrival += ((2.0*int32(timestampFrequency)*timeNow.Usec + 1000000.0) / 2000000) 153 | // note: rounding 154 | transit := arrival - int32(rtpTimestamp) 155 | if stats.lastTransit == -1 { 156 | stats.lastTransit = int(transit) // hack for first time 157 | } 158 | d := transit - int32(stats.lastTransit) 159 | stats.lastTransit = int(transit) 160 | if d < 0 { 161 | d = -d 162 | } 163 | stats.jitter += (1.0 / 16.0) * (float32(d) - stats.jitter) 164 | } 165 | 166 | // Return the 'presentation time' that corresponds to "rtpTimestamp": 167 | if stats.syncTime.Sec == 0 && stats.syncTime.Usec == 0 { 168 | // This is the first timestamp that we've seen, so use the current 169 | // 'wall clock' time as the synchronization time. (This will be 170 | // corrected later when we receive RTCP SRs.) 171 | stats.syncTimestamp = rtpTimestamp 172 | stats.syncTime = timeNow 173 | } 174 | 175 | timestampDiff := rtpTimestamp - stats.syncTimestamp 176 | // Note: This works even if the timestamp wraps around 177 | // (as long as "int" is 32 bits) 178 | 179 | // Divide this by the timestamp frequency to get real time: 180 | timeDiff := float32(timestampDiff) / float32(timestampFrequency) 181 | 182 | // Add this to the 'sync time' to get our result: 183 | var million float32 = 1000000 184 | var seconds, uSeconds int32 185 | if timeDiff >= 0.0 { 186 | seconds = stats.syncTime.Sec + int32(timeDiff) 187 | uSeconds = stats.syncTime.Usec + int32((timeDiff-float32(int32(timeDiff)))*million) 188 | if uSeconds >= int32(million) { 189 | uSeconds -= int32(million) 190 | seconds++ 191 | } 192 | } else { 193 | timeDiff = -timeDiff 194 | seconds = stats.syncTime.Sec - int32(timeDiff) 195 | uSeconds = stats.syncTime.Usec - int32((timeDiff-float32(int32(timeDiff)))*million) 196 | if uSeconds < 0 { 197 | uSeconds += int32(million) 198 | seconds-- 199 | } 200 | } 201 | 202 | resultPresentationTime.Sec = int32(seconds) 203 | resultPresentationTime.Usec = int32(uSeconds) 204 | resultHasBeenSyncedUsingRTCP = stats.hasBeenSynchronized 205 | 206 | // Save these as the new synchronization timestamp & time: 207 | stats.syncTimestamp = rtpTimestamp 208 | stats.syncTime = resultPresentationTime 209 | 210 | stats.previousPacketRTPTimestamp = rtpTimestamp 211 | return 212 | } 213 | 214 | func (stats *RTPReceptionStats) noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp uint32) { 215 | stats.lastReceivedSR_NTPmsw = ntpTimestampMSW 216 | stats.lastReceivedSR_NTPlsw = ntpTimestampLSW 217 | 218 | sys.Gettimeofday(&stats.lastReceivedSR_time) 219 | 220 | // Use this SR to update time synchronization information: 221 | stats.syncTimestamp = rtpTimestamp 222 | stats.syncTime.Sec = int32(ntpTimestampMSW - 0x83AA7E80) // 1/1/1900 -> 1/1/1970 223 | microseconds := float32((ntpTimestampLSW * 15625.0) / 0x04000000) // 10^6/2^32 224 | stats.syncTime.Usec = int32(microseconds + 0.5) 225 | stats.hasBeenSynchronized = true 226 | } 227 | 228 | func (stats *RTPReceptionStats) NumPacketsReceivedSinceLastReset() uint32 { 229 | return stats.numPacketsReceivedSinceLastReset 230 | } 231 | 232 | ////////// RTPReceptionStatsDB ////////// 233 | 234 | type RTPReceptionStatsDB struct { 235 | table map[uint32]*RTPReceptionStats 236 | totNumPacketsReceived uint32 237 | numActiveSourcesSinceLastReset uint32 238 | } 239 | 240 | func NewRTPReceptionStatsDB() *RTPReceptionStatsDB { 241 | statsDB := new(RTPReceptionStatsDB) 242 | statsDB.table = make(map[uint32]*RTPReceptionStats) 243 | return statsDB 244 | } 245 | 246 | func (statsDB *RTPReceptionStatsDB) add(SSRC uint32, stats *RTPReceptionStats) { 247 | statsDB.table[SSRC] = stats 248 | } 249 | 250 | func (statsDB *RTPReceptionStatsDB) lookup(SSRC uint32) *RTPReceptionStats { 251 | stats, _ := statsDB.table[SSRC] 252 | return stats 253 | } 254 | 255 | func (statsDB *RTPReceptionStatsDB) noteIncomingPacket(SSRC, seqNum, 256 | rtpTimestamp, timestampFrequency, packetSize uint32, 257 | useForJitterCalculation bool) (presentationTime sys.Timeval, hasBeenSyncedUsingRTCP bool) { 258 | statsDB.totNumPacketsReceived++ 259 | 260 | stats := statsDB.lookup(SSRC) 261 | if stats == nil { 262 | stats = NewRTPReceptionStats(SSRC, seqNum) 263 | if stats == nil { 264 | return 265 | } 266 | 267 | statsDB.add(SSRC, stats) 268 | } 269 | 270 | if stats.NumPacketsReceivedSinceLastReset() == 0 { 271 | statsDB.numActiveSourcesSinceLastReset++ 272 | } 273 | 274 | presentationTime, hasBeenSyncedUsingRTCP = stats.noteIncomingPacket(seqNum, rtpTimestamp, 275 | timestampFrequency, packetSize, useForJitterCalculation) 276 | return 277 | } 278 | 279 | func (statsDB *RTPReceptionStatsDB) noteIncomingSR(SSRC, ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp uint32) { 280 | stats := statsDB.lookup(SSRC) 281 | if stats == nil { 282 | stats = NewRTPReceptionStats(SSRC, 0) 283 | if stats == nil { 284 | return 285 | } 286 | statsDB.table[SSRC] = stats 287 | } 288 | stats.noteIncomingSR(ntpTimestampMSW, ntpTimestampLSW, rtpTimestamp) 289 | } 290 | -------------------------------------------------------------------------------- /livemedia/rtcp_linux_386.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | sys "syscall" 5 | 6 | gs "github.com/djwackey/dorsvr/groupsock" 7 | "github.com/djwackey/gitea/log" 8 | ) 9 | 10 | const ( 11 | // RTCP packet types: 12 | RTCP_PT_SR = 200 13 | RTCP_PT_RR = 201 14 | RTCP_PT_SDES = 202 15 | RTCP_PT_BYE = 203 16 | RTCP_PT_APP = 204 17 | 18 | // SDES tags: 19 | RTCP_SDES_END = 0 20 | RTCP_SDES_CNAME = 1 21 | RTCP_SDES_NAME = 2 22 | RTCP_SDES_EMAIL = 3 23 | RTCP_SDES_PHONE = 4 24 | RTCP_SDES_LOC = 5 25 | RTCP_SDES_TOOL = 6 26 | RTCP_SDES_NOTE = 7 27 | RTCP_SDES_PRIV = 8 28 | 29 | // overhead (bytes) of IP and UDP hdrs 30 | IP_UDP_HDR_SIZE = 28 31 | 32 | PACKET_UNKNOWN_TYPE = 0 33 | PAKCET_RTP = 1 34 | PACKET_RTCP_REPORT = 2 35 | PACKET_BYE = 3 36 | ) 37 | 38 | type SDESItem struct { 39 | data []byte 40 | } 41 | 42 | // bytes, (1500, minus some allowance for IP, UDP, UMTP headers) 43 | const ( 44 | maxRTCPPacketSize uint = 1450 45 | preferredPacketSize uint = 1000 // bytes 46 | ) 47 | 48 | type RTCPInstance struct { 49 | typeOfEvent uint 50 | lastSentSize uint 51 | totSessionBW uint 52 | lastPacketSentSize uint 53 | haveJustSentPacket bool 54 | prevReportTime int32 55 | nextReportTime int32 56 | inBuf []byte 57 | CNAME *SDESItem 58 | Sink *RTPSink 59 | Source *RTPSource 60 | outBuf *OutPacketBuffer 61 | netInterface *RTPInterface 62 | byeHandlerTask interface{} 63 | SRHandlerTask interface{} 64 | RRHandlerTask interface{} 65 | byeHandlerClientData interface{} 66 | } 67 | 68 | func newSDESItem(tag int, value string) *SDESItem { 69 | length := len(value) 70 | if length > 0xFF { 71 | length = 0xFF // maximum data length for a SDES item 72 | } 73 | 74 | return &SDESItem{ 75 | data: []byte{byte(tag), byte(length)}, 76 | } 77 | } 78 | 79 | func dTimeNow() int32 { 80 | var timeNow sys.Timeval 81 | sys.Gettimeofday(&timeNow) 82 | return timeNow.Sec + timeNow.Usec/1000000.0 83 | } 84 | 85 | func (s *SDESItem) totalSize() uint { 86 | return 2 + uint(s.data[1]) 87 | } 88 | 89 | func newRTCPInstance(rtcpGS *gs.GroupSock, totSessionBW uint, cname string) *RTCPInstance { 90 | // saved OutPacketBuffer's max size temporarily 91 | savedMaxSize := OutPacketBufferMaxSize 92 | OutPacketBufferMaxSize = maxRTCPPacketSize 93 | 94 | reportTime := dTimeNow() 95 | rtcp := &RTCPInstance{ 96 | typeOfEvent: EVENT_REPORT, 97 | totSessionBW: totSessionBW, 98 | prevReportTime: reportTime, 99 | nextReportTime: reportTime, 100 | CNAME: newSDESItem(RTCP_SDES_CNAME, cname), 101 | outBuf: newOutPacketBuffer(preferredPacketSize, maxRTCPPacketSize), 102 | inBuf: make([]byte, maxRTCPPacketSize), 103 | } 104 | // resume common OutPacketBuffer's max size 105 | OutPacketBufferMaxSize = savedMaxSize 106 | 107 | if rtcp.totSessionBW == 0 { 108 | log.Warn("[newRTCPInstance] totSessionBW can't be zero!") 109 | rtcp.totSessionBW = 1 110 | } 111 | 112 | rtcp.netInterface = newRTPInterface(rtcp, rtcpGS) 113 | rtcp.netInterface.startNetworkReading(rtcp.incomingReportHandler) 114 | 115 | rtcp.onExpire() 116 | return rtcp 117 | } 118 | 119 | func (r *RTCPInstance) numMembers() uint { 120 | return 0 121 | } 122 | 123 | func (r *RTCPInstance) setSpecificRRHandler(handlerTask interface{}) { 124 | } 125 | 126 | func (r *RTCPInstance) SetByeHandler(handlerTask interface{}, clientData interface{}) { 127 | r.byeHandlerTask = handlerTask 128 | r.byeHandlerClientData = clientData 129 | } 130 | 131 | func (r *RTCPInstance) setSRHandler(handlerTask interface{}, clientData interface{}) { 132 | r.SRHandlerTask = handlerTask 133 | } 134 | 135 | func (r *RTCPInstance) setRRHandler(handlerTask interface{}, clientData interface{}) { 136 | r.RRHandlerTask = handlerTask 137 | } 138 | 139 | func (r *RTCPInstance) incomingReportHandler() { 140 | var callByeHandler bool 141 | for { 142 | readBytes, err := r.netInterface.handleRead(r.inBuf) 143 | if err != nil { 144 | log.Warn("RTCP Interface failed to handle read.%s", err.Error()) 145 | break 146 | } 147 | 148 | packet, packetSize := r.inBuf[:readBytes], uint(readBytes) 149 | 150 | totPacketSize := IP_UDP_HDR_SIZE + packetSize 151 | 152 | if packetSize < 4 { 153 | log.Warn("RTCP Interface packet Size less than 4.") 154 | continue 155 | } 156 | 157 | var rtcpHdr uint32 158 | rtcpHdr, _ = gs.Ntohl(packet) 159 | 160 | if (rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR << 16)) { 161 | log.Warn("rejected bad RTCP packet: header 0x%08x", rtcpHdr) 162 | continue 163 | } 164 | 165 | typeOfPacket := PACKET_UNKNOWN_TYPE 166 | var packetOk bool 167 | var reportSenderSSRC uint32 168 | 169 | for { 170 | rc := (rtcpHdr >> 24) & 0x1F 171 | pt := (rtcpHdr >> 16) & 0xFF 172 | // doesn't count hdr 173 | length := uint(4 * (rtcpHdr & 0xFFFF)) 174 | // skip over the header 175 | packet, packetSize = ADVANCE(packet, packetSize, 4) 176 | if length > packetSize { 177 | break 178 | } 179 | 180 | // Assume that each RTCP subpacket begins with a 4-byte SSRC: 181 | if length < 4 { 182 | break 183 | } 184 | length -= 4 185 | 186 | reportSenderSSRC, _ = gs.Ntohl(packet) 187 | 188 | packet, packetSize = ADVANCE(packet, packetSize, 4) 189 | 190 | var subPacketOk bool 191 | switch pt { 192 | case RTCP_PT_SR: 193 | if length >= 20 { 194 | length -= 20 195 | 196 | // Extract the NTP timestamp, and note this: 197 | NTPmsw, _ := gs.Ntohl(packet) 198 | packet, packetSize = ADVANCE(packet, packetSize, 4) 199 | 200 | NTPlsm, _ := gs.Ntohl(packet) 201 | packet, packetSize = ADVANCE(packet, packetSize, 4) 202 | 203 | rtpTimestamp, _ := gs.Ntohl(packet) 204 | packet, packetSize = ADVANCE(packet, packetSize, 4) 205 | 206 | if r.Source != nil { 207 | r.Source.receptionStatsDB.noteIncomingSR(reportSenderSSRC, NTPmsw, NTPlsm, rtpTimestamp) 208 | } 209 | 210 | packet, packetSize = ADVANCE(packet, packetSize, 8) 211 | 212 | // If a 'SR handler' was set, call it now: 213 | if r.SRHandlerTask != nil { 214 | r.SRHandlerTask.(func())() 215 | } 216 | } 217 | 218 | fallthrough 219 | case RTCP_PT_RR: 220 | reportBlocksSize := uint(rc * (6 * 4)) 221 | if length >= reportBlocksSize { 222 | length -= reportBlocksSize 223 | 224 | if r.Sink != nil { 225 | } else { 226 | packet, packetSize = ADVANCE(packet, packetSize, reportBlocksSize) 227 | } 228 | 229 | if pt == RTCP_PT_RR { 230 | log.Info("RTCP_PT_RR") 231 | if r.RRHandlerTask != nil { 232 | r.RRHandlerTask.(func())() 233 | } 234 | } 235 | 236 | subPacketOk = true 237 | typeOfPacket = PACKET_RTCP_REPORT 238 | } 239 | 240 | case RTCP_PT_BYE: 241 | log.Info("RTCP_PT_BYE") 242 | callByeHandler = true 243 | 244 | subPacketOk = true 245 | typeOfPacket = PACKET_BYE 246 | default: 247 | subPacketOk = true 248 | } 249 | 250 | if !subPacketOk { 251 | break 252 | } 253 | 254 | packet, packetSize = ADVANCE(packet, packetSize, length) 255 | 256 | if packetSize == 0 { 257 | packetOk = true 258 | break 259 | } else if packetSize < 4 { 260 | log.Error(1, "extraneous %d bytes at end of RTCP packet!", packetSize) 261 | break 262 | } 263 | 264 | rtcpHdr, _ = gs.Ntohl(packet) 265 | 266 | if (rtcpHdr & 0xC0000000) != 0x80000000 { 267 | log.Error(1, "bad RTCP subpacket: header 0x%08x", rtcpHdr) 268 | break 269 | } 270 | } 271 | 272 | if !packetOk { 273 | log.Warn("rejected bad RTCP subpacket: header 0x%08x", rtcpHdr) 274 | continue 275 | } else { 276 | log.Info("validated entire RTCP packet") 277 | } 278 | 279 | r.onReceive(typeOfPacket, totPacketSize, uint(reportSenderSSRC)) 280 | 281 | if callByeHandler && r.byeHandlerTask != nil { 282 | r.byeHandlerTask.(func(subsession *MediaSubsession))(r.byeHandlerClientData.(*MediaSubsession)) 283 | } 284 | } 285 | log.Info("incomingReportHandler ending.") 286 | } 287 | 288 | func (r *RTCPInstance) onReceive(typeOfPacket int, totPacketSize, ssrc uint) { 289 | OnReceive() 290 | } 291 | 292 | func (r *RTCPInstance) sendReport() { 293 | // Begin by including a SR and/or RR report: 294 | r.addReport() 295 | 296 | // Then, include a SDES: 297 | r.addSDES() 298 | 299 | // Send the report: 300 | r.sendBuiltPacket() 301 | } 302 | 303 | func (r *RTCPInstance) sendBye() { 304 | r.addReport() 305 | 306 | r.addBYE() 307 | r.sendBuiltPacket() 308 | } 309 | 310 | func (r *RTCPInstance) sendBuiltPacket() { 311 | reportSize := r.outBuf.curPacketSize() 312 | r.netInterface.sendPacket(r.outBuf.packet(), reportSize) 313 | r.outBuf.resetOffset() 314 | 315 | r.lastSentSize = uint(IP_UDP_HDR_SIZE) + reportSize 316 | r.haveJustSentPacket = true 317 | r.lastPacketSentSize = reportSize 318 | } 319 | 320 | func (r *RTCPInstance) addReport() { 321 | if r.Sink != nil { 322 | if r.Sink.enableRTCPReports { 323 | return 324 | } 325 | 326 | if r.Sink.nextTimestampHasBeenPreset { 327 | return 328 | } 329 | 330 | r.addSR() 331 | } else if r.Source != nil { 332 | r.addRR() 333 | } 334 | } 335 | 336 | func (r *RTCPInstance) addSDES() { 337 | var numBytes uint = 4 338 | numBytes += r.CNAME.totalSize() 339 | numBytes += 1 340 | 341 | num4ByteWords := (numBytes + 3) / 4 342 | 343 | var rtcpHdr uint32 = 0x81000000 // version 2, no padding, 1 SSRC chunk 344 | rtcpHdr |= (RTCP_PT_SDES << 16) 345 | rtcpHdr |= uint32(num4ByteWords) 346 | r.outBuf.enqueueWord(rtcpHdr) 347 | } 348 | 349 | func (r *RTCPInstance) addBYE() { 350 | var rtcpHdr uint32 = 0x81000000 351 | rtcpHdr |= RTCP_PT_BYE << 16 352 | rtcpHdr |= 1 353 | r.outBuf.enqueueWord(rtcpHdr) 354 | 355 | if r.Source != nil { 356 | r.outBuf.enqueueWord(uint32(r.Source.ssrc)) 357 | } else if r.Sink != nil { 358 | r.outBuf.enqueueWord(uint32(r.Sink.ssrc)) 359 | } 360 | } 361 | 362 | func (r *RTCPInstance) addSR() { 363 | r.enqueueCommonReportPrefix(RTCP_PT_SR, r.Source.ssrc, 0) 364 | r.enqueueCommonReportSuffix() 365 | } 366 | 367 | func (r *RTCPInstance) addRR() { 368 | r.enqueueCommonReportPrefix(RTCP_PT_RR, r.Source.ssrc, 0) 369 | r.enqueueCommonReportSuffix() 370 | } 371 | 372 | func (r *RTCPInstance) onExpire() { 373 | // Note: totsessionbw is kbits per second 374 | var rtcpBW float32 = 0.05 * float32(r.totSessionBW) * 1024 / 8 375 | 376 | var senders uint 377 | if r.Sink != nil { 378 | senders = 1 379 | } 380 | 381 | OnExpire(r, r.numMembers(), senders, rtcpBW) 382 | } 383 | 384 | func (r *RTCPInstance) unsetSpecificRRHandler() { 385 | } 386 | 387 | func (r *RTCPInstance) enqueueCommonReportPrefix(packetType, ssrc, numExtraWords uint32) { 388 | } 389 | 390 | func (r *RTCPInstance) enqueueCommonReportSuffix() { 391 | } 392 | 393 | func (r *RTCPInstance) destroy() { 394 | r.netInterface.stopNetworkReading() 395 | 396 | r.sendBye() 397 | } 398 | -------------------------------------------------------------------------------- /livemedia/common.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | const maxCommandNum = 9 10 | 11 | // Handler routines for specific RTSP commands: 12 | var AllowedCommandNames [maxCommandNum]string = [maxCommandNum]string{ 13 | "OPTIONS", 14 | "DESCRIBE", 15 | "SETUP", 16 | "TEARDOWN", 17 | "PLAY", 18 | "PAUSE", 19 | "RECORD", 20 | "GET_PARAMETER", 21 | "SET_PARAMETER", 22 | } 23 | 24 | type RTSPRequestInfo struct { 25 | Cseq string 26 | CmdName string 27 | SessionIDStr string 28 | UrlPreSuffix string 29 | UrlSuffix string 30 | ContentLength string 31 | } 32 | 33 | type HTTPRequestInfo struct { 34 | CmdName string 35 | UrlPreSuffix string 36 | UrlSuffix string 37 | AcceptStr string 38 | SessionCookie string 39 | } 40 | 41 | type TransportHeader struct { 42 | StreamingMode uint 43 | ClientRTPPortNum uint 44 | ClientRTCPPortNum uint 45 | RTPChannelID uint 46 | RTCPChannelID uint 47 | DestinationTTL uint 48 | DestinationAddr string 49 | StreamingModeStr string 50 | } 51 | 52 | type RangeHeader struct { 53 | RangeStart float32 54 | RangeEnd float32 55 | AbsStartTime string 56 | AbsEndTime string 57 | } 58 | 59 | const ( 60 | RTP_UDP = iota 61 | RTP_TCP 62 | RAW_UDP 63 | ) 64 | 65 | func ParseRTSPRequestString(reqStr string, reqStrSize int) (*RTSPRequestInfo, bool) { 66 | reqInfo := new(RTSPRequestInfo) 67 | 68 | // Read everything up to the first space as the command name: 69 | i := 0 70 | for i = 0; i < reqStrSize && reqStr[i] != ' ' && reqStr[i] != '\t'; i++ { 71 | reqInfo.CmdName += string(reqStr[i]) 72 | } 73 | if i >= reqStrSize { 74 | return nil, false // parse failed 75 | } 76 | 77 | // skip over any additional white space 78 | j := i + 1 79 | for ; j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t'); j++ { 80 | } 81 | for ; j < reqStrSize-8; j++ { 82 | if (reqStr[j+0] == 'r' || reqStr[j+0] == 'R') && 83 | (reqStr[j+1] == 't' || reqStr[j+1] == 'T') && 84 | (reqStr[j+2] == 's' || reqStr[j+2] == 'S') && 85 | (reqStr[j+3] == 'p' || reqStr[j+3] == 'P') && 86 | reqStr[j+4] == ':' && reqStr[j+5] == '/' { 87 | j += 6 88 | if reqStr[j] == '/' { 89 | j++ 90 | for ; j < reqStrSize && reqStr[j] != '/' && reqStr[j] != ' '; j++ { 91 | } 92 | } else { 93 | j-- 94 | } 95 | i = j 96 | break 97 | } 98 | } 99 | 100 | // Look for the URL suffix 101 | for k := i + 1; k < reqStrSize-5; k++ { 102 | if reqStr[k+0] == 'R' && 103 | reqStr[k+1] == 'T' && 104 | reqStr[k+2] == 'S' && 105 | reqStr[k+3] == 'P' && 106 | reqStr[k+4] == '/' { 107 | for k--; k >= i && reqStr[k] == ' '; k-- { 108 | } 109 | k1 := k 110 | for ; k1 > i && reqStr[k1] != '/'; k1-- { 111 | } 112 | 113 | k2 := k1 + 1 114 | if i <= k { 115 | for ; k2 <= k; k2++ { 116 | reqInfo.UrlSuffix += string(reqStr[k2]) 117 | } 118 | } 119 | k2 = i + 1 120 | if i <= k { 121 | for ; k2 <= k1-1; k2++ { 122 | reqInfo.UrlPreSuffix += string(reqStr[k2]) 123 | } 124 | } 125 | 126 | i = k + 7 127 | break 128 | } 129 | } 130 | 131 | // Look for "CSeq:" 132 | for j = i; j < reqStrSize-5; j++ { 133 | if strings.EqualFold("CSeq:", reqStr[j:j+5]) { 134 | j += 5 135 | for ; j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t'); j++ { 136 | } 137 | for ; reqStr[j] != '\r' && reqStr[j] != '\n'; j++ { 138 | reqInfo.Cseq += string(reqStr[j]) 139 | } 140 | } 141 | } 142 | 143 | // Look for "Session:" 144 | for j = i; j < reqStrSize-8; j++ { 145 | if strings.EqualFold("Session:", reqStr[j:j+8]) { 146 | j += 8 147 | for ; j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t'); j++ { 148 | } 149 | for ; reqStr[j] != '\r' && reqStr[j] != '\n'; j++ { 150 | reqInfo.SessionIDStr += string(reqStr[j]) 151 | } 152 | } 153 | } 154 | 155 | // Also: Look for "Content-Length:" (optional, case insensitive) 156 | for j = i; j < reqStrSize-15; j++ { 157 | if strings.EqualFold("Content-Length:", reqStr[j:j+15]) { 158 | j += 15 159 | for ; j < reqStrSize && (reqStr[j] == ' ' || reqStr[j] == '\t'); j++ { 160 | } 161 | if n, _ := fmt.Sscanf(reqStr[j:j+15], "%d", &reqInfo.ContentLength); n == 1 { 162 | break 163 | } 164 | } 165 | } 166 | 167 | return reqInfo, true 168 | } 169 | 170 | func ParseHTTPRequestString(reqStr string, reqStrSize int) (*HTTPRequestInfo, bool) { 171 | var cmdName, acceptStr, sessionCookie string 172 | 173 | i := 0 174 | for i = 0; i < reqStrSize && reqStr[i] != ' ' && reqStr[i] != '\t'; i++ { 175 | cmdName += string(reqStr[i]) 176 | } 177 | if i >= reqStrSize { 178 | return nil, false // parse failed 179 | } 180 | 181 | // Look for the string "HTTP/", before the first \r or \n: 182 | for ; i < reqStrSize && (reqStr[i] == '\r' || reqStr[i] == '\n'); i++ { 183 | if reqStr[i+0] == 'H' && 184 | reqStr[i+1] == 'T' && 185 | reqStr[i+2] == 'T' && 186 | reqStr[i+3] == 'P' && 187 | reqStr[i+4] == '/' { 188 | i += 5 189 | break 190 | } 191 | } 192 | 193 | // Look for various headers that we're interested in: 194 | sessionCookie, _ = lookForHeader("x-sessioncookie", reqStr[i:], reqStrSize-i) 195 | acceptStr, _ = lookForHeader("Accept", reqStr[i:], reqStrSize-i) 196 | return &HTTPRequestInfo{ 197 | CmdName: cmdName, 198 | AcceptStr: acceptStr, 199 | SessionCookie: sessionCookie, 200 | }, true 201 | } 202 | 203 | func lookForHeader(headerName, source string, sourceLen int) (resultStr string, resultMaxSize int) { 204 | headerNameLen := len(headerName) 205 | for i := 0; i < (sourceLen - headerNameLen); i++ { 206 | if strings.EqualFold(source[i:], headerName) && source[i+headerNameLen] == ':' { 207 | for i += headerNameLen + 1; i < sourceLen && (source[i] == ' ' || source[i] == '\t'); i++ { 208 | } 209 | for j := i; j < sourceLen; j++ { 210 | if source[j] == '\r' || source[j] == '\n' { 211 | resultStr = string(source[i:]) 212 | break 213 | } 214 | } 215 | } 216 | } 217 | return resultStr, resultMaxSize 218 | } 219 | 220 | func ParseTransportHeader(reqStr string) *TransportHeader { 221 | // Initialize the result parameters to default values: 222 | header := &TransportHeader{ 223 | StreamingMode: RTP_UDP, 224 | RTPChannelID: 0xFF, 225 | RTCPChannelID: 0xFF, 226 | DestinationTTL: 255, 227 | ClientRTPPortNum: 0, 228 | ClientRTCPPortNum: 1, 229 | } 230 | 231 | for { 232 | // First, find "Transport:" 233 | index := strings.Index(reqStr, "Transport:") 234 | if index == -1 { 235 | break 236 | } 237 | 238 | var n int 239 | var p1, p2, rtpCid, rtcpCid, ttl uint 240 | 241 | tranStr := reqStr[index+10:] 242 | fields := strings.Split(tranStr, ";") 243 | 244 | for _, field := range fields { 245 | field = strings.TrimSpace(field) 246 | 247 | if strings.EqualFold(field, "RTP/AVP/TCP") { 248 | header.StreamingMode = RTP_TCP 249 | } else if strings.EqualFold(field, "RAW/RAW/UDP") || 250 | strings.EqualFold(field, "MP2T/H2221/UDP") { 251 | header.StreamingMode = RAW_UDP 252 | header.StreamingModeStr = field 253 | } else if strings.Index(field, "destination=") != -1 { 254 | header.DestinationAddr = field[12:] 255 | } else if n, _ = fmt.Sscanf(field, "ttl%d", &ttl); n == 1 { 256 | header.DestinationTTL = ttl 257 | } else if n, _ = fmt.Sscanf(field, "client_port=%d-%d", &p1, &p2); n == 2 { 258 | header.ClientRTPPortNum = p1 259 | if header.StreamingMode == RAW_UDP { 260 | header.ClientRTCPPortNum = 0 261 | } else { 262 | header.ClientRTCPPortNum = p2 263 | } 264 | } else if n, _ = fmt.Sscanf(field, "client_port=%s", &p1); n == 1 { 265 | header.ClientRTPPortNum = p1 266 | if header.StreamingMode == RAW_UDP { 267 | header.ClientRTCPPortNum = 0 268 | } else { 269 | header.ClientRTCPPortNum = p1 270 | } 271 | } else if n, _ = fmt.Sscanf(field, "interleaved=%d-%d", &rtpCid, &rtcpCid); n == 2 { 272 | header.RTPChannelID = rtpCid 273 | header.RTCPChannelID = rtcpCid 274 | } 275 | } 276 | break 277 | } 278 | 279 | return header 280 | } 281 | 282 | func parseRangeParam(paramStr string) *RangeHeader { 283 | rangeHeader := new(RangeHeader) 284 | 285 | var start, end float32 286 | var numCharsMatched int 287 | if n, _ := fmt.Sscanf(paramStr, "npt = %lf - %lf", &start, &end); n == 2 { 288 | rangeHeader.RangeStart = start 289 | rangeHeader.RangeEnd = end 290 | } else { 291 | if n, _ = fmt.Sscanf(paramStr, "npt = %lf -", &start); n == 1 { 292 | rangeHeader.RangeStart = start 293 | } else { 294 | if strings.EqualFold(paramStr, "npt = now -") { 295 | rangeHeader.RangeStart = 0.0 296 | rangeHeader.RangeEnd = 0.0 297 | } else { 298 | _, err := fmt.Sscanf(paramStr, "clock = %n", &numCharsMatched) 299 | if err != nil { 300 | return nil 301 | } 302 | 303 | if numCharsMatched > 0 { 304 | as, ae := "", "" 305 | utcTimes := string(paramStr[numCharsMatched:]) 306 | if n, _ = fmt.Sscanf(utcTimes, "%[^-]-%s", &as, &ae); n == 2 { 307 | rangeHeader.AbsStartTime = as 308 | rangeHeader.AbsEndTime = ae 309 | } else if n == 1 { 310 | rangeHeader.AbsStartTime = as 311 | } 312 | } else { 313 | fmt.Sscanf(paramStr, "smtpe = %n", &numCharsMatched) 314 | } 315 | } 316 | } 317 | } 318 | 319 | return rangeHeader 320 | } 321 | 322 | func ParseRangeHeader(buf string) (*RangeHeader, bool) { 323 | var rangeParam *RangeHeader 324 | var result bool 325 | 326 | for { 327 | // First, find "Range:" 328 | var finded bool 329 | for i := 0; i < len(buf); i++ { 330 | if strings.EqualFold(buf, "Range: ") { 331 | finded = true 332 | break 333 | } 334 | } 335 | if !finded { 336 | break 337 | } 338 | 339 | rangeParam = parseRangeParam(buf) 340 | if rangeParam == nil { 341 | break 342 | } 343 | result = true 344 | break 345 | } 346 | 347 | return rangeParam, result 348 | } 349 | 350 | func ParsePlayNowHeader(buf string) bool { 351 | // Find "x-playNow:" header, if present 352 | var finded bool 353 | index := strings.Index(buf, "x-playNow:") 354 | if index != -1 { 355 | finded = true 356 | } 357 | 358 | return finded 359 | } 360 | 361 | func ParseScaleHeader(buf string) (float32, bool) { 362 | // Initialize the result parameter to a default value: 363 | var scale float32 = 1.0 364 | var result bool 365 | for { 366 | index := strings.Index(buf, "Scale:") 367 | if index == -1 { 368 | break 369 | } 370 | 371 | fields := buf[index:] 372 | i := 0 373 | for { 374 | if string(fields[i]) != " " { 375 | break 376 | } 377 | i++ 378 | } 379 | var sc float32 380 | if n, _ := fmt.Sscanf(fields, "%f", &sc); n == 1 { 381 | scale, result = sc, true 382 | } 383 | 384 | break 385 | } 386 | 387 | return scale, result 388 | } 389 | 390 | // A "Date:" header that can be used in a RTSP (or HTTP) response 391 | func DateHeader() string { 392 | return fmt.Sprintf("Date: %s\r\n", time.Now()) 393 | } 394 | -------------------------------------------------------------------------------- /livemedia/multi_framed_rtp_sink_linux_386.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | sys "syscall" 5 | "time" 6 | 7 | gs "github.com/djwackey/dorsvr/groupsock" 8 | ) 9 | 10 | const rtpHeaderSize uint = 12 11 | 12 | type MultiFramedRTPSink struct { 13 | RTPSink 14 | outBuf *OutPacketBuffer 15 | nextSendTime sys.Timeval 16 | noFramesLeft bool 17 | isFirstPacket bool 18 | currentTimestamp uint32 19 | ourMaxPacketSize uint 20 | timestampPosition uint 21 | specialHeaderSize uint 22 | numFramesUsedSoFar uint 23 | specialHeaderPosition uint 24 | curFragmentationOffset uint 25 | curFrameSpecificHeaderSize uint 26 | totalFrameSpecificHeaderSizes uint 27 | curFrameSpecificHeaderPosition uint 28 | previousFrameEndedFragmentation bool 29 | onSendErrorFunc interface{} 30 | } 31 | 32 | func (s *MultiFramedRTPSink) InitMultiFramedRTPSink(rtpSink IMediaSink, 33 | rtpGroupSock *gs.GroupSock, rtpPayloadType, rtpTimestampFrequency uint32, rtpPayloadFormatName string) { 34 | // Default max packet size (1500, minus allowance for IP, UDP, UMTP headers) 35 | // (Also, make it a multiple of 4 bytes, just in case that matters.) 36 | s.setPacketSizes(1000, 1448) 37 | s.InitRTPSink(rtpSink, rtpGroupSock, rtpPayloadType, rtpTimestampFrequency, rtpPayloadFormatName) 38 | } 39 | 40 | func (s *MultiFramedRTPSink) setPacketSizes(preferredPacketSize, maxPacketSize uint) { 41 | s.outBuf = newOutPacketBuffer(preferredPacketSize, maxPacketSize) 42 | s.ourMaxPacketSize = maxPacketSize 43 | } 44 | 45 | func (s *MultiFramedRTPSink) multiFramedPlaying() { 46 | s.buildAndSendPacket(true) 47 | } 48 | 49 | func (s *MultiFramedRTPSink) buildAndSendPacket(isFirstPacket bool) { 50 | s.isFirstPacket = isFirstPacket 51 | 52 | // Set up the RTP header: 53 | var rtpHdr uint32 = 0x80000000 54 | rtpHdr |= s.rtpPayloadType << 16 55 | rtpHdr |= s.seqNo 56 | s.outBuf.enqueueWord(rtpHdr) 57 | 58 | s.timestampPosition = s.outBuf.curPacketSize() 59 | s.outBuf.skipBytes(4) 60 | 61 | s.outBuf.enqueueWord(s.ssrc) 62 | 63 | // Allow for a special, payload-format-specific header following the RTP header: 64 | s.specialHeaderPosition = s.outBuf.curPacketSize() 65 | s.specialHeaderSize = s.SpecialHeaderSize() 66 | s.outBuf.skipBytes(s.specialHeaderSize) 67 | 68 | // Begin packing as many (complete) frames into the packet as we can: 69 | s.noFramesLeft = false 70 | s.numFramesUsedSoFar = 0 71 | s.totalFrameSpecificHeaderSizes = 0 72 | 73 | s.packFrame() 74 | } 75 | 76 | func (s *MultiFramedRTPSink) packFrame() { 77 | if s.outBuf.haveOverflowData() { 78 | // Use this frame before reading a new one from the source 79 | frameSize := s.outBuf.overflowDataSize 80 | presentationTime := s.outBuf.overflowPresentationTime 81 | durationInMicroseconds := s.outBuf.overflowDurationInMicroseconds 82 | 83 | s.outBuf.useOverflowData() 84 | s.afterGettingFrame(frameSize, durationInMicroseconds, presentationTime) 85 | } else { 86 | // Normal case: we need to read a new frame from the source 87 | if s.Source == nil { 88 | return 89 | } 90 | 91 | s.curFrameSpecificHeaderPosition = s.outBuf.curPacketSize() 92 | s.curFrameSpecificHeaderSize = s.frameSpecificHeaderSize() 93 | s.outBuf.skipBytes(s.curFrameSpecificHeaderSize) 94 | s.totalFrameSpecificHeaderSizes += s.curFrameSpecificHeaderSize 95 | 96 | // H264FUAFragmenter 97 | s.Source.GetNextFrame(s.outBuf.curPtr(), s.outBuf.totalBytesAvailable(), 98 | s.afterGettingFrame, s.ourHandlerClosure) 99 | } 100 | } 101 | 102 | func (s *MultiFramedRTPSink) afterGettingFrame(frameSize, durationInMicroseconds uint, presentationTime sys.Timeval) { 103 | if s.isFirstPacket { 104 | // Record the fact that we're starting to play now: 105 | sys.Gettimeofday(&s.nextSendTime) 106 | } 107 | 108 | curFragmentationOffset := s.curFragmentationOffset 109 | numFrameBytesToUse := frameSize 110 | var overflowBytes uint 111 | 112 | if s.numFramesUsedSoFar > 0 { 113 | if s.previousFrameEndedFragmentation && 114 | !s.allowOtherFramesAfterLastFragment() && 115 | !s.rtpSink.frameCanAppearAfterPacketStart(s.outBuf.curPtr(), frameSize) { 116 | numFrameBytesToUse = 0 117 | s.outBuf.setOverflowData(s.outBuf.curPacketSize(), frameSize, durationInMicroseconds, presentationTime) 118 | } 119 | } 120 | s.previousFrameEndedFragmentation = false 121 | 122 | if numFrameBytesToUse > 0 { 123 | // Check whether this frame overflows the packet 124 | if s.outBuf.wouldOverflow(frameSize) { 125 | if s.isTooBigForAPacket(frameSize) && (s.numFramesUsedSoFar == 0 || s.allowOtherFramesAfterLastFragment()) { 126 | overflowBytes = s.computeOverflowForNewFrame(frameSize) 127 | numFrameBytesToUse -= overflowBytes 128 | s.curFragmentationOffset += numFrameBytesToUse 129 | } else { 130 | overflowBytes = frameSize 131 | numFrameBytesToUse = 0 132 | } 133 | s.outBuf.setOverflowData(s.outBuf.curPacketSize()+numFrameBytesToUse, overflowBytes, durationInMicroseconds, presentationTime) 134 | } else if s.curFragmentationOffset > 0 { 135 | s.curFragmentationOffset = 0 136 | s.previousFrameEndedFragmentation = true 137 | } 138 | } 139 | 140 | if numFrameBytesToUse == 0 && frameSize > 0 { 141 | // Send our packet now, because we have filled it up: 142 | s.sendPacketIfNecessary() 143 | } else { 144 | // Use this frame in our outgoing packet: 145 | frameStart := s.outBuf.curPtr() 146 | s.outBuf.increment(numFrameBytesToUse) 147 | // do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes 148 | 149 | // Here's where any payload format specific processing gets done: 150 | s.rtpSink.doSpecialFrameHandling(curFragmentationOffset, numFrameBytesToUse, overflowBytes, frameStart, presentationTime) 151 | 152 | s.numFramesUsedSoFar++ 153 | 154 | // Update the time at which the next packet should be sent, based 155 | // on the duration of the frame that we just packed into it. 156 | // However, if this frame has overflow data remaining, then don't 157 | // count its duration yet. 158 | if overflowBytes == 0 { 159 | s.nextSendTime.Usec += int32(durationInMicroseconds) 160 | s.nextSendTime.Sec += s.nextSendTime.Usec / 1000000 161 | s.nextSendTime.Usec %= 1000000 162 | } 163 | 164 | // Send our packet now if (i) it's already at our preferred size, or 165 | // (ii) (heuristic) another frame of the same size as the one we just 166 | // read would overflow the packet, or 167 | // (iii) it contains the last fragment of a fragmented frame, and we 168 | // don't allow anything else to follow this or 169 | // (iv) one frame per packet is allowed: 170 | if s.outBuf.isPreferredSize() || 171 | s.outBuf.wouldOverflow(numFrameBytesToUse) || 172 | s.previousFrameEndedFragmentation && !s.allowOtherFramesAfterLastFragment() || 173 | !s.rtpSink.frameCanAppearAfterPacketStart(s.outBuf.curPtr(), frameSize) { 174 | // The packet is ready to be sent now 175 | s.sendPacketIfNecessary() 176 | } else { 177 | // There's room for more frames; try getting another: 178 | s.packFrame() 179 | } 180 | } 181 | } 182 | 183 | func (s *MultiFramedRTPSink) isTooBigForAPacket(numBytes uint) bool { 184 | numBytes += rtpHeaderSize + s.SpecialHeaderSize() + s.frameSpecificHeaderSize() 185 | return s.outBuf.isTooBigForAPacket(numBytes) 186 | } 187 | 188 | func (s *MultiFramedRTPSink) sendPacketIfNecessary() { 189 | if s.numFramesUsedSoFar > 0 { 190 | if !s.rtpInterface.sendPacket(s.outBuf.packet(), s.outBuf.curPacketSize()) { 191 | // if failure handler has been specified, call it 192 | if s.onSendErrorFunc != nil { 193 | } 194 | } 195 | 196 | s.packetCount++ 197 | s.totalOctetCount += s.outBuf.curPacketSize() 198 | s.octetCount += s.outBuf.curPacketSize() - rtpHeaderSize - s.specialHeaderSize - s.totalFrameSpecificHeaderSizes 199 | 200 | s.seqNo++ // for next time 201 | } 202 | 203 | if s.outBuf.haveOverflowData() && 204 | s.outBuf.totalBytesAvailable() > s.outBuf.totalBufferSize()/2 { 205 | // Efficiency hack: Reset the packet start pointer to just in front of 206 | // the overflow data (allowing for the RTP header and special headers), 207 | // so that we probably don't have to "memmove()" the overflow data 208 | // into place when building the next packet: 209 | newPacketStart := s.outBuf.curPacketSize() - (rtpHeaderSize + s.specialHeaderSize + s.frameSpecificHeaderSize()) 210 | s.outBuf.adjustPacketStart(newPacketStart) 211 | } else { 212 | // Normal case: Reset the packet start pointer back to the start: 213 | s.outBuf.resetPacketStart() 214 | } 215 | 216 | s.outBuf.resetOffset() 217 | s.numFramesUsedSoFar = 0 218 | 219 | if s.noFramesLeft { 220 | // We're done: 221 | s.OnSourceClosure() 222 | } else { 223 | // We have more frames left to send. Figure out when the next frame 224 | // is due to start playing, then make sure that we wait this long before 225 | // sending the next packet. 226 | var timeNow sys.Timeval 227 | sys.Gettimeofday(&timeNow) 228 | secsDiff := s.nextSendTime.Sec - timeNow.Sec 229 | uSecondsToGo := secsDiff*1000000 + (s.nextSendTime.Usec - timeNow.Usec) 230 | if uSecondsToGo < 0 || secsDiff < 0 { // sanity check: Make sure that the time-to-delay is non-negative: 231 | uSecondsToGo = 0 232 | } 233 | 234 | // Delay this amount of time: 235 | //log.Debug("[MultiFramedRTPSink::sendPacketIfNecessary] uSecondsToGo: %d", uSecondsToGo) 236 | time.Sleep(time.Duration(uSecondsToGo) * time.Microsecond) 237 | s.sendNext() 238 | } 239 | } 240 | 241 | func (s *MultiFramedRTPSink) sendNext() { 242 | s.buildAndSendPacket(false) 243 | } 244 | 245 | func (s *MultiFramedRTPSink) ourHandlerClosure() { 246 | s.noFramesLeft = true 247 | s.sendPacketIfNecessary() 248 | } 249 | 250 | func (s *MultiFramedRTPSink) isFirstFrameInPacket() bool { 251 | return s.numFramesUsedSoFar == 0 252 | } 253 | 254 | func (s *MultiFramedRTPSink) setTimestamp(framePresentationTime sys.Timeval) { 255 | // First, convert the presentation time to a 32-bit RTP timestamp: 256 | s.currentTimestamp = s.convertToRTPTimestamp(framePresentationTime) 257 | 258 | // Then, insert it into the RTP packet: 259 | s.outBuf.insertWord(s.currentTimestamp, s.timestampPosition) 260 | } 261 | 262 | // default implementation: Assume no special header: 263 | func (s *MultiFramedRTPSink) SpecialHeaderSize() uint { 264 | return 0 265 | } 266 | 267 | // default implementation: Assume no frame-specific header: 268 | func (s *MultiFramedRTPSink) frameSpecificHeaderSize() uint { 269 | return 0 270 | } 271 | 272 | func (s *MultiFramedRTPSink) allowOtherFramesAfterLastFragment() bool { 273 | return false 274 | } 275 | 276 | func (s *MultiFramedRTPSink) frameCanAppearAfterPacketStart(frameStart []byte, numBytesInFrame uint) bool { 277 | return true 278 | } 279 | 280 | func (s *MultiFramedRTPSink) computeOverflowForNewFrame(newFrameSize uint) uint { 281 | return s.outBuf.numOverflowBytes(newFrameSize) 282 | } 283 | 284 | func (s *MultiFramedRTPSink) setMarkerBit() { 285 | rtpHdr := s.outBuf.extractWord(0) 286 | rtpHdr |= 0x00800000 287 | s.outBuf.insertWord(rtpHdr, 0) 288 | } 289 | 290 | func (s *MultiFramedRTPSink) doSpecialFrameHandling(fragmentationOffset, numBytesInFrame, numRemainingBytes uint, 291 | frameStart []byte, framePresentationTime sys.Timeval) { 292 | if s.isFirstFrameInPacket() { 293 | s.setTimestamp(framePresentationTime) 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /livemedia/multi_framed_rtp_sink_linux_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | sys "syscall" 5 | "time" 6 | 7 | gs "github.com/djwackey/dorsvr/groupsock" 8 | ) 9 | 10 | const rtpHeaderSize uint = 12 11 | 12 | type MultiFramedRTPSink struct { 13 | RTPSink 14 | outBuf *OutPacketBuffer 15 | nextSendTime sys.Timeval 16 | noFramesLeft bool 17 | isFirstPacket bool 18 | currentTimestamp uint32 19 | ourMaxPacketSize uint 20 | timestampPosition uint 21 | specialHeaderSize uint 22 | numFramesUsedSoFar uint 23 | specialHeaderPosition uint 24 | curFragmentationOffset uint 25 | curFrameSpecificHeaderSize uint 26 | totalFrameSpecificHeaderSizes uint 27 | curFrameSpecificHeaderPosition uint 28 | previousFrameEndedFragmentation bool 29 | onSendErrorFunc interface{} 30 | } 31 | 32 | func (s *MultiFramedRTPSink) InitMultiFramedRTPSink(rtpSink IMediaSink, 33 | rtpGroupSock *gs.GroupSock, rtpPayloadType, rtpTimestampFrequency uint32, rtpPayloadFormatName string) { 34 | // Default max packet size (1500, minus allowance for IP, UDP, UMTP headers) 35 | // (Also, make it a multiple of 4 bytes, just in case that matters.) 36 | s.setPacketSizes(1000, 1448) 37 | s.InitRTPSink(rtpSink, rtpGroupSock, rtpPayloadType, rtpTimestampFrequency, rtpPayloadFormatName) 38 | } 39 | 40 | func (s *MultiFramedRTPSink) setPacketSizes(preferredPacketSize, maxPacketSize uint) { 41 | s.outBuf = newOutPacketBuffer(preferredPacketSize, maxPacketSize) 42 | s.ourMaxPacketSize = maxPacketSize 43 | } 44 | 45 | func (s *MultiFramedRTPSink) multiFramedPlaying() { 46 | s.buildAndSendPacket(true) 47 | } 48 | 49 | func (s *MultiFramedRTPSink) buildAndSendPacket(isFirstPacket bool) { 50 | s.isFirstPacket = isFirstPacket 51 | 52 | // Set up the RTP header: 53 | var rtpHdr uint32 = 0x80000000 54 | rtpHdr |= s._rtpPayloadType << 16 55 | rtpHdr |= s.seqNo 56 | s.outBuf.enqueueWord(rtpHdr) 57 | 58 | s.timestampPosition = s.outBuf.curPacketSize() 59 | s.outBuf.skipBytes(4) 60 | 61 | s.outBuf.enqueueWord(s._ssrc) 62 | 63 | // Allow for a special, payload-format-specific header following the RTP header: 64 | s.specialHeaderPosition = s.outBuf.curPacketSize() 65 | s.specialHeaderSize = s.SpecialHeaderSize() 66 | s.outBuf.skipBytes(s.specialHeaderSize) 67 | 68 | // Begin packing as many (complete) frames into the packet as we can: 69 | s.noFramesLeft = false 70 | s.numFramesUsedSoFar = 0 71 | s.totalFrameSpecificHeaderSizes = 0 72 | 73 | s.packFrame() 74 | } 75 | 76 | func (s *MultiFramedRTPSink) packFrame() { 77 | if s.outBuf.haveOverflowData() { 78 | // Use this frame before reading a new one from the source 79 | frameSize := s.outBuf.overflowDataSize 80 | presentationTime := s.outBuf.overflowPresentationTime 81 | durationInMicroseconds := s.outBuf.overflowDurationInMicroseconds 82 | 83 | s.outBuf.useOverflowData() 84 | s.afterGettingFrame(frameSize, durationInMicroseconds, presentationTime) 85 | } else { 86 | // Normal case: we need to read a new frame from the source 87 | if s.Source == nil { 88 | return 89 | } 90 | 91 | s.curFrameSpecificHeaderPosition = s.outBuf.curPacketSize() 92 | s.curFrameSpecificHeaderSize = s.frameSpecificHeaderSize() 93 | s.outBuf.skipBytes(s.curFrameSpecificHeaderSize) 94 | s.totalFrameSpecificHeaderSizes += s.curFrameSpecificHeaderSize 95 | 96 | // H264FUAFragmenter 97 | s.Source.GetNextFrame(s.outBuf.curPtr(), s.outBuf.totalBytesAvailable(), 98 | s.afterGettingFrame, s.ourHandlerClosure) 99 | } 100 | } 101 | 102 | func (s *MultiFramedRTPSink) afterGettingFrame(frameSize, durationInMicroseconds uint, presentationTime sys.Timeval) { 103 | if s.isFirstPacket { 104 | // Record the fact that we're starting to play now: 105 | sys.Gettimeofday(&s.nextSendTime) 106 | } 107 | 108 | curFragmentationOffset := s.curFragmentationOffset 109 | numFrameBytesToUse := frameSize 110 | var overflowBytes uint 111 | 112 | if s.numFramesUsedSoFar > 0 { 113 | if s.previousFrameEndedFragmentation && 114 | !s.allowOtherFramesAfterLastFragment() && 115 | !s.rtpSink.frameCanAppearAfterPacketStart(s.outBuf.curPtr(), frameSize) { 116 | numFrameBytesToUse = 0 117 | s.outBuf.setOverflowData(s.outBuf.curPacketSize(), frameSize, durationInMicroseconds, presentationTime) 118 | } 119 | } 120 | s.previousFrameEndedFragmentation = false 121 | 122 | if numFrameBytesToUse > 0 { 123 | // Check whether this frame overflows the packet 124 | if s.outBuf.wouldOverflow(frameSize) { 125 | if s.isTooBigForAPacket(frameSize) && (s.numFramesUsedSoFar == 0 || s.allowOtherFramesAfterLastFragment()) { 126 | overflowBytes = s.computeOverflowForNewFrame(frameSize) 127 | numFrameBytesToUse -= overflowBytes 128 | s.curFragmentationOffset += numFrameBytesToUse 129 | } else { 130 | overflowBytes = frameSize 131 | numFrameBytesToUse = 0 132 | } 133 | s.outBuf.setOverflowData(s.outBuf.curPacketSize()+numFrameBytesToUse, overflowBytes, durationInMicroseconds, presentationTime) 134 | } else if s.curFragmentationOffset > 0 { 135 | s.curFragmentationOffset = 0 136 | s.previousFrameEndedFragmentation = true 137 | } 138 | } 139 | 140 | if numFrameBytesToUse == 0 && frameSize > 0 { 141 | // Send our packet now, because we have filled it up: 142 | s.sendPacketIfNecessary() 143 | } else { 144 | // Use this frame in our outgoing packet: 145 | frameStart := s.outBuf.curPtr() 146 | s.outBuf.increment(numFrameBytesToUse) 147 | // do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes 148 | 149 | // Here's where any payload format specific processing gets done: 150 | s.rtpSink.doSpecialFrameHandling(curFragmentationOffset, numFrameBytesToUse, overflowBytes, frameStart, presentationTime) 151 | 152 | s.numFramesUsedSoFar++ 153 | 154 | // Update the time at which the next packet should be sent, based 155 | // on the duration of the frame that we just packed into it. 156 | // However, if this frame has overflow data remaining, then don't 157 | // count its duration yet. 158 | if overflowBytes == 0 { 159 | s.nextSendTime.Usec += int64(durationInMicroseconds) 160 | s.nextSendTime.Sec += s.nextSendTime.Usec / 1000000 161 | s.nextSendTime.Usec %= 1000000 162 | } 163 | 164 | // Send our packet now if (i) it's already at our preferred size, or 165 | // (ii) (heuristic) another frame of the same size as the one we just 166 | // read would overflow the packet, or 167 | // (iii) it contains the last fragment of a fragmented frame, and we 168 | // don't allow anything else to follow this or 169 | // (iv) one frame per packet is allowed: 170 | if s.outBuf.isPreferredSize() || 171 | s.outBuf.wouldOverflow(numFrameBytesToUse) || 172 | s.previousFrameEndedFragmentation && !s.allowOtherFramesAfterLastFragment() || 173 | !s.rtpSink.frameCanAppearAfterPacketStart(s.outBuf.curPtr(), frameSize) { 174 | // The packet is ready to be sent now 175 | s.sendPacketIfNecessary() 176 | } else { 177 | // There's room for more frames; try getting another: 178 | s.packFrame() 179 | } 180 | } 181 | } 182 | 183 | func (s *MultiFramedRTPSink) isTooBigForAPacket(numBytes uint) bool { 184 | numBytes += rtpHeaderSize + s.SpecialHeaderSize() + s.frameSpecificHeaderSize() 185 | return s.outBuf.isTooBigForAPacket(numBytes) 186 | } 187 | 188 | func (s *MultiFramedRTPSink) sendPacketIfNecessary() { 189 | if s.numFramesUsedSoFar > 0 { 190 | if !s.rtpInterface.sendPacket(s.outBuf.packet(), s.outBuf.curPacketSize()) { 191 | // if failure handler has been specified, call it 192 | if s.onSendErrorFunc != nil { 193 | } 194 | } 195 | 196 | s._packetCount++ 197 | s.totalOctetCount += s.outBuf.curPacketSize() 198 | s._octetCount += s.outBuf.curPacketSize() - rtpHeaderSize - s.specialHeaderSize - s.totalFrameSpecificHeaderSizes 199 | 200 | s.seqNo++ // for next time 201 | } 202 | 203 | if s.outBuf.haveOverflowData() && 204 | s.outBuf.totalBytesAvailable() > s.outBuf.totalBufferSize()/2 { 205 | // Efficiency hack: Reset the packet start pointer to just in front of 206 | // the overflow data (allowing for the RTP header and special headers), 207 | // so that we probably don't have to "memmove()" the overflow data 208 | // into place when building the next packet: 209 | newPacketStart := s.outBuf.curPacketSize() - (rtpHeaderSize + s.specialHeaderSize + s.frameSpecificHeaderSize()) 210 | s.outBuf.adjustPacketStart(newPacketStart) 211 | } else { 212 | // Normal case: Reset the packet start pointer back to the start: 213 | s.outBuf.resetPacketStart() 214 | } 215 | 216 | s.outBuf.resetOffset() 217 | s.numFramesUsedSoFar = 0 218 | 219 | if s.noFramesLeft { 220 | // We're done: 221 | s.OnSourceClosure() 222 | } else { 223 | // We have more frames left to send. Figure out when the next frame 224 | // is due to start playing, then make sure that we wait this long before 225 | // sending the next packet. 226 | var timeNow sys.Timeval 227 | sys.Gettimeofday(&timeNow) 228 | secsDiff := s.nextSendTime.Sec - timeNow.Sec 229 | uSecondsToGo := secsDiff*1000000 + (s.nextSendTime.Usec - timeNow.Usec) 230 | if uSecondsToGo < 0 || secsDiff < 0 { // sanity check: Make sure that the time-to-delay is non-negative: 231 | uSecondsToGo = 0 232 | } 233 | 234 | // Delay this amount of time: 235 | //log.Debug("[MultiFramedRTPSink::sendPacketIfNecessary] uSecondsToGo: %d", uSecondsToGo) 236 | time.Sleep(time.Duration(uSecondsToGo) * time.Microsecond) 237 | s.sendNext() 238 | } 239 | } 240 | 241 | func (s *MultiFramedRTPSink) sendNext() { 242 | s.buildAndSendPacket(false) 243 | } 244 | 245 | func (s *MultiFramedRTPSink) ourHandlerClosure() { 246 | s.noFramesLeft = true 247 | s.sendPacketIfNecessary() 248 | } 249 | 250 | func (s *MultiFramedRTPSink) isFirstFrameInPacket() bool { 251 | return s.numFramesUsedSoFar == 0 252 | } 253 | 254 | func (s *MultiFramedRTPSink) setTimestamp(framePresentationTime sys.Timeval) { 255 | // First, convert the presentation time to a 32-bit RTP timestamp: 256 | s.currentTimestamp = s.convertToRTPTimestamp(framePresentationTime) 257 | 258 | // Then, insert it into the RTP packet: 259 | s.outBuf.insertWord(s.currentTimestamp, s.timestampPosition) 260 | } 261 | 262 | // default implementation: Assume no special header: 263 | func (s *MultiFramedRTPSink) SpecialHeaderSize() uint { 264 | return 0 265 | } 266 | 267 | // default implementation: Assume no frame-specific header: 268 | func (s *MultiFramedRTPSink) frameSpecificHeaderSize() uint { 269 | return 0 270 | } 271 | 272 | func (s *MultiFramedRTPSink) allowOtherFramesAfterLastFragment() bool { 273 | return false 274 | } 275 | 276 | func (s *MultiFramedRTPSink) frameCanAppearAfterPacketStart(frameStart []byte, numBytesInFrame uint) bool { 277 | return true 278 | } 279 | 280 | func (s *MultiFramedRTPSink) computeOverflowForNewFrame(newFrameSize uint) uint { 281 | return s.outBuf.numOverflowBytes(newFrameSize) 282 | } 283 | 284 | func (s *MultiFramedRTPSink) setMarkerBit() { 285 | rtpHdr := s.outBuf.extractWord(0) 286 | rtpHdr |= 0x00800000 287 | s.outBuf.insertWord(rtpHdr, 0) 288 | } 289 | 290 | func (s *MultiFramedRTPSink) doSpecialFrameHandling(fragmentationOffset, numBytesInFrame, numRemainingBytes uint, 291 | frameStart []byte, framePresentationTime sys.Timeval) { 292 | if s.isFirstFrameInPacket() { 293 | s.setTimestamp(framePresentationTime) 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /livemedia/multi_framed_rtp_sink_darwin_amd64.go: -------------------------------------------------------------------------------- 1 | package livemedia 2 | 3 | import ( 4 | sys "syscall" 5 | "time" 6 | 7 | gs "github.com/djwackey/dorsvr/groupsock" 8 | ) 9 | 10 | const rtpHeaderSize uint = 12 11 | 12 | type MultiFramedRTPSink struct { 13 | RTPSink 14 | outBuf *OutPacketBuffer 15 | nextSendTime sys.Timeval 16 | noFramesLeft bool 17 | isFirstPacket bool 18 | currentTimestamp uint32 19 | ourMaxPacketSize uint 20 | timestampPosition uint 21 | specialHeaderSize uint 22 | numFramesUsedSoFar uint 23 | specialHeaderPosition uint 24 | curFragmentationOffset uint 25 | curFrameSpecificHeaderSize uint 26 | totalFrameSpecificHeaderSizes uint 27 | curFrameSpecificHeaderPosition uint 28 | previousFrameEndedFragmentation bool 29 | onSendErrorFunc interface{} 30 | } 31 | 32 | func (s *MultiFramedRTPSink) InitMultiFramedRTPSink(rtpSink IMediaSink, 33 | rtpGroupSock *gs.GroupSock, rtpPayloadType, rtpTimestampFrequency uint32, rtpPayloadFormatName string) { 34 | // Default max packet size (1500, minus allowance for IP, UDP, UMTP headers) 35 | // (Also, make it a multiple of 4 bytes, just in case that matters.) 36 | s.setPacketSizes(1000, 1448) 37 | s.InitRTPSink(rtpSink, rtpGroupSock, rtpPayloadType, rtpTimestampFrequency, rtpPayloadFormatName) 38 | } 39 | 40 | func (s *MultiFramedRTPSink) setPacketSizes(preferredPacketSize, maxPacketSize uint) { 41 | s.outBuf = newOutPacketBuffer(preferredPacketSize, maxPacketSize) 42 | s.ourMaxPacketSize = maxPacketSize 43 | } 44 | 45 | func (s *MultiFramedRTPSink) multiFramedPlaying() { 46 | s.buildAndSendPacket(true) 47 | } 48 | 49 | func (s *MultiFramedRTPSink) buildAndSendPacket(isFirstPacket bool) { 50 | s.isFirstPacket = isFirstPacket 51 | 52 | // Set up the RTP header: 53 | var rtpHdr uint32 = 0x80000000 54 | rtpHdr |= s._rtpPayloadType << 16 55 | rtpHdr |= s.seqNo 56 | s.outBuf.enqueueWord(rtpHdr) 57 | 58 | s.timestampPosition = s.outBuf.curPacketSize() 59 | s.outBuf.skipBytes(4) 60 | 61 | s.outBuf.enqueueWord(s._ssrc) 62 | 63 | // Allow for a special, payload-format-specific header following the RTP header: 64 | s.specialHeaderPosition = s.outBuf.curPacketSize() 65 | s.specialHeaderSize = s.SpecialHeaderSize() 66 | s.outBuf.skipBytes(s.specialHeaderSize) 67 | 68 | // Begin packing as many (complete) frames into the packet as we can: 69 | s.noFramesLeft = false 70 | s.numFramesUsedSoFar = 0 71 | s.totalFrameSpecificHeaderSizes = 0 72 | 73 | s.packFrame() 74 | } 75 | 76 | func (s *MultiFramedRTPSink) packFrame() { 77 | if s.outBuf.haveOverflowData() { 78 | // Use this frame before reading a new one from the source 79 | frameSize := s.outBuf.overflowDataSize 80 | presentationTime := s.outBuf.overflowPresentationTime 81 | durationInMicroseconds := s.outBuf.overflowDurationInMicroseconds 82 | 83 | s.outBuf.useOverflowData() 84 | s.afterGettingFrame(frameSize, durationInMicroseconds, presentationTime) 85 | } else { 86 | // Normal case: we need to read a new frame from the source 87 | if s.Source == nil { 88 | return 89 | } 90 | 91 | s.curFrameSpecificHeaderPosition = s.outBuf.curPacketSize() 92 | s.curFrameSpecificHeaderSize = s.frameSpecificHeaderSize() 93 | s.outBuf.skipBytes(s.curFrameSpecificHeaderSize) 94 | s.totalFrameSpecificHeaderSizes += s.curFrameSpecificHeaderSize 95 | 96 | // H264FUAFragmenter 97 | s.Source.GetNextFrame(s.outBuf.curPtr(), s.outBuf.totalBytesAvailable(), 98 | s.afterGettingFrame, s.ourHandlerClosure) 99 | } 100 | } 101 | 102 | func (s *MultiFramedRTPSink) afterGettingFrame(frameSize, durationInMicroseconds uint, presentationTime sys.Timeval) { 103 | if s.isFirstPacket { 104 | // Record the fact that we're starting to play now: 105 | sys.Gettimeofday(&s.nextSendTime) 106 | } 107 | 108 | curFragmentationOffset := s.curFragmentationOffset 109 | numFrameBytesToUse := frameSize 110 | var overflowBytes uint 111 | 112 | if s.numFramesUsedSoFar > 0 { 113 | if s.previousFrameEndedFragmentation && 114 | !s.allowOtherFramesAfterLastFragment() && 115 | !s.rtpSink.frameCanAppearAfterPacketStart(s.outBuf.curPtr(), frameSize) { 116 | numFrameBytesToUse = 0 117 | s.outBuf.setOverflowData(s.outBuf.curPacketSize(), frameSize, durationInMicroseconds, presentationTime) 118 | } 119 | } 120 | s.previousFrameEndedFragmentation = false 121 | 122 | if numFrameBytesToUse > 0 { 123 | // Check whether this frame overflows the packet 124 | if s.outBuf.wouldOverflow(frameSize) { 125 | if s.isTooBigForAPacket(frameSize) && (s.numFramesUsedSoFar == 0 || s.allowOtherFramesAfterLastFragment()) { 126 | overflowBytes = s.computeOverflowForNewFrame(frameSize) 127 | numFrameBytesToUse -= overflowBytes 128 | s.curFragmentationOffset += numFrameBytesToUse 129 | } else { 130 | overflowBytes = frameSize 131 | numFrameBytesToUse = 0 132 | } 133 | s.outBuf.setOverflowData(s.outBuf.curPacketSize()+numFrameBytesToUse, overflowBytes, durationInMicroseconds, presentationTime) 134 | } else if s.curFragmentationOffset > 0 { 135 | s.curFragmentationOffset = 0 136 | s.previousFrameEndedFragmentation = true 137 | } 138 | } 139 | 140 | if numFrameBytesToUse == 0 && frameSize > 0 { 141 | // Send our packet now, because we have filled it up: 142 | s.sendPacketIfNecessary() 143 | } else { 144 | // Use this frame in our outgoing packet: 145 | frameStart := s.outBuf.curPtr() 146 | s.outBuf.increment(numFrameBytesToUse) 147 | // do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes 148 | 149 | // Here's where any payload format specific processing gets done: 150 | s.rtpSink.doSpecialFrameHandling(curFragmentationOffset, numFrameBytesToUse, overflowBytes, frameStart, presentationTime) 151 | 152 | s.numFramesUsedSoFar++ 153 | 154 | // Update the time at which the next packet should be sent, based 155 | // on the duration of the frame that we just packed into it. 156 | // However, if this frame has overflow data remaining, then don't 157 | // count its duration yet. 158 | if overflowBytes == 0 { 159 | s.nextSendTime.Usec += int32(durationInMicroseconds) 160 | s.nextSendTime.Sec += int64(s.nextSendTime.Usec) / 1000000 161 | s.nextSendTime.Usec %= 1000000 162 | } 163 | 164 | // Send our packet now if (i) it's already at our preferred size, or 165 | // (ii) (heuristic) another frame of the same size as the one we just 166 | // read would overflow the packet, or 167 | // (iii) it contains the last fragment of a fragmented frame, and we 168 | // don't allow anything else to follow this or 169 | // (iv) one frame per packet is allowed: 170 | if s.outBuf.isPreferredSize() || 171 | s.outBuf.wouldOverflow(numFrameBytesToUse) || 172 | s.previousFrameEndedFragmentation && !s.allowOtherFramesAfterLastFragment() || 173 | !s.rtpSink.frameCanAppearAfterPacketStart(s.outBuf.curPtr(), frameSize) { 174 | // The packet is ready to be sent now 175 | s.sendPacketIfNecessary() 176 | } else { 177 | // There's room for more frames; try getting another: 178 | s.packFrame() 179 | } 180 | } 181 | } 182 | 183 | func (s *MultiFramedRTPSink) isTooBigForAPacket(numBytes uint) bool { 184 | numBytes += rtpHeaderSize + s.SpecialHeaderSize() + s.frameSpecificHeaderSize() 185 | return s.outBuf.isTooBigForAPacket(numBytes) 186 | } 187 | 188 | func (s *MultiFramedRTPSink) sendPacketIfNecessary() { 189 | if s.numFramesUsedSoFar > 0 { 190 | if !s.rtpInterface.sendPacket(s.outBuf.packet(), s.outBuf.curPacketSize()) { 191 | // if failure handler has been specified, call it 192 | if s.onSendErrorFunc != nil { 193 | } 194 | } 195 | 196 | s._packetCount++ 197 | s.totalOctetCount += s.outBuf.curPacketSize() 198 | s._octetCount += s.outBuf.curPacketSize() - rtpHeaderSize - s.specialHeaderSize - s.totalFrameSpecificHeaderSizes 199 | 200 | s.seqNo++ // for next time 201 | } 202 | 203 | if s.outBuf.haveOverflowData() && 204 | s.outBuf.totalBytesAvailable() > s.outBuf.totalBufferSize()/2 { 205 | // Efficiency hack: Reset the packet start pointer to just in front of 206 | // the overflow data (allowing for the RTP header and special headers), 207 | // so that we probably don't have to "memmove()" the overflow data 208 | // into place when building the next packet: 209 | newPacketStart := s.outBuf.curPacketSize() - (rtpHeaderSize + s.specialHeaderSize + s.frameSpecificHeaderSize()) 210 | s.outBuf.adjustPacketStart(newPacketStart) 211 | } else { 212 | // Normal case: Reset the packet start pointer back to the start: 213 | s.outBuf.resetPacketStart() 214 | } 215 | 216 | s.outBuf.resetOffset() 217 | s.numFramesUsedSoFar = 0 218 | 219 | if s.noFramesLeft { 220 | // We're done: 221 | s.OnSourceClosure() 222 | } else { 223 | // We have more frames left to send. Figure out when the next frame 224 | // is due to start playing, then make sure that we wait this long before 225 | // sending the next packet. 226 | var timeNow sys.Timeval 227 | sys.Gettimeofday(&timeNow) 228 | secsDiff := s.nextSendTime.Sec - timeNow.Sec 229 | uSecondsToGo := secsDiff*1000000 + int64((s.nextSendTime.Usec)-timeNow.Usec) 230 | if uSecondsToGo < 0 || secsDiff < 0 { // sanity check: Make sure that the time-to-delay is non-negative: 231 | uSecondsToGo = 0 232 | } 233 | 234 | // Delay this amount of time: 235 | //log.Debug("[MultiFramedRTPSink::sendPacketIfNecessary] uSecondsToGo: %d", uSecondsToGo) 236 | time.Sleep(time.Duration(uSecondsToGo) * time.Microsecond) 237 | s.sendNext() 238 | } 239 | } 240 | 241 | func (s *MultiFramedRTPSink) sendNext() { 242 | s.buildAndSendPacket(false) 243 | } 244 | 245 | func (s *MultiFramedRTPSink) ourHandlerClosure() { 246 | s.noFramesLeft = true 247 | s.sendPacketIfNecessary() 248 | } 249 | 250 | func (s *MultiFramedRTPSink) isFirstFrameInPacket() bool { 251 | return s.numFramesUsedSoFar == 0 252 | } 253 | 254 | func (s *MultiFramedRTPSink) setTimestamp(framePresentationTime sys.Timeval) { 255 | // First, convert the presentation time to a 32-bit RTP timestamp: 256 | s.currentTimestamp = s.convertToRTPTimestamp(framePresentationTime) 257 | 258 | // Then, insert it into the RTP packet: 259 | s.outBuf.insertWord(s.currentTimestamp, s.timestampPosition) 260 | } 261 | 262 | // default implementation: Assume no special header: 263 | func (s *MultiFramedRTPSink) SpecialHeaderSize() uint { 264 | return 0 265 | } 266 | 267 | // default implementation: Assume no frame-specific header: 268 | func (s *MultiFramedRTPSink) frameSpecificHeaderSize() uint { 269 | return 0 270 | } 271 | 272 | func (s *MultiFramedRTPSink) allowOtherFramesAfterLastFragment() bool { 273 | return false 274 | } 275 | 276 | func (s *MultiFramedRTPSink) frameCanAppearAfterPacketStart(frameStart []byte, numBytesInFrame uint) bool { 277 | return true 278 | } 279 | 280 | func (s *MultiFramedRTPSink) computeOverflowForNewFrame(newFrameSize uint) uint { 281 | return s.outBuf.numOverflowBytes(newFrameSize) 282 | } 283 | 284 | func (s *MultiFramedRTPSink) setMarkerBit() { 285 | rtpHdr := s.outBuf.extractWord(0) 286 | rtpHdr |= 0x00800000 287 | s.outBuf.insertWord(rtpHdr, 0) 288 | } 289 | 290 | func (s *MultiFramedRTPSink) doSpecialFrameHandling(fragmentationOffset, numBytesInFrame, numRemainingBytes uint, 291 | frameStart []byte, framePresentationTime sys.Timeval) { 292 | if s.isFirstFrameInPacket() { 293 | s.setTimestamp(framePresentationTime) 294 | } 295 | } 296 | --------------------------------------------------------------------------------