From 211a6390d4d9cce5a9d9b916e536f552b0661dea Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 27 Dec 2018 13:01:38 +1030 Subject: [PATCH 001/137] mts: using static arrays to store mpegts and pes packets --- stream/mts/encoder.go | 10 ++++++---- stream/mts/mpegts.go | 7 +++++-- stream/mts/pes/pes.go | 9 ++++++--- stream/mts/pes/pes_test.go | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 31fe4923..c1715761 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -86,6 +86,8 @@ type Encoder struct { clock time.Duration frameInterval time.Duration ptsOffset time.Duration + tsSpace [mpegTsSize]byte + pesSpace [pes.MaxPesSize]byte psiCount int @@ -130,7 +132,7 @@ func (e *Encoder) Encode(nalu []byte) error { Data: nalu, HeaderLength: 5, } - buf := pesPkt.Bytes() + buf := pesPkt.Bytes(e.pesSpace[:pes.MaxPesSize]) pusi := true for len(buf) != 0 { @@ -158,7 +160,7 @@ func (e *Encoder) Encode(nalu []byte) error { } } e.psiCount-- - _, err := e.dst.Write(pkt.Bytes()) + _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:mpegTsSize])) if err != nil { return err } @@ -180,7 +182,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: addPadding(patTable), } - _, err := e.dst.Write(patPkt.Bytes()) + _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:mpegTsSize])) if err != nil { return err } @@ -203,7 +205,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: addPadding(pmtTable), } - _, err = e.dst.Write(pmtPkt.Bytes()) + _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:mpegTsSize])) if err != nil { return err } diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 36b4d2fe..287aeb09 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -168,7 +168,11 @@ func asByte(b bool) byte { // ToByteSlice interprets the fields of the ts packet instance and outputs a // corresponding byte slice -func (p *Packet) Bytes() []byte { +func (p *Packet) Bytes(buf []byte) []byte { + if buf == nil || cap(buf) != mpegTsSize { + buf = make([]byte, 0, mpegTsSize) + } + buf = buf[:] stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 - asInt(p.OPCRF)*6 - asInt(p.SPF) var stuffing []byte @@ -179,7 +183,6 @@ func (p *Packet) Bytes() []byte { stuffing[i] = 0xFF } afl := 1 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + asInt(p.SPF) + asInt(p.TPDF) + len(p.TPD) + len(stuffing) - buf := make([]byte, 0, mpegTsSize) buf = append(buf, []byte{ 0x47, (asByte(p.TEI)<<7 | asByte(p.PUSI)<<6 | asByte(p.Priority)<<5 | byte((p.PID&0xFF00)>>8)), diff --git a/stream/mts/pes/pes.go b/stream/mts/pes/pes.go index 1ec9bf71..9995e6a3 100644 --- a/stream/mts/pes/pes.go +++ b/stream/mts/pes/pes.go @@ -26,7 +26,7 @@ LICENSE package pes -const maxPesSize = 10000 +const MaxPesSize = 10000 /* The below data struct encapsulates the fields of an PES packet. Below is @@ -92,8 +92,11 @@ type Packet struct { Data []byte // Pes packet data } -func (p *Packet) Bytes() []byte { - buf := make([]byte, 0, maxPesSize) +func (p *Packet) Bytes(buf []byte) []byte { + if buf == nil || cap(buf) != MaxPesSize { + buf = make([]byte, 0, MaxPesSize) + } + buf = buf[:] buf = append(buf, []byte{ 0x00, 0x00, 0x01, p.StreamID, diff --git a/stream/mts/pes/pes_test.go b/stream/mts/pes/pes_test.go index ee436253..d54d0f4a 100644 --- a/stream/mts/pes/pes_test.go +++ b/stream/mts/pes/pes_test.go @@ -46,7 +46,7 @@ func TestPesToByteSlice(t *testing.T) { Stuff: []byte{0xFF, 0xFF}, Data: []byte{0xEA, 0x4B, 0x12}, } - got := pkt.Bytes() + got := pkt.Bytes(nil) want := []byte{ 0x00, // packet start code prefix byte 1 0x00, // packet start code prefix byte 2 From e386f06adfe72737bb7754504b26aae289a820be Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 27 Dec 2018 13:19:11 +1030 Subject: [PATCH 002/137] mts: fixed slicing issue --- stream/mts/mpegts.go | 2 +- stream/mts/pes/pes.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 287aeb09..482ea6b0 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -172,7 +172,7 @@ func (p *Packet) Bytes(buf []byte) []byte { if buf == nil || cap(buf) != mpegTsSize { buf = make([]byte, 0, mpegTsSize) } - buf = buf[:] + buf = buf[:0] stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 - asInt(p.OPCRF)*6 - asInt(p.SPF) var stuffing []byte diff --git a/stream/mts/pes/pes.go b/stream/mts/pes/pes.go index 9995e6a3..c74a2889 100644 --- a/stream/mts/pes/pes.go +++ b/stream/mts/pes/pes.go @@ -26,6 +26,8 @@ LICENSE package pes +import () + const MaxPesSize = 10000 /* @@ -96,7 +98,7 @@ func (p *Packet) Bytes(buf []byte) []byte { if buf == nil || cap(buf) != MaxPesSize { buf = make([]byte, 0, MaxPesSize) } - buf = buf[:] + buf = buf[:0] buf = append(buf, []byte{ 0x00, 0x00, 0x01, p.StreamID, From 3a872d46c8344f8e7b2f709344b8e43be9bac2db Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 27 Dec 2018 13:25:51 +1030 Subject: [PATCH 003/137] mts: improved some const naming --- stream/mts/encoder.go | 8 ++++---- stream/mts/mpegts.go | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index c1715761..b9189c4b 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -86,7 +86,7 @@ type Encoder struct { clock time.Duration frameInterval time.Duration ptsOffset time.Duration - tsSpace [mpegTsSize]byte + tsSpace [PktLen]byte pesSpace [pes.MaxPesSize]byte psiCount int @@ -160,7 +160,7 @@ func (e *Encoder) Encode(nalu []byte) error { } } e.psiCount-- - _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:mpegTsSize])) + _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:PktLen])) if err != nil { return err } @@ -182,7 +182,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: addPadding(patTable), } - _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:mpegTsSize])) + _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PktLen])) if err != nil { return err } @@ -205,7 +205,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: addPadding(pmtTable), } - _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:mpegTsSize])) + _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PktLen])) if err != nil { return err } diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 482ea6b0..427a4640 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -33,8 +33,8 @@ import ( ) const ( - mpegTsSize = 188 - mpegtsPayloadSize = 176 + PktLen = 188 + PayloadLen = 176 ) /* @@ -130,13 +130,13 @@ type Packet struct { // FindPMT will take a clip of mpegts and try to find a PMT table - if one // is found, then it is returned, otherwise nil and an error is returned. func FindPMT(d []byte) (p []byte, err error) { - if len(d) < mpegTsSize { + if len(d) < PktLen { return nil, errors.New("Mmpegts data not of valid length") } - for i := 0; i < len(d); i += mpegTsSize { + for i := 0; i < len(d); i += PktLen { pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2]) if pid == pmtPid { - p = d[i+4 : i+mpegTsSize] + p = d[i+4 : i+PktLen] return } } @@ -148,7 +148,7 @@ func FindPMT(d []byte) (p []byte, err error) { func (p *Packet) FillPayload(data []byte) int { currentPktLength := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + asInt(p.SPF)*1 + asInt(p.TPDF)*1 + len(p.TPD) - p.Payload = make([]byte, mpegtsPayloadSize-currentPktLength) + p.Payload = make([]byte, PayloadLen-currentPktLength) return copy(p.Payload, data) } @@ -169,8 +169,8 @@ func asByte(b bool) byte { // ToByteSlice interprets the fields of the ts packet instance and outputs a // corresponding byte slice func (p *Packet) Bytes(buf []byte) []byte { - if buf == nil || cap(buf) != mpegTsSize { - buf = make([]byte, 0, mpegTsSize) + if buf == nil || cap(buf) != PktLen { + buf = make([]byte, 0, PktLen) } buf = buf[:0] stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 - From 49a6acbde88200ea28849f5b74aad144289866a4 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 27 Dec 2018 13:29:08 +1030 Subject: [PATCH 004/137] mts: some more const naming improvements --- stream/mts/encoder.go | 7 +++---- stream/mts/psi/psi.go | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index b9189c4b..e921248f 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -37,8 +37,7 @@ import ( ) const ( - psiPacketSize = 184 - psiSendCount = 7 + psiSndCnt = 7 ) type MetaData struct { @@ -209,14 +208,14 @@ func (e *Encoder) writePSI() error { if err != nil { return err } - e.psiCount = psiSendCount + e.psiCount = psiSndCnt return nil } // addPadding adds an appropriate amount of padding to a pat or pmt table for // addition to an mpegts packet func addPadding(d []byte) []byte { - for len(d) < psiPacketSize { + for len(d) < psi.PktLen { d = append(d, 0xff) } return d diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 56abc260..4db81a59 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -30,6 +30,11 @@ import ( "hash/crc32" ) +// Misc consts +const ( + PktLen = 184 +) + // Lengths of section definitions const ( ESSDDefLen = 5 From b28861d690f6085a68e6de07ced4245e3d7341ad Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 27 Dec 2018 13:36:38 +1030 Subject: [PATCH 005/137] pes: MaxPesSize to MaxPesLen --- stream/mts/encoder.go | 2 +- stream/mts/pes/pes.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index e921248f..57422a3f 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -86,7 +86,7 @@ type Encoder struct { frameInterval time.Duration ptsOffset time.Duration tsSpace [PktLen]byte - pesSpace [pes.MaxPesSize]byte + pesSpace [pes.MaxPesLen]byte psiCount int diff --git a/stream/mts/pes/pes.go b/stream/mts/pes/pes.go index c74a2889..fb0878db 100644 --- a/stream/mts/pes/pes.go +++ b/stream/mts/pes/pes.go @@ -28,7 +28,7 @@ package pes import () -const MaxPesSize = 10000 +const MaxPesLen = 10000 /* The below data struct encapsulates the fields of an PES packet. Below is @@ -95,8 +95,8 @@ type Packet struct { } func (p *Packet) Bytes(buf []byte) []byte { - if buf == nil || cap(buf) != MaxPesSize { - buf = make([]byte, 0, MaxPesSize) + if buf == nil || cap(buf) != MaxPesLen { + buf = make([]byte, 0, MaxPesLen) } buf = buf[:0] buf = append(buf, []byte{ From c739b10f860b61b01dca628f517f4c87330ad4e2 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 27 Dec 2018 14:11:23 +1030 Subject: [PATCH 006/137] mts: removed repeated use of addPadding func --- stream/mts/encoder.go | 20 ++++++-------------- stream/mts/psi/op.go | 9 +++++++++ stream/mts/psi/psi.go | 1 + stream/mts/psi/psi_test.go | 20 +++++++++----------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 57422a3f..2cafc167 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -131,7 +131,7 @@ func (e *Encoder) Encode(nalu []byte) error { Data: nalu, HeaderLength: 5, } - buf := pesPkt.Bytes(e.pesSpace[:pes.MaxPesSize]) + buf := pesPkt.Bytes(e.pesSpace[:pes.MaxPesLen]) pusi := true for len(buf) != 0 { @@ -179,7 +179,7 @@ func (e *Encoder) writePSI() error { PID: patPid, CC: e.ccFor(patPid), AFC: hasPayload, - Payload: addPadding(patTable), + Payload: patTable, } _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PktLen])) if err != nil { @@ -202,7 +202,7 @@ func (e *Encoder) writePSI() error { PID: pmtPid, CC: e.ccFor(pmtPid), AFC: hasPayload, - Payload: addPadding(pmtTable), + Payload: pmtTable, } _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PktLen])) if err != nil { @@ -212,15 +212,6 @@ func (e *Encoder) writePSI() error { return nil } -// addPadding adds an appropriate amount of padding to a pat or pmt table for -// addition to an mpegts packet -func addPadding(d []byte) []byte { - for len(d) < psi.PktLen { - d = append(d, 0xff) - } - return d -} - // tick advances the clock one frame interval. func (e *Encoder) tick() { e.clock += e.frameInterval @@ -239,7 +230,8 @@ func (e *Encoder) pcr() uint64 { // ccFor returns the next continuity counter for pid. func (e *Encoder) ccFor(pid int) byte { cc := e.continuity[pid] - const continuityCounterMask = 0xf - e.continuity[pid] = (cc + 1) & continuityCounterMask + // Continuity counter mask + const ccMask = 0xf + e.continuity[pid] = (cc + 1) & ccMask return cc } diff --git a/stream/mts/psi/op.go b/stream/mts/psi/op.go index e78cdac7..d71dbf61 100644 --- a/stream/mts/psi/op.go +++ b/stream/mts/psi/op.go @@ -143,3 +143,12 @@ func updateCrc(out []byte) []byte { out[len(out)-1] = byte(crc32) return out } + +// addPadding adds an appropriate amount of padding to a pat or pmt table for +// addition to an mpegts packet +func addPadding(d []byte) []byte { + for len(d) < PktLen { + d = append(d, 0xff) + } + return d +} diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 4db81a59..3aa8bcd4 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -238,6 +238,7 @@ func (p *PSI) Bytes() []byte { out[3] = byte(p.Sl) out = append(out, p.Tss.Bytes()...) out = addCrc(out) + out = addPadding(out) return out } diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 86c19f60..d6d6d1cc 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -91,10 +91,10 @@ var ( } // Bytes representing pmt with time1 and location1 - pmtTimeLocationBytes1 = buildPmtTimeLocationBytes(locationTstStr1) + pmtTimeLocBytes1 = buildPmtTimeLocBytes(locationTstStr1) // bytes representing pmt with with time1 and location 2 - pmtTimeLocationBytes2 = buildPmtTimeLocationBytes(locationTstStr2) + pmtTimeLocBytes2 = buildPmtTimeLocBytes(locationTstStr2) ) // bytesTests contains data for testing the Bytes() funcs for the PSI data struct @@ -189,7 +189,7 @@ var bytesTests = []struct { }, }, }, - want: buildPmtTimeLocationBytes(locationTstStr1), + want: buildPmtTimeLocBytes(locationTstStr1), }, } @@ -198,9 +198,7 @@ var bytesTests = []struct { func TestBytes(t *testing.T) { for _, test := range bytesTests { got := test.input.Bytes() - // Remove crc32s - got = got[:len(got)-4] - if !bytes.Equal(got, test.want) { + if !bytes.Equal(got, addPadding(addCrc(test.want))) { t.Errorf("unexpected error for test %v: got:%v want:%v", test.name, got, test.want) } @@ -259,23 +257,23 @@ func TestLocationGet(t *testing.T) { // TestLocationUpdate checks to see if we can update the location string in a pmt correctly func TestLocationUpdate(t *testing.T) { - cpy := make([]byte, len(pmtTimeLocationBytes1)) - copy(cpy, pmtTimeLocationBytes1) + cpy := make([]byte, len(pmtTimeLocBytes1)) + copy(cpy, pmtTimeLocBytes1) cpy = addCrc(cpy) err := UpdateLocation(cpy, locationTstStr2) cpy = cpy[:len(cpy)-4] if err != nil { t.Errorf("Update time returned err: %v", err) } - if !bytes.Equal(pmtTimeLocationBytes2, cpy) { - t.Errorf(errCmp, "TestLocationUpdate", pmtTimeLocationBytes2, cpy) + if !bytes.Equal(pmtTimeLocBytes2, cpy) { + t.Errorf(errCmp, "TestLocationUpdate", pmtTimeLocBytes2, cpy) } } // buildPmtTimeLocationBytes is a helper function to help construct the byte slices // for pmts with time and location, as the location data field is 32 bytes, i.e. quite large // to type out -func buildPmtTimeLocationBytes(tstStr string) []byte { +func buildPmtTimeLocBytes(tstStr string) []byte { return append(append(append(make([]byte, 0), pmtTimeLocationBytesPart1...), LocationStrBytes(tstStr)...), pmtTimeLocationBytesPart2...) } From ca681a6176ad15eb7c503557add391a38bc50dd6 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 28 Dec 2018 10:53:10 +1030 Subject: [PATCH 007/137] revid: fatal when raspivid cannot be started --- revid/revid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revid/revid.go b/revid/revid.go index 93bcdf38..135c5600 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -487,7 +487,7 @@ func (r *Revid) startRaspivid() error { } err = r.cmd.Start() if err != nil { - return err + r.config.Logger.Log(smartlogger.Fatal, pkg+"cannot start raspivid", "error", err.Error()) } r.config.Logger.Log(smartlogger.Info, pkg+"reading camera data") From 5a181f3576eb17f58a0e63fbd42c35abf1e4899f Mon Sep 17 00:00:00 2001 From: Saxon Milton Date: Fri, 28 Dec 2018 03:09:41 +0000 Subject: [PATCH 008/137] revid: fix rtmp sending Approved-by: kortschak --- revid/revid.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/revid/revid.go b/revid/revid.go index 93bcdf38..e883bb45 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -157,7 +157,7 @@ func (p *packer) Write(frame []byte) (int, error) { } p.packetCount++ now = time.Now() - if now.Sub(prevTime) > clipDuration && p.packetCount%7 == 0 { + if (p.owner.config.Output1 != Rtmp && now.Sub(prevTime) > clipDuration && p.packetCount%7 == 0) || p.owner.config.Output1 == Rtmp { p.owner.buffer.Flush() p.packetCount = 0 prevTime = now From 1e3b4b1ab89c82adfdffc00d624ef9956f76ff38 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Mon, 10 Dec 2018 09:39:20 +1030 Subject: [PATCH 009/137] cmd/revid-cli,revid: reduce stringly typing in config/flags --- cmd/revid-cli/main.go | 148 ++++++++++++++----------------------- revid/config.go | 166 +++++++++++++++--------------------------- revid/revid.go | 41 ++++------- revid/senders.go | 4 +- 4 files changed, 129 insertions(+), 230 deletions(-) diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 32b18be6..3fda0862 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -33,6 +33,7 @@ import ( "os" "runtime/pprof" "strconv" + "strings" "time" "bitbucket.org/ausocean/av/revid" @@ -101,22 +102,21 @@ func handleFlags() revid.Config { output2Ptr = flag.String("Output2", "", "The second output type: Http, Rtmp, File, Udp, Rtp") rtmpMethodPtr = flag.String("RtmpMethod", "", "The method used to send over rtmp: Ffmpeg, Librtmp") packetizationPtr = flag.String("Packetization", "", "The method of data packetisation: Flv, Mpegts, None") - quantizationModePtr = flag.String("QuantizationMode", "", "Whether quantization if on or off (variable bitrate): On, Off") + quantizePtr = flag.Bool("Quantize", false, "Quantize input (non-variable bitrate)") verbosityPtr = flag.String("Verbosity", "", "Verbosity: Info, Warning, Error, Fatal") - framesPerClipPtr = flag.String("FramesPerClip", "", "Number of frames per clip sent") + framesPerClipPtr = flag.Uint("FramesPerClip", 0, "Number of frames per clip sent") rtmpUrlPtr = flag.String("RtmpUrl", "", "Url of rtmp endpoint") - bitratePtr = flag.String("Bitrate", "", "Bitrate of recorded video") + bitratePtr = flag.Uint("Bitrate", 0, "Bitrate of recorded video") outputFileNamePtr = flag.String("OutputFileName", "", "The directory of the output file") inputFileNamePtr = flag.String("InputFileName", "", "The directory of the input file") - heightPtr = flag.String("Height", "", "Height in pixels") - widthPtr = flag.String("Width", "", "Width in pixels") - frameRatePtr = flag.String("FrameRate", "", "Frame rate of captured video") + heightPtr = flag.Uint("Height", 0, "Height in pixels") + widthPtr = flag.Uint("Width", 0, "Width in pixels") + frameRatePtr = flag.Uint("FrameRate", 0, "Frame rate of captured video") httpAddressPtr = flag.String("HttpAddress", "", "Destination address of http posts") - quantizationPtr = flag.String("Quantization", "", "Desired quantization value: 0-40") - timeoutPtr = flag.String("Timeout", "", "Http timeout in seconds") - intraRefreshPeriodPtr = flag.String("IntraRefreshPeriod", "", "The IntraRefreshPeriod i.e. how many keyframes we send") - verticalFlipPtr = flag.String("VerticalFlip", "", "Flip video vertically: Yes, No") - horizontalFlipPtr = flag.String("HorizontalFlip", "", "Flip video horizontally: Yes, No") + quantizationPtr = flag.Uint("Quantization", 0, "Desired quantization value: 0-40") + intraRefreshPeriodPtr = flag.Uint("IntraRefreshPeriod", 0, "The IntraRefreshPeriod i.e. how many keyframes we send") + verticalFlipPtr = flag.Bool("VerticalFlip", false, "Flip video vertically: Yes, No") + horizontalFlipPtr = flag.Bool("HorizontalFlip", false, "Flip video horizontally: Yes, No") logPathPtr = flag.String("LogPath", defaultLogPath, "Path for logging files (default is /var/log/netsender/)") rtpAddrPtr = flag.String("RtpAddr", "", "Rtp destination address: : (port is generally 6970-6999)") ) @@ -218,16 +218,6 @@ func handleFlags() revid.Config { logger.Log(smartlogger.Error, pkg+"bad packetization argument") } - switch *quantizationModePtr { - case "QuantizationOn": - cfg.QuantizationMode = revid.QuantizationOn - case "QuantizationOff": - cfg.QuantizationMode = revid.QuantizationOff - case "": - default: - logger.Log(smartlogger.Error, pkg+"bad quantization mode argument") - } - switch *verbosityPtr { case "No": cfg.LogLevel = smartlogger.Fatal @@ -239,32 +229,10 @@ func handleFlags() revid.Config { logger.Log(smartlogger.Error, pkg+"bad verbosity argument") } - switch *horizontalFlipPtr { - case "No": - cfg.FlipHorizontal = false - case "Yes": - cfg.FlipHorizontal = true - case "": - cfg.FlipHorizontal = false - default: - logger.Log(smartlogger.Error, pkg+"bad horizontal flip option") - } - - switch *verticalFlipPtr { - case "No": - cfg.FlipVertical = false - case "Yes": - cfg.FlipVertical = true - case "": - cfg.FlipVertical = false - default: - logger.Log(smartlogger.Error, pkg+"bad vertical flip option") - } - - fpc, err := strconv.Atoi(*framesPerClipPtr) - if err == nil && fpc > 0 { - cfg.FramesPerClip = fpc - } + cfg.Quantize = *quantizePtr + cfg.FlipHorizontal = *horizontalFlipPtr + cfg.FlipVertical = *verticalFlipPtr + cfg.FramesPerClip = *framesPerClipPtr cfg.RtmpUrl = *rtmpUrlPtr cfg.Bitrate = *bitratePtr cfg.OutputFileName = *outputFileNamePtr @@ -274,7 +242,6 @@ func handleFlags() revid.Config { cfg.FrameRate = *frameRatePtr cfg.HttpAddress = *httpAddressPtr cfg.Quantization = *quantizationPtr - cfg.Timeout = *timeoutPtr cfg.IntraRefreshPeriod = *intraRefreshPeriodPtr cfg.RtpAddress = *rtpAddrPtr @@ -406,79 +373,76 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m continue } case "FramesPerClip": - fpc, err := strconv.Atoi(value) - if fpc > 0 && err == nil { - cfg.FramesPerClip = fpc - } else { - logger.Log(smartlogger.Warning, pkg+"invalid FramesPerClip param", "value", value) + f, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid framesperclip param", "value", value) + break } + cfg.FramesPerClip = uint(f) case "RtmpUrl": cfg.RtmpUrl = value case "Bitrate": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.Bitrate = value - } else { - logger.Log(smartlogger.Warning, pkg+"invalid Bitrate param", "value", value) + r, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid framerate param", "value", value) + break } + cfg.Bitrate = uint(r) case "OutputFileName": cfg.OutputFileName = value case "InputFileName": cfg.InputFileName = value case "Height": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.Height = value - } else { - logger.Log(smartlogger.Warning, pkg+"invalid Height param", "value", value) + h, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid height param", "value", value) + break } + cfg.Height = uint(h) case "Width": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.Width = value - } else { - logger.Log(smartlogger.Warning, pkg+"invalid Width param", "value", value) + w, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid width param", "value", value) + break } + cfg.Width = uint(w) case "FrameRate": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.FrameRate = value - } else { - logger.Log(smartlogger.Warning, pkg+"invalid FrameRate param", "value", value) + r, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid framerate param", "value", value) + break } + cfg.FrameRate = uint(r) case "HttpAddress": cfg.HttpAddress = value case "Quantization": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.Quantization = value - } else { - logger.Log(smartlogger.Warning, pkg+"invalid Quantization param", "value", value) - } - case "Timeout": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.Timeout = value + q, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid quantization param", "value", value) + break } + cfg.Quantization = uint(q) case "IntraRefreshPeriod": - asInt, err := strconv.Atoi(value) - if asInt > 0 && err == nil { - cfg.IntraRefreshPeriod = value + p, err := strconv.ParseUint(value, 10, 0) + if err != nil { + logger.Log(smartlogger.Warning, pkg+"invalid intrarefreshperiod param", "value", value) + break } + cfg.IntraRefreshPeriod = uint(p) case "HorizontalFlip": - switch value { - case "Yes": + switch strings.ToLower(value) { + case "true": cfg.FlipHorizontal = true - case "No": + case "false": cfg.FlipHorizontal = false default: logger.Log(smartlogger.Warning, pkg+"invalid HorizontalFlip param", "value", value) } case "VerticalFlip": - switch value { - case "Yes": + switch strings.ToLower(value) { + case "true": cfg.FlipVertical = true - case "No": + case "false": cfg.FlipVertical = false default: logger.Log(smartlogger.Warning, pkg+"invalid VerticalFlip param", "value", value) diff --git a/revid/config.go b/revid/config.go index 06a4b8da..8becc91b 100644 --- a/revid/config.go +++ b/revid/config.go @@ -29,7 +29,6 @@ package revid import ( "errors" - "strconv" "bitbucket.org/ausocean/utils/smartlogger" ) @@ -37,32 +36,36 @@ import ( // Config provides parameters relevant to a revid instance. A new config must // be passed to the constructor. type Config struct { - Input uint8 - InputCodec uint8 - Output1 uint8 - Output2 uint8 - RtmpMethod uint8 - Packetization uint8 - QuantizationMode uint8 - LogLevel int8 + LogLevel int8 + + Input uint8 + InputCodec uint8 + Output1 uint8 + Output2 uint8 + RtmpMethod uint8 + Packetization uint8 + + // Quantize specifies whether the input to + // revid will have constant or variable + // bitrate. + Quantize bool // FlipHorizonatla and FlipVertical specify // whether video frames should be flipped. FlipHorizontal bool FlipVertical bool - FramesPerClip int + FramesPerClip uint RtmpUrl string - Bitrate string + Bitrate uint OutputFileName string InputFileName string - Height string - Width string - FrameRate string + Height uint + Width uint + FrameRate uint HttpAddress string - Quantization string - Timeout string - IntraRefreshPeriod string + Quantization uint + IntraRefreshPeriod uint RtpAddress string Logger Logger SendRetry bool @@ -98,20 +101,18 @@ const ( defaultInput = Raspivid defaultOutput = Http defaultPacketization = Flv - defaultFrameRate = "25" - defaultWidth = "1280" - defaultHeight = "720" - defaultIntraRefreshPeriod = "100" - defaultTimeout = "0" - defaultQuantization = "40" - defaultBitrate = "400000" + defaultFrameRate = 25 + defaultWidth = 1280 + defaultHeight = 720 + defaultIntraRefreshPeriod = 100 + defaultTimeout = 0 + defaultQuantization = 40 + defaultBitrate = 400000 defaultQuantizationMode = QuantizationOff defaultFramesPerClip = 1 - defaultVerticalFlip = No - defaultHorizontalFlip = No httpFramesPerClip = 560 defaultInputCodec = H264 - defaultVerbosity = No + defaultVerbosity = No // FIXME(kortschak): This makes no sense whatsoever. No is currently 15. defaultRtpAddr = "localhost:6970" ) @@ -129,17 +130,6 @@ func (c *Config) Validate(r *Revid) error { return errors.New("bad LogLevel defined in config") } - switch c.QuantizationMode { - case QuantizationOn: - case QuantizationOff: - case NothingDefined: - c.Logger.Log(smartlogger.Warning, pkg+"no quantization mode defined, defaulting", - "quantizationMode", QuantizationOff) - c.QuantizationMode = QuantizationOff - default: - return errors.New("bad QuantizationMode defined in config") - } - switch c.Input { case Raspivid: case File: @@ -153,29 +143,23 @@ func (c *Config) Validate(r *Revid) error { switch c.InputCodec { case H264: - if c.Bitrate != "" && c.Quantization != "" { - bitrate, err := strconv.Atoi(c.Bitrate) - if err != nil { - return errors.New("bitrate not an integer") - } - quantization, err := strconv.Atoi(c.Quantization) - if err != nil { - return errors.New("quantization not an integer") - } - if (bitrate > 0 && quantization > 0) || (bitrate == 0 && quantization == 0) { - return errors.New("bad bitrate and quantization combination for H264 input") - } + // FIXME(kortschak): This is not really what we want. + // Configuration really needs to be rethought here. + if c.Quantize && c.Quantization == 0 { + c.Quantization = defaultQuantization + } else { + c.Bitrate = defaultBitrate } + + if (c.Bitrate > 0 && c.Quantization > 0) || (c.Bitrate == 0 && c.Quantization == 0) { + return errors.New("bad bitrate and quantization combination for H264 input") + } + case Mjpeg: - if c.Quantization != "" { - quantization, err := strconv.Atoi(c.Quantization) - if err != nil { - return errors.New("quantization not an integer") - } - if quantization > 0 || c.Bitrate == "" { - return errors.New("bad bitrate or quantization for mjpeg input") - } + if c.Quantization > 0 || c.Bitrate == 0 { + return errors.New("bad bitrate or quantization for mjpeg input") } + case NothingDefined: c.Logger.Log(smartlogger.Warning, pkg+"no input codec defined, defaulting", "inputCodec", defaultInputCodec) @@ -183,6 +167,7 @@ func (c *Config) Validate(r *Revid) error { c.Logger.Log(smartlogger.Warning, pkg+"defaulting quantization", "quantization", defaultQuantization) c.Quantization = defaultQuantization + default: return errors.New("bad input codec defined in config") } @@ -234,73 +219,36 @@ func (c *Config) Validate(r *Revid) error { c.FramesPerClip = defaultFramesPerClip } - if c.Width == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no width defined, defaulting", "width", - defaultWidth) + if c.Width == 0 { + c.Logger.Log(smartlogger.Warning, pkg+"no width defined, defaulting", "width", defaultWidth) c.Width = defaultWidth - } else { - if integer, err := strconv.Atoi(c.Width); integer < 0 || err != nil { - return errors.New("width not unsigned integer") - } } - if c.Height == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no height defined, defaulting", "height", - defaultHeight) + if c.Height == 0 { + c.Logger.Log(smartlogger.Warning, pkg+"no height defined, defaulting", "height", defaultHeight) c.Height = defaultHeight - } else { - if integer, err := strconv.Atoi(c.Height); integer < 0 || err != nil { - return errors.New("height not unsigned integer") - } } - if c.FrameRate == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no frame rate defined, defaulting", "fps", - defaultFrameRate) + if c.FrameRate == 0 { + c.Logger.Log(smartlogger.Warning, pkg+"no frame rate defined, defaulting", "fps", defaultFrameRate) c.FrameRate = defaultFrameRate - } else { - if integer, err := strconv.Atoi(c.FrameRate); integer < 0 || err != nil { - return errors.New("frame rate not unsigned integer") - } } - if c.Bitrate == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no bitrate defined, defaulting", "bitrate", - defaultBitrate) + if c.Bitrate == 0 { + c.Logger.Log(smartlogger.Warning, pkg+"no bitrate defined, defaulting", "bitrate", defaultBitrate) c.Bitrate = defaultBitrate - } else { - if integer, err := strconv.Atoi(c.Bitrate); integer < 0 || err != nil { - return errors.New("bitrate not unsigned integer") - } } - if c.Timeout == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no timeout defined, defaulting", "timeout", defaultTimeout) - c.Timeout = defaultTimeout - } else { - if integer, err := strconv.Atoi(c.Timeout); integer < 0 || err != nil { - return errors.New("timeout not unsigned integer") - } - } - - if c.IntraRefreshPeriod == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh", - defaultIntraRefreshPeriod) + if c.IntraRefreshPeriod == 0 { + c.Logger.Log(smartlogger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh", defaultIntraRefreshPeriod) c.IntraRefreshPeriod = defaultIntraRefreshPeriod - } else { - if integer, err := strconv.Atoi(c.IntraRefreshPeriod); integer < 0 || err != nil { - return errors.New("intra refresh not unsigned integer") - } } - if c.Quantization == "" { - c.Logger.Log(smartlogger.Warning, pkg+"no quantization defined, defaulting", "quantization", - defaultQuantization) + if c.Quantization == 0 { + c.Logger.Log(smartlogger.Warning, pkg+"no quantization defined, defaulting", "quantization", defaultQuantization) c.Quantization = defaultQuantization - } else { - if integer, err := strconv.Atoi(c.Quantization); integer < 0 || integer > 51 || err != nil { - return errors.New("quantisation not unsigned integer or is over threshold") - } + } else if c.Quantization > 51 { + return errors.New("quantisation is over threshold") } if c.RtpAddress == "" { diff --git a/revid/revid.go b/revid/revid.go index e883bb45..4a5d1be7 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -133,7 +133,7 @@ var prevTime = now type packer struct { owner *Revid - packetCount int + packetCount uint } // Write implements the io.Writer interface. @@ -227,7 +227,7 @@ func (r *Revid) reset(config Config) error { } r.destination[outNo] = s case FfmpegRtmp: - s, err := newFfmpegSender(config.RtmpUrl, r.config.FrameRate) + s, err := newFfmpegSender(config.RtmpUrl, fmt.Sprint(r.config.FrameRate)) if err != nil { return err } @@ -247,10 +247,7 @@ func (r *Revid) reset(config Config) error { } r.destination[outNo] = s case Rtp: - // TODO: framerate in config should probably be an int, make conversions early - // when setting config fields in revid-cli - fps, _ := strconv.Atoi(r.config.FrameRate) - s, err := newRtpSender(r.config.RtpAddress, r.config.Logger.Log, fps) + s, err := newRtpSender(r.config.RtpAddress, r.config.Logger.Log, r.config.FrameRate) if err != nil { return err } @@ -292,12 +289,10 @@ func (r *Revid) reset(config Config) error { r.encoder = stream.NopEncoder(&r.packer) case Mpegts: r.config.Logger.Log(smartlogger.Info, pkg+"using MPEGTS packetisation") - frameRate, _ := strconv.ParseFloat(r.config.FrameRate, 64) - r.encoder = mts.NewEncoder(&r.packer, frameRate) + r.encoder = mts.NewEncoder(&r.packer, float64(r.config.FrameRate)) case Flv: r.config.Logger.Log(smartlogger.Info, pkg+"using FLV packetisation") - frameRate, _ := strconv.Atoi(r.config.FrameRate) - r.encoder, err = flv.NewEncoder(&r.packer, true, true, frameRate) + r.encoder, err = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate)) if err != nil { return err } @@ -447,10 +442,10 @@ func (r *Revid) startRaspivid() error { "--output", "-", "--nopreview", "--timeout", disabled, - "--width", r.config.Width, - "--height", r.config.Height, - "--bitrate", r.config.Bitrate, - "--framerate", r.config.FrameRate, + "--width", fmt.Sprint(r.config.Width), + "--height", fmt.Sprint(r.config.Height), + "--bitrate", fmt.Sprint(r.config.Bitrate), + "--framerate", fmt.Sprint(r.config.FrameRate), } if r.config.FlipHorizontal { args = append(args, "--hflip") @@ -465,10 +460,10 @@ func (r *Revid) startRaspivid() error { args = append(args, "--codec", "H264", "--inline", - "--intra", r.config.IntraRefreshPeriod, + "--intra", fmt.Sprint(r.config.IntraRefreshPeriod), ) - if r.config.QuantizationMode == QuantizationOn { - args = append(args, "-qp", r.config.Quantization) + if r.config.Quantize { + args = append(args, "-qp", fmt.Sprint(r.config.Quantization)) } case Mjpeg: args = append(args, "--codec", "MJPEG") @@ -476,11 +471,6 @@ func (r *Revid) startRaspivid() error { r.config.Logger.Log(smartlogger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " ")) r.cmd = exec.Command("raspivid", args...) - d, err := strconv.Atoi(r.config.FrameRate) - if err != nil { - return err - } - delay := time.Second / time.Duration(d) stdout, err := r.cmd.StdoutPipe() if err != nil { return err @@ -491,6 +481,7 @@ func (r *Revid) startRaspivid() error { } r.config.Logger.Log(smartlogger.Info, pkg+"reading camera data") + delay := time.Second / time.Duration(r.config.FrameRate) err = r.lexTo(r.encoder, stdout, delay) r.config.Logger.Log(smartlogger.Info, pkg+"finished reading camera data") return err @@ -498,11 +489,7 @@ func (r *Revid) startRaspivid() error { // setupInputForFile sets things up for getting input from a file func (r *Revid) setupInputForFile() error { - fps, err := strconv.Atoi(r.config.FrameRate) - if err != nil { - return err - } - delay := time.Second / time.Duration(fps) + delay := time.Second / time.Duration(r.config.FrameRate) f, err := os.Open(r.config.InputFileName) if err != nil { diff --git a/revid/senders.go b/revid/senders.go index 9309e940..6606350e 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -371,14 +371,14 @@ type rtpSender struct { encoder *rtp.Encoder } -func newRtpSender(addr string, log func(lvl int8, msg string, args ...interface{}), fps int) (*rtpSender, error) { +func newRtpSender(addr string, log func(lvl int8, msg string, args ...interface{}), fps uint) (*rtpSender, error) { conn, err := net.Dial("udp", addr) if err != nil { return nil, err } s := &rtpSender{ log: log, - encoder: rtp.NewEncoder(conn, fps), + encoder: rtp.NewEncoder(conn, int(fps)), } return s, nil } From 24bc974b01e203cd7b5e2076aa1dd6b547d90657 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 28 Dec 2018 15:29:31 +1030 Subject: [PATCH 010/137] revid: removed fatal log when raspivid doesn't start --- revid/revid.go | 1 - 1 file changed, 1 deletion(-) diff --git a/revid/revid.go b/revid/revid.go index 72fc964f..93bcdf38 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -487,7 +487,6 @@ func (r *Revid) startRaspivid() error { } err = r.cmd.Start() if err != nil { - r.config.Logger.Log(smartlogger.Fatal, pkg+"cannot start revid", "error", err.Error()) return err } From 52b8f7bf54f248cb389e84550ffd770049305245 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 10:23:02 +1030 Subject: [PATCH 011/137] mts: replaced usage of Len with Size for PktSize and PayloadSize etc --- stream/mts/encoder.go | 8 ++++---- stream/mts/mpegts.go | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 2cafc167..41e8522e 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -85,7 +85,7 @@ type Encoder struct { clock time.Duration frameInterval time.Duration ptsOffset time.Duration - tsSpace [PktLen]byte + tsSpace [PktSize]byte pesSpace [pes.MaxPesLen]byte psiCount int @@ -159,7 +159,7 @@ func (e *Encoder) Encode(nalu []byte) error { } } e.psiCount-- - _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:PktLen])) + _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:PktSize])) if err != nil { return err } @@ -181,7 +181,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: patTable, } - _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PktLen])) + _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PktSize])) if err != nil { return err } @@ -204,7 +204,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: pmtTable, } - _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PktLen])) + _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PktSize])) if err != nil { return err } diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 427a4640..d4d755c1 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -33,8 +33,8 @@ import ( ) const ( - PktLen = 188 - PayloadLen = 176 + PktSize = 188 + PayloadSize = 176 ) /* @@ -130,13 +130,13 @@ type Packet struct { // FindPMT will take a clip of mpegts and try to find a PMT table - if one // is found, then it is returned, otherwise nil and an error is returned. func FindPMT(d []byte) (p []byte, err error) { - if len(d) < PktLen { + if len(d) < PktSize { return nil, errors.New("Mmpegts data not of valid length") } - for i := 0; i < len(d); i += PktLen { + for i := 0; i < len(d); i += PktSize { pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2]) if pid == pmtPid { - p = d[i+4 : i+PktLen] + p = d[i+4 : i+PktSize] return } } @@ -146,9 +146,9 @@ func FindPMT(d []byte) (p []byte, err error) { // FillPayload takes a channel and fills the packets Payload field until the // channel is empty or we've the packet reaches capacity func (p *Packet) FillPayload(data []byte) int { - currentPktLength := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + + currentPktSizegth := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + asInt(p.SPF)*1 + asInt(p.TPDF)*1 + len(p.TPD) - p.Payload = make([]byte, PayloadLen-currentPktLength) + p.Payload = make([]byte, PayloadSize-currentPktSizegth) return copy(p.Payload, data) } @@ -169,8 +169,8 @@ func asByte(b bool) byte { // ToByteSlice interprets the fields of the ts packet instance and outputs a // corresponding byte slice func (p *Packet) Bytes(buf []byte) []byte { - if buf == nil || cap(buf) != PktLen { - buf = make([]byte, 0, PktLen) + if buf == nil || cap(buf) != PktSize { + buf = make([]byte, 0, PktSize) } buf = buf[:0] stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 - From 692f5772a13e7159f1598dbddce2bfcf73f4d757 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 10:26:34 +1030 Subject: [PATCH 012/137] mts: removed usage of word len, and removed empty import --- stream/mts/mpegts.go | 4 ++-- stream/mts/pes/pes.go | 8 +++----- stream/mts/psi/op.go | 2 +- stream/mts/psi/psi.go | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index d4d755c1..3756262d 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -146,9 +146,9 @@ func FindPMT(d []byte) (p []byte, err error) { // FillPayload takes a channel and fills the packets Payload field until the // channel is empty or we've the packet reaches capacity func (p *Packet) FillPayload(data []byte) int { - currentPktSizegth := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + + currentPktLen := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + asInt(p.SPF)*1 + asInt(p.TPDF)*1 + len(p.TPD) - p.Payload = make([]byte, PayloadSize-currentPktSizegth) + p.Payload = make([]byte, PayloadSize-currentPktLen) return copy(p.Payload, data) } diff --git a/stream/mts/pes/pes.go b/stream/mts/pes/pes.go index fb0878db..1e085166 100644 --- a/stream/mts/pes/pes.go +++ b/stream/mts/pes/pes.go @@ -26,9 +26,7 @@ LICENSE package pes -import () - -const MaxPesLen = 10000 +const MaxPesSize = 10000 /* The below data struct encapsulates the fields of an PES packet. Below is @@ -95,8 +93,8 @@ type Packet struct { } func (p *Packet) Bytes(buf []byte) []byte { - if buf == nil || cap(buf) != MaxPesLen { - buf = make([]byte, 0, MaxPesLen) + if buf == nil || cap(buf) != MaxPesSize { + buf = make([]byte, 0, MaxPesSize) } buf = buf[:0] buf = append(buf, []byte{ diff --git a/stream/mts/psi/op.go b/stream/mts/psi/op.go index d71dbf61..fdb10bfc 100644 --- a/stream/mts/psi/op.go +++ b/stream/mts/psi/op.go @@ -147,7 +147,7 @@ func updateCrc(out []byte) []byte { // addPadding adds an appropriate amount of padding to a pat or pmt table for // addition to an mpegts packet func addPadding(d []byte) []byte { - for len(d) < PktLen { + for len(d) < PktSize { d = append(d, 0xff) } return d diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 3aa8bcd4..30d4b8de 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -32,7 +32,7 @@ import ( // Misc consts const ( - PktLen = 184 + PktSize = 184 ) // Lengths of section definitions From 1436d1f256b177097e83ebf759c2460d1c18a359 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 10:36:46 +1030 Subject: [PATCH 013/137] mts: fixed build error --- stream/mts/encoder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 41e8522e..2480ae90 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -86,7 +86,7 @@ type Encoder struct { frameInterval time.Duration ptsOffset time.Duration tsSpace [PktSize]byte - pesSpace [pes.MaxPesLen]byte + pesSpace [pes.MaxPesSize]byte psiCount int @@ -131,7 +131,7 @@ func (e *Encoder) Encode(nalu []byte) error { Data: nalu, HeaderLength: 5, } - buf := pesPkt.Bytes(e.pesSpace[:pes.MaxPesLen]) + buf := pesPkt.Bytes(e.pesSpace[:pes.MaxPesSize]) pusi := true for len(buf) != 0 { From a4c73cf6c5a19c111cdac4d9d472d1f2287fa36c Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 15:39:47 +1030 Subject: [PATCH 014/137] av: updated logging to suit changes made on iot and utils --- cmd/revid-cli/main.go | 72 ++++++++++---------- revid/cmd/h264-file-to-flv-rtmp/main.go | 4 +- revid/cmd/h264-file-to-mpgets-file/main.go | 4 +- revid/config.go | 34 +++++----- revid/revid.go | 78 +++++++++++----------- revid/senders.go | 18 ++--- 6 files changed, 105 insertions(+), 105 deletions(-) diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 3fda0862..68d5c791 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -38,7 +38,8 @@ import ( "bitbucket.org/ausocean/av/revid" "bitbucket.org/ausocean/iot/pi/netsender" - "bitbucket.org/ausocean/utils/smartlogger" + "bitbucket.org/ausocean/iot/pi/smartlogger" + "bitbucket.org/ausocean/utils/logger" ) const ( @@ -46,7 +47,7 @@ const ( progName = "revid-cli" // Logging is set to INFO level. - defaultLogVerbosity = smartlogger.Debug + defaultLogVerbosity = logger.Debug ) // Other misc consts @@ -62,7 +63,7 @@ const ( var canProfile = true // The logger that will be used throughout -var logger *smartlogger.Logger +var log *logger.Logger func main() { useNetsender := flag.Bool("NetSender", false, "Are we checking vars through netsender?") @@ -74,7 +75,7 @@ func main() { // run revid for the specified duration rv, _, err := startRevid(nil, cfg) if err != nil { - cfg.Logger.Log(smartlogger.Fatal, pkg+"failed to start revid", err.Error()) + cfg.Logger.Log(logger.Fatal, pkg+"failed to start revid", err.Error()) } time.Sleep(*runDurationPtr) stopRevid(rv) @@ -83,7 +84,7 @@ func main() { err := run(nil, cfg) if err != nil { - logger.Log(smartlogger.Fatal, pkg+"failed to run revid", "error", err.Error()) + log.Log(logger.Fatal, pkg+"failed to run revid", "error", err.Error()) os.Exit(1) } } @@ -117,28 +118,27 @@ func handleFlags() revid.Config { intraRefreshPeriodPtr = flag.Uint("IntraRefreshPeriod", 0, "The IntraRefreshPeriod i.e. how many keyframes we send") verticalFlipPtr = flag.Bool("VerticalFlip", false, "Flip video vertically: Yes, No") horizontalFlipPtr = flag.Bool("HorizontalFlip", false, "Flip video horizontally: Yes, No") - logPathPtr = flag.String("LogPath", defaultLogPath, "Path for logging files (default is /var/log/netsender/)") rtpAddrPtr = flag.String("RtpAddr", "", "Rtp destination address: : (port is generally 6970-6999)") ) flag.Parse() - logger = smartlogger.New(defaultLogVerbosity, *logPathPtr) + log = logger.New(defaultLogVerbosity, &smartlogger.New("/var/log/netsender").LogRoller) - cfg.Logger = logger + cfg.Logger = log if *cpuprofile != "" { if canProfile { f, err := os.Create(*cpuprofile) if err != nil { - logger.Log(smartlogger.Fatal, pkg+"could not create CPU profile", "error", err.Error()) + log.Log(logger.Fatal, pkg+"could not create CPU profile", "error", err.Error()) } if err := pprof.StartCPUProfile(f); err != nil { - logger.Log(smartlogger.Fatal, pkg+"could not start CPU profile", "error", err.Error()) + log.Log(logger.Fatal, pkg+"could not start CPU profile", "error", err.Error()) } defer pprof.StopCPUProfile() } else { - logger.Log(smartlogger.Warning, pkg+"ignoring cpuprofile flag - http/pprof built in.") + log.Log(logger.Warning, pkg+"ignoring cpuprofile flag - http/pprof built in.") } } @@ -149,7 +149,7 @@ func handleFlags() revid.Config { cfg.Input = revid.File case "": default: - logger.Log(smartlogger.Error, pkg+"bad input argument") + log.Log(logger.Error, pkg+"bad input argument") } switch *inputCodecPtr { @@ -157,7 +157,7 @@ func handleFlags() revid.Config { cfg.InputCodec = revid.H264 case "": default: - logger.Log(smartlogger.Error, pkg+"bad input codec argument") + log.Log(logger.Error, pkg+"bad input codec argument") } switch *output1Ptr { @@ -175,7 +175,7 @@ func handleFlags() revid.Config { cfg.Output1 = revid.Rtp case "": default: - logger.Log(smartlogger.Error, pkg+"bad output 1 argument") + log.Log(logger.Error, pkg+"bad output 1 argument") } switch *output2Ptr { @@ -193,7 +193,7 @@ func handleFlags() revid.Config { cfg.Output2 = revid.Rtp case "": default: - logger.Log(smartlogger.Error, pkg+"bad output 2 argument") + log.Log(logger.Error, pkg+"bad output 2 argument") } switch *rtmpMethodPtr { @@ -203,7 +203,7 @@ func handleFlags() revid.Config { cfg.RtmpMethod = revid.LibRtmp case "": default: - logger.Log(smartlogger.Error, pkg+"bad rtmp method argument") + log.Log(logger.Error, pkg+"bad rtmp method argument") } switch *packetizationPtr { @@ -215,18 +215,18 @@ func handleFlags() revid.Config { cfg.Packetization = revid.Flv case "": default: - logger.Log(smartlogger.Error, pkg+"bad packetization argument") + log.Log(logger.Error, pkg+"bad packetization argument") } switch *verbosityPtr { case "No": - cfg.LogLevel = smartlogger.Fatal + cfg.LogLevel = logger.Fatal case "Debug": - cfg.LogLevel = smartlogger.Debug - //logger.SetLevel(smartlogger.Debug) + cfg.LogLevel = logger.Debug + //logger.SetLevel(logger.Debug) case "": default: - logger.Log(smartlogger.Error, pkg+"bad verbosity argument") + log.Log(logger.Error, pkg+"bad verbosity argument") } cfg.Quantize = *quantizePtr @@ -252,10 +252,10 @@ func handleFlags() revid.Config { func run(rv *revid.Revid, cfg revid.Config) error { // initialize NetSender and use NetSender's logger //config.Logger = netsender.Logger() - logger.Log(smartlogger.Info, pkg+"running in NetSender mode") + log.Log(logger.Info, pkg+"running in NetSender mode") var ns netsender.Sender - err := ns.Init(logger, nil, nil, nil) + err := ns.Init(log, nil, nil, nil) if err != nil { return err } @@ -274,7 +274,7 @@ func run(rv *revid.Revid, cfg revid.Config) error { for { if err := send(&ns, rv); err != nil { - logger.Log(smartlogger.Error, pkg+"polling failed", "error", err.Error()) + log.Log(logger.Error, pkg+"polling failed", "error", err.Error()) time.Sleep(netSendRetryTime) continue } @@ -283,14 +283,14 @@ func run(rv *revid.Revid, cfg revid.Config) error { // vars changed vars, err := ns.Vars() if err != nil { - logger.Log(smartlogger.Error, pkg+"netSender failed to get vars", "error", err.Error()) + log.Log(logger.Error, pkg+"netSender failed to get vars", "error", err.Error()) time.Sleep(netSendRetryTime) continue } vs = ns.VarSum() if vars["mode"] == "Paused" { if !paused { - logger.Log(smartlogger.Info, pkg+"pausing revid") + log.Log(logger.Info, pkg+"pausing revid") stopRevid(rv) paused = true } @@ -369,13 +369,13 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m case "FfmpegRtmp": cfg.Output1 = revid.FfmpegRtmp default: - logger.Log(smartlogger.Warning, pkg+"invalid Output1 param", "value", value) + log.Log(logger.Warning, pkg+"invalid Output1 param", "value", value) continue } case "FramesPerClip": f, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid framesperclip param", "value", value) + log.Log(logger.Warning, pkg+"invalid framesperclip param", "value", value) break } cfg.FramesPerClip = uint(f) @@ -384,7 +384,7 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m case "Bitrate": r, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid framerate param", "value", value) + log.Log(logger.Warning, pkg+"invalid framerate param", "value", value) break } cfg.Bitrate = uint(r) @@ -395,21 +395,21 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m case "Height": h, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid height param", "value", value) + log.Log(logger.Warning, pkg+"invalid height param", "value", value) break } cfg.Height = uint(h) case "Width": w, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid width param", "value", value) + log.Log(logger.Warning, pkg+"invalid width param", "value", value) break } cfg.Width = uint(w) case "FrameRate": r, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid framerate param", "value", value) + log.Log(logger.Warning, pkg+"invalid framerate param", "value", value) break } cfg.FrameRate = uint(r) @@ -418,14 +418,14 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m case "Quantization": q, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid quantization param", "value", value) + log.Log(logger.Warning, pkg+"invalid quantization param", "value", value) break } cfg.Quantization = uint(q) case "IntraRefreshPeriod": p, err := strconv.ParseUint(value, 10, 0) if err != nil { - logger.Log(smartlogger.Warning, pkg+"invalid intrarefreshperiod param", "value", value) + log.Log(logger.Warning, pkg+"invalid intrarefreshperiod param", "value", value) break } cfg.IntraRefreshPeriod = uint(p) @@ -436,7 +436,7 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m case "false": cfg.FlipHorizontal = false default: - logger.Log(smartlogger.Warning, pkg+"invalid HorizontalFlip param", "value", value) + log.Log(logger.Warning, pkg+"invalid HorizontalFlip param", "value", value) } case "VerticalFlip": switch strings.ToLower(value) { @@ -445,7 +445,7 @@ func updateRevid(ns *netsender.Sender, rv *revid.Revid, cfg revid.Config, vars m case "false": cfg.FlipVertical = false default: - logger.Log(smartlogger.Warning, pkg+"invalid VerticalFlip param", "value", value) + log.Log(logger.Warning, pkg+"invalid VerticalFlip param", "value", value) } default: } diff --git a/revid/cmd/h264-file-to-flv-rtmp/main.go b/revid/cmd/h264-file-to-flv-rtmp/main.go index 907e414b..5eba1e4e 100644 --- a/revid/cmd/h264-file-to-flv-rtmp/main.go +++ b/revid/cmd/h264-file-to-flv-rtmp/main.go @@ -61,11 +61,11 @@ func main() { RtmpMethod: revid.LibRtmp, RtmpUrl: *rtmpUrlPtr, Packetization: revid.Flv, - Logger: smartlogger.New(smartlogger.Info, logPath), + Logger: smartlogger.New(logger.Info, logPath), } revidInst, err := revid.New(config, nil) if err != nil { - config.Logger.Log(smartlogger.Error, "Should not have got an error!: ", err.Error()) + config.log.Log(logger.Error, "Should not have got an error!: ", err.Error()) return } revidInst.Start() diff --git a/revid/cmd/h264-file-to-mpgets-file/main.go b/revid/cmd/h264-file-to-mpgets-file/main.go index 5530f6c4..218940ab 100644 --- a/revid/cmd/h264-file-to-mpgets-file/main.go +++ b/revid/cmd/h264-file-to-mpgets-file/main.go @@ -52,11 +52,11 @@ func main() { Output1: revid.File, OutputFileName: outputFile, Packetization: revid.Mpegts, - Logger: smartlogger.New(smartlogger.Info, logPath), + Logger: smartlogger.New(logger.Info, logPath), } revidInst, err := revid.New(config, nil) if err != nil { - config.Logger.Log(smartlogger.Error, "Should not have got an error!:", err.Error()) + config.log.Log(logger.Error, "Should not have got an error!:", err.Error()) return } revidInst.Start() diff --git a/revid/config.go b/revid/config.go index 8becc91b..5f98de65 100644 --- a/revid/config.go +++ b/revid/config.go @@ -30,7 +30,7 @@ package revid import ( "errors" - "bitbucket.org/ausocean/utils/smartlogger" + "bitbucket.org/ausocean/utils/logger" ) // Config provides parameters relevant to a revid instance. A new config must @@ -124,7 +124,7 @@ func (c *Config) Validate(r *Revid) error { case No: case NothingDefined: c.LogLevel = defaultVerbosity - c.Logger.Log(smartlogger.Warning, pkg+"no LogLevel mode defined, defaulting", + c.Logger.Log(logger.Warning, pkg+"no LogLevel mode defined, defaulting", "LogLevel", defaultVerbosity) default: return errors.New("bad LogLevel defined in config") @@ -134,7 +134,7 @@ func (c *Config) Validate(r *Revid) error { case Raspivid: case File: case NothingDefined: - c.Logger.Log(smartlogger.Warning, pkg+"no input type defined, defaulting", "input", + c.Logger.Log(logger.Warning, pkg+"no input type defined, defaulting", "input", defaultInput) c.Input = defaultInput default: @@ -161,10 +161,10 @@ func (c *Config) Validate(r *Revid) error { } case NothingDefined: - c.Logger.Log(smartlogger.Warning, pkg+"no input codec defined, defaulting", + c.Logger.Log(logger.Warning, pkg+"no input codec defined, defaulting", "inputCodec", defaultInputCodec) c.InputCodec = defaultInputCodec - c.Logger.Log(smartlogger.Warning, pkg+"defaulting quantization", "quantization", + c.Logger.Log(logger.Warning, pkg+"defaulting quantization", "quantization", defaultQuantization) c.Quantization = defaultQuantization @@ -177,20 +177,20 @@ func (c *Config) Validate(r *Revid) error { case Udp: case Rtmp, FfmpegRtmp: if c.RtmpUrl == "" { - c.Logger.Log(smartlogger.Info, pkg+"no RTMP URL: falling back to HTTP") + c.Logger.Log(logger.Info, pkg+"no RTMP URL: falling back to HTTP") c.Output1 = Http break } - c.Logger.Log(smartlogger.Info, pkg+"defaulting frames per clip for rtmp out", + c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for rtmp out", "framesPerClip", defaultFramesPerClip) c.FramesPerClip = defaultFramesPerClip case NothingDefined: - c.Logger.Log(smartlogger.Warning, pkg+"no output defined, defaulting", "output", + c.Logger.Log(logger.Warning, pkg+"no output defined, defaulting", "output", defaultOutput) c.Output1 = defaultOutput fallthrough case Http, Rtp: - c.Logger.Log(smartlogger.Info, pkg+"defaulting frames per clip for http out", + c.Logger.Log(logger.Info, pkg+"defaulting frames per clip for http out", "framesPerClip", httpFramesPerClip) c.FramesPerClip = httpFramesPerClip default: @@ -203,7 +203,7 @@ func (c *Config) Validate(r *Revid) error { case Udp: case Rtmp, FfmpegRtmp: if c.RtmpUrl == "" { - c.Logger.Log(smartlogger.Info, pkg+"no RTMP URL: falling back to HTTP") + c.Logger.Log(logger.Info, pkg+"no RTMP URL: falling back to HTTP") c.Output2 = Http break } @@ -214,38 +214,38 @@ func (c *Config) Validate(r *Revid) error { } if c.FramesPerClip < 1 { - c.Logger.Log(smartlogger.Warning, pkg+"no FramesPerClip defined, defaulting", + c.Logger.Log(logger.Warning, pkg+"no FramesPerClip defined, defaulting", "framesPerClip", defaultFramesPerClip) c.FramesPerClip = defaultFramesPerClip } if c.Width == 0 { - c.Logger.Log(smartlogger.Warning, pkg+"no width defined, defaulting", "width", defaultWidth) + c.Logger.Log(logger.Warning, pkg+"no width defined, defaulting", "width", defaultWidth) c.Width = defaultWidth } if c.Height == 0 { - c.Logger.Log(smartlogger.Warning, pkg+"no height defined, defaulting", "height", defaultHeight) + c.Logger.Log(logger.Warning, pkg+"no height defined, defaulting", "height", defaultHeight) c.Height = defaultHeight } if c.FrameRate == 0 { - c.Logger.Log(smartlogger.Warning, pkg+"no frame rate defined, defaulting", "fps", defaultFrameRate) + c.Logger.Log(logger.Warning, pkg+"no frame rate defined, defaulting", "fps", defaultFrameRate) c.FrameRate = defaultFrameRate } if c.Bitrate == 0 { - c.Logger.Log(smartlogger.Warning, pkg+"no bitrate defined, defaulting", "bitrate", defaultBitrate) + c.Logger.Log(logger.Warning, pkg+"no bitrate defined, defaulting", "bitrate", defaultBitrate) c.Bitrate = defaultBitrate } if c.IntraRefreshPeriod == 0 { - c.Logger.Log(smartlogger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh", defaultIntraRefreshPeriod) + c.Logger.Log(logger.Warning, pkg+"no intra refresh defined, defaulting", "intraRefresh", defaultIntraRefreshPeriod) c.IntraRefreshPeriod = defaultIntraRefreshPeriod } if c.Quantization == 0 { - c.Logger.Log(smartlogger.Warning, pkg+"no quantization defined, defaulting", "quantization", defaultQuantization) + c.Logger.Log(logger.Warning, pkg+"no quantization defined, defaulting", "quantization", defaultQuantization) c.Quantization = defaultQuantization } else if c.Quantization > 51 { return errors.New("quantisation is over threshold") diff --git a/revid/revid.go b/revid/revid.go index 9e9efbd1..d509c6bb 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -44,8 +44,8 @@ import ( "bitbucket.org/ausocean/av/stream/lex" "bitbucket.org/ausocean/av/stream/mts" "bitbucket.org/ausocean/iot/pi/netsender" + "bitbucket.org/ausocean/utils/logger" "bitbucket.org/ausocean/utils/ring" - "bitbucket.org/ausocean/utils/smartlogger" ) // Misc constants @@ -143,16 +143,16 @@ type packer struct { // write may include a dropped frame. func (p *packer) Write(frame []byte) (int, error) { if len(frame) > ringBufferElementSize { - p.owner.config.Logger.Log(smartlogger.Warning, pkg+"frame was too big", "frame size", len(frame)) + p.owner.config.Logger.Log(logger.Warning, pkg+"frame was too big", "frame size", len(frame)) return len(frame), nil } n, err := p.owner.buffer.Write(frame) if err != nil { if err == ring.ErrDropped { - p.owner.config.Logger.Log(smartlogger.Warning, pkg+"dropped frame", "frame size", len(frame)) + p.owner.config.Logger.Log(logger.Warning, pkg+"dropped frame", "frame size", len(frame)) return len(frame), nil } - p.owner.config.Logger.Log(smartlogger.Error, pkg+"unexpected ring buffer write error", "error", err.Error()) + p.owner.config.Logger.Log(logger.Error, pkg+"unexpected ring buffer write error", "error", err.Error()) return n, err } p.packetCount++ @@ -263,10 +263,10 @@ func (r *Revid) reset(config Config) error { } switch r.config.InputCodec { case H264: - r.config.Logger.Log(smartlogger.Info, pkg+"using H264 lexer") + r.config.Logger.Log(logger.Info, pkg+"using H264 lexer") r.lexTo = lex.H264 case Mjpeg: - r.config.Logger.Log(smartlogger.Info, pkg+"using MJPEG lexer") + r.config.Logger.Log(logger.Info, pkg+"using MJPEG lexer") r.lexTo = lex.MJPEG } @@ -288,10 +288,10 @@ func (r *Revid) reset(config Config) error { } r.encoder = stream.NopEncoder(&r.packer) case Mpegts: - r.config.Logger.Log(smartlogger.Info, pkg+"using MPEGTS packetisation") + r.config.Logger.Log(logger.Info, pkg+"using MPEGTS packetisation") r.encoder = mts.NewEncoder(&r.packer, float64(r.config.FrameRate)) case Flv: - r.config.Logger.Log(smartlogger.Info, pkg+"using FLV packetisation") + r.config.Logger.Log(logger.Info, pkg+"using FLV packetisation") r.encoder, err = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate)) if err != nil { return err @@ -310,29 +310,29 @@ func (r *Revid) IsRunning() bool { // and packetising (if theres packetization) to a defined output. func (r *Revid) Start() { if r.isRunning { - r.config.Logger.Log(smartlogger.Warning, pkg+"revid.Start() called but revid already running") + r.config.Logger.Log(logger.Warning, pkg+"revid.Start() called but revid already running") return } - r.config.Logger.Log(smartlogger.Info, pkg+"starting Revid") - r.config.Logger.Log(smartlogger.Debug, pkg+"setting up output") + r.config.Logger.Log(logger.Info, pkg+"starting Revid") + r.config.Logger.Log(logger.Debug, pkg+"setting up output") r.isRunning = true - r.config.Logger.Log(smartlogger.Info, pkg+"starting output routine") + r.config.Logger.Log(logger.Info, pkg+"starting output routine") go r.outputClips() - r.config.Logger.Log(smartlogger.Info, pkg+"setting up input and receiving content") + r.config.Logger.Log(logger.Info, pkg+"setting up input and receiving content") go r.setupInput() } // Stop halts any processing of video data from a camera or file func (r *Revid) Stop() { if !r.isRunning { - r.config.Logger.Log(smartlogger.Warning, pkg+"revid.Stop() called but revid not running") + r.config.Logger.Log(logger.Warning, pkg+"revid.Stop() called but revid not running") return } - r.config.Logger.Log(smartlogger.Info, pkg+"stopping revid") + r.config.Logger.Log(logger.Info, pkg+"stopping revid") r.isRunning = false - r.config.Logger.Log(smartlogger.Info, pkg+"killing input proccess") + r.config.Logger.Log(logger.Info, pkg+"killing input proccess") // If a cmd process is running, we kill! if r.cmd != nil && r.cmd.Process != nil { r.cmd.Process.Kill() @@ -352,53 +352,53 @@ loop: case nil: // Do nothing. case ring.ErrTimeout: - r.config.Logger.Log(smartlogger.Warning, pkg+"ring buffer read timeout") + r.config.Logger.Log(logger.Warning, pkg+"ring buffer read timeout") continue default: - r.config.Logger.Log(smartlogger.Error, pkg+"unexpected error", "error", err.Error()) + r.config.Logger.Log(logger.Error, pkg+"unexpected error", "error", err.Error()) fallthrough case io.EOF: break loop } count += chunk.Len() - r.config.Logger.Log(smartlogger.Debug, pkg+"about to send") + r.config.Logger.Log(logger.Debug, pkg+"about to send") for i, dest := range r.destination { err = dest.load(chunk) if err != nil { - r.config.Logger.Log(smartlogger.Error, pkg+"failed to load clip to output"+strconv.Itoa(i)) + r.config.Logger.Log(logger.Error, pkg+"failed to load clip to output"+strconv.Itoa(i)) } } for i, dest := range r.destination { err = dest.send() if err == nil { - r.config.Logger.Log(smartlogger.Debug, pkg+"sent clip to output "+strconv.Itoa(i)) + r.config.Logger.Log(logger.Debug, pkg+"sent clip to output "+strconv.Itoa(i)) } else if r.config.SendRetry == false { - r.config.Logger.Log(smartlogger.Warning, pkg+"send to output "+strconv.Itoa(i)+"failed", "error", err.Error()) + r.config.Logger.Log(logger.Warning, pkg+"send to output "+strconv.Itoa(i)+"failed", "error", err.Error()) } else { - r.config.Logger.Log(smartlogger.Error, pkg+"send to output "+strconv.Itoa(i)+ + r.config.Logger.Log(logger.Error, pkg+"send to output "+strconv.Itoa(i)+ "failed, trying again", "error", err.Error()) err = dest.send() if err != nil && chunk.Len() > 11 { - r.config.Logger.Log(smartlogger.Error, pkg+"second send attempted failed, restarting connection", "error", err.Error()) + r.config.Logger.Log(logger.Error, pkg+"second send attempted failed, restarting connection", "error", err.Error()) for err != nil { time.Sleep(sendFailedDelay) if rs, ok := dest.(restarter); ok { - r.config.Logger.Log(smartlogger.Debug, pkg+"restarting session", "session", rs) + r.config.Logger.Log(logger.Debug, pkg+"restarting session", "session", rs) err = rs.restart() if err != nil { - r.config.Logger.Log(smartlogger.Error, pkg+"failed to restart rtmp session", "error", err.Error()) + r.config.Logger.Log(logger.Error, pkg+"failed to restart rtmp session", "error", err.Error()) r.isRunning = false return } - r.config.Logger.Log(smartlogger.Info, pkg+"restarted rtmp session") + r.config.Logger.Log(logger.Info, pkg+"restarted rtmp session") } err = dest.send() if err != nil { - r.config.Logger.Log(smartlogger.Error, pkg+"send failed again, with error", "error", err.Error()) + r.config.Logger.Log(logger.Error, pkg+"send failed again, with error", "error", err.Error()) } } } @@ -409,7 +409,7 @@ loop: for _, dest := range r.destination { dest.release() } - r.config.Logger.Log(smartlogger.Debug, pkg+"done reading that clip from ring buffer") + r.config.Logger.Log(logger.Debug, pkg+"done reading that clip from ring buffer") // Log some information regarding bitrate and ring buffer size if it's time now := time.Now() @@ -417,17 +417,17 @@ loop: if deltaTime > bitrateTime { // FIXME(kortschak): For subsecond deltaTime, this will give infinite bitrate. r.bitrate = int(float64(count*8) / float64(deltaTime/time.Second)) - r.config.Logger.Log(smartlogger.Debug, pkg+"bitrate (bits/s)", "bitrate", r.bitrate) - r.config.Logger.Log(smartlogger.Debug, pkg+"ring buffer size", "value", r.buffer.Len()) + r.config.Logger.Log(logger.Debug, pkg+"bitrate (bits/s)", "bitrate", r.bitrate) + r.config.Logger.Log(logger.Debug, pkg+"ring buffer size", "value", r.buffer.Len()) lastTime = now count = 0 } } - r.config.Logger.Log(smartlogger.Info, pkg+"not outputting clips anymore") + r.config.Logger.Log(logger.Info, pkg+"not outputting clips anymore") for i, dest := range r.destination { err := dest.close() if err != nil { - r.config.Logger.Log(smartlogger.Error, pkg+"failed to close output"+strconv.Itoa(i)+" destination", "error", err.Error()) + r.config.Logger.Log(logger.Error, pkg+"failed to close output"+strconv.Itoa(i)+" destination", "error", err.Error()) } } } @@ -435,7 +435,7 @@ loop: // startRaspivid sets up things for input from raspivid i.e. starts // a raspivid process and pipes it's data output. func (r *Revid) startRaspivid() error { - r.config.Logger.Log(smartlogger.Info, pkg+"starting raspivid") + r.config.Logger.Log(logger.Info, pkg+"starting raspivid") const disabled = "0" args := []string{ @@ -468,7 +468,7 @@ func (r *Revid) startRaspivid() error { case Mjpeg: args = append(args, "--codec", "MJPEG") } - r.config.Logger.Log(smartlogger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " ")) + r.config.Logger.Log(logger.Info, pkg+"raspivid args", "raspividArgs", strings.Join(args, " ")) r.cmd = exec.Command("raspivid", args...) stdout, err := r.cmd.StdoutPipe() @@ -477,13 +477,13 @@ func (r *Revid) startRaspivid() error { } err = r.cmd.Start() if err != nil { - r.config.Logger.Log(smartlogger.Fatal, pkg+"cannot start raspivid", "error", err.Error()) + r.config.Logger.Log(logger.Fatal, pkg+"cannot start raspivid", "error", err.Error()) } - r.config.Logger.Log(smartlogger.Info, pkg+"reading camera data") + r.config.Logger.Log(logger.Info, pkg+"reading camera data") delay := time.Second / time.Duration(r.config.FrameRate) err = r.lexTo(r.encoder, stdout, delay) - r.config.Logger.Log(smartlogger.Info, pkg+"finished reading camera data") + r.config.Logger.Log(logger.Info, pkg+"finished reading camera data") return err } @@ -493,7 +493,7 @@ func (r *Revid) setupInputForFile() error { f, err := os.Open(r.config.InputFileName) if err != nil { - r.config.Logger.Log(smartlogger.Error, err.Error()) + r.config.Logger.Log(logger.Error, err.Error()) r.Stop() return err } diff --git a/revid/senders.go b/revid/senders.go index 6606350e..9c9abca1 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -39,8 +39,8 @@ import ( "bitbucket.org/ausocean/av/stream/mts" "bitbucket.org/ausocean/av/stream/rtp" "bitbucket.org/ausocean/iot/pi/netsender" + "bitbucket.org/ausocean/utils/logger" "bitbucket.org/ausocean/utils/ring" - "bitbucket.org/ausocean/utils/smartlogger" ) // loadSender is a destination to send a *ring.Chunk to. @@ -166,18 +166,18 @@ func (s *httpSender) extractMeta(r string) error { // Extract time from reply t, err := dec.Int("ts") if err != nil { - s.log(smartlogger.Warning, pkg+"No timestamp in reply") + s.log(logger.Warning, pkg+"No timestamp in reply") } else { - s.log(smartlogger.Debug, fmt.Sprintf("%v got timestamp: %v", pkg, t)) + s.log(logger.Debug, fmt.Sprintf("%v got timestamp: %v", pkg, t)) mts.SetTimeStamp(uint64(t)) } // Extract location from reply g, err := dec.String("ll") if err != nil { - s.log(smartlogger.Warning, pkg+"No location in reply") + s.log(logger.Warning, pkg+"No location in reply") } else { - s.log(smartlogger.Debug, fmt.Sprintf("%v got location: %v", pkg, g)) + s.log(logger.Debug, fmt.Sprintf("%v got location: %v", pkg, g)) mts.SetLocation(g) } @@ -269,10 +269,10 @@ func newRtmpSender(url string, timeout uint, retries int, log func(lvl int8, msg if err == nil { break } - log(smartlogger.Error, err.Error()) + log(logger.Error, err.Error()) sess.Close() if n < retries-1 { - log(smartlogger.Info, pkg+"retry rtmp connection") + log(logger.Info, pkg+"retry rtmp connection") } } if err != nil { @@ -315,10 +315,10 @@ func (s *rtmpSender) restart() error { if err == nil { break } - s.log(smartlogger.Error, err.Error()) + s.log(logger.Error, err.Error()) s.sess.Close() if n < s.retries-1 { - s.log(smartlogger.Info, pkg+"retry rtmp connection") + s.log(logger.Info, pkg+"retry rtmp connection") } } return err From 94e43975941208f07aa003847f0b8c2c067f45fa Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 17:21:03 +1030 Subject: [PATCH 015/137] revid: trying to work out bug --- cmd/revid-cli/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 68d5c791..4d408659 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -38,7 +38,6 @@ import ( "bitbucket.org/ausocean/av/revid" "bitbucket.org/ausocean/iot/pi/netsender" - "bitbucket.org/ausocean/iot/pi/smartlogger" "bitbucket.org/ausocean/utils/logger" ) From 686a507ff8dec43486ccd2b8ecac6803fd75d478 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 17:21:33 +1030 Subject: [PATCH 016/137] revid: fixed smartlogger import path --- cmd/revid-cli/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 4d408659..68d5c791 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -38,6 +38,7 @@ import ( "bitbucket.org/ausocean/av/revid" "bitbucket.org/ausocean/iot/pi/netsender" + "bitbucket.org/ausocean/iot/pi/smartlogger" "bitbucket.org/ausocean/utils/logger" ) From cca95f1c5dd78b61300306cb7f8cb2528f630b6d Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 17:32:20 +1030 Subject: [PATCH 017/137] revid: fixed cmds used for testing --- revid/cmd/h264-file-to-flv-rtmp/main.go | 7 ++++--- revid/cmd/h264-file-to-mpgets-file/main.go | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/revid/cmd/h264-file-to-flv-rtmp/main.go b/revid/cmd/h264-file-to-flv-rtmp/main.go index 5eba1e4e..5f79df4a 100644 --- a/revid/cmd/h264-file-to-flv-rtmp/main.go +++ b/revid/cmd/h264-file-to-flv-rtmp/main.go @@ -33,7 +33,8 @@ import ( "time" "bitbucket.org/ausocean/av/revid" - "bitbucket.org/ausocean/utils/smartlogger" + "bitbucket.org/ausocean/iot/pi/smartlogger" + "bitbucket.org/ausocean/utils/logger" ) const ( @@ -61,11 +62,11 @@ func main() { RtmpMethod: revid.LibRtmp, RtmpUrl: *rtmpUrlPtr, Packetization: revid.Flv, - Logger: smartlogger.New(logger.Info, logPath), + Logger: logger.New(logger.Info, &smartlogger.New(logPath).LogRoller), } revidInst, err := revid.New(config, nil) if err != nil { - config.log.Log(logger.Error, "Should not have got an error!: ", err.Error()) + config.Logger.Log(logger.Error, "Should not have got an error!: ", err.Error()) return } revidInst.Start() diff --git a/revid/cmd/h264-file-to-mpgets-file/main.go b/revid/cmd/h264-file-to-mpgets-file/main.go index 218940ab..03a39fde 100644 --- a/revid/cmd/h264-file-to-mpgets-file/main.go +++ b/revid/cmd/h264-file-to-mpgets-file/main.go @@ -31,7 +31,8 @@ import ( "time" "bitbucket.org/ausocean/av/revid" - "bitbucket.org/ausocean/utils/smartlogger" + "bitbucket.org/ausocean/iot/pi/smartlogger" + "bitbucket.org/ausocean/utils/logger" ) const ( @@ -52,11 +53,11 @@ func main() { Output1: revid.File, OutputFileName: outputFile, Packetization: revid.Mpegts, - Logger: smartlogger.New(logger.Info, logPath), + Logger: logger.New(logger.Info, &smartlogger.New(logPath).LogRoller), } revidInst, err := revid.New(config, nil) if err != nil { - config.log.Log(logger.Error, "Should not have got an error!:", err.Error()) + config.Logger.Log(logger.Error, "Should not have got an error!:", err.Error()) return } revidInst.Start() From 04d7540b348496f49ab350ea978bb9b3cff71cfc Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 2 Jan 2019 18:20:30 +1030 Subject: [PATCH 018/137] Replaced Unix sockets with IPConn. Sock_XXX funtions have been left as is for PR readability. --- rtmp/rtmp_headers.go | 3 ++- rtmp/socket.go | 31 +++++++++++++------------------ rtmp/timeval_amd64.go | 7 ------- 3 files changed, 15 insertions(+), 26 deletions(-) delete mode 100644 rtmp/timeval_amd64.go diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 62e667e1..90ae39db 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -139,7 +139,8 @@ type C_RTMPPacket struct { // typedef struct RTMPSockBuf // rtmp.h +127 type C_RTMPSockBuf struct { - conn *net.TCPConn + conn *net.IPConn + timeout int32 sb_size int sb_start int sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const diff --git a/rtmp/socket.go b/rtmp/socket.go index f871cfbc..627db8b0 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -37,8 +37,7 @@ import ( "fmt" "log" "net" - - "golang.org/x/sys/unix" + "time" ) // int RTMP_Connect(RTMP *r, RTMPPacket* cp); @@ -54,11 +53,11 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { } else { hostname = fmt.Sprintf("%s:%d", r.Link.hostname, r.Link.port) } - addr, err := net.ResolveTCPAddr("tcp4", hostname) + addr, err := net.ResolveIPAddr("tcp4", hostname) if err != nil { return false } - r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) + r.m_sb.conn, err = net.DialIP("tcp4", nil, addr) if err != nil { return false } @@ -68,25 +67,14 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { } } - f, err := r.m_sb.conn.File() - if err != nil { - log.Printf("failed to get fd to set timeout: %v", err) - return false - } - tv := setTimeval(int(r.Link.timeout)) - err = unix.SetsockoptTimeval(int(f.Fd()), unix.SOL_SOCKET, unix.SO_RCVTIMEO, &tv) - if err != nil { - log.Printf("failed to set timeout: %v", err) - } - + r.m_sb.timeout= r.Link.timeout r.m_bSendCounter = true - return C_RTMP_Connect1(r, cp) } // int SocksNegotiate(RTMP* r); // rtmp.c +1062 -func C_SocksNegotiate(r *C_RTMP, addr *net.TCPAddr) (ok bool) { +func C_SocksNegotiate(r *C_RTMP, addr *net.IPAddr) (ok bool) { ip := addr.IP.To4() packet := []byte{ 0x4, // SOCKS version @@ -116,7 +104,10 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { if sb.sb_size == 0 { sb.sb_start = 0 } - + err := sb.conn.SetReadDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) + if err != nil { + return -1 + } n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) if err != nil { return 0 @@ -129,6 +120,10 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { // rtmp.c +4297 // TODO replace send with golang net connection send func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) int32 { + err := sb.conn.SetWriteDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) + if err != nil { + return -1 + } n, err := sb.conn.Write(buf) if err != nil { return -1 diff --git a/rtmp/timeval_amd64.go b/rtmp/timeval_amd64.go deleted file mode 100644 index df7f4c4e..00000000 --- a/rtmp/timeval_amd64.go +++ /dev/null @@ -1,7 +0,0 @@ -package rtmp - -import "golang.org/x/sys/unix" - -func setTimeval(sec int) unix.Timeval { - return unix.Timeval{Sec: int64(sec)} -} From 0b2c38117ff2d0591af032adc288769bd9b07f46 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 21:28:20 +1030 Subject: [PATCH 019/137] revid: trying to fix rtmp --- rtmp/rtmp_headers.go | 2 +- rtmp/socket.go | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 90ae39db..eb33814f 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -139,7 +139,7 @@ type C_RTMPPacket struct { // typedef struct RTMPSockBuf // rtmp.h +127 type C_RTMPSockBuf struct { - conn *net.IPConn + conn *net.TCPConn timeout int32 sb_size int sb_start int diff --git a/rtmp/socket.go b/rtmp/socket.go index 627db8b0..35533471 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -53,12 +53,15 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { } else { hostname = fmt.Sprintf("%s:%d", r.Link.hostname, r.Link.port) } - addr, err := net.ResolveIPAddr("tcp4", hostname) + addr, err := net.ResolveTCPAddr("tcp4", hostname) if err != nil { + fmt.Println(err) + fmt.Println("socket.go:58") return false } - r.m_sb.conn, err = net.DialIP("tcp4", nil, addr) + r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) if err != nil { + fmt.Println("socket.go:63") return false } if r.Link.socksport != 0 { @@ -67,14 +70,14 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { } } - r.m_sb.timeout= r.Link.timeout + r.m_sb.timeout = r.Link.timeout r.m_bSendCounter = true return C_RTMP_Connect1(r, cp) } // int SocksNegotiate(RTMP* r); // rtmp.c +1062 -func C_SocksNegotiate(r *C_RTMP, addr *net.IPAddr) (ok bool) { +func C_SocksNegotiate(r *C_RTMP, addr *net.TCPAddr) (ok bool) { ip := addr.IP.To4() packet := []byte{ 0x4, // SOCKS version @@ -106,6 +109,7 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { } err := sb.conn.SetReadDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) if err != nil { + fmt.Println("socket.go:111") return -1 } n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) @@ -122,6 +126,7 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) int32 { err := sb.conn.SetWriteDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) if err != nil { + fmt.Println("socket.go:128") return -1 } n, err := sb.conn.Write(buf) From cec95cd65253d43fba00f61a1165f7cf8280a203 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 21:54:01 +1030 Subject: [PATCH 020/137] rtmp: using TCP rather IP --- revid/revid.go | 1 - 1 file changed, 1 deletion(-) diff --git a/revid/revid.go b/revid/revid.go index d509c6bb..62e22d64 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -490,7 +490,6 @@ func (r *Revid) startRaspivid() error { // setupInputForFile sets things up for getting input from a file func (r *Revid) setupInputForFile() error { delay := time.Second / time.Duration(r.config.FrameRate) - f, err := os.Open(r.config.InputFileName) if err != nil { r.config.Logger.Log(logger.Error, err.Error()) From e340c9b9be5736382dfbf256d3d4427c7bda0825 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 2 Jan 2019 21:55:43 +1030 Subject: [PATCH 021/137] rtmp: removed debug prints --- rtmp/socket.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rtmp/socket.go b/rtmp/socket.go index 35533471..beef8e5c 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -55,13 +55,10 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { } addr, err := net.ResolveTCPAddr("tcp4", hostname) if err != nil { - fmt.Println(err) - fmt.Println("socket.go:58") return false } r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) if err != nil { - fmt.Println("socket.go:63") return false } if r.Link.socksport != 0 { @@ -109,7 +106,6 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { } err := sb.conn.SetReadDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) if err != nil { - fmt.Println("socket.go:111") return -1 } n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) @@ -126,7 +122,6 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) int32 { err := sb.conn.SetWriteDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) if err != nil { - fmt.Println("socket.go:128") return -1 } n, err := sb.conn.Write(buf) From 474b3a324ad17700d85717a7926b8bf53cb16987 Mon Sep 17 00:00:00 2001 From: Saxon Milton Date: Thu, 3 Jan 2019 02:08:55 +0000 Subject: [PATCH 022/137] removing changes not relevant to this PR --- stream/mts/psi/psi_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index d6d6d1cc..6d5595f0 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -91,10 +91,10 @@ var ( } // Bytes representing pmt with time1 and location1 - pmtTimeLocBytes1 = buildPmtTimeLocBytes(locationTstStr1) + pmtTimeLocationBytes1 = buildPmtTimeLocationBytes(locationTstStr1) // bytes representing pmt with with time1 and location 2 - pmtTimeLocBytes2 = buildPmtTimeLocBytes(locationTstStr2) + pmtTimeLocationBytes2 = buildPmtTimeLocationBytes(locationTstStr2) ) // bytesTests contains data for testing the Bytes() funcs for the PSI data struct @@ -257,23 +257,23 @@ func TestLocationGet(t *testing.T) { // TestLocationUpdate checks to see if we can update the location string in a pmt correctly func TestLocationUpdate(t *testing.T) { - cpy := make([]byte, len(pmtTimeLocBytes1)) - copy(cpy, pmtTimeLocBytes1) + cpy := make([]byte, len(pmtTimeLocationBytes1)) + copy(cpy, pmtTimeLocationBytes1) cpy = addCrc(cpy) err := UpdateLocation(cpy, locationTstStr2) cpy = cpy[:len(cpy)-4] if err != nil { t.Errorf("Update time returned err: %v", err) } - if !bytes.Equal(pmtTimeLocBytes2, cpy) { - t.Errorf(errCmp, "TestLocationUpdate", pmtTimeLocBytes2, cpy) + if !bytes.Equal(pmtTimeLocationBytes2, cpy) { + t.Errorf(errCmp, "TestLocationUpdate", pmtTimeLocationBytes2, cpy) } } // buildPmtTimeLocationBytes is a helper function to help construct the byte slices // for pmts with time and location, as the location data field is 32 bytes, i.e. quite large // to type out -func buildPmtTimeLocBytes(tstStr string) []byte { +func buildPmtTimeLocationBytes(tstStr string) []byte { return append(append(append(make([]byte, 0), pmtTimeLocationBytesPart1...), LocationStrBytes(tstStr)...), pmtTimeLocationBytesPart2...) } From 04203ae74438f31444f8f7451f788f011d98e542 Mon Sep 17 00:00:00 2001 From: Saxon Milton Date: Thu, 3 Jan 2019 02:10:32 +0000 Subject: [PATCH 023/137] psi: missed a loc =>location conversion --- stream/mts/psi/psi_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 6d5595f0..ea8920fd 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -189,7 +189,7 @@ var bytesTests = []struct { }, }, }, - want: buildPmtTimeLocBytes(locationTstStr1), + want: buildPmtTimeLocationBytes(locationTstStr1), }, } From fb5acb8e6fb0e1dcb58cddf39d49157d3de0b7be Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 3 Jan 2019 17:56:08 +1030 Subject: [PATCH 024/137] psi: fix crc update issue by not indexing from end of pmt slice, but rather getting syntax section length field and using that to index crc --- stream/mts/psi/op.go | 18 +++++++++++++----- stream/mts/psi/psi.go | 6 ++++++ stream/mts/psi/psi_test.go | 12 ++++++------ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/stream/mts/psi/op.go b/stream/mts/psi/op.go index fdb10bfc..e474081a 100644 --- a/stream/mts/psi/op.go +++ b/stream/mts/psi/op.go @@ -77,6 +77,13 @@ func UpdateTime(dst []byte, t uint64) error { return nil } +// SyntaxSecLenFrom takes a byte slice representation of a psi and extracts +// it's syntax section length +func SyntaxSecLenFrom(p []byte) (l uint8) { + l = uint8(p[syntaxSecLenIndx]) - crcSize + return +} + // TimeFrom takes a byte slice representation of a psi-pmt and extracts it's // timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil // if it does not exist @@ -136,11 +143,12 @@ func addCrc(out []byte) []byte { // updateCrc updates the crc of psi bytes slice that may have been modified func updateCrc(out []byte) []byte { - crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[1:len(out)-4]) - out[len(out)-4] = byte(crc32 >> 24) - out[len(out)-3] = byte(crc32 >> 16) - out[len(out)-2] = byte(crc32 >> 8) - out[len(out)-1] = byte(crc32) + crcIndx := 4 + SyntaxSecLenFrom(out) + crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[1:crcIndx]) + out[crcIndx] = byte(crc32 >> 24) + out[crcIndx+1] = byte(crc32 >> 16) + out[crcIndx+2] = byte(crc32 >> 8) + out[crcIndx+3] = byte(crc32) return out } diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 30d4b8de..3b6b8f7a 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -67,6 +67,12 @@ const ( locationDataSize = 32 // bytes ) +// Other misc consts +const ( + syntaxSecLenIndx = 3 + crcSize = 4 +) + // Program specific information type PSI struct { Pf byte // Point field diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index ea8920fd..c1098432 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -46,7 +46,7 @@ const ( // err message const ( - errCmp = "Incorrect output, for: %v wanted: %v, got: %v" + errCmp = "Incorrect output, for: %v \nwant: %v, \ngot: %v" ) // Test time to bytes test Data @@ -59,7 +59,7 @@ var ( // Parts to construct bytes of pmt with time and bytes var ( pmtTimeLocationBytesPart1 = []byte{ - 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, + 0x00, 0x02, 0xb0, 0x3e, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, byte(timeDescTag), // Descriptor tag for timestamp byte(timeDataSize), // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // Timestamp data @@ -74,7 +74,7 @@ var ( var ( // Bytes representing pmt with tstTime1 pmtTimeBytes1 = []byte{ - 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, + 0x00, 0x02, 0xb0, 0x1c, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, byte(timeDescTag), // Descriptor tag byte(timeDataSize), // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp @@ -83,7 +83,7 @@ var ( // Bytes representing pmt with tstTime 2 pmtTimeBytes2 = []byte{ - 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, + 0x00, 0x02, 0xb0, 0x1c, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, byte(timeDescTag), // Descriptor tag byte(timeDataSize), // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp @@ -124,7 +124,7 @@ var bytesTests = []struct { Pf: 0x00, Tid: 0x02, Ssi: true, - Sl: uint16(0x12), + Sl: uint16(0x1c), Tss: &TSS{ Tide: uint16(0x01), V: 0, @@ -159,7 +159,7 @@ var bytesTests = []struct { Pf: 0x00, Tid: 0x02, Ssi: true, - Sl: uint16(0x12), + Sl: uint16(0x3e), Tss: &TSS{ Tide: uint16(0x01), V: 0, From 04a76e424aad4a21dde0af42c0c5753b6c5959e2 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sat, 5 Jan 2019 08:48:47 +1030 Subject: [PATCH 025/137] Added LogPath and ConfigFile command line flags. --- cmd/revid-cli/main.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 68d5c791..14cffb13 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -119,11 +119,13 @@ func handleFlags() revid.Config { verticalFlipPtr = flag.Bool("VerticalFlip", false, "Flip video vertically: Yes, No") horizontalFlipPtr = flag.Bool("HorizontalFlip", false, "Flip video horizontally: Yes, No") rtpAddrPtr = flag.String("RtpAddr", "", "Rtp destination address: : (port is generally 6970-6999)") + logPathPtr = flag.String("LogPath", defaultLogPath, "The log path") + configFilePtr = flag.String("ConfigFile", "", "NetSender config file") ) flag.Parse() - log = logger.New(defaultLogVerbosity, &smartlogger.New("/var/log/netsender").LogRoller) + log = logger.New(defaultLogVerbosity, &smartlogger.New(*logPathPtr).LogRoller) cfg.Logger = log @@ -229,6 +231,10 @@ func handleFlags() revid.Config { log.Log(logger.Error, pkg+"bad verbosity argument") } + if *configFilePtr != "" { + netsender.ConfigFile = *configFilePtr + } + cfg.Quantize = *quantizePtr cfg.FlipHorizontal = *horizontalFlipPtr cfg.FlipVertical = *verticalFlipPtr From b46e267983475a4c72788d41b95984eb02da99e1 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Sat, 5 Jan 2019 10:33:32 +1030 Subject: [PATCH 026/137] cmd/revid-cli: use sensible default for encoding option --- cmd/revid-cli/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/revid-cli/main.go b/cmd/revid-cli/main.go index 14cffb13..4c991897 100644 --- a/cmd/revid-cli/main.go +++ b/cmd/revid-cli/main.go @@ -209,13 +209,12 @@ func handleFlags() revid.Config { } switch *packetizationPtr { - case "None": + case "", "None": cfg.Packetization = revid.None case "Mpegts": cfg.Packetization = revid.Mpegts case "Flv": cfg.Packetization = revid.Flv - case "": default: log.Log(logger.Error, pkg+"bad packetization argument") } From 05e0672e8972d89c30697abca19dad21c5dcece2 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Sat, 5 Jan 2019 17:56:55 +1030 Subject: [PATCH 027/137] stream/flv: remove unnecessary header write on creation --- revid/revid.go | 5 +---- stream/flv/encoder.go | 10 ++-------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/revid/revid.go b/revid/revid.go index 62e22d64..51a14c91 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -292,10 +292,7 @@ func (r *Revid) reset(config Config) error { r.encoder = mts.NewEncoder(&r.packer, float64(r.config.FrameRate)) case Flv: r.config.Logger.Log(logger.Info, pkg+"using FLV packetisation") - r.encoder, err = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate)) - if err != nil { - return err - } + r.encoder = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate)) } return nil diff --git a/stream/flv/encoder.go b/stream/flv/encoder.go index e15552df..cb663fe6 100644 --- a/stream/flv/encoder.go +++ b/stream/flv/encoder.go @@ -66,19 +66,13 @@ type Encoder struct { } // NewEncoder retuns a new FLV encoder. -func NewEncoder(dst io.Writer, audio, video bool, fps int) (*Encoder, error) { - e := Encoder{ +func NewEncoder(dst io.Writer, audio, video bool, fps int) *Encoder { + return &Encoder{ dst: dst, fps: fps, audio: audio, video: video, } - // TODO(kortschak): Do this lazily. - _, err := e.dst.Write(e.HeaderBytes()) - if err != nil { - return nil, err - } - return &e, nil } // HeaderBytes returns the a From 513b84ad87f4b11723659a5c76f421c1ee7c1035 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sat, 5 Jan 2019 23:13:19 +1030 Subject: [PATCH 028/137] Further cleaned up sockets related code. --- rtmp/rtmp.go | 50 +++++++++----------------- rtmp/rtmp_headers.go | 16 ++++----- rtmp/socket.go | 85 +++++++++++++++----------------------------- 3 files changed, 50 insertions(+), 101 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 5a6bd9e7..88ea8db2 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -40,8 +40,8 @@ import ( "fmt" "log" "math/rand" + "net" "strconv" - "strings" "time" ) @@ -141,10 +141,11 @@ func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { C_RTMP_EnableWrite(rtmp) C_RTMP_SetBufferMS(rtmp, 3600*1000) - if !C_RTMP_Connect(rtmp, nil) { + err := C_RTMP_Connect(rtmp, nil) + if err != nil { C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) - return nil, errors.New("rtmp startSession: Failed to connect!") + return nil, errors.New("rtmp startSession: Failed to connect with error: " + err.Error()) } // TODO: port this @@ -236,30 +237,13 @@ func C_RTMP_SetBufferMS(r *C_RTMP, size int32) { // void SocksSetup(RTMP *r, C_AVal* sockshost); // rtmp.c +410 -func C_SocksSetup(r *C_RTMP, sockshost string) { - if sockshost != "" { - p := strings.SplitN(sockshost, ":", 2) - r.Link.sockshost = p[0] - r.Link.socksport = 1080 - if len(p) != 1 { - port, err := strconv.Atoi(p[1]) - if err != nil { - port = 1080 - log.Println("C_SocksSetup: bad string conversion!") - } - r.Link.socksport = uint16(port) - } - } else { - r.Link.sockshost = "" - r.Link.socksport = 0 - } -} +// DELETED // int RTMP_SetupURL(RTMP *r, char* url); // rtmp.c +757 // NOTE: code dealing with rtmp over http has been disregarded func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) { - r.Link.protocol, r.Link.hostname, r.Link.port, r.Link.app, r.Link.playpath0, ok = C_RTMP_ParseURL(addr) + r.Link.protocol, r.Link.host, r.Link.port, r.Link.app, r.Link.playpath0, ok = C_RTMP_ParseURL(addr) if !ok { return false } @@ -268,15 +252,13 @@ func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) { if r.Link.tcUrl == "" { if r.Link.app != "" { r.Link.tcUrl = fmt.Sprintf("%v://%v:%v/%v", - RTMPProtocolStringsLower[r.Link.protocol], r.Link.hostname, r.Link.port, r.Link.app) + RTMPProtocolStringsLower[r.Link.protocol], r.Link.host, r.Link.port, r.Link.app) r.Link.lFlags |= RTMP_LF_FTCU } else { r.Link.tcUrl = addr } } - C_SocksSetup(r, r.Link.sockshost) - if r.Link.port == 0 { switch { case (r.Link.protocol & RTMP_FEATURE_SSL) != 0: @@ -411,7 +393,6 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { // rtmp.c +1390 func C_ReadN(r *C_RTMP, buf []byte) int { nOriginalSize := len(buf) - r.m_sb.sb_timedout = false for len(buf) != 0 { nBytes := 0 @@ -419,8 +400,9 @@ func C_ReadN(r *C_RTMP, buf []byte) int { avail := int(r.m_sb.sb_size) if avail == 0 { - if C_RTMPSockBuf_Fill(&r.m_sb) < 1 { - if !r.m_sb.sb_timedout { + _, err := C_RTMPSockBuf_Fill(&r.m_sb) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { return 0 } } @@ -439,7 +421,7 @@ func C_ReadN(r *C_RTMP, buf []byte) int { r.m_sb.sb_size -= nRead nBytes = nRead r.m_nBytesIn += int32(nRead) - if r.m_bSendCounter && r.m_nBytesIn > (r.m_nBytesInSent+r.m_nClientBW/10) { + if r.m_nBytesIn > (r.m_nBytesInSent + r.m_nClientBW/10) { if !C_SendBytesReceived(r) { return 0 } @@ -462,20 +444,20 @@ func C_ReadN(r *C_RTMP, buf []byte) int { // rtmp.c +1502 func C_WriteN(r *C_RTMP, buf []byte) (ok bool) { for len(buf) != 0 { - nBytes := int(C_RTMPSockBuf_Send(&r.m_sb, buf)) - if nBytes < 0 { + n, err := C_RTMPSockBuf_Send(&r.m_sb, buf) + if n < 0 { if debugMode { - log.Println("C_WriteN, RTMP send error") + log.Println("C_WriteN, RTMP send error: " + err.Error()) } C_RTMP_Close(r) return false } - if nBytes == 0 { + if n == 0 { break } - buf = buf[nBytes:] + buf = buf[n:] } // !ok here is equivalent to io.ErrShortWrite. diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index eb33814f..2e440713 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -139,12 +139,11 @@ type C_RTMPPacket struct { // typedef struct RTMPSockBuf // rtmp.h +127 type C_RTMPSockBuf struct { - conn *net.TCPConn - timeout int32 - sb_size int - sb_start int - sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const - sb_timedout bool + conn *net.TCPConn + timeout int32 + sb_size int + sb_start int + sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const } // RTMPPacket_IsReady(a) @@ -156,8 +155,7 @@ func C_RTMPPacket_IsReady(p *C_RTMPPacket) bool { // typedef struct RTMP_LNK // rtmp.h +144 type C_RTMP_LNK struct { - hostname string - sockshost string + host string playpath0 string playpath string tcUrl string @@ -173,7 +171,6 @@ type C_RTMP_LNK struct { swfAge int32 protocol int32 timeout int32 - socksport uint16 port uint16 } @@ -201,7 +198,6 @@ type C_RTMP struct { m_nClientBW2 uint8 m_bPlaying bool m_bSendEncoding bool - m_bSendCounter bool m_numInvokes int32 m_methodCalls []C_RTMP_METHOD m_channelsAllocatedIn int32 diff --git a/rtmp/socket.go b/rtmp/socket.go index beef8e5c..fd044e26 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -34,113 +34,84 @@ LICENSE package rtmp import ( + "errors" "fmt" - "log" "net" "time" ) // int RTMP_Connect(RTMP *r, RTMPPacket* cp); // rtmp.c +1032 -func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { - if r.Link.hostname == "" { - return false +func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { + if r.Link.host == "" { + return errors.New("Empty host") } - var hostname string - if r.Link.socksport != 0 { - hostname = fmt.Sprintf("%s:%d", r.Link.sockshost, r.Link.socksport) - } else { - hostname = fmt.Sprintf("%s:%d", r.Link.hostname, r.Link.port) - } - addr, err := net.ResolveTCPAddr("tcp4", hostname) + hostPort := fmt.Sprintf("%s:%d", r.Link.host, r.Link.port) + addr, err := net.ResolveTCPAddr("tcp4", hostPort) if err != nil { - return false + return err } r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) if err != nil { - return false - } - if r.Link.socksport != 0 { - if !C_SocksNegotiate(r, addr) { - return false - } + return err } r.m_sb.timeout = r.Link.timeout - r.m_bSendCounter = true - return C_RTMP_Connect1(r, cp) + if C_RTMP_Connect1(r, cp) { + return nil + } else { + return errors.New("RTMP connection failed") + } } // int SocksNegotiate(RTMP* r); // rtmp.c +1062 -func C_SocksNegotiate(r *C_RTMP, addr *net.TCPAddr) (ok bool) { - ip := addr.IP.To4() - packet := []byte{ - 0x4, // SOCKS version - 0x1, // Establish a TCP/IP stream connection - byte(r.Link.port >> 8), byte(r.Link.port), - ip[0], ip[1], ip[2], ip[3], - 0x0, // Empty user ID string - } - - C_WriteN(r, packet) - - if C_ReadN(r, packet[:8]) != 8 { - return false - } - - if packet[0] == 0x0 && packet[1] == 0x5a { - return true - } - // TODO: use new logger here - log.Println("C_SocksNegotitate: SOCKS returned error code!") - return false -} +// DELETED // int RTMPSockBuf_Fill(RTMPSockBuf* sb); // rtmp.c +4253 -func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) int { +func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) (int, error) { if sb.sb_size == 0 { sb.sb_start = 0 } - err := sb.conn.SetReadDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) + err := sb.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(sb.timeout))) if err != nil { - return -1 + return -1, err } n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) if err != nil { - return 0 + return 0, err } sb.sb_size += n - return n + return n, nil } // int RTMPSockBuf_Send(RTMPSockBuf* sb, const char* buf, int len); // rtmp.c +4297 // TODO replace send with golang net connection send -func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) int32 { - err := sb.conn.SetWriteDeadline(time.Now().Local().Add(time.Second * time.Duration(sb.timeout))) +func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) (int, error) { + err := sb.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(sb.timeout))) if err != nil { - return -1 + return -1, err } n, err := sb.conn.Write(buf) if err != nil { - return -1 + return -1, err } - return int32(n) + return n, nil } // int // RTMPSockBuf_Close(RTMPSockBuf *sb) // rtmp.c +4369 -func C_RTMPSockBuf_Close(sb *C_RTMPSockBuf) int32 { +func C_RTMPSockBuf_Close(sb *C_RTMPSockBuf) error { if sb.conn != nil { err := sb.conn.Close() sb.conn = nil - if err == nil { - return 1 + if err != nil { + return err } } - return 0 + return nil } From 4d767f106c1f6ebcf23a712d003f4e1995d8415b Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 08:25:46 +1030 Subject: [PATCH 029/137] Make for Go idiomatic. --- rtmp/rtmp.go | 8 +++++--- rtmp/socket.go | 37 +++++++++++-------------------------- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 88ea8db2..d4dcd18e 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -447,9 +447,8 @@ func C_WriteN(r *C_RTMP, buf []byte) (ok bool) { n, err := C_RTMPSockBuf_Send(&r.m_sb, buf) if n < 0 { if debugMode { - log.Println("C_WriteN, RTMP send error: " + err.Error()) + log.Printf("C_WriteN, RTMP send error: %v\n", err) } - C_RTMP_Close(r) return false } @@ -1476,7 +1475,10 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { } C_SendDeleteStream(r, float64(i)) } - C_RTMPSockBuf_Close(&r.m_sb) + err := C_RTMPSockBuf_Close(&r.m_sb) + if err != nil && debugMode { + log.Printf("C_RTMPSockBuf_Close error: %v\n", err) + } } r.m_stream_id = -1 diff --git a/rtmp/socket.go b/rtmp/socket.go index fd044e26..80b0e253 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -35,8 +35,8 @@ package rtmp import ( "errors" - "fmt" "net" + "strconv" "time" ) @@ -46,9 +46,7 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { if r.Link.host == "" { return errors.New("Empty host") } - - hostPort := fmt.Sprintf("%s:%d", r.Link.host, r.Link.port) - addr, err := net.ResolveTCPAddr("tcp4", hostPort) + addr, err := net.ResolveTCPAddr("tcp4", r.Link.host+":"+strconv.Itoa(int(r.Link.port))) if err != nil { return err } @@ -56,13 +54,11 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { if err != nil { return err } - r.m_sb.timeout = r.Link.timeout - if C_RTMP_Connect1(r, cp) { - return nil - } else { + if !C_RTMP_Connect1(r, cp) { return errors.New("RTMP connection failed") } + return nil } // int SocksNegotiate(RTMP* r); @@ -76,15 +72,12 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) (int, error) { sb.sb_start = 0 } err := sb.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(sb.timeout))) - if err != nil { - return -1, err - } - n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) if err != nil { return 0, err } + n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) sb.sb_size += n - return n, nil + return n, err } // int RTMPSockBuf_Send(RTMPSockBuf* sb, const char* buf, int len); @@ -93,25 +86,17 @@ func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) (int, error) { func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) (int, error) { err := sb.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(sb.timeout))) if err != nil { - return -1, err + return 0, err } - n, err := sb.conn.Write(buf) - if err != nil { - return -1, err - } - return n, nil + return sb.conn.Write(buf) } // int // RTMPSockBuf_Close(RTMPSockBuf *sb) // rtmp.c +4369 func C_RTMPSockBuf_Close(sb *C_RTMPSockBuf) error { - if sb.conn != nil { - err := sb.conn.Close() - sb.conn = nil - if err != nil { - return err - } + if sb.conn == nil { + return nil } - return nil + return sb.conn.Close() } From e173153ca642b487ca337c7046b5346c50e9ba8d Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 09:20:32 +1030 Subject: [PATCH 030/137] Use Go errors for C_RTMP_ParseURL, C_RTMP_SetupURL, C_RTMP_Connect1, and C_RTMP_ConnectStream. --- rtmp/parseurl.go | 12 ++++++------ rtmp/rtmp.go | 50 +++++++++++++++++++++++++++++++----------------- rtmp/socket.go | 5 +---- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/rtmp/parseurl.go b/rtmp/parseurl.go index 5c8872a5..8faa6414 100644 --- a/rtmp/parseurl.go +++ b/rtmp/parseurl.go @@ -42,11 +42,11 @@ import ( // int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app); // parseurl.c +33 -func C_RTMP_ParseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, ok bool) { +func C_RTMP_ParseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, err error) { u, err := url.Parse(addr) if err != nil { log.Printf("failed to parse addr: %v", err) - return protocol, host, port, app, playpath, false + return protocol, host, port, app, playpath, err } switch u.Scheme { @@ -66,20 +66,20 @@ func C_RTMP_ParseURL(addr string) (protocol int32, host string, port uint16, app protocol = RTMP_PROTOCOL_RTMPTS default: log.Printf("unknown scheme: %q", u.Scheme) - return protocol, host, port, app, playpath, false + return protocol, host, port, app, playpath, errUnknownScheme } host = u.Host if p := u.Port(); p != "" { pi, err := strconv.Atoi(p) if err != nil { - return protocol, host, port, app, playpath, false + return protocol, host, port, app, playpath, err } port = uint16(pi) } if !path.IsAbs(u.Path) { - return protocol, host, port, app, playpath, true + return protocol, host, port, app, playpath, nil } elems := strings.SplitN(u.Path[1:], "/", 3) app = elems[0] @@ -99,5 +99,5 @@ func C_RTMP_ParseURL(addr string) (protocol int32, host string, port uint16, app } } - return protocol, host, port, app, playpath, true + return protocol, host, port, app, playpath, nil } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index d4dcd18e..eb703007 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -128,31 +128,39 @@ var ( } ) +var ( + errUnknownScheme = errors.New("Unknown scheme") + errHandshake = errors.New("Handshake failed") + errConnSend = errors.New("Connection send error") + errConnStream = errors.New("Connection stream error") +) + func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { connect_timeout := int32(timeout) rtmp = C_RTMP_Alloc() C_RTMP_Init(rtmp) rtmp.Link.timeout = connect_timeout - if !C_RTMP_SetupURL(rtmp, u) { + err := C_RTMP_SetupURL(rtmp, u) + if err != nil { C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) - return nil, errors.New("rtmp startSession: Failed to setup URL!") + return nil, err } C_RTMP_EnableWrite(rtmp) C_RTMP_SetBufferMS(rtmp, 3600*1000) - err := C_RTMP_Connect(rtmp, nil) + err = C_RTMP_Connect(rtmp, nil) if err != nil { C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) - return nil, errors.New("rtmp startSession: Failed to connect with error: " + err.Error()) + return nil, err } - // TODO: port this - if !C_RTMP_ConnectStream(rtmp, 0) { + err = C_RTMP_ConnectStream(rtmp, 0) + if err != nil { C_RTMP_Close(rtmp) //C.RTMP_Free(rtmp) - return nil, errors.New("rtmp startSession: Failed to connect stream!") + return nil, err } return rtmp, nil @@ -242,10 +250,10 @@ func C_RTMP_SetBufferMS(r *C_RTMP, size int32) { // int RTMP_SetupURL(RTMP *r, char* url); // rtmp.c +757 // NOTE: code dealing with rtmp over http has been disregarded -func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) { - r.Link.protocol, r.Link.host, r.Link.port, r.Link.app, r.Link.playpath0, ok = C_RTMP_ParseURL(addr) - if !ok { - return false +func C_RTMP_SetupURL(r *C_RTMP, addr string) (err error) { + r.Link.protocol, r.Link.host, r.Link.port, r.Link.app, r.Link.playpath0, err = C_RTMP_ParseURL(addr) + if err != nil { + return nil } r.Link.playpath = r.Link.playpath0 @@ -269,32 +277,33 @@ func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) { r.Link.port = 1935 } } - return true + return nil } // int RTMP_Connect1(RTMP* r, RTMPPacket* cp); // rtmp.c +978 -func C_RTMP_Connect1(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { +func C_RTMP_Connect1(r *C_RTMP, cp *C_RTMPPacket) error { if debugMode { log.Println("... connected, handshaking...") } if !C_HandShake(r, 1) { log.Println("C_RTMP_Connect1: handshake failed!") - return false + return errHandshake } if debugMode { log.Println("... handshaked...") } if !C_SendConnectPacket(r, cp) { log.Println("RTMP connect failed!") - return false + return errConnSend } - return true + return nil } // int RTMP_ConnectStream(RTMP* r, int seekTime); // rtmp.c +1099 -func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) (playing bool) { +// Side effects: r.m_bPlaying is set true upon successful connection +func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { var packet C_RTMPPacket if seekTime > 0 { @@ -323,7 +332,11 @@ func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) (playing bool) { C_RTMPPacket_Free(&packet) } } - return r.m_bPlaying + + if !r.m_bPlaying { + return errConnStream + } + return nil } // int RTMP_ClientPacket() @@ -836,6 +849,7 @@ func C_AV_erase(m []C_RTMP_METHOD, i int) []C_RTMP_METHOD { // int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); // rtmp.c +2912 +// Side effects: r.m_bPlaying set to true upon av_NetStream_Publish_Start func C_HandleInvoke(r *C_RTMP, body []byte) (ok bool) { if body[0] != 0x02 { // TODO use new logger here diff --git a/rtmp/socket.go b/rtmp/socket.go index 80b0e253..22675c14 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -55,10 +55,7 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { return err } r.m_sb.timeout = r.Link.timeout - if !C_RTMP_Connect1(r, cp) { - return errors.New("RTMP connection failed") - } - return nil + return C_RTMP_Connect1(r, cp) } // int SocksNegotiate(RTMP* r); From be1610b67fcc00a0a1cc4c00fd546ca6b9a014b7 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 09:37:21 +1030 Subject: [PATCH 031/137] Use unsigned ints for timeouts. --- rtmp/rtmp.go | 5 ++--- rtmp/rtmp_headers.go | 4 ++-- rtmp/session.go | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index eb703007..342f1369 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -135,11 +135,10 @@ var ( errConnStream = errors.New("Connection stream error") ) -func startSession(rtmp *C_RTMP, u string, timeout uint32) (*C_RTMP, error) { - connect_timeout := int32(timeout) +func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { rtmp = C_RTMP_Alloc() C_RTMP_Init(rtmp) - rtmp.Link.timeout = connect_timeout + rtmp.Link.timeout = timeout err := C_RTMP_SetupURL(rtmp, u) if err != nil { C_RTMP_Close(rtmp) diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 2e440713..6ae32190 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -140,7 +140,7 @@ type C_RTMPPacket struct { // rtmp.h +127 type C_RTMPSockBuf struct { conn *net.TCPConn - timeout int32 + timeout uint sb_size int sb_start int sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const @@ -170,7 +170,7 @@ type C_RTMP_LNK struct { lFlags int32 swfAge int32 protocol int32 - timeout int32 + timeout uint port uint16 } diff --git a/rtmp/session.go b/rtmp/session.go index d06037e4..f07107e7 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -58,7 +58,7 @@ func (s *Session) Open() error { return errors.New("rtmp: attempt to start already running session") } var err error - s.rtmp, err = startSession(s.rtmp, s.url, uint32(s.timeout)) + s.rtmp, err = startSession(s.rtmp, s.url, s.timeout) if s.rtmp == nil { return err } From 26157f47a2e4702168ba79f6ab3009405b7bb7ff Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 10:11:31 +1030 Subject: [PATCH 032/137] Refactored C_ReadN() to use io.ReadFull() and removed now obsolete RTMPSockBuf_Fill(). --- rtmp/rtmp.go | 72 +++++++++++++++----------------------------- rtmp/rtmp_headers.go | 4 +-- rtmp/socket.go | 14 ++------- 3 files changed, 28 insertions(+), 62 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 342f1369..f4790488 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -38,9 +38,9 @@ import ( "encoding/binary" "errors" "fmt" + "io" "log" "math/rand" - "net" "strconv" "time" ) @@ -404,52 +404,31 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { // int ReadN(RTMP* r, char* buffer, int n); // rtmp.c +1390 func C_ReadN(r *C_RTMP, buf []byte) int { - nOriginalSize := len(buf) - - for len(buf) != 0 { - nBytes := 0 - var nRead int - - avail := int(r.m_sb.sb_size) - if avail == 0 { - _, err := C_RTMPSockBuf_Fill(&r.m_sb) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return 0 - } - } - avail = int(r.m_sb.sb_size) - } - - if len(buf) < avail { - nRead = len(buf) - } else { - nRead = avail - } - - if nRead > 0 { - copy(buf, r.m_sb.sb_buf[r.m_sb.sb_start:][:nRead]) - r.m_sb.sb_start += nRead - r.m_sb.sb_size -= nRead - nBytes = nRead - r.m_nBytesIn += int32(nRead) - if r.m_nBytesIn > (r.m_nBytesInSent + r.m_nClientBW/10) { - if !C_SendBytesReceived(r) { - return 0 - } - } - } - - if nBytes == 0 { - log.Println("RTMP socket closed by peer") - C_RTMP_Close(r) - break - } - - buf = buf[nBytes:] + err := r.m_sb.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(r.m_sb.timeout))) + if err != nil { + return 0 } - - return nOriginalSize - len(buf) + n, err := io.ReadFull(r.m_sb.conn, buf) + if err != nil { + if debugMode { + log.Printf("C_ReadN error: %v\n", err) + } + return 0 + } + if n == 0 { + if debugMode { + log.Println("RTMP socket closed by peer") + } + C_RTMP_Close(r) + return 0 + } + r.m_nBytesIn += int32(n) + if r.m_nBytesIn > (r.m_nBytesInSent + r.m_nClientBW/10) { + if !C_SendBytesReceived(r) { + return 0 + } + } + return n } // int WriteN(RTMP* r, const char* buffer, int n); @@ -1519,7 +1498,6 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { r.m_numInvokes = 0 r.m_bPlaying = false - r.m_sb.sb_size = 0 r.m_msgCounter = 0 r.m_resplen = 0 diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 6ae32190..e1ea2168 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -138,12 +138,10 @@ type C_RTMPPacket struct { // typedef struct RTMPSockBuf // rtmp.h +127 +// TODO: Incorporate what is left of C_RTMPSockBuf into C_RTMP_LNK type C_RTMPSockBuf struct { conn *net.TCPConn timeout uint - sb_size int - sb_start int - sb_buf [RTMP_BUFFER_CACHE_SIZE]byte // port const } // RTMPPacket_IsReady(a) diff --git a/rtmp/socket.go b/rtmp/socket.go index 22675c14..453d079c 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -64,18 +64,8 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { // int RTMPSockBuf_Fill(RTMPSockBuf* sb); // rtmp.c +4253 -func C_RTMPSockBuf_Fill(sb *C_RTMPSockBuf) (int, error) { - if sb.sb_size == 0 { - sb.sb_start = 0 - } - err := sb.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(sb.timeout))) - if err != nil { - return 0, err - } - n, err := sb.conn.Read(sb.sb_buf[sb.sb_start+sb.sb_size:]) - sb.sb_size += n - return n, err -} +// DELETED + // int RTMPSockBuf_Send(RTMPSockBuf* sb, const char* buf, int len); // rtmp.c +4297 From b7871cb2f223f6ec55b0e49238ca7260b06873c7 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 12:06:16 +1030 Subject: [PATCH 033/137] Prefix errors with rtmp: --- rtmp/rtmp.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index f4790488..5c67b2b2 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -129,10 +129,10 @@ var ( ) var ( - errUnknownScheme = errors.New("Unknown scheme") - errHandshake = errors.New("Handshake failed") - errConnSend = errors.New("Connection send error") - errConnStream = errors.New("Connection stream error") + errUnknownScheme = errors.New("rtmp: unknown scheme") + errHandshake = errors.New("rtmp: handshake failed") + errConnSend = errors.New("rtmp: connection send error") + errConnStream = errors.New("rtmp: connection stream error") ) func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { @@ -252,7 +252,7 @@ func C_RTMP_SetBufferMS(r *C_RTMP, size int32) { func C_RTMP_SetupURL(r *C_RTMP, addr string) (err error) { r.Link.protocol, r.Link.host, r.Link.port, r.Link.app, r.Link.playpath0, err = C_RTMP_ParseURL(addr) if err != nil { - return nil + return err } r.Link.playpath = r.Link.playpath0 From 81b92b230201fccb69c93cd255b395bbb7521de2 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 14:42:51 +1030 Subject: [PATCH 034/137] All rmtp functions now return an error (or nothing), except for C_RTMP_IsConnected() which now returns a bool instead of an int. --- rtmp/rtmp.go | 296 ++++++++++++++++++++++++++---------------------- rtmp/session.go | 7 +- rtmp/socket.go | 39 +------ 3 files changed, 165 insertions(+), 177 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 5c67b2b2..3142389e 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -41,6 +41,7 @@ import ( "io" "log" "math/rand" + "net" "strconv" "time" ) @@ -133,6 +134,12 @@ var ( errHandshake = errors.New("rtmp: handshake failed") errConnSend = errors.New("rtmp: connection send error") errConnStream = errors.New("rtmp: connection stream error") + errInvalidHeader = errors.New("rtmp: invalid header") + errInvalidBody = errors.New("rtmp: invalid body") + errTinyPacket = errors.New("rtmp: packet too small") + errEncoding = errors.New("rtmp: encoding error") + errDecoding = errors.New("rtmp: decoding error") + errCopying = errors.New("rtmp: copying error") ) func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { @@ -183,12 +190,11 @@ func C_RTMP_GetTime() int32 { // int RTMPPacket_Alloc(RTMPPacket* p, uint32_t nSize); // rtmp.c +189 -func C_RTMPPacket_Alloc(p *C_RTMPPacket, nSize uint32) (ok bool) { +func C_RTMPPacket_Alloc(p *C_RTMPPacket, nSize uint32) { buf := make([]byte, RTMP_MAX_HEADER_SIZE+nSize) p.m_header = buf p.m_body = buf[RTMP_MAX_HEADER_SIZE:] p.m_nBytesRead = 0 - return true } // void RTMPPacket_Free(RTMPPacket* p); @@ -229,11 +235,11 @@ func C_RTMP_EnableWrite(r *C_RTMP) { // int RTMP_IsConnected(RTMP *r); // rtmp.c +363 -func C_RTMP_IsConnected(r *C_RTMP) int32 { +func C_RTMP_IsConnected(r *C_RTMP) bool { if r.m_sb.conn != nil { - return 1 + return true } - return 0 + return false } // void RTMP_SetBufferMS(RTMP *r, int size); @@ -279,26 +285,41 @@ func C_RTMP_SetupURL(r *C_RTMP, addr string) (err error) { return nil } -// int RTMP_Connect1(RTMP* r, RTMPPacket* cp); -// rtmp.c +978 -func C_RTMP_Connect1(r *C_RTMP, cp *C_RTMPPacket) error { +// int RTMP_Connect(RTMP *r, RTMPPacket* cp); +// rtmp.c +1032 +func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { + addr, err := net.ResolveTCPAddr("tcp4", r.Link.host+":"+strconv.Itoa(int(r.Link.port))) + if err != nil { + return err + } + r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) + if err != nil { + return err + } + r.m_sb.timeout = r.Link.timeout if debugMode { log.Println("... connected, handshaking...") } - if !C_HandShake(r, 1) { + err = C_HandShake(r, 1) + if err != nil { log.Println("C_RTMP_Connect1: handshake failed!") return errHandshake } if debugMode { log.Println("... handshaked...") } - if !C_SendConnectPacket(r, cp) { + err = C_SendConnectPacket(r, cp) + if err != nil { log.Println("RTMP connect failed!") return errConnSend } return nil } +// int RTMP_Connect1(RTMP* r, RTMPPacket* cp); +// rtmp.c +978 +// DELETED - subsumed by RTMP_Connect + // int RTMP_ConnectStream(RTMP* r, int seekTime); // rtmp.c +1099 // Side effects: r.m_bPlaying is set true upon successful connection @@ -311,8 +332,11 @@ func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { r.m_mediaChannel = 0 - // TODO: read packet - for !r.m_bPlaying && C_RTMP_IsConnected(r) != 0 && C_RTMP_ReadPacket(r, &packet) { + for !r.m_bPlaying && C_RTMP_IsConnected(r) { + err := C_RTMP_ReadPacket(r, &packet) + if err != nil { + break + } // TODO: port is ready if C_RTMPPacket_IsReady(&packet) { if packet.m_nBodySize == 0 { @@ -385,7 +409,8 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { // TODO use new logger here //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,packet.m_nBodySize); - if C_HandleInvoke(r, packet.m_body[:packet.m_nBodySize]) { + err := C_HandleInvoke(r, packet.m_body[:packet.m_nBodySize]) + if err != nil { // This will never happen with the methods we implement. log.Println("HasMediaPacket") bHasMediaPacket = 2 @@ -403,60 +428,50 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { // int ReadN(RTMP* r, char* buffer, int n); // rtmp.c +1390 -func C_ReadN(r *C_RTMP, buf []byte) int { +func C_ReadN(r *C_RTMP, buf []byte) error { err := r.m_sb.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(r.m_sb.timeout))) if err != nil { - return 0 + return err } n, err := io.ReadFull(r.m_sb.conn, buf) if err != nil { if debugMode { log.Printf("C_ReadN error: %v\n", err) } - return 0 - } - if n == 0 { - if debugMode { - log.Println("RTMP socket closed by peer") - } C_RTMP_Close(r) - return 0 + return err } r.m_nBytesIn += int32(n) if r.m_nBytesIn > (r.m_nBytesInSent + r.m_nClientBW/10) { - if !C_SendBytesReceived(r) { - return 0 + err := C_SendBytesReceived(r) + if err != nil { + return err } } - return n + return nil } // int WriteN(RTMP* r, const char* buffer, int n); // rtmp.c +1502 -func C_WriteN(r *C_RTMP, buf []byte) (ok bool) { - for len(buf) != 0 { - n, err := C_RTMPSockBuf_Send(&r.m_sb, buf) - if n < 0 { - if debugMode { - log.Printf("C_WriteN, RTMP send error: %v\n", err) - } - C_RTMP_Close(r) - return false - } - - if n == 0 { - break - } - buf = buf[n:] +func C_WriteN(r *C_RTMP, buf []byte) error { + err := r.m_sb.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.m_sb.timeout))) + if err != nil { + return err } - - // !ok here is equivalent to io.ErrShortWrite. - return len(buf) == 0 + _, err = r.m_sb.conn.Write(buf) + if err != nil { + if debugMode { + log.Printf("C_WriteN, RTMP send error: %v\n", err) + } + C_RTMP_Close(r) + return err + } + return nil } // int SendConnectPacket(RTMP* r, RTMPPacket* cp); // rtmp.c +1579 -func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { +func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { if cp != nil { return C_RTMP_SendPacket(r, cp, 1) } @@ -484,60 +499,60 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { enc = C_AMF_EncodeNamedString(enc, av_app, r.Link.app) if enc == nil { - return false + return errEncoding } if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { enc = C_AMF_EncodeNamedString(enc, av_type, av_nonprivate) if enc == nil { - return false + return errEncoding } } if r.Link.flashVer != "" { enc = C_AMF_EncodeNamedString(enc, av_flashVer, r.Link.flashVer) if enc == nil { - return false + return errEncoding } } if r.Link.swfUrl != "" { enc = C_AMF_EncodeNamedString(enc, av_swfUrl, r.Link.swfUrl) if enc == nil { - return false + return errEncoding } } if r.Link.tcUrl != "" { enc = C_AMF_EncodeNamedString(enc, av_tcUrl, r.Link.tcUrl) if enc == nil { - return false + return errEncoding } } if r.Link.protocol&RTMP_FEATURE_WRITE == 0 { enc = C_AMF_EncodeNamedBoolean(enc, av_fpad, false) if enc == nil { - return false + return errEncoding } enc = C_AMF_EncodeNamedNumber(enc, av_capabilities, 15) if enc == nil { - return false + return errEncoding } enc = C_AMF_EncodeNamedNumber(enc, av_audioCodecs, r.m_fAudioCodecs) if enc == nil { - return false + return errEncoding } enc = C_AMF_EncodeNamedNumber(enc, av_videoCodecs, r.m_fVideoCodecs) if enc == nil { - return false + return errEncoding } enc = C_AMF_EncodeNamedNumber(enc, av_videoFunction, 1) if enc == nil { - return false + return errEncoding } if r.Link.pageUrl != "" { enc = C_AMF_EncodeNamedString(enc, av_pageUrl, r.Link.pageUrl) if enc == nil { - return false + return errEncoding } } } @@ -545,12 +560,12 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { if r.m_fEncoding != 0.0 || r.m_bSendEncoding { enc = C_AMF_EncodeNamedNumber(enc, av_objectEncoding, r.m_fEncoding) if enc == nil { - return false + return errEncoding } } if copy(enc, []byte{0, 0, AMF_OBJECT_END}) != 3 { - return false + return errCopying // TODO: is this even possible? } enc = enc[3:] @@ -558,18 +573,18 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { if r.Link.auth != "" { enc = C_AMF_EncodeBoolean(enc, r.Link.lFlags&RTMP_LF_AUTH != 0) if enc == nil { - return false + return errEncoding } enc = C_AMF_EncodeString(enc, r.Link.auth) if enc == nil { - return false + return errEncoding } } for i := range r.Link.extras.o_props { enc = C_AMF_PropEncode(&r.Link.extras.o_props[i], enc) if enc == nil { - return false + return errEncoding } } @@ -580,7 +595,7 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) (ok bool) { // int RTMP_SendCreateStream(RTMP* r); // rtmp.c +1725 -func C_RTMP_SendCreateStream(r *C_RTMP) (ok bool) { +func C_RTMP_SendCreateStream(r *C_RTMP) error { var pbuf [256]byte packet := C_RTMPPacket{ m_nChannel: 0x03, /* control channel (invoke) */ @@ -607,7 +622,7 @@ func C_RTMP_SendCreateStream(r *C_RTMP) (ok bool) { // int SendReleaseStream(RTMP* r); // rtmp.c +1816 -func C_SendReleaseStream(r *C_RTMP) (ok bool) { +func C_SendReleaseStream(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ m_nChannel: 0x03, /* control channel (invoke) */ @@ -628,7 +643,7 @@ func C_SendReleaseStream(r *C_RTMP) (ok bool) { enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) if enc == nil { - return false + return errEncoding } packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) @@ -638,7 +653,7 @@ func C_SendReleaseStream(r *C_RTMP) (ok bool) { // int SendFCPublish(RTMP* r); // rtmp.c +1846 -func C_SendFCPublish(r *C_RTMP) (ok bool) { +func C_SendFCPublish(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ m_nChannel: 0x03, /* control channel (invoke) */ @@ -659,7 +674,7 @@ func C_SendFCPublish(r *C_RTMP) (ok bool) { enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) if enc == nil { - return false + return errEncoding } packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) @@ -669,7 +684,7 @@ func C_SendFCPublish(r *C_RTMP) (ok bool) { // int SendFCUnpublish(RTMP *r); // rtmp.c +1875 -func C_SendFCUnpublish(r *C_RTMP) (ok bool) { +func C_SendFCUnpublish(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ m_nChannel: 0x03, /* control channel (invoke) */ @@ -691,7 +706,7 @@ func C_SendFCUnpublish(r *C_RTMP) (ok bool) { enc = C_AMF_EncodeString(enc, r.Link.playpath) if enc == nil { - return false + return errEncoding } packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) @@ -701,7 +716,7 @@ func C_SendFCUnpublish(r *C_RTMP) (ok bool) { // int SendPublish(RTMP* r); // rtmp.c +1908 -func C_SendPublish(r *C_RTMP) (ok bool) { +func C_SendPublish(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ m_nChannel: 0x04, /* source channel (invoke) */ @@ -721,14 +736,12 @@ func C_SendPublish(r *C_RTMP) (ok bool) { enc[0] = AMF_NULL enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) - if enc == nil { - return false + return errEncoding } - enc = C_AMF_EncodeString(enc, av_live) if enc == nil { - return false + return errEncoding } packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) @@ -739,7 +752,7 @@ func C_SendPublish(r *C_RTMP) (ok bool) { // int // SendDeleteStream(RTMP *r, double dStreamId) // rtmp.c +1942 -func C_SendDeleteStream(r *C_RTMP, dStreamId float64) (ok bool) { +func C_SendDeleteStream(r *C_RTMP, dStreamId float64) error { var pbuf [256]byte packet := C_RTMPPacket{ m_nChannel: 0x03, /* control channel (invoke) */ @@ -754,12 +767,20 @@ func C_SendDeleteStream(r *C_RTMP, dStreamId float64) (ok bool) { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_deleteStream) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] enc = C_AMF_EncodeNumber(enc, dStreamId) - + if enc == nil { + return errEncoding + } packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) /* no response expected */ @@ -768,7 +789,7 @@ func C_SendDeleteStream(r *C_RTMP, dStreamId float64) (ok bool) { // int SendBytesReceived(RTMP* r); // rtmp.c +2080 -func C_SendBytesReceived(r *C_RTMP) (ok bool) { +func C_SendBytesReceived(r *C_RTMP) error { var pbuf [256]byte packet := C_RTMPPacket{ m_nChannel: 0x02, /* control channel (invoke) */ @@ -792,7 +813,7 @@ func C_SendBytesReceived(r *C_RTMP) (ok bool) { // int SendCheckBW(RTMP* r); // rtmp.c +2105 -func C_SendCheckBW(r *C_RTMP) (ok bool) { +func C_SendCheckBW(r *C_RTMP) error { var pbuf [256]byte packet := C_RTMPPacket{ m_nChannel: 0x03, /* control channel (invoke) */ @@ -828,19 +849,14 @@ func C_AV_erase(m []C_RTMP_METHOD, i int) []C_RTMP_METHOD { // int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); // rtmp.c +2912 // Side effects: r.m_bPlaying set to true upon av_NetStream_Publish_Start -func C_HandleInvoke(r *C_RTMP, body []byte) (ok bool) { +func C_HandleInvoke(r *C_RTMP, body []byte) error { if body[0] != 0x02 { - // TODO use new logger here - //RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", - //__FUNCTION__); - return false + return errInvalidBody } var obj C_AMFObject nRes := C_AMF_Decode(&obj, body, 0) if nRes < 0 { - // TODO use new logger here - //RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); - return false + return errDecoding } // NOTE we don't really need this ?? still functions without it @@ -969,7 +985,7 @@ func C_HandleInvoke(r *C_RTMP, body []byte) (ok bool) { leave: C_AMF_Reset(&obj) // None of the methods we implement will result in a true return. - return ok + return nil } // void HandleChangeChunkSize(RTMP* r, const RTMPPacket* packet); @@ -1022,13 +1038,14 @@ func C_EncodeInt32LE(dst []byte, v int32) int32 { // int RTMP_ReadPacket(RTMP* r, RTMPPacket* packet); // rtmp.c +3550 -func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { +func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) error { var hbuf [RTMP_MAX_HEADER_SIZE]byte header := hbuf[:] - if C_ReadN(r, header[:1]) != 1 { + err := C_ReadN(r, header[:1]) + if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet header!") - return false + return err } packet.m_headerType = (header[0] & 0xc0) >> 6 packet.m_nChannel = int32(header[0] & 0x3f) @@ -1036,17 +1053,19 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { switch { case packet.m_nChannel == 0: - if C_ReadN(r, header[:1]) != 1 { + err = C_ReadN(r, header[:1]) + if err != nil { log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header 2nd byte.") - return false + return err } header = header[1:] packet.m_nChannel = int32(header[0]) + 64 case packet.m_nChannel == 1: - if C_ReadN(r, header[:2]) != 2 { + err = C_ReadN(r, header[:2]) + if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet 3rd byte") - return false + return err } header = header[2:] packet.m_nChannel = int32(binary.BigEndian.Uint16(header[:2])) + 64 @@ -1088,9 +1107,12 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { nSize-- - if nSize > 0 && C_ReadN(r, header[:nSize]) != int(nSize) { - log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header.") - return false + if nSize > 0 { + err = C_ReadN(r, header[:nSize]) + if err != nil { + log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header.") + return err + } } hSize := len(hbuf) - len(header) + nSize @@ -1114,9 +1136,10 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { extendedTimestamp := packet.m_nTimeStamp == 0xffffff if extendedTimestamp { - if C_ReadN(r, header[nSize:nSize+4]) != 4 { + err = C_ReadN(r, header[nSize:nSize+4]) + if err != nil { log.Println("RTMPRead_Packet: Failed to read extended timestamp") - return false + return err } // TODO: port this packet.m_nTimeStamp = C_AMF_DecodeInt32(header[nSize : nSize+4]) @@ -1125,10 +1148,7 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { if packet.m_nBodySize > 0 && packet.m_body == nil { // TODO: port this - if !C_RTMPPacket_Alloc(packet, packet.m_nBodySize) { - log.Println("RTMPRead_Packet: failed to allocate packet") - return false - } + C_RTMPPacket_Alloc(packet, packet.m_nBodySize) packet.m_headerType = (hbuf[0] & 0xc0) >> 6 } @@ -1145,9 +1165,10 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { packet.m_chunk.c_chunk = packet.m_body[packet.m_nBytesRead : packet.m_nBytesRead+uint32(nChunk)] } - if C_ReadN(r, packet.m_body[packet.m_nBytesRead:][:nChunk]) != int(nChunk) { + err = C_ReadN(r, packet.m_body[packet.m_nBytesRead:][:nChunk]) + if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet body") - return false + return err } packet.m_nBytesRead += uint32(nChunk) @@ -1176,12 +1197,12 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) (ok bool) { } else { packet.m_body = nil /* so it won't be erased on free */ } - return true + return nil } // int HandShake(RTMP* r, int FP9HandShake); // rtmp.c +3744 -func C_HandShake(r *C_RTMP, FP9HandShake int32) (ok bool) { +func C_HandShake(r *C_RTMP, FP9HandShake int32) error { var clientbuf [RTMP_SIG_SIZE + 1]byte clientsig := clientbuf[1:] @@ -1196,13 +1217,15 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) (ok bool) { clientsig[i] = byte(rand.Intn(256)) } - if !C_WriteN(r, clientbuf[:]) { - return false + err := C_WriteN(r, clientbuf[:]) + if err != nil { + return err } var typ [1]byte - if C_ReadN(r, typ[:]) != 1 { - return false + err = C_ReadN(r, typ[:]) + if err != nil { + return err } if debugMode { @@ -1212,8 +1235,9 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) (ok bool) { log.Printf("C_HandShake: type mismatch: client sent %v, server sent: %v\n", clientbuf[0], typ) } - if C_ReadN(r, serversig[:]) != RTMP_SIG_SIZE { - return false + err = C_ReadN(r, serversig[:]) + if err != nil { + return err } // decode server response @@ -1224,24 +1248,26 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) (ok bool) { // serversig[4], serversig[5], serversig[6], serversig[7]) // 2nd part of handshake - if !C_WriteN(r, serversig[:]) { - return false + err = C_WriteN(r, serversig[:]) + if err != nil { + return err } - if C_ReadN(r, serversig[:]) != RTMP_SIG_SIZE { - return false + err = C_ReadN(r, serversig[:]) + if err != nil { + return err } if !bytes.Equal(serversig[:RTMP_SIG_SIZE], clientbuf[1:RTMP_SIG_SIZE+1]) { log.Printf("Client signature does not match: %q != %q", serversig[:RTMP_SIG_SIZE], clientbuf[1:RTMP_SIG_SIZE+1]) } - return true + return nil } // int RTMP_SendPacket(RTMP* r, RTMPPacket* packet, int queue); // rtmp.c +3896 -func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) (ok bool) { +func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { var prevPacket *C_RTMPPacket var last int @@ -1280,7 +1306,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) (ok bool) { if packet.m_headerType > 3 { log.Printf("Sanity failed! trying to send header of type: 0x%02x.", packet.m_headerType) - return false + return errInvalidHeader } var headBytes []byte @@ -1388,8 +1414,9 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) (ok bool) { nChunkSize = nSize } - if !C_WriteN(r, headBytes[origIdx:][:nChunkSize+hSize]) { - return false + err := C_WriteN(r, headBytes[origIdx:][:nChunkSize+hSize]) + if err != nil { + return err } n := nChunkSize + hSize // Since C_WriteN doesn't return number of bytes written. @@ -1445,7 +1472,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) (ok bool) { } *(r.m_vecChannelsOut[packet.m_nChannel]) = *packet - return true + return nil } // void RTMP_Close(RTMP *r); @@ -1459,7 +1486,7 @@ func C_RTMP_Close(r *C_RTMP) { func C_CloseInternal(r *C_RTMP, reconnect bool) { var i int32 - if C_RTMP_IsConnected(r) != 0 { + if C_RTMP_IsConnected(r) { if r.m_stream_id > 0 { i = r.m_stream_id if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { @@ -1467,7 +1494,7 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { } C_SendDeleteStream(r, float64(i)) } - err := C_RTMPSockBuf_Close(&r.m_sb) + err := r.m_sb.conn.Close() if err != nil && debugMode { log.Printf("C_RTMPSockBuf_Close error: %v\n", err) } @@ -1515,7 +1542,7 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { /// int RTMP_Write(RTMP* r, const char* buf, int size); // rtmp.c +5136 -func C_RTMP_Write(r *C_RTMP, buf []byte) int { +func C_RTMP_Write(r *C_RTMP, buf []byte) error { // TODO: port RTMPPacket var pkt = &r.m_write var enc []byte @@ -1527,9 +1554,7 @@ func C_RTMP_Write(r *C_RTMP, buf []byte) int { for len(buf) != 0 { if pkt.m_nBytesRead == 0 { if size < minDataSize { - log.Printf("size: %d\n", size) - log.Printf("too small \n") - return 0 + return errTinyPacket } if buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V' { @@ -1558,10 +1583,7 @@ func C_RTMP_Write(r *C_RTMP, buf []byte) int { pkt.m_headerType = RTMP_PACKET_SIZE_MEDIUM } // TODO: Port this - if !C_RTMPPacket_Alloc(pkt, pkt.m_nBodySize) { - log.Println("Failed to allocate packet") - return 0 - } + C_RTMPPacket_Alloc(pkt, pkt.m_nBodySize) enc = pkt.m_body[:pkt.m_nBodySize] if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { @@ -1581,21 +1603,19 @@ func C_RTMP_Write(r *C_RTMP, buf []byte) int { pkt.m_nBytesRead += uint32(num) buf = buf[num:] if pkt.m_nBytesRead == pkt.m_nBodySize { - // TODO: Port this - ok := C_RTMP_SendPacket(r, pkt, 0) - // TODO: Port this + err := C_RTMP_SendPacket(r, pkt, 0) C_RTMPPacket_Free(pkt) pkt.m_nBytesRead = 0 - if !ok { - return -1 + if err != nil { + return err } if len(buf) < 4 { - return size + (len(buf) - 4) + return nil } buf = buf[4:] } } - return size + return nil } var rtmpErrs = [...]string{ diff --git a/rtmp/session.go b/rtmp/session.go index f07107e7..ff2cbd83 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -84,13 +84,14 @@ func (s *Session) Write(data []byte) (int, error) { return 0, Err(3) } - if C_RTMP_IsConnected(s.rtmp) == 0 { - //if C.RTMP_IsConnected(s.rtmp) == 0 { + if !C_RTMP_IsConnected(s.rtmp) { return 0, Err(1) } - if C_RTMP_Write(s.rtmp, data) == 0 { + err := C_RTMP_Write(s.rtmp, data) + if err != nil { //if C.RTMP_Write(s.rtmp, (*byte)(unsafe.Pointer(&data[0])), int32(len(data))) == 0 { + // TODO: propagate err return 0, Err(2) } return len(data), nil diff --git a/rtmp/socket.go b/rtmp/socket.go index 453d079c..974b4ebf 100644 --- a/rtmp/socket.go +++ b/rtmp/socket.go @@ -33,30 +33,9 @@ LICENSE package rtmp -import ( - "errors" - "net" - "strconv" - "time" -) - // int RTMP_Connect(RTMP *r, RTMPPacket* cp); // rtmp.c +1032 -func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { - if r.Link.host == "" { - return errors.New("Empty host") - } - addr, err := net.ResolveTCPAddr("tcp4", r.Link.host+":"+strconv.Itoa(int(r.Link.port))) - if err != nil { - return err - } - r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) - if err != nil { - return err - } - r.m_sb.timeout = r.Link.timeout - return C_RTMP_Connect1(r, cp) -} +// MOVED to rtmp.go // int SocksNegotiate(RTMP* r); // rtmp.c +1062 @@ -69,21 +48,9 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { // int RTMPSockBuf_Send(RTMPSockBuf* sb, const char* buf, int len); // rtmp.c +4297 -// TODO replace send with golang net connection send -func C_RTMPSockBuf_Send(sb *C_RTMPSockBuf, buf []byte) (int, error) { - err := sb.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(sb.timeout))) - if err != nil { - return 0, err - } - return sb.conn.Write(buf) -} +// DELETED // int // RTMPSockBuf_Close(RTMPSockBuf *sb) // rtmp.c +4369 -func C_RTMPSockBuf_Close(sb *C_RTMPSockBuf) error { - if sb.conn == nil { - return nil - } - return sb.conn.Close() -} +// DELETED From e876491f24351825bb4d1ee56a09ee867f88a4ba Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 14:44:29 +1030 Subject: [PATCH 035/137] Removed since now obsolete. --- rtmp/socket.go | 56 -------------------------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 rtmp/socket.go diff --git a/rtmp/socket.go b/rtmp/socket.go deleted file mode 100644 index 974b4ebf..00000000 --- a/rtmp/socket.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -NAME - rtmp.go - -DESCRIPTION - See Readme.md - -AUTHORS - Saxon Nelson-Milton - Dan Kortschak - -LICENSE - rtmp.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) - - It is free software: you can redistribute it and/or modify them - under the terms of the GNU General Public License as published by the - Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - It is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License - along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. - - Derived from librtmp under the GNU Lesser General Public License 2.1 - Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org - Copyright (C) 2008-2009 Andrej Stepanchuk - Copyright (C) 2009-2010 Howard Chu -*/ - -package rtmp - -// int RTMP_Connect(RTMP *r, RTMPPacket* cp); -// rtmp.c +1032 -// MOVED to rtmp.go - -// int SocksNegotiate(RTMP* r); -// rtmp.c +1062 -// DELETED - -// int RTMPSockBuf_Fill(RTMPSockBuf* sb); -// rtmp.c +4253 -// DELETED - - -// int RTMPSockBuf_Send(RTMPSockBuf* sb, const char* buf, int len); -// rtmp.c +4297 -// DELETED - -// int -// RTMPSockBuf_Close(RTMPSockBuf *sb) -// rtmp.c +4369 -// DELETED From c7b96f1c31c4f0cd0ee48626da10292362e08ab2 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 15:19:58 +1030 Subject: [PATCH 036/137] Check for an propagate encoding errors. --- rtmp/rtmp.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 3142389e..8e2177db 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -490,10 +490,14 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_connect) - + if enc == nil { + return errEncoding + } r.m_numInvokes += 1 enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) - + if enc == nil { + return errEncoding + } enc[0] = AMF_OBJECT enc = enc[1:] @@ -610,8 +614,14 @@ func C_RTMP_SendCreateStream(r *C_RTMP) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_createStream) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] @@ -637,15 +647,20 @@ func C_SendReleaseStream(r *C_RTMP) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_releaseStream) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) if enc == nil { return errEncoding } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 0) @@ -668,8 +683,14 @@ func C_SendFCPublish(r *C_RTMP) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_FCPublish) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) @@ -699,12 +720,17 @@ func C_SendFCUnpublish(r *C_RTMP) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_FCUnpublish) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) - if enc == nil { return errEncoding } @@ -731,8 +757,14 @@ func C_SendPublish(r *C_RTMP) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av_publish) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] enc = C_AMF_EncodeString(enc, r.Link.playpath) @@ -804,8 +836,10 @@ func C_SendBytesReceived(r *C_RTMP) error { enc := packet.m_body r.m_nBytesInSent = r.m_nBytesIn - C_AMF_EncodeInt32(enc, r.m_nBytesIn) - + enc = C_AMF_EncodeInt32(enc, r.m_nBytesIn) + if enc == nil { + return errEncoding + } packet.m_nBodySize = 4 return C_RTMP_SendPacket(r, &packet, 0) @@ -828,8 +862,14 @@ func C_SendCheckBW(r *C_RTMP) error { enc := packet.m_body enc = C_AMF_EncodeString(enc, av__checkbw) + if enc == nil { + return errEncoding + } r.m_numInvokes++ enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + if enc == nil { + return errEncoding + } enc[0] = AMF_NULL enc = enc[1:] @@ -1588,6 +1628,9 @@ func C_RTMP_Write(r *C_RTMP, buf []byte) error { enc = pkt.m_body[:pkt.m_nBodySize] if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { enc = C_AMF_EncodeString(enc, setDataFrame) + if enc == nil { + return errEncoding + } pkt.m_nBytesRead = uint32(len(pkt.m_body) - len(enc)) } From 474642f53be2afa65ee0e3e0552731869986f472 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 15:35:55 +1030 Subject: [PATCH 037/137] Simplified C_RTMP_IsConnected(). --- rtmp/rtmp.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 8e2177db..b99da20c 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -236,10 +236,7 @@ func C_RTMP_EnableWrite(r *C_RTMP) { // int RTMP_IsConnected(RTMP *r); // rtmp.c +363 func C_RTMP_IsConnected(r *C_RTMP) bool { - if r.m_sb.conn != nil { - return true - } - return false + return r.m_sb.conn != nil } // void RTMP_SetBufferMS(RTMP *r, int size); From 6133032a0580813d050435f7a6be21749b4414ec Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 15:54:49 +1030 Subject: [PATCH 038/137] Moved conn from C_RTMPSockBuf to C_RTMP_LNK and deleted the former. --- rtmp/rtmp.go | 21 ++++++++++----------- rtmp/rtmp_headers.go | 8 ++------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index b99da20c..2be64af1 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -236,7 +236,7 @@ func C_RTMP_EnableWrite(r *C_RTMP) { // int RTMP_IsConnected(RTMP *r); // rtmp.c +363 func C_RTMP_IsConnected(r *C_RTMP) bool { - return r.m_sb.conn != nil + return r.Link.conn != nil } // void RTMP_SetBufferMS(RTMP *r, int size); @@ -289,11 +289,10 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { if err != nil { return err } - r.m_sb.conn, err = net.DialTCP("tcp4", nil, addr) + r.Link.conn, err = net.DialTCP("tcp4", nil, addr) if err != nil { return err } - r.m_sb.timeout = r.Link.timeout if debugMode { log.Println("... connected, handshaking...") } @@ -426,11 +425,11 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { // int ReadN(RTMP* r, char* buffer, int n); // rtmp.c +1390 func C_ReadN(r *C_RTMP, buf []byte) error { - err := r.m_sb.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(r.m_sb.timeout))) + err := r.Link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(r.Link.timeout))) if err != nil { return err } - n, err := io.ReadFull(r.m_sb.conn, buf) + n, err := io.ReadFull(r.Link.conn, buf) if err != nil { if debugMode { log.Printf("C_ReadN error: %v\n", err) @@ -451,11 +450,11 @@ func C_ReadN(r *C_RTMP, buf []byte) error { // int WriteN(RTMP* r, const char* buffer, int n); // rtmp.c +1502 func C_WriteN(r *C_RTMP, buf []byte) error { - err := r.m_sb.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.m_sb.timeout))) + err := r.Link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.Link.timeout))) if err != nil { return err } - _, err = r.m_sb.conn.Write(buf) + _, err = r.Link.conn.Write(buf) if err != nil { if debugMode { log.Printf("C_WriteN, RTMP send error: %v\n", err) @@ -1435,8 +1434,8 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { nChunkSize := int(r.m_outChunkSize) if debugMode { - if r.m_sb.conn != nil { - f, err := r.m_sb.conn.File() + if r.Link.conn != nil { + f, err := r.Link.conn.File() if err != nil { log.Printf("could not get file: %v", err) } else { @@ -1531,14 +1530,14 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { } C_SendDeleteStream(r, float64(i)) } - err := r.m_sb.conn.Close() + err := r.Link.conn.Close() if err != nil && debugMode { log.Printf("C_RTMPSockBuf_Close error: %v\n", err) } } r.m_stream_id = -1 - r.m_sb.conn = nil + r.Link.conn = nil r.m_nBWCheckCounter = 0 r.m_nBytesIn = 0 r.m_nBytesInSent = 0 diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index e1ea2168..53061517 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -138,11 +138,7 @@ type C_RTMPPacket struct { // typedef struct RTMPSockBuf // rtmp.h +127 -// TODO: Incorporate what is left of C_RTMPSockBuf into C_RTMP_LNK -type C_RTMPSockBuf struct { - conn *net.TCPConn - timeout uint -} +// DELETED: subsumed by C_RTMP_LNK // RTMPPacket_IsReady(a) // rtmp.h +142 @@ -170,6 +166,7 @@ type C_RTMP_LNK struct { protocol int32 timeout uint port uint16 + conn *net.TCPConn } // typedef struct RTMPMethod @@ -211,6 +208,5 @@ type C_RTMP struct { m_resplen int32 m_unackd int32 m_write C_RTMPPacket - m_sb C_RTMPSockBuf Link C_RTMP_LNK } From 309680f019759a8dd04100d9e0399dad19c2847a Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 16:13:06 +1030 Subject: [PATCH 039/137] Removed references to C.RTMP_Free(). --- rtmp/rtmp.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 2be64af1..468a7299 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -149,7 +149,6 @@ func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { err := C_RTMP_SetupURL(rtmp, u) if err != nil { C_RTMP_Close(rtmp) - //C.RTMP_Free(rtmp) return nil, err } @@ -158,14 +157,12 @@ func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { err = C_RTMP_Connect(rtmp, nil) if err != nil { C_RTMP_Close(rtmp) - //C.RTMP_Free(rtmp) return nil, err } err = C_RTMP_ConnectStream(rtmp, 0) if err != nil { C_RTMP_Close(rtmp) - //C.RTMP_Free(rtmp) return nil, err } @@ -178,7 +175,6 @@ func endSession(rtmp *C_RTMP) uint32 { } C_RTMP_Close(rtmp) - //C.RTMP_Free(rtmp) return 0 } From 9335e2701d432d5624656aad505c07b034034a72 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 17:17:51 +1030 Subject: [PATCH 040/137] Fixed debug mode for C_RTMP_SendPacket(). --- rtmp/rtmp.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 468a7299..f05769fa 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -1431,16 +1431,12 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { if debugMode { if r.Link.conn != nil { - f, err := r.Link.conn.File() - if err != nil { - log.Printf("could not get file: %v", err) - } else { - log.Printf("C_RTMP_SendPacket: fd=%d, size=%v", f.Fd(), nSize) - } + log.Printf("C_RTMP_SendPacket: %v=>%v, size=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize) } } // TODO(kortschak): Rewrite this horrific peice of premature optimisation. + // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. for nSize+hSize != 0 { if nChunkSize > nSize { nChunkSize = nSize @@ -1450,10 +1446,9 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { if err != nil { return err } - n := nChunkSize + hSize // Since C_WriteN doesn't return number of bytes written. nSize -= nChunkSize - origIdx += n + origIdx += nChunkSize + hSize hSize = 0 if nSize > 0 { From 3dccec4f35344f966f6b97b23781b2242045cb51 Mon Sep 17 00:00:00 2001 From: scruzin Date: Sun, 6 Jan 2019 17:43:48 +1030 Subject: [PATCH 041/137] Removed C_RTMP_SetBufferMS(). --- rtmp/rtmp.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index f05769fa..b649d262 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -153,7 +153,7 @@ func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { } C_RTMP_EnableWrite(rtmp) - C_RTMP_SetBufferMS(rtmp, 3600*1000) + rtmp.m_nBufferMS = 3600 * 1000 err = C_RTMP_Connect(rtmp, nil) if err != nil { C_RTMP_Close(rtmp) @@ -237,9 +237,7 @@ func C_RTMP_IsConnected(r *C_RTMP) bool { // void RTMP_SetBufferMS(RTMP *r, int size); // rtmp.c +381 -func C_RTMP_SetBufferMS(r *C_RTMP, size int32) { - r.m_nBufferMS = size -} +// DELETED // void SocksSetup(RTMP *r, C_AVal* sockshost); // rtmp.c +410 From 647f10f212a6929c30dfae7f0ad7ca7cad2fa1d3 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 08:44:04 +1030 Subject: [PATCH 042/137] Only set ts in C_RTMP_SendPacket() when prevPacket != nil. --- rtmp/rtmp.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index b649d262..ef36a3f1 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -444,6 +444,7 @@ func C_ReadN(r *C_RTMP, buf []byte) error { // int WriteN(RTMP* r, const char* buffer, int n); // rtmp.c +1502 func C_WriteN(r *C_RTMP, buf []byte) error { + //ToDo: consider using a different timeout for writes than for reads err := r.Link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.Link.timeout))) if err != nil { return err @@ -1366,11 +1367,14 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { hSize += cSize } - t := uint32(int(packet.m_nTimeStamp) - last) - if t >= 0xffffff { + var ts uint32 + if prevPacket != nil { + ts = uint32(int(packet.m_nTimeStamp) - last) + } + if ts >= 0xffffff { origIdx -= 4 hSize += 4 - log.Printf("Larger timestamp than 24-bit: 0x%v", t) + log.Printf("Larger timestamp than 24-bit: 0x%v", ts) } headerIdx := origIdx @@ -1399,8 +1403,8 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { } if packetSize[packet.m_headerType] > 1 { - res := t - if t > 0xffffff { + res := ts + if ts > 0xffffff { res = 0xffffff } C_AMF_EncodeInt24(headBytes[headerIdx:], int32(res)) @@ -1419,8 +1423,8 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx += n } - if t >= 0xffffff { - C_AMF_EncodeInt32(headBytes[headerIdx:], int32(t)) + if ts >= 0xffffff { + C_AMF_EncodeInt32(headBytes[headerIdx:], int32(ts)) headerIdx += 4 // 32bits } @@ -1429,7 +1433,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { if debugMode { if r.Link.conn != nil { - log.Printf("C_RTMP_SendPacket: %v=>%v, size=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize) + log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize) } } @@ -1453,7 +1457,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { origIdx -= 1 + cSize hSize = 1 + cSize - if t >= 0xffffff { + if ts >= 0xffffff { origIdx -= 4 hSize += 4 } @@ -1468,9 +1472,9 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headBytes[origIdx+2] = byte(tmp >> 8) } } - if t >= 0xffffff { + if ts >= 0xffffff { extendedTimestamp := headBytes[origIdx+1+cSize:] - C_AMF_EncodeInt32(extendedTimestamp[:4], int32(t)) + C_AMF_EncodeInt32(extendedTimestamp[:4], int32(ts)) } } } From 9b14883bd3e7da55ad8016987b9b29b2c8ecf519 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 10:22:04 +1030 Subject: [PATCH 043/137] Log packetType when sending. --- rtmp/rtmp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index ef36a3f1..a9ee9a55 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -1433,7 +1433,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { if debugMode { if r.Link.conn != nil { - log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize) + log.Printf("C_RTMP_SendPacket: %v->%v, size=%v, type=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize, packet.m_packetType) } } From fc72f0734a7a3691e4da045f27ff5e2045675a66 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 10:30:13 +1030 Subject: [PATCH 044/137] mts/psi: remove read funcs as we're not using them at this time --- stream/mts/psi/psi.go | 103 ------------------------------------------ 1 file changed, 103 deletions(-) diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 56abc260..9d9763ce 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -118,109 +118,6 @@ type Desc struct { Dd []byte // Descriptor data } -// ReadPSI creates a PSI data structure from a given byte slice that represents a PSI -func ReadPSI(data []byte) *PSI { - psi := PSI{} - pos := 0 - psi.Pf = data[pos] - if psi.Pf != 0 { - panic("No support for pointer filler bytes") - } - psi.Tid = data[pos] - pos++ - psi.Ssi = byteToBool(data[pos] & 0x80) - psi.Pb = byteToBool(data[pos] & 0x40) - psi.Sl = uint16(data[pos]&0x03)<<8 | uint16(data[pos+1]) - pos += 2 - psi.Tss = readTSS(data[pos:], &psi) - return &psi -} - -// ReadTSS creates a TSS data structure from a given byte slice that represents a TSS -func readTSS(data []byte, p *PSI) *TSS { - tss := TSS{} - pos := 0 - tss.Tide = uint16(data[pos])<<8 | uint16(data[pos+1]) - pos += 2 - tss.V = (data[pos] & 0x3e) >> 1 - tss.Cni = byteToBool(data[pos] & 0x01) - pos++ - tss.Sn = data[pos] - pos++ - tss.Lsn = data[pos] - pos++ - switch p.Tid { - case PATTableID: - tss.Sd = readPAT(data[pos:], &tss) - case PMTTableID: - tss.Sd = readPMT(data[pos:], &tss) - default: - panic("Can't yet deal with tables that are not PAT or PMT") - } - return &tss -} - -// readPAT creates a pat struct based on a bytes slice representing a pat -func readPAT(data []byte, p *TSS) *PAT { - pat := PAT{} - pos := 0 - pat.Pn = uint16(data[pos])<<8 | uint16(data[pos+1]) - pos += 2 - pat.Pmpid = uint16(data[pos]&0x1f)<<8 | uint16(data[pos+1]) - return &pat -} - -// readPMT creates a pmt struct based on a bytes slice that represents a pmt -func readPMT(data []byte, p *TSS) *PAT { - pmt := PMT{} - pos := 0 - pmt.Pcrpid = uint16(data[pos]&0x1f)<<8 | uint16(data[pos+1]) - pos += 2 - pmt.Pil = uint16(data[pos]&0x03)<<8 | uint16(data[pos+1]) - pos += 2 - if pmt.Pil != 0 { - pmt.Pd = readDescs(data[pos:], int(pmt.Pil)) - } - pos += int(pmt.Pil) - // TODO Read ES stuff - pmt.Essd = readEssd(data[pos:]) - return nil -} - -// readDescs reads provides a slice of Descs given a byte slice that represents Descs -// and the no of bytes that the descs accumilate -func readDescs(data []byte, descLen int) (o []Desc) { - pos := 0 - o = make([]Desc, 1) - o[0].Dt = data[pos] - pos++ - o[0].Dl = data[pos] - pos++ - o[0].Dd = make([]byte, o[0].Dl) - for i := 0; i < int(o[0].Dl); i++ { - o[0].Dd[i] = data[pos] - pos++ - } - if 2+len(o[0].Dd) != descLen { - panic("No support for reading more than one descriptor") - } - return -} - -// readEESD creates an ESSD struct based on a bytes slice that represents ESSD -func readEssd(data []byte) *ESSD { - essd := ESSD{} - pos := 0 - essd.St = data[pos] - pos++ - essd.Epid = uint16(data[pos]&0x1f)<<8 | uint16(data[pos+1]) - pos += 2 - essd.Esil = uint16(data[pos]&0x03)<<8 | uint16(data[pos+1]) - pos += 2 - essd.Esd = readDescs(data[pos:], int(essd.Esil)) - return &essd -} - // Bytes outputs a byte slice representation of the PSI func (p *PSI) Bytes() []byte { out := make([]byte, 4) From 8878063dc8d0f601b5117a9456e84f05adae4d4c Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 10:43:30 +1030 Subject: [PATCH 045/137] revid: clean up vars for use in calculation of time between writes to packer --- revid/revid.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/revid/revid.go b/revid/revid.go index 62e22d64..0c989ba8 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -125,9 +125,6 @@ type Revid struct { isRunning bool } -var now = time.Now() -var prevTime = now - // packer takes data segments and packs them into clips // of the number frames specified in the owners config. type packer struct { @@ -136,6 +133,10 @@ type packer struct { packetCount uint } +// lastTime is used in the packer's Write method to determine how much time has +// passed since last write to packer +var lastTime time.Time + // Write implements the io.Writer interface. // // Unless the ring buffer returns an error, all writes @@ -156,11 +157,11 @@ func (p *packer) Write(frame []byte) (int, error) { return n, err } p.packetCount++ - now = time.Now() - if (p.owner.config.Output1 != Rtmp && now.Sub(prevTime) > clipDuration && p.packetCount%7 == 0) || p.owner.config.Output1 == Rtmp { + now := time.Now() + if (p.owner.config.Output1 != Rtmp && now.Sub(lastTime) > clipDuration && p.packetCount%7 == 0) || p.owner.config.Output1 == Rtmp { p.owner.buffer.Flush() p.packetCount = 0 - prevTime = now + lastTime = now } return len(frame), nil } From 703f0a052382c877a83f1fbf1a4b7ea804362f28 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 10:45:22 +1030 Subject: [PATCH 046/137] revid: cleaned up send logic in httpSender send func --- revid/senders.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/revid/senders.go b/revid/senders.go index 9c9abca1..523bd61e 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -146,11 +146,12 @@ func (s *httpSender) send() error { } var err error var reply string - if send { - reply, _, err = s.client.Send(netsender.RequestRecv, pins) - if err != nil { - return err - } + if !send { + return nil + } + reply, _, err = s.client.Send(netsender.RequestRecv, pins) + if err != nil { + return err } return s.extractMeta(reply) From 7a73f69fe9b592e51ca84ae9049a4747128aec7b Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 11:59:46 +1030 Subject: [PATCH 047/137] psi: MetaData struct => TimeLocation and also not initialising anymore --- stream/mts/encoder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 31fe4923..618817ea 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -41,12 +41,12 @@ const ( psiSendCount = 7 ) -type MetaData struct { +type TimeLocation struct { time uint64 location string } -var metaData = MetaData{time: 0, location: ""} +var metaData = TimeLocation{} func SetTimeStamp(t uint64) { metaData.time = t From 239e1dfc936ce39515d7e0570491264bd6eea5da Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 12:05:05 +1030 Subject: [PATCH 048/137] psi: made TimeLocation struct member vars exported --- stream/mts/encoder.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 618817ea..49294b31 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -42,18 +42,18 @@ const ( ) type TimeLocation struct { - time uint64 - location string + Time uint64 + Location string } var metaData = TimeLocation{} func SetTimeStamp(t uint64) { - metaData.time = t + metaData.Time = t } func SetLocation(g string) { - metaData.location = g + metaData.Location = g } var ( @@ -186,11 +186,11 @@ func (e *Encoder) writePSI() error { } // Update pmt table time and location - err = psi.UpdateTime(pmtTable, metaData.time) + err = psi.UpdateTime(pmtTable, metaData.Time) if err != nil { return err } - err = psi.UpdateLocation(pmtTable, metaData.location) + err = psi.UpdateLocation(pmtTable, metaData.Location) if err != nil { return nil } From a0079ef046daf900807baf10b9cef6a15d68d337 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 12:26:58 +1030 Subject: [PATCH 049/137] psi: created crc.go to put crc stuff in and renamed op.go to helpers.go --- stream/mts/psi/op.go | 145 ------------------------------------------ stream/mts/psi/psi.go | 27 -------- 2 files changed, 172 deletions(-) delete mode 100644 stream/mts/psi/op.go diff --git a/stream/mts/psi/op.go b/stream/mts/psi/op.go deleted file mode 100644 index e78cdac7..00000000 --- a/stream/mts/psi/op.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -NAME - op.go -DESCRIPTION - op.go provides functionality for editing and reading bytes slices - directly in order to insert/read timestamp and location data in psi. - -AUTHOR - Saxon Milton - -LICENSE - op.go is Copyright (C) 2018 the Australian Ocean Lab (AusOcean) - - It is free software: you can redistribute it and/or modify them - under the terms of the GNU General Public License as published by the - Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - It is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License - along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. -*/ - -package psi - -import ( - "bytes" - "encoding/binary" - "errors" - "hash/crc32" - "math/bits" -) - -// TimeBytes takes a timestamp as an uint64 and converts to an 8 byte slice - -// allows for updating of timestamp in pmt time descriptor. -func TimeBytes(t uint64) []byte { - var s [timeDataSize]byte - binary.BigEndian.PutUint64(s[:], t) - return s[:] -} - -// HasTime takes a psi as a byte slice and checks to see if it has a time descriptor -// - if so return nil, otherwise return error -func HasTime(p []byte) error { - if p[timeTagIndx] != timeDescTag { - return errors.New("PSI does not contain a time descriptor, cannot update") - } - return nil -} - -// HasLocation takes a psi as a byte slice and checks to see if it has a location descriptor -// - if so return nil, otherwise return error -func HasLocation(p []byte) error { - if p[locationTagIndx] != locationDescTag { - return errors.New("PSI does not contain a location descriptor, cannot update") - } - return nil -} - -// UpdateTime takes the byte slice representation of a psi-pmt as well as a time -// as an integer and attempts to update the time descriptor in the pmt with the -// given time if the time descriptor exists, otherwise an error is returned -func UpdateTime(dst []byte, t uint64) error { - err := HasTime(dst) - if err != nil { - return err - } - ts := TimeBytes(uint64(t)) - for i := range dst[timeDataIndx : timeDataIndx+timeDataSize] { - dst[i+timeDataIndx] = ts[i] - } - dst = updateCrc(dst) - return nil -} - -// TimeFrom takes a byte slice representation of a psi-pmt and extracts it's -// timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil -// if it does not exist -func TimeFrom(p []byte) (t uint64, err error) { - err = HasTime(p) - if err != nil { - return 0, err - } - t = binary.BigEndian.Uint64(p[timeDataIndx : timeDataIndx+timeDataSize]) - return t, nil -} - -// LocationFrom takes a byte slice representation of a psi-pmt and extracts it's -// timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil -// if it does not exist -func LocationFrom(p []byte) (g string, err error) { - err = HasLocation(p) - if err != nil { - return "", err - } - gBytes := p[locationDataIndx : locationDataIndx+locationDataSize] - gBytes = bytes.Trim(gBytes, "\x00") - g = string(gBytes) - return g, nil -} - -// LocationStrBytes take a string of location data and converts to a 32 byte slice - -// easy update of slice representation of a pmt with location descriptor -func LocationStrBytes(s string) []byte { - var b [locationDataSize]byte - copy(b[:], s) - return b[:] -} - -// UpdateLocation takes a byte slice representation of a psi-pmt containing a location -// descriptor and attempts to update the location data value with the passed string. -// If the psi does not contain a location descriptor, and error is returned. -func UpdateLocation(d []byte, s string) error { - err := HasLocation(d) - if err != nil { - return err - } - gb := LocationStrBytes(s) - for i := range d[locationDataIndx : locationDataIndx+locationDataSize] { - d[i+locationDataIndx] = gb[i] - } - d = updateCrc(d) - return nil -} - -// addCrc appends a crc table to a given psi table in bytes -func addCrc(out []byte) []byte { - out = append(out, make([]byte, 4)...) - out = updateCrc(out) - return out -} - -// updateCrc updates the crc of psi bytes slice that may have been modified -func updateCrc(out []byte) []byte { - crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[1:len(out)-4]) - out[len(out)-4] = byte(crc32 >> 24) - out[len(out)-3] = byte(crc32 >> 16) - out[len(out)-2] = byte(crc32 >> 8) - out[len(out)-1] = byte(crc32) - return out -} diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 56abc260..07d460bb 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -26,10 +26,6 @@ LICENSE package psi -import ( - "hash/crc32" -) - // Lengths of section definitions const ( ESSDDefLen = 5 @@ -308,26 +304,3 @@ func byteToBool(b byte) (o bool) { } return } - -func crc32_MakeTable(poly uint32) *crc32.Table { - var t crc32.Table - for i := range t { - crc := uint32(i) << 24 - for j := 0; j < 8; j++ { - if crc&0x80000000 != 0 { - crc = (crc << 1) ^ poly - } else { - crc <<= 1 - } - } - t[i] = crc - } - return &t -} - -func crc32_Update(crc uint32, tab *crc32.Table, p []byte) uint32 { - for _, v := range p { - crc = tab[byte(crc>>24)^v] ^ (crc << 8) - } - return crc -} From 189338d81beea138a61aff900f8cc16dbabf43b9 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 13:25:14 +1030 Subject: [PATCH 050/137] Removed since unused. --- rtmp/timeval_arm.go | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 rtmp/timeval_arm.go diff --git a/rtmp/timeval_arm.go b/rtmp/timeval_arm.go deleted file mode 100644 index 8bf02ed6..00000000 --- a/rtmp/timeval_arm.go +++ /dev/null @@ -1,7 +0,0 @@ -package rtmp - -import "golang.org/x/sys/unix" - -func setTimeval(sec int) unix.Timeval { - return unix.Timeval{Sec: int32(sec)} -} From 6975f5172ebe68326ecb51a1ba6e69aca4c8ced0 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 13:33:07 +1030 Subject: [PATCH 051/137] Make member names camel case. --- rtmp/rtmp.go | 631 ++++++++++++++++++++++--------------------- rtmp/rtmp_headers.go | 91 ++++--- 2 files changed, 369 insertions(+), 353 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index a9ee9a55..c585d13b 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -48,7 +48,7 @@ import ( const ( minDataSize = 11 - debugMode = false + debugMode = true length = 512 ) @@ -153,7 +153,7 @@ func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { } C_RTMP_EnableWrite(rtmp) - rtmp.m_nBufferMS = 3600 * 1000 + rtmp.nBufferMS = 3600 * 1000 err = C_RTMP_Connect(rtmp, nil) if err != nil { C_RTMP_Close(rtmp) @@ -188,16 +188,16 @@ func C_RTMP_GetTime() int32 { // rtmp.c +189 func C_RTMPPacket_Alloc(p *C_RTMPPacket, nSize uint32) { buf := make([]byte, RTMP_MAX_HEADER_SIZE+nSize) - p.m_header = buf - p.m_body = buf[RTMP_MAX_HEADER_SIZE:] - p.m_nBytesRead = 0 + p.header = buf + p.body = buf[RTMP_MAX_HEADER_SIZE:] + p.nBytesRead = 0 } // void RTMPPacket_Free(RTMPPacket* p); // rtmp.c +203 func C_RTMPPacket_Free(p *C_RTMPPacket) { - if p.m_body != nil { - p.m_body = nil + if p.body != nil { + p.body = nil } } @@ -211,14 +211,14 @@ func C_RTMP_Alloc() *C_RTMP { // rtmp.c +329 func C_RTMP_Init(r *C_RTMP) { *r = C_RTMP{} - r.m_inChunkSize = RTMP_DEFAULT_CHUNKSIZE - r.m_outChunkSize = RTMP_DEFAULT_CHUNKSIZE - r.m_nBufferMS = 30000 - r.m_nClientBW = 2500000 - r.m_nClientBW2 = 2 - r.m_nServerBW = 2500000 - r.m_fAudioCodecs = 3191.0 - r.m_fVideoCodecs = 252.0 + r.inChunkSize = RTMP_DEFAULT_CHUNKSIZE + r.outChunkSize = RTMP_DEFAULT_CHUNKSIZE + r.nBufferMS = 30000 + r.nClientBW = 2500000 + r.nClientBW2 = 2 + r.nServerBW = 2500000 + r.fAudioCodecs = 3191.0 + r.fVideoCodecs = 252.0 r.Link.timeout = 30 r.Link.swfAge = 30 } @@ -312,7 +312,7 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { // int RTMP_ConnectStream(RTMP* r, int seekTime); // rtmp.c +1099 -// Side effects: r.m_bPlaying is set true upon successful connection +// Side effects: r.bPlaying is set true upon successful connection func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { var packet C_RTMPPacket @@ -320,22 +320,22 @@ func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { r.Link.seekTime = seekTime } - r.m_mediaChannel = 0 + r.mediaChannel = 0 - for !r.m_bPlaying && C_RTMP_IsConnected(r) { + for !r.bPlaying && C_RTMP_IsConnected(r) { err := C_RTMP_ReadPacket(r, &packet) if err != nil { break } // TODO: port is ready if C_RTMPPacket_IsReady(&packet) { - if packet.m_nBodySize == 0 { + if packet.nBodySize == 0 { continue } - if packet.m_packetType == RTMP_PACKET_TYPE_AUDIO || - packet.m_packetType == RTMP_PACKET_TYPE_VIDEO || - packet.m_packetType == RTMP_PACKET_TYPE_INFO { + if packet.packetType == RTMP_PACKET_TYPE_AUDIO || + packet.packetType == RTMP_PACKET_TYPE_VIDEO || + packet.packetType == RTMP_PACKET_TYPE_INFO { log.Println("C_RTMP_ConnectStream: got packet before play()! Ignoring.") C_RTMPPacket_Free(&packet) continue @@ -346,7 +346,7 @@ func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { } } - if !r.m_bPlaying { + if !r.bPlaying { return errConnStream } return nil @@ -357,7 +357,7 @@ func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { // NOTE cases have been commented out that are not currently used by AusOcean func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { var bHasMediaPacket int32 - switch packet.m_packetType { + switch packet.packetType { case RTMP_PACKET_TYPE_CHUNK_SIZE: // TODO: port this @@ -397,9 +397,9 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { case RTMP_PACKET_TYPE_INVOKE: log.Println("RTMP_PACKET_TYPE_INVOKE:") // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,packet.m_nBodySize); + //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,packet.nBodySize); - err := C_HandleInvoke(r, packet.m_body[:packet.m_nBodySize]) + err := C_HandleInvoke(r, packet.body[:packet.nBodySize]) if err != nil { // This will never happen with the methods we implement. log.Println("HasMediaPacket") @@ -411,7 +411,7 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { default: // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,packet.m_packetType); + // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,packet.packetType); } return bHasMediaPacket } @@ -431,8 +431,8 @@ func C_ReadN(r *C_RTMP, buf []byte) error { C_RTMP_Close(r) return err } - r.m_nBytesIn += int32(n) - if r.m_nBytesIn > (r.m_nBytesInSent + r.m_nClientBW/10) { + r.nBytesIn += int32(n) + if r.nBytesIn > (r.nBytesInSent + r.nClientBW/10) { err := C_SendBytesReceived(r) if err != nil { return err @@ -469,23 +469,23 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { var pbuf [4096]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, - m_headerType: RTMP_PACKET_SIZE_LARGE, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, + headerType: RTMP_PACKET_SIZE_LARGE, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_connect) if enc == nil { return errEncoding } - r.m_numInvokes += 1 - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes += 1 + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } @@ -532,11 +532,11 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_audioCodecs, r.m_fAudioCodecs) + enc = C_AMF_EncodeNamedNumber(enc, av_audioCodecs, r.fAudioCodecs) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_videoCodecs, r.m_fVideoCodecs) + enc = C_AMF_EncodeNamedNumber(enc, av_videoCodecs, r.fVideoCodecs) if enc == nil { return errEncoding } @@ -552,8 +552,8 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { } } - if r.m_fEncoding != 0.0 || r.m_bSendEncoding { - enc = C_AMF_EncodeNamedNumber(enc, av_objectEncoding, r.m_fEncoding) + if r.fEncoding != 0.0 || r.bSendEncoding { + enc = C_AMF_EncodeNamedNumber(enc, av_objectEncoding, r.fEncoding) if enc == nil { return errEncoding } @@ -583,7 +583,7 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { } } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 1) } @@ -593,30 +593,30 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { func C_RTMP_SendCreateStream(r *C_RTMP) error { var pbuf [256]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_MEDIUM, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_createStream) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 1) } @@ -626,23 +626,23 @@ func C_RTMP_SendCreateStream(r *C_RTMP) error { func C_SendReleaseStream(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_MEDIUM, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_releaseStream) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } @@ -652,7 +652,7 @@ func C_SendReleaseStream(r *C_RTMP) error { if enc == nil { return errEncoding } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 0) } @@ -662,23 +662,23 @@ func C_SendReleaseStream(r *C_RTMP) error { func C_SendFCPublish(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_MEDIUM, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_FCPublish) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } @@ -689,7 +689,7 @@ func C_SendFCPublish(r *C_RTMP) error { return errEncoding } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 0) } @@ -699,23 +699,23 @@ func C_SendFCPublish(r *C_RTMP) error { func C_SendFCUnpublish(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_MEDIUM, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_FCUnpublish) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } @@ -726,7 +726,7 @@ func C_SendFCUnpublish(r *C_RTMP) error { return errEncoding } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 0) } @@ -736,23 +736,23 @@ func C_SendFCUnpublish(r *C_RTMP) error { func C_SendPublish(r *C_RTMP) error { var pbuf [1024]byte packet := C_RTMPPacket{ - m_nChannel: 0x04, /* source channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_LARGE, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: r.m_stream_id, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x04, /* source channel (invoke) */ + headerType: RTMP_PACKET_SIZE_LARGE, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: r.streamID, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_publish) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } @@ -767,7 +767,7 @@ func C_SendPublish(r *C_RTMP) error { return errEncoding } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 1) } @@ -778,23 +778,23 @@ func C_SendPublish(r *C_RTMP) error { func C_SendDeleteStream(r *C_RTMP, dStreamId float64) error { var pbuf [256]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_MEDIUM, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av_deleteStream) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } @@ -804,7 +804,7 @@ func C_SendDeleteStream(r *C_RTMP, dStreamId float64) error { if enc == nil { return errEncoding } - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) /* no response expected */ return C_RTMP_SendPacket(r, &packet, 0) @@ -815,23 +815,23 @@ func C_SendDeleteStream(r *C_RTMP, dStreamId float64) error { func C_SendBytesReceived(r *C_RTMP) error { var pbuf [256]byte packet := C_RTMPPacket{ - m_nChannel: 0x02, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_MEDIUM, - m_packetType: RTMP_PACKET_TYPE_BYTES_READ_REPORT, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x02, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_BYTES_READ_REPORT, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body - r.m_nBytesInSent = r.m_nBytesIn - enc = C_AMF_EncodeInt32(enc, r.m_nBytesIn) + r.nBytesInSent = r.nBytesIn + enc = C_AMF_EncodeInt32(enc, r.nBytesIn) if enc == nil { return errEncoding } - packet.m_nBodySize = 4 + packet.nBodySize = 4 return C_RTMP_SendPacket(r, &packet, 0) } @@ -841,30 +841,30 @@ func C_SendBytesReceived(r *C_RTMP) error { func C_SendCheckBW(r *C_RTMP) error { var pbuf [256]byte packet := C_RTMPPacket{ - m_nChannel: 0x03, /* control channel (invoke) */ - m_headerType: RTMP_PACKET_SIZE_LARGE, - m_packetType: RTMP_PACKET_TYPE_INVOKE, - m_nTimeStamp: 0, - m_nInfoField2: 0, - m_hasAbsTimestamp: false, - m_header: pbuf[:], - m_body: pbuf[RTMP_MAX_HEADER_SIZE:], + nChannel: 0x03, /* control channel (invoke) */ + headerType: RTMP_PACKET_SIZE_LARGE, + packetType: RTMP_PACKET_TYPE_INVOKE, + nTimeStamp: 0, + nInfoField2: 0, + hasAbsTimestamp: false, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.m_body + enc := packet.body enc = C_AMF_EncodeString(enc, av__checkbw) if enc == nil { return errEncoding } - r.m_numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.m_numInvokes)) + r.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - packet.m_nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) return C_RTMP_SendPacket(r, &packet, 0) } @@ -879,7 +879,7 @@ func C_AV_erase(m []C_RTMP_METHOD, i int) []C_RTMP_METHOD { // int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); // rtmp.c +2912 -// Side effects: r.m_bPlaying set to true upon av_NetStream_Publish_Start +// Side effects: r.bPlaying set to true upon av_NetStream_Publish_Start func C_HandleInvoke(r *C_RTMP, body []byte) error { if body[0] != 0x02 { return errInvalidBody @@ -901,10 +901,10 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { switch method { case av__result: var methodInvoked string - for i, m := range r.m_methodCalls { + for i, m := range r.methodCalls { if float64(m.num) == txn { methodInvoked = m.name - r.m_methodCalls = C_AV_erase(r.m_methodCalls, i) + r.methodCalls = C_AV_erase(r.methodCalls, i) break } } @@ -936,7 +936,7 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { } case av_createStream: - r.m_stream_id = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) + r.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { C_SendPublish(r) @@ -950,7 +950,7 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { //C.free(unsafe.Pointer(methodInvoked.av_val)) case av_onBWDone: - if r.m_nBWCheckCounter == 0 { + if r.nBWCheckCounter == 0 { C_SendCheckBW(r) } @@ -989,10 +989,10 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { panic("Unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") case av_NetStream_Publish_Start: - r.m_bPlaying = true - for i, m := range r.m_methodCalls { + r.bPlaying = true + for i, m := range r.methodCalls { if m.name == av_publish { - r.m_methodCalls = C_AV_erase(r.m_methodCalls, i) + r.methodCalls = C_AV_erase(r.methodCalls, i) break } } @@ -1022,36 +1022,36 @@ leave: // void HandleChangeChunkSize(RTMP* r, const RTMPPacket* packet); // rtmp.c +3345 func C_HandleChangeChunkSize(r *C_RTMP, packet *C_RTMPPacket) { - if packet.m_nBodySize >= 4 { - //r.m_inChunkSize = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) - r.m_inChunkSize = int32(C_AMF_DecodeInt32(packet.m_body[:4])) + if packet.nBodySize >= 4 { + //r.inChunkSize = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.body)))) + r.inChunkSize = int32(C_AMF_DecodeInt32(packet.body[:4])) // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, r.m_inChunkSize); + // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, r.inChunkSize); } } // void HandleServerBW(RTMP* r, const RTMPPacket* packet); // rtmp.c +3508 func C_HandlServerBW(r *C_RTMP, packet *C_RTMPPacket) { - r.m_nServerBW = int32(C_AMF_DecodeInt32(packet.m_body[:4])) + r.nServerBW = int32(C_AMF_DecodeInt32(packet.body[:4])) // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r.m_nServerBW); + // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r.nServerBW); } // void HandleClientBW(RTMP* r, const RTMPPacket* packet); // rtmp.c +3515 func C_HandleClientBW(r *C_RTMP, packet *C_RTMPPacket) { - r.m_nClientBW = int32(C_AMF_DecodeInt32(packet.m_body[:4])) - //r.m_nClientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.m_body)))) + r.nClientBW = int32(C_AMF_DecodeInt32(packet.body[:4])) + //r.nClientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.body)))) - if packet.m_nBodySize > 4 { - r.m_nClientBW2 = packet.m_body[4] + if packet.nBodySize > 4 { + r.nClientBW2 = packet.body[4] } else { - r.m_nClientBW2 = 0xff + r.nClientBW2 = 0xff } // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r.m_nClientBW, - //r.m_nClientBW2); + // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r.nClientBW, + //r.nClientBW2); } // static int DecodeInt32LE(const char* data); @@ -1078,61 +1078,61 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) error { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet header!") return err } - packet.m_headerType = (header[0] & 0xc0) >> 6 - packet.m_nChannel = int32(header[0] & 0x3f) + packet.headerType = (header[0] & 0xc0) >> 6 + packet.nChannel = int32(header[0] & 0x3f) header = header[1:] switch { - case packet.m_nChannel == 0: + case packet.nChannel == 0: err = C_ReadN(r, header[:1]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header 2nd byte.") return err } header = header[1:] - packet.m_nChannel = int32(header[0]) + 64 + packet.nChannel = int32(header[0]) + 64 - case packet.m_nChannel == 1: + case packet.nChannel == 1: err = C_ReadN(r, header[:2]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet 3rd byte") return err } header = header[2:] - packet.m_nChannel = int32(binary.BigEndian.Uint16(header[:2])) + 64 + packet.nChannel = int32(binary.BigEndian.Uint16(header[:2])) + 64 } - if packet.m_nChannel >= r.m_channelsAllocatedIn { - n := packet.m_nChannel + 10 - timestamp := append(r.m_channelTimestamp, make([]int32, 10)...) + if packet.nChannel >= r.channelsAllocatedIn { + n := packet.nChannel + 10 + timestamp := append(r.channelTimestamp, make([]int32, 10)...) var packets []*C_RTMPPacket - if r.m_vecChannelsIn == nil { + if r.vecChannelsIn == nil { packets = make([]*C_RTMPPacket, n) } else { - packets = append(r.m_vecChannelsIn[:packet.m_nChannel:packet.m_nChannel], make([]*C_RTMPPacket, 10)...) + packets = append(r.vecChannelsIn[:packet.nChannel:packet.nChannel], make([]*C_RTMPPacket, 10)...) } - r.m_channelTimestamp = timestamp - r.m_vecChannelsIn = packets + r.channelTimestamp = timestamp + r.vecChannelsIn = packets - for i := int(r.m_channelsAllocatedIn); i < len(r.m_channelTimestamp); i++ { - r.m_channelTimestamp[i] = 0 + for i := int(r.channelsAllocatedIn); i < len(r.channelTimestamp); i++ { + r.channelTimestamp[i] = 0 } - for i := int(r.m_channelsAllocatedIn); i < int(n); i++ { - r.m_vecChannelsIn[i] = nil + for i := int(r.channelsAllocatedIn); i < int(n); i++ { + r.vecChannelsIn[i] = nil } - r.m_channelsAllocatedIn = n + r.channelsAllocatedIn = n } - nSize := packetSize[packet.m_headerType] + nSize := packetSize[packet.headerType] switch { case nSize == RTMP_LARGE_HEADER_SIZE: - packet.m_hasAbsTimestamp = true + packet.hasAbsTimestamp = true case nSize < RTMP_LARGE_HEADER_SIZE: - if r.m_vecChannelsIn[packet.m_nChannel] != nil { - *packet = *(r.m_vecChannelsIn[packet.m_nChannel]) + if r.vecChannelsIn[packet.nChannel] != nil { + *packet = *(r.vecChannelsIn[packet.nChannel]) } } @@ -1149,23 +1149,23 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) error { hSize := len(hbuf) - len(header) + nSize if nSize >= 3 { - packet.m_nTimeStamp = C_AMF_DecodeInt24(header[:3]) + packet.nTimeStamp = C_AMF_DecodeInt24(header[:3]) if nSize >= 6 { - packet.m_nBodySize = C_AMF_DecodeInt24(header[3:6]) - packet.m_nBytesRead = 0 + packet.nBodySize = C_AMF_DecodeInt24(header[3:6]) + packet.nBytesRead = 0 if nSize > 6 { - packet.m_packetType = header[6] + packet.packetType = header[6] if nSize == 11 { - packet.m_nInfoField2 = C_DecodeInt32LE(header[7:11]) + packet.nInfoField2 = C_DecodeInt32LE(header[7:11]) } } } } - extendedTimestamp := packet.m_nTimeStamp == 0xffffff + extendedTimestamp := packet.nTimeStamp == 0xffffff if extendedTimestamp { err = C_ReadN(r, header[nSize:nSize+4]) if err != nil { @@ -1173,60 +1173,60 @@ func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) error { return err } // TODO: port this - packet.m_nTimeStamp = C_AMF_DecodeInt32(header[nSize : nSize+4]) + packet.nTimeStamp = C_AMF_DecodeInt32(header[nSize : nSize+4]) hSize += 4 } - if packet.m_nBodySize > 0 && packet.m_body == nil { + if packet.nBodySize > 0 && packet.body == nil { // TODO: port this - C_RTMPPacket_Alloc(packet, packet.m_nBodySize) - packet.m_headerType = (hbuf[0] & 0xc0) >> 6 + C_RTMPPacket_Alloc(packet, packet.nBodySize) + packet.headerType = (hbuf[0] & 0xc0) >> 6 } - nToRead := int32(packet.m_nBodySize - packet.m_nBytesRead) - nChunk := r.m_inChunkSize + nToRead := int32(packet.nBodySize - packet.nBytesRead) + nChunk := r.inChunkSize if nToRead < nChunk { nChunk = nToRead } - if packet.m_chunk != nil { - packet.m_chunk.c_headerSize = int32(hSize) - copy(packet.m_chunk.c_header[:], hbuf[:hSize]) - packet.m_chunk.c_chunk = packet.m_body[packet.m_nBytesRead : packet.m_nBytesRead+uint32(nChunk)] + if packet.chunk != nil { + packet.chunk.headerSize = int32(hSize) + copy(packet.chunk.header[:], hbuf[:hSize]) + packet.chunk.data = packet.body[packet.nBytesRead : packet.nBytesRead+uint32(nChunk)] } - err = C_ReadN(r, packet.m_body[packet.m_nBytesRead:][:nChunk]) + err = C_ReadN(r, packet.body[packet.nBytesRead:][:nChunk]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet body") return err } - packet.m_nBytesRead += uint32(nChunk) + packet.nBytesRead += uint32(nChunk) // keep the packet as ref for other packets on this channel - if r.m_vecChannelsIn[packet.m_nChannel] == nil { - r.m_vecChannelsIn[packet.m_nChannel] = &C_RTMPPacket{} + if r.vecChannelsIn[packet.nChannel] == nil { + r.vecChannelsIn[packet.nChannel] = &C_RTMPPacket{} } - *(r.m_vecChannelsIn[packet.m_nChannel]) = *packet + *(r.vecChannelsIn[packet.nChannel]) = *packet if extendedTimestamp { - r.m_vecChannelsIn[packet.m_nChannel].m_nTimeStamp = 0xffffff + r.vecChannelsIn[packet.nChannel].nTimeStamp = 0xffffff } // TODO: port this if C_RTMPPacket_IsReady(packet) { - if !packet.m_hasAbsTimestamp { + if !packet.hasAbsTimestamp { // timestamps seem to always be relative - packet.m_nTimeStamp += uint32(r.m_channelTimestamp[packet.m_nChannel]) + packet.nTimeStamp += uint32(r.channelTimestamp[packet.nChannel]) } - r.m_channelTimestamp[packet.m_nChannel] = int32(packet.m_nTimeStamp) + r.channelTimestamp[packet.nChannel] = int32(packet.nTimeStamp) - r.m_vecChannelsIn[packet.m_nChannel].m_body = nil - r.m_vecChannelsIn[packet.m_nChannel].m_nBytesRead = 0 - r.m_vecChannelsIn[packet.m_nChannel].m_hasAbsTimestamp = false + r.vecChannelsIn[packet.nChannel].body = nil + r.vecChannelsIn[packet.nChannel].nBytesRead = 0 + r.vecChannelsIn[packet.nChannel].hasAbsTimestamp = false } else { - packet.m_body = nil /* so it won't be erased on free */ + packet.body = nil /* so it won't be erased on free */ } return nil } @@ -1302,50 +1302,50 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { var prevPacket *C_RTMPPacket var last int - if packet.m_nChannel >= r.m_channelsAllocatedOut { - n := int(packet.m_nChannel + 10) + if packet.nChannel >= r.channelsAllocatedOut { + n := int(packet.nChannel + 10) var packets []*C_RTMPPacket - if r.m_vecChannelsOut == nil { + if r.vecChannelsOut == nil { packets = make([]*C_RTMPPacket, n) } else { - packets = append(r.m_vecChannelsOut[:packet.m_nChannel:packet.m_nChannel], make([]*C_RTMPPacket, 10)...) + packets = append(r.vecChannelsOut[:packet.nChannel:packet.nChannel], make([]*C_RTMPPacket, 10)...) } - r.m_vecChannelsOut = packets + r.vecChannelsOut = packets - for i := int(r.m_channelsAllocatedOut); i < n; i++ { - r.m_vecChannelsOut[i] = nil + for i := int(r.channelsAllocatedOut); i < n; i++ { + r.vecChannelsOut[i] = nil } - r.m_channelsAllocatedOut = int32(n) + r.channelsAllocatedOut = int32(n) } - prevPacket = r.m_vecChannelsOut[packet.m_nChannel] + prevPacket = r.vecChannelsOut[packet.nChannel] - if prevPacket != nil && packet.m_headerType != RTMP_PACKET_SIZE_LARGE { + if prevPacket != nil && packet.headerType != RTMP_PACKET_SIZE_LARGE { // compress a bit by using the prev packet's attributes - if prevPacket.m_nBodySize == packet.m_nBodySize && prevPacket.m_packetType == packet.m_packetType && packet.m_headerType == RTMP_PACKET_SIZE_MEDIUM { - packet.m_headerType = RTMP_PACKET_SIZE_SMALL + if prevPacket.nBodySize == packet.nBodySize && prevPacket.packetType == packet.packetType && packet.headerType == RTMP_PACKET_SIZE_MEDIUM { + packet.headerType = RTMP_PACKET_SIZE_SMALL } - if prevPacket.m_nTimeStamp == packet.m_nTimeStamp && packet.m_headerType == RTMP_PACKET_SIZE_SMALL { - packet.m_headerType = RTMP_PACKET_SIZE_MINIMUM + if prevPacket.nTimeStamp == packet.nTimeStamp && packet.headerType == RTMP_PACKET_SIZE_SMALL { + packet.headerType = RTMP_PACKET_SIZE_MINIMUM } - last = int(prevPacket.m_nTimeStamp) + last = int(prevPacket.nTimeStamp) } - if packet.m_headerType > 3 { + if packet.headerType > 3 { log.Printf("Sanity failed! trying to send header of type: 0x%02x.", - packet.m_headerType) + packet.headerType) return errInvalidHeader } var headBytes []byte var origIdx int - if packet.m_body != nil { + if packet.body != nil { // Span from -packetsize for the type to the start of the body. - headBytes = packet.m_header - origIdx = RTMP_MAX_HEADER_SIZE - packetSize[packet.m_headerType] + headBytes = packet.header + origIdx = RTMP_MAX_HEADER_SIZE - packetSize[packet.headerType] } else { // Allocate a new header and allow 6 bytes of movement backward. var hbuf [RTMP_MAX_HEADER_SIZE]byte @@ -1355,13 +1355,13 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { var cSize int switch { - case packet.m_nChannel > 319: + case packet.nChannel > 319: cSize = 2 - case packet.m_nChannel > 63: + case packet.nChannel > 63: cSize = 1 } - hSize := packetSize[packet.m_headerType] + hSize := packetSize[packet.headerType] if cSize != 0 { origIdx -= cSize hSize += cSize @@ -1369,7 +1369,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { var ts uint32 if prevPacket != nil { - ts = uint32(int(packet.m_nTimeStamp) - last) + ts = uint32(int(packet.nTimeStamp) - last) } if ts >= 0xffffff { origIdx -= 4 @@ -1379,10 +1379,10 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx := origIdx - c := packet.m_headerType << 6 + c := packet.headerType << 6 switch cSize { case 0: - c |= byte(packet.m_nChannel) + c |= byte(packet.nChannel) case 1: // Do nothing. case 2: @@ -1392,7 +1392,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx++ if cSize != 0 { - tmp := packet.m_nChannel - 64 + tmp := packet.nChannel - 64 headBytes[headerIdx] = byte(tmp & 0xff) headerIdx++ @@ -1402,7 +1402,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { } } - if packetSize[packet.m_headerType] > 1 { + if packetSize[packet.headerType] > 1 { res := ts if ts > 0xffffff { res = 0xffffff @@ -1411,15 +1411,15 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx += 3 // 24bits } - if packetSize[packet.m_headerType] > 4 { - C_AMF_EncodeInt24(headBytes[headerIdx:], int32(packet.m_nBodySize)) + if packetSize[packet.headerType] > 4 { + C_AMF_EncodeInt24(headBytes[headerIdx:], int32(packet.nBodySize)) headerIdx += 3 // 24bits - headBytes[headerIdx] = packet.m_packetType + headBytes[headerIdx] = packet.packetType headerIdx++ } - if packetSize[packet.m_headerType] > 8 { - n := int(C_EncodeInt32LE(headBytes[headerIdx:headerIdx+4], packet.m_nInfoField2)) + if packetSize[packet.headerType] > 8 { + n := int(C_EncodeInt32LE(headBytes[headerIdx:headerIdx+4], packet.nInfoField2)) headerIdx += n } @@ -1428,26 +1428,42 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx += 4 // 32bits } - nSize := int(packet.m_nBodySize) - nChunkSize := int(r.m_outChunkSize) + nSize := int(packet.nBodySize) + nChunkSize := int(r.outChunkSize) if debugMode { - if r.Link.conn != nil { - log.Printf("C_RTMP_SendPacket: %v->%v, size=%v, type=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize, packet.m_packetType) + log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize) + } + + // Send the previously deferred packet if combining it with the next packet would exceed the chunk size. + if r.defered != nil && len(r.defered)+nSize+hSize > nChunkSize { + err := C_WriteN(r, r.defered) + if err != nil { + return err } + r.defered = nil } // TODO(kortschak): Rewrite this horrific peice of premature optimisation. // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. for nSize+hSize != 0 { + if r.defered == nil && packet.packetType == RTMP_PACKET_TYPE_AUDIO && nSize < nChunkSize { + r.defered = headBytes[origIdx:][:nSize+hSize] + break + } if nChunkSize > nSize { nChunkSize = nSize } - - err := C_WriteN(r, headBytes[origIdx:][:nChunkSize+hSize]) + bytes := headBytes[origIdx:][:nChunkSize+hSize] + if r.defered != nil { + // Prepend the previously deferred packet and write it with the current one. + bytes = append(r.defered, bytes...) + } + err := C_WriteN(r, bytes) if err != nil { return err } + r.defered = nil nSize -= nChunkSize origIdx += nChunkSize + hSize @@ -1465,7 +1481,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headBytes[origIdx] = 0xc0 | c if cSize != 0 { - tmp := int(packet.m_nChannel) - 64 + tmp := int(packet.nChannel) - 64 headBytes[origIdx+1] = byte(tmp) if cSize == 2 { @@ -1481,8 +1497,8 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { // We invoked a remote method // TODO: port the const - if packet.m_packetType == RTMP_PACKET_TYPE_INVOKE { - buf := packet.m_body[1:] + if packet.packetType == RTMP_PACKET_TYPE_INVOKE { + buf := packet.body[1:] method := C_AMF_DecodeString(buf) if debugMode { @@ -1492,14 +1508,14 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { if queue != 0 { buf = buf[3+len(method):] txn := int32(C_AMF_DecodeNumber(buf[:8])) - r.m_methodCalls = append(r.m_methodCalls, C_RTMP_METHOD{name: method, num: txn}) + r.methodCalls = append(r.methodCalls, C_RTMP_METHOD{name: method, num: txn}) } } - if r.m_vecChannelsOut[packet.m_nChannel] == nil { - r.m_vecChannelsOut[packet.m_nChannel] = &C_RTMPPacket{} + if r.vecChannelsOut[packet.nChannel] == nil { + r.vecChannelsOut[packet.nChannel] = &C_RTMPPacket{} } - *(r.m_vecChannelsOut[packet.m_nChannel]) = *packet + *(r.vecChannelsOut[packet.nChannel]) = *packet return nil } @@ -1516,8 +1532,8 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { var i int32 if C_RTMP_IsConnected(r) { - if r.m_stream_id > 0 { - i = r.m_stream_id + if r.streamID > 0 { + i = r.streamID if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { C_SendFCUnpublish(r) } @@ -1529,35 +1545,35 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { } } - r.m_stream_id = -1 + r.streamID = -1 r.Link.conn = nil - r.m_nBWCheckCounter = 0 - r.m_nBytesIn = 0 - r.m_nBytesInSent = 0 + r.nBWCheckCounter = 0 + r.nBytesIn = 0 + r.nBytesInSent = 0 - r.m_write.m_nBytesRead = 0 - C_RTMPPacket_Free(&r.m_write) + r.write.nBytesRead = 0 + C_RTMPPacket_Free(&r.write) // NOTE: C frees - not using in our case - for i := 0; i < int(r.m_channelsAllocatedIn); i++ { - if r.m_vecChannelsIn[i] != nil { - r.m_vecChannelsIn[i] = nil + for i := 0; i < int(r.channelsAllocatedIn); i++ { + if r.vecChannelsIn[i] != nil { + r.vecChannelsIn[i] = nil } } - //C.free(unsafe.Pointer(r.m_vecChannelsOut)) - r.m_vecChannelsOut = nil - r.m_channelsAllocatedOut = 0 - r.m_methodCalls = nil //C_AV_clear(r.m_methodCalls, r.m_numCalls) + //C.free(unsafe.Pointer(r.vecChannelsOut)) + r.vecChannelsOut = nil + r.channelsAllocatedOut = 0 + r.methodCalls = nil //C_AV_clear(r.methodCalls, r.numCalls) - r.m_methodCalls = r.m_methodCalls[:0] - r.m_numInvokes = 0 + r.methodCalls = r.methodCalls[:0] + r.numInvokes = 0 - r.m_bPlaying = false + r.bPlaying = false - r.m_msgCounter = 0 - r.m_resplen = 0 - r.m_unackd = 0 + r.msgCounter = 0 + r.resplen = 0 + r.unackd = 0 if ((r.Link.lFlags & RTMP_LF_FTCU) != 0) && !reconnect { r.Link.app = "" @@ -1573,15 +1589,15 @@ func C_CloseInternal(r *C_RTMP, reconnect bool) { // rtmp.c +5136 func C_RTMP_Write(r *C_RTMP, buf []byte) error { // TODO: port RTMPPacket - var pkt = &r.m_write + var pkt = &r.write var enc []byte size := len(buf) var num int - pkt.m_nChannel = 0x04 - pkt.m_nInfoField2 = r.m_stream_id + pkt.nChannel = 0x04 + pkt.nInfoField2 = r.streamID for len(buf) != 0 { - if pkt.m_nBytesRead == 0 { + if pkt.nBytesRead == 0 { if size < minDataSize { return errTinyPacket } @@ -1590,54 +1606,53 @@ func C_RTMP_Write(r *C_RTMP, buf []byte) error { buf = buf[13:] } - pkt.m_packetType = buf[0] + pkt.packetType = buf[0] buf = buf[1:] - pkt.m_nBodySize = C_AMF_DecodeInt24(buf[:3]) + pkt.nBodySize = C_AMF_DecodeInt24(buf[:3]) buf = buf[3:] - pkt.m_nTimeStamp = C_AMF_DecodeInt24(buf[:3]) + pkt.nTimeStamp = C_AMF_DecodeInt24(buf[:3]) buf = buf[3:] - pkt.m_nTimeStamp |= uint32(buf[0]) << 24 + pkt.nTimeStamp |= uint32(buf[0]) << 24 buf = buf[4:] - if ((pkt.m_packetType == RTMP_PACKET_TYPE_AUDIO || - pkt.m_packetType == RTMP_PACKET_TYPE_VIDEO) && - pkt.m_nTimeStamp == 0) || pkt.m_packetType == RTMP_PACKET_TYPE_INFO { - - pkt.m_headerType = RTMP_PACKET_SIZE_LARGE - - if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { - pkt.m_nBodySize += 16 + pkt.headerType = RTMP_PACKET_SIZE_MEDIUM + switch pkt.packetType { + case RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_AUDIO: + if pkt.nTimeStamp == 0 { + pkt.headerType = RTMP_PACKET_SIZE_LARGE } - } else { - pkt.m_headerType = RTMP_PACKET_SIZE_MEDIUM + case RTMP_PACKET_TYPE_INFO: + pkt.headerType = RTMP_PACKET_SIZE_LARGE + pkt.nBodySize += 16 } - // TODO: Port this - C_RTMPPacket_Alloc(pkt, pkt.m_nBodySize) - enc = pkt.m_body[:pkt.m_nBodySize] - if pkt.m_packetType == RTMP_PACKET_TYPE_INFO { + // TODO: Port this + C_RTMPPacket_Alloc(pkt, pkt.nBodySize) + + enc = pkt.body[:pkt.nBodySize] + if pkt.packetType == RTMP_PACKET_TYPE_INFO { enc = C_AMF_EncodeString(enc, setDataFrame) if enc == nil { return errEncoding } - pkt.m_nBytesRead = uint32(len(pkt.m_body) - len(enc)) + pkt.nBytesRead = uint32(len(pkt.body) - len(enc)) } } else { - enc = pkt.m_body[:pkt.m_nBodySize][pkt.m_nBytesRead:] + enc = pkt.body[:pkt.nBodySize][pkt.nBytesRead:] } - num = int(pkt.m_nBodySize - pkt.m_nBytesRead) + num = int(pkt.nBodySize - pkt.nBytesRead) if num > len(buf) { num = len(buf) } copy(enc[:num], buf[:num]) - pkt.m_nBytesRead += uint32(num) + pkt.nBytesRead += uint32(num) buf = buf[num:] - if pkt.m_nBytesRead == pkt.m_nBodySize { + if pkt.nBytesRead == pkt.nBodySize { err := C_RTMP_SendPacket(r, pkt, 0) C_RTMPPacket_Free(pkt) - pkt.m_nBytesRead = 0 + pkt.nBytesRead = 0 if err != nil { return err } diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 53061517..4174c332 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -115,25 +115,25 @@ const ( // typedef struct RTMPChunk // rtmp.h +105 type C_RTMPChunk struct { - c_headerSize int32 - c_chunk []byte - c_header [RTMP_MAX_HEADER_SIZE]byte + headerSize int32 + data []byte + header [RTMP_MAX_HEADER_SIZE]byte } // typedef struct RTMPPacket // rtmp.h +113 type C_RTMPPacket struct { - m_headerType uint8 - m_packetType uint8 - m_hasAbsTimestamp bool - m_nChannel int32 - m_nTimeStamp uint32 - m_nInfoField2 int32 - m_nBodySize uint32 - m_nBytesRead uint32 - m_chunk *C_RTMPChunk - m_header []byte - m_body []byte + headerType uint8 + packetType uint8 + hasAbsTimestamp bool + nChannel int32 + nTimeStamp uint32 + nInfoField2 int32 + nBodySize uint32 + nBytesRead uint32 + chunk *C_RTMPChunk + header []byte + body []byte } // typedef struct RTMPSockBuf @@ -143,7 +143,7 @@ type C_RTMPPacket struct { // RTMPPacket_IsReady(a) // rtmp.h +142 func C_RTMPPacket_IsReady(p *C_RTMPPacket) bool { - return p.m_nBytesRead == p.m_nBodySize + return p.nBytesRead == p.nBodySize } // typedef struct RTMP_LNK @@ -179,34 +179,35 @@ type C_RTMP_METHOD struct { // typedef struct RTMP // rtmp.h +237 type C_RTMP struct { - m_inChunkSize int32 - m_outChunkSize int32 - m_nBWCheckCounter int32 - m_nBytesIn int32 - m_nBytesInSent int32 - m_nBufferMS int32 - m_stream_id int32 - m_mediaChannel int32 - m_pausing int32 - m_nServerBW int32 - m_nClientBW int32 - m_nClientBW2 uint8 - m_bPlaying bool - m_bSendEncoding bool - m_numInvokes int32 - m_methodCalls []C_RTMP_METHOD - m_channelsAllocatedIn int32 - m_channelsAllocatedOut int32 - m_vecChannelsIn []*C_RTMPPacket - m_vecChannelsOut []*C_RTMPPacket - m_channelTimestamp []int32 - m_fAudioCodecs float64 - m_fVideoCodecs float64 - m_fEncoding float64 - m_fDuration float64 - m_msgCounter int32 - m_resplen int32 - m_unackd int32 - m_write C_RTMPPacket - Link C_RTMP_LNK + inChunkSize int32 + outChunkSize int32 + nBWCheckCounter int32 + nBytesIn int32 + nBytesInSent int32 + nBufferMS int32 + streamID int32 + mediaChannel int32 + pausing int32 + nServerBW int32 + nClientBW int32 + nClientBW2 uint8 + bPlaying bool + bSendEncoding bool + numInvokes int32 + methodCalls []C_RTMP_METHOD + channelsAllocatedIn int32 + channelsAllocatedOut int32 + vecChannelsIn []*C_RTMPPacket + vecChannelsOut []*C_RTMPPacket + channelTimestamp []int32 + fAudioCodecs float64 + fVideoCodecs float64 + fEncoding float64 + fDuration float64 + msgCounter int32 + resplen int32 + unackd int32 + write C_RTMPPacket + defered []byte + Link C_RTMP_LNK } From 31683b4194f7bb84b68730177400bd0c73319421 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 13:49:47 +1030 Subject: [PATCH 052/137] psi: HasTime and HasLocation now return bool instead of error --- stream/mts/psi/crc.go | 72 +++++++++++++++++++++ stream/mts/psi/helpers.go | 128 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 stream/mts/psi/crc.go create mode 100644 stream/mts/psi/helpers.go diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go new file mode 100644 index 00000000..55bad3ed --- /dev/null +++ b/stream/mts/psi/crc.go @@ -0,0 +1,72 @@ +/* +NAME + crc.go +DESCRIPTION + See Readme.md + +AUTHOR + Saxon Milton + +LICENSE + crc.go is Copyright (C) 2018 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +package psi + +import ( + "hash/crc32" + "math/bits" +) + +// addCrc appends a crc table to a given psi table in bytes +func addCrc(out []byte) []byte { + out = append(out, make([]byte, 4)...) + out = updateCrc(out) + return out +} + +// updateCrc updates the crc of psi bytes slice that may have been modified +func updateCrc(out []byte) []byte { + crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[1:len(out)-4]) + out[len(out)-4] = byte(crc32 >> 24) + out[len(out)-3] = byte(crc32 >> 16) + out[len(out)-2] = byte(crc32 >> 8) + out[len(out)-1] = byte(crc32) + return out +} + +func crc32_MakeTable(poly uint32) *crc32.Table { + var t crc32.Table + for i := range t { + crc := uint32(i) << 24 + for j := 0; j < 8; j++ { + if crc&0x80000000 != 0 { + crc = (crc << 1) ^ poly + } else { + crc <<= 1 + } + } + t[i] = crc + } + return &t +} + +func crc32_Update(crc uint32, tab *crc32.Table, p []byte) uint32 { + for _, v := range p { + crc = tab[byte(crc>>24)^v] ^ (crc << 8) + } + return crc +} diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go new file mode 100644 index 00000000..be8a9541 --- /dev/null +++ b/stream/mts/psi/helpers.go @@ -0,0 +1,128 @@ +/* +NAME + helpers.go +DESCRIPTION + helpers.go provides functionality for editing and reading bytes slices + directly in order to insert/read timestamp and location data in psi. + +AUTHOR + Saxon Milton + +LICENSE + helpers.go is Copyright (C) 2018 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +package psi + +import ( + "bytes" + "encoding/binary" + "errors" +) + +// error message +var ( + ErrNoLocation = errors.New("psi does not have location descriptor") + ErrNoTime = errors.New("psi does not have time descriptor") +) + +// TimeBytes takes a timestamp as an uint64 and converts to an 8 byte slice - +// allows for updating of timestamp in pmt time descriptor. +func TimeBytes(t uint64) []byte { + var s [timeDataSize]byte + binary.BigEndian.PutUint64(s[:], t) + return s[:] +} + +// HasTime takes a psi as a byte slice and checks to see if it has a time descriptor +// - if so return nil, otherwise return error +func HasTime(p []byte) bool { + if p[timeTagIndx] == timeDescTag { + return true + } + return false +} + +// HasLocation takes a psi as a byte slice and checks to see if it has a location descriptor +// - if so return nil, otherwise return error +func HasLocation(p []byte) bool { + if p[locationTagIndx] == locationDescTag { + return true + } + return false +} + +// UpdateTime takes the byte slice representation of a psi-pmt as well as a time +// as an integer and attempts to update the time descriptor in the pmt with the +// given time if the time descriptor exists, otherwise an error is returned +func UpdateTime(dst []byte, t uint64) error { + if !HasTime(dst) { + return ErrNoTime + } + ts := TimeBytes(uint64(t)) + for i := range dst[timeDataIndx : timeDataIndx+timeDataSize] { + dst[i+timeDataIndx] = ts[i] + } + dst = updateCrc(dst) + return nil +} + +// TimeFrom takes a byte slice representation of a psi-pmt and extracts it's +// timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil +// if it does not exist +func TimeFrom(p []byte) (t uint64, err error) { + if !HasTime(p) { + return 0, ErrNoTime + } + t = binary.BigEndian.Uint64(p[timeDataIndx : timeDataIndx+timeDataSize]) + return t, nil +} + +// LocationFrom takes a byte slice representation of a psi-pmt and extracts it's +// timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil +// if it does not exist +func LocationFrom(p []byte) (g string, err error) { + if !HasLocation(p) { + return "", ErrNoLocation + } + gBytes := p[locationDataIndx : locationDataIndx+locationDataSize] + gBytes = bytes.Trim(gBytes, "\x00") + g = string(gBytes) + return g, nil +} + +// LocationStrBytes take a string of location data and converts to a 32 byte slice - +// easy update of slice representation of a pmt with location descriptor +func LocationStrBytes(s string) []byte { + var b [locationDataSize]byte + copy(b[:], s) + return b[:] +} + +// UpdateLocation takes a byte slice representation of a psi-pmt containing a location +// descriptor and attempts to update the location data value with the passed string. +// If the psi does not contain a location descriptor, and error is returned. +func UpdateLocation(d []byte, s string) error { + if !HasLocation(d) { + return ErrNoLocation + } + gb := LocationStrBytes(s) + for i := range d[locationDataIndx : locationDataIndx+locationDataSize] { + d[i+locationDataIndx] = gb[i] + } + d = updateCrc(d) + return nil +} From 8cf2181958367f3154f8b30ad25d18eb7f243481 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 13:56:25 +1030 Subject: [PATCH 053/137] psi: modified error messages for updateTime and updateLocation to make clearer --- stream/mts/psi/helpers.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index be8a9541..a3275fc3 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -33,12 +33,6 @@ import ( "errors" ) -// error message -var ( - ErrNoLocation = errors.New("psi does not have location descriptor") - ErrNoTime = errors.New("psi does not have time descriptor") -) - // TimeBytes takes a timestamp as an uint64 and converts to an 8 byte slice - // allows for updating of timestamp in pmt time descriptor. func TimeBytes(t uint64) []byte { @@ -70,7 +64,7 @@ func HasLocation(p []byte) bool { // given time if the time descriptor exists, otherwise an error is returned func UpdateTime(dst []byte, t uint64) error { if !HasTime(dst) { - return ErrNoTime + return errors.New("pmt does not have time descriptor, cannot update") } ts := TimeBytes(uint64(t)) for i := range dst[timeDataIndx : timeDataIndx+timeDataSize] { @@ -85,7 +79,7 @@ func UpdateTime(dst []byte, t uint64) error { // if it does not exist func TimeFrom(p []byte) (t uint64, err error) { if !HasTime(p) { - return 0, ErrNoTime + return 0, errors.New("pmt does not have a time descriptor") } t = binary.BigEndian.Uint64(p[timeDataIndx : timeDataIndx+timeDataSize]) return t, nil @@ -96,7 +90,7 @@ func TimeFrom(p []byte) (t uint64, err error) { // if it does not exist func LocationFrom(p []byte) (g string, err error) { if !HasLocation(p) { - return "", ErrNoLocation + return "", errors.New("pmt does not have location descriptor") } gBytes := p[locationDataIndx : locationDataIndx+locationDataSize] gBytes = bytes.Trim(gBytes, "\x00") @@ -117,7 +111,7 @@ func LocationStrBytes(s string) []byte { // If the psi does not contain a location descriptor, and error is returned. func UpdateLocation(d []byte, s string) error { if !HasLocation(d) { - return ErrNoLocation + return errors.New("pmt does not location descriptor, cannot update") } gb := LocationStrBytes(s) for i := range d[locationDataIndx : locationDataIndx+locationDataSize] { From 0d3abd5798832fd4d4fb817dbba1e4e7af9bec6c Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 14:24:54 +1030 Subject: [PATCH 054/137] psi: using own trim function to get rid of null chars in gps string --- stream/mts/psi/helpers.go | 9 +++++++++ stream/mts/psi/psi_test.go | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index a3275fc3..9a4bb849 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -120,3 +120,12 @@ func UpdateLocation(d []byte, s string) error { d = updateCrc(d) return nil } + +func trim(d []byte, t byte) []byte { + for i, b := range d { + if b == t { + return d[:i] + } + } + return d +} diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 86c19f60..909791c3 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -272,6 +272,15 @@ func TestLocationUpdate(t *testing.T) { } } +func TestTrim(t *testing.T) { + test := []byte{0xa3, 0x01, 0x03, 0x00, 0xde} + want := []byte{0xa3, 0x01, 0x03} + got := trim(test, 0x00) + if !bytes.Equal(got, want) { + t.Errorf(errCmp, "TestTrim", want, got) + } +} + // buildPmtTimeLocationBytes is a helper function to help construct the byte slices // for pmts with time and location, as the location data field is 32 bytes, i.e. quite large // to type out From 261118187b84f35dd0726b8fd2754cfb59bf2d91 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:16:53 +1030 Subject: [PATCH 055/137] psi: using copy inside UpdateLocation() instead of range --- stream/mts/psi/helpers.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index 9a4bb849..3448a0eb 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -114,9 +114,7 @@ func UpdateLocation(d []byte, s string) error { return errors.New("pmt does not location descriptor, cannot update") } gb := LocationStrBytes(s) - for i := range d[locationDataIndx : locationDataIndx+locationDataSize] { - d[i+locationDataIndx] = gb[i] - } + copy(d[locationDataIndx:locationDataIndx+locationDataSize], gb) d = updateCrc(d) return nil } From 6ebb982238fd39238cde6bf30b360d15d5ecc387 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:18:20 +1030 Subject: [PATCH 056/137] psi: improved comment for updateCrc --- stream/mts/psi/crc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go index 55bad3ed..c173d63c 100644 --- a/stream/mts/psi/crc.go +++ b/stream/mts/psi/crc.go @@ -38,7 +38,7 @@ func addCrc(out []byte) []byte { return out } -// updateCrc updates the crc of psi bytes slice that may have been modified +// updateCrc updates the crc of bytes slice, writing the checksum into the last four bytes. func updateCrc(out []byte) []byte { crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[1:len(out)-4]) out[len(out)-4] = byte(crc32 >> 24) From fa0154ad3d64dbf526741e522514f3358473dfee Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:20:06 +1030 Subject: [PATCH 057/137] psi: using b instead of out for updateCrc --- stream/mts/psi/crc.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go index c173d63c..82736abd 100644 --- a/stream/mts/psi/crc.go +++ b/stream/mts/psi/crc.go @@ -39,13 +39,13 @@ func addCrc(out []byte) []byte { } // updateCrc updates the crc of bytes slice, writing the checksum into the last four bytes. -func updateCrc(out []byte) []byte { - crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), out[1:len(out)-4]) - out[len(out)-4] = byte(crc32 >> 24) - out[len(out)-3] = byte(crc32 >> 16) - out[len(out)-2] = byte(crc32 >> 8) - out[len(out)-1] = byte(crc32) - return out +func updateCrc(b []byte) []byte { + crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), b[1:len(b)-4]) + b[len(b)-4] = byte(crc32 >> 24) + b[len(b)-3] = byte(crc32 >> 16) + b[len(b)-2] = byte(crc32 >> 8) + b[len(b)-1] = byte(crc32) + return b } func crc32_MakeTable(poly uint32) *crc32.Table { From ad55d315773a64a8e011928344e665213013cc57 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 16:20:35 +1030 Subject: [PATCH 058/137] Made type and variable names idiomatic, and merged C_RTMP into Session. --- rtmp/rtmp.go | 929 ++++++++++++++++++------------------------- rtmp/rtmp_headers.go | 115 ++---- rtmp/session.go | 128 ++++-- 3 files changed, 525 insertions(+), 647 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index c585d13b..e85f1658 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -8,9 +8,10 @@ DESCRIPTION AUTHORS Saxon Nelson-Milton Dan Kortschak + Alan Noble LICENSE - rtmp.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + rtmp.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the @@ -142,40 +143,10 @@ var ( errCopying = errors.New("rtmp: copying error") ) -func startSession(rtmp *C_RTMP, u string, timeout uint) (*C_RTMP, error) { - rtmp = C_RTMP_Alloc() - C_RTMP_Init(rtmp) - rtmp.Link.timeout = timeout - err := C_RTMP_SetupURL(rtmp, u) - if err != nil { - C_RTMP_Close(rtmp) - return nil, err - } - - C_RTMP_EnableWrite(rtmp) - rtmp.nBufferMS = 3600 * 1000 - err = C_RTMP_Connect(rtmp, nil) - if err != nil { - C_RTMP_Close(rtmp) - return nil, err - } - - err = C_RTMP_ConnectStream(rtmp, 0) - if err != nil { - C_RTMP_Close(rtmp) - return nil, err - } - - return rtmp, nil -} - -func endSession(rtmp *C_RTMP) uint32 { - if rtmp == nil { - return 3 - } - - C_RTMP_Close(rtmp) - return 0 +// RTMPPacket_IsReady(a) +// rtmp.h +142 +func C_RTMPPacket_IsReady(pkt *packet) bool { + return pkt.bytesRead == pkt.bodySize } // uint32_t RTMP_GetTime(); @@ -184,55 +155,10 @@ func C_RTMP_GetTime() int32 { return int32(time.Now().UnixNano() / 1000000) } -// int RTMPPacket_Alloc(RTMPPacket* p, uint32_t nSize); -// rtmp.c +189 -func C_RTMPPacket_Alloc(p *C_RTMPPacket, nSize uint32) { - buf := make([]byte, RTMP_MAX_HEADER_SIZE+nSize) - p.header = buf - p.body = buf[RTMP_MAX_HEADER_SIZE:] - p.nBytesRead = 0 -} - -// void RTMPPacket_Free(RTMPPacket* p); -// rtmp.c +203 -func C_RTMPPacket_Free(p *C_RTMPPacket) { - if p.body != nil { - p.body = nil - } -} - -// RTMP* RTMP_IsConnected(); -// rtmp.c +317 -func C_RTMP_Alloc() *C_RTMP { - return &C_RTMP{} -} - -// void RTMP_Init(RTMP *r); -// rtmp.c +329 -func C_RTMP_Init(r *C_RTMP) { - *r = C_RTMP{} - r.inChunkSize = RTMP_DEFAULT_CHUNKSIZE - r.outChunkSize = RTMP_DEFAULT_CHUNKSIZE - r.nBufferMS = 30000 - r.nClientBW = 2500000 - r.nClientBW2 = 2 - r.nServerBW = 2500000 - r.fAudioCodecs = 3191.0 - r.fVideoCodecs = 252.0 - r.Link.timeout = 30 - r.Link.swfAge = 30 -} - // void RTMP_EnableWrite(RTMP *r); // rtmp.c +351 -func C_RTMP_EnableWrite(r *C_RTMP) { - r.Link.protocol |= RTMP_FEATURE_WRITE -} - -// int RTMP_IsConnected(RTMP *r); -// rtmp.c +363 -func C_RTMP_IsConnected(r *C_RTMP) bool { - return r.Link.conn != nil +func C_RTMP_EnableWrite(s *Session) { + s.link.protocol |= RTMP_FEATURE_WRITE } // void RTMP_SetBufferMS(RTMP *r, int size); @@ -246,31 +172,30 @@ func C_RTMP_IsConnected(r *C_RTMP) bool { // int RTMP_SetupURL(RTMP *r, char* url); // rtmp.c +757 // NOTE: code dealing with rtmp over http has been disregarded -func C_RTMP_SetupURL(r *C_RTMP, addr string) (err error) { - r.Link.protocol, r.Link.host, r.Link.port, r.Link.app, r.Link.playpath0, err = C_RTMP_ParseURL(addr) +func C_RTMP_SetupURL(s *Session, addr string) (err error) { + s.link.protocol, s.link.host, s.link.port, s.link.app, s.link.playpath, err = C_RTMP_ParseURL(addr) if err != nil { return err } - r.Link.playpath = r.Link.playpath0 - if r.Link.tcUrl == "" { - if r.Link.app != "" { - r.Link.tcUrl = fmt.Sprintf("%v://%v:%v/%v", - RTMPProtocolStringsLower[r.Link.protocol], r.Link.host, r.Link.port, r.Link.app) - r.Link.lFlags |= RTMP_LF_FTCU + if s.link.tcUrl == "" { + if s.link.app != "" { + s.link.tcUrl = fmt.Sprintf("%v://%v:%v/%v", + RTMPProtocolStringsLower[s.link.protocol], s.link.host, s.link.port, s.link.app) + s.link.lFlags |= RTMP_LF_FTCU } else { - r.Link.tcUrl = addr + s.link.tcUrl = addr } } - if r.Link.port == 0 { + if s.link.port == 0 { switch { - case (r.Link.protocol & RTMP_FEATURE_SSL) != 0: - r.Link.port = 433 - case (r.Link.protocol & RTMP_FEATURE_HTTP) != 0: - r.Link.port = 80 + case (s.link.protocol & RTMP_FEATURE_SSL) != 0: + s.link.port = 433 + case (s.link.protocol & RTMP_FEATURE_HTTP) != 0: + s.link.port = 80 default: - r.Link.port = 1935 + s.link.port = 1935 } } return nil @@ -278,19 +203,19 @@ func C_RTMP_SetupURL(r *C_RTMP, addr string) (err error) { // int RTMP_Connect(RTMP *r, RTMPPacket* cp); // rtmp.c +1032 -func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { - addr, err := net.ResolveTCPAddr("tcp4", r.Link.host+":"+strconv.Itoa(int(r.Link.port))) +func C_RTMP_Connect(s *Session, cp *packet) error { + addr, err := net.ResolveTCPAddr("tcp4", s.link.host+":"+strconv.Itoa(int(s.link.port))) if err != nil { return err } - r.Link.conn, err = net.DialTCP("tcp4", nil, addr) + s.link.conn, err = net.DialTCP("tcp4", nil, addr) if err != nil { return err } if debugMode { log.Println("... connected, handshaking...") } - err = C_HandShake(r, 1) + err = C_HandShake(s, 1) if err != nil { log.Println("C_RTMP_Connect1: handshake failed!") return errHandshake @@ -298,7 +223,7 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { if debugMode { log.Println("... handshaked...") } - err = C_SendConnectPacket(r, cp) + err = C_SendConnectPacket(s, cp) if err != nil { log.Println("RTMP connect failed!") return errConnSend @@ -312,41 +237,39 @@ func C_RTMP_Connect(r *C_RTMP, cp *C_RTMPPacket) error { // int RTMP_ConnectStream(RTMP* r, int seekTime); // rtmp.c +1099 -// Side effects: r.bPlaying is set true upon successful connection -func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { - var packet C_RTMPPacket +// Side effects: s.isPlaying is set true upon successful connection +func C_RTMP_ConnectStream(s *Session, seekTime int32) error { + var pkt packet if seekTime > 0 { - r.Link.seekTime = seekTime + s.link.seekTime = seekTime } - r.mediaChannel = 0 - - for !r.bPlaying && C_RTMP_IsConnected(r) { - err := C_RTMP_ReadPacket(r, &packet) + for !s.isPlaying && s.isConnected() { + err := C_RTMP_ReadPacket(s, &pkt) if err != nil { break } // TODO: port is ready - if C_RTMPPacket_IsReady(&packet) { - if packet.nBodySize == 0 { + if C_RTMPPacket_IsReady(&pkt) { + if pkt.bodySize == 0 { continue } - if packet.packetType == RTMP_PACKET_TYPE_AUDIO || - packet.packetType == RTMP_PACKET_TYPE_VIDEO || - packet.packetType == RTMP_PACKET_TYPE_INFO { + if pkt.packetType == RTMP_PACKET_TYPE_AUDIO || + pkt.packetType == RTMP_PACKET_TYPE_VIDEO || + pkt.packetType == RTMP_PACKET_TYPE_INFO { log.Println("C_RTMP_ConnectStream: got packet before play()! Ignoring.") - C_RTMPPacket_Free(&packet) + pkt.body = nil continue } - C_RTMP_ClientPacket(r, &packet) - C_RTMPPacket_Free(&packet) + C_RTMP_ClientPacket(s, &pkt) + pkt.body = nil } } - if !r.bPlaying { + if !s.isPlaying { return errConnStream } return nil @@ -355,13 +278,13 @@ func C_RTMP_ConnectStream(r *C_RTMP, seekTime int32) error { // int RTMP_ClientPacket() // rtmp.c +1226 // NOTE cases have been commented out that are not currently used by AusOcean -func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { - var bHasMediaPacket int32 - switch packet.packetType { +func C_RTMP_ClientPacket(s *Session, pkt *packet) int32 { + var hasMediaPacket int32 + switch pkt.packetType { case RTMP_PACKET_TYPE_CHUNK_SIZE: // TODO: port this - C_HandleChangeChunkSize(r, packet) + C_HandleChangeChunkSize(s, pkt) case RTMP_PACKET_TYPE_BYTES_READ_REPORT: // TODO: usue new logger here @@ -372,15 +295,15 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { /* log.Println("RTMP_PACKET_TYPE_CONTROL") // TODO: port this - C.HandleCtrl(r, packet) + C.HandleCtrl(s, pkt) */ case RTMP_PACKET_TYPE_SERVER_BW: // TODO: port this - C_HandlServerBW(r, packet) + C_HandlServerBW(s, pkt) case RTMP_PACKET_TYPE_CLIENT_BW: // TODO: port this - C_HandleClientBW(r, packet) + C_HandleClientBW(s, pkt) case RTMP_PACKET_TYPE_AUDIO: panic("Unsupported packet type RTMP_PACKET_TYPE_AUDIO") @@ -397,13 +320,13 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { case RTMP_PACKET_TYPE_INVOKE: log.Println("RTMP_PACKET_TYPE_INVOKE:") // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,packet.nBodySize); + //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,pkt.bodySize); - err := C_HandleInvoke(r, packet.body[:packet.nBodySize]) + err := C_HandleInvoke(s, pkt.body[:pkt.bodySize]) if err != nil { // This will never happen with the methods we implement. log.Println("HasMediaPacket") - bHasMediaPacket = 2 + hasMediaPacket = 2 } case RTMP_PACKET_TYPE_FLASH_VIDEO: @@ -411,29 +334,29 @@ func C_RTMP_ClientPacket(r *C_RTMP, packet *C_RTMPPacket) int32 { default: // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,packet.packetType); + // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,pkt.packetType); } - return bHasMediaPacket + return hasMediaPacket } // int ReadN(RTMP* r, char* buffer, int n); // rtmp.c +1390 -func C_ReadN(r *C_RTMP, buf []byte) error { - err := r.Link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(r.Link.timeout))) +func C_ReadN(s *Session, buf []byte) error { + err := s.link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { return err } - n, err := io.ReadFull(r.Link.conn, buf) + n, err := io.ReadFull(s.link.conn, buf) if err != nil { if debugMode { log.Printf("C_ReadN error: %v\n", err) } - C_RTMP_Close(r) + s.close() return err } - r.nBytesIn += int32(n) - if r.nBytesIn > (r.nBytesInSent + r.nClientBW/10) { - err := C_SendBytesReceived(r) + s.nBytesIn += int32(n) + if s.nBytesIn > (s.nBytesInSent + s.clientBW/10) { + err := C_SendBytesReceived(s) if err != nil { return err } @@ -443,18 +366,18 @@ func C_ReadN(r *C_RTMP, buf []byte) error { // int WriteN(RTMP* r, const char* buffer, int n); // rtmp.c +1502 -func C_WriteN(r *C_RTMP, buf []byte) error { +func C_WriteN(s *Session, buf []byte) error { //ToDo: consider using a different timeout for writes than for reads - err := r.Link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(r.Link.timeout))) + err := s.link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { return err } - _, err = r.Link.conn.Write(buf) + _, err = s.link.conn.Write(buf) if err != nil { if debugMode { log.Printf("C_WriteN, RTMP send error: %v\n", err) } - C_RTMP_Close(r) + s.close() return err } return nil @@ -462,68 +385,68 @@ func C_WriteN(r *C_RTMP, buf []byte) error { // int SendConnectPacket(RTMP* r, RTMPPacket* cp); // rtmp.c +1579 -func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { +func C_SendConnectPacket(s *Session, cp *packet) error { if cp != nil { - return C_RTMP_SendPacket(r, cp, 1) + return C_RTMP_SendPacket(s, cp, 1) } var pbuf [4096]byte - packet := C_RTMPPacket{ - nChannel: 0x03, + pkt := packet{ + channel: 0x03, headerType: RTMP_PACKET_SIZE_LARGE, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_connect) if enc == nil { return errEncoding } - r.numInvokes += 1 - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes += 1 + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_OBJECT enc = enc[1:] - enc = C_AMF_EncodeNamedString(enc, av_app, r.Link.app) + enc = C_AMF_EncodeNamedString(enc, av_app, s.link.app) if enc == nil { return errEncoding } - if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { + if s.link.protocol&RTMP_FEATURE_WRITE != 0 { enc = C_AMF_EncodeNamedString(enc, av_type, av_nonprivate) if enc == nil { return errEncoding } } - if r.Link.flashVer != "" { - enc = C_AMF_EncodeNamedString(enc, av_flashVer, r.Link.flashVer) + if s.link.flashVer != "" { + enc = C_AMF_EncodeNamedString(enc, av_flashVer, s.link.flashVer) if enc == nil { return errEncoding } } - if r.Link.swfUrl != "" { - enc = C_AMF_EncodeNamedString(enc, av_swfUrl, r.Link.swfUrl) + if s.link.swfUrl != "" { + enc = C_AMF_EncodeNamedString(enc, av_swfUrl, s.link.swfUrl) if enc == nil { return errEncoding } } - if r.Link.tcUrl != "" { - enc = C_AMF_EncodeNamedString(enc, av_tcUrl, r.Link.tcUrl) + if s.link.tcUrl != "" { + enc = C_AMF_EncodeNamedString(enc, av_tcUrl, s.link.tcUrl) if enc == nil { return errEncoding } } - if r.Link.protocol&RTMP_FEATURE_WRITE == 0 { + if s.link.protocol&RTMP_FEATURE_WRITE == 0 { enc = C_AMF_EncodeNamedBoolean(enc, av_fpad, false) if enc == nil { return errEncoding @@ -532,11 +455,11 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_audioCodecs, r.fAudioCodecs) + enc = C_AMF_EncodeNamedNumber(enc, av_audioCodecs, s.audioCodecs) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_videoCodecs, r.fVideoCodecs) + enc = C_AMF_EncodeNamedNumber(enc, av_videoCodecs, s.videoCodecs) if enc == nil { return errEncoding } @@ -544,16 +467,16 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { if enc == nil { return errEncoding } - if r.Link.pageUrl != "" { - enc = C_AMF_EncodeNamedString(enc, av_pageUrl, r.Link.pageUrl) + if s.link.pageUrl != "" { + enc = C_AMF_EncodeNamedString(enc, av_pageUrl, s.link.pageUrl) if enc == nil { return errEncoding } } } - if r.fEncoding != 0.0 || r.bSendEncoding { - enc = C_AMF_EncodeNamedNumber(enc, av_objectEncoding, r.fEncoding) + if s.encoding != 0.0 || s.sendEncoding { + enc = C_AMF_EncodeNamedNumber(enc, av_objectEncoding, s.encoding) if enc == nil { return errEncoding } @@ -565,200 +488,200 @@ func C_SendConnectPacket(r *C_RTMP, cp *C_RTMPPacket) error { enc = enc[3:] /* add auth string */ - if r.Link.auth != "" { - enc = C_AMF_EncodeBoolean(enc, r.Link.lFlags&RTMP_LF_AUTH != 0) + if s.link.auth != "" { + enc = C_AMF_EncodeBoolean(enc, s.link.lFlags&RTMP_LF_AUTH != 0) if enc == nil { return errEncoding } - enc = C_AMF_EncodeString(enc, r.Link.auth) + enc = C_AMF_EncodeString(enc, s.link.auth) if enc == nil { return errEncoding } } - for i := range r.Link.extras.o_props { - enc = C_AMF_PropEncode(&r.Link.extras.o_props[i], enc) + for i := range s.link.extras.o_props { + enc = C_AMF_PropEncode(&s.link.extras.o_props[i], enc) if enc == nil { return errEncoding } } - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 1) + return C_RTMP_SendPacket(s, &pkt, 1) } // int RTMP_SendCreateStream(RTMP* r); // rtmp.c +1725 -func C_RTMP_SendCreateStream(r *C_RTMP) error { +func C_RTMP_SendCreateStream(s *Session) error { var pbuf [256]byte - packet := C_RTMPPacket{ - nChannel: 0x03, /* control channel (invoke) */ + pkt := packet{ + channel: 0x03, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_MEDIUM, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_createStream) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 1) + return C_RTMP_SendPacket(s, &pkt, 1) } // int SendReleaseStream(RTMP* r); // rtmp.c +1816 -func C_SendReleaseStream(r *C_RTMP) error { +func C_SendReleaseStream(s *Session) error { var pbuf [1024]byte - packet := C_RTMPPacket{ - nChannel: 0x03, /* control channel (invoke) */ + pkt := packet{ + channel: 0x03, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_MEDIUM, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_releaseStream) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - enc = C_AMF_EncodeString(enc, r.Link.playpath) + enc = C_AMF_EncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 0) + return C_RTMP_SendPacket(s, &pkt, 0) } // int SendFCPublish(RTMP* r); // rtmp.c +1846 -func C_SendFCPublish(r *C_RTMP) error { +func C_SendFCPublish(s *Session) error { var pbuf [1024]byte - packet := C_RTMPPacket{ - nChannel: 0x03, /* control channel (invoke) */ + pkt := packet{ + channel: 0x03, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_MEDIUM, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_FCPublish) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - enc = C_AMF_EncodeString(enc, r.Link.playpath) + enc = C_AMF_EncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 0) + return C_RTMP_SendPacket(s, &pkt, 0) } // int SendFCUnpublish(RTMP *r); // rtmp.c +1875 -func C_SendFCUnpublish(r *C_RTMP) error { +func C_SendFCUnpublish(s *Session) error { var pbuf [1024]byte - packet := C_RTMPPacket{ - nChannel: 0x03, /* control channel (invoke) */ + pkt := packet{ + channel: 0x03, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_MEDIUM, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_FCUnpublish) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - enc = C_AMF_EncodeString(enc, r.Link.playpath) + enc = C_AMF_EncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 0) + return C_RTMP_SendPacket(s, &pkt, 0) } // int SendPublish(RTMP* r); // rtmp.c +1908 -func C_SendPublish(r *C_RTMP) error { +func C_SendPublish(s *Session) error { var pbuf [1024]byte - packet := C_RTMPPacket{ - nChannel: 0x04, /* source channel (invoke) */ + pkt := packet{ + channel: 0x04, /* source channel (invoke) */ headerType: RTMP_PACKET_SIZE_LARGE, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: r.streamID, + timestamp: 0, + info: s.streamID, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_publish) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - enc = C_AMF_EncodeString(enc, r.Link.playpath) + enc = C_AMF_EncodeString(enc, s.link.playpath) if enc == nil { return errEncoding } @@ -767,34 +690,34 @@ func C_SendPublish(r *C_RTMP) error { return errEncoding } - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 1) + return C_RTMP_SendPacket(s, &pkt, 1) } // int // SendDeleteStream(RTMP *r, double dStreamId) // rtmp.c +1942 -func C_SendDeleteStream(r *C_RTMP, dStreamId float64) error { +func C_SendDeleteStream(s *Session, dStreamId float64) error { var pbuf [256]byte - packet := C_RTMPPacket{ - nChannel: 0x03, /* control channel (invoke) */ + pkt := packet{ + channel: 0x03, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_MEDIUM, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av_deleteStream) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } @@ -804,83 +727,83 @@ func C_SendDeleteStream(r *C_RTMP, dStreamId float64) error { if enc == nil { return errEncoding } - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) /* no response expected */ - return C_RTMP_SendPacket(r, &packet, 0) + return C_RTMP_SendPacket(s, &pkt, 0) } // int SendBytesReceived(RTMP* r); // rtmp.c +2080 -func C_SendBytesReceived(r *C_RTMP) error { +func C_SendBytesReceived(s *Session) error { var pbuf [256]byte - packet := C_RTMPPacket{ - nChannel: 0x02, /* control channel (invoke) */ + pkt := packet{ + channel: 0x02, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_MEDIUM, packetType: RTMP_PACKET_TYPE_BYTES_READ_REPORT, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body - r.nBytesInSent = r.nBytesIn - enc = C_AMF_EncodeInt32(enc, r.nBytesIn) + s.nBytesInSent = s.nBytesIn + enc = C_AMF_EncodeInt32(enc, s.nBytesIn) if enc == nil { return errEncoding } - packet.nBodySize = 4 + pkt.bodySize = 4 - return C_RTMP_SendPacket(r, &packet, 0) + return C_RTMP_SendPacket(s, &pkt, 0) } // int SendCheckBW(RTMP* r); // rtmp.c +2105 -func C_SendCheckBW(r *C_RTMP) error { +func C_SendCheckBW(s *Session) error { var pbuf [256]byte - packet := C_RTMPPacket{ - nChannel: 0x03, /* control channel (invoke) */ + pkt := packet{ + channel: 0x03, /* control channel (invoke) */ headerType: RTMP_PACKET_SIZE_LARGE, packetType: RTMP_PACKET_TYPE_INVOKE, - nTimeStamp: 0, - nInfoField2: 0, + timestamp: 0, + info: 0, hasAbsTimestamp: false, header: pbuf[:], body: pbuf[RTMP_MAX_HEADER_SIZE:], } - enc := packet.body + enc := pkt.body enc = C_AMF_EncodeString(enc, av__checkbw) if enc == nil { return errEncoding } - r.numInvokes++ - enc = C_AMF_EncodeNumber(enc, float64(r.numInvokes)) + s.numInvokes++ + enc = C_AMF_EncodeNumber(enc, float64(s.numInvokes)) if enc == nil { return errEncoding } enc[0] = AMF_NULL enc = enc[1:] - packet.nBodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(r, &packet, 0) + return C_RTMP_SendPacket(s, &pkt, 0) } -// void AV_erase(C_RTMP_METHOD* vals, int* num, int i, int freeit); +// void AV_erase(method* vals, int* num, int i, int freeit); // rtmp.c +2393 -func C_AV_erase(m []C_RTMP_METHOD, i int) []C_RTMP_METHOD { +func C_AV_erase(m []method, i int) []method { copy(m[i:], m[i+1:]) - m[len(m)-1] = C_RTMP_METHOD{} + m[len(m)-1] = method{} return m[:len(m)-1] } -// int HandleInvoke(RTMP* r, const char* body, unsigned int nBodySize); +// int HandleInvoke(RTMP* r, const char* body, unsigned int bodySize); // rtmp.c +2912 -// Side effects: r.bPlaying set to true upon av_NetStream_Publish_Start -func C_HandleInvoke(r *C_RTMP, body []byte) error { +// Side effects: s.isPlaying set to true upon av_NetStream_Publish_Start +func C_HandleInvoke(s *Session, body []byte) error { if body[0] != 0x02 { return errInvalidBody } @@ -893,18 +816,18 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { // NOTE we don't really need this ?? still functions without it //C.AMF_Dump(&obj) //C.AMFProp_GetString(C_AMF_GetProp(&obj, nil, 0), &method) - method := C_AMFProp_GetString(C_AMF_GetProp(&obj, "", 0)) + meth := C_AMFProp_GetString(C_AMF_GetProp(&obj, "", 0)) txn := C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 1)) // TODO use new logger here // RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); - switch method { + switch meth { case av__result: var methodInvoked string - for i, m := range r.methodCalls { + for i, m := range s.methodCalls { if float64(m.num) == txn { methodInvoked = m.name - r.methodCalls = C_AV_erase(r.methodCalls, i) + s.methodCalls = C_AV_erase(s.methodCalls, i) break } } @@ -919,27 +842,27 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { //methodInvoked.av_val); switch methodInvoked { case av_connect: - if r.Link.token != "" { + if s.link.token != "" { panic("No support for link token") } - if (r.Link.protocol & RTMP_FEATURE_WRITE) != 0 { - C_SendReleaseStream(r) - C_SendFCPublish(r) + if (s.link.protocol & RTMP_FEATURE_WRITE) != 0 { + C_SendReleaseStream(s) + C_SendFCPublish(s) } else { panic("Link protocol has no RTMP_FEATURE_WRITE") } - C_RTMP_SendCreateStream(r) - if (r.Link.protocol & RTMP_FEATURE_WRITE) == 0 { + C_RTMP_SendCreateStream(s) + if (s.link.protocol & RTMP_FEATURE_WRITE) == 0 { panic("Link protocol has no RTMP_FEATURE_WRITE") } case av_createStream: - r.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) + s.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) - if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { - C_SendPublish(r) + if s.link.protocol&RTMP_FEATURE_WRITE != 0 { + C_SendPublish(s) } else { panic("Link protocol has no RTMP_FEATURE_WRITE") } @@ -950,8 +873,8 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { //C.free(unsafe.Pointer(methodInvoked.av_val)) case av_onBWDone: - if r.nBWCheckCounter == 0 { - C_SendCheckBW(r) + if s.bwCheckCounter == 0 { + C_SendCheckBW(s) } case av_onFCUnsubscribe, av_onFCSubscribe: @@ -989,10 +912,10 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { panic("Unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") case av_NetStream_Publish_Start: - r.bPlaying = true - for i, m := range r.methodCalls { + s.isPlaying = true + for i, m := range s.methodCalls { if m.name == av_publish { - r.methodCalls = C_AV_erase(r.methodCalls, i) + s.methodCalls = C_AV_erase(s.methodCalls, i) break } } @@ -1011,7 +934,7 @@ func C_HandleInvoke(r *C_RTMP, body []byte) error { panic("Unsupported method av_playlist_ready") default: - panic(fmt.Sprintf("unknown method: %q", method)) + panic(fmt.Sprintf("unknown method: %q", meth)) } leave: C_AMF_Reset(&obj) @@ -1021,37 +944,36 @@ leave: // void HandleChangeChunkSize(RTMP* r, const RTMPPacket* packet); // rtmp.c +3345 -func C_HandleChangeChunkSize(r *C_RTMP, packet *C_RTMPPacket) { - if packet.nBodySize >= 4 { - //r.inChunkSize = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.body)))) - r.inChunkSize = int32(C_AMF_DecodeInt32(packet.body[:4])) +func C_HandleChangeChunkSize(s *Session, pkt *packet) { + if pkt.bodySize >= 4 { + s.inChunkSize = int32(C_AMF_DecodeInt32(pkt.body[:4])) // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, r.inChunkSize); + // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, s.inChunkSize); } } // void HandleServerBW(RTMP* r, const RTMPPacket* packet); // rtmp.c +3508 -func C_HandlServerBW(r *C_RTMP, packet *C_RTMPPacket) { - r.nServerBW = int32(C_AMF_DecodeInt32(packet.body[:4])) +func C_HandlServerBW(s *Session, pkt *packet) { + s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r.nServerBW); + // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, s.serverBW); } // void HandleClientBW(RTMP* r, const RTMPPacket* packet); // rtmp.c +3515 -func C_HandleClientBW(r *C_RTMP, packet *C_RTMPPacket) { - r.nClientBW = int32(C_AMF_DecodeInt32(packet.body[:4])) - //r.nClientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(packet.body)))) +func C_HandleClientBW(s *Session, pkt *packet) { + s.clientBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) + //s.clientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(pkt.body)))) - if packet.nBodySize > 4 { - r.nClientBW2 = packet.body[4] + if pkt.bodySize > 4 { + s.clientBW2 = pkt.body[4] } else { - r.nClientBW2 = 0xff + s.clientBW2 = 0xff } // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r.nClientBW, - //r.nClientBW2); + // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, s.clientBW, + //s.clientBW2); } // static int DecodeInt32LE(const char* data); @@ -1069,171 +991,177 @@ func C_EncodeInt32LE(dst []byte, v int32) int32 { // int RTMP_ReadPacket(RTMP* r, RTMPPacket* packet); // rtmp.c +3550 -func C_RTMP_ReadPacket(r *C_RTMP, packet *C_RTMPPacket) error { +func C_RTMP_ReadPacket(s *Session, pkt *packet) error { var hbuf [RTMP_MAX_HEADER_SIZE]byte header := hbuf[:] - err := C_ReadN(r, header[:1]) + err := C_ReadN(s, header[:1]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet header!") return err } - packet.headerType = (header[0] & 0xc0) >> 6 - packet.nChannel = int32(header[0] & 0x3f) + pkt.headerType = (header[0] & 0xc0) >> 6 + pkt.channel = int32(header[0] & 0x3f) header = header[1:] switch { - case packet.nChannel == 0: - err = C_ReadN(r, header[:1]) + case pkt.channel == 0: + err = C_ReadN(s, header[:1]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header 2nd byte.") return err } header = header[1:] - packet.nChannel = int32(header[0]) + 64 + pkt.channel = int32(header[0]) + 64 - case packet.nChannel == 1: - err = C_ReadN(r, header[:2]) + case pkt.channel == 1: + err = C_ReadN(s, header[:2]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet 3rd byte") return err } header = header[2:] - packet.nChannel = int32(binary.BigEndian.Uint16(header[:2])) + 64 + pkt.channel = int32(binary.BigEndian.Uint16(header[:2])) + 64 } - if packet.nChannel >= r.channelsAllocatedIn { - n := packet.nChannel + 10 - timestamp := append(r.channelTimestamp, make([]int32, 10)...) + if pkt.channel >= s.channelsAllocatedIn { + n := pkt.channel + 10 + timestamp := append(s.channelTimestamp, make([]int32, 10)...) - var packets []*C_RTMPPacket - if r.vecChannelsIn == nil { - packets = make([]*C_RTMPPacket, n) + var pkts []*packet + if s.vecChannelsIn == nil { + pkts = make([]*packet, n) } else { - packets = append(r.vecChannelsIn[:packet.nChannel:packet.nChannel], make([]*C_RTMPPacket, 10)...) + pkts = append(s.vecChannelsIn[:pkt.channel:pkt.channel], make([]*packet, 10)...) } - r.channelTimestamp = timestamp - r.vecChannelsIn = packets + s.channelTimestamp = timestamp + s.vecChannelsIn = pkts - for i := int(r.channelsAllocatedIn); i < len(r.channelTimestamp); i++ { - r.channelTimestamp[i] = 0 + for i := int(s.channelsAllocatedIn); i < len(s.channelTimestamp); i++ { + s.channelTimestamp[i] = 0 } - for i := int(r.channelsAllocatedIn); i < int(n); i++ { - r.vecChannelsIn[i] = nil + for i := int(s.channelsAllocatedIn); i < int(n); i++ { + s.vecChannelsIn[i] = nil } - r.channelsAllocatedIn = n + s.channelsAllocatedIn = n } - nSize := packetSize[packet.headerType] + size := packetSize[pkt.headerType] switch { - case nSize == RTMP_LARGE_HEADER_SIZE: - packet.hasAbsTimestamp = true - case nSize < RTMP_LARGE_HEADER_SIZE: - if r.vecChannelsIn[packet.nChannel] != nil { - *packet = *(r.vecChannelsIn[packet.nChannel]) + case size == RTMP_LARGE_HEADER_SIZE: + pkt.hasAbsTimestamp = true + case size < RTMP_LARGE_HEADER_SIZE: + if s.vecChannelsIn[pkt.channel] != nil { + *pkt = *(s.vecChannelsIn[pkt.channel]) } } - nSize-- + size-- - if nSize > 0 { - err = C_ReadN(r, header[:nSize]) + if size > 0 { + err = C_ReadN(s, header[:size]) if err != nil { - log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header.") + log.Println("C_RTMP_ReadPacket: failed to read rtmp packet heades.") return err } } - hSize := len(hbuf) - len(header) + nSize + hSize := len(hbuf) - len(header) + size - if nSize >= 3 { - packet.nTimeStamp = C_AMF_DecodeInt24(header[:3]) + if size >= 3 { + pkt.timestamp = C_AMF_DecodeInt24(header[:3]) - if nSize >= 6 { - packet.nBodySize = C_AMF_DecodeInt24(header[3:6]) - packet.nBytesRead = 0 + if size >= 6 { + pkt.bodySize = C_AMF_DecodeInt24(header[3:6]) + pkt.bytesRead = 0 - if nSize > 6 { - packet.packetType = header[6] + if size > 6 { + pkt.packetType = header[6] - if nSize == 11 { - packet.nInfoField2 = C_DecodeInt32LE(header[7:11]) + if size == 11 { + pkt.info = C_DecodeInt32LE(header[7:11]) } } } } - extendedTimestamp := packet.nTimeStamp == 0xffffff + extendedTimestamp := pkt.timestamp == 0xffffff if extendedTimestamp { - err = C_ReadN(r, header[nSize:nSize+4]) + err = C_ReadN(s, header[size:size+4]) if err != nil { log.Println("RTMPRead_Packet: Failed to read extended timestamp") return err } // TODO: port this - packet.nTimeStamp = C_AMF_DecodeInt32(header[nSize : nSize+4]) + pkt.timestamp = C_AMF_DecodeInt32(header[size : size+4]) hSize += 4 } - if packet.nBodySize > 0 && packet.body == nil { - // TODO: port this - C_RTMPPacket_Alloc(packet, packet.nBodySize) - packet.headerType = (hbuf[0] & 0xc0) >> 6 + if pkt.bodySize > 0 && pkt.body == nil { + resizePacket(pkt, pkt.bodySize, (hbuf[0]&0xc0)>>6) } - nToRead := int32(packet.nBodySize - packet.nBytesRead) - nChunk := r.inChunkSize + toRead := int32(pkt.bodySize - pkt.bytesRead) + chunkSize := s.inChunkSize - if nToRead < nChunk { - nChunk = nToRead + if toRead < chunkSize { + chunkSize = toRead } - if packet.chunk != nil { - packet.chunk.headerSize = int32(hSize) - copy(packet.chunk.header[:], hbuf[:hSize]) - packet.chunk.data = packet.body[packet.nBytesRead : packet.nBytesRead+uint32(nChunk)] + if pkt.chunk != nil { + pkt.chunk.headerSize = int32(hSize) + copy(pkt.chunk.header[:], hbuf[:hSize]) + pkt.chunk.data = pkt.body[pkt.bytesRead : pkt.bytesRead+uint32(chunkSize)] } - err = C_ReadN(r, packet.body[packet.nBytesRead:][:nChunk]) + err = C_ReadN(s, pkt.body[pkt.bytesRead:][:chunkSize]) if err != nil { log.Println("C_RTMP_ReadPacket: failed to read RTMP packet body") return err } - packet.nBytesRead += uint32(nChunk) + pkt.bytesRead += uint32(chunkSize) // keep the packet as ref for other packets on this channel - if r.vecChannelsIn[packet.nChannel] == nil { - r.vecChannelsIn[packet.nChannel] = &C_RTMPPacket{} + if s.vecChannelsIn[pkt.channel] == nil { + s.vecChannelsIn[pkt.channel] = &packet{} } - *(r.vecChannelsIn[packet.nChannel]) = *packet + *(s.vecChannelsIn[pkt.channel]) = *pkt if extendedTimestamp { - r.vecChannelsIn[packet.nChannel].nTimeStamp = 0xffffff + s.vecChannelsIn[pkt.channel].timestamp = 0xffffff } // TODO: port this - if C_RTMPPacket_IsReady(packet) { - if !packet.hasAbsTimestamp { + if C_RTMPPacket_IsReady(pkt) { + if !pkt.hasAbsTimestamp { // timestamps seem to always be relative - packet.nTimeStamp += uint32(r.channelTimestamp[packet.nChannel]) + pkt.timestamp += uint32(s.channelTimestamp[pkt.channel]) } - r.channelTimestamp[packet.nChannel] = int32(packet.nTimeStamp) + s.channelTimestamp[pkt.channel] = int32(pkt.timestamp) - r.vecChannelsIn[packet.nChannel].body = nil - r.vecChannelsIn[packet.nChannel].nBytesRead = 0 - r.vecChannelsIn[packet.nChannel].hasAbsTimestamp = false + s.vecChannelsIn[pkt.channel].body = nil + s.vecChannelsIn[pkt.channel].bytesRead = 0 + s.vecChannelsIn[pkt.channel].hasAbsTimestamp = false } else { - packet.body = nil /* so it won't be erased on free */ + pkt.body = nil /* so it won't be erased on free */ } return nil } +// resizePacket adjust the packet's storage to accommodate a body of the given size. +func resizePacket(pkt *packet, size uint32, ht uint8) { + buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) + pkt.headerType = ht + pkt.header = buf + pkt.body = buf[RTMP_MAX_HEADER_SIZE:] +} + // int HandShake(RTMP* r, int FP9HandShake); // rtmp.c +3744 -func C_HandShake(r *C_RTMP, FP9HandShake int32) error { +func C_HandShake(s *Session, FP9HandShake int32) error { var clientbuf [RTMP_SIG_SIZE + 1]byte clientsig := clientbuf[1:] @@ -1248,13 +1176,13 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) error { clientsig[i] = byte(rand.Intn(256)) } - err := C_WriteN(r, clientbuf[:]) + err := C_WriteN(s, clientbuf[:]) if err != nil { return err } var typ [1]byte - err = C_ReadN(r, typ[:]) + err = C_ReadN(s, typ[:]) if err != nil { return err } @@ -1266,7 +1194,7 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) error { log.Printf("C_HandShake: type mismatch: client sent %v, server sent: %v\n", clientbuf[0], typ) } - err = C_ReadN(r, serversig[:]) + err = C_ReadN(s, serversig[:]) if err != nil { return err } @@ -1279,12 +1207,12 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) error { // serversig[4], serversig[5], serversig[6], serversig[7]) // 2nd part of handshake - err = C_WriteN(r, serversig[:]) + err = C_WriteN(s, serversig[:]) if err != nil { return err } - err = C_ReadN(r, serversig[:]) + err = C_ReadN(s, serversig[:]) if err != nil { return err } @@ -1298,54 +1226,54 @@ func C_HandShake(r *C_RTMP, FP9HandShake int32) error { // int RTMP_SendPacket(RTMP* r, RTMPPacket* packet, int queue); // rtmp.c +3896 -func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { - var prevPacket *C_RTMPPacket +func C_RTMP_SendPacket(s *Session, pkt *packet, queue int) error { + var prevPkt *packet var last int - if packet.nChannel >= r.channelsAllocatedOut { - n := int(packet.nChannel + 10) + if pkt.channel >= s.channelsAllocatedOut { + n := int(pkt.channel + 10) - var packets []*C_RTMPPacket - if r.vecChannelsOut == nil { - packets = make([]*C_RTMPPacket, n) + var pkts []*packet + if s.vecChannelsOut == nil { + pkts = make([]*packet, n) } else { - packets = append(r.vecChannelsOut[:packet.nChannel:packet.nChannel], make([]*C_RTMPPacket, 10)...) + pkts = append(s.vecChannelsOut[:pkt.channel:pkt.channel], make([]*packet, 10)...) } - r.vecChannelsOut = packets + s.vecChannelsOut = pkts - for i := int(r.channelsAllocatedOut); i < n; i++ { - r.vecChannelsOut[i] = nil + for i := int(s.channelsAllocatedOut); i < n; i++ { + s.vecChannelsOut[i] = nil } - r.channelsAllocatedOut = int32(n) + s.channelsAllocatedOut = int32(n) } - prevPacket = r.vecChannelsOut[packet.nChannel] + prevPkt = s.vecChannelsOut[pkt.channel] - if prevPacket != nil && packet.headerType != RTMP_PACKET_SIZE_LARGE { + if prevPkt != nil && pkt.headerType != RTMP_PACKET_SIZE_LARGE { // compress a bit by using the prev packet's attributes - if prevPacket.nBodySize == packet.nBodySize && prevPacket.packetType == packet.packetType && packet.headerType == RTMP_PACKET_SIZE_MEDIUM { - packet.headerType = RTMP_PACKET_SIZE_SMALL + if prevPkt.bodySize == pkt.bodySize && prevPkt.packetType == pkt.packetType && pkt.headerType == RTMP_PACKET_SIZE_MEDIUM { + pkt.headerType = RTMP_PACKET_SIZE_SMALL } - if prevPacket.nTimeStamp == packet.nTimeStamp && packet.headerType == RTMP_PACKET_SIZE_SMALL { - packet.headerType = RTMP_PACKET_SIZE_MINIMUM + if prevPkt.timestamp == pkt.timestamp && pkt.headerType == RTMP_PACKET_SIZE_SMALL { + pkt.headerType = RTMP_PACKET_SIZE_MINIMUM } - last = int(prevPacket.nTimeStamp) + last = int(prevPkt.timestamp) } - if packet.headerType > 3 { + if pkt.headerType > 3 { log.Printf("Sanity failed! trying to send header of type: 0x%02x.", - packet.headerType) + pkt.headerType) return errInvalidHeader } var headBytes []byte var origIdx int - if packet.body != nil { + if pkt.body != nil { // Span from -packetsize for the type to the start of the body. - headBytes = packet.header - origIdx = RTMP_MAX_HEADER_SIZE - packetSize[packet.headerType] + headBytes = pkt.header + origIdx = RTMP_MAX_HEADER_SIZE - packetSize[pkt.headerType] } else { // Allocate a new header and allow 6 bytes of movement backward. var hbuf [RTMP_MAX_HEADER_SIZE]byte @@ -1355,21 +1283,21 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { var cSize int switch { - case packet.nChannel > 319: + case pkt.channel > 319: cSize = 2 - case packet.nChannel > 63: + case pkt.channel > 63: cSize = 1 } - hSize := packetSize[packet.headerType] + hSize := packetSize[pkt.headerType] if cSize != 0 { origIdx -= cSize hSize += cSize } var ts uint32 - if prevPacket != nil { - ts = uint32(int(packet.nTimeStamp) - last) + if prevPkt != nil { + ts = uint32(int(pkt.timestamp) - last) } if ts >= 0xffffff { origIdx -= 4 @@ -1379,10 +1307,10 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx := origIdx - c := packet.headerType << 6 + c := pkt.headerType << 6 switch cSize { case 0: - c |= byte(packet.nChannel) + c |= byte(pkt.channel) case 1: // Do nothing. case 2: @@ -1392,7 +1320,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx++ if cSize != 0 { - tmp := packet.nChannel - 64 + tmp := pkt.channel - 64 headBytes[headerIdx] = byte(tmp & 0xff) headerIdx++ @@ -1402,7 +1330,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { } } - if packetSize[packet.headerType] > 1 { + if packetSize[pkt.headerType] > 1 { res := ts if ts > 0xffffff { res = 0xffffff @@ -1411,15 +1339,15 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx += 3 // 24bits } - if packetSize[packet.headerType] > 4 { - C_AMF_EncodeInt24(headBytes[headerIdx:], int32(packet.nBodySize)) + if packetSize[pkt.headerType] > 4 { + C_AMF_EncodeInt24(headBytes[headerIdx:], int32(pkt.bodySize)) headerIdx += 3 // 24bits - headBytes[headerIdx] = packet.packetType + headBytes[headerIdx] = pkt.packetType headerIdx++ } - if packetSize[packet.headerType] > 8 { - n := int(C_EncodeInt32LE(headBytes[headerIdx:headerIdx+4], packet.nInfoField2)) + if packetSize[pkt.headerType] > 8 { + n := int(C_EncodeInt32LE(headBytes[headerIdx:headerIdx+4], pkt.info)) headerIdx += n } @@ -1428,48 +1356,48 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headerIdx += 4 // 32bits } - nSize := int(packet.nBodySize) - nChunkSize := int(r.outChunkSize) + size := int(pkt.bodySize) + chunkSize := int(s.outChunkSize) if debugMode { - log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", r.Link.conn.LocalAddr(), r.Link.conn.RemoteAddr(), nSize) + log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", s.link.conn.LocalAddr(), s.link.conn.RemoteAddr(), size) } // Send the previously deferred packet if combining it with the next packet would exceed the chunk size. - if r.defered != nil && len(r.defered)+nSize+hSize > nChunkSize { - err := C_WriteN(r, r.defered) + if s.defered != nil && len(s.defered)+size+hSize > chunkSize { + err := C_WriteN(s, s.defered) if err != nil { return err } - r.defered = nil + s.defered = nil } // TODO(kortschak): Rewrite this horrific peice of premature optimisation. // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. - for nSize+hSize != 0 { - if r.defered == nil && packet.packetType == RTMP_PACKET_TYPE_AUDIO && nSize < nChunkSize { - r.defered = headBytes[origIdx:][:nSize+hSize] + for size+hSize != 0 { + if s.defered == nil && pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { + s.defered = headBytes[origIdx:][:size+hSize] break } - if nChunkSize > nSize { - nChunkSize = nSize + if chunkSize > size { + chunkSize = size } - bytes := headBytes[origIdx:][:nChunkSize+hSize] - if r.defered != nil { + bytes := headBytes[origIdx:][:chunkSize+hSize] + if s.defered != nil { // Prepend the previously deferred packet and write it with the current one. - bytes = append(r.defered, bytes...) + bytes = append(s.defered, bytes...) } - err := C_WriteN(r, bytes) + err := C_WriteN(s, bytes) if err != nil { return err } - r.defered = nil + s.defered = nil - nSize -= nChunkSize - origIdx += nChunkSize + hSize + size -= chunkSize + origIdx += chunkSize + hSize hSize = 0 - if nSize > 0 { + if size > 0 { origIdx -= 1 + cSize hSize = 1 + cSize @@ -1481,7 +1409,7 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { headBytes[origIdx] = 0xc0 | c if cSize != 0 { - tmp := int(packet.nChannel) - 64 + tmp := int(pkt.channel) - 64 headBytes[origIdx+1] = byte(tmp) if cSize == 2 { @@ -1497,107 +1425,41 @@ func C_RTMP_SendPacket(r *C_RTMP, packet *C_RTMPPacket, queue int) error { // We invoked a remote method // TODO: port the const - if packet.packetType == RTMP_PACKET_TYPE_INVOKE { - buf := packet.body[1:] - method := C_AMF_DecodeString(buf) + if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { + buf := pkt.body[1:] + meth := C_AMF_DecodeString(buf) if debugMode { - log.Printf("invoking %v", method) + log.Printf("invoking %v", meth) } // keep it in call queue till result arrives if queue != 0 { - buf = buf[3+len(method):] + buf = buf[3+len(meth):] txn := int32(C_AMF_DecodeNumber(buf[:8])) - r.methodCalls = append(r.methodCalls, C_RTMP_METHOD{name: method, num: txn}) + s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) } } - if r.vecChannelsOut[packet.nChannel] == nil { - r.vecChannelsOut[packet.nChannel] = &C_RTMPPacket{} + if s.vecChannelsOut[pkt.channel] == nil { + s.vecChannelsOut[pkt.channel] = &packet{} } - *(r.vecChannelsOut[packet.nChannel]) = *packet + *(s.vecChannelsOut[pkt.channel]) = *pkt return nil } -// void RTMP_Close(RTMP *r); -// rtmp.c +4168 -func C_RTMP_Close(r *C_RTMP) { - C_CloseInternal(r, false) -} - -// static void CloseInternal(RTMP *r, int reconnect); -// rtmp.c +4175 -func C_CloseInternal(r *C_RTMP, reconnect bool) { - var i int32 - - if C_RTMP_IsConnected(r) { - if r.streamID > 0 { - i = r.streamID - if r.Link.protocol&RTMP_FEATURE_WRITE != 0 { - C_SendFCUnpublish(r) - } - C_SendDeleteStream(r, float64(i)) - } - err := r.Link.conn.Close() - if err != nil && debugMode { - log.Printf("C_RTMPSockBuf_Close error: %v\n", err) - } - } - - r.streamID = -1 - r.Link.conn = nil - r.nBWCheckCounter = 0 - r.nBytesIn = 0 - r.nBytesInSent = 0 - - r.write.nBytesRead = 0 - C_RTMPPacket_Free(&r.write) - - // NOTE: C frees - not using in our case - for i := 0; i < int(r.channelsAllocatedIn); i++ { - if r.vecChannelsIn[i] != nil { - r.vecChannelsIn[i] = nil - } - } - - //C.free(unsafe.Pointer(r.vecChannelsOut)) - r.vecChannelsOut = nil - r.channelsAllocatedOut = 0 - r.methodCalls = nil //C_AV_clear(r.methodCalls, r.numCalls) - - r.methodCalls = r.methodCalls[:0] - r.numInvokes = 0 - - r.bPlaying = false - - r.msgCounter = 0 - r.resplen = 0 - r.unackd = 0 - - if ((r.Link.lFlags & RTMP_LF_FTCU) != 0) && !reconnect { - r.Link.app = "" - r.Link.lFlags ^= RTMP_LF_FAPU - } - - if !reconnect { - r.Link.playpath0 = "" - } -} - /// int RTMP_Write(RTMP* r, const char* buf, int size); // rtmp.c +5136 -func C_RTMP_Write(r *C_RTMP, buf []byte) error { - // TODO: port RTMPPacket - var pkt = &r.write +func C_RTMP_Write(s *Session, buf []byte) error { + var pkt = &s.write var enc []byte size := len(buf) var num int - pkt.nChannel = 0x04 - pkt.nInfoField2 = r.streamID + pkt.channel = 0x04 + pkt.info = s.streamID for len(buf) != 0 { - if pkt.nBytesRead == 0 { + if pkt.bytesRead == 0 { if size < minDataSize { return errTinyPacket } @@ -1608,51 +1470,50 @@ func C_RTMP_Write(r *C_RTMP, buf []byte) error { pkt.packetType = buf[0] buf = buf[1:] - pkt.nBodySize = C_AMF_DecodeInt24(buf[:3]) + pkt.bodySize = C_AMF_DecodeInt24(buf[:3]) buf = buf[3:] - pkt.nTimeStamp = C_AMF_DecodeInt24(buf[:3]) + pkt.timestamp = C_AMF_DecodeInt24(buf[:3]) buf = buf[3:] - pkt.nTimeStamp |= uint32(buf[0]) << 24 + pkt.timestamp |= uint32(buf[0]) << 24 buf = buf[4:] - pkt.headerType = RTMP_PACKET_SIZE_MEDIUM + headerType := uint8(RTMP_PACKET_SIZE_MEDIUM) switch pkt.packetType { case RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_AUDIO: - if pkt.nTimeStamp == 0 { - pkt.headerType = RTMP_PACKET_SIZE_LARGE + if pkt.timestamp == 0 { + headerType = RTMP_PACKET_SIZE_LARGE } case RTMP_PACKET_TYPE_INFO: - pkt.headerType = RTMP_PACKET_SIZE_LARGE - pkt.nBodySize += 16 + headerType = RTMP_PACKET_SIZE_LARGE + pkt.bodySize += 16 } - // TODO: Port this - C_RTMPPacket_Alloc(pkt, pkt.nBodySize) + resizePacket(pkt, pkt.bodySize, headerType) - enc = pkt.body[:pkt.nBodySize] + enc = pkt.body[:pkt.bodySize] if pkt.packetType == RTMP_PACKET_TYPE_INFO { enc = C_AMF_EncodeString(enc, setDataFrame) if enc == nil { return errEncoding } - pkt.nBytesRead = uint32(len(pkt.body) - len(enc)) + pkt.bytesRead = uint32(len(pkt.body) - len(enc)) } } else { - enc = pkt.body[:pkt.nBodySize][pkt.nBytesRead:] + enc = pkt.body[:pkt.bodySize][pkt.bytesRead:] } - num = int(pkt.nBodySize - pkt.nBytesRead) + num = int(pkt.bodySize - pkt.bytesRead) if num > len(buf) { num = len(buf) } copy(enc[:num], buf[:num]) - pkt.nBytesRead += uint32(num) + pkt.bytesRead += uint32(num) buf = buf[num:] - if pkt.nBytesRead == pkt.nBodySize { - err := C_RTMP_SendPacket(r, pkt, 0) - C_RTMPPacket_Free(pkt) - pkt.nBytesRead = 0 + if pkt.bytesRead == pkt.bodySize { + err := C_RTMP_SendPacket(s, pkt, 0) + pkt.body = nil + pkt.bytesRead = 0 if err != nil { return err } diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 4174c332..2260f01d 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -7,9 +7,11 @@ DESCRIPTION AUTHORS Saxon Nelson-Milton + Dan Kortschak + Alan Noble LICENSE - rtmp_headers.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + rtmp_headers.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the @@ -112,102 +114,47 @@ const ( RTMP_MAX_HEADER_SIZE = 18 ) -// typedef struct RTMPChunk -// rtmp.h +105 -type C_RTMPChunk struct { +type chunk struct { headerSize int32 - data []byte + data []byte header [RTMP_MAX_HEADER_SIZE]byte } -// typedef struct RTMPPacket -// rtmp.h +113 -type C_RTMPPacket struct { +type packet struct { headerType uint8 packetType uint8 hasAbsTimestamp bool - nChannel int32 - nTimeStamp uint32 - nInfoField2 int32 - nBodySize uint32 - nBytesRead uint32 - chunk *C_RTMPChunk + channel int32 + timestamp uint32 + info int32 + bodySize uint32 + bytesRead uint32 + chunk *chunk header []byte body []byte } -// typedef struct RTMPSockBuf -// rtmp.h +127 -// DELETED: subsumed by C_RTMP_LNK - -// RTMPPacket_IsReady(a) -// rtmp.h +142 -func C_RTMPPacket_IsReady(p *C_RTMPPacket) bool { - return p.nBytesRead == p.nBodySize +type link struct { + host string + playpath string + tcUrl string + swfUrl string + pageUrl string + app string + auth string + flashVer string + token string + extras C_AMFObject + seekTime int32 + lFlags int32 + swfAge int32 + protocol int32 + timeout uint + port uint16 + conn *net.TCPConn } -// typedef struct RTMP_LNK -// rtmp.h +144 -type C_RTMP_LNK struct { - host string - playpath0 string - playpath string - tcUrl string - swfUrl string - pageUrl string - app string - auth string - flashVer string - token string - extras C_AMFObject - seekTime int32 - lFlags int32 - swfAge int32 - protocol int32 - timeout uint - port uint16 - conn *net.TCPConn -} - -// typedef struct RTMPMethod -// rtmp.h +231 -type C_RTMP_METHOD struct { +type method struct { name string num int32 } - -// typedef struct RTMP -// rtmp.h +237 -type C_RTMP struct { - inChunkSize int32 - outChunkSize int32 - nBWCheckCounter int32 - nBytesIn int32 - nBytesInSent int32 - nBufferMS int32 - streamID int32 - mediaChannel int32 - pausing int32 - nServerBW int32 - nClientBW int32 - nClientBW2 uint8 - bPlaying bool - bSendEncoding bool - numInvokes int32 - methodCalls []C_RTMP_METHOD - channelsAllocatedIn int32 - channelsAllocatedOut int32 - vecChannelsIn []*C_RTMPPacket - vecChannelsOut []*C_RTMPPacket - channelTimestamp []int32 - fAudioCodecs float64 - fVideoCodecs float64 - fEncoding float64 - fDuration float64 - msgCounter int32 - resplen int32 - unackd int32 - write C_RTMPPacket - defered []byte - Link C_RTMP_LNK -} diff --git a/rtmp/session.go b/rtmp/session.go index ff2cbd83..b6b066f4 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -8,9 +8,10 @@ DESCRIPTION AUTHORS Saxon Nelson-Milton Dan Kortschak + Alan Noble LICENSE - session.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + session.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the @@ -36,14 +37,37 @@ import ( "errors" ) -// session provides parameters required for an rtmp communication session. +// Session holds the state for an RTMP session. type Session struct { - rtmp *C_RTMP - url string - timeout uint + url string + timeout uint + inChunkSize int32 + outChunkSize int32 + bwCheckCounter int32 + nBytesIn int32 + nBytesInSent int32 + streamID int32 + serverBW int32 + clientBW int32 + clientBW2 uint8 + isPlaying bool + sendEncoding bool + numInvokes int32 + methodCalls []method + channelsAllocatedIn int32 + channelsAllocatedOut int32 + vecChannelsIn []*packet + vecChannelsOut []*packet + channelTimestamp []int32 + audioCodecs float64 + videoCodecs float64 + encoding float64 + write packet + defered []byte + link link } -// NewSession returns a new session. +// NewSession returns a new Session. func NewSession(url string, connectTimeout uint) *Session { return &Session{ url: url, @@ -51,48 +75,94 @@ func NewSession(url string, connectTimeout uint) *Session { } } -// Open establishes an rtmp connection with the url passed into the -// constructor +// Open establishes an rtmp connection with the url passed into the constructor. func (s *Session) Open() error { - if s.rtmp != nil { + if s.isConnected() { return errors.New("rtmp: attempt to start already running session") } - var err error - s.rtmp, err = startSession(s.rtmp, s.url, s.timeout) - if s.rtmp == nil { + err := s.start() + if err != nil { return err } return nil } -// Close terminates the rtmp connection -func (s *Session) Close() error { - if s.rtmp == nil { - return Err(3) +// start does the heavylifting for Open(). +func (s *Session) start() error { + s.init() + err := C_RTMP_SetupURL(s, s.url) + if err != nil { + s.close() + return err } - ret := endSession(s.rtmp) - s.rtmp = nil - if ret != 0 { - return Err(ret) + + C_RTMP_EnableWrite(s) + err = C_RTMP_Connect(s, nil) + if err != nil { + s.close() + return err + } + + err = C_RTMP_ConnectStream(s, 0) + if err != nil { + s.close() + return err } return nil } -// Write writes a frame (flv tag) to the rtmp connection -func (s *Session) Write(data []byte) (int, error) { - if s.rtmp == nil { - return 0, Err(3) - } +// init initializes various RTMP defauls. +// ToDo: define consts for the magic numbers. +func (s *Session) init() { + s.inChunkSize = RTMP_DEFAULT_CHUNKSIZE + s.outChunkSize = RTMP_DEFAULT_CHUNKSIZE + s.clientBW = 2500000 + s.clientBW2 = 2 + s.serverBW = 2500000 + s.audioCodecs = 3191.0 + s.videoCodecs = 252.0 + s.link.timeout = s.timeout + s.link.swfAge = 30 +} - if !C_RTMP_IsConnected(s.rtmp) { +// Close terminates the rtmp connection, +func (s *Session) Close() error { + if !s.isConnected() { + return Err(3) + } + s.close() + return nil +} + +// close does the heavylifting for Close(). +// Any errors are ignored as it is often called in response to an earlier error. +func (s *Session) close() { + if s.isConnected() { + if s.streamID > 0 { + if s.link.protocol&RTMP_FEATURE_WRITE != 0 { + C_SendFCUnpublish(s) + } + C_SendDeleteStream(s, float64(s.streamID)) + } + s.link.conn.Close() + } + s = &Session{} +} + +// Write writes a frame (flv tag) to the rtmp connection. +func (s *Session) Write(data []byte) (int, error) { + if !s.isConnected() { return 0, Err(1) } - - err := C_RTMP_Write(s.rtmp, data) + err := C_RTMP_Write(s, data) if err != nil { - //if C.RTMP_Write(s.rtmp, (*byte)(unsafe.Pointer(&data[0])), int32(len(data))) == 0 { // TODO: propagate err return 0, Err(2) } return len(data), nil } + +// isConnected returns true if the RTMP connection is up. +func (s *Session) isConnected() bool { + return s.link.conn != nil +} From 26f26cbeeecb41a5d76aa14c70819ca1f8973d8a Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:23:38 +1030 Subject: [PATCH 059/137] psi: using binary.BigEndian.PutUin32 to put crc32 into the psi --- stream/mts/psi/crc.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go index 82736abd..a2e3fa9a 100644 --- a/stream/mts/psi/crc.go +++ b/stream/mts/psi/crc.go @@ -27,6 +27,7 @@ LICENSE package psi import ( + "encoding/binary" "hash/crc32" "math/bits" ) @@ -41,10 +42,7 @@ func addCrc(out []byte) []byte { // updateCrc updates the crc of bytes slice, writing the checksum into the last four bytes. func updateCrc(b []byte) []byte { crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), b[1:len(b)-4]) - b[len(b)-4] = byte(crc32 >> 24) - b[len(b)-3] = byte(crc32 >> 16) - b[len(b)-2] = byte(crc32 >> 8) - b[len(b)-1] = byte(crc32) + binary.BigEndian.PutUint32(b[len(b)-4:], crc32) return b } From 0c0afa8bde7c7383510fe873353274a6e2367b4b Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:25:08 +1030 Subject: [PATCH 060/137] psi: removed redundent return in updateCrc --- stream/mts/psi/crc.go | 5 ++--- stream/mts/psi/helpers.go | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go index a2e3fa9a..2da603f7 100644 --- a/stream/mts/psi/crc.go +++ b/stream/mts/psi/crc.go @@ -35,15 +35,14 @@ import ( // addCrc appends a crc table to a given psi table in bytes func addCrc(out []byte) []byte { out = append(out, make([]byte, 4)...) - out = updateCrc(out) + updateCrc(out) return out } // updateCrc updates the crc of bytes slice, writing the checksum into the last four bytes. -func updateCrc(b []byte) []byte { +func updateCrc(b []byte) { crc32 := crc32_Update(0xffffffff, crc32_MakeTable(bits.Reverse32(crc32.IEEE)), b[1:len(b)-4]) binary.BigEndian.PutUint32(b[len(b)-4:], crc32) - return b } func crc32_MakeTable(poly uint32) *crc32.Table { diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index 3448a0eb..fa29fc3f 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -70,7 +70,7 @@ func UpdateTime(dst []byte, t uint64) error { for i := range dst[timeDataIndx : timeDataIndx+timeDataSize] { dst[i+timeDataIndx] = ts[i] } - dst = updateCrc(dst) + updateCrc(dst) return nil } @@ -115,7 +115,7 @@ func UpdateLocation(d []byte, s string) error { } gb := LocationStrBytes(s) copy(d[locationDataIndx:locationDataIndx+locationDataSize], gb) - d = updateCrc(d) + updateCrc(d) return nil } From ba9e5a31366c694302d15bc55400414a56c4c150 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:27:18 +1030 Subject: [PATCH 061/137] psi: renamed SD (specific data) interface to SpecificData --- stream/mts/psi/psi.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 07d460bb..f046496e 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -72,26 +72,26 @@ type PSI struct { // Table syntax section type TSS struct { - Tide uint16 // Table ID extension - V byte // Version number - Cni bool // Current/next indicator - Sn byte // Section number - Lsn byte // Last section number - Sd SD // Specific data PAT/PMT + Tide uint16 // Table ID extension + V byte // Version number + Cni bool // Current/next indicator + Sn byte // Section number + Lsn byte // Last section number + Sd SpecificData // Specific data PAT/PMT } // Specific Data, (could be PAT or PMT) -type SD interface { +type SpecificData interface { Bytes() []byte } -// Program association table, implements SD +// Program association table, implements SpecificData type PAT struct { Pn uint16 // Program Number Pmpid uint16 // Program map PID } -// Program mapping table, implements SD +// Program mapping table, implements SpecificData type PMT struct { Pcrpid uint16 // Program clock reference pid Pil uint16 // Program info length From 13b8c233519a67d30911f9f144f530812a284097 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:30:48 +1030 Subject: [PATCH 062/137] psi: removed byteToBool func as can do this by other means --- stream/mts/psi/psi.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index f046496e..d316d79b 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -124,8 +124,8 @@ func ReadPSI(data []byte) *PSI { } psi.Tid = data[pos] pos++ - psi.Ssi = byteToBool(data[pos] & 0x80) - psi.Pb = byteToBool(data[pos] & 0x40) + psi.Ssi = data[pos]&0x80 != 0 + psi.Pb = data[pos]&0x40 != 0 psi.Sl = uint16(data[pos]&0x03)<<8 | uint16(data[pos+1]) pos += 2 psi.Tss = readTSS(data[pos:], &psi) @@ -139,7 +139,7 @@ func readTSS(data []byte, p *PSI) *TSS { tss.Tide = uint16(data[pos])<<8 | uint16(data[pos+1]) pos += 2 tss.V = (data[pos] & 0x3e) >> 1 - tss.Cni = byteToBool(data[pos] & 0x01) + tss.Cni = data[pos]&0x01 != 0 pos++ tss.Sn = data[pos] pos++ @@ -237,7 +237,7 @@ func (t *TSS) Bytes() []byte { out := make([]byte, TSSDefLen) out[0] = byte(t.Tide >> 8) out[1] = byte(t.Tide) - out[2] = 0xc0 | (0x3e & (t.V << 1)) | (0x01 & boolToByte(t.Cni)) + out[2] = 0xc0 | (0x3e & (t.V << 1)) | (0x01 & asByte(t.Cni)) out[3] = t.Sn out[4] = t.Lsn out = append(out, t.Sd.Bytes()...) @@ -291,16 +291,9 @@ func (e *ESSD) Bytes() []byte { return out } -func boolToByte(b bool) (o byte) { +func asByte(b bool) (o byte) { if b { o = 0x01 } return } - -func byteToBool(b byte) (o bool) { - if b != 0 { - o = true - } - return -} From b2b5db1f1fc4807bbda9cdfa72975fdf01f67faa Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:32:18 +1030 Subject: [PATCH 063/137] psi: using lowercase letters in hex --- stream/mts/psi/psi_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 909791c3..3971601a 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -33,8 +33,8 @@ import ( // Times as ints for testing const ( - tstTime1 = 1235367435 // 0x49A2360B - tstTime2 = 1735357535 // 0x676F745F + tstTime1 = 1235367435 // 0x49a2360b + tstTime2 = 1735357535 // 0x676f745f ) // GPS string for testing From 83e4a4c5c52c5c206f0f120b669f4e572e2fe176 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 16:58:12 +1030 Subject: [PATCH 064/137] psi: removing uneccessary conversion in psi_test.go --- stream/mts/psi/psi_test.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 3971601a..0066cb5c 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -60,11 +60,11 @@ var ( var ( pmtTimeLocationBytesPart1 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, - byte(timeDescTag), // Descriptor tag for timestamp - byte(timeDataSize), // Length of bytes to follow + timeDescTag, // Descriptor tag for timestamp + timeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // Timestamp data - byte(locationDescTag), // Descriptor tag for location - byte(locationDataSize), // Length of bytes to follow + locationDescTag, // Descriptor tag for location + locationDataSize, // Length of bytes to follow } pmtTimeLocationBytesPart2 = []byte{ 0x1b, 0xe1, 0x00, 0xf0, 0x00, @@ -75,8 +75,8 @@ var ( // Bytes representing pmt with tstTime1 pmtTimeBytes1 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, - byte(timeDescTag), // Descriptor tag - byte(timeDataSize), // Length of bytes to follow + timeDescTag, // Descriptor tag + timeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } @@ -84,8 +84,8 @@ var ( // Bytes representing pmt with tstTime 2 pmtTimeBytes2 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, - byte(timeDescTag), // Descriptor tag - byte(timeDataSize), // Length of bytes to follow + timeDescTag, // Descriptor tag + timeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } @@ -124,9 +124,9 @@ var bytesTests = []struct { Pf: 0x00, Tid: 0x02, Ssi: true, - Sl: uint16(0x12), + Sl: 0x12, Tss: &TSS{ - Tide: uint16(0x01), + Tide: 0x01, V: 0, Cni: true, Sn: 0, @@ -136,8 +136,8 @@ var bytesTests = []struct { Pil: 10, Pd: []Desc{ Desc{ - Dt: byte(timeDescTag), - Dl: byte(timeDataSize), + Dt: timeDescTag, + Dl: timeDataSize, Dd: TimeBytes(tstTime1), }, }, @@ -159,9 +159,9 @@ var bytesTests = []struct { Pf: 0x00, Tid: 0x02, Ssi: true, - Sl: uint16(0x12), + Sl: 0x12, Tss: &TSS{ - Tide: uint16(0x01), + Tide: 0x01, V: 0, Cni: true, Sn: 0, @@ -171,13 +171,13 @@ var bytesTests = []struct { Pil: 10, Pd: []Desc{ Desc{ - Dt: byte(timeDescTag), - Dl: byte(timeDataSize), + Dt: timeDescTag, + Dl: timeDataSize, Dd: TimeBytes(tstTime2), }, Desc{ - Dt: byte(locationDescTag), - Dl: byte(locationDataSize), + Dt: locationDescTag, + Dl: locationDataSize, Dd: LocationStrBytes(locationTstStr1), }, }, From 2284d38492f200edd414b03d0cdbea6250d3ca5a Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 17:00:48 +1030 Subject: [PATCH 065/137] psi: removed more unnecessary conversions --- stream/mts/psi/std.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/stream/mts/psi/std.go b/stream/mts/psi/std.go index de28d44a..0cf4d5fb 100644 --- a/stream/mts/psi/std.go +++ b/stream/mts/psi/std.go @@ -38,16 +38,16 @@ var ( Tid: 0x00, Ssi: true, Pb: false, - Sl: uint16(0x0d), + Sl: 0x0d, Tss: &TSS{ - Tide: uint16(0x01), + Tide: 0x01, V: 0, Cni: true, Sn: 0, Lsn: 0, Sd: &PAT{ - Pn: uint16(0x01), - Pmpid: uint16(0x1000), + Pn: 0x01, + Pmpid: 0x1000, }, }, } @@ -57,9 +57,9 @@ var ( Pf: 0x00, Tid: 0x02, Ssi: true, - Sl: uint16(0x12), + Sl: 0x12, Tss: &TSS{ - Tide: uint16(0x01), + Tide: 0x01, V: 0, Cni: true, Sn: 0, @@ -81,9 +81,9 @@ var ( Pf: 0x00, Tid: 0x02, Ssi: true, - Sl: uint16(0x3e), + Sl: 0x3e, Tss: &TSS{ - Tide: uint16(0x01), + Tide: 0x01, V: 0, Cni: true, Sn: 0, @@ -93,13 +93,13 @@ var ( Pil: pmtTimeLocationPil, Pd: []Desc{ Desc{ - Dt: byte(timeDescTag), - Dl: byte(timeDataSize), + Dt: timeDescTag, + Dl: timeDataSize, Dd: make([]byte, timeDataSize), }, Desc{ - Dt: byte(locationDescTag), - Dl: byte(locationDataSize), + Dt: locationDescTag, + Dl: locationDataSize, Dd: make([]byte, locationDataSize), }, }, From d19327b6d520e6865ff3bb9084bc918984936e69 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 17:02:57 +1030 Subject: [PATCH 066/137] psi: renamed std... to standard... --- stream/mts/psi/psi_test.go | 10 +++++----- stream/mts/psi/std.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 0066cb5c..ccffbad1 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -106,15 +106,15 @@ var bytesTests = []struct { // Pat test { name: "pat Bytes()", - input: StdPat, - want: StdPatBytes, + input: StandardPat, + want: StandardPatBytes, }, // Pmt test data no descriptor { name: "pmt to Bytes() without descriptors", - input: StdPmt, - want: StdPmtBytes, + input: StandardPmt, + want: StandardPmtBytes, }, // Pmt with time descriptor @@ -243,7 +243,7 @@ func TestTimeGet(t *testing.T) { // TestLocationGet checks that we can correctly get location data from a pmt table func TestLocationGet(t *testing.T) { - pb := StdPmtTimeLocation.Bytes() + pb := StandardPmtTimeLocation.Bytes() err := UpdateLocation(pb, locationTstStr1) if err != nil { t.Errorf("Error for TestLocationGet UpdateLocation(pb, locationTstStr1): %v", err) diff --git a/stream/mts/psi/std.go b/stream/mts/psi/std.go index 0cf4d5fb..329f37cd 100644 --- a/stream/mts/psi/std.go +++ b/stream/mts/psi/std.go @@ -33,7 +33,7 @@ const ( // Some common manifestations of PSI var ( // PSI struct to represent basic pat - StdPat = PSI{ + StandardPat = PSI{ Pf: 0x00, Tid: 0x00, Ssi: true, @@ -53,7 +53,7 @@ var ( } // PSI struct to represent basic pmt without descriptors for time and location - StdPmt = PSI{ + StandardPmt = PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, @@ -77,7 +77,7 @@ var ( } // Std pmt with time and location descriptors, time and location fields are zeroed out - StdPmtTimeLocation = PSI{ + StandardPmtTimeLocation = PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, @@ -115,7 +115,7 @@ var ( // Std PSI in bytes form var ( - StdPatBytes = []byte{ + StandardPatBytes = []byte{ 0x00, // pointer // ---- section included in data sent to CRC32 during check @@ -136,7 +136,7 @@ var ( // 0x2a, 0xb1, 0x04, 0xb2, // CRC // ---- } - StdPmtBytes = []byte{ + StandardPmtBytes = []byte{ 0x00, // pointer // ---- section included in data sent to CRC32 during check From 561e603d9645a2c78e491af904b675edac4ddd04 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 17:04:57 +1030 Subject: [PATCH 067/137] psi: ran gofmt -s -w on files --- stream/mts/psi/psi_test.go | 6 +++--- stream/mts/psi/std.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index ccffbad1..afb361f5 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -135,7 +135,7 @@ var bytesTests = []struct { Pcrpid: 0x0100, // wrong Pil: 10, Pd: []Desc{ - Desc{ + { Dt: timeDescTag, Dl: timeDataSize, Dd: TimeBytes(tstTime1), @@ -170,12 +170,12 @@ var bytesTests = []struct { Pcrpid: 0x0100, // wrong Pil: 10, Pd: []Desc{ - Desc{ + { Dt: timeDescTag, Dl: timeDataSize, Dd: TimeBytes(tstTime2), }, - Desc{ + { Dt: locationDescTag, Dl: locationDataSize, Dd: LocationStrBytes(locationTstStr1), diff --git a/stream/mts/psi/std.go b/stream/mts/psi/std.go index 329f37cd..4fd9618c 100644 --- a/stream/mts/psi/std.go +++ b/stream/mts/psi/std.go @@ -92,12 +92,12 @@ var ( Pcrpid: 0x0100, Pil: pmtTimeLocationPil, Pd: []Desc{ - Desc{ + { Dt: timeDescTag, Dl: timeDataSize, Dd: make([]byte, timeDataSize), }, - Desc{ + { Dt: locationDescTag, Dl: locationDataSize, Dd: make([]byte, locationDataSize), From ffc1af2cd46554ed463812d71f3eed9611cec2e7 Mon Sep 17 00:00:00 2001 From: saxon Date: Mon, 7 Jan 2019 17:13:50 +1030 Subject: [PATCH 068/137] psi: removed declaration and initialisation of standard psi structures in std.go as this is dangerous --- stream/mts/encoder.go | 87 ++++++++++++++++++++++++++++- stream/mts/psi/helpers.go | 18 +++--- stream/mts/psi/psi.go | 16 +++--- stream/mts/psi/psi_test.go | 111 ++++++++++++++++++++++++++++++++----- stream/mts/psi/std.go | 85 +--------------------------- 5 files changed, 200 insertions(+), 117 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 49294b31..ce84bd16 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -36,6 +36,89 @@ import ( "bitbucket.org/ausocean/av/stream/mts/psi" ) +// Some common manifestations of PSI +var ( + // PSI struct to represent basic pat + StandardPat = psi.PSI{ + Pf: 0x00, + Tid: 0x00, + Ssi: true, + Pb: false, + Sl: 0x0d, + Tss: &psi.TSS{ + Tide: 0x01, + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &psi.PAT{ + Pn: 0x01, + Pmpid: 0x1000, + }, + }, + } + + // PSI struct to represent basic pmt without descriptors for time and location + StandardPmt = psi.PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: 0x12, + Tss: &psi.TSS{ + Tide: 0x01, + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &psi.PMT{ + Pcrpid: 0x0100, // wrong + Pil: 0, + Essd: &psi.ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + } + + // Std pmt with time and location descriptors, time and location fields are zeroed out + StandardPmtTimeLocation = psi.PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: 0x3e, + Tss: &psi.TSS{ + Tide: 0x01, + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &psi.PMT{ + Pcrpid: 0x0100, + Pil: psi.PmtTimeLocationPil, + Pd: []psi.Desc{ + { + Dt: psi.TimeDescTag, + Dl: psi.TimeDataSize, + Dd: make([]byte, psi.TimeDataSize), + }, + { + Dt: psi.LocationDescTag, + Dl: psi.LocationDataSize, + Dd: make([]byte, psi.LocationDataSize), + }, + }, + Essd: &psi.ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + } +) + const ( psiPacketSize = 184 psiSendCount = 7 @@ -57,8 +140,8 @@ func SetLocation(g string) { } var ( - patTable = psi.StdPat.Bytes() - pmtTable = psi.StdPmtTimeLocation.Bytes() + patTable = StandardPat.Bytes() + pmtTable = StandardPmtTimeLocation.Bytes() ) const ( diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index fa29fc3f..b04e9f76 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -36,7 +36,7 @@ import ( // TimeBytes takes a timestamp as an uint64 and converts to an 8 byte slice - // allows for updating of timestamp in pmt time descriptor. func TimeBytes(t uint64) []byte { - var s [timeDataSize]byte + var s [TimeDataSize]byte binary.BigEndian.PutUint64(s[:], t) return s[:] } @@ -44,7 +44,7 @@ func TimeBytes(t uint64) []byte { // HasTime takes a psi as a byte slice and checks to see if it has a time descriptor // - if so return nil, otherwise return error func HasTime(p []byte) bool { - if p[timeTagIndx] == timeDescTag { + if p[TimeTagIndx] == TimeDescTag { return true } return false @@ -53,7 +53,7 @@ func HasTime(p []byte) bool { // HasLocation takes a psi as a byte slice and checks to see if it has a location descriptor // - if so return nil, otherwise return error func HasLocation(p []byte) bool { - if p[locationTagIndx] == locationDescTag { + if p[LocationTagIndx] == LocationDescTag { return true } return false @@ -67,8 +67,8 @@ func UpdateTime(dst []byte, t uint64) error { return errors.New("pmt does not have time descriptor, cannot update") } ts := TimeBytes(uint64(t)) - for i := range dst[timeDataIndx : timeDataIndx+timeDataSize] { - dst[i+timeDataIndx] = ts[i] + for i := range dst[TimeDataIndx : TimeDataIndx+TimeDataSize] { + dst[i+TimeDataIndx] = ts[i] } updateCrc(dst) return nil @@ -81,7 +81,7 @@ func TimeFrom(p []byte) (t uint64, err error) { if !HasTime(p) { return 0, errors.New("pmt does not have a time descriptor") } - t = binary.BigEndian.Uint64(p[timeDataIndx : timeDataIndx+timeDataSize]) + t = binary.BigEndian.Uint64(p[TimeDataIndx : TimeDataIndx+TimeDataSize]) return t, nil } @@ -92,7 +92,7 @@ func LocationFrom(p []byte) (g string, err error) { if !HasLocation(p) { return "", errors.New("pmt does not have location descriptor") } - gBytes := p[locationDataIndx : locationDataIndx+locationDataSize] + gBytes := p[LocationDataIndx : LocationDataIndx+LocationDataSize] gBytes = bytes.Trim(gBytes, "\x00") g = string(gBytes) return g, nil @@ -101,7 +101,7 @@ func LocationFrom(p []byte) (g string, err error) { // LocationStrBytes take a string of location data and converts to a 32 byte slice - // easy update of slice representation of a pmt with location descriptor func LocationStrBytes(s string) []byte { - var b [locationDataSize]byte + var b [LocationDataSize]byte copy(b[:], s) return b[:] } @@ -114,7 +114,7 @@ func UpdateLocation(d []byte, s string) error { return errors.New("pmt does not location descriptor, cannot update") } gb := LocationStrBytes(s) - copy(d[locationDataIndx:locationDataIndx+locationDataSize], gb) + copy(d[LocationDataIndx:LocationDataIndx+LocationDataSize], gb) updateCrc(d) return nil } diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index d316d79b..821ea341 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -44,18 +44,18 @@ const ( // Consts relating to time description const ( - timeDescTag = 234 - timeTagIndx = 13 - timeDataIndx = 15 - timeDataSize = 8 // bytes, because time is stored in uint64 + TimeDescTag = 234 + TimeTagIndx = 13 + TimeDataIndx = 15 + TimeDataSize = 8 // bytes, because time is stored in uint64 ) // Consts relating to location description const ( - locationDescTag = 235 - locationTagIndx = 23 - locationDataIndx = 25 - locationDataSize = 32 // bytes + LocationDescTag = 235 + LocationTagIndx = 23 + LocationDataIndx = 25 + LocationDataSize = 32 // bytes ) // Program specific information diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index afb361f5..d3380d9a 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -31,6 +31,89 @@ import ( "testing" ) +// Some common manifestations of PSI +var ( + // PSI struct to represent basic pat + StandardPat = PSI{ + Pf: 0x00, + Tid: 0x00, + Ssi: true, + Pb: false, + Sl: 0x0d, + Tss: &TSS{ + Tide: 0x01, + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PAT{ + Pn: 0x01, + Pmpid: 0x1000, + }, + }, + } + + // PSI struct to represent basic pmt without descriptors for time and location + StandardPmt = PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: 0x12, + Tss: &TSS{ + Tide: 0x01, + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PMT{ + Pcrpid: 0x0100, // wrong + Pil: 0, + Essd: &ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + } + + // Std pmt with time and location descriptors, time and location fields are zeroed out + StandardPmtTimeLocation = PSI{ + Pf: 0x00, + Tid: 0x02, + Ssi: true, + Sl: 0x3e, + Tss: &TSS{ + Tide: 0x01, + V: 0, + Cni: true, + Sn: 0, + Lsn: 0, + Sd: &PMT{ + Pcrpid: 0x0100, + Pil: PmtTimeLocationPil, + Pd: []Desc{ + { + Dt: TimeDescTag, + Dl: TimeDataSize, + Dd: make([]byte, TimeDataSize), + }, + { + Dt: LocationDescTag, + Dl: LocationDataSize, + Dd: make([]byte, LocationDataSize), + }, + }, + Essd: &ESSD{ + St: 0x1b, + Epid: 0x0100, + Esil: 0x00, + }, + }, + }, + } +) + // Times as ints for testing const ( tstTime1 = 1235367435 // 0x49a2360b @@ -60,11 +143,11 @@ var ( var ( pmtTimeLocationBytesPart1 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, - timeDescTag, // Descriptor tag for timestamp - timeDataSize, // Length of bytes to follow + TimeDescTag, // Descriptor tag for timestamp + TimeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // Timestamp data - locationDescTag, // Descriptor tag for location - locationDataSize, // Length of bytes to follow + LocationDescTag, // Descriptor tag for location + LocationDataSize, // Length of bytes to follow } pmtTimeLocationBytesPart2 = []byte{ 0x1b, 0xe1, 0x00, 0xf0, 0x00, @@ -75,8 +158,8 @@ var ( // Bytes representing pmt with tstTime1 pmtTimeBytes1 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, - timeDescTag, // Descriptor tag - timeDataSize, // Length of bytes to follow + TimeDescTag, // Descriptor tag + TimeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } @@ -84,8 +167,8 @@ var ( // Bytes representing pmt with tstTime 2 pmtTimeBytes2 = []byte{ 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, - timeDescTag, // Descriptor tag - timeDataSize, // Length of bytes to follow + TimeDescTag, // Descriptor tag + TimeDataSize, // Length of bytes to follow 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } @@ -136,8 +219,8 @@ var bytesTests = []struct { Pil: 10, Pd: []Desc{ { - Dt: timeDescTag, - Dl: timeDataSize, + Dt: TimeDescTag, + Dl: TimeDataSize, Dd: TimeBytes(tstTime1), }, }, @@ -171,13 +254,13 @@ var bytesTests = []struct { Pil: 10, Pd: []Desc{ { - Dt: timeDescTag, - Dl: timeDataSize, + Dt: TimeDescTag, + Dl: TimeDataSize, Dd: TimeBytes(tstTime2), }, { - Dt: locationDescTag, - Dl: locationDataSize, + Dt: LocationDescTag, + Dl: LocationDataSize, Dd: LocationStrBytes(locationTstStr1), }, }, diff --git a/stream/mts/psi/std.go b/stream/mts/psi/std.go index 4fd9618c..1ebe5ea3 100644 --- a/stream/mts/psi/std.go +++ b/stream/mts/psi/std.go @@ -27,90 +27,7 @@ LICENSE package psi const ( - pmtTimeLocationPil = 44 -) - -// Some common manifestations of PSI -var ( - // PSI struct to represent basic pat - StandardPat = PSI{ - Pf: 0x00, - Tid: 0x00, - Ssi: true, - Pb: false, - Sl: 0x0d, - Tss: &TSS{ - Tide: 0x01, - V: 0, - Cni: true, - Sn: 0, - Lsn: 0, - Sd: &PAT{ - Pn: 0x01, - Pmpid: 0x1000, - }, - }, - } - - // PSI struct to represent basic pmt without descriptors for time and location - StandardPmt = PSI{ - Pf: 0x00, - Tid: 0x02, - Ssi: true, - Sl: 0x12, - Tss: &TSS{ - Tide: 0x01, - V: 0, - Cni: true, - Sn: 0, - Lsn: 0, - Sd: &PMT{ - Pcrpid: 0x0100, // wrong - Pil: 0, - Essd: &ESSD{ - St: 0x1b, - Epid: 0x0100, - Esil: 0x00, - }, - }, - }, - } - - // Std pmt with time and location descriptors, time and location fields are zeroed out - StandardPmtTimeLocation = PSI{ - Pf: 0x00, - Tid: 0x02, - Ssi: true, - Sl: 0x3e, - Tss: &TSS{ - Tide: 0x01, - V: 0, - Cni: true, - Sn: 0, - Lsn: 0, - Sd: &PMT{ - Pcrpid: 0x0100, - Pil: pmtTimeLocationPil, - Pd: []Desc{ - { - Dt: timeDescTag, - Dl: timeDataSize, - Dd: make([]byte, timeDataSize), - }, - { - Dt: locationDescTag, - Dl: locationDataSize, - Dd: make([]byte, locationDataSize), - }, - }, - Essd: &ESSD{ - St: 0x1b, - Epid: 0x0100, - Esil: 0x00, - }, - }, - }, - } + PmtTimeLocationPil = 44 ) // Std PSI in bytes form From 31b1a6a7d3e099ee94671482ba5cbc9c849c4958 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 18:00:42 +1030 Subject: [PATCH 069/137] Made function names camel case and factored packet functions into packet.go. --- rtmp/packet.go | 489 ++++++++++++++++++++++++++++ rtmp/parseurl.go | 5 +- rtmp/rtmp.go | 742 ++++++------------------------------------- rtmp/rtmp_headers.go | 46 +-- rtmp/session.go | 26 +- 5 files changed, 614 insertions(+), 694 deletions(-) create mode 100644 rtmp/packet.go diff --git a/rtmp/packet.go b/rtmp/packet.go new file mode 100644 index 00000000..310abed1 --- /dev/null +++ b/rtmp/packet.go @@ -0,0 +1,489 @@ +/* +NAME + packet.go + +DESCRIPTION + See Readme.md + +AUTHORS + Saxon Nelson-Milton + Dan Kortschak + Alan Noble + +LICENSE + packet.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. + + Derived from librtmp under the GNU Lesser General Public License 2.1 + Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org + Copyright (C) 2008-2009 Andrej Stepanchuk + Copyright (C) 2009-2010 Howard Chu +*/ + +package rtmp + +import ( + "encoding/binary" + "log" +) + +const ( + RTMP_PACKET_TYPE_CHUNK_SIZE = 0x01 + RTMP_PACKET_TYPE_BYTES_READ_REPORT = 0x03 + RTMP_PACKET_TYPE_CONTROL = 0x04 + RTMP_PACKET_TYPE_SERVER_BW = 0x05 + RTMP_PACKET_TYPE_CLIENT_BW = 0x06 + RTMP_PACKET_TYPE_AUDIO = 0x08 + RTMP_PACKET_TYPE_VIDEO = 0x09 + RTMP_PACKET_TYPE_FLEX_STREAM_SEND = 0x0F + RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT = 0x10 + RTMP_PACKET_TYPE_FLEX_MESSAGE = 0x11 + RTMP_PACKET_TYPE_INFO = 0x12 + RTMP_PACKET_TYPE_INVOKE = 0x14 + RTMP_PACKET_TYPE_FLASH_VIDEO = 0x16 +) + +const ( + RTMP_PACKET_SIZE_LARGE = 0 + RTMP_PACKET_SIZE_MEDIUM = 1 + RTMP_PACKET_SIZE_SMALL = 2 + RTMP_PACKET_SIZE_MINIMUM = 3 +) + +// packetSize defines valid packet sizes. +var packetSize = [...]int{12, 8, 4, 1} + +// packet defines an RTMP packet. +type packet struct { + headerType uint8 + packetType uint8 + channel int32 + hasAbsTimestamp bool + timestamp uint32 + info int32 + bodySize uint32 + bytesRead uint32 + chunk *chunk + header []byte + body []byte +} + +// chunk defines an RTMP packet chunk. +type chunk struct { + headerSize int32 + data []byte + header [RTMP_MAX_HEADER_SIZE]byte +} + +// ToDo: Consider making the following functions into methods. +// readPacket reads a packet. +func readPacket(s *Session, pkt *packet) error { + var hbuf [RTMP_MAX_HEADER_SIZE]byte + header := hbuf[:] + + err := readN(s, header[:1]) + if err != nil { + log.Println("readPacket: failed to read RTMP packet header!") + return err + } + pkt.headerType = (header[0] & 0xc0) >> 6 + pkt.channel = int32(header[0] & 0x3f) + header = header[1:] + + switch { + case pkt.channel == 0: + err = readN(s, header[:1]) + if err != nil { + log.Println("readPacket: failed to read rtmp packet header 2nd byte.") + return err + } + header = header[1:] + pkt.channel = int32(header[0]) + 64 + + case pkt.channel == 1: + err = readN(s, header[:2]) + if err != nil { + log.Println("readPacket: failed to read RTMP packet 3rd byte") + return err + } + header = header[2:] + pkt.channel = int32(binary.BigEndian.Uint16(header[:2])) + 64 + + } + + if pkt.channel >= s.channelsAllocatedIn { + n := pkt.channel + 10 + timestamp := append(s.channelTimestamp, make([]int32, 10)...) + + var pkts []*packet + if s.vecChannelsIn == nil { + pkts = make([]*packet, n) + } else { + pkts = append(s.vecChannelsIn[:pkt.channel:pkt.channel], make([]*packet, 10)...) + } + + s.channelTimestamp = timestamp + s.vecChannelsIn = pkts + + for i := int(s.channelsAllocatedIn); i < len(s.channelTimestamp); i++ { + s.channelTimestamp[i] = 0 + } + for i := int(s.channelsAllocatedIn); i < int(n); i++ { + s.vecChannelsIn[i] = nil + } + s.channelsAllocatedIn = n + } + + size := packetSize[pkt.headerType] + switch { + case size == RTMP_LARGE_HEADER_SIZE: + pkt.hasAbsTimestamp = true + case size < RTMP_LARGE_HEADER_SIZE: + if s.vecChannelsIn[pkt.channel] != nil { + *pkt = *(s.vecChannelsIn[pkt.channel]) + } + } + + size-- + + if size > 0 { + err = readN(s, header[:size]) + if err != nil { + log.Println("readPacket: failed to read rtmp packet heades.") + return err + } + } + + hSize := len(hbuf) - len(header) + size + + if size >= 3 { + pkt.timestamp = C_AMF_DecodeInt24(header[:3]) + + if size >= 6 { + pkt.bodySize = C_AMF_DecodeInt24(header[3:6]) + pkt.bytesRead = 0 + + if size > 6 { + pkt.packetType = header[6] + + if size == 11 { + pkt.info = decodeInt32LE(header[7:11]) + } + } + } + } + + extendedTimestamp := pkt.timestamp == 0xffffff + if extendedTimestamp { + err = readN(s, header[size:size+4]) + if err != nil { + log.Println("RTMPRead_Packet: Failed to read extended timestamp") + return err + } + // TODO: port this + pkt.timestamp = C_AMF_DecodeInt32(header[size : size+4]) + hSize += 4 + } + + if pkt.bodySize > 0 && pkt.body == nil { + resizePacket(pkt, pkt.bodySize, (hbuf[0]&0xc0)>>6) + } + + toRead := int32(pkt.bodySize - pkt.bytesRead) + chunkSize := s.inChunkSize + + if toRead < chunkSize { + chunkSize = toRead + } + + if pkt.chunk != nil { + pkt.chunk.headerSize = int32(hSize) + copy(pkt.chunk.header[:], hbuf[:hSize]) + pkt.chunk.data = pkt.body[pkt.bytesRead : pkt.bytesRead+uint32(chunkSize)] + } + + err = readN(s, pkt.body[pkt.bytesRead:][:chunkSize]) + if err != nil { + log.Println("readPacket: failed to read RTMP packet body") + return err + } + + pkt.bytesRead += uint32(chunkSize) + + // keep the packet as ref for other packets on this channel + if s.vecChannelsIn[pkt.channel] == nil { + s.vecChannelsIn[pkt.channel] = &packet{} + } + *(s.vecChannelsIn[pkt.channel]) = *pkt + + if extendedTimestamp { + s.vecChannelsIn[pkt.channel].timestamp = 0xffffff + } + + if pkt.bytesRead != pkt.bodySize { + panic("readPacket: bytesRead != bodySize") + } + + if !pkt.hasAbsTimestamp { + // timestamps seem to always be relative + pkt.timestamp += uint32(s.channelTimestamp[pkt.channel]) + } + s.channelTimestamp[pkt.channel] = int32(pkt.timestamp) + + s.vecChannelsIn[pkt.channel].body = nil + s.vecChannelsIn[pkt.channel].bytesRead = 0 + s.vecChannelsIn[pkt.channel].hasAbsTimestamp = false + return nil +} + +// resizePacket adjust the packet's storage to accommodate a body of the given size. +func resizePacket(pkt *packet, size uint32, ht uint8) { + buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) + pkt.headerType = ht + pkt.header = buf + pkt.body = buf[RTMP_MAX_HEADER_SIZE:] +} + +// sendPacket sends a packet. +func sendPacket(s *Session, pkt *packet, queue int) error { + var prevPkt *packet + var last int + + if pkt.channel >= s.channelsAllocatedOut { + n := int(pkt.channel + 10) + + var pkts []*packet + if s.vecChannelsOut == nil { + pkts = make([]*packet, n) + } else { + pkts = append(s.vecChannelsOut[:pkt.channel:pkt.channel], make([]*packet, 10)...) + } + s.vecChannelsOut = pkts + + for i := int(s.channelsAllocatedOut); i < n; i++ { + s.vecChannelsOut[i] = nil + } + + s.channelsAllocatedOut = int32(n) + } + prevPkt = s.vecChannelsOut[pkt.channel] + + if prevPkt != nil && pkt.headerType != RTMP_PACKET_SIZE_LARGE { + // compress a bit by using the prev packet's attributes + if prevPkt.bodySize == pkt.bodySize && prevPkt.packetType == pkt.packetType && pkt.headerType == RTMP_PACKET_SIZE_MEDIUM { + pkt.headerType = RTMP_PACKET_SIZE_SMALL + } + + if prevPkt.timestamp == pkt.timestamp && pkt.headerType == RTMP_PACKET_SIZE_SMALL { + pkt.headerType = RTMP_PACKET_SIZE_MINIMUM + } + + last = int(prevPkt.timestamp) + } + + if pkt.headerType > 3 { + log.Printf("Sanity failed! trying to send header of type: 0x%02x.", + pkt.headerType) + return errInvalidHeader + } + + var headBytes []byte + var origIdx int + if pkt.body != nil { + // Span from -packetsize for the type to the start of the body. + headBytes = pkt.header + origIdx = RTMP_MAX_HEADER_SIZE - packetSize[pkt.headerType] + } else { + // Allocate a new header and allow 6 bytes of movement backward. + var hbuf [RTMP_MAX_HEADER_SIZE]byte + headBytes = hbuf[:] + origIdx = 6 + } + + var cSize int + switch { + case pkt.channel > 319: + cSize = 2 + case pkt.channel > 63: + cSize = 1 + } + + hSize := packetSize[pkt.headerType] + if cSize != 0 { + origIdx -= cSize + hSize += cSize + } + + var ts uint32 + if prevPkt != nil { + ts = uint32(int(pkt.timestamp) - last) + } + if ts >= 0xffffff { + origIdx -= 4 + hSize += 4 + log.Printf("Larger timestamp than 24-bit: 0x%v", ts) + } + + headerIdx := origIdx + + c := pkt.headerType << 6 + switch cSize { + case 0: + c |= byte(pkt.channel) + case 1: + // Do nothing. + case 2: + c |= 1 + } + headBytes[headerIdx] = c + headerIdx++ + + if cSize != 0 { + tmp := pkt.channel - 64 + headBytes[headerIdx] = byte(tmp & 0xff) + headerIdx++ + + if cSize == 2 { + headBytes[headerIdx] = byte(tmp >> 8) + headerIdx++ + } + } + + if packetSize[pkt.headerType] > 1 { + res := ts + if ts > 0xffffff { + res = 0xffffff + } + C_AMF_EncodeInt24(headBytes[headerIdx:], int32(res)) + headerIdx += 3 // 24bits + } + + if packetSize[pkt.headerType] > 4 { + C_AMF_EncodeInt24(headBytes[headerIdx:], int32(pkt.bodySize)) + headerIdx += 3 // 24bits + headBytes[headerIdx] = pkt.packetType + headerIdx++ + } + + if packetSize[pkt.headerType] > 8 { + n := int(encodeInt32LE(headBytes[headerIdx:headerIdx+4], pkt.info)) + headerIdx += n + } + + if ts >= 0xffffff { + C_AMF_EncodeInt32(headBytes[headerIdx:], int32(ts)) + headerIdx += 4 // 32bits + } + + size := int(pkt.bodySize) + chunkSize := int(s.outChunkSize) + + if debugMode { + log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", s.link.conn.LocalAddr(), s.link.conn.RemoteAddr(), size) + } + + // Send the previously deferred packet if combining it with the next packet would exceed the chunk size. + if s.defered != nil && len(s.defered)+size+hSize > chunkSize { + err := writeN(s, s.defered) + if err != nil { + return err + } + s.defered = nil + } + + // TODO(kortschak): Rewrite this horrific peice of premature optimisation. + // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. + for size+hSize != 0 { + if s.defered == nil && pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { + s.defered = headBytes[origIdx:][:size+hSize] + break + } + if chunkSize > size { + chunkSize = size + } + bytes := headBytes[origIdx:][:chunkSize+hSize] + if s.defered != nil { + // Prepend the previously deferred packet and write it with the current one. + bytes = append(s.defered, bytes...) + } + err := writeN(s, bytes) + if err != nil { + return err + } + s.defered = nil + + size -= chunkSize + origIdx += chunkSize + hSize + hSize = 0 + + if size > 0 { + origIdx -= 1 + cSize + hSize = 1 + cSize + + if ts >= 0xffffff { + origIdx -= 4 + hSize += 4 + } + + headBytes[origIdx] = 0xc0 | c + + if cSize != 0 { + tmp := int(pkt.channel) - 64 + headBytes[origIdx+1] = byte(tmp) + + if cSize == 2 { + headBytes[origIdx+2] = byte(tmp >> 8) + } + } + if ts >= 0xffffff { + extendedTimestamp := headBytes[origIdx+1+cSize:] + C_AMF_EncodeInt32(extendedTimestamp[:4], int32(ts)) + } + } + } + + // We invoked a remote method + // TODO: port the const + if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { + buf := pkt.body[1:] + meth := C_AMF_DecodeString(buf) + + if debugMode { + log.Printf("invoking %v", meth) + } + // keep it in call queue till result arrives + if queue != 0 { + buf = buf[3+len(meth):] + txn := int32(C_AMF_DecodeNumber(buf[:8])) + s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) + } + } + + if s.vecChannelsOut[pkt.channel] == nil { + s.vecChannelsOut[pkt.channel] = &packet{} + } + *(s.vecChannelsOut[pkt.channel]) = *pkt + + return nil +} + +func decodeInt32LE(data []byte) int32 { + return int32(data[3])<<24 | int32(data[2])<<16 | int32(data[1])<<8 | int32(data[0]) +} + +func encodeInt32LE(dst []byte, v int32) int32 { + binary.LittleEndian.PutUint32(dst, uint32(v)) + return 4 +} diff --git a/rtmp/parseurl.go b/rtmp/parseurl.go index 8faa6414..97110eb8 100644 --- a/rtmp/parseurl.go +++ b/rtmp/parseurl.go @@ -40,9 +40,8 @@ import ( "strings" ) -// int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app); -// parseurl.c +33 -func C_RTMP_ParseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, err error) { +// parseURL parses an RTMP URL (ok, technically it is lexing). +func parseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, err error) { u, err := url.Parse(addr) if err != nil { log.Printf("failed to parse addr: %v", err) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index e85f1658..b9121c66 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -108,30 +108,23 @@ const ( av_videoFunction = "videoFunction" ) -var RTMPT_cmds = []string{ - "open", - "send", - "idle", - "close", +// RTMP protocol strings. +var rtmpProtocolStrings = [...]string{ + "rtmp", + "rtmpt", + "rtmpe", + "rtmpte", + "rtmps", + "rtmpts", + "", + "", + "rtmfp", } -var ( - packetSize = [...]int{12, 8, 4, 1} - RTMPProtocolStringsLower = [...]string{ - "rtmp", - "rtmpt", - "rtmpe", - "rtmpte", - "rtmps", - "rtmpts", - "", - "", - "rtmfp", - } -) - +// RTMP errors. var ( errUnknownScheme = errors.New("rtmp: unknown scheme") + errNotConnected = errors.New("rtmp: not connected") errHandshake = errors.New("rtmp: handshake failed") errConnSend = errors.New("rtmp: connection send error") errConnStream = errors.New("rtmp: connection stream error") @@ -143,37 +136,9 @@ var ( errCopying = errors.New("rtmp: copying error") ) -// RTMPPacket_IsReady(a) -// rtmp.h +142 -func C_RTMPPacket_IsReady(pkt *packet) bool { - return pkt.bytesRead == pkt.bodySize -} - -// uint32_t RTMP_GetTime(); -// rtmp.c +156 -func C_RTMP_GetTime() int32 { - return int32(time.Now().UnixNano() / 1000000) -} - -// void RTMP_EnableWrite(RTMP *r); -// rtmp.c +351 -func C_RTMP_EnableWrite(s *Session) { - s.link.protocol |= RTMP_FEATURE_WRITE -} - -// void RTMP_SetBufferMS(RTMP *r, int size); -// rtmp.c +381 -// DELETED - -// void SocksSetup(RTMP *r, C_AVal* sockshost); -// rtmp.c +410 -// DELETED - -// int RTMP_SetupURL(RTMP *r, char* url); -// rtmp.c +757 -// NOTE: code dealing with rtmp over http has been disregarded -func C_RTMP_SetupURL(s *Session, addr string) (err error) { - s.link.protocol, s.link.host, s.link.port, s.link.app, s.link.playpath, err = C_RTMP_ParseURL(addr) +// setupURL parses the RTMP URL. +func setupURL(s *Session, addr string) (err error) { + s.link.protocol, s.link.host, s.link.port, s.link.app, s.link.playpath, err = parseURL(addr) if err != nil { return err } @@ -181,7 +146,7 @@ func C_RTMP_SetupURL(s *Session, addr string) (err error) { if s.link.tcUrl == "" { if s.link.app != "" { s.link.tcUrl = fmt.Sprintf("%v://%v:%v/%v", - RTMPProtocolStringsLower[s.link.protocol], s.link.host, s.link.port, s.link.app) + rtmpProtocolStrings[s.link.protocol], s.link.host, s.link.port, s.link.app) s.link.lFlags |= RTMP_LF_FTCU } else { s.link.tcUrl = addr @@ -201,9 +166,8 @@ func C_RTMP_SetupURL(s *Session, addr string) (err error) { return nil } -// int RTMP_Connect(RTMP *r, RTMPPacket* cp); -// rtmp.c +1032 -func C_RTMP_Connect(s *Session, cp *packet) error { +// connect establishes an RTMP connection. +func connect(s *Session, cp *packet) error { addr, err := net.ResolveTCPAddr("tcp4", s.link.host+":"+strconv.Itoa(int(s.link.port))) if err != nil { return err @@ -215,30 +179,24 @@ func C_RTMP_Connect(s *Session, cp *packet) error { if debugMode { log.Println("... connected, handshaking...") } - err = C_HandShake(s, 1) + err = handshake(s, 1) if err != nil { - log.Println("C_RTMP_Connect1: handshake failed!") + log.Println("connect: handshake failed") return errHandshake } if debugMode { log.Println("... handshaked...") } - err = C_SendConnectPacket(s, cp) + err = sendConnectPacket(s, cp) if err != nil { - log.Println("RTMP connect failed!") + log.Println("connect: sendConnect failed") return errConnSend } return nil } -// int RTMP_Connect1(RTMP* r, RTMPPacket* cp); -// rtmp.c +978 -// DELETED - subsumed by RTMP_Connect - -// int RTMP_ConnectStream(RTMP* r, int seekTime); -// rtmp.c +1099 -// Side effects: s.isPlaying is set true upon successful connection -func C_RTMP_ConnectStream(s *Session, seekTime int32) error { +// connectStream reads a packet and handles it +func connectStream(s *Session, seekTime int32) error { var pkt packet if seekTime > 0 { @@ -246,27 +204,25 @@ func C_RTMP_ConnectStream(s *Session, seekTime int32) error { } for !s.isPlaying && s.isConnected() { - err := C_RTMP_ReadPacket(s, &pkt) + err := readPacket(s, &pkt) if err != nil { break } - // TODO: port is ready - if C_RTMPPacket_IsReady(&pkt) { - if pkt.bodySize == 0 { - continue - } - if pkt.packetType == RTMP_PACKET_TYPE_AUDIO || - pkt.packetType == RTMP_PACKET_TYPE_VIDEO || - pkt.packetType == RTMP_PACKET_TYPE_INFO { - log.Println("C_RTMP_ConnectStream: got packet before play()! Ignoring.") - pkt.body = nil - continue - } - - C_RTMP_ClientPacket(s, &pkt) - pkt.body = nil + if pkt.bodySize == 0 { + continue } + + if pkt.packetType == RTMP_PACKET_TYPE_AUDIO || + pkt.packetType == RTMP_PACKET_TYPE_VIDEO || + pkt.packetType == RTMP_PACKET_TYPE_INFO { + log.Println("connectStream: got packet before play()! Ignoring.") + pkt.body = nil + continue + } + + handlePacket(s, &pkt) + pkt.body = nil } if !s.isPlaying { @@ -275,35 +231,33 @@ func C_RTMP_ConnectStream(s *Session, seekTime int32) error { return nil } -// int RTMP_ClientPacket() -// rtmp.c +1226 -// NOTE cases have been commented out that are not currently used by AusOcean -func C_RTMP_ClientPacket(s *Session, pkt *packet) int32 { +// handlePacket handles a packet that the client has received. +// NB: cases have been commented out that are not currently used by AusOcean +func handlePacket(s *Session, pkt *packet) int32 { var hasMediaPacket int32 switch pkt.packetType { case RTMP_PACKET_TYPE_CHUNK_SIZE: - // TODO: port this - C_HandleChangeChunkSize(s, pkt) + if pkt.bodySize >= 4 { + s.inChunkSize = int32(C_AMF_DecodeInt32(pkt.body[:4])) + } case RTMP_PACKET_TYPE_BYTES_READ_REPORT: - // TODO: usue new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); + s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) case RTMP_PACKET_TYPE_CONTROL: panic("Unsupported packet type RTMP_PACKET_TYPE_CONTROL") - /* - log.Println("RTMP_PACKET_TYPE_CONTROL") - // TODO: port this - C.HandleCtrl(s, pkt) - */ + case RTMP_PACKET_TYPE_SERVER_BW: - // TODO: port this - C_HandlServerBW(s, pkt) + s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) case RTMP_PACKET_TYPE_CLIENT_BW: - // TODO: port this - C_HandleClientBW(s, pkt) + s.clientBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) + if pkt.bodySize > 4 { + s.clientBW2 = pkt.body[4] + } else { + s.clientBW2 = 0xff + } case RTMP_PACKET_TYPE_AUDIO: panic("Unsupported packet type RTMP_PACKET_TYPE_AUDIO") @@ -318,11 +272,10 @@ func C_RTMP_ClientPacket(s *Session, pkt *packet) int32 { panic("Unsupported packet type RTMP_PACKET_TYPE_INFO") case RTMP_PACKET_TYPE_INVOKE: - log.Println("RTMP_PACKET_TYPE_INVOKE:") - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,pkt.bodySize); - - err := C_HandleInvoke(s, pkt.body[:pkt.bodySize]) + if debugMode { + log.Println("RTMP_PACKET_TYPE_INVOKE:") + } + err := handleInvoke(s, pkt.body[:pkt.bodySize]) if err != nil { // This will never happen with the methods we implement. log.Println("HasMediaPacket") @@ -339,9 +292,7 @@ func C_RTMP_ClientPacket(s *Session, pkt *packet) int32 { return hasMediaPacket } -// int ReadN(RTMP* r, char* buffer, int n); -// rtmp.c +1390 -func C_ReadN(s *Session, buf []byte) error { +func readN(s *Session, buf []byte) error { err := s.link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { return err @@ -349,14 +300,14 @@ func C_ReadN(s *Session, buf []byte) error { n, err := io.ReadFull(s.link.conn, buf) if err != nil { if debugMode { - log.Printf("C_ReadN error: %v\n", err) + log.Printf("readN error: %v\n", err) } s.close() return err } s.nBytesIn += int32(n) if s.nBytesIn > (s.nBytesInSent + s.clientBW/10) { - err := C_SendBytesReceived(s) + err := sendBytesReceived(s) if err != nil { return err } @@ -364,9 +315,7 @@ func C_ReadN(s *Session, buf []byte) error { return nil } -// int WriteN(RTMP* r, const char* buffer, int n); -// rtmp.c +1502 -func C_WriteN(s *Session, buf []byte) error { +func writeN(s *Session, buf []byte) error { //ToDo: consider using a different timeout for writes than for reads err := s.link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { @@ -375,7 +324,7 @@ func C_WriteN(s *Session, buf []byte) error { _, err = s.link.conn.Write(buf) if err != nil { if debugMode { - log.Printf("C_WriteN, RTMP send error: %v\n", err) + log.Printf("writeN, RTMP send error: %v\n", err) } s.close() return err @@ -383,11 +332,9 @@ func C_WriteN(s *Session, buf []byte) error { return nil } -// int SendConnectPacket(RTMP* r, RTMPPacket* cp); -// rtmp.c +1579 -func C_SendConnectPacket(s *Session, cp *packet) error { +func sendConnectPacket(s *Session, cp *packet) error { if cp != nil { - return C_RTMP_SendPacket(s, cp, 1) + return sendPacket(s, cp, 1) } var pbuf [4096]byte @@ -508,12 +455,10 @@ func C_SendConnectPacket(s *Session, cp *packet) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 1) + return sendPacket(s, &pkt, 1) } -// int RTMP_SendCreateStream(RTMP* r); -// rtmp.c +1725 -func C_RTMP_SendCreateStream(s *Session) error { +func sendCreateStream(s *Session) error { var pbuf [256]byte pkt := packet{ channel: 0x03, /* control channel (invoke) */ @@ -541,12 +486,10 @@ func C_RTMP_SendCreateStream(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 1) + return sendPacket(s, &pkt, 1) } -// int SendReleaseStream(RTMP* r); -// rtmp.c +1816 -func C_SendReleaseStream(s *Session) error { +func sendReleaseStream(s *Session) error { var pbuf [1024]byte pkt := packet{ channel: 0x03, /* control channel (invoke) */ @@ -577,12 +520,10 @@ func C_SendReleaseStream(s *Session) error { } pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, 0) } -// int SendFCPublish(RTMP* r); -// rtmp.c +1846 -func C_SendFCPublish(s *Session) error { +func sendFCPublish(s *Session) error { var pbuf [1024]byte pkt := packet{ channel: 0x03, /* control channel (invoke) */ @@ -614,12 +555,10 @@ func C_SendFCPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, 0) } -// int SendFCUnpublish(RTMP *r); -// rtmp.c +1875 -func C_SendFCUnpublish(s *Session) error { +func sendFCUnpublish(s *Session) error { var pbuf [1024]byte pkt := packet{ channel: 0x03, /* control channel (invoke) */ @@ -651,12 +590,10 @@ func C_SendFCUnpublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, 0) } -// int SendPublish(RTMP* r); -// rtmp.c +1908 -func C_SendPublish(s *Session) error { +func sendPublish(s *Session) error { var pbuf [1024]byte pkt := packet{ channel: 0x04, /* source channel (invoke) */ @@ -692,13 +629,10 @@ func C_SendPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 1) + return sendPacket(s, &pkt, 1) } -// int -// SendDeleteStream(RTMP *r, double dStreamId) -// rtmp.c +1942 -func C_SendDeleteStream(s *Session, dStreamId float64) error { +func sendDeleteStream(s *Session, dStreamId float64) error { var pbuf [256]byte pkt := packet{ channel: 0x03, /* control channel (invoke) */ @@ -730,12 +664,11 @@ func C_SendDeleteStream(s *Session, dStreamId float64) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) /* no response expected */ - return C_RTMP_SendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, 0) } -// int SendBytesReceived(RTMP* r); -// rtmp.c +2080 -func C_SendBytesReceived(s *Session) error { +// sendBytesReceived tells the server how many bytes the client has received. +func sendBytesReceived(s *Session) error { var pbuf [256]byte pkt := packet{ channel: 0x02, /* control channel (invoke) */ @@ -756,12 +689,10 @@ func C_SendBytesReceived(s *Session) error { } pkt.bodySize = 4 - return C_RTMP_SendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, 0) } -// int SendCheckBW(RTMP* r); -// rtmp.c +2105 -func C_SendCheckBW(s *Session) error { +func sendCheckBW(s *Session) error { var pbuf [256]byte pkt := packet{ channel: 0x03, /* control channel (invoke) */ @@ -789,21 +720,18 @@ func C_SendCheckBW(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return C_RTMP_SendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, 0) } -// void AV_erase(method* vals, int* num, int i, int freeit); -// rtmp.c +2393 -func C_AV_erase(m []method, i int) []method { +func eraseMethod(m []method, i int) []method { copy(m[i:], m[i+1:]) m[len(m)-1] = method{} return m[:len(m)-1] } -// int HandleInvoke(RTMP* r, const char* body, unsigned int bodySize); -// rtmp.c +2912 +// int handleInvoke handles a packet invoke request // Side effects: s.isPlaying set to true upon av_NetStream_Publish_Start -func C_HandleInvoke(s *Session, body []byte) error { +func handleInvoke(s *Session, body []byte) error { if body[0] != 0x02 { return errInvalidBody } @@ -827,7 +755,7 @@ func C_HandleInvoke(s *Session, body []byte) error { for i, m := range s.methodCalls { if float64(m.num) == txn { methodInvoked = m.name - s.methodCalls = C_AV_erase(s.methodCalls, i) + s.methodCalls = eraseMethod(s.methodCalls, i) break } } @@ -847,13 +775,13 @@ func C_HandleInvoke(s *Session, body []byte) error { } if (s.link.protocol & RTMP_FEATURE_WRITE) != 0 { - C_SendReleaseStream(s) - C_SendFCPublish(s) + sendReleaseStream(s) + sendFCPublish(s) } else { panic("Link protocol has no RTMP_FEATURE_WRITE") } - C_RTMP_SendCreateStream(s) + sendCreateStream(s) if (s.link.protocol & RTMP_FEATURE_WRITE) == 0 { panic("Link protocol has no RTMP_FEATURE_WRITE") } @@ -862,7 +790,7 @@ func C_HandleInvoke(s *Session, body []byte) error { s.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) if s.link.protocol&RTMP_FEATURE_WRITE != 0 { - C_SendPublish(s) + sendPublish(s) } else { panic("Link protocol has no RTMP_FEATURE_WRITE") } @@ -874,7 +802,7 @@ func C_HandleInvoke(s *Session, body []byte) error { case av_onBWDone: if s.bwCheckCounter == 0 { - C_SendCheckBW(s) + sendCheckBW(s) } case av_onFCUnsubscribe, av_onFCSubscribe: @@ -915,7 +843,7 @@ func C_HandleInvoke(s *Session, body []byte) error { s.isPlaying = true for i, m := range s.methodCalls { if m.name == av_publish { - s.methodCalls = C_AV_erase(s.methodCalls, i) + s.methodCalls = eraseMethod(s.methodCalls, i) break } } @@ -942,226 +870,9 @@ leave: return nil } -// void HandleChangeChunkSize(RTMP* r, const RTMPPacket* packet); -// rtmp.c +3345 -func C_HandleChangeChunkSize(s *Session, pkt *packet) { - if pkt.bodySize >= 4 { - s.inChunkSize = int32(C_AMF_DecodeInt32(pkt.body[:4])) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, s.inChunkSize); - } -} - -// void HandleServerBW(RTMP* r, const RTMPPacket* packet); -// rtmp.c +3508 -func C_HandlServerBW(s *Session, pkt *packet) { - s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, s.serverBW); -} - -// void HandleClientBW(RTMP* r, const RTMPPacket* packet); -// rtmp.c +3515 -func C_HandleClientBW(s *Session, pkt *packet) { - s.clientBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) - //s.clientBW = int32(C.AMF_DecodeInt32((*byte)(unsafe.Pointer(pkt.body)))) - - if pkt.bodySize > 4 { - s.clientBW2 = pkt.body[4] - } else { - s.clientBW2 = 0xff - } - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, s.clientBW, - //s.clientBW2); -} - -// static int DecodeInt32LE(const char* data); -// rtmp.c +3527 -func C_DecodeInt32LE(data []byte) int32 { - return int32(data[3])<<24 | int32(data[2])<<16 | int32(data[1])<<8 | int32(data[0]) -} - -// int EncodeInt32LE(char* output, int nVal); -// rtmp.c +3537 -func C_EncodeInt32LE(dst []byte, v int32) int32 { - binary.LittleEndian.PutUint32(dst, uint32(v)) - return 4 -} - -// int RTMP_ReadPacket(RTMP* r, RTMPPacket* packet); -// rtmp.c +3550 -func C_RTMP_ReadPacket(s *Session, pkt *packet) error { - var hbuf [RTMP_MAX_HEADER_SIZE]byte - header := hbuf[:] - - err := C_ReadN(s, header[:1]) - if err != nil { - log.Println("C_RTMP_ReadPacket: failed to read RTMP packet header!") - return err - } - pkt.headerType = (header[0] & 0xc0) >> 6 - pkt.channel = int32(header[0] & 0x3f) - header = header[1:] - - switch { - case pkt.channel == 0: - err = C_ReadN(s, header[:1]) - if err != nil { - log.Println("C_RTMP_ReadPacket: failed to read rtmp packet header 2nd byte.") - return err - } - header = header[1:] - pkt.channel = int32(header[0]) + 64 - - case pkt.channel == 1: - err = C_ReadN(s, header[:2]) - if err != nil { - log.Println("C_RTMP_ReadPacket: failed to read RTMP packet 3rd byte") - return err - } - header = header[2:] - pkt.channel = int32(binary.BigEndian.Uint16(header[:2])) + 64 - - } - - if pkt.channel >= s.channelsAllocatedIn { - n := pkt.channel + 10 - timestamp := append(s.channelTimestamp, make([]int32, 10)...) - - var pkts []*packet - if s.vecChannelsIn == nil { - pkts = make([]*packet, n) - } else { - pkts = append(s.vecChannelsIn[:pkt.channel:pkt.channel], make([]*packet, 10)...) - } - - s.channelTimestamp = timestamp - s.vecChannelsIn = pkts - - for i := int(s.channelsAllocatedIn); i < len(s.channelTimestamp); i++ { - s.channelTimestamp[i] = 0 - } - for i := int(s.channelsAllocatedIn); i < int(n); i++ { - s.vecChannelsIn[i] = nil - } - s.channelsAllocatedIn = n - } - - size := packetSize[pkt.headerType] - switch { - case size == RTMP_LARGE_HEADER_SIZE: - pkt.hasAbsTimestamp = true - case size < RTMP_LARGE_HEADER_SIZE: - if s.vecChannelsIn[pkt.channel] != nil { - *pkt = *(s.vecChannelsIn[pkt.channel]) - } - } - - size-- - - if size > 0 { - err = C_ReadN(s, header[:size]) - if err != nil { - log.Println("C_RTMP_ReadPacket: failed to read rtmp packet heades.") - return err - } - } - - hSize := len(hbuf) - len(header) + size - - if size >= 3 { - pkt.timestamp = C_AMF_DecodeInt24(header[:3]) - - if size >= 6 { - pkt.bodySize = C_AMF_DecodeInt24(header[3:6]) - pkt.bytesRead = 0 - - if size > 6 { - pkt.packetType = header[6] - - if size == 11 { - pkt.info = C_DecodeInt32LE(header[7:11]) - } - } - } - } - - extendedTimestamp := pkt.timestamp == 0xffffff - if extendedTimestamp { - err = C_ReadN(s, header[size:size+4]) - if err != nil { - log.Println("RTMPRead_Packet: Failed to read extended timestamp") - return err - } - // TODO: port this - pkt.timestamp = C_AMF_DecodeInt32(header[size : size+4]) - hSize += 4 - } - - if pkt.bodySize > 0 && pkt.body == nil { - resizePacket(pkt, pkt.bodySize, (hbuf[0]&0xc0)>>6) - } - - toRead := int32(pkt.bodySize - pkt.bytesRead) - chunkSize := s.inChunkSize - - if toRead < chunkSize { - chunkSize = toRead - } - - if pkt.chunk != nil { - pkt.chunk.headerSize = int32(hSize) - copy(pkt.chunk.header[:], hbuf[:hSize]) - pkt.chunk.data = pkt.body[pkt.bytesRead : pkt.bytesRead+uint32(chunkSize)] - } - - err = C_ReadN(s, pkt.body[pkt.bytesRead:][:chunkSize]) - if err != nil { - log.Println("C_RTMP_ReadPacket: failed to read RTMP packet body") - return err - } - - pkt.bytesRead += uint32(chunkSize) - - // keep the packet as ref for other packets on this channel - if s.vecChannelsIn[pkt.channel] == nil { - s.vecChannelsIn[pkt.channel] = &packet{} - } - *(s.vecChannelsIn[pkt.channel]) = *pkt - - if extendedTimestamp { - s.vecChannelsIn[pkt.channel].timestamp = 0xffffff - } - - // TODO: port this - if C_RTMPPacket_IsReady(pkt) { - if !pkt.hasAbsTimestamp { - // timestamps seem to always be relative - pkt.timestamp += uint32(s.channelTimestamp[pkt.channel]) - } - s.channelTimestamp[pkt.channel] = int32(pkt.timestamp) - - s.vecChannelsIn[pkt.channel].body = nil - s.vecChannelsIn[pkt.channel].bytesRead = 0 - s.vecChannelsIn[pkt.channel].hasAbsTimestamp = false - } else { - pkt.body = nil /* so it won't be erased on free */ - } - return nil -} - -// resizePacket adjust the packet's storage to accommodate a body of the given size. -func resizePacket(pkt *packet, size uint32, ht uint8) { - buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) - pkt.headerType = ht - pkt.header = buf - pkt.body = buf[RTMP_MAX_HEADER_SIZE:] -} - // int HandShake(RTMP* r, int FP9HandShake); // rtmp.c +3744 -func C_HandShake(s *Session, FP9HandShake int32) error { +func handshake(s *Session, FP9HandShake int32) error { var clientbuf [RTMP_SIG_SIZE + 1]byte clientsig := clientbuf[1:] @@ -1169,32 +880,32 @@ func C_HandShake(s *Session, FP9HandShake int32) error { clientbuf[0] = 0x03 // not encrypted - binary.BigEndian.PutUint32(clientsig, uint32(C_RTMP_GetTime())) + binary.BigEndian.PutUint32(clientsig, uint32(time.Now().UnixNano()/1000000)) copy(clientsig[4:8], []byte{0, 0, 0, 0}) for i := 8; i < RTMP_SIG_SIZE; i++ { clientsig[i] = byte(rand.Intn(256)) } - err := C_WriteN(s, clientbuf[:]) + err := writeN(s, clientbuf[:]) if err != nil { return err } var typ [1]byte - err = C_ReadN(s, typ[:]) + err = readN(s, typ[:]) if err != nil { return err } if debugMode { - log.Printf("C_HandShake: Type answer: %v\n", typ[0]) + log.Printf("handshake: Type answer: %v\n", typ[0]) } if typ[0] != clientbuf[0] { - log.Printf("C_HandShake: type mismatch: client sent %v, server sent: %v\n", + log.Printf("handshake: type mismatch: client sent %v, server sent: %v\n", clientbuf[0], typ) } - err = C_ReadN(s, serversig[:]) + err = readN(s, serversig[:]) if err != nil { return err } @@ -1207,12 +918,12 @@ func C_HandShake(s *Session, FP9HandShake int32) error { // serversig[4], serversig[5], serversig[6], serversig[7]) // 2nd part of handshake - err = C_WriteN(s, serversig[:]) + err = writeN(s, serversig[:]) if err != nil { return err } - err = C_ReadN(s, serversig[:]) + err = readN(s, serversig[:]) if err != nil { return err } @@ -1224,233 +935,8 @@ func C_HandShake(s *Session, FP9HandShake int32) error { return nil } -// int RTMP_SendPacket(RTMP* r, RTMPPacket* packet, int queue); -// rtmp.c +3896 -func C_RTMP_SendPacket(s *Session, pkt *packet, queue int) error { - var prevPkt *packet - var last int - - if pkt.channel >= s.channelsAllocatedOut { - n := int(pkt.channel + 10) - - var pkts []*packet - if s.vecChannelsOut == nil { - pkts = make([]*packet, n) - } else { - pkts = append(s.vecChannelsOut[:pkt.channel:pkt.channel], make([]*packet, 10)...) - } - s.vecChannelsOut = pkts - - for i := int(s.channelsAllocatedOut); i < n; i++ { - s.vecChannelsOut[i] = nil - } - - s.channelsAllocatedOut = int32(n) - } - prevPkt = s.vecChannelsOut[pkt.channel] - - if prevPkt != nil && pkt.headerType != RTMP_PACKET_SIZE_LARGE { - // compress a bit by using the prev packet's attributes - if prevPkt.bodySize == pkt.bodySize && prevPkt.packetType == pkt.packetType && pkt.headerType == RTMP_PACKET_SIZE_MEDIUM { - pkt.headerType = RTMP_PACKET_SIZE_SMALL - } - - if prevPkt.timestamp == pkt.timestamp && pkt.headerType == RTMP_PACKET_SIZE_SMALL { - pkt.headerType = RTMP_PACKET_SIZE_MINIMUM - } - - last = int(prevPkt.timestamp) - } - - if pkt.headerType > 3 { - log.Printf("Sanity failed! trying to send header of type: 0x%02x.", - pkt.headerType) - return errInvalidHeader - } - - var headBytes []byte - var origIdx int - if pkt.body != nil { - // Span from -packetsize for the type to the start of the body. - headBytes = pkt.header - origIdx = RTMP_MAX_HEADER_SIZE - packetSize[pkt.headerType] - } else { - // Allocate a new header and allow 6 bytes of movement backward. - var hbuf [RTMP_MAX_HEADER_SIZE]byte - headBytes = hbuf[:] - origIdx = 6 - } - - var cSize int - switch { - case pkt.channel > 319: - cSize = 2 - case pkt.channel > 63: - cSize = 1 - } - - hSize := packetSize[pkt.headerType] - if cSize != 0 { - origIdx -= cSize - hSize += cSize - } - - var ts uint32 - if prevPkt != nil { - ts = uint32(int(pkt.timestamp) - last) - } - if ts >= 0xffffff { - origIdx -= 4 - hSize += 4 - log.Printf("Larger timestamp than 24-bit: 0x%v", ts) - } - - headerIdx := origIdx - - c := pkt.headerType << 6 - switch cSize { - case 0: - c |= byte(pkt.channel) - case 1: - // Do nothing. - case 2: - c |= 1 - } - headBytes[headerIdx] = c - headerIdx++ - - if cSize != 0 { - tmp := pkt.channel - 64 - headBytes[headerIdx] = byte(tmp & 0xff) - headerIdx++ - - if cSize == 2 { - headBytes[headerIdx] = byte(tmp >> 8) - headerIdx++ - } - } - - if packetSize[pkt.headerType] > 1 { - res := ts - if ts > 0xffffff { - res = 0xffffff - } - C_AMF_EncodeInt24(headBytes[headerIdx:], int32(res)) - headerIdx += 3 // 24bits - } - - if packetSize[pkt.headerType] > 4 { - C_AMF_EncodeInt24(headBytes[headerIdx:], int32(pkt.bodySize)) - headerIdx += 3 // 24bits - headBytes[headerIdx] = pkt.packetType - headerIdx++ - } - - if packetSize[pkt.headerType] > 8 { - n := int(C_EncodeInt32LE(headBytes[headerIdx:headerIdx+4], pkt.info)) - headerIdx += n - } - - if ts >= 0xffffff { - C_AMF_EncodeInt32(headBytes[headerIdx:], int32(ts)) - headerIdx += 4 // 32bits - } - - size := int(pkt.bodySize) - chunkSize := int(s.outChunkSize) - - if debugMode { - log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", s.link.conn.LocalAddr(), s.link.conn.RemoteAddr(), size) - } - - // Send the previously deferred packet if combining it with the next packet would exceed the chunk size. - if s.defered != nil && len(s.defered)+size+hSize > chunkSize { - err := C_WriteN(s, s.defered) - if err != nil { - return err - } - s.defered = nil - } - - // TODO(kortschak): Rewrite this horrific peice of premature optimisation. - // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. - for size+hSize != 0 { - if s.defered == nil && pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { - s.defered = headBytes[origIdx:][:size+hSize] - break - } - if chunkSize > size { - chunkSize = size - } - bytes := headBytes[origIdx:][:chunkSize+hSize] - if s.defered != nil { - // Prepend the previously deferred packet and write it with the current one. - bytes = append(s.defered, bytes...) - } - err := C_WriteN(s, bytes) - if err != nil { - return err - } - s.defered = nil - - size -= chunkSize - origIdx += chunkSize + hSize - hSize = 0 - - if size > 0 { - origIdx -= 1 + cSize - hSize = 1 + cSize - - if ts >= 0xffffff { - origIdx -= 4 - hSize += 4 - } - - headBytes[origIdx] = 0xc0 | c - - if cSize != 0 { - tmp := int(pkt.channel) - 64 - headBytes[origIdx+1] = byte(tmp) - - if cSize == 2 { - headBytes[origIdx+2] = byte(tmp >> 8) - } - } - if ts >= 0xffffff { - extendedTimestamp := headBytes[origIdx+1+cSize:] - C_AMF_EncodeInt32(extendedTimestamp[:4], int32(ts)) - } - } - } - - // We invoked a remote method - // TODO: port the const - if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { - buf := pkt.body[1:] - meth := C_AMF_DecodeString(buf) - - if debugMode { - log.Printf("invoking %v", meth) - } - // keep it in call queue till result arrives - if queue != 0 { - buf = buf[3+len(meth):] - txn := int32(C_AMF_DecodeNumber(buf[:8])) - s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) - } - } - - if s.vecChannelsOut[pkt.channel] == nil { - s.vecChannelsOut[pkt.channel] = &packet{} - } - *(s.vecChannelsOut[pkt.channel]) = *pkt - - return nil -} - -/// int RTMP_Write(RTMP* r, const char* buf, int size); -// rtmp.c +5136 -func C_RTMP_Write(s *Session, buf []byte) error { +// write prepares data to write then sends it. +func (s *Session) write(buf []byte) error { var pkt = &s.write var enc []byte size := len(buf) @@ -1511,7 +997,7 @@ func C_RTMP_Write(s *Session, buf []byte) error { pkt.bytesRead += uint32(num) buf = buf[num:] if pkt.bytesRead == pkt.bodySize { - err := C_RTMP_SendPacket(s, pkt, 0) + err := sendPacket(s, pkt, 0) pkt.body = nil pkt.bytesRead = 0 if err != nil { @@ -1525,21 +1011,3 @@ func C_RTMP_Write(s *Session, buf []byte) error { } return nil } - -var rtmpErrs = [...]string{ - 1: "rtmp: not connected", - 2: "rtmp: write error", - 3: "rtmp: not started", -} - -type Err uint - -func (e Err) Error() string { - if 0 <= int(e) && int(e) < len(rtmpErrs) { - s := rtmpErrs[e] - if s != "" { - return s - } - } - return "rtmp: " + strconv.Itoa(int(e)) -} diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 2260f01d..9ff28427 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -33,7 +33,9 @@ LICENSE */ package rtmp -import "net" +import ( + "net" +) const ( RTMPT_OPEN = iota @@ -42,28 +44,6 @@ const ( RTMPT_CLOSE ) -const ( - RTMP_PACKET_TYPE_CHUNK_SIZE = 0x01 - RTMP_PACKET_TYPE_BYTES_READ_REPORT = 0x03 - RTMP_PACKET_TYPE_CONTROL = 0x04 - RTMP_PACKET_TYPE_SERVER_BW = 0x05 - RTMP_PACKET_TYPE_CLIENT_BW = 0x06 - RTMP_PACKET_TYPE_AUDIO = 0x08 - RTMP_PACKET_TYPE_VIDEO = 0x09 - RTMP_PACKET_TYPE_FLEX_STREAM_SEND = 0x0F - RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT = 0x10 - RTMP_PACKET_TYPE_FLEX_MESSAGE = 0x11 - RTMP_PACKET_TYPE_INFO = 0x12 - RTMP_PACKET_TYPE_INVOKE = 0x14 - RTMP_PACKET_TYPE_FLASH_VIDEO = 0x16 -) - -const ( - RTMP_PACKET_SIZE_LARGE = 0 - RTMP_PACKET_SIZE_MEDIUM = 1 - RTMP_PACKET_SIZE_SMALL = 2 - RTMP_PACKET_SIZE_MINIMUM = 3 -) const ( RTMP_READ_HEADER = 0x01 RTMP_READ_RESUME = 0x02 @@ -114,26 +94,6 @@ const ( RTMP_MAX_HEADER_SIZE = 18 ) -type chunk struct { - headerSize int32 - data []byte - header [RTMP_MAX_HEADER_SIZE]byte -} - -type packet struct { - headerType uint8 - packetType uint8 - hasAbsTimestamp bool - channel int32 - timestamp uint32 - info int32 - bodySize uint32 - bytesRead uint32 - chunk *chunk - header []byte - body []byte -} - type link struct { host string playpath string diff --git a/rtmp/session.go b/rtmp/session.go index b6b066f4..869a9733 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -90,20 +90,20 @@ func (s *Session) Open() error { // start does the heavylifting for Open(). func (s *Session) start() error { s.init() - err := C_RTMP_SetupURL(s, s.url) + err := setupURL(s, s.url) if err != nil { s.close() return err } - C_RTMP_EnableWrite(s) - err = C_RTMP_Connect(s, nil) + s.enableWrite() + err = connect(s, nil) if err != nil { s.close() return err } - err = C_RTMP_ConnectStream(s, 0) + err = connectStream(s, 0) if err != nil { s.close() return err @@ -128,7 +128,7 @@ func (s *Session) init() { // Close terminates the rtmp connection, func (s *Session) Close() error { if !s.isConnected() { - return Err(3) + return errNotConnected } s.close() return nil @@ -140,9 +140,9 @@ func (s *Session) close() { if s.isConnected() { if s.streamID > 0 { if s.link.protocol&RTMP_FEATURE_WRITE != 0 { - C_SendFCUnpublish(s) + sendFCUnpublish(s) } - C_SendDeleteStream(s, float64(s.streamID)) + sendDeleteStream(s, float64(s.streamID)) } s.link.conn.Close() } @@ -152,12 +152,11 @@ func (s *Session) close() { // Write writes a frame (flv tag) to the rtmp connection. func (s *Session) Write(data []byte) (int, error) { if !s.isConnected() { - return 0, Err(1) + return 0, errNotConnected } - err := C_RTMP_Write(s, data) + err := s.write(data) if err != nil { - // TODO: propagate err - return 0, Err(2) + return 0, err } return len(data), nil } @@ -166,3 +165,8 @@ func (s *Session) Write(data []byte) (int, error) { func (s *Session) isConnected() bool { return s.link.conn != nil } + +// enableWrite enables the current session for writing. +func (s *Session) enableWrite() { + s.link.protocol |= RTMP_FEATURE_WRITE +} From 1a7048e9f32657fcb35190d8c4714b61a9b43db0 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 18:01:22 +1030 Subject: [PATCH 070/137] Updated copyright year. --- rtmp/amf_headers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtmp/amf_headers.go b/rtmp/amf_headers.go index 4fcc86f6..453b38de 100644 --- a/rtmp/amf_headers.go +++ b/rtmp/amf_headers.go @@ -9,7 +9,7 @@ AUTHORS Saxon Nelson-Milton LICENSE - amf_headers.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + amf_headers.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the From ad43420270e4f818b940640078187801aa8e1ace Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 19:31:54 +1030 Subject: [PATCH 071/137] Remove another unused file. --- rtmp/rtmp_sys.go | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 rtmp/rtmp_sys.go diff --git a/rtmp/rtmp_sys.go b/rtmp/rtmp_sys.go deleted file mode 100644 index 13fdc361..00000000 --- a/rtmp/rtmp_sys.go +++ /dev/null @@ -1,7 +0,0 @@ -package rtmp - -// #define SET_RCVTIMEO(tv,s) int tv = s*1000 -// rtmp_sys.h +43 -func SET_RCVTIMEO(tv *int32, s int32) { - *tv = s * 1000 -} From 8ed3c04186a2f95dbfa14875439a96b462d602ca Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 20:15:12 +1030 Subject: [PATCH 072/137] Change log messages to reflect new function names. --- rtmp/packet.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 310abed1..ebd79e11 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -189,7 +189,7 @@ func readPacket(s *Session, pkt *packet) error { if extendedTimestamp { err = readN(s, header[size:size+4]) if err != nil { - log.Println("RTMPRead_Packet: Failed to read extended timestamp") + log.Println("readPacket: Failed to read extended timestamp") return err } // TODO: port this @@ -391,7 +391,7 @@ func sendPacket(s *Session, pkt *packet, queue int) error { chunkSize := int(s.outChunkSize) if debugMode { - log.Printf("C_RTMP_SendPacket: %v->%v, size=%v", s.link.conn.LocalAddr(), s.link.conn.RemoteAddr(), size) + log.Printf("sendPacket: %v->%v, size=%v", s.link.conn.LocalAddr(), s.link.conn.RemoteAddr(), size) } // Send the previously deferred packet if combining it with the next packet would exceed the chunk size. From 0b869523b40c6833f6a46161c5844e38f277fdd8 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 20:16:11 +1030 Subject: [PATCH 073/137] Removed packet member from Session. --- rtmp/rtmp.go | 9 +++------ rtmp/session.go | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index b9121c66..eb2bb394 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -54,7 +54,6 @@ const ( ) const ( - // av_setDataFrame is a static const global in rtmp.c setDataFrame = "@setDataFrame" av__checkbw = "_checkbw" @@ -870,8 +869,6 @@ leave: return nil } -// int HandShake(RTMP* r, int FP9HandShake); -// rtmp.c +3744 func handshake(s *Session, FP9HandShake int32) error { var clientbuf [RTMP_SIG_SIZE + 1]byte clientsig := clientbuf[1:] @@ -937,7 +934,7 @@ func handshake(s *Session, FP9HandShake int32) error { // write prepares data to write then sends it. func (s *Session) write(buf []byte) error { - var pkt = &s.write + var pkt packet var enc []byte size := len(buf) var num int @@ -974,7 +971,7 @@ func (s *Session) write(buf []byte) error { pkt.bodySize += 16 } - resizePacket(pkt, pkt.bodySize, headerType) + resizePacket(&pkt, pkt.bodySize, headerType) enc = pkt.body[:pkt.bodySize] if pkt.packetType == RTMP_PACKET_TYPE_INFO { @@ -997,7 +994,7 @@ func (s *Session) write(buf []byte) error { pkt.bytesRead += uint32(num) buf = buf[num:] if pkt.bytesRead == pkt.bodySize { - err := sendPacket(s, pkt, 0) + err := sendPacket(s, &pkt, 0) pkt.body = nil pkt.bytesRead = 0 if err != nil { diff --git a/rtmp/session.go b/rtmp/session.go index 869a9733..5cdd8924 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -62,7 +62,6 @@ type Session struct { audioCodecs float64 videoCodecs float64 encoding float64 - write packet defered []byte link link } From 04679e47574a157718e24a4019ae8e09bf99d44a Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 21:45:00 +1030 Subject: [PATCH 074/137] Removed used fields and initalize packets lazily. --- rtmp/packet.go | 11 ++- rtmp/rtmp.go | 178 +++++++++++++++++-------------------------- rtmp/rtmp_headers.go | 1 - rtmp/session.go | 4 +- 4 files changed, 78 insertions(+), 116 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index ebd79e11..95efb658 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -62,6 +62,12 @@ const ( RTMP_PACKET_SIZE_MINIMUM = 3 ) +const ( + RTMP_CHANNEL_BYTES_READ = 0x02 + RTMP_CHANNEL_CONTROL = 0x03 + RTMP_CHANNEL_SOURCE = 0x04 +) + // packetSize defines valid packet sizes. var packetSize = [...]int{12, 8, 4, 1} @@ -257,7 +263,7 @@ func resizePacket(pkt *packet, size uint32, ht uint8) { } // sendPacket sends a packet. -func sendPacket(s *Session, pkt *packet, queue int) error { +func sendPacket(s *Session, pkt *packet, queue bool) error { var prevPkt *packet var last int @@ -455,7 +461,6 @@ func sendPacket(s *Session, pkt *packet, queue int) error { } // We invoked a remote method - // TODO: port the const if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { buf := pkt.body[1:] meth := C_AMF_DecodeString(buf) @@ -464,7 +469,7 @@ func sendPacket(s *Session, pkt *packet, queue int) error { log.Printf("invoking %v", meth) } // keep it in call queue till result arrives - if queue != 0 { + if queue { buf = buf[3+len(meth):] txn := int32(C_AMF_DecodeNumber(buf[:8])) s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index eb2bb394..b1515b6d 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -166,7 +166,7 @@ func setupURL(s *Session, addr string) (err error) { } // connect establishes an RTMP connection. -func connect(s *Session, cp *packet) error { +func connect(s *Session) error { addr, err := net.ResolveTCPAddr("tcp4", s.link.host+":"+strconv.Itoa(int(s.link.port))) if err != nil { return err @@ -178,7 +178,7 @@ func connect(s *Session, cp *packet) error { if debugMode { log.Println("... connected, handshaking...") } - err = handshake(s, 1) + err = handshake(s) if err != nil { log.Println("connect: handshake failed") return errHandshake @@ -186,7 +186,7 @@ func connect(s *Session, cp *packet) error { if debugMode { log.Println("... handshaked...") } - err = sendConnectPacket(s, cp) + err = sendConnectPacket(s) if err != nil { log.Println("connect: sendConnect failed") return errConnSend @@ -195,13 +195,9 @@ func connect(s *Session, cp *packet) error { } // connectStream reads a packet and handles it -func connectStream(s *Session, seekTime int32) error { +func connectStream(s *Session) error { var pkt packet - if seekTime > 0 { - s.link.seekTime = seekTime - } - for !s.isPlaying && s.isConnected() { err := readPacket(s, &pkt) if err != nil { @@ -331,21 +327,14 @@ func writeN(s *Session, buf []byte) error { return nil } -func sendConnectPacket(s *Session, cp *packet) error { - if cp != nil { - return sendPacket(s, cp, 1) - } - +func sendConnectPacket(s *Session) error { var pbuf [4096]byte pkt := packet{ - channel: 0x03, - headerType: RTMP_PACKET_SIZE_LARGE, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_LARGE, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -454,20 +443,17 @@ func sendConnectPacket(s *Session, cp *packet) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 1) + return sendPacket(s, &pkt, true) } func sendCreateStream(s *Session) error { var pbuf [256]byte pkt := packet{ - channel: 0x03, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -485,20 +471,17 @@ func sendCreateStream(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 1) + return sendPacket(s, &pkt, true) } func sendReleaseStream(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: 0x03, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -519,20 +502,17 @@ func sendReleaseStream(s *Session) error { } pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, false) } func sendFCPublish(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: 0x03, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -554,20 +534,17 @@ func sendFCPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, false) } func sendFCUnpublish(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: 0x03, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -589,20 +566,17 @@ func sendFCUnpublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, false) } func sendPublish(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: 0x04, /* source channel (invoke) */ - headerType: RTMP_PACKET_SIZE_LARGE, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: s.streamID, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_SOURCE, + headerType: RTMP_PACKET_SIZE_LARGE, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -628,20 +602,17 @@ func sendPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 1) + return sendPacket(s, &pkt, true) } func sendDeleteStream(s *Session, dStreamId float64) error { var pbuf [256]byte pkt := packet{ - channel: 0x03, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -663,21 +634,18 @@ func sendDeleteStream(s *Session, dStreamId float64) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) /* no response expected */ - return sendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, false) } // sendBytesReceived tells the server how many bytes the client has received. func sendBytesReceived(s *Session) error { var pbuf [256]byte pkt := packet{ - channel: 0x02, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_BYTES_READ_REPORT, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_BYTES_READ, + headerType: RTMP_PACKET_SIZE_MEDIUM, + packetType: RTMP_PACKET_TYPE_BYTES_READ_REPORT, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -688,20 +656,17 @@ func sendBytesReceived(s *Session) error { } pkt.bodySize = 4 - return sendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, false) } func sendCheckBW(s *Session) error { var pbuf [256]byte pkt := packet{ - channel: 0x03, /* control channel (invoke) */ - headerType: RTMP_PACKET_SIZE_LARGE, - packetType: RTMP_PACKET_TYPE_INVOKE, - timestamp: 0, - info: 0, - hasAbsTimestamp: false, - header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + channel: RTMP_CHANNEL_CONTROL, + headerType: RTMP_PACKET_SIZE_LARGE, + packetType: RTMP_PACKET_TYPE_INVOKE, + header: pbuf[:], + body: pbuf[RTMP_MAX_HEADER_SIZE:], } enc := pkt.body @@ -719,7 +684,7 @@ func sendCheckBW(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, 0) + return sendPacket(s, &pkt, false) } func eraseMethod(m []method, i int) []method { @@ -740,9 +705,6 @@ func handleInvoke(s *Session, body []byte) error { return errDecoding } - // NOTE we don't really need this ?? still functions without it - //C.AMF_Dump(&obj) - //C.AMFProp_GetString(C_AMF_GetProp(&obj, nil, 0), &method) meth := C_AMFProp_GetString(C_AMF_GetProp(&obj, "", 0)) txn := C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 1)) // TODO use new logger here @@ -865,18 +827,15 @@ func handleInvoke(s *Session, body []byte) error { } leave: C_AMF_Reset(&obj) - // None of the methods we implement will result in a true return. return nil } -func handshake(s *Session, FP9HandShake int32) error { +func handshake(s *Session) error { var clientbuf [RTMP_SIG_SIZE + 1]byte clientsig := clientbuf[1:] var serversig [RTMP_SIG_SIZE]byte - - clientbuf[0] = 0x03 // not encrypted - + clientbuf[0] = RTMP_CHANNEL_CONTROL binary.BigEndian.PutUint32(clientsig, uint32(time.Now().UnixNano()/1000000)) copy(clientsig[4:8], []byte{0, 0, 0, 0}) @@ -934,16 +893,15 @@ func handshake(s *Session, FP9HandShake int32) error { // write prepares data to write then sends it. func (s *Session) write(buf []byte) error { - var pkt packet + pkt := packet{ + channel: RTMP_CHANNEL_SOURCE, + info: s.streamID, + } var enc []byte - size := len(buf) - var num int - pkt.channel = 0x04 - pkt.info = s.streamID for len(buf) != 0 { if pkt.bytesRead == 0 { - if size < minDataSize { + if len(buf) < minDataSize { return errTinyPacket } @@ -985,7 +943,7 @@ func (s *Session) write(buf []byte) error { } else { enc = pkt.body[:pkt.bodySize][pkt.bytesRead:] } - num = int(pkt.bodySize - pkt.bytesRead) + num := int(pkt.bodySize - pkt.bytesRead) if num > len(buf) { num = len(buf) } @@ -994,7 +952,7 @@ func (s *Session) write(buf []byte) error { pkt.bytesRead += uint32(num) buf = buf[num:] if pkt.bytesRead == pkt.bodySize { - err := sendPacket(s, &pkt, 0) + err := sendPacket(s, &pkt, false) pkt.body = nil pkt.bytesRead = 0 if err != nil { diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index 9ff28427..becb48be 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -105,7 +105,6 @@ type link struct { flashVer string token string extras C_AMFObject - seekTime int32 lFlags int32 swfAge int32 protocol int32 diff --git a/rtmp/session.go b/rtmp/session.go index 5cdd8924..bac165e6 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -96,13 +96,13 @@ func (s *Session) start() error { } s.enableWrite() - err = connect(s, nil) + err = connect(s) if err != nil { s.close() return err } - err = connectStream(s, 0) + err = connectStream(s) if err != nil { s.close() return err From 1fe011b5fb5b3dc862046b154c9827b94455da99 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 21:58:19 +1030 Subject: [PATCH 075/137] Reinitialize *s in close(). --- rtmp/session.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtmp/session.go b/rtmp/session.go index bac165e6..6f059904 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -145,11 +145,11 @@ func (s *Session) close() { } s.link.conn.Close() } - s = &Session{} + *s = Session{} } // Write writes a frame (flv tag) to the rtmp connection. -func (s *Session) Write(data []byte) (int, error) { +//func (s *Session) Write(data []byte) (int, error) { if !s.isConnected() { return 0, errNotConnected } From 9b7feb4dfc5517a92c04af1e2fbfacad113f7ef1 Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 22:11:35 +1030 Subject: [PATCH 076/137] Fix typo. --- rtmp/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtmp/session.go b/rtmp/session.go index 6f059904..6d1fad40 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -149,7 +149,7 @@ func (s *Session) close() { } // Write writes a frame (flv tag) to the rtmp connection. -//func (s *Session) Write(data []byte) (int, error) { +func (s *Session) Write(data []byte) (int, error) { if !s.isConnected() { return 0, errNotConnected } From c4bc4f7c610cd82b593c17613c142d3e2651de2d Mon Sep 17 00:00:00 2001 From: Alan Noble Date: Mon, 7 Jan 2019 11:49:14 +0000 Subject: [PATCH 077/137] Set debugMode false by default. --- rtmp/rtmp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index b1515b6d..d73c2fc2 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -49,7 +49,7 @@ import ( const ( minDataSize = 11 - debugMode = true + debugMode = false length = 512 ) From cbe6149c8fa4d9a2618fda5aa2a535d43a0f58eb Mon Sep 17 00:00:00 2001 From: scruzin Date: Mon, 7 Jan 2019 23:59:41 +1030 Subject: [PATCH 078/137] Added proper logging. --- revid/senders.go | 4 +- rtmp/packet.go | 51 ++++++++------------ rtmp/rtmp.go | 121 ++++++++++++++++++----------------------------- rtmp/session.go | 26 ++++++---- 4 files changed, 86 insertions(+), 116 deletions(-) diff --git a/revid/senders.go b/revid/senders.go index 9c9abca1..f09d34fa 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -264,7 +264,7 @@ func newRtmpSender(url string, timeout uint, retries int, log func(lvl int8, msg var sess *rtmp.Session var err error for n := 0; n < retries; n++ { - sess = rtmp.NewSession(url, timeout) + sess = rtmp.NewSession(url, timeout, log) err = sess.Open() if err == nil { break @@ -310,7 +310,7 @@ func (s *rtmpSender) restart() error { return err } for n := 0; n < s.retries; n++ { - s.sess = rtmp.NewSession(s.url, s.timeout) + s.sess = rtmp.NewSession(s.url, s.timeout, s.log) err = s.sess.Open() if err == nil { break diff --git a/rtmp/packet.go b/rtmp/packet.go index 95efb658..d97d99ea 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -36,7 +36,6 @@ package rtmp import ( "encoding/binary" - "log" ) const ( @@ -101,7 +100,7 @@ func readPacket(s *Session, pkt *packet) error { err := readN(s, header[:1]) if err != nil { - log.Println("readPacket: failed to read RTMP packet header!") + s.log(DebugLevel, pkg+"failed to read packet header 1st byte", "error", err.Error()) return err } pkt.headerType = (header[0] & 0xc0) >> 6 @@ -112,7 +111,7 @@ func readPacket(s *Session, pkt *packet) error { case pkt.channel == 0: err = readN(s, header[:1]) if err != nil { - log.Println("readPacket: failed to read rtmp packet header 2nd byte.") + s.log(DebugLevel, pkg+"failed to read packet header 2nd byte", "error", err.Error()) return err } header = header[1:] @@ -121,12 +120,11 @@ func readPacket(s *Session, pkt *packet) error { case pkt.channel == 1: err = readN(s, header[:2]) if err != nil { - log.Println("readPacket: failed to read RTMP packet 3rd byte") + s.log(DebugLevel, pkg+"failed to read packet header 3rd byte", "error", err.Error()) return err } header = header[2:] pkt.channel = int32(binary.BigEndian.Uint16(header[:2])) + 64 - } if pkt.channel >= s.channelsAllocatedIn { @@ -161,22 +159,19 @@ func readPacket(s *Session, pkt *packet) error { *pkt = *(s.vecChannelsIn[pkt.channel]) } } - size-- if size > 0 { err = readN(s, header[:size]) if err != nil { - log.Println("readPacket: failed to read rtmp packet heades.") + s.log(DebugLevel, pkg+"failed to read packet header", "error", err.Error()) return err } } - hSize := len(hbuf) - len(header) + size if size >= 3 { pkt.timestamp = C_AMF_DecodeInt24(header[:3]) - if size >= 6 { pkt.bodySize = C_AMF_DecodeInt24(header[3:6]) pkt.bytesRead = 0 @@ -195,7 +190,7 @@ func readPacket(s *Session, pkt *packet) error { if extendedTimestamp { err = readN(s, header[size:size+4]) if err != nil { - log.Println("readPacket: Failed to read extended timestamp") + s.log(DebugLevel, pkg+"failed to read extended timestamp", "error", err.Error()) return err } // TODO: port this @@ -222,7 +217,7 @@ func readPacket(s *Session, pkt *packet) error { err = readN(s, pkt.body[pkt.bytesRead:][:chunkSize]) if err != nil { - log.Println("readPacket: failed to read RTMP packet body") + s.log(DebugLevel, pkg+"failed to read packet body", "error", err.Error()) return err } @@ -254,7 +249,7 @@ func readPacket(s *Session, pkt *packet) error { return nil } -// resizePacket adjust the packet's storage to accommodate a body of the given size. +// resizePacket adjusts the packet's storage to accommodate a body of the given size. func resizePacket(pkt *packet, size uint32, ht uint8) { buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) pkt.headerType = ht @@ -300,8 +295,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { } if pkt.headerType > 3 { - log.Printf("Sanity failed! trying to send header of type: 0x%02x.", - pkt.headerType) + s.log(WarnLevel, pkg+"unexpected header type", "type", pkt.headerType) return errInvalidHeader } @@ -339,7 +333,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { if ts >= 0xffffff { origIdx -= 4 hSize += 4 - log.Printf("Larger timestamp than 24-bit: 0x%v", ts) + s.log(DebugLevel, pkg+"larger timestamp than 24 bits", "timestamp", ts) } headerIdx := origIdx @@ -396,39 +390,37 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { size := int(pkt.bodySize) chunkSize := int(s.outChunkSize) - if debugMode { - log.Printf("sendPacket: %v->%v, size=%v", s.link.conn.LocalAddr(), s.link.conn.RemoteAddr(), size) - } + s.log(DebugLevel, pkg+"sending packet", "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr(), "size", size) - // Send the previously deferred packet if combining it with the next packet would exceed the chunk size. - if s.defered != nil && len(s.defered)+size+hSize > chunkSize { - err := writeN(s, s.defered) + if s.deferred != nil && len(s.deferred)+size+hSize > chunkSize { + err := writeN(s, s.deferred) if err != nil { return err } - s.defered = nil + s.deferred = nil } // TODO(kortschak): Rewrite this horrific peice of premature optimisation. // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. for size+hSize != 0 { - if s.defered == nil && pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { - s.defered = headBytes[origIdx:][:size+hSize] + if s.deferred == nil && pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { + s.deferred = headBytes[origIdx:][:size+hSize] + s.log(DebugLevel, pkg+"deferred sending packet") break } if chunkSize > size { chunkSize = size } bytes := headBytes[origIdx:][:chunkSize+hSize] - if s.defered != nil { + if s.deferred != nil { // Prepend the previously deferred packet and write it with the current one. - bytes = append(s.defered, bytes...) + bytes = append(s.deferred, bytes...) } err := writeN(s, bytes) if err != nil { return err } - s.defered = nil + s.deferred = nil size -= chunkSize origIdx += chunkSize + hSize @@ -464,10 +456,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { buf := pkt.body[1:] meth := C_AMF_DecodeString(buf) - - if debugMode { - log.Printf("invoking %v", meth) - } + s.log(DebugLevel, "invoking method", "method", meth) // keep it in call queue till result arrives if queue { buf = buf[3+len(meth):] diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index d73c2fc2..2574a785 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -38,9 +38,7 @@ import ( "bytes" "encoding/binary" "errors" - "fmt" "io" - "log" "math/rand" "net" "strconv" @@ -48,9 +46,8 @@ import ( ) const ( + pkg = "rtmp:" minDataSize = 11 - debugMode = false - length = 512 ) const ( @@ -123,6 +120,7 @@ var rtmpProtocolStrings = [...]string{ // RTMP errors. var ( errUnknownScheme = errors.New("rtmp: unknown scheme") + errConnected = errors.New("rtmp: already connected") errNotConnected = errors.New("rtmp: not connected") errHandshake = errors.New("rtmp: handshake failed") errConnSend = errors.New("rtmp: connection send error") @@ -144,8 +142,7 @@ func setupURL(s *Session, addr string) (err error) { if s.link.tcUrl == "" { if s.link.app != "" { - s.link.tcUrl = fmt.Sprintf("%v://%v:%v/%v", - rtmpProtocolStrings[s.link.protocol], s.link.host, s.link.port, s.link.app) + s.link.tcUrl = rtmpProtocolStrings[s.link.protocol] + "://" + s.link.host + ":" + strconv.Itoa(int(s.link.port)) + "/" + s.link.app s.link.lFlags |= RTMP_LF_FTCU } else { s.link.tcUrl = addr @@ -156,6 +153,7 @@ func setupURL(s *Session, addr string) (err error) { switch { case (s.link.protocol & RTMP_FEATURE_SSL) != 0: s.link.port = 433 + s.log(FatalLevel, pkg+"SSL not supported") case (s.link.protocol & RTMP_FEATURE_HTTP) != 0: s.link.port = 80 default: @@ -173,22 +171,19 @@ func connect(s *Session) error { } s.link.conn, err = net.DialTCP("tcp4", nil, addr) if err != nil { + s.log(WarnLevel, pkg+"dial failed", "error", err.Error()) return err } - if debugMode { - log.Println("... connected, handshaking...") - } + s.log(DebugLevel, pkg+"connected") err = handshake(s) if err != nil { - log.Println("connect: handshake failed") + s.log(WarnLevel, pkg+"handshake failed", "error", err.Error()) return errHandshake } - if debugMode { - log.Println("... handshaked...") - } + s.log(DebugLevel, pkg+"handshaked") err = sendConnectPacket(s) if err != nil { - log.Println("connect: sendConnect failed") + s.log(WarnLevel, pkg+"sendConnect failed", "error", err.Error()) return errConnSend } return nil @@ -211,7 +206,7 @@ func connectStream(s *Session) error { if pkt.packetType == RTMP_PACKET_TYPE_AUDIO || pkt.packetType == RTMP_PACKET_TYPE_VIDEO || pkt.packetType == RTMP_PACKET_TYPE_INFO { - log.Println("connectStream: got packet before play()! Ignoring.") + s.log(DebugLevel, pkg+"got packet before play; ignoring") pkt.body = nil continue } @@ -241,7 +236,7 @@ func handlePacket(s *Session, pkt *packet) int32 { s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) case RTMP_PACKET_TYPE_CONTROL: - panic("Unsupported packet type RTMP_PACKET_TYPE_CONTROL") + s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_CONTROL") case RTMP_PACKET_TYPE_SERVER_BW: s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) @@ -255,34 +250,30 @@ func handlePacket(s *Session, pkt *packet) int32 { } case RTMP_PACKET_TYPE_AUDIO: - panic("Unsupported packet type RTMP_PACKET_TYPE_AUDIO") + s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_AUDIO") case RTMP_PACKET_TYPE_VIDEO: - panic("Unsupported packet type RTMP_PACKET_TYPE_VIDEO") + s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_VIDEO") case RTMP_PACKET_TYPE_FLEX_MESSAGE: - panic("Unsupported packet type RTMP_PACKET_TYPE_FLEX_MESSAGE") + s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_FLEX_MESSAGE") case RTMP_PACKET_TYPE_INFO: - panic("Unsupported packet type RTMP_PACKET_TYPE_INFO") + s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_INFO") case RTMP_PACKET_TYPE_INVOKE: - if debugMode { - log.Println("RTMP_PACKET_TYPE_INVOKE:") - } err := handleInvoke(s, pkt.body[:pkt.bodySize]) if err != nil { // This will never happen with the methods we implement. - log.Println("HasMediaPacket") + s.log(WarnLevel, pkg+"unexpected error from handleInvoke", "error", err.Error()) hasMediaPacket = 2 } case RTMP_PACKET_TYPE_FLASH_VIDEO: - panic("Unsupported packet type RTMP_PACKET_TYPE_FLASH_VIDEO") + s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_FLASH_VIDEO") default: - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,pkt.packetType); + s.log(WarnLevel, pkg+"unknown packet type", "type", pkt.packetType) } return hasMediaPacket } @@ -294,9 +285,7 @@ func readN(s *Session, buf []byte) error { } n, err := io.ReadFull(s.link.conn, buf) if err != nil { - if debugMode { - log.Printf("readN error: %v\n", err) - } + s.log(WarnLevel, pkg+"read failed", "error", err.Error()) s.close() return err } @@ -318,9 +307,7 @@ func writeN(s *Session, buf []byte) error { } _, err = s.link.conn.Write(buf) if err != nil { - if debugMode { - log.Printf("writeN, RTMP send error: %v\n", err) - } + s.log(WarnLevel, pkg+"write failed", "error", err.Error()) s.close() return err } @@ -706,9 +693,8 @@ func handleInvoke(s *Session, body []byte) error { } meth := C_AMFProp_GetString(C_AMF_GetProp(&obj, "", 0)) + s.log(DebugLevel, pkg+"invoking", "method", meth) txn := C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 1)) - // TODO use new logger here - // RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); switch meth { case av__result: @@ -721,30 +707,27 @@ func handleInvoke(s *Session, body []byte) error { } } if methodInvoked == "" { - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", - //__FUNCTION__, txn); + s.log(WarnLevel, pkg+"received result without matching request", "id", txn) goto leave } - // TODO use new logger here - //RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, - //methodInvoked.av_val); + s.log(DebugLevel, pkg+"received result for method", "id", txn) + switch methodInvoked { case av_connect: if s.link.token != "" { - panic("No support for link token") + s.log(FatalLevel, "no support for link token") } if (s.link.protocol & RTMP_FEATURE_WRITE) != 0 { sendReleaseStream(s) sendFCPublish(s) } else { - panic("Link protocol has no RTMP_FEATURE_WRITE") + s.log(FatalLevel, "link protocol has no RTMP_FEATURE_WRITE") } sendCreateStream(s) if (s.link.protocol & RTMP_FEATURE_WRITE) == 0 { - panic("Link protocol has no RTMP_FEATURE_WRITE") + s.log(FatalLevel, "link protocol has no RTMP_FEATURE_WRITE") } case av_createStream: @@ -753,13 +736,12 @@ func handleInvoke(s *Session, body []byte) error { if s.link.protocol&RTMP_FEATURE_WRITE != 0 { sendPublish(s) } else { - panic("Link protocol has no RTMP_FEATURE_WRITE") + s.log(FatalLevel, "link protocol has no RTMP_FEATURE_WRITE") } case av_play, av_publish: - panic("Unsupported method av_play/av_publish") + s.log(FatalLevel, "unsupported method av_play/av_publish") } - //C.free(unsafe.Pointer(methodInvoked.av_val)) case av_onBWDone: if s.bwCheckCounter == 0 { @@ -767,38 +749,34 @@ func handleInvoke(s *Session, body []byte) error { } case av_onFCUnsubscribe, av_onFCSubscribe: - panic("Unsupported method av_onFCUnsubscribe/av_onFCSubscribe") + s.log(FatalLevel, "unsupported method av_onFCUnsubscribe/av_onFCSubscribe") case av_ping: - panic("Unsupported method av_ping") + s.log(FatalLevel, "unsupported method av_ping") case av__onbwcheck: - panic("Unsupported method av_onbwcheck") + s.log(FatalLevel, "unsupported method av_onbwcheck") case av__onbwdone: - panic("Unsupported method av_onbwdone") + s.log(FatalLevel, "unsupported method av_onbwdone") case av_close: - panic("Unsupported method av_close") + s.log(FatalLevel, "unsupported method av_close") case av_onStatus: var obj2 C_AMFObject C_AMFProp_GetObject(C_AMF_GetProp(&obj, "", 3), &obj2) code := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_code, -1)) - level := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_level, -1)) // Not used. - _ = level - - // TODO use new logger - // RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); + s.log(DebugLevel, pkg+"onStatus", "code", code, "level", level) switch code { case av_NetStream_Failed, av_NetStream_Play_Failed, av_NetStream_Play_StreamNotFound, av_NetConnection_Connect_InvalidApp: - panic("Unsupported method av_NetStream/av_NetStream_Play_Failed/av_netSTream_Play_StreamNotFound/av_netConnection_Connect_invalidApp") + s.log(FatalLevel, "unsupported method av_NetStream/av_NetStream_Play_Failed/av_netSTream_Play_StreamNotFound/av_netConnection_Connect_invalidApp") case av_NetStream_Play_Start, av_NetStream_Play_PublishNotify: - panic("Unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") + s.log(FatalLevel, "unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") case av_NetStream_Publish_Start: s.isPlaying = true @@ -810,20 +788,20 @@ func handleInvoke(s *Session, body []byte) error { } case av_NetStream_Play_Complete, av_NetStream_Play_Stop, av_NetStream_Play_UnpublishNotify: - panic("Unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") + s.log(FatalLevel, "unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") case av_NetStream_Seek_Notify: - panic("Unsupported method av_netStream_Seek_Notify") + s.log(FatalLevel, "unsupported method av_netStream_Seek_Notify") case av_NetStream_Pause_Notify: - panic("Unsupported method av_NetStream_Pause_Notify") + s.log(FatalLevel, "unsupported method av_NetStream_Pause_Notify") } case av_playlist_ready: - panic("Unsupported method av_playlist_ready") + s.log(FatalLevel, "unsupported method av_playlist_ready") default: - panic(fmt.Sprintf("unknown method: %q", meth)) + s.log(FatalLevel, "unknown method", "method", meth) } leave: C_AMF_Reset(&obj) @@ -854,12 +832,9 @@ func handshake(s *Session) error { return err } - if debugMode { - log.Printf("handshake: Type answer: %v\n", typ[0]) - } + s.log(DebugLevel, pkg+"handshake", "received", typ[0]) if typ[0] != clientbuf[0] { - log.Printf("handshake: type mismatch: client sent %v, server sent: %v\n", - clientbuf[0], typ) + s.log(WarnLevel, pkg+"handshake type mismatch", "sent", clientbuf[0], "received", typ) } err = readN(s, serversig[:]) if err != nil { @@ -868,10 +843,7 @@ func handshake(s *Session) error { // decode server response suptime := binary.BigEndian.Uint32(serversig[:4]) - _ = suptime - // RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime) - // RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__, - // serversig[4], serversig[5], serversig[6], serversig[7]) + s.log(DebugLevel, pkg+"server uptime", "uptime", suptime) // 2nd part of handshake err = writeN(s, serversig[:]) @@ -885,8 +857,7 @@ func handshake(s *Session) error { } if !bytes.Equal(serversig[:RTMP_SIG_SIZE], clientbuf[1:RTMP_SIG_SIZE+1]) { - log.Printf("Client signature does not match: %q != %q", - serversig[:RTMP_SIG_SIZE], clientbuf[1:RTMP_SIG_SIZE+1]) + s.log(WarnLevel, pkg+"signature mismatch", "serversig", serversig[:RTMP_SIG_SIZE], "clientsig", clientbuf[1:RTMP_SIG_SIZE+1]) } return nil } diff --git a/rtmp/session.go b/rtmp/session.go index 6d1fad40..a04f39d5 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -33,10 +33,6 @@ LICENSE */ package rtmp -import ( - "errors" -) - // Session holds the state for an RTMP session. type Session struct { url string @@ -62,22 +58,36 @@ type Session struct { audioCodecs float64 videoCodecs float64 encoding float64 - defered []byte + deferred []byte link link + log Log } +// Log defines the RTMP logging function. +type Log func(level int8, message string, params ...interface{}) + +// Log levels used by Log. +const ( + DebugLevel int8 = -1 + InfoLevel int8 = 0 + WarnLevel int8 = 1 + ErrorLevel int8 = 2 + FatalLevel int8 = 5 +) + // NewSession returns a new Session. -func NewSession(url string, connectTimeout uint) *Session { +func NewSession(url string, timeout uint, log Log) *Session { return &Session{ url: url, - timeout: connectTimeout, + timeout: timeout, + log: log, } } // Open establishes an rtmp connection with the url passed into the constructor. func (s *Session) Open() error { if s.isConnected() { - return errors.New("rtmp: attempt to start already running session") + return errConnected } err := s.start() if err != nil { From 872d6899576c07fb0054ba36047efaa0ff5415e1 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 08:42:30 +1030 Subject: [PATCH 079/137] psi: PatTableID=>patID and PmtTableID=>pmtID i.e. also made unexported --- stream/mts/psi/psi.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 821ea341..3ad8ab93 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -38,8 +38,8 @@ const ( // Table Type IDs const ( - PATTableID = 0x00 - PMTTableID = 0x02 + patID = 0x00 + pmtID = 0x02 ) // Consts relating to time description @@ -146,9 +146,9 @@ func readTSS(data []byte, p *PSI) *TSS { tss.Lsn = data[pos] pos++ switch p.Tid { - case PATTableID: + case patID: tss.Sd = readPAT(data[pos:], &tss) - case PMTTableID: + case pmtID: tss.Sd = readPMT(data[pos:], &tss) default: panic("Can't yet deal with tables that are not PAT or PMT") From ebd6d5816d8cbfa31548d7fe41cb6db3ac93adfe Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 19:39:22 +1030 Subject: [PATCH 080/137] psi: full stop at end of comment, made lastTime belong to packer and moved some vars to a more appropriate location --- revid/revid.go | 12 ++++-------- revid/senders.go | 5 +++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/revid/revid.go b/revid/revid.go index 0c989ba8..ab9c4999 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -128,15 +128,11 @@ type Revid struct { // packer takes data segments and packs them into clips // of the number frames specified in the owners config. type packer struct { - owner *Revid - + owner *Revid + lastTime time.Time packetCount uint } -// lastTime is used in the packer's Write method to determine how much time has -// passed since last write to packer -var lastTime time.Time - // Write implements the io.Writer interface. // // Unless the ring buffer returns an error, all writes @@ -158,10 +154,10 @@ func (p *packer) Write(frame []byte) (int, error) { } p.packetCount++ now := time.Now() - if (p.owner.config.Output1 != Rtmp && now.Sub(lastTime) > clipDuration && p.packetCount%7 == 0) || p.owner.config.Output1 == Rtmp { + if (p.owner.config.Output1 != Rtmp && now.Sub(p.lastTime) > clipDuration && p.packetCount%7 == 0) || p.owner.config.Output1 == Rtmp { p.owner.buffer.Flush() p.packetCount = 0 - lastTime = now + p.lastTime = now } return len(frame), nil } diff --git a/revid/senders.go b/revid/senders.go index 523bd61e..5c64e982 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -144,11 +144,12 @@ func (s *httpSender) send() error { break } } - var err error - var reply string + if !send { return nil } + var err error + var reply string reply, _, err = s.client.Send(netsender.RequestRecv, pins) if err != nil { return err From b75dfaa4de7e5619982de8ca546f9e59d610c4e4 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 19:42:46 +1030 Subject: [PATCH 081/137] mts: added full stops to some comments --- stream/mts/encoder.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index ce84bd16..8e43242f 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -38,7 +38,7 @@ import ( // Some common manifestations of PSI var ( - // PSI struct to represent basic pat + // PSI struct to represent basic pat. StandardPat = psi.PSI{ Pf: 0x00, Tid: 0x00, @@ -58,7 +58,7 @@ var ( }, } - // PSI struct to represent basic pmt without descriptors for time and location + // PSI struct to represent basic pmt without descriptors for time and location. StandardPmt = psi.PSI{ Pf: 0x00, Tid: 0x02, @@ -82,7 +82,7 @@ var ( }, } - // Std pmt with time and location descriptors, time and location fields are zeroed out + // Std pmt with time and location descriptors, time and location fields are zeroed out. StandardPmtTimeLocation = psi.PSI{ Pf: 0x00, Tid: 0x02, @@ -155,7 +155,7 @@ const ( // Time related constants. const ( // ptsOffset is the offset added to the clock to determine - // the current presentation timestamp, + // the current presentation timestamp. ptsOffset = 700 * time.Millisecond // pcrFreq is the base Program Clock Reference frequency. @@ -202,7 +202,7 @@ const ( ) // generate handles the incoming data and generates equivalent mpegts packets - -// sending them to the output channel +// sending them to the output channel. func (e *Encoder) Encode(nalu []byte) error { // Prepare PES data. @@ -255,7 +255,7 @@ func (e *Encoder) Encode(nalu []byte) error { // writePSI creates mpegts with pat and pmt tables - with pmt table having updated // location and time data. func (e *Encoder) writePSI() error { - // Write PAT + // Write PAT. patPkt := Packet{ PUSI: true, PID: patPid, @@ -268,7 +268,7 @@ func (e *Encoder) writePSI() error { return err } - // Update pmt table time and location + // Update pmt table time and location. err = psi.UpdateTime(pmtTable, metaData.Time) if err != nil { return err @@ -278,7 +278,7 @@ func (e *Encoder) writePSI() error { return nil } - // Create mts packet from pmt table + // Create mts packet from pmt table. pmtPkt := Packet{ PUSI: true, PID: pmtPid, @@ -295,7 +295,7 @@ func (e *Encoder) writePSI() error { } // addPadding adds an appropriate amount of padding to a pat or pmt table for -// addition to an mpegts packet +// addition to an mpegts packet. func addPadding(d []byte) []byte { for len(d) < psiPacketSize { d = append(d, 0xff) From a63013ca4a543e3eac2979f786fbf58afec0494c Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 19:44:33 +1030 Subject: [PATCH 082/137] mts: UCed some words and any std => standard --- stream/mts/encoder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 8e43242f..abbc0fac 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -38,7 +38,7 @@ import ( // Some common manifestations of PSI var ( - // PSI struct to represent basic pat. + // PSI struct to represent basic PAT. StandardPat = psi.PSI{ Pf: 0x00, Tid: 0x00, @@ -58,7 +58,7 @@ var ( }, } - // PSI struct to represent basic pmt without descriptors for time and location. + // PSI struct to represent basic PMT without descriptors for time and location. StandardPmt = psi.PSI{ Pf: 0x00, Tid: 0x02, @@ -71,7 +71,7 @@ var ( Sn: 0, Lsn: 0, Sd: &psi.PMT{ - Pcrpid: 0x0100, // wrong + Pcrpid: 0x0100, Pil: 0, Essd: &psi.ESSD{ St: 0x1b, @@ -82,7 +82,7 @@ var ( }, } - // Std pmt with time and location descriptors, time and location fields are zeroed out. + // Standard PMT with time and location descriptors, time and location fields are zeroed out. StandardPmtTimeLocation = psi.PSI{ Pf: 0x00, Tid: 0x02, From ddf7a94ab87c7b7177decd9a4ced4e18558b1655 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 19:46:24 +1030 Subject: [PATCH 083/137] mts: made standard pat and pmt tables unexported --- stream/mts/encoder.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index abbc0fac..172a56e6 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -39,7 +39,7 @@ import ( // Some common manifestations of PSI var ( // PSI struct to represent basic PAT. - StandardPat = psi.PSI{ + standardPat = psi.PSI{ Pf: 0x00, Tid: 0x00, Ssi: true, @@ -59,7 +59,7 @@ var ( } // PSI struct to represent basic PMT without descriptors for time and location. - StandardPmt = psi.PSI{ + standardPmt = psi.PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, @@ -83,7 +83,7 @@ var ( } // Standard PMT with time and location descriptors, time and location fields are zeroed out. - StandardPmtTimeLocation = psi.PSI{ + standardPmtTimeLocation = psi.PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, @@ -140,8 +140,8 @@ func SetLocation(g string) { } var ( - patTable = StandardPat.Bytes() - pmtTable = StandardPmtTimeLocation.Bytes() + patTable = standardPat.Bytes() + pmtTable = standardPmtTimeLocation.Bytes() ) const ( From 6cb56421d3944019d7714349d062ff6ea77068e4 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 20:01:30 +1030 Subject: [PATCH 084/137] mts: add getters and setters with mutex to the TimeLocation struct. --- stream/mts/encoder.go | 48 +++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 172a56e6..9e0bcc6f 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -30,6 +30,7 @@ package mts import ( "io" + "sync" "time" "bitbucket.org/ausocean/av/stream/mts/pes" @@ -124,21 +125,46 @@ const ( psiSendCount = 7 ) -type TimeLocation struct { - Time uint64 - Location string +// TimeLocation can hold time and location data for the insertion into mpegts +// packets. +type timeLocation struct { + sync.RWMutex + time uint64 + location string } -var metaData = TimeLocation{} - -func SetTimeStamp(t uint64) { - metaData.Time = t +// SetTimeStamp sets the time field of a TimeLocation. +func (tl *timeLocation) SetTimeStamp(t uint64) { + tl.Lock() + tl.time = t + tl.Unlock() } -func SetLocation(g string) { - metaData.Location = g +// GetTimeStamp returns the location of a TimeLocation. +func (tl *timeLocation) GetTimeStamp() uint64 { + tl.RLock() + defer tl.RUnlock() + return tl.time } +// SetLocation sets the location of a TimeLocation. +func (tl *timeLocation) SetLocation(l string) { + tl.Lock() + tl.location = l + tl.Unlock() +} + +// GetLocation returns the location of a TimeLocation. +func (tl *timeLocation) GetLocation() string { + tl.RLock() + defer tl.RUnlock() + return tl.location +} + +// MetData will hold time and location data which may be set externally if +// this data is available. It is then inserted into mpegts packets outputted. +var MetaData timeLocation + var ( patTable = standardPat.Bytes() pmtTable = standardPmtTimeLocation.Bytes() @@ -269,11 +295,11 @@ func (e *Encoder) writePSI() error { } // Update pmt table time and location. - err = psi.UpdateTime(pmtTable, metaData.Time) + err = psi.UpdateTime(pmtTable, MetaData.GetTimeStamp()) if err != nil { return err } - err = psi.UpdateLocation(pmtTable, metaData.Location) + err = psi.UpdateLocation(pmtTable, MetaData.GetLocation()) if err != nil { return nil } From d665bece23ba277d50fcadf8131df5d341180747 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 20:02:59 +1030 Subject: [PATCH 085/137] psi: added dan as an author to crc.go --- stream/mts/psi/crc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go index 2da603f7..3d967ed8 100644 --- a/stream/mts/psi/crc.go +++ b/stream/mts/psi/crc.go @@ -5,6 +5,7 @@ DESCRIPTION See Readme.md AUTHOR + Dan Kortschak Saxon Milton LICENSE From fc7f5501e0d5690792d3de66614764b5dea71982 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 20:04:45 +1030 Subject: [PATCH 086/137] psi: removed conditional in HasTime and HasLocation and just used result of logic operation --- stream/mts/psi/helpers.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index b04e9f76..15029e36 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -44,19 +44,13 @@ func TimeBytes(t uint64) []byte { // HasTime takes a psi as a byte slice and checks to see if it has a time descriptor // - if so return nil, otherwise return error func HasTime(p []byte) bool { - if p[TimeTagIndx] == TimeDescTag { - return true - } - return false + return p[TimeTagIndx] == TimeDescTag } // HasLocation takes a psi as a byte slice and checks to see if it has a location descriptor // - if so return nil, otherwise return error func HasLocation(p []byte) bool { - if p[LocationTagIndx] == LocationDescTag { - return true - } - return false + return p[LocationTagIndx] == LocationDescTag } // UpdateTime takes the byte slice representation of a psi-pmt as well as a time From cda6999639e159fa7bb85307995578fa6f92f5a6 Mon Sep 17 00:00:00 2001 From: saxon Date: Tue, 8 Jan 2019 20:06:07 +1030 Subject: [PATCH 087/137] psi: not using named byte in asByte --- stream/mts/psi/psi.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 3ad8ab93..c9c6b4bb 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -291,9 +291,9 @@ func (e *ESSD) Bytes() []byte { return out } -func asByte(b bool) (o byte) { +func asByte(b bool) byte { if b { - o = 0x01 + return 0x01 } - return + return 0x00 } From c76c4aafb9f7593822390bf905d16c0f347b8acf Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 9 Jan 2019 09:12:46 +1030 Subject: [PATCH 088/137] mts: fixed some commenting --- stream/mts/encoder.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 9e0bcc6f..29875588 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -39,7 +39,7 @@ import ( // Some common manifestations of PSI var ( - // PSI struct to represent basic PAT. + // PSI struct to represent a bare minimum PAT. standardPat = psi.PSI{ Pf: 0x00, Tid: 0x00, @@ -59,7 +59,7 @@ var ( }, } - // PSI struct to represent basic PMT without descriptors for time and location. + // PSI struct to represent a bare minimum PMT without descriptors for time and location. standardPmt = psi.PSI{ Pf: 0x00, Tid: 0x02, @@ -125,8 +125,7 @@ const ( psiSendCount = 7 ) -// TimeLocation can hold time and location data for the insertion into mpegts -// packets. +// timeLocation holds time and location data type timeLocation struct { sync.RWMutex time uint64 From f432950c28917f854fc34f39eac458df4e4995bf Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 9 Jan 2019 09:15:08 +1030 Subject: [PATCH 089/137] mts: GetLocation()=>Location() and GetTimeStamp()=>TimeStamp() --- stream/mts/encoder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 29875588..41dd94d2 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -140,7 +140,7 @@ func (tl *timeLocation) SetTimeStamp(t uint64) { } // GetTimeStamp returns the location of a TimeLocation. -func (tl *timeLocation) GetTimeStamp() uint64 { +func (tl *timeLocation) TimeStamp() uint64 { tl.RLock() defer tl.RUnlock() return tl.time @@ -154,7 +154,7 @@ func (tl *timeLocation) SetLocation(l string) { } // GetLocation returns the location of a TimeLocation. -func (tl *timeLocation) GetLocation() string { +func (tl *timeLocation) Location() string { tl.RLock() defer tl.RUnlock() return tl.location @@ -294,11 +294,11 @@ func (e *Encoder) writePSI() error { } // Update pmt table time and location. - err = psi.UpdateTime(pmtTable, MetaData.GetTimeStamp()) + err = psi.UpdateTime(pmtTable, MetaData.TimeStamp()) if err != nil { return err } - err = psi.UpdateLocation(pmtTable, MetaData.GetLocation()) + err = psi.UpdateLocation(pmtTable, MetaData.Location()) if err != nil { return nil } From 459b1895def0602af558e5ea193e72ba2bc39d91 Mon Sep 17 00:00:00 2001 From: saxon Date: Wed, 9 Jan 2019 09:16:57 +1030 Subject: [PATCH 090/137] mts: got rid of blank line --- stream/mts/encoder.go | 1 - 1 file changed, 1 deletion(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 41dd94d2..27f80ee5 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -229,7 +229,6 @@ const ( // generate handles the incoming data and generates equivalent mpegts packets - // sending them to the output channel. func (e *Encoder) Encode(nalu []byte) error { - // Prepare PES data. pesPkt := pes.Packet{ StreamID: streamID, From 88e1415b10eaed1f649a1df0dde98fe3d381e331 Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 9 Jan 2019 16:08:02 +1030 Subject: [PATCH 091/137] Include pkg name in fatal log messages. --- rtmp/rtmp.go | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 2574a785..35084bb0 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -236,7 +236,7 @@ func handlePacket(s *Session, pkt *packet) int32 { s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) case RTMP_PACKET_TYPE_CONTROL: - s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_CONTROL") + s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_CONTROL") case RTMP_PACKET_TYPE_SERVER_BW: s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) @@ -250,16 +250,16 @@ func handlePacket(s *Session, pkt *packet) int32 { } case RTMP_PACKET_TYPE_AUDIO: - s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_AUDIO") + s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_AUDIO") case RTMP_PACKET_TYPE_VIDEO: - s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_VIDEO") + s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_VIDEO") case RTMP_PACKET_TYPE_FLEX_MESSAGE: - s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_FLEX_MESSAGE") + s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_FLEX_MESSAGE") case RTMP_PACKET_TYPE_INFO: - s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_INFO") + s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_INFO") case RTMP_PACKET_TYPE_INVOKE: err := handleInvoke(s, pkt.body[:pkt.bodySize]) @@ -270,7 +270,7 @@ func handlePacket(s *Session, pkt *packet) int32 { } case RTMP_PACKET_TYPE_FLASH_VIDEO: - s.log(FatalLevel, "unsupported packet type RTMP_PACKET_TYPE_FLASH_VIDEO") + s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_FLASH_VIDEO") default: s.log(WarnLevel, pkg+"unknown packet type", "type", pkt.packetType) @@ -693,7 +693,7 @@ func handleInvoke(s *Session, body []byte) error { } meth := C_AMFProp_GetString(C_AMF_GetProp(&obj, "", 0)) - s.log(DebugLevel, pkg+"invoking", "method", meth) + s.log(DebugLevel, pkg+"invoking method "+meth) txn := C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 1)) switch meth { @@ -715,19 +715,19 @@ func handleInvoke(s *Session, body []byte) error { switch methodInvoked { case av_connect: if s.link.token != "" { - s.log(FatalLevel, "no support for link token") + s.log(FatalLevel, pkg+"no support for link token") } if (s.link.protocol & RTMP_FEATURE_WRITE) != 0 { sendReleaseStream(s) sendFCPublish(s) } else { - s.log(FatalLevel, "link protocol has no RTMP_FEATURE_WRITE") + s.log(FatalLevel, pkg+"link protocol has no RTMP_FEATURE_WRITE") } sendCreateStream(s) if (s.link.protocol & RTMP_FEATURE_WRITE) == 0 { - s.log(FatalLevel, "link protocol has no RTMP_FEATURE_WRITE") + s.log(FatalLevel, pkg+"link protocol has no RTMP_FEATURE_WRITE") } case av_createStream: @@ -736,11 +736,11 @@ func handleInvoke(s *Session, body []byte) error { if s.link.protocol&RTMP_FEATURE_WRITE != 0 { sendPublish(s) } else { - s.log(FatalLevel, "link protocol has no RTMP_FEATURE_WRITE") + s.log(FatalLevel, pkg+"link protocol has no RTMP_FEATURE_WRITE") } case av_play, av_publish: - s.log(FatalLevel, "unsupported method av_play/av_publish") + s.log(FatalLevel, pkg+"unsupported method av_play/av_publish") } case av_onBWDone: @@ -749,19 +749,19 @@ func handleInvoke(s *Session, body []byte) error { } case av_onFCUnsubscribe, av_onFCSubscribe: - s.log(FatalLevel, "unsupported method av_onFCUnsubscribe/av_onFCSubscribe") + s.log(FatalLevel, pkg+"unsupported method av_onFCUnsubscribe/av_onFCSubscribe") case av_ping: - s.log(FatalLevel, "unsupported method av_ping") + s.log(FatalLevel, pkg+"unsupported method av_ping") case av__onbwcheck: - s.log(FatalLevel, "unsupported method av_onbwcheck") + s.log(FatalLevel, pkg+"unsupported method av_onbwcheck") case av__onbwdone: - s.log(FatalLevel, "unsupported method av_onbwdone") + s.log(FatalLevel, pkg+"unsupported method av_onbwdone") case av_close: - s.log(FatalLevel, "unsupported method av_close") + s.log(FatalLevel, pkg+"unsupported method av_close") case av_onStatus: var obj2 C_AMFObject @@ -773,10 +773,10 @@ func handleInvoke(s *Session, body []byte) error { switch code { case av_NetStream_Failed, av_NetStream_Play_Failed, av_NetStream_Play_StreamNotFound, av_NetConnection_Connect_InvalidApp: - s.log(FatalLevel, "unsupported method av_NetStream/av_NetStream_Play_Failed/av_netSTream_Play_StreamNotFound/av_netConnection_Connect_invalidApp") + s.log(FatalLevel, pkg+"unsupported method av_NetStream/av_NetStream_Play_Failed/av_netSTream_Play_StreamNotFound/av_netConnection_Connect_invalidApp") case av_NetStream_Play_Start, av_NetStream_Play_PublishNotify: - s.log(FatalLevel, "unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") + s.log(FatalLevel, pkg+"unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") case av_NetStream_Publish_Start: s.isPlaying = true @@ -788,20 +788,20 @@ func handleInvoke(s *Session, body []byte) error { } case av_NetStream_Play_Complete, av_NetStream_Play_Stop, av_NetStream_Play_UnpublishNotify: - s.log(FatalLevel, "unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") + s.log(FatalLevel, pkg+"unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") case av_NetStream_Seek_Notify: - s.log(FatalLevel, "unsupported method av_netStream_Seek_Notify") + s.log(FatalLevel, pkg+"unsupported method av_netStream_Seek_Notify") case av_NetStream_Pause_Notify: - s.log(FatalLevel, "unsupported method av_NetStream_Pause_Notify") + s.log(FatalLevel, pkg+"unsupported method av_NetStream_Pause_Notify") } case av_playlist_ready: - s.log(FatalLevel, "unsupported method av_playlist_ready") + s.log(FatalLevel, pkg+"unsupported method av_playlist_ready") default: - s.log(FatalLevel, "unknown method", "method", meth) + s.log(FatalLevel, pkg+"unknown method "+meth) } leave: C_AMF_Reset(&obj) From 0a69c59f5059f30730e056f19734edd487fda598 Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 9 Jan 2019 17:33:19 +1030 Subject: [PATCH 092/137] Additional logging. --- rtmp/packet.go | 4 ++-- rtmp/rtmp.go | 4 ++-- rtmp/session.go | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index d97d99ea..9589dc6b 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -390,7 +390,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { size := int(pkt.bodySize) chunkSize := int(s.outChunkSize) - s.log(DebugLevel, pkg+"sending packet", "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr(), "size", size) + s.log(DebugLevel, pkg+"sending packet", "size", size, "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr()) if s.deferred != nil && len(s.deferred)+size+hSize > chunkSize { err := writeN(s, s.deferred) @@ -456,9 +456,9 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { buf := pkt.body[1:] meth := C_AMF_DecodeString(buf) - s.log(DebugLevel, "invoking method", "method", meth) // keep it in call queue till result arrives if queue { + s.log(DebugLevel, pkg+"queuing method "+meth) buf = buf[3+len(meth):] txn := int32(C_AMF_DecodeNumber(buf[:8])) s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 35084bb0..c4a15108 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -285,7 +285,7 @@ func readN(s *Session, buf []byte) error { } n, err := io.ReadFull(s.link.conn, buf) if err != nil { - s.log(WarnLevel, pkg+"read failed", "error", err.Error()) + s.log(DebugLevel, pkg+"read failed", "error", err.Error()) s.close() return err } @@ -710,7 +710,7 @@ func handleInvoke(s *Session, body []byte) error { s.log(WarnLevel, pkg+"received result without matching request", "id", txn) goto leave } - s.log(DebugLevel, pkg+"received result for method", "id", txn) + s.log(DebugLevel, pkg+"received result for "+methodInvoked) switch methodInvoked { case av_connect: diff --git a/rtmp/session.go b/rtmp/session.go index a04f39d5..fe548e75 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -98,6 +98,7 @@ func (s *Session) Open() error { // start does the heavylifting for Open(). func (s *Session) start() error { + s.log(DebugLevel, pkg+"Session.start") s.init() err := setupURL(s, s.url) if err != nil { @@ -146,6 +147,7 @@ func (s *Session) Close() error { // close does the heavylifting for Close(). // Any errors are ignored as it is often called in response to an earlier error. func (s *Session) close() { + s.log(DebugLevel, pkg+"Session.close") if s.isConnected() { if s.streamID > 0 { if s.link.protocol&RTMP_FEATURE_WRITE != 0 { From d1958ff75d73ca0ea56e5b1a1358c34025227bc4 Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 9 Jan 2019 17:34:38 +1030 Subject: [PATCH 093/137] Initial revision. --- rtmp/rtmp_test.go | 163 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 rtmp/rtmp_test.go diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go new file mode 100644 index 00000000..ea5f687c --- /dev/null +++ b/rtmp/rtmp_test.go @@ -0,0 +1,163 @@ +/* +NAME + rtmp_test.go + +DESCRIPTION + RTMP tests + +AUTHORS + Alan Noble + +LICENSE + rtmp_test.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) + + It is free software: you can redistribute it and/or modify them + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. +*/ + +package rtmp + +import ( + "fmt" + "io/ioutil" + "os" + "runtime" + "testing" +) + +const ( + rtmpProtocol = "rtmp" + testHost = "a.rtmp.youtube.com" + testApp = "live2" + testBaseURL = rtmpProtocol + "://" + testHost + "/" + testApp + "/" + testTimeout = 30 +) + +// debug enables extra logging +var testDebug bool + +// testKey is the RTMP key required for YouTube streaming (RTMP_TEST_KEY env var) +var testKey string + +// testFile is the test video file (RTMP_TEST_FILE env var) +var testFile string + +// testLog is a bare bones logger that logs to stdout. +func testLog(level int8, msg string, params ...interface{}) { + logLevels := [...]string{"Debug", "Info", "Warn", "Error", "", "", "Fatal"} + if level < -1 || level > 5 { + panic("Invalid log level") + } + if testDebug && len(params) >= 2 { + switch params[0].(string) { + case "error": + fmt.Printf("%s: %s, error=%v\n", logLevels[level+1], msg, params[1].(string)) + case "size": + fmt.Printf("%s: %s, size=%d\n", logLevels[level+1], msg, params[1].(int)) + default: + fmt.Printf("%s: %s\n", logLevels[level+1], msg) + } + } else { + fmt.Printf("%s: %s\n", logLevels[level+1], msg) + } + if level == 5 { + // Fatal + buf := make([]byte, 1<<16) + size := runtime.Stack(buf, true) + fmt.Printf("%s\n", string(buf[:size])) + os.Exit(1) + } +} + +func TestKey(t *testing.T) { + testLog(0, "TestKey") + testKey := os.Getenv("RTMP_TEST_KEY") + if testKey == "" { + t.Errorf("RTMP_TEST_KEY environment variable not defined") + os.Exit(1) + } + testLog(0, "Testing against URL "+testBaseURL+testKey) +} + +func TestSetupURL(t *testing.T) { + testLog(0, "TestSetupURL") + // test with just the base URL + s := NewSession(testBaseURL, testTimeout, testLog) + if s.url != testBaseURL && s.link.timeout != testTimeout { + t.Errorf("NewSession failed") + } + err := setupURL(s, s.url) + if err != nil { + t.Errorf("setupURL(testBaseURL) failed with error: %v", err) + } + // test again with the full URL + s = NewSession(testBaseURL+testKey, testTimeout, testLog) + err = setupURL(s, s.url) + if err != nil { + t.Errorf("setupURL(testBaseURL+testKey) failed with error: %v", err) + } + // test the parts are as expected + if rtmpProtocolStrings[s.link.protocol] != rtmpProtocol { + t.Errorf("setupURL returned wrong protocol: %v", s.link.protocol) + } + if s.link.host != testHost { + t.Errorf("setupURL returned wrong host: %v", s.link.host) + } + if s.link.app != testApp { + t.Errorf("setupURL returned wrong app: %v", s.link.app) + } +} + +func TestOpenClose(t *testing.T) { + testLog(0, "TestOpenClose") + s := NewSession(testBaseURL+testKey, testTimeout, testLog) + err := s.Open() + if err != nil { + t.Errorf("Session.Open failed with error: %v", err) + return + } + err = s.Close() + if err != nil { + t.Errorf("Session.Close failed with error: %v", err) + } +} + +func TestFromFile(t *testing.T) { + testLog(0, "TestFromFile") + testFile := os.Getenv("RTMP_TEST_FILE") + if testKey == "" { + t.Errorf("RTMP_TEST_FILE environment variable not defined") + os.Exit(1) + } + s := NewSession(testBaseURL+testKey, testTimeout, testLog) + err := s.Open() + if err != nil { + t.Errorf("Session.Open failed with error: %v", err) + } + video, err := ioutil.ReadFile(testFile) + if err != nil { + t.Errorf("Cannot open video file: %v", testFile) + } + // ToDo: rate limit writing + n, err := s.Write(video) + if err != nil { + t.Errorf("Session.Write failed with error: %v", err) + } + if n != len(video) { + t.Errorf("Session.Write retuned wrong length") + } + err = s.Close() + if err != nil { + t.Errorf("Session.Close failed with error: %v", err) + } +} From c8ec31782367dd69f07b56c465d68675c62a67ce Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 9 Jan 2019 22:05:04 +1030 Subject: [PATCH 094/137] Merge init into NewSession. --- rtmp/session.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/rtmp/session.go b/rtmp/session.go index fe548e75..bd2a9e6d 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -78,9 +78,19 @@ const ( // NewSession returns a new Session. func NewSession(url string, timeout uint, log Log) *Session { return &Session{ - url: url, - timeout: timeout, - log: log, + url: url, + inChunkSize: 128, + outChunkSize: 128, + clientBW: 2500000, + clientBW2: 2, + serverBW: 2500000, + audioCodecs: 3191.0, + videoCodecs: 252.0, + log: log, + link: link{ + timeout: timeout, + swfAge: 30, + }, } } @@ -99,7 +109,6 @@ func (s *Session) Open() error { // start does the heavylifting for Open(). func (s *Session) start() error { s.log(DebugLevel, pkg+"Session.start") - s.init() err := setupURL(s, s.url) if err != nil { s.close() @@ -121,20 +130,6 @@ func (s *Session) start() error { return nil } -// init initializes various RTMP defauls. -// ToDo: define consts for the magic numbers. -func (s *Session) init() { - s.inChunkSize = RTMP_DEFAULT_CHUNKSIZE - s.outChunkSize = RTMP_DEFAULT_CHUNKSIZE - s.clientBW = 2500000 - s.clientBW2 = 2 - s.serverBW = 2500000 - s.audioCodecs = 3191.0 - s.videoCodecs = 252.0 - s.link.timeout = s.timeout - s.link.swfAge = 30 -} - // Close terminates the rtmp connection, func (s *Session) Close() error { if !s.isConnected() { From c386f45bbd7e35466af9269ee8ef4bb4233dcc33 Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 9 Jan 2019 22:25:10 +1030 Subject: [PATCH 095/137] Removed out of date comment. --- rtmp/rtmp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index c4a15108..125410f3 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -767,7 +767,7 @@ func handleInvoke(s *Session, body []byte) error { var obj2 C_AMFObject C_AMFProp_GetObject(C_AMF_GetProp(&obj, "", 3), &obj2) code := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_code, -1)) - level := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_level, -1)) // Not used. + level := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_level, -1)) s.log(DebugLevel, pkg+"onStatus", "code", code, "level", level) switch code { From 076a9c030a50ef5cb6fad2c615d7318e4398036f Mon Sep 17 00:00:00 2001 From: scruzin Date: Wed, 9 Jan 2019 22:51:07 +1030 Subject: [PATCH 096/137] Better field names. --- rtmp/packet.go | 43 ++++++++++++++++++++++--------------------- rtmp/rtmp.go | 2 +- rtmp/session.go | 7 +++---- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 9589dc6b..8980cf5d 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -132,20 +132,20 @@ func readPacket(s *Session, pkt *packet) error { timestamp := append(s.channelTimestamp, make([]int32, 10)...) var pkts []*packet - if s.vecChannelsIn == nil { + if s.channelsIn == nil { pkts = make([]*packet, n) } else { - pkts = append(s.vecChannelsIn[:pkt.channel:pkt.channel], make([]*packet, 10)...) + pkts = append(s.channelsIn[:pkt.channel:pkt.channel], make([]*packet, 10)...) } s.channelTimestamp = timestamp - s.vecChannelsIn = pkts + s.channelsIn = pkts for i := int(s.channelsAllocatedIn); i < len(s.channelTimestamp); i++ { s.channelTimestamp[i] = 0 } for i := int(s.channelsAllocatedIn); i < int(n); i++ { - s.vecChannelsIn[i] = nil + s.channelsIn[i] = nil } s.channelsAllocatedIn = n } @@ -155,8 +155,8 @@ func readPacket(s *Session, pkt *packet) error { case size == RTMP_LARGE_HEADER_SIZE: pkt.hasAbsTimestamp = true case size < RTMP_LARGE_HEADER_SIZE: - if s.vecChannelsIn[pkt.channel] != nil { - *pkt = *(s.vecChannelsIn[pkt.channel]) + if s.channelsIn[pkt.channel] != nil { + *pkt = *(s.channelsIn[pkt.channel]) } } size-- @@ -224,13 +224,13 @@ func readPacket(s *Session, pkt *packet) error { pkt.bytesRead += uint32(chunkSize) // keep the packet as ref for other packets on this channel - if s.vecChannelsIn[pkt.channel] == nil { - s.vecChannelsIn[pkt.channel] = &packet{} + if s.channelsIn[pkt.channel] == nil { + s.channelsIn[pkt.channel] = &packet{} } - *(s.vecChannelsIn[pkt.channel]) = *pkt + *(s.channelsIn[pkt.channel]) = *pkt if extendedTimestamp { - s.vecChannelsIn[pkt.channel].timestamp = 0xffffff + s.channelsIn[pkt.channel].timestamp = 0xffffff } if pkt.bytesRead != pkt.bodySize { @@ -243,9 +243,9 @@ func readPacket(s *Session, pkt *packet) error { } s.channelTimestamp[pkt.channel] = int32(pkt.timestamp) - s.vecChannelsIn[pkt.channel].body = nil - s.vecChannelsIn[pkt.channel].bytesRead = 0 - s.vecChannelsIn[pkt.channel].hasAbsTimestamp = false + s.channelsIn[pkt.channel].body = nil + s.channelsIn[pkt.channel].bytesRead = 0 + s.channelsIn[pkt.channel].hasAbsTimestamp = false return nil } @@ -266,20 +266,20 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { n := int(pkt.channel + 10) var pkts []*packet - if s.vecChannelsOut == nil { + if s.channelsOut == nil { pkts = make([]*packet, n) } else { - pkts = append(s.vecChannelsOut[:pkt.channel:pkt.channel], make([]*packet, 10)...) + pkts = append(s.channelsOut[:pkt.channel:pkt.channel], make([]*packet, 10)...) } - s.vecChannelsOut = pkts + s.channelsOut = pkts for i := int(s.channelsAllocatedOut); i < n; i++ { - s.vecChannelsOut[i] = nil + s.channelsOut[i] = nil } s.channelsAllocatedOut = int32(n) } - prevPkt = s.vecChannelsOut[pkt.channel] + prevPkt = s.channelsOut[pkt.channel] if prevPkt != nil && pkt.headerType != RTMP_PACKET_SIZE_LARGE { // compress a bit by using the prev packet's attributes @@ -415,6 +415,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { if s.deferred != nil { // Prepend the previously deferred packet and write it with the current one. bytes = append(s.deferred, bytes...) + s.deferred = nil } err := writeN(s, bytes) if err != nil { @@ -465,10 +466,10 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { } } - if s.vecChannelsOut[pkt.channel] == nil { - s.vecChannelsOut[pkt.channel] = &packet{} + if s.channelsOut[pkt.channel] == nil { + s.channelsOut[pkt.channel] = &packet{} } - *(s.vecChannelsOut[pkt.channel]) = *pkt + *(s.channelsOut[pkt.channel]) = *pkt return nil } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 125410f3..eb0d5621 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -744,7 +744,7 @@ func handleInvoke(s *Session, body []byte) error { } case av_onBWDone: - if s.bwCheckCounter == 0 { + if s.checkCounter == 0 { // ToDo: why is this always zero? sendCheckBW(s) } diff --git a/rtmp/session.go b/rtmp/session.go index bd2a9e6d..a402a320 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -36,10 +36,9 @@ package rtmp // Session holds the state for an RTMP session. type Session struct { url string - timeout uint inChunkSize int32 outChunkSize int32 - bwCheckCounter int32 + checkCounter int32 nBytesIn int32 nBytesInSent int32 streamID int32 @@ -52,8 +51,8 @@ type Session struct { methodCalls []method channelsAllocatedIn int32 channelsAllocatedOut int32 - vecChannelsIn []*packet - vecChannelsOut []*packet + channelsIn []*packet + channelsOut []*packet channelTimestamp []int32 audioCodecs float64 videoCodecs float64 From 304d5501ac5897fefb83be42dfd151f69ef109d0 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 10 Jan 2019 11:12:33 +1030 Subject: [PATCH 097/137] mts: improved some commenting --- stream/mts/encoder.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 27f80ee5..7bc511ed 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -39,7 +39,7 @@ import ( // Some common manifestations of PSI var ( - // PSI struct to represent a bare minimum PAT. + // standardPat is a minimal PAT. standardPat = psi.PSI{ Pf: 0x00, Tid: 0x00, @@ -59,7 +59,7 @@ var ( }, } - // PSI struct to represent a bare minimum PMT without descriptors for time and location. + // standardPmt is a minimal PMT without descriptors for time and location. standardPmt = psi.PSI{ Pf: 0x00, Tid: 0x02, @@ -83,7 +83,7 @@ var ( }, } - // Standard PMT with time and location descriptors, time and location fields are zeroed out. + // standardPmtTimeLocation is a minimal pmt with descriptors for time and location. standardPmtTimeLocation = psi.PSI{ Pf: 0x00, Tid: 0x02, From e772d37a1b383ff77a6207f4e95a96d42ab3c799 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 12:48:31 +1030 Subject: [PATCH 098/137] Warn about EOF errors. --- rtmp/packet.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 8980cf5d..79bd99fe 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -35,6 +35,7 @@ LICENSE package rtmp import ( + "io" "encoding/binary" ) @@ -101,6 +102,9 @@ func readPacket(s *Session, pkt *packet) error { err := readN(s, header[:1]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 1st byte", "error", err.Error()) + if err == io.EOF { + s.log(WarnLevel, pkg+"EOF error; connection likely terminated") + } return err } pkt.headerType = (header[0] & 0xc0) >> 6 @@ -415,7 +419,6 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { if s.deferred != nil { // Prepend the previously deferred packet and write it with the current one. bytes = append(s.deferred, bytes...) - s.deferred = nil } err := writeN(s, bytes) if err != nil { From 3dbaa810fc6d483293ad3d006d20f9a8f7028b3e Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 12:49:52 +1030 Subject: [PATCH 099/137] Additional logging. --- rtmp/rtmp.go | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index eb0d5621..2c6300d5 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -191,40 +191,38 @@ func connect(s *Session) error { // connectStream reads a packet and handles it func connectStream(s *Session) error { - var pkt packet - + var err error for !s.isPlaying && s.isConnected() { - err := readPacket(s, &pkt) + pkt := packet{} + err = readPacket(s, &pkt) if err != nil { break } - if pkt.bodySize == 0 { continue } - if pkt.packetType == RTMP_PACKET_TYPE_AUDIO || pkt.packetType == RTMP_PACKET_TYPE_VIDEO || pkt.packetType == RTMP_PACKET_TYPE_INFO { s.log(DebugLevel, pkg+"got packet before play; ignoring") - pkt.body = nil continue } - handlePacket(s, &pkt) - pkt.body = nil + err = handlePacket(s, &pkt) + if err != nil { + break + } } if !s.isPlaying { - return errConnStream + return err } return nil } // handlePacket handles a packet that the client has received. // NB: cases have been commented out that are not currently used by AusOcean -func handlePacket(s *Session, pkt *packet) int32 { - var hasMediaPacket int32 +func handlePacket(s *Session, pkt *packet) error { switch pkt.packetType { case RTMP_PACKET_TYPE_CHUNK_SIZE: @@ -266,7 +264,7 @@ func handlePacket(s *Session, pkt *packet) int32 { if err != nil { // This will never happen with the methods we implement. s.log(WarnLevel, pkg+"unexpected error from handleInvoke", "error", err.Error()) - hasMediaPacket = 2 + return err } case RTMP_PACKET_TYPE_FLASH_VIDEO: @@ -275,7 +273,7 @@ func handlePacket(s *Session, pkt *packet) int32 { default: s.log(WarnLevel, pkg+"unknown packet type", "type", pkt.packetType) } - return hasMediaPacket + return nil } func readN(s *Session, buf []byte) error { @@ -286,7 +284,6 @@ func readN(s *Session, buf []byte) error { n, err := io.ReadFull(s.link.conn, buf) if err != nil { s.log(DebugLevel, pkg+"read failed", "error", err.Error()) - s.close() return err } s.nBytesIn += int32(n) @@ -308,7 +305,6 @@ func writeN(s *Session, buf []byte) error { _, err = s.link.conn.Write(buf) if err != nil { s.log(WarnLevel, pkg+"write failed", "error", err.Error()) - s.close() return err } return nil @@ -404,12 +400,10 @@ func sendConnectPacket(s *Session) error { } } - if copy(enc, []byte{0, 0, AMF_OBJECT_END}) != 3 { - return errCopying // TODO: is this even possible? - } + copy(enc, []byte{0, 0, AMF_OBJECT_END}) enc = enc[3:] - /* add auth string */ + // add auth string if s.link.auth != "" { enc = C_AMF_EncodeBoolean(enc, s.link.lFlags&RTMP_LF_AUTH != 0) if enc == nil { @@ -430,7 +424,7 @@ func sendConnectPacket(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, true) + return sendPacket(s, &pkt, true) // response expected } func sendCreateStream(s *Session) error { @@ -458,7 +452,7 @@ func sendCreateStream(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, true) + return sendPacket(s, &pkt, true) // response expected } func sendReleaseStream(s *Session) error { @@ -589,7 +583,7 @@ func sendPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, true) + return sendPacket(s, &pkt, true) // response expected } func sendDeleteStream(s *Session, dStreamId float64) error { @@ -779,6 +773,7 @@ func handleInvoke(s *Session, body []byte) error { s.log(FatalLevel, pkg+"unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") case av_NetStream_Publish_Start: + s.log(DebugLevel, pkg+"playing") s.isPlaying = true for i, m := range s.methodCalls { if m.name == av_publish { @@ -786,6 +781,7 @@ func handleInvoke(s *Session, body []byte) error { break } } + // ToDo: handle case when av_publish method not found case av_NetStream_Play_Complete, av_NetStream_Play_Stop, av_NetStream_Play_UnpublishNotify: s.log(FatalLevel, pkg+"unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") @@ -825,14 +821,15 @@ func handshake(s *Session) error { if err != nil { return err } + s.log(DebugLevel, pkg+"handshake sent") var typ [1]byte err = readN(s, typ[:]) if err != nil { return err } + s.log(DebugLevel, pkg+"handshake received") - s.log(DebugLevel, pkg+"handshake", "received", typ[0]) if typ[0] != clientbuf[0] { s.log(WarnLevel, pkg+"handshake type mismatch", "sent", clientbuf[0], "received", typ) } From 97964650186f3c3863f176cbe9e44f963545bbde Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 12:51:18 +1030 Subject: [PATCH 100/137] Exit if RTMP_TEST_KEY not defined. --- rtmp/rtmp_test.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index ea5f687c..2dc6781d 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -29,10 +29,10 @@ package rtmp import ( "fmt" - "io/ioutil" "os" "runtime" "testing" + "io/ioutil" ) const ( @@ -81,9 +81,9 @@ func testLog(level int8, msg string, params ...interface{}) { func TestKey(t *testing.T) { testLog(0, "TestKey") - testKey := os.Getenv("RTMP_TEST_KEY") + testKey = os.Getenv("RTMP_TEST_KEY") if testKey == "" { - t.Errorf("RTMP_TEST_KEY environment variable not defined") + fmt.Printf("RTMP_TEST_KEY environment variable not defined\n") os.Exit(1) } testLog(0, "Testing against URL "+testBaseURL+testKey) @@ -118,25 +118,35 @@ func TestSetupURL(t *testing.T) { } } +func TestOpen(t *testing.T) { + testLog(0, "TestOpen") + s := NewSession(testBaseURL+testKey, testTimeout, testLog) + err := setupURL(s, s.url) + if err != nil { + t.Errorf("setupURL failed with error: %v", err) + } + s.enableWrite() + err = s.Open() + if err != nil { + t.Errorf("connect failed with error: %v", err) + } +} + func TestOpenClose(t *testing.T) { testLog(0, "TestOpenClose") s := NewSession(testBaseURL+testKey, testTimeout, testLog) err := s.Open() if err != nil { - t.Errorf("Session.Open failed with error: %v", err) + t.Errorf("Open failed with error: %v", err) return } - err = s.Close() - if err != nil { - t.Errorf("Session.Close failed with error: %v", err) - } } func TestFromFile(t *testing.T) { testLog(0, "TestFromFile") testFile := os.Getenv("RTMP_TEST_FILE") if testKey == "" { - t.Errorf("RTMP_TEST_FILE environment variable not defined") + fmt.Printf("RTMP_TEST_FILE environment variable not defined\n") os.Exit(1) } s := NewSession(testBaseURL+testKey, testTimeout, testLog) @@ -150,6 +160,7 @@ func TestFromFile(t *testing.T) { } // ToDo: rate limit writing n, err := s.Write(video) + if err != nil { t.Errorf("Session.Write failed with error: %v", err) } From 2333d1953e4593f843c1642315b51915f7e82e87 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 13:23:12 +1030 Subject: [PATCH 101/137] packetSize -> headerSizes. --- rtmp/packet.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 79bd99fe..6d33731c 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -68,8 +68,12 @@ const ( RTMP_CHANNEL_SOURCE = 0x04 ) -// packetSize defines valid packet sizes. -var packetSize = [...]int{12, 8, 4, 1} +// headerSizes defines header sizes for header types 0, 1, 2 and 3 respectively: +// 0: full header (12 bytes) +// 1: header without message ID (8 bytes) +// 2: basic header + timestamp (4 byes) +// 3: basic header (chunk type and stream ID) (1 byte) +var headerSizes = [...]int{12, 8, 4, 1} // packet defines an RTMP packet. type packet struct { @@ -154,7 +158,7 @@ func readPacket(s *Session, pkt *packet) error { s.channelsAllocatedIn = n } - size := packetSize[pkt.headerType] + size := headerSizes[pkt.headerType] switch { case size == RTMP_LARGE_HEADER_SIZE: pkt.hasAbsTimestamp = true @@ -308,7 +312,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { if pkt.body != nil { // Span from -packetsize for the type to the start of the body. headBytes = pkt.header - origIdx = RTMP_MAX_HEADER_SIZE - packetSize[pkt.headerType] + origIdx = RTMP_MAX_HEADER_SIZE - headerSizes[pkt.headerType] } else { // Allocate a new header and allow 6 bytes of movement backward. var hbuf [RTMP_MAX_HEADER_SIZE]byte @@ -324,7 +328,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { cSize = 1 } - hSize := packetSize[pkt.headerType] + hSize := headerSizes[pkt.headerType] if cSize != 0 { origIdx -= cSize hSize += cSize @@ -365,7 +369,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { } } - if packetSize[pkt.headerType] > 1 { + if headerSizes[pkt.headerType] > 1 { res := ts if ts > 0xffffff { res = 0xffffff @@ -374,14 +378,14 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { headerIdx += 3 // 24bits } - if packetSize[pkt.headerType] > 4 { + if headerSizes[pkt.headerType] > 4 { C_AMF_EncodeInt24(headBytes[headerIdx:], int32(pkt.bodySize)) headerIdx += 3 // 24bits headBytes[headerIdx] = pkt.packetType headerIdx++ } - if packetSize[pkt.headerType] > 8 { + if headerSizes[pkt.headerType] > 8 { n := int(encodeInt32LE(headBytes[headerIdx:headerIdx+4], pkt.info)) headerIdx += n } From d9c98a0341dbb06b57429416495a69ec3c469ed5 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 10 Jan 2019 13:56:20 +1030 Subject: [PATCH 102/137] rtmp: added streaming from file testing --- rtmp/rtmp_test.go | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index ea5f687c..0dd0e58c 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -33,6 +33,10 @@ import ( "os" "runtime" "testing" + "time" + + "bitbucket.org/ausocean/av/stream/flv" + "bitbucket.org/ausocean/av/stream/lex" ) const ( @@ -44,7 +48,7 @@ const ( ) // debug enables extra logging -var testDebug bool +var testDebug = true // testKey is the RTMP key required for YouTube streaming (RTMP_TEST_KEY env var) var testKey string @@ -81,7 +85,7 @@ func testLog(level int8, msg string, params ...interface{}) { func TestKey(t *testing.T) { testLog(0, "TestKey") - testKey := os.Getenv("RTMP_TEST_KEY") + testKey = os.Getenv("RTMP_TEST_KEY") if testKey == "" { t.Errorf("RTMP_TEST_KEY environment variable not defined") os.Exit(1) @@ -134,28 +138,43 @@ func TestOpenClose(t *testing.T) { func TestFromFile(t *testing.T) { testLog(0, "TestFromFile") - testFile := os.Getenv("RTMP_TEST_FILE") - if testKey == "" { - t.Errorf("RTMP_TEST_FILE environment variable not defined") - os.Exit(1) - } s := NewSession(testBaseURL+testKey, testTimeout, testLog) err := s.Open() if err != nil { t.Errorf("Session.Open failed with error: %v", err) } - video, err := ioutil.ReadFile(testFile) - if err != nil { - t.Errorf("Cannot open video file: %v", testFile) - } - // ToDo: rate limit writing - n, err := s.Write(video) - if err != nil { - t.Errorf("Session.Write failed with error: %v", err) - } - if n != len(video) { - t.Errorf("Session.Write retuned wrong length") + + // This read from a h264 file + if true { + // Open file + f, err := os.Open("../../test/test-data/av/input/betterInput.h264") + if err != nil { + t.Errorf("Cannot open video file: %v", testFile) + } + defer f.Close() + + // Passing rtmp session, true for audio, true for video, and 25 fps + flvEncoder := flv.NewEncoder(s, true, true, 25) + err = lex.H264(flvEncoder, f, time.Second/time.Duration(25)) + if err != nil { + t.Errorf("Lexing and encoding failed with error: %v", err) + } + + // This reads a single h264 frame and sends + } else { + b, err := ioutil.ReadFile("/home/saxon/Downloads/ausoceanFrame.h264") // b has type []byte + if err != nil { + t.Errorf("Could not read file, failed with error: %v", err) + } + flvEncoder := flv.NewEncoder(s, true, true, 25) + for i := 0; i < 10000; i++ { + err := flvEncoder.Encode(b) + if err != nil { + t.Errorf("Encoding failed!") + } + } } + err = s.Close() if err != nil { t.Errorf("Session.Close failed with error: %v", err) From a318f9c3ebdfa9df6a3d466e873c8e4322650437 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 15:33:24 +1030 Subject: [PATCH 103/137] Added media streaming tests and improved comments. --- rtmp/rtmp_test.go | 133 +++++++++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 54 deletions(-) diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index 4f927138..296809c6 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -29,14 +29,15 @@ package rtmp import ( "fmt" + "io/ioutil" "os" + "path/filepath" "runtime" "testing" "time" "bitbucket.org/ausocean/av/stream/flv" "bitbucket.org/ausocean/av/stream/lex" - "io/ioutil" ) const ( @@ -45,10 +46,15 @@ const ( testApp = "live2" testBaseURL = rtmpProtocol + "://" + testHost + "/" + testApp + "/" testTimeout = 30 + testDataDir = "../../test/test-data/av/input" ) -// debug enables extra logging -var testDebug = true +// testVerbosity controls the amount of output +// NB: This is not the log level, which is DebugLevel. +// 0: suppress logging completely +// 1: log messages only +// 2: log messages with errors, if any +var testVerbosity = 1 // testKey is the RTMP key required for YouTube streaming (RTMP_TEST_KEY env var) var testKey string @@ -59,10 +65,14 @@ var testFile string // testLog is a bare bones logger that logs to stdout. func testLog(level int8, msg string, params ...interface{}) { logLevels := [...]string{"Debug", "Info", "Warn", "Error", "", "", "Fatal"} + if testVerbosity == 0 { + return + } if level < -1 || level > 5 { panic("Invalid log level") } - if testDebug && len(params) >= 2 { + if testVerbosity == 2 && len(params) >= 2 { + // extract the params we know about, otherwise just print the message switch params[0].(string) { case "error": fmt.Printf("%s: %s, error=%v\n", logLevels[level+1], msg, params[1].(string)) @@ -83,16 +93,19 @@ func testLog(level int8, msg string, params ...interface{}) { } } +// TestKey tests that the RTMP_TEST_KEY environment variable is present func TestKey(t *testing.T) { testLog(0, "TestKey") testKey = os.Getenv("RTMP_TEST_KEY") if testKey == "" { - fmt.Printf("RTMP_TEST_KEY environment variable not defined\n") - os.Exit(1) + msg := "RTMP_TEST_KEY environment variable not defined" + testLog(0, msg) + t.Skip(msg) } testLog(0, "Testing against URL "+testBaseURL+testKey) } +// TestSetupURL tests URL parsing. func TestSetupURL(t *testing.T) { testLog(0, "TestSetupURL") // test with just the base URL @@ -104,12 +117,6 @@ func TestSetupURL(t *testing.T) { if err != nil { t.Errorf("setupURL(testBaseURL) failed with error: %v", err) } - // test again with the full URL - s = NewSession(testBaseURL+testKey, testTimeout, testLog) - err = setupURL(s, s.url) - if err != nil { - t.Errorf("setupURL(testBaseURL+testKey) failed with error: %v", err) - } // test the parts are as expected if rtmpProtocolStrings[s.link.protocol] != rtmpProtocol { t.Errorf("setupURL returned wrong protocol: %v", s.link.protocol) @@ -122,66 +129,48 @@ func TestSetupURL(t *testing.T) { } } -func TestOpen(t *testing.T) { - testLog(0, "TestOpen") - s := NewSession(testBaseURL+testKey, testTimeout, testLog) - err := setupURL(s, s.url) - if err != nil { - t.Errorf("setupURL failed with error: %v", err) - } - s.enableWrite() - err = s.Open() - if err != nil { - t.Errorf("connect failed with error: %v", err) - } -} - +// TestOpenClose tests opening an closing an RTMP connection. func TestOpenClose(t *testing.T) { testLog(0, "TestOpenClose") + if testKey == "" { + t.Skip("Skipping TestOpenClose since no RTMP_TEST_KEY") + } s := NewSession(testBaseURL+testKey, testTimeout, testLog) err := s.Open() if err != nil { t.Errorf("Open failed with error: %v", err) return } + err = s.Close() + if err != nil { + t.Errorf("Close failed with error: %v", err) + return + } } -func TestFromFile(t *testing.T) { - testLog(0, "TestFromFile") +// TestFromFrame tests streaming from a single H.264 frame which is repeated. +func TestFromFrame(t *testing.T) { + testLog(0, "TestFromFrame") + if testKey == "" { + t.Skip("Skipping TestFromFrame since no RTMP_TEST_KEY") + } s := NewSession(testBaseURL+testKey, testTimeout, testLog) err := s.Open() if err != nil { t.Errorf("Session.Open failed with error: %v", err) } - // This read from a h264 file - if true { - // Open file - f, err := os.Open("../../test/test-data/av/input/betterInput.h264") - if err != nil { - t.Errorf("Cannot open video file: %v", testFile) - } - defer f.Close() + b, err := ioutil.ReadFile(filepath.Join(testDataDir, "AusOcean_logo_1080p.h264")) + if err != nil { + t.Errorf("ReadFile failed with error: %v", err) + } - // Passing rtmp session, true for audio, true for video, and 25 fps - flvEncoder := flv.NewEncoder(s, true, true, 25) - err = lex.H264(flvEncoder, f, time.Second/time.Duration(25)) + // Pass RTMP session, true for audio, true for video, and 25 FPS + flvEncoder := flv.NewEncoder(s, true, true, 25) + for i := 0; i < 25; i++ { + err := flvEncoder.Encode(b) if err != nil { - t.Errorf("Lexing and encoding failed with error: %v", err) - } - - // This reads a single h264 frame and sends - } else { - b, err := ioutil.ReadFile("/home/saxon/Downloads/ausoceanFrame.h264") // b has type []byte - if err != nil { - t.Errorf("Could not read file, failed with error: %v", err) - } - flvEncoder := flv.NewEncoder(s, true, true, 25) - for i := 0; i < 10000; i++ { - err := flvEncoder.Encode(b) - if err != nil { - t.Errorf("Encoding failed!") - } + t.Errorf("Encoding failed with error: %v", err) } } @@ -190,3 +179,39 @@ func TestFromFile(t *testing.T) { t.Errorf("Session.Close failed with error: %v", err) } } + +// TestFromFile tests streaming from an video file comprising raw H.264. +// The test file is supplied via the RTMP_TEST_FILE environment variable. +func TestFromFile(t *testing.T) { + testLog(0, "TestFromFile") + testFile := os.Getenv("RTMP_TEST_FILE") + if testFile == "" { + t.Skip("Skipping TestFromFile since no RTMP_TEST_FILE") + } + if testKey == "" { + t.Skip("Skipping TestFromFile since no RTMP_TEST_KEY") + } + s := NewSession(testBaseURL+testKey, testTimeout, testLog) + err := s.Open() + if err != nil { + t.Errorf("Session.Open failed with error: %v", err) + } + + f, err := os.Open(testFile) + if err != nil { + t.Errorf("Open failed with error: %v", err) + } + defer f.Close() + + // Pass RTMP session, true for audio, true for video, and 25 FPS + flvEncoder := flv.NewEncoder(s, true, true, 25) + err = lex.H264(flvEncoder, f, time.Second/time.Duration(25)) + if err != nil { + t.Errorf("Lexing and encoding failed with error: %v", err) + } + + err = s.Close() + if err != nil { + t.Errorf("Session.Close failed with error: %v", err) + } +} From 3c83ba0022dddd8f8a6905ffe49d64bc5431cf42 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 16:34:00 +1030 Subject: [PATCH 104/137] Added RTMP_PACKET_SIZE_AUTO. --- rtmp/packet.go | 19 ++++++++++++++++++- rtmp/rtmp_headers.go | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 6d33731c..c3dff96b 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -60,6 +60,7 @@ const ( RTMP_PACKET_SIZE_MEDIUM = 1 RTMP_PACKET_SIZE_SMALL = 2 RTMP_PACKET_SIZE_MINIMUM = 3 + RTMP_PACKET_SIZE_AUTO = 4 ) const ( @@ -260,9 +261,25 @@ func readPacket(s *Session, pkt *packet) error { // resizePacket adjusts the packet's storage to accommodate a body of the given size. func resizePacket(pkt *packet, size uint32, ht uint8) { buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) - pkt.headerType = ht pkt.header = buf pkt.body = buf[RTMP_MAX_HEADER_SIZE:] + if ht != RTMP_PACKET_SIZE_AUTO { + pkt.headerType = ht + return + } + switch pkt.packetType { + case RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_AUDIO: + if pkt.timestamp == 0 { + pkt.headerType = RTMP_PACKET_SIZE_LARGE + } else { + pkt.headerType = RTMP_PACKET_SIZE_MEDIUM + } + case RTMP_PACKET_TYPE_INFO: + pkt.headerType = RTMP_PACKET_SIZE_LARGE + pkt.bodySize += 16 + default: + pkt.headerType = RTMP_PACKET_SIZE_MEDIUM + } } // sendPacket sends a packet. diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index becb48be..d50675b5 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -91,7 +91,7 @@ const ( RTMP_BUFFER_CACHE_SIZE = (16 * 1024) RTMP_SIG_SIZE = 1536 RTMP_LARGE_HEADER_SIZE = 12 - RTMP_MAX_HEADER_SIZE = 18 + RTMP_MAX_HEADER_SIZE = RTMP_LARGE_HEADER_SIZE ) type link struct { From 7fe8356b11a969682999a7dace94e11d4ac4511a Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 10 Jan 2019 17:26:48 +1030 Subject: [PATCH 105/137] mts: nonembeded mutex in TimeLocation --- stream/mts/encoder.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 7bc511ed..a5550644 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -127,37 +127,39 @@ const ( // timeLocation holds time and location data type timeLocation struct { - sync.RWMutex + mu sync.RWMutex time uint64 location string } // SetTimeStamp sets the time field of a TimeLocation. func (tl *timeLocation) SetTimeStamp(t uint64) { - tl.Lock() + tl.mu.Lock() tl.time = t - tl.Unlock() + tl.mu.Unlock() } // GetTimeStamp returns the location of a TimeLocation. func (tl *timeLocation) TimeStamp() uint64 { - tl.RLock() - defer tl.RUnlock() - return tl.time + tl.mu.RLock() + t := tl.time + tl.mu.RUnlock() + return t } // SetLocation sets the location of a TimeLocation. func (tl *timeLocation) SetLocation(l string) { - tl.Lock() + tl.mu.Lock() tl.location = l - tl.Unlock() + tl.mu.Unlock() } // GetLocation returns the location of a TimeLocation. func (tl *timeLocation) Location() string { - tl.RLock() - defer tl.RUnlock() - return tl.location + tl.mu.RLock() + l := tl.location + tl.mu.RUnlock() + return l } // MetData will hold time and location data which may be set externally if From 4b09a4f60a00d1d0f904cdbdbd1a257452260227 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 10 Jan 2019 17:31:14 +1030 Subject: [PATCH 106/137] psi: trim() => trimTo() --- stream/mts/psi/helpers.go | 2 +- stream/mts/psi/psi_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index 15029e36..f49754ed 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -113,7 +113,7 @@ func UpdateLocation(d []byte, s string) error { return nil } -func trim(d []byte, t byte) []byte { +func trimTo(d []byte, t byte) []byte { for i, b := range d { if b == t { return d[:i] diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index d3380d9a..0397bc33 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -358,7 +358,7 @@ func TestLocationUpdate(t *testing.T) { func TestTrim(t *testing.T) { test := []byte{0xa3, 0x01, 0x03, 0x00, 0xde} want := []byte{0xa3, 0x01, 0x03} - got := trim(test, 0x00) + got := trimTo(test, 0x00) if !bytes.Equal(got, want) { t.Errorf(errCmp, "TestTrim", want, got) } From b6c0433476f539f575706254656ad167a7050915 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 10 Jan 2019 17:32:42 +1030 Subject: [PATCH 107/137] psi: made some global vars for pat and pmt tables unexported in psi_test.go --- stream/mts/psi/psi_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 0397bc33..96d9f5d5 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -34,7 +34,7 @@ import ( // Some common manifestations of PSI var ( // PSI struct to represent basic pat - StandardPat = PSI{ + standardPat = PSI{ Pf: 0x00, Tid: 0x00, Ssi: true, @@ -54,7 +54,7 @@ var ( } // PSI struct to represent basic pmt without descriptors for time and location - StandardPmt = PSI{ + standardPmt = PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, @@ -78,7 +78,7 @@ var ( } // Std pmt with time and location descriptors, time and location fields are zeroed out - StandardPmtTimeLocation = PSI{ + standardPmtTimeLocation = PSI{ Pf: 0x00, Tid: 0x02, Ssi: true, From f752712deccb233e100c8760547807b367f91c46 Mon Sep 17 00:00:00 2001 From: saxon Date: Thu, 10 Jan 2019 17:34:54 +1030 Subject: [PATCH 108/137] psi: made some comments in psi_test.go more english like and corrected some errors --- stream/mts/psi/psi_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 96d9f5d5..5f2e20a5 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -33,7 +33,7 @@ import ( // Some common manifestations of PSI var ( - // PSI struct to represent basic pat + // standardPat is a minimal PAT. standardPat = PSI{ Pf: 0x00, Tid: 0x00, @@ -53,7 +53,7 @@ var ( }, } - // PSI struct to represent basic pmt without descriptors for time and location + // standardPmt is a minimal PMT, without time and location descriptors. standardPmt = PSI{ Pf: 0x00, Tid: 0x02, @@ -77,7 +77,7 @@ var ( }, } - // Std pmt with time and location descriptors, time and location fields are zeroed out + // standardPmtTimeLocation is a minimal PMT with time and location descriptors. standardPmtTimeLocation = PSI{ Pf: 0x00, Tid: 0x02, @@ -189,14 +189,14 @@ var bytesTests = []struct { // Pat test { name: "pat Bytes()", - input: StandardPat, + input: standardPat, want: StandardPatBytes, }, // Pmt test data no descriptor { name: "pmt to Bytes() without descriptors", - input: StandardPmt, + input: standardPmt, want: StandardPmtBytes, }, @@ -326,7 +326,7 @@ func TestTimeGet(t *testing.T) { // TestLocationGet checks that we can correctly get location data from a pmt table func TestLocationGet(t *testing.T) { - pb := StandardPmtTimeLocation.Bytes() + pb := standardPmtTimeLocation.Bytes() err := UpdateLocation(pb, locationTstStr1) if err != nil { t.Errorf("Error for TestLocationGet UpdateLocation(pb, locationTstStr1): %v", err) From e6504734d0692c5ce087c92c6a171e49b6c2dbfa Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 21:22:00 +1030 Subject: [PATCH 109/137] Rate limit TestFromFrame output to 25 FPS. --- rtmp/rtmp_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index 296809c6..949ebeef 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -172,6 +172,7 @@ func TestFromFrame(t *testing.T) { if err != nil { t.Errorf("Encoding failed with error: %v", err) } + time.Sleep(40 * time.Millisecond) // rate limit to 1/25s } err = s.Close() From 56a3bf8b26d4c753e987b4b5c2a90491fd901982 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 22:52:43 +1030 Subject: [PATCH 110/137] Simplified Session.write(). --- rtmp/rtmp.go | 86 ++++++++++------------------------------------------ 1 file changed, 16 insertions(+), 70 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 2c6300d5..ea5d05cb 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -130,7 +130,7 @@ var ( errTinyPacket = errors.New("rtmp: packet too small") errEncoding = errors.New("rtmp: encoding error") errDecoding = errors.New("rtmp: decoding error") - errCopying = errors.New("rtmp: copying error") + errUnimplemented = errors.New("rtmp: unimplemented feature") ) // setupURL parses the RTMP URL. @@ -861,76 +861,22 @@ func handshake(s *Session) error { // write prepares data to write then sends it. func (s *Session) write(buf []byte) error { + if buf[0] == RTMP_PACKET_TYPE_INFO || (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V') { + return errUnimplemented + } + if len(buf) < minDataSize { + return errTinyPacket + } + pkt := packet{ - channel: RTMP_CHANNEL_SOURCE, - info: s.streamID, + packetType: buf[0], + bodySize: C_AMF_DecodeInt24(buf[1:4]), + timestamp: C_AMF_DecodeInt24(buf[4:7]) | uint32(buf[7])<<24, + channel: RTMP_CHANNEL_SOURCE, + info: s.streamID, } - var enc []byte - for len(buf) != 0 { - if pkt.bytesRead == 0 { - if len(buf) < minDataSize { - return errTinyPacket - } - - if buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V' { - buf = buf[13:] - } - - pkt.packetType = buf[0] - buf = buf[1:] - pkt.bodySize = C_AMF_DecodeInt24(buf[:3]) - buf = buf[3:] - pkt.timestamp = C_AMF_DecodeInt24(buf[:3]) - buf = buf[3:] - pkt.timestamp |= uint32(buf[0]) << 24 - buf = buf[4:] - - headerType := uint8(RTMP_PACKET_SIZE_MEDIUM) - switch pkt.packetType { - case RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_AUDIO: - if pkt.timestamp == 0 { - headerType = RTMP_PACKET_SIZE_LARGE - } - case RTMP_PACKET_TYPE_INFO: - headerType = RTMP_PACKET_SIZE_LARGE - pkt.bodySize += 16 - } - - resizePacket(&pkt, pkt.bodySize, headerType) - - enc = pkt.body[:pkt.bodySize] - if pkt.packetType == RTMP_PACKET_TYPE_INFO { - enc = C_AMF_EncodeString(enc, setDataFrame) - if enc == nil { - return errEncoding - } - pkt.bytesRead = uint32(len(pkt.body) - len(enc)) - } - - } else { - enc = pkt.body[:pkt.bodySize][pkt.bytesRead:] - } - num := int(pkt.bodySize - pkt.bytesRead) - if num > len(buf) { - num = len(buf) - } - - copy(enc[:num], buf[:num]) - pkt.bytesRead += uint32(num) - buf = buf[num:] - if pkt.bytesRead == pkt.bodySize { - err := sendPacket(s, &pkt, false) - pkt.body = nil - pkt.bytesRead = 0 - if err != nil { - return err - } - if len(buf) < 4 { - return nil - } - buf = buf[4:] - } - } - return nil + resizePacket(&pkt, pkt.bodySize, RTMP_PACKET_SIZE_AUTO) + copy(pkt.body, buf[minDataSize:minDataSize+pkt.bodySize]) + return sendPacket(s, &pkt, false) } From 26e8133a6e9de0042d12454cc8d3a097d4f76bbe Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 23:05:24 +1030 Subject: [PATCH 111/137] Merge Session.write() into Session.Write(). --- rtmp/rtmp.go | 22 ---------------------- rtmp/session.go | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index ea5d05cb..1216058c 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -858,25 +858,3 @@ func handshake(s *Session) error { } return nil } - -// write prepares data to write then sends it. -func (s *Session) write(buf []byte) error { - if buf[0] == RTMP_PACKET_TYPE_INFO || (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V') { - return errUnimplemented - } - if len(buf) < minDataSize { - return errTinyPacket - } - - pkt := packet{ - packetType: buf[0], - bodySize: C_AMF_DecodeInt24(buf[1:4]), - timestamp: C_AMF_DecodeInt24(buf[4:7]) | uint32(buf[7])<<24, - channel: RTMP_CHANNEL_SOURCE, - info: s.streamID, - } - - resizePacket(&pkt, pkt.bodySize, RTMP_PACKET_SIZE_AUTO) - copy(pkt.body, buf[minDataSize:minDataSize+pkt.bodySize]) - return sendPacket(s, &pkt, false) -} diff --git a/rtmp/session.go b/rtmp/session.go index a402a320..6a994919 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -159,7 +159,24 @@ func (s *Session) Write(data []byte) (int, error) { if !s.isConnected() { return 0, errNotConnected } - err := s.write(data) + if data[0] == RTMP_PACKET_TYPE_INFO || (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { + return 0, errUnimplemented + } + if len(data) < minDataSize { + return 0, errTinyPacket + } + + pkt := packet{ + packetType: data[0], + bodySize: C_AMF_DecodeInt24(data[1:4]), + timestamp: C_AMF_DecodeInt24(data[4:7]) | uint32(data[7])<<24, + channel: RTMP_CHANNEL_SOURCE, + info: s.streamID, + } + + resizePacket(&pkt, pkt.bodySize, RTMP_PACKET_SIZE_AUTO) + copy(pkt.body, data[minDataSize:minDataSize+pkt.bodySize]) + err := sendPacket(s, &pkt, false) if err != nil { return 0, err } From 6a8e78a2569701a4a77e2c31a0f27f2d24129574 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 23:16:20 +1030 Subject: [PATCH 112/137] readN()/writeN() now Session.read()/write() respectfully. --- rtmp/packet.go | 18 +++++++++--------- rtmp/rtmp.go | 45 +++++---------------------------------------- rtmp/session.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index c3dff96b..0c272657 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -35,8 +35,8 @@ LICENSE package rtmp import ( - "io" "encoding/binary" + "io" ) const ( @@ -104,7 +104,7 @@ func readPacket(s *Session, pkt *packet) error { var hbuf [RTMP_MAX_HEADER_SIZE]byte header := hbuf[:] - err := readN(s, header[:1]) + err := s.read(header[:1]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 1st byte", "error", err.Error()) if err == io.EOF { @@ -118,7 +118,7 @@ func readPacket(s *Session, pkt *packet) error { switch { case pkt.channel == 0: - err = readN(s, header[:1]) + err = s.read(header[:1]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 2nd byte", "error", err.Error()) return err @@ -127,7 +127,7 @@ func readPacket(s *Session, pkt *packet) error { pkt.channel = int32(header[0]) + 64 case pkt.channel == 1: - err = readN(s, header[:2]) + err = s.read(header[:2]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 3rd byte", "error", err.Error()) return err @@ -171,7 +171,7 @@ func readPacket(s *Session, pkt *packet) error { size-- if size > 0 { - err = readN(s, header[:size]) + err = s.read(header[:size]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header", "error", err.Error()) return err @@ -197,7 +197,7 @@ func readPacket(s *Session, pkt *packet) error { extendedTimestamp := pkt.timestamp == 0xffffff if extendedTimestamp { - err = readN(s, header[size:size+4]) + err = s.read(header[size : size+4]) if err != nil { s.log(DebugLevel, pkg+"failed to read extended timestamp", "error", err.Error()) return err @@ -224,7 +224,7 @@ func readPacket(s *Session, pkt *packet) error { pkt.chunk.data = pkt.body[pkt.bytesRead : pkt.bytesRead+uint32(chunkSize)] } - err = readN(s, pkt.body[pkt.bytesRead:][:chunkSize]) + err = s.read(pkt.body[pkt.bytesRead:][:chunkSize]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet body", "error", err.Error()) return err @@ -418,7 +418,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { s.log(DebugLevel, pkg+"sending packet", "size", size, "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr()) if s.deferred != nil && len(s.deferred)+size+hSize > chunkSize { - err := writeN(s, s.deferred) + err := s.write(s.deferred) if err != nil { return err } @@ -441,7 +441,7 @@ func sendPacket(s *Session, pkt *packet, queue bool) error { // Prepend the previously deferred packet and write it with the current one. bytes = append(s.deferred, bytes...) } - err := writeN(s, bytes) + err := s.write(bytes) if err != nil { return err } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 1216058c..b38a1a1a 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -38,7 +38,6 @@ import ( "bytes" "encoding/binary" "errors" - "io" "math/rand" "net" "strconv" @@ -276,40 +275,6 @@ func handlePacket(s *Session, pkt *packet) error { return nil } -func readN(s *Session, buf []byte) error { - err := s.link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) - if err != nil { - return err - } - n, err := io.ReadFull(s.link.conn, buf) - if err != nil { - s.log(DebugLevel, pkg+"read failed", "error", err.Error()) - return err - } - s.nBytesIn += int32(n) - if s.nBytesIn > (s.nBytesInSent + s.clientBW/10) { - err := sendBytesReceived(s) - if err != nil { - return err - } - } - return nil -} - -func writeN(s *Session, buf []byte) error { - //ToDo: consider using a different timeout for writes than for reads - err := s.link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) - if err != nil { - return err - } - _, err = s.link.conn.Write(buf) - if err != nil { - s.log(WarnLevel, pkg+"write failed", "error", err.Error()) - return err - } - return nil -} - func sendConnectPacket(s *Session) error { var pbuf [4096]byte pkt := packet{ @@ -817,14 +782,14 @@ func handshake(s *Session) error { clientsig[i] = byte(rand.Intn(256)) } - err := writeN(s, clientbuf[:]) + err := s.write(clientbuf[:]) if err != nil { return err } s.log(DebugLevel, pkg+"handshake sent") var typ [1]byte - err = readN(s, typ[:]) + err = s.read(typ[:]) if err != nil { return err } @@ -833,7 +798,7 @@ func handshake(s *Session) error { if typ[0] != clientbuf[0] { s.log(WarnLevel, pkg+"handshake type mismatch", "sent", clientbuf[0], "received", typ) } - err = readN(s, serversig[:]) + err = s.read(serversig[:]) if err != nil { return err } @@ -843,12 +808,12 @@ func handshake(s *Session) error { s.log(DebugLevel, pkg+"server uptime", "uptime", suptime) // 2nd part of handshake - err = writeN(s, serversig[:]) + err = s.write(serversig[:]) if err != nil { return err } - err = readN(s, serversig[:]) + err = s.read(serversig[:]) if err != nil { return err } diff --git a/rtmp/session.go b/rtmp/session.go index 6a994919..2aa62fe0 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -33,6 +33,11 @@ LICENSE */ package rtmp +import ( + "io" + "time" +) + // Session holds the state for an RTMP session. type Session struct { url string @@ -183,6 +188,43 @@ func (s *Session) Write(data []byte) (int, error) { return len(data), nil } +// I/O functions +// read from an RTMP connection. +func (s *Session) read(buf []byte) error { + err := s.link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) + if err != nil { + return err + } + n, err := io.ReadFull(s.link.conn, buf) + if err != nil { + s.log(DebugLevel, pkg+"read failed", "error", err.Error()) + return err + } + s.nBytesIn += int32(n) + if s.nBytesIn > (s.nBytesInSent + s.clientBW/10) { + err := sendBytesReceived(s) + if err != nil { + return err + } + } + return nil +} + +// write to an RTMP connection. +func (s *Session) write(buf []byte) error { + //ToDo: consider using a different timeout for writes than for reads + err := s.link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) + if err != nil { + return err + } + _, err = s.link.conn.Write(buf) + if err != nil { + s.log(WarnLevel, pkg+"write failed", "error", err.Error()) + return err + } + return nil +} + // isConnected returns true if the RTMP connection is up. func (s *Session) isConnected() bool { return s.link.conn != nil From 058cc4135686eb7803e2b68424a3728b1e3e8607 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 23:29:51 +1030 Subject: [PATCH 113/137] readPacket(), sendPacket() and resizePacket() now Packet methods read(), write() and resize() respectively. --- rtmp/packet.go | 14 +++++++------- rtmp/rtmp.go | 20 ++++++++++---------- rtmp/session.go | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 0c272657..99c9e029 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -99,8 +99,8 @@ type chunk struct { } // ToDo: Consider making the following functions into methods. -// readPacket reads a packet. -func readPacket(s *Session, pkt *packet) error { +// read reads a packet. +func (pkt *packet) read(s *Session) error { var hbuf [RTMP_MAX_HEADER_SIZE]byte header := hbuf[:] @@ -208,7 +208,7 @@ func readPacket(s *Session, pkt *packet) error { } if pkt.bodySize > 0 && pkt.body == nil { - resizePacket(pkt, pkt.bodySize, (hbuf[0]&0xc0)>>6) + pkt.resize(pkt.bodySize, (hbuf[0]&0xc0)>>6) } toRead := int32(pkt.bodySize - pkt.bytesRead) @@ -258,8 +258,8 @@ func readPacket(s *Session, pkt *packet) error { return nil } -// resizePacket adjusts the packet's storage to accommodate a body of the given size. -func resizePacket(pkt *packet, size uint32, ht uint8) { +// resize adjusts the packet's storage to accommodate a body of the given size. +func (pkt *packet) resize(size uint32, ht uint8) { buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) pkt.header = buf pkt.body = buf[RTMP_MAX_HEADER_SIZE:] @@ -282,8 +282,8 @@ func resizePacket(pkt *packet, size uint32, ht uint8) { } } -// sendPacket sends a packet. -func sendPacket(s *Session, pkt *packet, queue bool) error { +// write sends a packet. +func (pkt *packet) write(s *Session, queue bool) error { var prevPkt *packet var last int diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index b38a1a1a..791c1b93 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -193,7 +193,7 @@ func connectStream(s *Session) error { var err error for !s.isPlaying && s.isConnected() { pkt := packet{} - err = readPacket(s, &pkt) + err = pkt.read(s) if err != nil { break } @@ -389,7 +389,7 @@ func sendConnectPacket(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, true) // response expected + return pkt.write(s, true) // response expected } func sendCreateStream(s *Session) error { @@ -417,7 +417,7 @@ func sendCreateStream(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, true) // response expected + return pkt.write(s, true) // response expected } func sendReleaseStream(s *Session) error { @@ -448,7 +448,7 @@ func sendReleaseStream(s *Session) error { } pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, false) + return pkt.write(s, false) } func sendFCPublish(s *Session) error { @@ -480,7 +480,7 @@ func sendFCPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, false) + return pkt.write(s, false) } func sendFCUnpublish(s *Session) error { @@ -512,7 +512,7 @@ func sendFCUnpublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, false) + return pkt.write(s, false) } func sendPublish(s *Session) error { @@ -548,7 +548,7 @@ func sendPublish(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, true) // response expected + return pkt.write(s, true) // response expected } func sendDeleteStream(s *Session, dStreamId float64) error { @@ -580,7 +580,7 @@ func sendDeleteStream(s *Session, dStreamId float64) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) /* no response expected */ - return sendPacket(s, &pkt, false) + return pkt.write(s, false) } // sendBytesReceived tells the server how many bytes the client has received. @@ -602,7 +602,7 @@ func sendBytesReceived(s *Session) error { } pkt.bodySize = 4 - return sendPacket(s, &pkt, false) + return pkt.write(s, false) } func sendCheckBW(s *Session) error { @@ -630,7 +630,7 @@ func sendCheckBW(s *Session) error { pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) - return sendPacket(s, &pkt, false) + return pkt.write(s, false) } func eraseMethod(m []method, i int) []method { diff --git a/rtmp/session.go b/rtmp/session.go index 2aa62fe0..e09b3caa 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -179,9 +179,9 @@ func (s *Session) Write(data []byte) (int, error) { info: s.streamID, } - resizePacket(&pkt, pkt.bodySize, RTMP_PACKET_SIZE_AUTO) + pkt.resize(pkt.bodySize, RTMP_PACKET_SIZE_AUTO) copy(pkt.body, data[minDataSize:minDataSize+pkt.bodySize]) - err := sendPacket(s, &pkt, false) + err := pkt.write(s, false) if err != nil { return 0, err } From f635be6712e8c5979f7fc18e162c8f96d799f911 Mon Sep 17 00:00:00 2001 From: scruzin Date: Thu, 10 Jan 2019 23:48:11 +1030 Subject: [PATCH 114/137] Propagate errors from handleInvoke(). --- rtmp/rtmp.go | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 791c1b93..4d432a00 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -121,6 +121,7 @@ var ( errUnknownScheme = errors.New("rtmp: unknown scheme") errConnected = errors.New("rtmp: already connected") errNotConnected = errors.New("rtmp: not connected") + errNotWritable = errors.New("rtmp: connection not writable") errHandshake = errors.New("rtmp: handshake failed") errConnSend = errors.New("rtmp: connection send error") errConnStream = errors.New("rtmp: connection stream error") @@ -675,27 +676,31 @@ func handleInvoke(s *Session, body []byte) error { case av_connect: if s.link.token != "" { s.log(FatalLevel, pkg+"no support for link token") - } - if (s.link.protocol & RTMP_FEATURE_WRITE) != 0 { - sendReleaseStream(s) - sendFCPublish(s) - } else { - s.log(FatalLevel, pkg+"link protocol has no RTMP_FEATURE_WRITE") - } - - sendCreateStream(s) if (s.link.protocol & RTMP_FEATURE_WRITE) == 0 { - s.log(FatalLevel, pkg+"link protocol has no RTMP_FEATURE_WRITE") + return errNotWritable + } + err := sendReleaseStream(s) + if err != nil { + return err + } + err = sendFCPublish(s) + if err != nil { + return err + } + err = sendCreateStream(s) + if err != nil { + return err } case av_createStream: s.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) - - if s.link.protocol&RTMP_FEATURE_WRITE != 0 { - sendPublish(s) - } else { - s.log(FatalLevel, pkg+"link protocol has no RTMP_FEATURE_WRITE") + if s.link.protocol&RTMP_FEATURE_WRITE == 0 { + return errNotWritable + } + err := sendPublish(s) + if err != nil { + return err } case av_play, av_publish: @@ -704,7 +709,10 @@ func handleInvoke(s *Session, body []byte) error { case av_onBWDone: if s.checkCounter == 0 { // ToDo: why is this always zero? - sendCheckBW(s) + err := sendCheckBW(s) + if err != nil { + return err + } } case av_onFCUnsubscribe, av_onFCSubscribe: From 53bccaaaa75897e916149d312ec50d52735ae868 Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:01:26 +1030 Subject: [PATCH 115/137] Simplifed connectStream(). --- rtmp/rtmp.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 4d432a00..a481aa84 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -192,22 +192,19 @@ func connect(s *Session) error { // connectStream reads a packet and handles it func connectStream(s *Session) error { var err error - for !s.isPlaying && s.isConnected() { + for !s.isPlaying { pkt := packet{} err = pkt.read(s) if err != nil { break } - if pkt.bodySize == 0 { - continue - } - if pkt.packetType == RTMP_PACKET_TYPE_AUDIO || - pkt.packetType == RTMP_PACKET_TYPE_VIDEO || - pkt.packetType == RTMP_PACKET_TYPE_INFO { - s.log(DebugLevel, pkg+"got packet before play; ignoring") - continue - } + switch pkt.packetType { + case RTMP_PACKET_TYPE_AUDIO, RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_INFO: + s.log(WarnLevel, pkg+"got packet before play; ignoring", "type", pkt.packetType) + default: + handlePacket(s, &pkt) // ignore errors + } err = handlePacket(s, &pkt) if err != nil { break From b388f5374f4ef105b39289484e4f317029842eda Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:06:44 +1030 Subject: [PATCH 116/137] Removed superfluous url param from setupURL() and obtain it from the Session object instead. --- rtmp/rtmp.go | 6 +++--- rtmp/rtmp_test.go | 2 +- rtmp/session.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index a481aa84..bacf300a 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -134,8 +134,8 @@ var ( ) // setupURL parses the RTMP URL. -func setupURL(s *Session, addr string) (err error) { - s.link.protocol, s.link.host, s.link.port, s.link.app, s.link.playpath, err = parseURL(addr) +func setupURL(s *Session) (err error) { + s.link.protocol, s.link.host, s.link.port, s.link.app, s.link.playpath, err = parseURL(s.url) if err != nil { return err } @@ -145,7 +145,7 @@ func setupURL(s *Session, addr string) (err error) { s.link.tcUrl = rtmpProtocolStrings[s.link.protocol] + "://" + s.link.host + ":" + strconv.Itoa(int(s.link.port)) + "/" + s.link.app s.link.lFlags |= RTMP_LF_FTCU } else { - s.link.tcUrl = addr + s.link.tcUrl = s.url } } diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index 949ebeef..6e07535a 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -113,7 +113,7 @@ func TestSetupURL(t *testing.T) { if s.url != testBaseURL && s.link.timeout != testTimeout { t.Errorf("NewSession failed") } - err := setupURL(s, s.url) + err := setupURL(s) if err != nil { t.Errorf("setupURL(testBaseURL) failed with error: %v", err) } diff --git a/rtmp/session.go b/rtmp/session.go index e09b3caa..4b2538a5 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -113,7 +113,7 @@ func (s *Session) Open() error { // start does the heavylifting for Open(). func (s *Session) start() error { s.log(DebugLevel, pkg+"Session.start") - err := setupURL(s, s.url) + err := setupURL(s) if err != nil { s.close() return err From 7bb9fcc2c7a625180e933f072c9a6fdfd07abda4 Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:10:29 +1030 Subject: [PATCH 117/137] Remove superfluous call to handlePacket() from connectStream(). --- rtmp/rtmp.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index bacf300a..e200387a 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -203,11 +203,10 @@ func connectStream(s *Session) error { case RTMP_PACKET_TYPE_AUDIO, RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_INFO: s.log(WarnLevel, pkg+"got packet before play; ignoring", "type", pkt.packetType) default: - handlePacket(s, &pkt) // ignore errors - } - err = handlePacket(s, &pkt) - if err != nil { - break + err = handlePacket(s, &pkt) + if err != nil { + break + } } } @@ -221,7 +220,6 @@ func connectStream(s *Session) error { // NB: cases have been commented out that are not currently used by AusOcean func handlePacket(s *Session, pkt *packet) error { switch pkt.packetType { - case RTMP_PACKET_TYPE_CHUNK_SIZE: if pkt.bodySize >= 4 { s.inChunkSize = int32(C_AMF_DecodeInt32(pkt.body[:4])) From 94446beddbc9ce48c333dfaf88f71c40eeb6cb4f Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:19:50 +1030 Subject: [PATCH 118/137] Split remaining code in rtmp_headers across rtmp.go and session.go. --- rtmp/rtmp.go | 59 ++++++++++++++++++++++++++++++- rtmp/rtmp_headers.go | 82 -------------------------------------------- rtmp/session.go | 29 +++++++++++++++- 3 files changed, 86 insertions(+), 84 deletions(-) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index e200387a..e9392019 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -3,7 +3,7 @@ NAME rtmp.go DESCRIPTION - See Readme.md + RTMP command functionality. AUTHORS Saxon Nelson-Milton @@ -49,6 +49,63 @@ const ( minDataSize = 11 ) +const ( + RTMPT_OPEN = iota + RTMPT_SEND + RTMPT_IDLE + RTMPT_CLOSE +) + +const ( + RTMP_READ_HEADER = 0x01 + RTMP_READ_RESUME = 0x02 + RTMP_READ_NO_IGNORE = 0x04 + RTMP_READ_GOTKF = 0x08 + RTMP_READ_GOTFLVK = 0x10 + RTMP_READ_SEEKING = 0x20 + RTMP_READ_COMPLETE = -3 + RTMP_READ_ERROR = -2 + RTMP_READ_EOF = -1 + RTMP_READ_IGNORE = 0 +) + +const ( + RTMP_LF_AUTH = 0x0001 /* using auth param */ + RTMP_LF_LIVE = 0x0002 /* stream is live */ + RTMP_LF_SWFV = 0x0004 /* do SWF verification */ + RTMP_LF_PLST = 0x0008 /* send playlist before play */ + RTMP_LF_BUFX = 0x0010 /* toggle stream on BufferEmpty msg */ + RTMP_LF_FTCU = 0x0020 /* free tcUrl on close */ + RTMP_LF_FAPU = 0x0040 /* free app on close */ +) + +const ( + RTMP_FEATURE_HTTP = 0x01 + RTMP_FEATURE_ENC = 0x02 + RTMP_FEATURE_SSL = 0x04 + RTMP_FEATURE_MFP = 0x08 /* not yet supported */ + RTMP_FEATURE_WRITE = 0x10 /* publish, not play */ + RTMP_FEATURE_HTTP2 = 0x20 /* server-side rtmpt */ +) + +const ( + RTMP_PROTOCOL_RTMP = 0 + RTMP_PROTOCOL_RTMPE = RTMP_FEATURE_ENC + RTMP_PROTOCOL_RTMPT = RTMP_FEATURE_HTTP + RTMP_PROTOCOL_RTMPS = RTMP_FEATURE_SSL + RTMP_PROTOCOL_RTMPTE = (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC) + RTMP_PROTOCOL_RTMPTS = (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL) + RTMP_PROTOCOL_RTMFP = RTMP_FEATURE_MFP +) + +const ( + RTMP_DEFAULT_CHUNKSIZE = 128 + RTMP_BUFFER_CACHE_SIZE = (16 * 1024) + RTMP_SIG_SIZE = 1536 + RTMP_LARGE_HEADER_SIZE = 12 + RTMP_MAX_HEADER_SIZE = RTMP_LARGE_HEADER_SIZE +) + const ( setDataFrame = "@setDataFrame" diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go index d50675b5..9c4854c5 100644 --- a/rtmp/rtmp_headers.go +++ b/rtmp/rtmp_headers.go @@ -33,87 +33,5 @@ LICENSE */ package rtmp -import ( - "net" -) -const ( - RTMPT_OPEN = iota - RTMPT_SEND - RTMPT_IDLE - RTMPT_CLOSE -) -const ( - RTMP_READ_HEADER = 0x01 - RTMP_READ_RESUME = 0x02 - RTMP_READ_NO_IGNORE = 0x04 - RTMP_READ_GOTKF = 0x08 - RTMP_READ_GOTFLVK = 0x10 - RTMP_READ_SEEKING = 0x20 - RTMP_READ_COMPLETE = -3 - RTMP_READ_ERROR = -2 - RTMP_READ_EOF = -1 - RTMP_READ_IGNORE = 0 -) - -const ( - RTMP_LF_AUTH = 0x0001 /* using auth param */ - RTMP_LF_LIVE = 0x0002 /* stream is live */ - RTMP_LF_SWFV = 0x0004 /* do SWF verification */ - RTMP_LF_PLST = 0x0008 /* send playlist before play */ - RTMP_LF_BUFX = 0x0010 /* toggle stream on BufferEmpty msg */ - RTMP_LF_FTCU = 0x0020 /* free tcUrl on close */ - RTMP_LF_FAPU = 0x0040 /* free app on close */ -) - -const ( - RTMP_FEATURE_HTTP = 0x01 - RTMP_FEATURE_ENC = 0x02 - RTMP_FEATURE_SSL = 0x04 - RTMP_FEATURE_MFP = 0x08 /* not yet supported */ - RTMP_FEATURE_WRITE = 0x10 /* publish, not play */ - RTMP_FEATURE_HTTP2 = 0x20 /* server-side rtmpt */ -) - -const ( - RTMP_PROTOCOL_RTMP = 0 - RTMP_PROTOCOL_RTMPE = RTMP_FEATURE_ENC - RTMP_PROTOCOL_RTMPT = RTMP_FEATURE_HTTP - RTMP_PROTOCOL_RTMPS = RTMP_FEATURE_SSL - RTMP_PROTOCOL_RTMPTE = (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC) - RTMP_PROTOCOL_RTMPTS = (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL) - RTMP_PROTOCOL_RTMFP = RTMP_FEATURE_MFP -) - -const ( - RTMP_DEFAULT_CHUNKSIZE = 128 - RTMP_BUFFER_CACHE_SIZE = (16 * 1024) - RTMP_SIG_SIZE = 1536 - RTMP_LARGE_HEADER_SIZE = 12 - RTMP_MAX_HEADER_SIZE = RTMP_LARGE_HEADER_SIZE -) - -type link struct { - host string - playpath string - tcUrl string - swfUrl string - pageUrl string - app string - auth string - flashVer string - token string - extras C_AMFObject - lFlags int32 - swfAge int32 - protocol int32 - timeout uint - port uint16 - conn *net.TCPConn -} - -type method struct { - name string - num int32 -} diff --git a/rtmp/session.go b/rtmp/session.go index 4b2538a5..2ea8e05f 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -3,7 +3,7 @@ NAME session.go DESCRIPTION - See Readme.md + RTMP session functionality. AUTHORS Saxon Nelson-Milton @@ -35,6 +35,7 @@ package rtmp import ( "io" + "net" "time" ) @@ -67,6 +68,32 @@ type Session struct { log Log } +// link represents RTMP URL and connection information. +type link struct { + host string + playpath string + tcUrl string + swfUrl string + pageUrl string + app string + auth string + flashVer string + token string + extras C_AMFObject + lFlags int32 + swfAge int32 + protocol int32 + timeout uint + port uint16 + conn *net.TCPConn +} + +// method represents an RTMP method. +type method struct { + name string + num int32 +} + // Log defines the RTMP logging function. type Log func(level int8, message string, params ...interface{}) From d4a0fad2bed5d3ad5cce9bf0995192048794471f Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:22:32 +1030 Subject: [PATCH 119/137] Removed now unused rtmp_headers.go. --- rtmp/rtmp_headers.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 rtmp/rtmp_headers.go diff --git a/rtmp/rtmp_headers.go b/rtmp/rtmp_headers.go deleted file mode 100644 index 9c4854c5..00000000 --- a/rtmp/rtmp_headers.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -NAME - rtmp_headers.go - -DESCRIPTION - See Readme.md - -AUTHORS - Saxon Nelson-Milton - Dan Kortschak - Alan Noble - -LICENSE - rtmp_headers.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) - - It is free software: you can redistribute it and/or modify them - under the terms of the GNU General Public License as published by the - Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - It is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License - along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. - - Derived from librtmp under the GNU Lesser General Public License 2.1 - Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org - Copyright (C) 2008-2009 Andrej Stepanchuk - Copyright (C) 2009-2010 Howard Chu -*/ -package rtmp - - - From b3b1b048147d764eca8f903556f9d35456494af7 Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:29:51 +1030 Subject: [PATCH 120/137] Merged Session.start()/close() into Open()/Close() respectively. --- rtmp/session.go | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/rtmp/session.go b/rtmp/session.go index 2ea8e05f..aa1ee80b 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -127,63 +127,46 @@ func NewSession(url string, timeout uint, log Log) *Session { // Open establishes an rtmp connection with the url passed into the constructor. func (s *Session) Open() error { + s.log(DebugLevel, pkg+"Session.Open") if s.isConnected() { return errConnected } - err := s.start() - if err != nil { - return err - } - return nil -} - -// start does the heavylifting for Open(). -func (s *Session) start() error { - s.log(DebugLevel, pkg+"Session.start") err := setupURL(s) if err != nil { - s.close() return err } s.enableWrite() err = connect(s) if err != nil { - s.close() + s.Close() return err } err = connectStream(s) if err != nil { - s.close() + s.Close() return err } return nil } -// Close terminates the rtmp connection, +// Close terminates the rtmp connection. +// NB: The session object is cleared completely. func (s *Session) Close() error { + s.log(DebugLevel, pkg+"Session.Close") if !s.isConnected() { return errNotConnected } - s.close() - return nil -} - -// close does the heavylifting for Close(). -// Any errors are ignored as it is often called in response to an earlier error. -func (s *Session) close() { - s.log(DebugLevel, pkg+"Session.close") - if s.isConnected() { - if s.streamID > 0 { - if s.link.protocol&RTMP_FEATURE_WRITE != 0 { - sendFCUnpublish(s) - } - sendDeleteStream(s, float64(s.streamID)) + if s.streamID > 0 { + if s.link.protocol&RTMP_FEATURE_WRITE != 0 { + sendFCUnpublish(s) } - s.link.conn.Close() + sendDeleteStream(s, float64(s.streamID)) } + s.link.conn.Close() *s = Session{} + return nil } // Write writes a frame (flv tag) to the rtmp connection. @@ -216,7 +199,10 @@ func (s *Session) Write(data []byte) (int, error) { } // I/O functions -// read from an RTMP connection. + +// read from an RTMP connection. Sends a bytes received message if the +// number of bytes received (nBytesIn) is greater than the number sent +// (nBytesInSent) by 10% of the bandwidth. func (s *Session) read(buf []byte) error { err := s.link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { From 82522643bb5b645d2f0434a074dc2cde6bab691f Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 00:41:28 +1030 Subject: [PATCH 121/137] First cut at refactoring packet.write(). --- rtmp/packet.go | 70 ++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 99c9e029..4fc2141b 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -3,7 +3,7 @@ NAME packet.go DESCRIPTION - See Readme.md + RTMP packet functionality. AUTHORS Saxon Nelson-Milton @@ -284,10 +284,12 @@ func (pkt *packet) resize(size uint32, ht uint8) { // write sends a packet. func (pkt *packet) write(s *Session, queue bool) error { - var prevPkt *packet - var last int + if pkt.body == nil { + return errInvalidBody + } if pkt.channel >= s.channelsAllocatedOut { + s.log(DebugLevel, pkg+"growing channelsOut", "channel", pkt.channel) n := int(pkt.channel + 10) var pkts []*packet @@ -304,8 +306,9 @@ func (pkt *packet) write(s *Session, queue bool) error { s.channelsAllocatedOut = int32(n) } - prevPkt = s.channelsOut[pkt.channel] + prevPkt := s.channelsOut[pkt.channel] + var last int if prevPkt != nil && pkt.headerType != RTMP_PACKET_SIZE_LARGE { // compress a bit by using the prev packet's attributes if prevPkt.bodySize == pkt.bodySize && prevPkt.packetType == pkt.packetType && pkt.headerType == RTMP_PACKET_SIZE_MEDIUM { @@ -324,20 +327,14 @@ func (pkt *packet) write(s *Session, queue bool) error { return errInvalidHeader } - var headBytes []byte - var origIdx int - if pkt.body != nil { - // Span from -packetsize for the type to the start of the body. - headBytes = pkt.header - origIdx = RTMP_MAX_HEADER_SIZE - headerSizes[pkt.headerType] - } else { - // Allocate a new header and allow 6 bytes of movement backward. - var hbuf [RTMP_MAX_HEADER_SIZE]byte - headBytes = hbuf[:] - origIdx = 6 - } + // The complete packet starts from headerSize _before_ the start the body. + // origIdx is the original offset, which will be 0 for a full (12-byte) header or 11 for a minimum (1-byte) header. + headBytes := pkt.header + hSize := headerSizes[pkt.headerType] + origIdx := RTMP_MAX_HEADER_SIZE - hSize - var cSize int + // adjust 1 or 2 bytes for the channel + cSize := 0 switch { case pkt.channel > 319: cSize = 2 @@ -345,12 +342,12 @@ func (pkt *packet) write(s *Session, queue bool) error { cSize = 1 } - hSize := headerSizes[pkt.headerType] if cSize != 0 { origIdx -= cSize hSize += cSize } + // adjust 4 bytes for the timestamp var ts uint32 if prevPkt != nil { ts = uint32(int(pkt.timestamp) - last) @@ -403,8 +400,8 @@ func (pkt *packet) write(s *Session, queue bool) error { } if headerSizes[pkt.headerType] > 8 { - n := int(encodeInt32LE(headBytes[headerIdx:headerIdx+4], pkt.info)) - headerIdx += n + binary.LittleEndian.PutUint32(headBytes[headerIdx:headerIdx+4], uint32(pkt.info)) + headerIdx += 4 // 32bits } if ts >= 0xffffff { @@ -415,31 +412,38 @@ func (pkt *packet) write(s *Session, queue bool) error { size := int(pkt.bodySize) chunkSize := int(s.outChunkSize) - s.log(DebugLevel, pkg+"sending packet", "size", size, "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr()) - - if s.deferred != nil && len(s.deferred)+size+hSize > chunkSize { - err := s.write(s.deferred) - if err != nil { - return err + if s.deferred == nil { + // Defer sending small audio packets (at most once). + if pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { + s.deferred = headBytes[origIdx:][:size+hSize] + s.log(DebugLevel, pkg+"deferred sending packet", "size", size, "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr()) + return nil + } + } else { + // Send previously deferrd packet if combining it with the next one would exceed the chunk size. + if len(s.deferred)+size+hSize > chunkSize { + s.log(DebugLevel, pkg+"sending deferred packet separately", "size", len(s.deferred)) + err := s.write(s.deferred) + if err != nil { + return err + } + s.deferred = nil } - s.deferred = nil } // TODO(kortschak): Rewrite this horrific peice of premature optimisation. // NB: RTMP wants packets in chunks which are 128 bytes by default, but the server may request a different size. + s.log(DebugLevel, pkg+"sending packet", "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr(), "size", size) for size+hSize != 0 { - if s.deferred == nil && pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { - s.deferred = headBytes[origIdx:][:size+hSize] - s.log(DebugLevel, pkg+"deferred sending packet") - break - } if chunkSize > size { chunkSize = size } bytes := headBytes[origIdx:][:chunkSize+hSize] if s.deferred != nil { // Prepend the previously deferred packet and write it with the current one. + s.log(DebugLevel, pkg+"combining deferred packet", "size", len(s.deferred)) bytes = append(s.deferred, bytes...) + s.deferred = nil } err := s.write(bytes) if err != nil { @@ -481,9 +485,9 @@ func (pkt *packet) write(s *Session, queue bool) error { if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { buf := pkt.body[1:] meth := C_AMF_DecodeString(buf) + s.log(DebugLevel, pkg+"invoking method "+meth) // keep it in call queue till result arrives if queue { - s.log(DebugLevel, pkg+"queuing method "+meth) buf = buf[3+len(meth):] txn := int32(C_AMF_DecodeNumber(buf[:8])) s.methodCalls = append(s.methodCalls, method{name: meth, num: txn}) From 8a23609f6ebb71c1c02f2e64671bc70b59e0f2ec Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 07:24:31 +1030 Subject: [PATCH 122/137] Exit test for errors and fatals errors regardless of testVerbosity. --- rtmp/rtmp_test.go | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index 6e07535a..be4cf276 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -6,6 +6,8 @@ DESCRIPTION RTMP tests AUTHORS + Saxon Nelson-Milton + Dan Kortschak Alan Noble LICENSE @@ -49,20 +51,22 @@ const ( testDataDir = "../../test/test-data/av/input" ) -// testVerbosity controls the amount of output +// testVerbosity controls the amount of output. // NB: This is not the log level, which is DebugLevel. // 0: suppress logging completely // 1: log messages only // 2: log messages with errors, if any var testVerbosity = 1 -// testKey is the RTMP key required for YouTube streaming (RTMP_TEST_KEY env var) +// testKey is the YouTube RTMP key required for YouTube streaming (RTMP_TEST_KEY env var). +// NB: don't share your key with others. var testKey string -// testFile is the test video file (RTMP_TEST_FILE env var) +// testFile is the test video file (RTMP_TEST_FILE env var). +// betterInput.h264 is a good one to use. var testFile string -// testLog is a bare bones logger that logs to stdout. +// testLog is a bare bones logger that logs to stdout, and exits upon either an error or fatal error. func testLog(level int8, msg string, params ...interface{}) { logLevels := [...]string{"Debug", "Info", "Warn", "Error", "", "", "Fatal"} if testVerbosity == 0 { @@ -71,21 +75,28 @@ func testLog(level int8, msg string, params ...interface{}) { if level < -1 || level > 5 { panic("Invalid log level") } - if testVerbosity == 2 && len(params) >= 2 { - // extract the params we know about, otherwise just print the message - switch params[0].(string) { - case "error": - fmt.Printf("%s: %s, error=%v\n", logLevels[level+1], msg, params[1].(string)) - case "size": - fmt.Printf("%s: %s, size=%d\n", logLevels[level+1], msg, params[1].(int)) - default: + switch testVerbosity { + case 0: + // silence is golden + case 1: + fmt.Printf("%s: %s\n", logLevels[level+1], msg) + case 2: + // extract the first param if it is one we care about, otherwise just print the message + if len(params) >= 2 { + switch params[0].(string) { + case "error": + fmt.Printf("%s: %s, error=%v\n", logLevels[level+1], msg, params[1].(string)) + case "size": + fmt.Printf("%s: %s, size=%d\n", logLevels[level+1], msg, params[1].(int)) + default: + fmt.Printf("%s: %s\n", logLevels[level+1], msg) + } + } else { fmt.Printf("%s: %s\n", logLevels[level+1], msg) } - } else { - fmt.Printf("%s: %s\n", logLevels[level+1], msg) } - if level == 5 { - // Fatal + if level >= 4 { + // Error or Fatal buf := make([]byte, 1<<16) size := runtime.Stack(buf, true) fmt.Printf("%s\n", string(buf[:size])) From 5be5aad6cf4c587f310572b3ab4fd588c539f241 Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 09:52:21 +1030 Subject: [PATCH 123/137] Idiomatic names for constants (1st pass). --- rtmp/packet.go | 90 +++++----- rtmp/rtmp.go | 438 +++++++++++++++++++++++------------------------- rtmp/session.go | 12 +- 3 files changed, 258 insertions(+), 282 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 4fc2141b..3c50b59d 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -39,34 +39,37 @@ import ( "io" ) +// Packet types. const ( - RTMP_PACKET_TYPE_CHUNK_SIZE = 0x01 - RTMP_PACKET_TYPE_BYTES_READ_REPORT = 0x03 - RTMP_PACKET_TYPE_CONTROL = 0x04 - RTMP_PACKET_TYPE_SERVER_BW = 0x05 - RTMP_PACKET_TYPE_CLIENT_BW = 0x06 - RTMP_PACKET_TYPE_AUDIO = 0x08 - RTMP_PACKET_TYPE_VIDEO = 0x09 - RTMP_PACKET_TYPE_FLEX_STREAM_SEND = 0x0F - RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT = 0x10 - RTMP_PACKET_TYPE_FLEX_MESSAGE = 0x11 - RTMP_PACKET_TYPE_INFO = 0x12 - RTMP_PACKET_TYPE_INVOKE = 0x14 - RTMP_PACKET_TYPE_FLASH_VIDEO = 0x16 + packetTypeChunkSize = 0x01 + packetTypeBytesReadReport = 0x03 + packetTypeControl = 0x04 + packetTypeServerBW = 0x05 + packetTypeClientBW = 0x06 + packetTypeAudio = 0x08 + packetTypeVideo = 0x09 + packetTypeFlexStreamSend = 0x0F // not implemented + packetTypeFlexSharedObject = 0x10 // not implemented + packetTypeFlexMessage = 0x11 // not implemented + packetTypeInfo = 0x12 + packetTypeInvoke = 0x14 + packetTypeFlashVideo = 0x16 // not implemented ) +// Header sizes. const ( - RTMP_PACKET_SIZE_LARGE = 0 - RTMP_PACKET_SIZE_MEDIUM = 1 - RTMP_PACKET_SIZE_SMALL = 2 - RTMP_PACKET_SIZE_MINIMUM = 3 - RTMP_PACKET_SIZE_AUTO = 4 + headerSizeLarge = 0 + headerSizeMedium = 1 + headerSizeSmall = 2 + headerSizeMinimum = 3 + headerSizeAuto = 4 ) +// Special channels. const ( - RTMP_CHANNEL_BYTES_READ = 0x02 - RTMP_CHANNEL_CONTROL = 0x03 - RTMP_CHANNEL_SOURCE = 0x04 + chanBytesRead = 0x02 + chanControl = 0x03 + chanSource = 0x04 ) // headerSizes defines header sizes for header types 0, 1, 2 and 3 respectively: @@ -95,13 +98,12 @@ type packet struct { type chunk struct { headerSize int32 data []byte - header [RTMP_MAX_HEADER_SIZE]byte + header [fullHeaderSize]byte } -// ToDo: Consider making the following functions into methods. // read reads a packet. func (pkt *packet) read(s *Session) error { - var hbuf [RTMP_MAX_HEADER_SIZE]byte + var hbuf [fullHeaderSize]byte header := hbuf[:] err := s.read(header[:1]) @@ -161,9 +163,9 @@ func (pkt *packet) read(s *Session) error { size := headerSizes[pkt.headerType] switch { - case size == RTMP_LARGE_HEADER_SIZE: + case size == fullHeaderSize: pkt.hasAbsTimestamp = true - case size < RTMP_LARGE_HEADER_SIZE: + case size < fullHeaderSize: if s.channelsIn[pkt.channel] != nil { *pkt = *(s.channelsIn[pkt.channel]) } @@ -219,6 +221,7 @@ func (pkt *packet) read(s *Session) error { } if pkt.chunk != nil { + panic("non-nil chunk") pkt.chunk.headerSize = int32(hSize) copy(pkt.chunk.header[:], hbuf[:hSize]) pkt.chunk.data = pkt.body[pkt.bytesRead : pkt.bytesRead+uint32(chunkSize)] @@ -260,25 +263,25 @@ func (pkt *packet) read(s *Session) error { // resize adjusts the packet's storage to accommodate a body of the given size. func (pkt *packet) resize(size uint32, ht uint8) { - buf := make([]byte, RTMP_MAX_HEADER_SIZE+size) + buf := make([]byte, fullHeaderSize+size) pkt.header = buf - pkt.body = buf[RTMP_MAX_HEADER_SIZE:] - if ht != RTMP_PACKET_SIZE_AUTO { + pkt.body = buf[fullHeaderSize:] + if ht != headerSizeAuto { pkt.headerType = ht return } switch pkt.packetType { - case RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_AUDIO: + case packetTypeVideo, packetTypeAudio: if pkt.timestamp == 0 { - pkt.headerType = RTMP_PACKET_SIZE_LARGE + pkt.headerType = headerSizeLarge } else { - pkt.headerType = RTMP_PACKET_SIZE_MEDIUM + pkt.headerType = headerSizeMedium } - case RTMP_PACKET_TYPE_INFO: - pkt.headerType = RTMP_PACKET_SIZE_LARGE + case packetTypeInfo: + pkt.headerType = headerSizeLarge pkt.bodySize += 16 default: - pkt.headerType = RTMP_PACKET_SIZE_MEDIUM + pkt.headerType = headerSizeMedium } } @@ -309,14 +312,14 @@ func (pkt *packet) write(s *Session, queue bool) error { prevPkt := s.channelsOut[pkt.channel] var last int - if prevPkt != nil && pkt.headerType != RTMP_PACKET_SIZE_LARGE { + if prevPkt != nil && pkt.headerType != headerSizeLarge { // compress a bit by using the prev packet's attributes - if prevPkt.bodySize == pkt.bodySize && prevPkt.packetType == pkt.packetType && pkt.headerType == RTMP_PACKET_SIZE_MEDIUM { - pkt.headerType = RTMP_PACKET_SIZE_SMALL + if prevPkt.bodySize == pkt.bodySize && prevPkt.packetType == pkt.packetType && pkt.headerType == headerSizeMedium { + pkt.headerType = headerSizeSmall } - if prevPkt.timestamp == pkt.timestamp && pkt.headerType == RTMP_PACKET_SIZE_SMALL { - pkt.headerType = RTMP_PACKET_SIZE_MINIMUM + if prevPkt.timestamp == pkt.timestamp && pkt.headerType == headerSizeSmall { + pkt.headerType = headerSizeMinimum } last = int(prevPkt.timestamp) @@ -331,7 +334,7 @@ func (pkt *packet) write(s *Session, queue bool) error { // origIdx is the original offset, which will be 0 for a full (12-byte) header or 11 for a minimum (1-byte) header. headBytes := pkt.header hSize := headerSizes[pkt.headerType] - origIdx := RTMP_MAX_HEADER_SIZE - hSize + origIdx := fullHeaderSize - hSize // adjust 1 or 2 bytes for the channel cSize := 0 @@ -414,7 +417,7 @@ func (pkt *packet) write(s *Session, queue bool) error { if s.deferred == nil { // Defer sending small audio packets (at most once). - if pkt.packetType == RTMP_PACKET_TYPE_AUDIO && size < chunkSize { + if pkt.packetType == packetTypeAudio && size < chunkSize { s.deferred = headBytes[origIdx:][:size+hSize] s.log(DebugLevel, pkg+"deferred sending packet", "size", size, "la", s.link.conn.LocalAddr(), "ra", s.link.conn.RemoteAddr()) return nil @@ -443,7 +446,6 @@ func (pkt *packet) write(s *Session, queue bool) error { // Prepend the previously deferred packet and write it with the current one. s.log(DebugLevel, pkg+"combining deferred packet", "size", len(s.deferred)) bytes = append(s.deferred, bytes...) - s.deferred = nil } err := s.write(bytes) if err != nil { @@ -482,7 +484,7 @@ func (pkt *packet) write(s *Session, queue bool) error { } // We invoked a remote method - if pkt.packetType == RTMP_PACKET_TYPE_INVOKE { + if pkt.packetType == packetTypeInvoke { buf := pkt.body[1:] meth := C_AMF_DecodeString(buf) s.log(DebugLevel, pkg+"invoking method "+meth) diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index e9392019..f48e9e53 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -45,119 +45,94 @@ import ( ) const ( - pkg = "rtmp:" - minDataSize = 11 + pkg = "rtmp:" + minDataSize = 11 // ToDo: this should be the same as fullHeaderSize + signatureSize = 1536 + fullHeaderSize = 12 ) +// Link flags. const ( - RTMPT_OPEN = iota - RTMPT_SEND - RTMPT_IDLE - RTMPT_CLOSE + linkAuth = 0x0001 // using auth param + linkLive = 0x0002 // stream is live + linkSWF = 0x0004 // do SWF verification - not implemented + linkPlaylist = 0x0008 // send playlist before play - not implemented + linkBufx = 0x0010 // toggle stream on BufferEmpty msg - not implemented ) +// Protocol features. const ( - RTMP_READ_HEADER = 0x01 - RTMP_READ_RESUME = 0x02 - RTMP_READ_NO_IGNORE = 0x04 - RTMP_READ_GOTKF = 0x08 - RTMP_READ_GOTFLVK = 0x10 - RTMP_READ_SEEKING = 0x20 - RTMP_READ_COMPLETE = -3 - RTMP_READ_ERROR = -2 - RTMP_READ_EOF = -1 - RTMP_READ_IGNORE = 0 + featureHTTP = 0x01 // not implemented + featureEncode = 0x02 // not implemented + featureSSL = 0x04 // not implemented + featureMFP = 0x08 // not implemented + featureWrite = 0x10 // publish, not play + featureHTTP2 = 0x20 // server-side RTMPT - not implemented ) +// RTMP protocols. const ( - RTMP_LF_AUTH = 0x0001 /* using auth param */ - RTMP_LF_LIVE = 0x0002 /* stream is live */ - RTMP_LF_SWFV = 0x0004 /* do SWF verification */ - RTMP_LF_PLST = 0x0008 /* send playlist before play */ - RTMP_LF_BUFX = 0x0010 /* toggle stream on BufferEmpty msg */ - RTMP_LF_FTCU = 0x0020 /* free tcUrl on close */ - RTMP_LF_FAPU = 0x0040 /* free app on close */ + protoRTMP = 0 + protoRTMPE = featureEncode + protoRTMPT = featureHTTP + protoRTMPS = featureSSL + protoRTMPTE = (featureHTTP | featureEncode) + protoRTMPTS = (featureHTTP | featureSSL) + protoRTMFP = featureMFP ) +// RTMP tokens (lexemes). +// NB: Underscores are deliberately preserved in const names where they exist in the corresponding tokens. const ( - RTMP_FEATURE_HTTP = 0x01 - RTMP_FEATURE_ENC = 0x02 - RTMP_FEATURE_SSL = 0x04 - RTMP_FEATURE_MFP = 0x08 /* not yet supported */ - RTMP_FEATURE_WRITE = 0x10 /* publish, not play */ - RTMP_FEATURE_HTTP2 = 0x20 /* server-side rtmpt */ -) - -const ( - RTMP_PROTOCOL_RTMP = 0 - RTMP_PROTOCOL_RTMPE = RTMP_FEATURE_ENC - RTMP_PROTOCOL_RTMPT = RTMP_FEATURE_HTTP - RTMP_PROTOCOL_RTMPS = RTMP_FEATURE_SSL - RTMP_PROTOCOL_RTMPTE = (RTMP_FEATURE_HTTP | RTMP_FEATURE_ENC) - RTMP_PROTOCOL_RTMPTS = (RTMP_FEATURE_HTTP | RTMP_FEATURE_SSL) - RTMP_PROTOCOL_RTMFP = RTMP_FEATURE_MFP -) - -const ( - RTMP_DEFAULT_CHUNKSIZE = 128 - RTMP_BUFFER_CACHE_SIZE = (16 * 1024) - RTMP_SIG_SIZE = 1536 - RTMP_LARGE_HEADER_SIZE = 12 - RTMP_MAX_HEADER_SIZE = RTMP_LARGE_HEADER_SIZE -) - -const ( - setDataFrame = "@setDataFrame" - - av__checkbw = "_checkbw" - av__onbwcheck = "_onbwcheck" - av__onbwdone = "_onbwdone" - av__result = "_result" - av_app = "app" - av_audioCodecs = "audioCodecs" - av_capabilities = "capabilities" - av_close = "close" - av_code = "code" - av_connect = "connect" - av_createStream = "createStream" - av_deleteStream = "deleteStream" - av_FCPublish = "FCPublish" - av_FCUnpublish = "FCUnpublish" - av_flashVer = "flashVer" - av_fpad = "fpad" - av_level = "level" - av_live = "live" - av_NetConnection_Connect_InvalidApp = "NetConnection.Connect.InvalidApp" - av_NetStream_Failed = "NetStream.Failed" - av_NetStream_Pause_Notify = "NetStream.Pause.Notify" - av_NetStream_Play_Complete = "NetStream.Play.Complete" - av_NetStream_Play_Failed = "NetStream.Play.Failed" - av_NetStream_Play_PublishNotify = "NetStream.Play.PublishNotify" - av_NetStream_Play_Start = "NetStream.Play.Start" - av_NetStream_Play_Stop = "NetStream.Play.Stop" - av_NetStream_Play_StreamNotFound = "NetStream.Play.StreamNotFound" - av_NetStream_Play_UnpublishNotify = "NetStream.Play.UnpublishNotify" - av_NetStream_Publish_Start = "NetStream.Publish.Start" - av_NetStream_Seek_Notify = "NetStream.Seek.Notify" - av_nonprivate = "nonprivate" - av_objectEncoding = "objectEncoding" - av_onBWDone = "onBWDone" - av_onFCSubscribe = "onFCSubscribe" - av_onFCUnsubscribe = "onFCUnsubscribe" - av_onStatus = "onStatus" - av_pageUrl = "pageUrl" - av_ping = "ping" - av_play = "play" - av_playlist_ready = "playlist_ready" - av_publish = "publish" - av_releaseStream = "releaseStream" - av_secureToken = "secureToken" - av_set_playlist = "set_playlist" - av_swfUrl = "swfUrl" - av_tcUrl = "tcUrl" - av_type = "type" - av_videoCodecs = "videoCodecs" - av_videoFunction = "videoFunction" + av_checkbw = "_checkbw" + av_onbwcheck = "_onbwcheck" + av_onbwdone = "_onbwdone" + av_result = "_result" + avApp = "app" + avAudioCodecs = "audioCodecs" + avCapabilities = "capabilities" + avClose = "close" + avCode = "code" + avConnect = "connect" + avCreatestream = "createStream" + avDeletestream = "deleteStream" + avFCPublish = "FCPublish" + avFCUnpublish = "FCUnpublish" + avFlashver = "flashVer" + avFpad = "fpad" + avLevel = "level" + avLive = "live" + avNetConnectionConnectInvalidApp = "NetConnection.Connect.InvalidApp" + avNetStreamFailed = "NetStream.Failed" + avNetStreamPauseNotify = "NetStream.Pause.Notify" + avNetStreamPlayComplete = "NetStream.Play.Complete" + avNetStreamPlayFailed = "NetStream.Play.Failed" + avNetStreamPlayPublishNotify = "NetStream.Play.PublishNotify" + avNetStreamPlayStart = "NetStream.Play.Start" + avNetStreamPlayStop = "NetStream.Play.Stop" + avNetStreamPlayStreamNotFound = "NetStream.Play.StreamNotFound" + avNetStreamPlayUnpublishNotify = "NetStream.Play.UnpublishNotify" + avNetStreamPublish_Start = "NetStream.Publish.Start" + avNetStreamSeekNotify = "NetStream.Seek.Notify" + avNonprivate = "nonprivate" + avObjectEncoding = "objectEncoding" + avOnBWDone = "onBWDone" + avOnFCSubscribe = "onFCSubscribe" + avOnFCUnsubscribe = "onFCUnsubscribe" + avOnStatus = "onStatus" + avPageUrl = "pageUrl" + avPing = "ping" + avPlay = "play" + avPlaylist_ready = "playlist_ready" + avPublish = "publish" + avReleasestream = "releaseStream" + avSecureToken = "secureToken" + avSet_playlist = "set_playlist" + avSwfUrl = "swfUrl" + avTcUrl = "tcUrl" + avType = "type" + avVideoCodecs = "videoCodecs" + avVideoFunction = "videoFunction" ) // RTMP protocol strings. @@ -200,7 +175,6 @@ func setupURL(s *Session) (err error) { if s.link.tcUrl == "" { if s.link.app != "" { s.link.tcUrl = rtmpProtocolStrings[s.link.protocol] + "://" + s.link.host + ":" + strconv.Itoa(int(s.link.port)) + "/" + s.link.app - s.link.lFlags |= RTMP_LF_FTCU } else { s.link.tcUrl = s.url } @@ -208,10 +182,10 @@ func setupURL(s *Session) (err error) { if s.link.port == 0 { switch { - case (s.link.protocol & RTMP_FEATURE_SSL) != 0: + case (s.link.protocol & featureSSL) != 0: s.link.port = 433 s.log(FatalLevel, pkg+"SSL not supported") - case (s.link.protocol & RTMP_FEATURE_HTTP) != 0: + case (s.link.protocol & featureHTTP) != 0: s.link.port = 80 default: s.link.port = 1935 @@ -257,7 +231,7 @@ func connectStream(s *Session) error { } switch pkt.packetType { - case RTMP_PACKET_TYPE_AUDIO, RTMP_PACKET_TYPE_VIDEO, RTMP_PACKET_TYPE_INFO: + case packetTypeAudio, packetTypeVideo, packetTypeInfo: s.log(WarnLevel, pkg+"got packet before play; ignoring", "type", pkt.packetType) default: err = handlePacket(s, &pkt) @@ -277,21 +251,21 @@ func connectStream(s *Session) error { // NB: cases have been commented out that are not currently used by AusOcean func handlePacket(s *Session, pkt *packet) error { switch pkt.packetType { - case RTMP_PACKET_TYPE_CHUNK_SIZE: + case packetTypeChunkSize: if pkt.bodySize >= 4 { s.inChunkSize = int32(C_AMF_DecodeInt32(pkt.body[:4])) } - case RTMP_PACKET_TYPE_BYTES_READ_REPORT: + case packetTypeBytesReadReport: s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) - case RTMP_PACKET_TYPE_CONTROL: - s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_CONTROL") + case packetTypeControl: + s.log(FatalLevel, pkg+"unsupported packet type packetTypeControl") - case RTMP_PACKET_TYPE_SERVER_BW: + case packetTypeServerBW: s.serverBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) - case RTMP_PACKET_TYPE_CLIENT_BW: + case packetTypeClientBW: s.clientBW = int32(C_AMF_DecodeInt32(pkt.body[:4])) if pkt.bodySize > 4 { s.clientBW2 = pkt.body[4] @@ -299,19 +273,19 @@ func handlePacket(s *Session, pkt *packet) error { s.clientBW2 = 0xff } - case RTMP_PACKET_TYPE_AUDIO: - s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_AUDIO") + case packetTypeAudio: + s.log(FatalLevel, pkg+"unsupported packet type packetTypeAudio") - case RTMP_PACKET_TYPE_VIDEO: - s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_VIDEO") + case packetTypeVideo: + s.log(FatalLevel, pkg+"unsupported packet type packetTypeVideo") - case RTMP_PACKET_TYPE_FLEX_MESSAGE: - s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_FLEX_MESSAGE") + case packetTypeFlexMessage: + s.log(FatalLevel, pkg+"unsupported packet type packetTypeFlexMessage") - case RTMP_PACKET_TYPE_INFO: - s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_INFO") + case packetTypeInfo: + s.log(FatalLevel, pkg+"unsupported packet type packetTypeInfo") - case RTMP_PACKET_TYPE_INVOKE: + case packetTypeInvoke: err := handleInvoke(s, pkt.body[:pkt.bodySize]) if err != nil { // This will never happen with the methods we implement. @@ -319,8 +293,8 @@ func handlePacket(s *Session, pkt *packet) error { return err } - case RTMP_PACKET_TYPE_FLASH_VIDEO: - s.log(FatalLevel, pkg+"unsupported packet type RTMP_PACKET_TYPE_FLASH_VIDEO") + case packetTypeFlashVideo: + s.log(FatalLevel, pkg+"unsupported packet type packetType_FLASHVideo") default: s.log(WarnLevel, pkg+"unknown packet type", "type", pkt.packetType) @@ -331,15 +305,15 @@ func handlePacket(s *Session, pkt *packet) error { func sendConnectPacket(s *Session) error { var pbuf [4096]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_LARGE, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeLarge, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_connect) + enc = C_AMF_EncodeString(enc, avConnect) if enc == nil { return errEncoding } @@ -351,60 +325,60 @@ func sendConnectPacket(s *Session) error { enc[0] = AMF_OBJECT enc = enc[1:] - enc = C_AMF_EncodeNamedString(enc, av_app, s.link.app) + enc = C_AMF_EncodeNamedString(enc, avApp, s.link.app) if enc == nil { return errEncoding } - if s.link.protocol&RTMP_FEATURE_WRITE != 0 { - enc = C_AMF_EncodeNamedString(enc, av_type, av_nonprivate) + if s.link.protocol&featureWrite != 0 { + enc = C_AMF_EncodeNamedString(enc, avType, avNonprivate) if enc == nil { return errEncoding } } if s.link.flashVer != "" { - enc = C_AMF_EncodeNamedString(enc, av_flashVer, s.link.flashVer) + enc = C_AMF_EncodeNamedString(enc, avFlashver, s.link.flashVer) if enc == nil { return errEncoding } } if s.link.swfUrl != "" { - enc = C_AMF_EncodeNamedString(enc, av_swfUrl, s.link.swfUrl) + enc = C_AMF_EncodeNamedString(enc, avSwfUrl, s.link.swfUrl) if enc == nil { return errEncoding } } if s.link.tcUrl != "" { - enc = C_AMF_EncodeNamedString(enc, av_tcUrl, s.link.tcUrl) + enc = C_AMF_EncodeNamedString(enc, avTcUrl, s.link.tcUrl) if enc == nil { return errEncoding } } - if s.link.protocol&RTMP_FEATURE_WRITE == 0 { - enc = C_AMF_EncodeNamedBoolean(enc, av_fpad, false) + if s.link.protocol&featureWrite == 0 { + enc = C_AMF_EncodeNamedBoolean(enc, avFpad, false) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_capabilities, 15) + enc = C_AMF_EncodeNamedNumber(enc, avCapabilities, 15) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_audioCodecs, s.audioCodecs) + enc = C_AMF_EncodeNamedNumber(enc, avAudioCodecs, s.audioCodecs) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_videoCodecs, s.videoCodecs) + enc = C_AMF_EncodeNamedNumber(enc, avVideoCodecs, s.videoCodecs) if enc == nil { return errEncoding } - enc = C_AMF_EncodeNamedNumber(enc, av_videoFunction, 1) + enc = C_AMF_EncodeNamedNumber(enc, avVideoFunction, 1) if enc == nil { return errEncoding } if s.link.pageUrl != "" { - enc = C_AMF_EncodeNamedString(enc, av_pageUrl, s.link.pageUrl) + enc = C_AMF_EncodeNamedString(enc, avPageUrl, s.link.pageUrl) if enc == nil { return errEncoding } @@ -412,7 +386,7 @@ func sendConnectPacket(s *Session) error { } if s.encoding != 0.0 || s.sendEncoding { - enc = C_AMF_EncodeNamedNumber(enc, av_objectEncoding, s.encoding) + enc = C_AMF_EncodeNamedNumber(enc, avObjectEncoding, s.encoding) if enc == nil { return errEncoding } @@ -423,7 +397,7 @@ func sendConnectPacket(s *Session) error { // add auth string if s.link.auth != "" { - enc = C_AMF_EncodeBoolean(enc, s.link.lFlags&RTMP_LF_AUTH != 0) + enc = C_AMF_EncodeBoolean(enc, s.link.flags&linkAuth != 0) if enc == nil { return errEncoding } @@ -440,7 +414,7 @@ func sendConnectPacket(s *Session) error { } } - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, true) // response expected } @@ -448,15 +422,15 @@ func sendConnectPacket(s *Session) error { func sendCreateStream(s *Session) error { var pbuf [256]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeMedium, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_createStream) + enc = C_AMF_EncodeString(enc, avCreatestream) if enc == nil { return errEncoding } @@ -468,7 +442,7 @@ func sendCreateStream(s *Session) error { enc[0] = AMF_NULL enc = enc[1:] - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, true) // response expected } @@ -476,15 +450,15 @@ func sendCreateStream(s *Session) error { func sendReleaseStream(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeMedium, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_releaseStream) + enc = C_AMF_EncodeString(enc, avReleasestream) if enc == nil { return errEncoding } @@ -499,7 +473,7 @@ func sendReleaseStream(s *Session) error { if enc == nil { return errEncoding } - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, false) } @@ -507,15 +481,15 @@ func sendReleaseStream(s *Session) error { func sendFCPublish(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeMedium, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_FCPublish) + enc = C_AMF_EncodeString(enc, avFCPublish) if enc == nil { return errEncoding } @@ -531,7 +505,7 @@ func sendFCPublish(s *Session) error { return errEncoding } - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, false) } @@ -539,15 +513,15 @@ func sendFCPublish(s *Session) error { func sendFCUnpublish(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeMedium, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_FCUnpublish) + enc = C_AMF_EncodeString(enc, avFCUnpublish) if enc == nil { return errEncoding } @@ -563,7 +537,7 @@ func sendFCUnpublish(s *Session) error { return errEncoding } - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, false) } @@ -571,15 +545,15 @@ func sendFCUnpublish(s *Session) error { func sendPublish(s *Session) error { var pbuf [1024]byte pkt := packet{ - channel: RTMP_CHANNEL_SOURCE, - headerType: RTMP_PACKET_SIZE_LARGE, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanSource, + headerType: headerSizeLarge, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_publish) + enc = C_AMF_EncodeString(enc, avPublish) if enc == nil { return errEncoding } @@ -594,12 +568,12 @@ func sendPublish(s *Session) error { if enc == nil { return errEncoding } - enc = C_AMF_EncodeString(enc, av_live) + enc = C_AMF_EncodeString(enc, avLive) if enc == nil { return errEncoding } - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, true) // response expected } @@ -607,15 +581,15 @@ func sendPublish(s *Session) error { func sendDeleteStream(s *Session, dStreamId float64) error { var pbuf [256]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeMedium, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av_deleteStream) + enc = C_AMF_EncodeString(enc, avDeletestream) if enc == nil { return errEncoding } @@ -630,7 +604,7 @@ func sendDeleteStream(s *Session, dStreamId float64) error { if enc == nil { return errEncoding } - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) /* no response expected */ return pkt.write(s, false) @@ -640,11 +614,11 @@ func sendDeleteStream(s *Session, dStreamId float64) error { func sendBytesReceived(s *Session) error { var pbuf [256]byte pkt := packet{ - channel: RTMP_CHANNEL_BYTES_READ, - headerType: RTMP_PACKET_SIZE_MEDIUM, - packetType: RTMP_PACKET_TYPE_BYTES_READ_REPORT, + channel: chanBytesRead, + headerType: headerSizeMedium, + packetType: packetTypeBytesReadReport, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body @@ -661,15 +635,15 @@ func sendBytesReceived(s *Session) error { func sendCheckBW(s *Session) error { var pbuf [256]byte pkt := packet{ - channel: RTMP_CHANNEL_CONTROL, - headerType: RTMP_PACKET_SIZE_LARGE, - packetType: RTMP_PACKET_TYPE_INVOKE, + channel: chanControl, + headerType: headerSizeLarge, + packetType: packetTypeInvoke, header: pbuf[:], - body: pbuf[RTMP_MAX_HEADER_SIZE:], + body: pbuf[fullHeaderSize:], } enc := pkt.body - enc = C_AMF_EncodeString(enc, av__checkbw) + enc = C_AMF_EncodeString(enc, av_checkbw) if enc == nil { return errEncoding } @@ -681,7 +655,7 @@ func sendCheckBW(s *Session) error { enc[0] = AMF_NULL enc = enc[1:] - pkt.bodySize = uint32((len(pbuf) - RTMP_MAX_HEADER_SIZE) - len(enc)) + pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) return pkt.write(s, false) } @@ -693,7 +667,7 @@ func eraseMethod(m []method, i int) []method { } // int handleInvoke handles a packet invoke request -// Side effects: s.isPlaying set to true upon av_NetStream_Publish_Start +// Side effects: s.isPlaying set to true upon avNetStreamPublish_Start func handleInvoke(s *Session, body []byte) error { if body[0] != 0x02 { return errInvalidBody @@ -709,7 +683,7 @@ func handleInvoke(s *Session, body []byte) error { txn := C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 1)) switch meth { - case av__result: + case av_result: var methodInvoked string for i, m := range s.methodCalls { if float64(m.num) == txn { @@ -725,11 +699,11 @@ func handleInvoke(s *Session, body []byte) error { s.log(DebugLevel, pkg+"received result for "+methodInvoked) switch methodInvoked { - case av_connect: + case avConnect: if s.link.token != "" { s.log(FatalLevel, pkg+"no support for link token") } - if (s.link.protocol & RTMP_FEATURE_WRITE) == 0 { + if (s.link.protocol & featureWrite) == 0 { return errNotWritable } err := sendReleaseStream(s) @@ -745,9 +719,9 @@ func handleInvoke(s *Session, body []byte) error { return err } - case av_createStream: + case avCreatestream: s.streamID = int32(C_AMFProp_GetNumber(C_AMF_GetProp(&obj, "", 3))) - if s.link.protocol&RTMP_FEATURE_WRITE == 0 { + if s.link.protocol&featureWrite == 0 { return errNotWritable } err := sendPublish(s) @@ -755,11 +729,11 @@ func handleInvoke(s *Session, body []byte) error { return err } - case av_play, av_publish: - s.log(FatalLevel, pkg+"unsupported method av_play/av_publish") + case avPlay, avPublish: + s.log(FatalLevel, pkg+"unsupported method avPlay/avPublish") } - case av_onBWDone: + case avOnBWDone: if s.checkCounter == 0 { // ToDo: why is this always zero? err := sendCheckBW(s) if err != nil { @@ -767,59 +741,59 @@ func handleInvoke(s *Session, body []byte) error { } } - case av_onFCUnsubscribe, av_onFCSubscribe: - s.log(FatalLevel, pkg+"unsupported method av_onFCUnsubscribe/av_onFCSubscribe") + case avOnFCUnsubscribe, avOnFCSubscribe: + s.log(FatalLevel, pkg+"unsupported method avOnFCUnsubscribe/avOonfcsubscribe") - case av_ping: - s.log(FatalLevel, pkg+"unsupported method av_ping") + case avPing: + s.log(FatalLevel, pkg+"unsupported method avPing") - case av__onbwcheck: + case av_onbwcheck: s.log(FatalLevel, pkg+"unsupported method av_onbwcheck") - case av__onbwdone: + case av_onbwdone: s.log(FatalLevel, pkg+"unsupported method av_onbwdone") - case av_close: - s.log(FatalLevel, pkg+"unsupported method av_close") + case avClose: + s.log(FatalLevel, pkg+"unsupported method avClose") - case av_onStatus: + case avOnStatus: var obj2 C_AMFObject C_AMFProp_GetObject(C_AMF_GetProp(&obj, "", 3), &obj2) - code := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_code, -1)) - level := C_AMFProp_GetString(C_AMF_GetProp(&obj2, av_level, -1)) + code := C_AMFProp_GetString(C_AMF_GetProp(&obj2, avCode, -1)) + level := C_AMFProp_GetString(C_AMF_GetProp(&obj2, avLevel, -1)) s.log(DebugLevel, pkg+"onStatus", "code", code, "level", level) switch code { - case av_NetStream_Failed, av_NetStream_Play_Failed, - av_NetStream_Play_StreamNotFound, av_NetConnection_Connect_InvalidApp: - s.log(FatalLevel, pkg+"unsupported method av_NetStream/av_NetStream_Play_Failed/av_netSTream_Play_StreamNotFound/av_netConnection_Connect_invalidApp") + case avNetStreamFailed, avNetStreamPlayFailed, + avNetStreamPlayStreamNotFound, avNetConnectionConnectInvalidApp: + s.log(FatalLevel, pkg+"unsupported method avNetStream/avNetStreamPlayFailed/avNetstream_play_streamnotfound/av_netConnection_Connect_invalidApp") - case av_NetStream_Play_Start, av_NetStream_Play_PublishNotify: - s.log(FatalLevel, pkg+"unsupported method av_NetStream_Play_Start/av_NetStream_Play_PublishNotify") + case avNetStreamPlayStart, avNetStreamPlayPublishNotify: + s.log(FatalLevel, pkg+"unsupported method avNetStreamPlayStart/avNetStreamPlayPublishNotify") - case av_NetStream_Publish_Start: + case avNetStreamPublish_Start: s.log(DebugLevel, pkg+"playing") s.isPlaying = true for i, m := range s.methodCalls { - if m.name == av_publish { + if m.name == avPublish { s.methodCalls = eraseMethod(s.methodCalls, i) break } } - // ToDo: handle case when av_publish method not found + // ToDo: handle case when avPublish method not found - case av_NetStream_Play_Complete, av_NetStream_Play_Stop, av_NetStream_Play_UnpublishNotify: - s.log(FatalLevel, pkg+"unsupported method av_NetStream_Play_Complete/av_NetStream_Play_Stop/av_NetStream_Play_UnpublishNotify") + case avNetStreamPlayComplete, avNetStreamPlayStop, avNetStreamPlayUnpublishNotify: + s.log(FatalLevel, pkg+"unsupported method avNetStreamPlayComplete/avNetStreamPlayStop/avNetStreamPlayUnpublishNotify") - case av_NetStream_Seek_Notify: - s.log(FatalLevel, pkg+"unsupported method av_netStream_Seek_Notify") + case avNetStreamSeekNotify: + s.log(FatalLevel, pkg+"unsupported method avNetstream_seek_notify") - case av_NetStream_Pause_Notify: - s.log(FatalLevel, pkg+"unsupported method av_NetStream_Pause_Notify") + case avNetStreamPauseNotify: + s.log(FatalLevel, pkg+"unsupported method avNetStreamPauseNotify") } - case av_playlist_ready: - s.log(FatalLevel, pkg+"unsupported method av_playlist_ready") + case avPlaylist_ready: + s.log(FatalLevel, pkg+"unsupported method avPlaylist_ready") default: s.log(FatalLevel, pkg+"unknown method "+meth) @@ -830,15 +804,15 @@ leave: } func handshake(s *Session) error { - var clientbuf [RTMP_SIG_SIZE + 1]byte + var clientbuf [signatureSize + 1]byte clientsig := clientbuf[1:] - var serversig [RTMP_SIG_SIZE]byte - clientbuf[0] = RTMP_CHANNEL_CONTROL + var serversig [signatureSize]byte + clientbuf[0] = chanControl binary.BigEndian.PutUint32(clientsig, uint32(time.Now().UnixNano()/1000000)) copy(clientsig[4:8], []byte{0, 0, 0, 0}) - for i := 8; i < RTMP_SIG_SIZE; i++ { + for i := 8; i < signatureSize; i++ { clientsig[i] = byte(rand.Intn(256)) } @@ -878,8 +852,8 @@ func handshake(s *Session) error { return err } - if !bytes.Equal(serversig[:RTMP_SIG_SIZE], clientbuf[1:RTMP_SIG_SIZE+1]) { - s.log(WarnLevel, pkg+"signature mismatch", "serversig", serversig[:RTMP_SIG_SIZE], "clientsig", clientbuf[1:RTMP_SIG_SIZE+1]) + if !bytes.Equal(serversig[:signatureSize], clientbuf[1:signatureSize+1]) { + s.log(WarnLevel, pkg+"signature mismatch", "serversig", serversig[:signatureSize], "clientsig", clientbuf[1:signatureSize+1]) } return nil } diff --git a/rtmp/session.go b/rtmp/session.go index aa1ee80b..aa4c0ac2 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -80,7 +80,7 @@ type link struct { flashVer string token string extras C_AMFObject - lFlags int32 + flags int32 swfAge int32 protocol int32 timeout uint @@ -159,7 +159,7 @@ func (s *Session) Close() error { return errNotConnected } if s.streamID > 0 { - if s.link.protocol&RTMP_FEATURE_WRITE != 0 { + if s.link.protocol&featureWrite != 0 { sendFCUnpublish(s) } sendDeleteStream(s, float64(s.streamID)) @@ -174,7 +174,7 @@ func (s *Session) Write(data []byte) (int, error) { if !s.isConnected() { return 0, errNotConnected } - if data[0] == RTMP_PACKET_TYPE_INFO || (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { + if data[0] == packetTypeInfo || (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { return 0, errUnimplemented } if len(data) < minDataSize { @@ -185,11 +185,11 @@ func (s *Session) Write(data []byte) (int, error) { packetType: data[0], bodySize: C_AMF_DecodeInt24(data[1:4]), timestamp: C_AMF_DecodeInt24(data[4:7]) | uint32(data[7])<<24, - channel: RTMP_CHANNEL_SOURCE, + channel: chanSource, info: s.streamID, } - pkt.resize(pkt.bodySize, RTMP_PACKET_SIZE_AUTO) + pkt.resize(pkt.bodySize, headerSizeAuto) copy(pkt.body, data[minDataSize:minDataSize+pkt.bodySize]) err := pkt.write(s, false) if err != nil { @@ -245,5 +245,5 @@ func (s *Session) isConnected() bool { // enableWrite enables the current session for writing. func (s *Session) enableWrite() { - s.link.protocol |= RTMP_FEATURE_WRITE + s.link.protocol |= featureWrite } From fc815c23e742ff6c83620960f2a638ad78c8a0ec Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 10:10:27 +1030 Subject: [PATCH 124/137] Session.Write() now checks len(data) before the FLV test. --- rtmp/session.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtmp/session.go b/rtmp/session.go index aa4c0ac2..8ba30d1c 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -152,7 +152,7 @@ func (s *Session) Open() error { } // Close terminates the rtmp connection. -// NB: The session object is cleared completely. +// NB: Close is idempotent and the session value is cleared completely. func (s *Session) Close() error { s.log(DebugLevel, pkg+"Session.Close") if !s.isConnected() { @@ -174,12 +174,12 @@ func (s *Session) Write(data []byte) (int, error) { if !s.isConnected() { return 0, errNotConnected } - if data[0] == packetTypeInfo || (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { - return 0, errUnimplemented - } if len(data) < minDataSize { return 0, errTinyPacket } + if data[0] == packetTypeInfo || (data[0] == 'F' && data[1] == 'L' && data[2] == 'V') { + return 0, errUnimplemented + } pkt := packet{ packetType: data[0], From 67cc591dd249364facff3b3b01eba49d10436aec Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 10:27:41 +1030 Subject: [PATCH 125/137] Use idiomatic consts. --- rtmp/parseurl.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/rtmp/parseurl.go b/rtmp/parseurl.go index 97110eb8..eae4277e 100644 --- a/rtmp/parseurl.go +++ b/rtmp/parseurl.go @@ -8,9 +8,10 @@ DESCRIPTION AUTHOR Dan Kortschak Saxon Nelson-Milton + Alan Noble LICENSE - parseurl.go is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean) + parseurl.go is Copyright (C) 2017-2019 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the @@ -33,7 +34,6 @@ LICENSE package rtmp import ( - "log" "net/url" "path" "strconv" @@ -41,30 +41,29 @@ import ( ) // parseURL parses an RTMP URL (ok, technically it is lexing). +// func parseURL(addr string) (protocol int32, host string, port uint16, app, playpath string, err error) { u, err := url.Parse(addr) if err != nil { - log.Printf("failed to parse addr: %v", err) return protocol, host, port, app, playpath, err } switch u.Scheme { case "rtmp": - protocol = RTMP_PROTOCOL_RTMP + protocol = protoRTMP case "rtmpt": - protocol = RTMP_PROTOCOL_RTMPT + protocol = protoRTMPT case "rtmps": - protocol = RTMP_PROTOCOL_RTMPS + protocol = protoRTMPS case "rtmpe": - protocol = RTMP_PROTOCOL_RTMPE + protocol = protoRTMPE case "rtmfp": - protocol = RTMP_PROTOCOL_RTMFP + protocol = protoRTMFP case "rtmpte": - protocol = RTMP_PROTOCOL_RTMPTE + protocol = protoRTMPTE case "rtmpts": - protocol = RTMP_PROTOCOL_RTMPTS + protocol = protoRTMPTS default: - log.Printf("unknown scheme: %q", u.Scheme) return protocol, host, port, app, playpath, errUnknownScheme } From 22b76b5bda7d95c73c097f03d61749a23cc20047 Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 10:35:20 +1030 Subject: [PATCH 126/137] Session.read()/write() both now return (int, error). --- rtmp/packet.go | 19 ++++++++++--------- rtmp/rtmp.go | 11 +++++------ rtmp/session.go | 20 ++++++++++---------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/rtmp/packet.go b/rtmp/packet.go index 3c50b59d..dac3d803 100644 --- a/rtmp/packet.go +++ b/rtmp/packet.go @@ -106,7 +106,7 @@ func (pkt *packet) read(s *Session) error { var hbuf [fullHeaderSize]byte header := hbuf[:] - err := s.read(header[:1]) + _, err := s.read(header[:1]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 1st byte", "error", err.Error()) if err == io.EOF { @@ -120,7 +120,7 @@ func (pkt *packet) read(s *Session) error { switch { case pkt.channel == 0: - err = s.read(header[:1]) + _, err = s.read(header[:1]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 2nd byte", "error", err.Error()) return err @@ -129,7 +129,7 @@ func (pkt *packet) read(s *Session) error { pkt.channel = int32(header[0]) + 64 case pkt.channel == 1: - err = s.read(header[:2]) + _, err = s.read(header[:2]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header 3rd byte", "error", err.Error()) return err @@ -173,7 +173,7 @@ func (pkt *packet) read(s *Session) error { size-- if size > 0 { - err = s.read(header[:size]) + _, err = s.read(header[:size]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet header", "error", err.Error()) return err @@ -199,7 +199,7 @@ func (pkt *packet) read(s *Session) error { extendedTimestamp := pkt.timestamp == 0xffffff if extendedTimestamp { - err = s.read(header[size : size+4]) + _, err = s.read(header[size : size+4]) if err != nil { s.log(DebugLevel, pkg+"failed to read extended timestamp", "error", err.Error()) return err @@ -227,7 +227,7 @@ func (pkt *packet) read(s *Session) error { pkt.chunk.data = pkt.body[pkt.bytesRead : pkt.bytesRead+uint32(chunkSize)] } - err = s.read(pkt.body[pkt.bytesRead:][:chunkSize]) + _, err = s.read(pkt.body[pkt.bytesRead:][:chunkSize]) if err != nil { s.log(DebugLevel, pkg+"failed to read packet body", "error", err.Error()) return err @@ -261,7 +261,7 @@ func (pkt *packet) read(s *Session) error { return nil } -// resize adjusts the packet's storage to accommodate a body of the given size. +// resize adjusts the packet's storage to accommodate a body of the given size and header type. func (pkt *packet) resize(size uint32, ht uint8) { buf := make([]byte, fullHeaderSize+size) pkt.header = buf @@ -286,6 +286,7 @@ func (pkt *packet) resize(size uint32, ht uint8) { } // write sends a packet. +// When queue is true, we expect a response to this request and cache the method on s.methodCalls. func (pkt *packet) write(s *Session, queue bool) error { if pkt.body == nil { return errInvalidBody @@ -426,7 +427,7 @@ func (pkt *packet) write(s *Session, queue bool) error { // Send previously deferrd packet if combining it with the next one would exceed the chunk size. if len(s.deferred)+size+hSize > chunkSize { s.log(DebugLevel, pkg+"sending deferred packet separately", "size", len(s.deferred)) - err := s.write(s.deferred) + _, err := s.write(s.deferred) if err != nil { return err } @@ -447,7 +448,7 @@ func (pkt *packet) write(s *Session, queue bool) error { s.log(DebugLevel, pkg+"combining deferred packet", "size", len(s.deferred)) bytes = append(s.deferred, bytes...) } - err := s.write(bytes) + _, err := s.write(bytes) if err != nil { return err } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index f48e9e53..4b334587 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -606,7 +606,6 @@ func sendDeleteStream(s *Session, dStreamId float64) error { } pkt.bodySize = uint32((len(pbuf) - fullHeaderSize) - len(enc)) - /* no response expected */ return pkt.write(s, false) } @@ -816,14 +815,14 @@ func handshake(s *Session) error { clientsig[i] = byte(rand.Intn(256)) } - err := s.write(clientbuf[:]) + _, err := s.write(clientbuf[:]) if err != nil { return err } s.log(DebugLevel, pkg+"handshake sent") var typ [1]byte - err = s.read(typ[:]) + _, err = s.read(typ[:]) if err != nil { return err } @@ -832,7 +831,7 @@ func handshake(s *Session) error { if typ[0] != clientbuf[0] { s.log(WarnLevel, pkg+"handshake type mismatch", "sent", clientbuf[0], "received", typ) } - err = s.read(serversig[:]) + _, err = s.read(serversig[:]) if err != nil { return err } @@ -842,12 +841,12 @@ func handshake(s *Session) error { s.log(DebugLevel, pkg+"server uptime", "uptime", suptime) // 2nd part of handshake - err = s.write(serversig[:]) + _, err = s.write(serversig[:]) if err != nil { return err } - err = s.read(serversig[:]) + _, err = s.read(serversig[:]) if err != nil { return err } diff --git a/rtmp/session.go b/rtmp/session.go index 8ba30d1c..eb97c938 100644 --- a/rtmp/session.go +++ b/rtmp/session.go @@ -203,39 +203,39 @@ func (s *Session) Write(data []byte) (int, error) { // read from an RTMP connection. Sends a bytes received message if the // number of bytes received (nBytesIn) is greater than the number sent // (nBytesInSent) by 10% of the bandwidth. -func (s *Session) read(buf []byte) error { +func (s *Session) read(buf []byte) (int, error) { err := s.link.conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { - return err + return 0, err } n, err := io.ReadFull(s.link.conn, buf) if err != nil { s.log(DebugLevel, pkg+"read failed", "error", err.Error()) - return err + return 0, err } s.nBytesIn += int32(n) if s.nBytesIn > (s.nBytesInSent + s.clientBW/10) { err := sendBytesReceived(s) if err != nil { - return err + return n, err // NB: we still read n bytes, even though send bytes failed } } - return nil + return n, nil } // write to an RTMP connection. -func (s *Session) write(buf []byte) error { +func (s *Session) write(buf []byte) (int, error) { //ToDo: consider using a different timeout for writes than for reads err := s.link.conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(s.link.timeout))) if err != nil { - return err + return 0, err } - _, err = s.link.conn.Write(buf) + n, err := s.link.conn.Write(buf) if err != nil { s.log(WarnLevel, pkg+"write failed", "error", err.Error()) - return err + return 0, err } - return nil + return n, nil } // isConnected returns true if the RTMP connection is up. From 3ac40bac99245e55fcc821e9ee78445fcd1f9972 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 11 Jan 2019 10:54:25 +1030 Subject: [PATCH 127/137] psi: using letters in hex for bytes in psi_test.go --- stream/mts/psi/psi_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 5f2e20a5..480f6592 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -145,7 +145,7 @@ var ( 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, TimeDescTag, // Descriptor tag for timestamp TimeDataSize, // Length of bytes to follow - 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // Timestamp data + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6f, 0x74, 0x5f, // Timestamp data LocationDescTag, // Descriptor tag for location LocationDataSize, // Length of bytes to follow } @@ -160,7 +160,7 @@ var ( 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, TimeDescTag, // Descriptor tag TimeDataSize, // Length of bytes to follow - 0x00, 0x00, 0x00, 0x00, 0x49, 0xA2, 0x36, 0x0B, // timestamp + 0x00, 0x00, 0x00, 0x00, 0x49, 0xa2, 0x36, 0x0b, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } @@ -169,7 +169,7 @@ var ( 0x00, 0x02, 0xb0, 0x12, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x0a, TimeDescTag, // Descriptor tag TimeDataSize, // Length of bytes to follow - 0x00, 0x00, 0x00, 0x00, 0x67, 0x6F, 0x74, 0x5F, // timestamp + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6f, 0x74, 0x5f, // timestamp 0x1b, 0xe1, 0x00, 0xf0, 0x00, } From f88115f8112bbb4dedda1ac290743ddc6ccc7e1e Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 11 Jan 2019 10:57:53 +1030 Subject: [PATCH 128/137] mts: fixed comment --- stream/mts/encoder.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index a5550644..270c99e4 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -83,7 +83,8 @@ var ( }, } - // standardPmtTimeLocation is a minimal pmt with descriptors for time and location. + // standardPmtTimeLocation is a minimal pmt with descriptors for time and location, + // time and location fields are zeroed out. standardPmtTimeLocation = psi.PSI{ Pf: 0x00, Tid: 0x02, From 2f3c2cc0e2d1c0859ed81031983508c159b9a77f Mon Sep 17 00:00:00 2001 From: scruzin Date: Fri, 11 Jan 2019 11:17:14 +1030 Subject: [PATCH 129/137] Added ToDo. --- rtmp/rtmp_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index be4cf276..01dad10f 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -177,13 +177,16 @@ func TestFromFrame(t *testing.T) { } // Pass RTMP session, true for audio, true for video, and 25 FPS + // ToDo: fix this. Although we can encode the file and YouTube + // doesn't complain, YouTube doesn't play it (even when we + // send 1 minute's worth). flvEncoder := flv.NewEncoder(s, true, true, 25) for i := 0; i < 25; i++ { err := flvEncoder.Encode(b) if err != nil { t.Errorf("Encoding failed with error: %v", err) } - time.Sleep(40 * time.Millisecond) // rate limit to 1/25s + time.Sleep(time.Millisecond / 25) // rate limit to 1/25s } err = s.Close() From 62a2e4a09a0a8e4efda48e21fee54172d0a59232 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 11 Jan 2019 11:23:53 +1030 Subject: [PATCH 130/137] psi: made addCrc less stupid by stopping the make of a redundant 4 bytes --- stream/mts/psi/crc.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stream/mts/psi/crc.go b/stream/mts/psi/crc.go index 3d967ed8..a361307d 100644 --- a/stream/mts/psi/crc.go +++ b/stream/mts/psi/crc.go @@ -35,9 +35,10 @@ import ( // addCrc appends a crc table to a given psi table in bytes func addCrc(out []byte) []byte { - out = append(out, make([]byte, 4)...) - updateCrc(out) - return out + t := make([]byte, len(out)+4) + copy(t, out) + updateCrc(t) + return t } // updateCrc updates the crc of bytes slice, writing the checksum into the last four bytes. From 0d6aac0247a79d10f1c659a334fb2c42ce6726b5 Mon Sep 17 00:00:00 2001 From: saxon Date: Fri, 11 Jan 2019 11:53:41 +1030 Subject: [PATCH 131/137] revid: fixed build errors by updating calls to mts.MetaData.SetLocation() and mts.MetaData.SetTimeStamp() --- revid/senders.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/revid/senders.go b/revid/senders.go index 5c64e982..a06762c6 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -171,7 +171,7 @@ func (s *httpSender) extractMeta(r string) error { s.log(logger.Warning, pkg+"No timestamp in reply") } else { s.log(logger.Debug, fmt.Sprintf("%v got timestamp: %v", pkg, t)) - mts.SetTimeStamp(uint64(t)) + mts.MetaData.SetTimeStamp(uint64(t)) } // Extract location from reply @@ -180,7 +180,7 @@ func (s *httpSender) extractMeta(r string) error { s.log(logger.Warning, pkg+"No location in reply") } else { s.log(logger.Debug, fmt.Sprintf("%v got location: %v", pkg, g)) - mts.SetLocation(g) + mts.MetaData.SetLocation(g) } return nil From 7f07c4cb201fc4670e7d258a33d0fbd0f92cda57 Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Fri, 11 Jan 2019 13:47:17 +1030 Subject: [PATCH 132/137] revid,stream/flv: add back initial header write --- revid/revid.go | 5 ++++- rtmp/rtmp_test.go | 10 ++++++++-- stream/flv/encoder.go | 9 +++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/revid/revid.go b/revid/revid.go index b9051a12..409ba702 100644 --- a/revid/revid.go +++ b/revid/revid.go @@ -289,7 +289,10 @@ func (r *Revid) reset(config Config) error { r.encoder = mts.NewEncoder(&r.packer, float64(r.config.FrameRate)) case Flv: r.config.Logger.Log(logger.Info, pkg+"using FLV packetisation") - r.encoder = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate)) + r.encoder, err = flv.NewEncoder(&r.packer, true, true, int(r.config.FrameRate)) + if err != nil { + r.config.Logger.Log(logger.Fatal, pkg+"failed to open FLV encoder", err.Error()) + } } return nil diff --git a/rtmp/rtmp_test.go b/rtmp/rtmp_test.go index 01dad10f..aeb86263 100644 --- a/rtmp/rtmp_test.go +++ b/rtmp/rtmp_test.go @@ -180,7 +180,10 @@ func TestFromFrame(t *testing.T) { // ToDo: fix this. Although we can encode the file and YouTube // doesn't complain, YouTube doesn't play it (even when we // send 1 minute's worth). - flvEncoder := flv.NewEncoder(s, true, true, 25) + flvEncoder, err := flv.NewEncoder(s, true, true, 25) + if err != nil { + t.Fatalf("failed to create encoder: %v", err) + } for i := 0; i < 25; i++ { err := flvEncoder.Encode(b) if err != nil { @@ -219,7 +222,10 @@ func TestFromFile(t *testing.T) { defer f.Close() // Pass RTMP session, true for audio, true for video, and 25 FPS - flvEncoder := flv.NewEncoder(s, true, true, 25) + flvEncoder, err := flv.NewEncoder(s, true, true, 25) + if err != nil { + t.Fatalf("failed to create encoder: %v", err) + } err = lex.H264(flvEncoder, f, time.Second/time.Duration(25)) if err != nil { t.Errorf("Lexing and encoding failed with error: %v", err) diff --git a/stream/flv/encoder.go b/stream/flv/encoder.go index cb663fe6..f527e864 100644 --- a/stream/flv/encoder.go +++ b/stream/flv/encoder.go @@ -66,13 +66,18 @@ type Encoder struct { } // NewEncoder retuns a new FLV encoder. -func NewEncoder(dst io.Writer, audio, video bool, fps int) *Encoder { - return &Encoder{ +func NewEncoder(dst io.Writer, audio, video bool, fps int) (*Encoder, error) { + e := Encoder{ dst: dst, fps: fps, audio: audio, video: video, } + _, err := dst.Write(e.HeaderBytes()) + if err != nil { + return nil, err + } + return &e, err } // HeaderBytes returns the a From 9d70949e2eaae3d9bec7c7d24959b584613101da Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Fri, 11 Jan 2019 14:55:05 +1030 Subject: [PATCH 133/137] stream/flv: write first previous tag size --- stream/flv/encoder.go | 22 ++++++++++++++++------ stream/flv/flv.go | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/stream/flv/encoder.go b/stream/flv/encoder.go index f527e864..46d0eacb 100644 --- a/stream/flv/encoder.go +++ b/stream/flv/encoder.go @@ -57,12 +57,11 @@ var ( type Encoder struct { dst io.Writer - fps int - audio bool - video bool - lastTagSize int - header Header - start time.Time + fps int + audio bool + video bool + header Header + start time.Time } // NewEncoder retuns a new FLV encoder. @@ -193,6 +192,17 @@ func (s *frameScanner) readByte() (b byte, ok bool) { func (e *Encoder) Encode(frame []byte) error { var frameType byte var packetType byte + if e.start.IsZero() { + // This is the first frame, so write the PreviousTagSize0. + // + // See https://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf + // section E.3. + var zero [4]byte + _, err := e.dst.Write(zero[:]) + if err != nil { + return err + } + } timeStamp := e.getNextTimestamp() // Do we have video to send off? if e.video { diff --git a/stream/flv/flv.go b/stream/flv/flv.go index 32deb0df..293b89f8 100644 --- a/stream/flv/flv.go +++ b/stream/flv/flv.go @@ -79,6 +79,8 @@ type Header struct { } func (h *Header) Bytes() []byte { + // See https://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf + // section E.2. const headerLength = 9 b := [headerLength]byte{ 0: 'F', 1: 'L', 2: 'V', 3: version, From 65e2ab3f6a1458caeebae78dda0c79dd4d517dbd Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Fri, 11 Jan 2019 22:29:48 +1030 Subject: [PATCH 134/137] stream/mts{,psi}: fix comments --- stream/mts/encoder.go | 6 +++--- stream/mts/psi/psi_test.go | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 270c99e4..d3d5479e 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -59,7 +59,7 @@ var ( }, } - // standardPmt is a minimal PMT without descriptors for time and location. + // standardPmt is a minimal PMT, without descriptors for time and location. standardPmt = psi.PSI{ Pf: 0x00, Tid: 0x02, @@ -83,8 +83,8 @@ var ( }, } - // standardPmtTimeLocation is a minimal pmt with descriptors for time and location, - // time and location fields are zeroed out. + // standardPmtTimeLocation is a standard PMT with time and location + // descriptors, but time and location fields zeroed out. standardPmtTimeLocation = psi.PSI{ Pf: 0x00, Tid: 0x02, diff --git a/stream/mts/psi/psi_test.go b/stream/mts/psi/psi_test.go index 480f6592..7be5d25e 100644 --- a/stream/mts/psi/psi_test.go +++ b/stream/mts/psi/psi_test.go @@ -77,7 +77,8 @@ var ( }, } - // standardPmtTimeLocation is a minimal PMT with time and location descriptors. + // standardPmtTimeLocation is a standard PMT with time and location + // descriptors, but time and location fields zeroed out. standardPmtTimeLocation = PSI{ Pf: 0x00, Tid: 0x02, From 010abcfd0cb563bbcab0950b9d5c8605ba2789cf Mon Sep 17 00:00:00 2001 From: saxon Date: Sat, 12 Jan 2019 17:34:43 +1030 Subject: [PATCH 135/137] mts: ccMask to continuityCounterMask in encoder.go --- stream/mts/encoder.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index f3ca3690..18ceee89 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -340,8 +340,7 @@ func (e *Encoder) pcr() uint64 { // ccFor returns the next continuity counter for pid. func (e *Encoder) ccFor(pid int) byte { cc := e.continuity[pid] - // Continuity counter mask - const ccMask = 0xf - e.continuity[pid] = (cc + 1) & ccMask + const continuityCounterMask = 0xf + e.continuity[pid] = (cc + 1) & continuityCounterMask return cc } From 46f5ffd6f237f471b9252e19a29214ee98f27e4d Mon Sep 17 00:00:00 2001 From: saxon Date: Sat, 12 Jan 2019 17:36:35 +1030 Subject: [PATCH 136/137] mts: PktSize to PacketSize --- stream/mts/encoder.go | 8 ++++---- stream/mts/mpegts.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/stream/mts/encoder.go b/stream/mts/encoder.go index 18ceee89..decc8558 100644 --- a/stream/mts/encoder.go +++ b/stream/mts/encoder.go @@ -196,7 +196,7 @@ type Encoder struct { clock time.Duration frameInterval time.Duration ptsOffset time.Duration - tsSpace [PktSize]byte + tsSpace [PacketSize]byte pesSpace [pes.MaxPesSize]byte psiCount int @@ -269,7 +269,7 @@ func (e *Encoder) Encode(nalu []byte) error { } } e.psiCount-- - _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:PktSize])) + _, err := e.dst.Write(pkt.Bytes(e.tsSpace[:PacketSize])) if err != nil { return err } @@ -291,7 +291,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: patTable, } - _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PktSize])) + _, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PacketSize])) if err != nil { return err } @@ -314,7 +314,7 @@ func (e *Encoder) writePSI() error { AFC: hasPayload, Payload: pmtTable, } - _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PktSize])) + _, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PacketSize])) if err != nil { return err } diff --git a/stream/mts/mpegts.go b/stream/mts/mpegts.go index 3756262d..e48f17f0 100644 --- a/stream/mts/mpegts.go +++ b/stream/mts/mpegts.go @@ -33,7 +33,7 @@ import ( ) const ( - PktSize = 188 + PacketSize = 188 PayloadSize = 176 ) @@ -130,13 +130,13 @@ type Packet struct { // FindPMT will take a clip of mpegts and try to find a PMT table - if one // is found, then it is returned, otherwise nil and an error is returned. func FindPMT(d []byte) (p []byte, err error) { - if len(d) < PktSize { + if len(d) < PacketSize { return nil, errors.New("Mmpegts data not of valid length") } - for i := 0; i < len(d); i += PktSize { + for i := 0; i < len(d); i += PacketSize { pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2]) if pid == pmtPid { - p = d[i+4 : i+PktSize] + p = d[i+4 : i+PacketSize] return } } @@ -169,8 +169,8 @@ func asByte(b bool) byte { // ToByteSlice interprets the fields of the ts packet instance and outputs a // corresponding byte slice func (p *Packet) Bytes(buf []byte) []byte { - if buf == nil || cap(buf) != PktSize { - buf = make([]byte, 0, PktSize) + if buf == nil || cap(buf) != PacketSize { + buf = make([]byte, 0, PacketSize) } buf = buf[:0] stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 - From c4e2ca64a95e52d1de8baba5128633b1eaabb035 Mon Sep 17 00:00:00 2001 From: saxon Date: Sat, 12 Jan 2019 17:51:39 +1030 Subject: [PATCH 137/137] psi: modified addPadding to only do one resize --- stream/mts/psi/helpers.go | 7 +++++-- stream/mts/psi/psi.go | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stream/mts/psi/helpers.go b/stream/mts/psi/helpers.go index 42f0c5fd..9beecab4 100644 --- a/stream/mts/psi/helpers.go +++ b/stream/mts/psi/helpers.go @@ -132,8 +132,11 @@ func trimTo(d []byte, t byte) []byte { // addPadding adds an appropriate amount of padding to a pat or pmt table for // addition to an mpegts packet func addPadding(d []byte) []byte { - for len(d) < PktSize { - d = append(d, 0xff) + t := make([]byte, PacketSize) + copy(t, d) + padding := t[len(d):] + for i := range padding { + padding[i] = 0xff } return d } diff --git a/stream/mts/psi/psi.go b/stream/mts/psi/psi.go index 4d76110d..09028ba6 100644 --- a/stream/mts/psi/psi.go +++ b/stream/mts/psi/psi.go @@ -26,9 +26,8 @@ LICENSE package psi -// Misc consts const ( - PktSize = 184 + PacketSize = 184 // packet size of a psi. ) // Lengths of section definitions