From 158811d0e8a44458d2bb454d45ac788a063817f8 Mon Sep 17 00:00:00 2001 From: q191201771 <191201771@qq.com> Date: Sat, 24 Apr 2021 12:21:44 +0800 Subject: [PATCH 1/5] =?UTF-8?q?[fix]=20rtmp=20ClientSession=E6=8F=A1?= =?UTF-8?q?=E6=89=8B=EF=BC=8Cc2=E7=9A=84=E5=8F=91=E9=80=81=E6=97=B6?= =?UTF-8?q?=E6=9C=BA=EF=BC=8C=E7=94=B1=E6=94=B6=E5=88=B0s0s1s2=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=94=B6=E5=88=B0s0s1=E5=B0=B1=E5=8F=91=E9=80=81?= =?UTF-8?q?=EF=BC=8C=E8=A7=A3=E5=86=B3=E6=8F=A1=E6=89=8B=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E7=9A=84case=20#42?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/rtmp/client_session.go | 9 +- pkg/rtmp/handshake.go | 164 ++++++++++++++++++++----------------- pkg/rtmp/handshake_test.go | 128 ++++++++++++++++++++++++++++- 3 files changed, 218 insertions(+), 83 deletions(-) diff --git a/pkg/rtmp/client_session.go b/pkg/rtmp/client_session.go index e6d2074..30319d9 100644 --- a/pkg/rtmp/client_session.go +++ b/pkg/rtmp/client_session.go @@ -297,15 +297,20 @@ func (s *ClientSession) handshake() error { return err } - if err := s.hc.ReadS0S1S2(s.conn); err != nil { + if err := s.hc.ReadS0S1(s.conn); err != nil { return err } - nazalog.Infof("[%s] < R Handshake S0+S1+S2.", s.uniqueKey) + nazalog.Infof("[%s] < R Handshake S0+S1.", s.uniqueKey) nazalog.Infof("[%s] > W Handshake C2.", s.uniqueKey) if err := s.hc.WriteC2(s.conn); err != nil { return err } + + if err := s.hc.ReadS2(s.conn); err != nil { + return err + } + nazalog.Infof("[%s] < R Handshake S2.", s.uniqueKey) return nil } diff --git a/pkg/rtmp/handshake.go b/pkg/rtmp/handshake.go index 40ae5ea..24624f4 100644 --- a/pkg/rtmp/handshake.go +++ b/pkg/rtmp/handshake.go @@ -12,9 +12,12 @@ import ( "bytes" "crypto/hmac" "crypto/sha256" + "fmt" "io" "time" + "github.com/q191201771/lal/pkg/base" + "github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/nazalog" ) @@ -24,24 +27,26 @@ import ( const version = uint8(3) const ( + c0c1Len = 1537 c2Len = 1536 + s0s1Len = 1537 s1Len = 1536 s2Len = 1536 - c0c1Len = 1537 - s0s1Len = 1537 s0s1s2Len = 3073 ) const ( clientPartKeyLen = 30 + clientFullKeyLen = 62 serverPartKeyLen = 36 serverFullKeyLen = 68 keyLen = 32 ) var ( - clientVersion = []byte{0x0C, 0x00, 0x0D, 0x0E} - serverVersion = []byte{0x0D, 0x0E, 0x0A, 0x0D} + clientVersionMockFromFFMPEG = []byte{9, 0, 124, 2} // emulated Flash client version - 9.0.124.2 on Linux + clientVersion = []byte{0x0C, 0x00, 0x0D, 0x0E} + serverVersion = []byte{0x0D, 0x0E, 0x0A, 0x0D} ) // 30+32 @@ -69,20 +74,19 @@ var serverKey = []byte{ var random1528Buf []byte -type HandshakeClient interface { +type IHandshakeClient interface { WriteC0C1(writer io.Writer) error - ReadS0S1S2(reader io.Reader) error + ReadS0S1(reader io.Reader) error WriteC2(writer io.Writer) error + ReadS2(reader io.Reader) error } type HandshakeClientSimple struct { - c0c1 []byte - c2 []byte + buf []byte } type HandshakeClientComplex struct { - c0c1 []byte - c2 []byte + buf []byte } type HandshakeServer struct { @@ -91,77 +95,78 @@ type HandshakeServer struct { } func (c *HandshakeClientSimple) WriteC0C1(writer io.Writer) error { - c.c0c1 = make([]byte, c0c1Len) - c.c0c1[0] = version - bele.BEPutUint32(c.c0c1[1:5], uint32(time.Now().UnixNano())) - //c.c0c1[1] = 0 - //c.c0c1[2] = 0 - //c.c0c1[3] = 0 - //c.c0c1[4] = 0 - //c.c0c1[5] = 9 - //c.c0c1[6] = 0 - //c.c0c1[7] = 124 - //c.c0c1[8] = 2 - random1528(c.c0c1[9:]) - - _, err := writer.Write(c.c0c1) + c.buf = make([]byte, c0c1Len) + c.buf[0] = version + bele.BEPutUint32(c.buf[1:5], uint32(time.Now().UnixNano())) + bele.BEPutUint32(c.buf[5:9], 0) // 4字节模式串保持为0,标识是简单模式 + random1528(c.buf[9:]) + + _, err := writer.Write(c.buf) return err } -func (c *HandshakeClientSimple) ReadS0S1S2(reader io.Reader) error { - s0s1s2 := make([]byte, s0s1s2Len) - if _, err := io.ReadAtLeast(reader, s0s1s2, s0s1s2Len); err != nil { - return err - } - //if s0s1s2[0] != version { - // return ErrRTMP - //} - // use s2 as c2 - c.c2 = append(c.c2, s0s1s2[s0s1Len:]...) +func (c *HandshakeClientSimple) ReadS0S1(reader io.Reader) error { + _, err := io.ReadAtLeast(reader, c.buf, s0s1Len) + return err +} - return nil +func (c *HandshakeClientSimple) WriteC2(writer io.Writer) error { + // use s1 as c2 + _, err := writer.Write(c.buf[1:]) + return err } -func (c *HandshakeClientSimple) WriteC2(write io.Writer) error { - _, err := write.Write(c.c2) +func (c *HandshakeClientSimple) ReadS2(reader io.Reader) error { + _, err := io.ReadAtLeast(reader, c.buf, s2Len) return err } func (c *HandshakeClientComplex) WriteC0C1(writer io.Writer) error { - c.c0c1 = make([]byte, c0c1Len) - c.c0c1[0] = version - bele.BEPutUint32(c.c0c1[1:5], uint32(time.Now().UnixNano())) - // - copy(c.c0c1[5:], clientVersion) - random1528(c.c0c1[9:]) - // offset - c.c0c1[9] = 0 - c.c0c1[10] = 0 - c.c0c1[11] = 0 - c.c0c1[12] = 0 - // digest - makeDigestWithoutCenterPart(c.c0c1[1:], 12, clientKey[:clientPartKeyLen], c.c0c1[13:]) - _, err := writer.Write(c.c0c1) + c.buf = make([]byte, c0c1Len) + + c.buf[0] = version + // mock ffmpeg + bele.BEPutUint32(c.buf[1:5], 0) + copy(c.buf[5:9], clientVersionMockFromFFMPEG) + random1528(c.buf[9:]) + + offs := int(c.buf[9]) + int(c.buf[10]) + int(c.buf[11]) + int(c.buf[12]) + offs = (offs % 728) + 12 + makeDigestWithoutCenterPart(c.buf[1:c0c1Len], offs, clientKey[:clientPartKeyLen], c.buf[1+offs:]) + + _, err := writer.Write(c.buf) return err } -func (c *HandshakeClientComplex) ReadS0S1S2(reader io.Reader) error { - s0s1s2 := make([]byte, s0s1s2Len) - if _, err := io.ReadAtLeast(reader, s0s1s2, s0s1s2Len); err != nil { +func (c *HandshakeClientComplex) ReadS0S1(reader io.Reader) error { + s0s1 := make([]byte, s0s1Len) + if _, err := io.ReadAtLeast(reader, s0s1, s0s1Len); err != nil { return err } - //if s0s1s2[0] != version { - // return ErrRTMP - //} - // TODO chef: 这里复杂模式的 c2 构造没有完全按照规范 - // nginx rtmp module 作为 server 端时,不会校验 c2 内容 - c.c2 = append(c.c2, s0s1s2[s0s1Len:]...) + c2key := parseChallenge(s0s1, serverKey[:serverPartKeyLen], clientKey[:clientFullKeyLen]) + + // simple mode + if c2key == nil { + // use s1 as c2 + copy(c.buf, s0s1[1:]) + return nil + } + + // complex mode + random1528(c.buf) + replayOffs := c2Len - keyLen + makeDigestWithoutCenterPart(c.buf[:c2Len], replayOffs, c2key, c.buf[replayOffs:replayOffs+keyLen]) return nil } -func (c *HandshakeClientComplex) WriteC2(write io.Writer) error { - _, err := write.Write(c.c2) +func (c *HandshakeClientComplex) WriteC2(writer io.Writer) error { + _, err := writer.Write(c.buf[:c2Len]) + return err +} + +func (c *HandshakeClientComplex) ReadS2(reader io.Reader) error { + _, err := io.ReadAtLeast(reader, c.buf, s2Len) return err } @@ -173,7 +178,7 @@ func (s *HandshakeServer) ReadC0C1(reader io.Reader) (err error) { s.s0s1s2 = make([]byte, s0s1s2Len) - s2key := parseChallenge(c0c1) + s2key := parseChallenge(c0c1, clientKey[:clientPartKeyLen], serverKey[:serverFullKeyLen]) s.isSimpleMode = len(s2key) == 0 s.s0s1s2[0] = version @@ -208,8 +213,8 @@ func (s *HandshakeServer) ReadC0C1(reader io.Reader) (err error) { return nil } -func (s *HandshakeServer) WriteS0S1S2(write io.Writer) error { - _, err := write.Write(s.s0s1s2) +func (s *HandshakeServer) WriteS0S1S2(writer io.Writer) error { + _, err := writer.Write(s.s0s1s2) return err } @@ -221,19 +226,21 @@ func (s *HandshakeServer) ReadC2(reader io.Reader) error { return nil } -func parseChallenge(c0c1 []byte) []byte { - //if c0c1[0] != version { +// c0c1 clientPartKey serverFullKey +// s0s1 serverPartKey clientFullKey +func parseChallenge(b []byte, peerKey []byte, key []byte) []byte { + //if b[0] != version { // return nil, ErrRTMP //} - ver := bele.BEUint32(c0c1[5:]) + ver := bele.BEUint32(b[5:]) if ver == 0 { nazalog.Debug("handshake simple mode.") return nil } - offs := findDigest(c0c1[1:], 764+8, clientKey[:clientPartKeyLen]) + offs := findDigest(b[1:], 764+8, peerKey) if offs == -1 { - offs = findDigest(c0c1[1:], 8, clientKey[:clientPartKeyLen]) + offs = findDigest(b[1:], 8, peerKey) } if offs == -1 { nazalog.Warn("get digest offs failed. roll back to try simple handshake.") @@ -242,20 +249,21 @@ func parseChallenge(c0c1 []byte) []byte { nazalog.Debug("handshake complex mode.") // use c0c1 digest to make a new digest - digest := makeDigest(c0c1[1+offs:1+offs+keyLen], serverKey[:serverFullKeyLen]) + digest := makeDigest(b[1+offs:1+offs+keyLen], key) return digest } -func findDigest(c1 []byte, base int, key []byte) int { +// @param b c1或s1 +func findDigest(b []byte, base int, key []byte) int { // calc offs - offs := int(c1[base]) + int(c1[base+1]) + int(c1[base+2]) + int(c1[base+3]) + offs := int(b[base]) + int(b[base+1]) + int(b[base+2]) + int(b[base+3]) offs = (offs % 728) + base + 4 // calc digest digest := make([]byte, keyLen) - makeDigestWithoutCenterPart(c1, offs, key, digest) + makeDigestWithoutCenterPart(b, offs, key, digest) // compare origin digest in buffer with calced digest - if bytes.Compare(digest, c1[offs:offs+keyLen]) == 0 { + if bytes.Compare(digest, b[offs:offs+keyLen]) == 0 { return offs } return -1 @@ -288,6 +296,8 @@ func random1528(out []byte) { func init() { random1528Buf = make([]byte, 1528) - //hack := fmt.Sprintf("random buf of rtmp handshake gen by %s", base.LALRTMPHandshakeWaterMark) - //copy(random1528Buf, []byte(hack)) + hack := []byte(fmt.Sprintf("random buf of rtmp handshake gen by %s", base.LALRTMPHandshakeWaterMark)) + for i := 0; i < 1528; i += len(hack) { + copy(random1528Buf[i:], hack) + } } diff --git a/pkg/rtmp/handshake_test.go b/pkg/rtmp/handshake_test.go index 6a77923..3c79398 100644 --- a/pkg/rtmp/handshake_test.go +++ b/pkg/rtmp/handshake_test.go @@ -10,12 +10,126 @@ package rtmp_test import ( "bytes" + "encoding/hex" "testing" . "github.com/q191201771/lal/pkg/rtmp" "github.com/q191201771/naza/pkg/assert" ) +func TestHandshakeServerCase(t *testing.T) { + type c struct { + c0c1hexstream string + c2hexstream string + } + + golden := []c{ + // obs + { + c0c1hexstream: "03f8a2c43a00000000f7982a2ece97a06e1f8e4e7156cf545c7546f8432fced96055a16f7bc212c95da8aeee3b55b60c33a2a0960792ef0b32e9739d2d64dd243c15ef421824585e52bbcdec2f91c8b663bbda29751567d70e4f9bb05f9d90a4431c561269287ed332298eff5a6b54ce3f875d08039f4b341a29c1505fa29bb92ea5c0981bca9e8f49800502740052b730fc9668532ba5907760e74b39036d432695f3a914ccc5b6215ee57366deb2d7421a2f205fc084f439f9763764d50864752814807aec8cab6eb712b1408fcd4329737c6c22ad735e000b63fc386784ca41efa7ba5664686778fdbaf64c845f6e5a7e01410460f9c84771326c5b9bc7631cac9fc55ac203766414e9f07fab615621a632262ef547c74db74d4556a055f15f95ac2c5dfd99f61f6fc1f7766e9bbc03ac5480530813370e8279c2418867b746fd35ee322a8f1a35a229ad624a7d8856a8b1d91a5d9a1e4aaf75281f2444461972a423548563017035ea2c6b8b74c4519d7bbf38869d4c2384daf97a2f4f7929eeab3d5b0ec5e22dab31f70212d3dd32d55c4c019da4623c635e25701aef57251da712309379991883aefc03801c25426d397212912c180941e81512ad0c435c2d61f53508edbe0099a8bb76b5113a1a868f605ac4d46e7924ce525b53cc5b19266ac359d4126e2c1533966e6be0f34631b512352fbf355f7ed79a41d8b2b91ca3918c636497ad218237ae0bcb3dbe5af708cb7facd1b96af1a9744df1344126d6cdfa02219edf1fe09806125ea31e6f50a977444ee60c0c1011e42255431c2b8f97884c8a54961b2c3e882a84d0a9568fb6b826257ad9281628e25d81ecb52a0f1fba0dd3184d367ee0987fa4b1ba0d068de45b0bb4e679b37c3619b3d83448cef47e053303fa515033e87426058138c51e791e2427d321d5bdae2d6102312b6e44931e70a1715666ec1d3e663c8b1e345c2a476c7e0b2e11fc9874e4fbcc67f96aad42a2234a076b64691445b73f13e21c193c4158b714071e03114a66a86363b7d344e2e7ac27c7baab48178473024203e56d6b1a39560693f53e348f876c96d6ca37772ece65d8ba4c436cdd7e62f80ffb76ce9fd55cffbcee4cce30ba4d3f4cda7a1cfbc31160f49a5995f8214c3f25474a3271de041d8cd9275bd87d3be05c074381986218a8490a7192ee695a9de1ad60013cb7373a82c45e71de41370ee97245c0b9227b682bd124bacb7820fd30875a7960a16102dac429bacec43a7a33e04e0ab753617123163f12f07046df51a1203518063fe65f1f29210ad14ae622f061455c835c76e51e34b5c06a586cd89148acbf175ffae528105f88126dea4cb75bb0c51a5f97d6a856f37fa066285f30335955b72cfe5c44388e443510f2ce2719c57e8304d3b9f75439f7c85328eade2e2200df2c31cf816484cd4906559853573d2136310845c058bd3df4367a5f015d4a722305e85a245a9463cc080f8e9d228a8cd9153ac3997d4155e079e95cfd760602df735813fe351912a83be8453c17428e11769ccb927aa2ad753b2d85da2a55635d70159b24034f814158cd969034b1879c00c7759024926e24086b37d70fec59777ac4d3b03158021f508008d323df1dd373e1564e29f6e22956abb0f357a9d7d33ffbb8f36c9e9ef5740fd380291293a048fea717263b28165b3568ab0b8f754622dd10d03fe53d037567c1d4279f65e5002eaa725452dd02342b4d102899563a46d47a0b20bb04ad51e1ba113682250446b79d3a3a3e77485ecdb78c67d7287747e85f1a60a2a98e63befd222b12db4006f37dee0ba3e28e56ba67c13e8604810b50ff4f43b6ff0239f722e27391c165035518870d637b4e2027b18600b677d80a4a539b09332479342eab377d3b99c7516f1a1d070f71b207bb931c542105272004cac161bffeb57906ec671bf88bb8438469ed08b05daa1a7ddbec2682ff3d0808b4511ec5880206984061108ec3d7604dd167757252de7248f5f661377c611c6f0d22445b15a0177b15da0edb45b80fed34f20a38b97023b51d903da6758e406606ce48e137064ecbbc3e7a20f2dc2ce2299c5d71e481372fd3bc30a8cbcc3cc7a14e2900255f690b5976653715d03c12ed20015239ac182543e752a85ce94c0a41c26c4e2f3c45", + c2hexstream: "9fa274a00000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + }, + // ffmpeg + { + c0c1hexstream: "030000000009007c02f778551eceab8e1e362f07c5868a70b266d40220e5086118a22e3697d5fc2b6329c22ee7c7c9b0b5b345dfa8398fea3bc879208a1068425aa1174cb57258cfebe24da42ec4ff9da19c7b406f18c95bfdd04ee48b6f833214a47b8bf68eb05254e25ae9a9a8b496c5496f6e9cd7c68cb6bcc84169e67f3e372ffe186da8513862c5afdeb5a1218a860baccdd869b253dbab8a0efb59a48c442099875e55b74fd07a1b69af303e099873cb77e2d9e2c4741eb1f506a7f0b2962af91f5051c890a320994f4c8b36a01fa9c0f15f4b7cf7b6c3817da60918611d3b02ff7cc94038937537d3382e416bd57577834df9caf748352150ce92d0dbb40e871fc364e18b7e3569e39876c1ae0cb79ff6d7ab32f9f011869c8da4191ef46d3c1bc6b72d83812f2243d1752a412632d66a2075f5d302f45cdf2b98b802b79805dc4837d84eded1cc42cba7eec3137bf86dd0b2d7f624d521a3ff655bcf0df4101341d230e94651628e45a7abf8183101ce981e5e86a6faec5c79aa7ec7181ea459ed966e0c932a6f9e4bf554ab7b1c905cde054ed40d2fb89bbfc65752e58cfde4f2a04dc3ad1e5ef4fc64d57b071c14146a451f6a043156d2880f5957d78dfd3ff2b880f624ac70f27094f37734eaf1924f66ac3d648ae232930544a4420b65eed4696f7f50fe05fc948516650bfb9b65f953d848e3456965d0d76b627a84e35e9ddabd8b43f0fadaee458cdf59ebd773922e0ae507d5e64092e90ec67ed8439e8f221c753a9f9216f1cb85b5107bc7cc7c4fb2c3eed1afc709b192f50de69c1ffca5650efb523b5fab5997cac4b7ef5af2bf5973e6293bae1fc1da499be5515077de4ddb3f7fbcd741711c62b7d803b8d054cf578182714d1c445bd692ba1b839a09e7266fc85daa8fad433dabbd206cde1ea571770c6bfa7956c266ee8435eb01caa6f583c0d83c33eefcf3f97db917bdb299f8a89fb8860ab1cd644c1e82cce1323a9eb79d25f5dc570b70b40bb20061743353d0536ecdabd82f2277756228cb54fe6932c4c9bd130d2bfc700bf5d85da6946e96f82e0afd8e9c20cfeabf9a6a189bb17bbef539d1d9e73ee966fcb5d6bdbf98444de865be1605e34087b34cdf04e3aff55bd4137c522d2e3c7bf2634db326b22a474db7f8a0952ab3510de555396c18a378a8390b73bff7772fb2760fa5786a3b0459c45a1d1b0dff4cbbb65da0ed34f32116c3c2cc2b95825111bcfbb246c1505aa00c970d06d76cc950ce368b220db20487cba02bb1fc54b36567b42913d6b885062cd7795601d31a4a8e6d56467fa5c52162676fd526122513c3e47f2e6d9d6a19c4f8795431a94ced039221d27ac68a7a770e9305252271459b71d05505c4977b70e96851208bacdcdf7e682b3848840880f1fec2a32fb1406862bc83cad89adf729f3ba6ffd3fe808e8403e7c6a0f6dcda3211898f6f16505b078cc78b80db600ded2f200f25ff5dcb31d32cbe2056b8bdc31091631abbd2ac28d61fe32480ec5035bbf35f173393f0d59a21d2300b2db4b59eaa63ffe0f93d65dc028516905f22a9c7dff387c4eb78c6300e7ebbb2a6663cfbe5a9977caf5764ba3f0aba57db4e6051e0aec6e05b1bdaa649a97875edbc40bcc4214bb7fef72730d4368acbd3e10ab4c7642a1c53f93ca26db9c766555be26fdff0e2a721425b95762b0b776fd3e3eb17313d53129c9f34003c99a5196a8c6aacb6d64b51dd98b27c5b59a70bd941a1ba0300fb744cfc499d997e6d93392d28a767d116dfbc2cb9eb40540d11eb304ad9322386fdc597d55d3d1c67bc54df35479afb19ea2cd204dd331efb49c1e172f6bb89fbf58207dcae6c12f8a7598bef1e872c0c35f22b4c29df10f9bb1add3aeb887ab5fb38c036b88346f761c42f295dfe0b437dfb4f30a71276ab7d409ce424963f9ebb4f1f47e52b18786f279b5a87226f66981dd6d0ad11f49af31055ff69710fda3457c4b7852db2e3936d0778bf879f1734ad3a24ffb1ae9584760b9cb0452a53ba0962ffe2f605af52830bd211a5488894cc0b052255048711cd198510a9e943bf8b839198455fbd41073005d303990b88d9b63656d43cfec8ed83748f4b0f0fc5120216794b22a054e5bc58abd8c41096070884393453ce509694afbeabe0", + c2hexstream: "be0c5bc80d0e0a0d72616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e30202867159f078a4353eb4680afeba40df616cc82b44ccd468e28bbeeed690f66f32473627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + }, + // vlc + { + c0c1hexstream: "030000000009007c02f778551eceab8e1e362f07c5868a70b266d40220e5086118a22e3697d5fc2b6329c22ee7c7c9b0b5b345dfa8398fea3bc879208a1068425aa1174cb57258cfebe24da42ec4ff9da19c7b406f18c95bfdd04ee48b6f833214a47b8bf68eb05254e25ae9a9a8b496c5496f6e9cd7c68cb6bcc84169e67f3e372ffe186da8513862c5afdeb5a1218a860baccdd869b253dbab8a0efb59a48c442099875e55b74fd07a1b69af303e099873cb77e2d9e2c4741eb1f506a7f0b2962af91f5051c890a320994f4c8b36a01fa9c0f15f4b7cf7b6c3817da60918611d3b02ff7cc94038937537d3382e416bd57577834df9caf748352150ce92d0dbb40e871fc364e18b7e3569e39876c1ae0cb79ff6d7ab32f9f011869c8da4191ef46d3c1bc6b72d83812f2243d1752a412632d66a2075f5d302f45cdf2b98b802b79805dc4837d84eded1cc42cba7eec3137bf86dd0b2d7f624d521a3ff655bcf0df4101341d230e94651628e45a7abf8183101ce981e5e86a6faec5c79aa7ec7181ea459ed966e0c932a6f9e4bf554ab7b1c905cde054ed40d2fb89bbfc65752e58cfde4f2a04dc3ad1e5ef4fc64d57b071c14146a451f6a043156d2880f5957d78dfd3ff2b880f624ac70f27094f37734eaf1924f66ac3d648ae232930544a4420b65eed4696f7f50fe05fc948516650bfb9b65f953d848e3456965d0d76b627a84e35e9ddabd8b43f0fadaee458cdf59ebd773922e0ae507d5e64092e90ec67ed8439e8f221c753a9f9216f1cb85b5107bc7cc7c4fb2c3eed1afc709b192f50de69c1ffca5650efb523b5fab5997cac4b7ef5af2bf5973e6293bae1fc1da499be5515077de4ddb3f7fbcd741711c62b7d803b8d054cf578182714d1c445bd692ba1b839a09e7266fc85daa8fad433dabbd206cde1ea571770c6bfa7956c266ee8435eb01caa6f583c0d83c33eefcf3f97db917bdb299f8a89fb8860ab1cd644c1e82cce1323a9eb79d25f5dc570b70b40bb20061743353d0536ecdabd82f2277756228cb54fe6932c4c9bd130d2bfc700bf5d85da6946e96f82e0afd8e9c20cfeabf9a6a189bb17bbef539d1d9e73ee966fcb5d6bdbf98444de865be1605e34087b34cdf04e3aff55bd4137c522d2e3c7bf2634db326b22a474db7f8a0952ab3510de555396c18a378a8390b73bff7772fb2760fa5786a3b0459c45a1d1b0dff4cbbb65da0ed34f32116c3c2cc2b95825111bcfbb246c1505aa00c970d06d76cc950ce368b220db20487cba02bb1fc54b36567b42913d6b885062cd7795601d31a4a8e6d56467fa5c52162676fd526122513c3e47f2e6d9d6a19c4f8795431a94ced039221d27ac68a7a770e9305252271459b71d05505c4977b70e96851208bacdcdf7e682b3848840880f1fec2a32fb1406862bc83cad89adf729f3ba6ffd3fe808e8403e7c6a0f6dcda3211898f6f16505b078cc78b80db600ded2f200f25ff5dcb31d32cbe2056b8bdc31091631abbd2ac28d61fe32480ec5035bbf35f173393f0d59a21d2300b2db4b59eaa63ffe0f93d65dc028516905f22a9c7dff387c4eb78c6300e7ebbb2a6663cfbe5a9977caf5764ba3f0aba57db4e6051e0aec6e05b1bdaa649a97875edbc40bcc4214bb7fef72730d4368acbd3e10ab4c7642a1c53f93ca26db9c766555be26fdff0e2a721425b95762b0b776fd3e3eb17313d53129c9f34003c99a5196a8c6aacb6d64b51dd98b27c5b59a70bd941a1ba0300fb744cfc499d997e6d93392d28a767d116dfbc2cb9eb40540d11eb304ad9322386fdc597d55d3d1c67bc54df35479afb19ea2cd204dd331efb49c1e172f6bb89fbf58207dcae6c12f8a7598bef1e872c0c35f22b4c29df10f9bb1add3aeb887ab5fb38c036b88346f761c42f295dfe0b437dfb4f30a71276ab7d409ce424963f9ebb4f1f47e52b18786f279b5a87226f66981dd6d0ad11f49af31055ff69710fda3457c4b7852db2e3936d0778bf879f1734ad3a24ffb1ae9584760b9cb0452a53ba0962ffe2f605af52830bd211a5488894cc0b052255048711cd198510a9e943bf8b839198455fbd41073005d303990b88d9b63656d43cfec8ed83748f4b0f0fc5120216794b22a054e5bc58abd8c41096070884393453ce509694afbeabe0", + c2hexstream: "e25c15c22caf72d0986fbd3eda0d7151973e19083e0abbefa76e17d8a5fa9570ed547a9786e535612d45228c773e0ebef805ac8764a67552496a3cc3355805f99d03606784b05f55361fb4c66e6164f9b882156930ed76e7f27df8ed6ae6c0837b41413ead7023f2bd2e9dced663c8305cbabc22c3ebba207ea2a8c35ecf7828dce2633d38c8c2e9decf2553b161a212fc9a962e4490fba31d24a2eba8ebb4a60d6cf429142ad26fb656b9f12f4b1c22c0cd65497864dd83f0d03162ddedbc4e857c0ca391ed2f1e5a64948d096081a114721c0bc8a1635bf27036b8bcc09ed5b11d85bd547ca361e2d865842d7fe64bc3a29895a97cec5b2d29294f41abc2bc466cf38d8d00886036bfa45e4bdf484e9e5fe10b024a986e63f2b0e109a3ea420e25288bcac6931141f876b57e37f39c8c1bff23b566a7887b18b618ca4ef347009dd5005e7f413becfd0b26fffe15ec6ec998226e39365626c560cb2413527ce574c0a436f1b1788ac8bcd8d4e0bed13e7c799673069a7d8249c003b633ec6587d3e2d7d3f5aa4007149c04ac3df0a348aa8b27c986684afb93870ec5a623cad0d074d98cfbc2e1c424229bd020e5cc45548ac4f8938952a4b24cac82a602e9d8c026480d108915efe0b28d7efcd34e38395b7128cacdbaa83500d408d26ad913024d5d9ee2a0938dc31f6abc531f5f5b82ac56afd454b1d429956aae6bb5836d76ade8b8276e9795dd1e7f79a20f5a09296a7381d58f9670c30b869ac8022459fc883ccdc16eb68cd6c123562a416b99597635057d8592cfaa71f4f430054b69f5419cf8399c75c3c3e9b6db0d1c0292b5f88ebd322aa4667e7d6abaa752019a850c0d054e879cec2b2d943ab7bc7410ef715feaad9e6a8fc6a9053feafc60af15e46f47d3bf06c6e7001c1cd743d00d4313bfe73cfdfa3422f175965a5cf2f2159457d394b7e1fcb573a994db6789fbccf7b48056bca207f9039fa753bea7e4b58fc66b1e38963fe0b69cbd54b6d72aeb793502afb19af949e69a00d61a93f453603098bc40d3f6e22db5586c54a2f34d85415394c277aae2269cb7f4d612d699ca72ad222bccdec6ba4303455c70f4c9c19c8d0e6a0729fafa0b6962fec2c1f4709c7099fe64424e03f021d0e2b20c2196c25d3911995bbee34993c5cb42090946f4672d53a6d5e2334f6ca419c255b9eeb3f5c48d04fd79e364002bb7e923e523713d86cbce7750b1112fd52660bc5d2df7c6f6faf22d2c17d86bda72a45a83017dd4eee9b6754ce0ed8a84feefa7107cd996915bc73fa772bb5bbab82dc6fdf11d60ab5fac043d739f8d8b4df56cbf4a26352c5c874aff6ff14f951522501d602a128a3d9c7c3b23bdc78724c7f1712a4c094d42e84e0f7d8abee9e9a1b9aec34b52d41933b1bc518235a9ca324d7e6fd953ff0bf2d70fed5ddd11b6b0ece00f49bccd9124bfa9bbe3a042f440e68d781cc7fb576a9ba35a9b91e7c5d57bfa3d19490c268e65fc526fc6064941ab91b879a39d76c25b0a86d6179e4b4d0eb8350f08fb8a283809d4419ef5e05eff642267fe6420c809f54e591522198d345b4308a43f2209901d9c0a9cdae324137c2471b36d669cec915310a0c94a4199f2dda891ba4123789f8c70178e2fa6e2dfad26c2e3d875f536e54dd1dc4ab3953c960cf27154b95f041ed92f4a75618689f6ed9b278b4d871a3f9eacb37732ff918ef1be7a1b4466361caf7e000142bd008d6f89fd2ac83b289be32fced5a36b55f6dfc87563c847f67a0bb089784f56673a90e34508a3fce3e89d4291a72f7e132a89b18aba0d56f4a919e070d0de247ecccd11674b9ad30a095227ed83554c6fb5ca6ae820348cb95cb0566fee97b7f3a795b894b2d360993e7bf4a32e8a80637691c73fccbb36bf74a620df4da66ba390a2e12683d7ef1840fbaf5f0147ef1c25fc8fcde45e5e4a255b76d956388956bbf0528e8cc85292b6c4cf9aa0941ebecc7b5627fda32ce75ed2791164b86ad3b2742f9ce7833ae3c90a240b5b01760db94853689c41c08fa2c6bd29b0d5cd9a8dd9bfacf01f35d9349f7a663f6ea8ed7540ce8d683fa14c01095c94735a19a23fc386c241e5b5a6bd93362aaa00eb6ee637cad57344788addf645f571c7fb90ecde7a5460e383b9502a0c4e2838", + }, + } + + for _, item := range golden { + c0c1buf, _ := hex.DecodeString(item.c0c1hexstream) + c2buf, _ := hex.DecodeString(item.c2hexstream) + var hs HandshakeServer + var err error + wb := &bytes.Buffer{} + c0c1 := &bytes.Buffer{} + c0c1.Write(c0c1buf) + c2 := &bytes.Buffer{} + c2.Write(c2buf) + + err = hs.ReadC0C1(c0c1) + assert.Equal(t, nil, err) + err = hs.WriteS0S1S2(wb) + assert.Equal(t, nil, err) + err = hs.ReadC2(c2) + assert.Equal(t, nil, err) + } +} + +func TestHandshakeClientCase(t *testing.T) { + type s struct { + s0s1hexstream string + s2hexstream string + } + + golden := []s{ + // simple #42 + { + s0s1hexstream: "0360812e7a0d0e0a0d66e7020000000000000000000000000000000000000000004f0b57fc7f0000881e7839187f000028007c0000000000701d9f3a187f000028007c0000000000503b222d187f000088be663d187f0000004b78000000000000000000000000007d6e0d39187f0000a82b242d187f00002c720d39187f0000a82b242d187f00003030500000000000586cf42c187f00003c4c4400000000000809ee2c187f000020d950000000000028007c00000000000000000000000000004b780000000000937e0d39187f00000809ee2c187f0000012ad536187f000000062e39187f0000b2a6440000000000503b222d187f0000e8d87bec177f00000809ee2c187f000073e8d436187f0000a82b242d187f0000e8d87bec177f00000809ee2c187f0000a82b242d187f0000e8d87bec177f00009a83d536187f0000e8d87bec177f0000a000bb0100000000c08c2246187f0000f8cc22463162d6c4e59eb81ee3bb3226280081aebf11abb63cb10fed5d6c12186de78b1400000000000000000000000008cc8b36187f0000e8d87bec177f00000000000000000000c08c2246187f0000d0ed5e37187f0000e8d87bec177f00000000000000000000d017f52c187f0000f077d536187f00000000000000000000ff511737187f0000c08c2246187f000020dcef2c187f0000d0ed5e37187f0000c8c6ea2c187f0000e0501737187f000028007c000000000030c90036187f0000c2f5ff35187f0000706d523e187f000040eaff35187f0000000000000000000020dcef2c187f0000e8d87bec177f0000000000000000000050303546187f00005fd10036187f0000609c770000000000000000000000000058d112ed177f000050510b57fc7f000040510b57fc7f000020510b57fc7f000030510b57fc7f000020d950000000000028007c00000000000000000000000000d0ed5e37187f0000937e0d39187f0000e8d87bec177f0000d400460000000000000000000000000028c6f42c187f00000809ee2c187f00000000000000000000004b78000000000091e8d436187f000058d112ed177f000010f72536187f000020dcef2c187f0000000000000000000050303546187f000030c90036187f000000000000000000001e531737187f000058319d35187f0000d0f0f52c187f0000e0501737187f000010f72536187f00000000000000000000000000000000000020dcef2c187f0000f75641000000000028c6f42c187f000010f72536187f00009012f52c187f000010f72536187f00000000000000000000bfe44100000000000000000000000000d017f52c187f0000f077d536187f000060edb535187f0000e8ebb535187f0000a000bb0100000000f7a86337187f000000c56337187f0000004b7800000000000bd649000000000028c6f42c187f0000d400460000000000c8c6ea2c187f0000d0ed5e37187f00000000000000000000010000000000000028c6f42c187f0000e1e4410000000000a3deac12e9c2d4c850607cec177f000008000000187f0000f4a86337000000000000000000000000402b6237ffffffff30ca9f3a187f0000e06dce0100000000f4a86337187f000000000000187f00000000000000000000ffffffff00000000102c8737187f000060edb535187f0000d0908737187f000050607cec177f000050303546187f000050303546187f000028007c000000000030ca9f3a187f0000402b6237187f0000b3a0500000000000000000000000000020540b57fc7f00001074212d187f00007298500000000000e8ebb535187f000030ca9f3a187f0000e8ebb535187f000000000000000000000000000000000000fff849000000000068837cec177f00003c4c440000000000e06dce0100000000fff8490000000000a000bb0100000000000000000000000020540b57fc7f0000000000000000000088be663d187f0000864a4400000000000000000000000000000000000000000060edb535187f000010608d37187f00002d1297d1ef166e84610000000000000050c0fc02000000002005ce0200000000b302fbc0a28ee462cb7b51e6cefaa4ddd161cb60cbe7db62191018e7932f387ebf867164f8149497cf8307800000000000", + s2hexstream: "241e34f80000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + }, + // simple nginx + { + s0s1hexstream: "03858766b80000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + s2hexstream: "858766b80000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + }, + // complex #42, + { + s0s1hexstream: "036081261c0d0e0a0d3cfb0200000000000000000000000000000000000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc010000f2c3dd5512bbf8449c70f37b590617b34b69ee7e223b6ee6a7e915c0a31e9a4000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000007fb18949de4e9c700034cf3d187f0000b834cf3d187f0000c100000000000000b8b78545187f0000b1000000000000000013cb020000000058b88545187f00008d625fd34ef5d50d1b3cc7c4fdffe0468bf460c07bc5b38714ecfc5d0dfde3b889941c3ece089cdaf39df9e006c16517afe352e90224e7a232a2f45431f81c492852529561979e856cd947d6e7dc17cce4b19479942d2386551d988102c2ae5e163f64bb5d2ed545310000000000000010b6d102000000002100000000000000d0", + s2hexstream: "036081261c0d0e0a0d3cfb0200000000000000000000000000000000000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc01000000003898bc0100000000941b5733e43abb17470100329600ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe668c9052de21ef1163bcf8b1079b3f63a5dcdaf05d369ce8e5b3e892df058e9", + }, + // complex nginx + { + s0s1hexstream: "03c81fb5df0d0e0a0d33d5259b17f8c816f456fbbf98ec7a0c7314335f11006de480ea2ad16af0949dc6b938ddb200f3a656ef65efdbe0fb4ff42eae052e1beaaf0514806f05150ccbce44a880449b279a8a8c89666c84b561b36366e17e5090836511f26a26fe35f442dd7586789c200328aa69952e1ef6e1815cc3ffad53821264747c8a72b17fb48ef43a06905a09b804724d3390431411a0d7104d2b925f8f06db1a788c992c1a8d66201dc12ad5c59c23f82d660d3e06e44f530fe1b29fe88db96019528d33dff354fcb47ed17a1af472475b7f866164d5b573b667129ef5cbff0e1d8c42fc7f96f83414caae2ebe207619a0fc7b04d1307787978a268c55259b73b1dd6f30736864873212b5f0332b0ad32785d7f8b54e804cd8a6d92ecb74a17c5110acc478114baa23009b562ca529532a004cdf4fcc2b277204553d78f6b9c907658d7f76d82a9ad9c5f0056a1a58941aa47369709e91e2a3e61f1bddd8e5e43e7263b44b8d4e24523f29bc5981507326c3dd96626e790554982031710515af787963c306b2e759f110154a9166bdb7299a4e8b08c7905d5fb18ed0b6a47f2e1de3f12395d87c86e892d07af88d3121287fad30463d8da6ee1c76a5c0f6d3ddd9c5006e9d7df4860fc4000751312879b10a2c528627e1a88f000e8b9c61725c4d897512a3e7ac33c8fb2ca00f908938717e3c95b5da9609ee3d1cf8b70ddbf6fe2cd26c7135e97a976dd42b19a8709b26ad30dc87c7e57504016dbb0e48b10c748378e5b8615f50ce337be8dcec830299b4de207bc3957fc5023ad34aebe0be6e58a327ba02778835f27011def41477a8f29723b62ca27b2edc4e78c72e363587d95c41dbd3c911c63a22a42e361cd629b3f9dfe09b5a0e77a8873ed7bd735e97d09f72a3688369a2a60ed0dc2aa605dd9fe4bd3a3eccb1e753efbe10631ca733bb1ad6239d40c6434e9620783d2555dc0a131748dfc82f32b8ed421b09ea4ec40424e8a164aee5b244052a812a805e3493757d723daca4f59ae610a3d05e68d4835076e7fe5b9a4260c4c48a4422bfd7973c49d4e8edca82d4da26a4398e79bcdeefa3dc4a3d1eaa02e2344604f31e9b2f677018553a9a2914c0cd4d4e46092c35ad087fea2729ec095e330e5151a981b819990d53333668f404b5424abf6f806c77ff569e2943a88776b6d8c75f597f79f28dcc26c3341ac7ea5c12a9cb921543916be1baae8941243f1aeb9f736b1866f8e48cbb19a683030295acce27c111b82cf273db7cb4ffbbceeb5a425672a84e57340970da8c73dc211faa48e0bb010cae74e72a28e7e5f7d2403928b2e17609157f79ef0ceccb2d0b7676eb3177f8dfebdf0913c6ef0a982f43c0e12436eb39b66428c251f4ef5c6a65489bdc407bc71f84dbe673e57ea2293f844d756f872bd3afed24a3dd810d42c9a91f0924e628a8c10e1ca78dbed0cc421d41b1a46d85545aa9f7372a057af3ae99fcd27f257a413396e8c055b88c97d5ce497a3bcece9577c5cda2ca479578e0924a5fb7c5a0ea5b88abb040374816059190405f5ed6d623a378eeea0e66caa0b1295776ca41d152ec829324caa9295b396aba974090bae309a8cd170f97b7c0c00e368a4f07dd3c897060531989ae52f368e933f9a316024ce3195b7ad01b3bde51c52d58a269e212c9352b53e47d464c667a450a9047567460b1ee30cc290e1def3c7591a557a46f8dcfc2714d08bdb38203bd134a1387abc475db909feaad8e26231fcb7ac33a0793fc78e00536938739519a8364212e29970ab936f467c41a8ae3e504a7200c3a1c841a21baada9f3fe43776365a58cfcaf4532a3acf6bd36d9a33b80c347badfcbd4018682aa7980edf0e352966f4e45b580e96176a6985049d3d00c1a8bece55fed6be197e56284d545d76bb525b16aa69acb1c40636c8a363d9650c88236276fa10906866b8b5cb062c7658778cf2d129b4a53feb6dd35f37385bbf6bbe3655dec6ce357f73f0759076de07f3c0e92d758e5d60ec20b023590bd2b4ca091a98cfd8ce3f4cceb4dd3582d52943be46c93c942a28b4da4c1dd61ec29027dd28e7a5f726e2b5dbbfeb5e950492435b5b8fef75a8a44fe4b225f374b61b51ef02f7d629ea8bf59a67442f6bd672b731f2a6a79b4af77ffd1d6", + s2hexstream: "361c38eb3a281bb78ab95f491205bd55fc7abc27eddb5158540507cb04d8a13bf4d9262f0141e68bfa46d40d4b9162470c1e6ff9f9c0514dc65818ca30b90525932c54946d3a206880f475cc86d71392f5828bee43dd3b093553d3660cd98b9f05df34721954da9a484f66ce2679601bfcec093fc94448fe971b64a4f4ef43f9ce776ce8cb46821496e8e2bc6143d85d2fe19cf826e4f6bd005b61f44aa5ee191c5a01e8a083fc366bdef3cc21cb2a50acc648d2ab3f90ab9af19fe4968dfdb3e7fe9b888197beec75b1b9977ce3e729a930fb546f8bff097d9fed132cebc614e9619c6bf85a576e0c100588f3ecb19d1cadf18b38f194b59082c9bc6d8fd056f16cc1e9c71957d3295c5b1d490dba65baabf1f29c85a82c0771e97400b9cbf1268cdbeda532c0cf8f1becd828a63de2512ed5eeb47d1abbee0330eebdfbe0e387bbd02ded90fc7cabe854d48e92b6dfc08bcd7408e830f6eb60e5a85bc58be2805b0f6deb0bea97f33e6b81d0216191ad2e05b51635ac029591aaf05636d3d691e2437dee2d14e16c7f633ca0c4cd4df2d3030908af0b9e40b58e96eb616c7d44affa32dd0e13498d76852d3a537b2d267e362e2d41cc6df65b03e2bc6f5f001e5932fb674644f4bcca21f71d9c24431a5a71479b3eb49199b7735526d256452b884092ce85868b4fa8826c44a6af5f0020a69b5e5b2cf712a04d3972a37e9e2bbe30fa43b785925f07fea3adad02adcea9482c0474241614714f8714cd253f8c5539cf0cbe626bc5600f720e111fdcba6708bedc2cd5f09d2477b1f29cf17ef22a4dfee9af6aae1079211e8a40fa45a80203842fd874ccfdec7eef886f6d7a99ba79826ae3317a5c5298e692922b3a942fbec3073390041f0ef3a77d6022161b9b99857ecaffda1c97c0ae29ece9bd1ba78122da1127f91f1aa19c7bc3b2965e4b1bdc151ab631b176e0da62c9977d7018a04b29c74448e1e5e45ca897f206e20de2f8279829d80f09b271d24aef43628f8e8c56d2d437b8b994605086673294492abbe25494f15d466230905173f3e0017f36d3536e8b0c02ec5c88538f1c9cb9d87e0d6c6f6bb2d29b43230e36040fb53ae208a86e15aa5a7133ad004f49ba27c8c79337324609cd892ddbbf31ea746bcd7c143b91be9503f14203418bbd685385fc8bb7429485cbc1608af24bfe5e187a72530c30e80f222a1263b5d0cb0955c7940c092891d4eaf25edc3d5c3a55d7aca8e3dd90f2ffba04626fd42d7829f50c36fe35c7d31fb931fbf68e364b65e2f348bf833abe3d3e20ad134e253c43327241673a1486f34681ead4b735399a298159acbb18eaf938970c86bc49c9eebb0b55f51fdbe9655dd339140872ae31f308deae20c8a8585fb4df1bfda80ab9b35faed33b9738986a72ac73e45ba4d8638286834a2edba9e3bac5e063cf99162e48e969df22014a94aebd780961506ce4d7ef2e05cad8e8859dc9e86c62fe9aaae8048a0a05d49eb39116bcf26728d63e170543e2dd2c677af54fe6574d800235848c3f8a60dd3df1f4fae35b22ba993abfdc1c9c088316fdd2fc551f7c575501e3948b4372c83466c217c1e5d15a1f90363b2c3fbe423c903e91afbbe804bccb99470e0b0f4271d25a32b72b8cd6bcc211e801cf2b3e5f69cf0e24b813e083ac2792b737d428092e5ac05ae69616a8a7feaa7629e8d593b7e3b76ff698f3a2bf8559f65981ff88dbbfe2c155f86afcf6147220fc47b3b32b6a23210216c4c29b1db8f49fb87c7a775e3ccd56a6c94dba3c6db6832069ae8a8cd08da2944f3db1073250bfaecb370d070463adcdb067091d1d8d3d863bc8130b55b59fa4f351ab25a16bd36ca2e073a6442073f4877d12a40a4f2b45173e516cf3f010e641bc0be327df4fc9bfc36f03e3e2f86b5f0a0f69593aaf717800dd6cf0ee5232aa5e15d13d649afc2709000bebf8764b0285b45bc063cc3863aaa45498f78642559b1392ffad8e27b68e32a186a8ec882da1e4ed04b026685acabcf2c1423416dd47a8dcf43703aac5354c4cdd38d40bd9b8f8de691e46c3e902b6aa44eac1213269fd26a001d166361db21455861f2f3f170da836536b1f5521c9990c8aba3ef4bd8d173d84d6a19f400dd82b5ff60a1a7c9a8e5dc5a1582df6cfb41ed80c9465", + }, + } + + s0s1s2Golden := []string{ + // simple lal + "03c9acbe800000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65c99683180000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + // simple s + "0360812d207bf2e6e89384448c138e374d4d46d437cf35b7e36bd390e94c4039c3639cb6a6342c94b899c153965f7bd495aab7b581dd7b6b4f5dec489a346a64881621353336badcc18b3666d39b49774ce93bbed5a7302ced2c659649b8e3c2b713e0db3baabee53cde521e888c7ec584a28b6058ac7d54c1cbd3198bbdc449b9b434dd65dbd192c832995fa7262c32b9a07c1853e25524b52f264fd5d4899d8fa68add91627e687e1eb13435ce50d77dbdd8b9ae3cce6a54deaa38b93bbe4fca573b6aa3aabc30b97c4dd7518eb5b7529c7feac1556324421d45e44112421a536e6ee71f3926c1a6649fe8dc63ae3df03c36b87a8bc5a5911998c314c3c658404346486d5d181aaba8eb9612a0bcebcee4b24f767ee616888dc2866097c789c41dc3406bc4441d7c3ea477d07072a55b2be5c29bdac12a6f92a1c0307850e586222ddad862e95b9194c368ed3c1c4f59eb20e5d4d21e526bb1198d305b819f669f884dea7891831c5cdc1089e251d3dc5ac7bf34cf1990871b2ca8679756b73dc7ed374f8eab54db8e4d6b7f8f466ad3143116d433976a3fad1990536056792f52a167c953a4abcbe22659795dad5b5ac7623ee3ea99299e9caae2e5e96b2343137b1b502eb02a18c76c822b20c676d0379dc2283ed5b8cb86a1bf7f13ccab1056b7516e76646f44b9e258c3afb89ad75c64e88b48a75dbf582c4554e9e15546a08f9d1de41b4ba5e6946fa4531882996d7a33a63082757197a3b787941bb6439c635187678d335d2993ea659d7cef11df2aa9169525793bb937ab54437088c8bccb57335f7b81791672c89cdfc69ecde14ed5856455a9257d636aa9bde48188b6c1ac1c43347f4a9856d77e237d5314bc2f822875333edb8799934b841bc541cd78472096b7533d1432a428a0e8256b26918484b5ab6e434ceb80c11554ecccb542d55ae22f80e752341ee3232d563bafc3a873761ea7b318367b16736ecb38a1af7b92c8ed802930901444ae53686c1d20c8842f7e4630a6ab372028ec49b3a2b64c79aabe94cb559116ecd5685fe371367789a5a6aa5260ca647ac5963c6f5379d9ee3e7cc8841cc87fda37cfcd99144b31a3e2c4e64a9551b562d9dac23b5aaa398a2eea1d33b9861de164d389622da3141e6ee359ed44155e2cd8275042ba7ab5d96cbb14345022259ede97e91a42ee2199d864952b6adc404aec7a75b6e5399658dd9b7e35a68cbc932ab4965da9a9e0901c7ca56f68cea25b5726204448a88d2d4a1253e28f1e7cabc3220f7bb4e012c16ca837bd7ecb27c6da39112aca9048238b8cee2a937ac66585c6c948b6cc10297c30d7ebe4efb8ce2fb3e1ea4a3815c6b512e14f75aea5eb84753a41483b53b55539a7482f671d5029ef415c2e4731cc43222aa9c1b89c4c3cc77676ecba3248dcc3811b318854437e86909eb7b279ebbd959b865c46bb821541e9eae42241d0d6b3dcf0423f42b2aec3576c7dc15e495debb8ab38833436ad242fa037617f151c6a144f9248ea481a50a58821edbb67e7821926ee3e4eaa53665a7cb9ca7ac63c7f1cb7b816e8bc579d4b619915ba8781c4967e12cd3856437bbbe55444ba79b5c73874c62f372db574775d7240cddcee6b620f478b4e73ef11674a4630b5e4e1d660b61488ccbbe5531f5f7cd642795095718027a9e525ab5b61e2751dce5de43d1ae9aed8ab9b32bceb9f993c20dabb8261d33a55ead69a5ac01e689d6453c36f4b804ee02a71a31c184b42292cee947ec8c0c5c19d6622646d73eac2b7bc40ec4480db57da8d5de3c9901be685996b5c683725ee8e385aec9d53b55b17df56446640844fb6d23a8e693e7bdfc8d04337165935967a80891ebc466bc434aa0f8bd485c39966ee2fb93c939f1372d33c7a335a179fc389ae86c02852ddc4526f9fc84247363767e05ce48661656986c885c9d0339361d32830e46b15afaf755d7ea08ea5c9de8c34d11a7e466ced15e2c5cf25673fe17958ccd55e8b8bc5d119746ea7445c3b6a3c46d16ba3ce6a94a248a21178927bc1665727e2d4d5c2de5038948685c0d9aaefb91da18e783d38a9d03a306a9edac1e6eaaac9ce73ae2e9452a5281985bc1045c2a2c541c8eedba02f1219beddc3abd77484acd041cb747c77857fed50803a1929f04b7bf2e6e80000000072616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b65", + // complex lal + "03f39734b80d0e0a0d72616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e30202867112c7dbaceca9c3d68475119fadcba4185f8a8f1b24b180c2daa95f400a4491f627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b6572616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e646f6d20627566206f662072746d702068616e647368616b652067656e206279206c616c2076302e32312e3020286769746875622e636f6d2f713139313230313737312f6c616c2972616e64c6b6fce259abf52bf6629fa4a270e145e89e931e7419cb43d8e43873e0af5381", + // complex s + "0360812aa10100050443fbdd0953525328332e302e3135372900c8841cc87fda37cfcd99144b31a3e2c4e64a9551b562d9dac23b5aaa398a2eea1d33b9861de164d389622da3141e6ee359ed44155e2cd8275042ba7ab5d96cbb14345022259ede97e91a42ee2199d864952b6adc404aec7a75b6e5399658dd9b7e35a68cbc932ab4965da9a9e0901c7ca56f68cea25b5726204448a88d2d4a1253e28f1e7cabc3220f7bb4e012c16ca837bd7ecb27c6da39112aca9048238b8cee2a937ac66585c6c948b6cc10297c30d7ebe4efb8ce2fb3e1ea4a3815c6b512e14f75aea5eb84753a41483b53b55539a7482f671d5029ef415c2e4731cc43222aa9c1b89c4c3cc77676ecba3248dcc3811b318854437e86909eb7b279ebbd959b865c46bb821541e9eae42241d0d6b3dcf0423f42b2aec3576c7dc15e495debb8ab38833436ad242fa037617f151c6a144f9248ea481a50a58821edbb67e7821926ee3e4eaa53665a7cb9ca7ac63c7f1cb7b816e8bc579d4b619915ba8781c4967e12cd3856437bbbe55444ba79b5c73874c62f372db574775d7240cddcee6b620f478b4e73ef11674a4630b5e4e1d660b61488ccbbe5531f5f7cd642795095718027a9e525ab5b61e2751dce5de43d1ae9aed8ab9b32bceb9f993c20dabb8261d33a55ead69a5ac01e689d6453c36f4b804ee02a71a31c184b42292cee947ec8c0c5c19d6622646d73eac2b7bc40ec4480db57da8d5de3c9901be685996b5c683725ee8e385aec9d53b55b17df56446640844fb6d23aff2a0772bd8b3ccac4699ce3c4c08ba666485419c06aeb159d446e903be0030d53525328332e302e3135372900c389ae86c02852ddc4526f9fc84247363767e05ce48661656986c885c9d0339361d32830e46b15afaf755d7ea08ea5c9de8c34d11a7e466ced15e2c5cf25673fe17958ccd55e8b8bc5d119746ea7445c3b6a3c46d16ba3ce6a94a248a21178927bc1665727e2d4d5c2de5038948685c0d9aaefb91da18e783d38a9d03a306a9edac1e6eaaac9ce73ae2e9452a5281985bc1045c2a2c541c8eedba02f1219beddc3abd77484acd053525328332e302e31353729008e374d4d46d437cf35b7e36bd390e94c4039c3639cb6a6342c94b899c153965f7bd495aab7b581dd7b6b4f5dec489a346a6488162135336f08dbb2f0796280f098680d7dd398446b03823d8640777abe32a40646263021f8769fadc21b6ca844327a4412e1ace1eb04db178e53f62418dabfb5590dc727cf744f17ae07457c0b8f620403176ae83f52b856beb3e3a5140a64786f351ec1b0f110304ae334807fa89d2c27a230c8b7bbc79f95b51dd35b09d4015f5090d053525328332e302e31353729007feac1556324421d45e44112421a536e6ee71f3926c1a6649fe8dc63ae3df03c36b87a8bc5a5911998c314c3c658404346486d5d181aaba8eb9612a0bcebcee4b24f767ee616888dc2866097c789c41dc3406bc4441d7c3ea477d07072a55b2be5c29bdac12a6f92a1c0307850e586222ddad862e95b9194c368ed3c1c4f59eb20e5d4d21e526bb1198d305b819f669f884dea7891831c5cdc1089e251d3dc5ac7bf34cf1990871b2ca8679756b73dc7ed374f8eab54db8e4d6b7f8f466ad3143116d433976a3fad1990536056792f52a167c953a4abcbe22659795dad5b5ac7623ee3ea99299e9caae2e5e96b2343137b1b502eb02a18c76c822b20c676d0379dc2283ed5b8cb86a1bf7f13ccab1056b7516e76646f44b9e258c3afb89ad75c64e88b48a75dbf582c4554e9e15546a08f9d1de41b4ba5e6946fa4531882996d7a33a63082757197a3b787941bb6439c635187678d335d2993ea659d7cef11df2aa9169525793bb937ab54437088c8bccb57335f7b81791672c89cdfc69ecde14ed5856455a9257d636aa9bde48188b6c1ac1c43347f4a9856d77e237d5314bc2f822875333edb8799934b841bc541cd78472096b7533d1432a428a0e8256b26918484b5ab6e434ceb80c11554ecccb542d55ae22f80e752341ee3232d563bafc3a873761ea7b318367b16736ecb38a1af7b92c8ed802930901444ae53686c1d20c8842f7e4630a6ab372028ec49b3a2b64c79aabe94cb559116ecd5685fe371367789a5a6aa521ecdffd253525328332e302e3135372900f04be3ed2d922b3094d21d5e8cddbb17989a426b1da7cb9435c0cd9eebd8b1ea2aa3de493c1062b9d3701e6654ca6fd673a2487a50221d6fd3d31cc5b2beb6ce68a31e8da471557ecb64d626354ce59ad73c233648299622ed9cd9af619e84b3518b4fdee58e64bfdb49cf207ec3a3640faf8348c9295bbdae3b731ecbe0ba237219ea67905d2d7a97ed8b24c03571b8d5e6e9a5164b6ab578cebc4ab6865e37885787279ea692449a2552614bac2830a118c6a0553f5cb61528e9b4974ed42e966b463b18c171a3d7ac14325f2553ea2f20996d49e72a471622e49e61c7bde039ec2b42bc85d7a338dcbe88101881282922865b18998b1fac7ea61c5572ee7f6520b3309691c4bf7491566d92c187add4140fdd9f8ce55a199a60571355c869668282ed1a4eb37fc810dd6ac26b26a5711e89179b7562a616abe61a0fbd6c6646e05a513d1cc1ee1ea667d120767e8286f08a3075d6bf7c90b5808979dde0a9c442ebea4fb4e856695e2f7ac69ee65b9d777419543b87ced9166661dc4d19a880eea1b9b1901e21e03e84adc5790f6ada74743d98e41280e369d3cfa8d57e2fd22ed98aa8e19c8f26304bd5924b4e7ba8aba2489fa5b9911e9b67af78d6cf59eeb0cda5a07843b799779b3babd29f5a8d509333df5cadeee01ca467e37aaad839868ccae7b889883e2bacdaef5a448394c89f82335b771b682a73538d243ab893b0898a771921a736be882c1fb5a09c8d462da9928db5e4a030463d3d69e6c228765389816537a82ab0bd32756cc0eb9bd69b3c7360271a795648a7a93570c29db45225207abe33328a5790df1e8a89dd34ae577dbf63df249496b6bb15885fb3c375c44d3ae968ad47e9934e7a233a97ba832488d7ec9672915b3c90cc84529fe21dd52b1544c1453c63849f77b045413a52ba184e577bc8a4a15f7734a2251ea9e33aa736e5de5b5769ebc02038ea4b73ab4cb212b9829f69d225867c3495341eb9c446adb192132a85bc33aead702868adc4636d5511c72e1f55933cdbb04ca3847b5744145c578a1f7c3fbed5592d892c81e86a7bb68983122cb1dfcde6896070d28e6d35cee13d512fe4357120a886929fd91c5c6a89607f41465336b8a58f913aedaf0fddde5214d1786ee327de7cafc6821b371a64a74c9bec735a98ebddc3df9bc3cc8824c961863e4b962bb855db4959214caeba8950ade59b54df8726c62ad2999be071ed6d984712b5e95097399aa17757620f901ede3363c4ab7299be4c41693b9b659143968ce18ece87b877373eb883375792247be7d12e687adda5ac4dc94fa4618341df73c0b4e37f3a21a7db95c73a37d49e25b4b57e3da22bd3e0dd318b4e9dbd341784d9ebed1a1ba3df9a7928ba54b7d0177c553e256918ee5632818dc045aac8bb8abab78dbe617b67c394302653e926b84e55c6a85ebbe7794b8348823c1744afc2e44c8854b8e02654183590104c57478b24e1d2d0d75b2b628c96878dc34656b683cf114ab7208fc0462eb97b6e101583e2d95bc03b7731b814aa54c8d99c856b7a87a638983de9cf54aa51acab58369440825b6ce27e2be72f71b617144a6b77ba18a161469137844a7a3fe6bb5e81ecd2ce5fc35373b26bcd776bd2abc7516cc8dbbf1e7bdf8bb760b3a42a202d1de3e465b646ca6f9aa6cf148081c4c2df9baca5a22e9334d6e5d8821eea9825d48b7b91c2540f63ecd06073602b444eafd9e461167f86d66b665f7257e8883a8213bd4b58b59f4b8ce8afde22dc33c2c41f2ac487a2a1e30f0f6457e0de8269e246a541ed4b7e80423c6d4d288a1ed5923aa028c55012c5486723374c969135c63d68ba71d7499c1aa0da2b31e2eab42b99c5d9d3c8ad2a36c1536c5ecd922b12ebd674c92720cdb8e3e9dad4da95e88361d0653084784f4cbcac9c914eb89440960f19ae18d76de5c74ec8b1d4c03b4497895d2b109d68b659ed4e90adcbc152cbcbe9cda965c178a490307f5f5cace0d6181acfa67394e868cb8724a55867802a5f54bdad244460a565c8eeaa83dd8784e065395ae329b3bda1c872ea36db1c7e3eca3a4b1d83d96b5a53525328332e302e3135372900fbd7f0aa3cb695c07554138c454b6401a8aaa78e27b1d9ef4e4ce88c616f9320", + } + + for _, item := range s0s1s2Golden { + golden = append(golden, s{ + s0s1hexstream: item[:1537*2], + s2hexstream: item[1537*2:], + }) + } + + for _, item := range golden { + s0s1buf, _ := hex.DecodeString(item.s0s1hexstream) + s2buf, _ := hex.DecodeString(item.s2hexstream) + var hc HandshakeClientSimple + var err error + wb := &bytes.Buffer{} + s0s1 := &bytes.Buffer{} + s0s1.Write(s0s1buf) + s2 := &bytes.Buffer{} + s2.Write(s2buf) + + err = hc.WriteC0C1(wb) + assert.Equal(t, nil, err) + err = hc.ReadS0S1(s0s1) + assert.Equal(t, nil, err) + err = hc.WriteC2(wb) + assert.Equal(t, nil, err) + err = hc.ReadS2(s2) + assert.Equal(t, nil, err) + } +} + func TestHandshakeSimple(t *testing.T) { var err error var hc HandshakeClientSimple @@ -27,7 +141,9 @@ func TestHandshakeSimple(t *testing.T) { assert.Equal(t, nil, err) err = hs.WriteS0S1S2(b) assert.Equal(t, nil, err) - err = hc.ReadS0S1S2(b) + err = hc.ReadS0S1(b) + assert.Equal(t, nil, err) + err = hc.ReadS2(b) assert.Equal(t, nil, err) err = hc.WriteC2(b) assert.Equal(t, nil, err) @@ -46,7 +162,9 @@ func TestHandshakeComplex(t *testing.T) { assert.Equal(t, nil, err) err = hs.WriteS0S1S2(b) assert.Equal(t, nil, err) - err = hc.ReadS0S1S2(b) + err = hc.ReadS0S1(b) + assert.Equal(t, nil, err) + err = hc.ReadS2(b) assert.Equal(t, nil, err) err = hc.WriteC2(b) assert.Equal(t, nil, err) @@ -62,7 +180,8 @@ func BenchmarkHandshakeSimple(b *testing.B) { _ = hc.WriteC0C1(b) _ = hs.ReadC0C1(b) _ = hs.WriteS0S1S2(b) - _ = hc.ReadS0S1S2(b) + _ = hc.ReadS0S1(b) + _ = hc.ReadS2(b) _ = hc.WriteC2(b) _ = hs.ReadC2(b) } @@ -76,7 +195,8 @@ func BenchmarkHandshakeComplex(b *testing.B) { _ = hc.WriteC0C1(b) _ = hs.ReadC0C1(b) _ = hs.WriteS0S1S2(b) - _ = hc.ReadS0S1S2(b) + _ = hc.ReadS0S1(b) + _ = hc.ReadS2(b) _ = hc.WriteC2(b) _ = hs.ReadC2(b) } From 76f090f45320bf9fc3cc79161b929bfb2ead9890 Mon Sep 17 00:00:00 2001 From: q191201771 <191201771@qq.com> Date: Sat, 24 Apr 2021 12:28:19 +0800 Subject: [PATCH 2/5] [doc] README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 364a7cd..ab09ab6 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ [![Release](https://img.shields.io/github/tag/q191201771/lal.svg?label=release)](https://github.com/q191201771/lal/releases) [![TravisCI](https://www.travis-ci.org/q191201771/lal.svg?branch=master)](https://www.travis-ci.org/q191201771/lal) [![goreportcard](https://goreportcard.com/badge/github.com/q191201771/lal)](https://goreportcard.com/report/github.com/q191201771/lal) -[![wechat](https://img.shields.io/:微信-q191201771-blue.svg)](https://pengrl.com/images/yoko_vx.jpeg) +![wechat](https://img.shields.io/:微信-q191201771-blue.svg) ![qqgroup](https://img.shields.io/:QQ群-1090510973-blue.svg) [中文文档](https://pengrl.com/lal/#/) -LAL is a live stream broadcast server written in Go. It's sort of like `nginx-rtmp-module`, but easier to use and with more features, e.g. RTMP/RTSP/HLS/HTTP[S]-FLV/HTTP-TS, H264/H265/AAC, relay, cluster, record, HTTP API/Notify, GOP cache. +LAL is a audio/video live streaming broadcast server written in Go. It's sort of like `nginx-rtmp-module`, but easier to use and with more features, e.g RTMP, RTSP(RTP/RTCP), HLS, HTTP[S]-FLV/HTTP-TS, WebSocket-FLV/TS, H264/H265/AAC, relay, cluster, record, HTTP API/Notify, GOP cache. And [more than a server, act as package and client](https://github.com/q191201771/lal#more-than-a-server-act-as-package-and-client) From f26092b8903f7e7519a5a47b24d83a53966bc9b8 Mon Sep 17 00:00:00 2001 From: q191201771 <191201771@qq.com> Date: Sat, 24 Apr 2021 13:04:11 +0800 Subject: [PATCH 3/5] patch --- pkg/base/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/base/version.go b/pkg/base/version.go index 5f6d971..ac39f58 100644 --- a/pkg/base/version.go +++ b/pkg/base/version.go @@ -37,7 +37,7 @@ var ( var ( // 植入rtmp握手随机字符串中 // e.g. lal v0.12.3 (github.com/q191201771/lal) - //LALRTMPHandshakeWaterMark string + LALRTMPHandshakeWaterMark string // 植入rtmp server中的connect result信令中 // 注意,有两个object,第一个object中的fmsVer我们保持通用公认的值,在第二个object中植入 @@ -123,5 +123,5 @@ func init() { LALHTTPFLVPullSessionUA = LALLibraryName + "/" + LALVersionDot LALRTSPPullSessionUA = LALLibraryName + "/" + LALVersionDot - //LALRTMPHandshakeWaterMark = LALFullInfo + LALRTMPHandshakeWaterMark = LALFullInfo } From 2e0615b496138610312dbb886e248f26f2c3b76a Mon Sep 17 00:00:00 2001 From: q191201771 <191201771@qq.com> Date: Sat, 24 Apr 2021 13:09:04 +0800 Subject: [PATCH 4/5] patch --- pkg/logic/iface_impl.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/logic/iface_impl.go b/pkg/logic/iface_impl.go index 33b7f92..b97ef9f 100644 --- a/pkg/logic/iface_impl.go +++ b/pkg/logic/iface_impl.go @@ -144,8 +144,8 @@ var _ hls.MuxerObserver = &Group{} var _ rtsp.BaseInSessionObserver = &Group{} // var _ rtmp.ServerSessionObserver = &rtmp.Server{} -var _ rtmp.HandshakeClient = &rtmp.HandshakeClientSimple{} -var _ rtmp.HandshakeClient = &rtmp.HandshakeClientComplex{} +var _ rtmp.IHandshakeClient = &rtmp.HandshakeClientSimple{} +var _ rtmp.IHandshakeClient = &rtmp.HandshakeClientComplex{} var _ rtsp.ServerCommandSessionObserver = &rtsp.Server{} var _ rtsp.ClientCommandSessionObserver = &rtsp.PushSession{} From d5d67a51a23fa12f654afe5391150f0fff1739a8 Mon Sep 17 00:00:00 2001 From: q191201771 <191201771@qq.com> Date: Sat, 24 Apr 2021 19:37:39 +0800 Subject: [PATCH 5/5] =?UTF-8?q?[feat]=20=E6=94=AF=E6=8C=81=E5=BD=95?= =?UTF-8?q?=E5=88=B6flv=E5=92=8Cts=E6=96=87=E4=BB=B6=20#14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- conf/lalserver.conf.json | 8 ++- conf/lalserver.conf.json.tmpl | 8 ++- conf/node2.conf.json | 6 ++- conf/onlyrtmp.conf.json | 6 ++- go.mod | 2 +- go.sum | 4 +- pkg/base/websocket.go | 8 +++ pkg/hls/hls.go | 2 - pkg/hls/muxer.go | 2 +- pkg/hls/path.go | 20 ++++--- pkg/httpflv/flv_file_writer.go | 30 +++++++++-- pkg/logic/config.go | 10 +++- pkg/logic/entry.go | 13 +++++ pkg/logic/group.go | 97 +++++++++++++++++++++++++++++++--- pkg/mpegts/file_writer.go | 42 +++++++++++++++ pkg/mpegts/mpegts.go | 4 ++ 17 files changed, 235 insertions(+), 29 deletions(-) create mode 100644 pkg/mpegts/file_writer.go diff --git a/README.md b/README.md index ab09ab6..790c0a6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [中文文档](https://pengrl.com/lal/#/) -LAL is a audio/video live streaming broadcast server written in Go. It's sort of like `nginx-rtmp-module`, but easier to use and with more features, e.g RTMP, RTSP(RTP/RTCP), HLS, HTTP[S]-FLV/HTTP-TS, WebSocket-FLV/TS, H264/H265/AAC, relay, cluster, record, HTTP API/Notify, GOP cache. +LAL is an audio/video live streaming broadcast server written in Go. It's sort of like `nginx-rtmp-module`, but easier to use and with more features, e.g RTMP, RTSP(RTP/RTCP), HLS, HTTP[S]-FLV/HTTP-TS, WebSocket-FLV/TS, H264/H265/AAC, relay, cluster, record, HTTP API/Notify, GOP cache. And [more than a server, act as package and client](https://github.com/q191201771/lal#more-than-a-server-act-as-package-and-client) diff --git a/conf/lalserver.conf.json b/conf/lalserver.conf.json index 40e7f56..e20b939 100644 --- a/conf/lalserver.conf.json +++ b/conf/lalserver.conf.json @@ -1,6 +1,6 @@ { "# doc of config": "https://pengrl.com/lal/#/ConfigBrief", - "conf_version": "v0.1.1", + "conf_version": "v0.1.2", "rtmp": { "enable": true, "addr": ":1935", @@ -32,6 +32,12 @@ "enable": true, "addr": ":5544" }, + "record": { + "enable_flv": false, + "flv_out_path": "/tmp/lal/flv/", + "enable_mpegts": false, + "mpegts_out_path": "/tmp/lal/mpegts" + }, "relay_push": { "enable": false, "addr_list":[ diff --git a/conf/lalserver.conf.json.tmpl b/conf/lalserver.conf.json.tmpl index 40e7f56..e20b939 100644 --- a/conf/lalserver.conf.json.tmpl +++ b/conf/lalserver.conf.json.tmpl @@ -1,6 +1,6 @@ { "# doc of config": "https://pengrl.com/lal/#/ConfigBrief", - "conf_version": "v0.1.1", + "conf_version": "v0.1.2", "rtmp": { "enable": true, "addr": ":1935", @@ -32,6 +32,12 @@ "enable": true, "addr": ":5544" }, + "record": { + "enable_flv": false, + "flv_out_path": "/tmp/lal/flv/", + "enable_mpegts": false, + "mpegts_out_path": "/tmp/lal/mpegts" + }, "relay_push": { "enable": false, "addr_list":[ diff --git a/conf/node2.conf.json b/conf/node2.conf.json index 6fb2cfd..63c5a84 100644 --- a/conf/node2.conf.json +++ b/conf/node2.conf.json @@ -1,6 +1,6 @@ { "# doc of config": "https://pengrl.com/lal/#/ConfigBrief", - "conf_version": "v0.1.1", + "conf_version": "v0.1.2", "rtmp": { "enable": true, "addr": ":1955", @@ -22,6 +22,10 @@ "rtsp": { "enable": false }, + "record": { + "enable_flv": false, + "enable_mpegts": false + }, "relay_push": { "enable": false }, diff --git a/conf/onlyrtmp.conf.json b/conf/onlyrtmp.conf.json index 4a8a9f9..6cce7f5 100644 --- a/conf/onlyrtmp.conf.json +++ b/conf/onlyrtmp.conf.json @@ -1,6 +1,6 @@ { "# doc of config": "https://pengrl.com/lal/#/ConfigBrief", - "conf_version": "v0.1.1", + "conf_version": "v0.1.2", "rtmp": { "enable": true, "addr": ":1935", @@ -32,6 +32,10 @@ "enable": false, "addr": ":5544" }, + "record": { + "enable_flv": false, + "enable_mpegts": false + }, "relay_push": { "enable": false, "addr_list":[ diff --git a/go.mod b/go.mod index 951afa1..9415bc2 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/q191201771/lal go 1.13 -require github.com/q191201771/naza v0.18.4 +require github.com/q191201771/naza v0.18.5 diff --git a/go.sum b/go.sum index db42625..e169668 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -github.com/q191201771/naza v0.18.4 h1:yk5EUz8q2FhCYg6pG5QcH9uXVJN5nmiXPu5Y5vlpArE= -github.com/q191201771/naza v0.18.4/go.mod h1:5LeGupZZFtYP1g/S203n9vXoUNVdlRnPIfM6rExjqt0= +github.com/q191201771/naza v0.18.5 h1:new/bBkivdVqPZpaviseZc2Z4qZ9I4KpKvJUmXCXjEk= +github.com/q191201771/naza v0.18.5/go.mod h1:5LeGupZZFtYP1g/S203n9vXoUNVdlRnPIfM6rExjqt0= diff --git a/pkg/base/websocket.go b/pkg/base/websocket.go index c3f9fde..a265630 100644 --- a/pkg/base/websocket.go +++ b/pkg/base/websocket.go @@ -1,3 +1,11 @@ +// Copyright 2021, Chef. All rights reserved. +// https://github.com/q191201771/lal +// +// Use of this source code is governed by a MIT-style license +// that can be found in the License file. +// +// Author: joestarzxh + package base import ( diff --git a/pkg/hls/hls.go b/pkg/hls/hls.go index df9d524..1c7d599 100644 --- a/pkg/hls/hls.go +++ b/pkg/hls/hls.go @@ -33,8 +33,6 @@ import ( var ErrHLS = errors.New("lal.hls: fxxk") -var _ StreamerObserver = &Muxer{} - var audNal = []byte{ 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, } diff --git a/pkg/hls/muxer.go b/pkg/hls/muxer.go index 13d7f98..d1ba3e8 100644 --- a/pkg/hls/muxer.go +++ b/pkg/hls/muxer.go @@ -92,8 +92,8 @@ func NewMuxer(streamName string, config *MuxerConfig, observer MuxerObserver) *M uk := base.GenUKHLSMuxer() op := getMuxerOutPath(config.OutPath, streamName) playlistFilename := getM3U8Filename(op, streamName) - playlistFilenameBak := fmt.Sprintf("%s.bak", playlistFilename) recordPlaylistFilename := getRecordM3U8Filename(op, streamName) + playlistFilenameBak := fmt.Sprintf("%s.bak", playlistFilename) recordPlaylistFilenameBak := fmt.Sprintf("%s.bak", recordPlaylistFilename) frags := make([]fragmentInfo, 2*config.FragmentNum+1) m := &Muxer{ diff --git a/pkg/hls/path.go b/pkg/hls/path.go index f856f0d..0c2c0b3 100644 --- a/pkg/hls/path.go +++ b/pkg/hls/path.go @@ -10,6 +10,7 @@ package hls import ( "fmt" + "path/filepath" "strings" ) @@ -56,25 +57,30 @@ func parseRequestInfo(uri string) (ri requestInfo) { return } +// // func readFileContent(rootOutPath string, ri requestInfo) ([]byte, error) { - filename := fmt.Sprintf("%s%s/%s", rootOutPath, ri.streamName, ri.fileName) + filename := filepath.Join(rootOutPath, ri.streamName, ri.fileName) return fslCtx.ReadFile(filename) } +// / func getMuxerOutPath(rootOutPath string, streamName string) string { - return fmt.Sprintf("%s%s/", rootOutPath, streamName) + return filepath.Join(rootOutPath, streamName) } -func getM3U8Filename(outpath string, streamName string) string { - return fmt.Sprintf("%s%s.m3u8", outpath, "playlist") +// @param outPath 参考func getMuxerOutPath +func getM3U8Filename(outPath string, streamName string) string { + return filepath.Join(outPath, "playlist.m3u8") } -func getRecordM3U8Filename(outpath string, streamName string) string { - return fmt.Sprintf("%s%s.m3u8", outpath, "record") +// @param outPath 参考func getMuxerOutPath +func getRecordM3U8Filename(outPath string, streamName string) string { + return filepath.Join(outPath, "record.m3u8") } +// @param outPath 参考func getMuxerOutPath func getTSFilenameWithPath(outpath string, filename string) string { - return fmt.Sprintf("%s%s", outpath, filename) + return filepath.Join(outpath, filename) } func getTSFilename(streamName string, id int, timestamp int) string { diff --git a/pkg/httpflv/flv_file_writer.go b/pkg/httpflv/flv_file_writer.go index 49b94b7..9382f09 100644 --- a/pkg/httpflv/flv_file_writer.go +++ b/pkg/httpflv/flv_file_writer.go @@ -10,6 +10,8 @@ package httpflv import "os" +// TODO chef: 结构体重命名为FileWriter,文件名重命名为file_writer.go。所有写流文件的(flv,hls,ts)统一重构 + type FLVFileWriter struct { fp *os.File } @@ -20,17 +22,39 @@ func (ffw *FLVFileWriter) Open(filename string) (err error) { } func (ffw *FLVFileWriter) WriteRaw(b []byte) (err error) { + if ffw.fp == nil { + return ErrHTTPFLV + } _, err = ffw.fp.Write(b) return } +func (ffw *FLVFileWriter) WriteFLVHeader() (err error) { + if ffw.fp == nil { + return ErrHTTPFLV + } + _, err = ffw.fp.Write(FLVHeader) + return +} + func (ffw *FLVFileWriter) WriteTag(tag Tag) (err error) { + if ffw.fp == nil { + return ErrHTTPFLV + } _, err = ffw.fp.Write(tag.Raw) return } -func (ffw *FLVFileWriter) Dispose() { - if ffw.fp != nil { - _ = ffw.fp.Close() +func (ffw *FLVFileWriter) Dispose() error { + if ffw.fp == nil { + return ErrHTTPFLV + } + return ffw.fp.Close() +} + +func (ffw *FLVFileWriter) Name() string { + if ffw.fp == nil { + return "" } + return ffw.fp.Name() } diff --git a/pkg/logic/config.go b/pkg/logic/config.go index cc99a2c..a0caa03 100644 --- a/pkg/logic/config.go +++ b/pkg/logic/config.go @@ -15,7 +15,7 @@ import ( "github.com/q191201771/naza/pkg/nazalog" ) -const ConfVersion = "v0.1.1" +const ConfVersion = "v0.1.2" type Config struct { ConfVersion string `json:"conf_version"` @@ -24,6 +24,7 @@ type Config struct { HLSConfig HLSConfig `json:"hls"` HTTPTSConfig HTTPTSConfig `json:"httpts"` RTSPConfig RTSPConfig `json:"rtsp"` + RecordConfig RecordConfig `json:"record"` RelayPushConfig RelayPushConfig `json:"relay_push"` RelayPullConfig RelayPullConfig `json:"relay_pull"` @@ -61,6 +62,13 @@ type RTSPConfig struct { Addr string `json:"addr"` } +type RecordConfig struct { + EnableFLV bool `json:"enable_flv"` + FLVOutPath string `json:"flv_out_path"` + EnableMPEGTS bool `json:"enable_mpegts"` + MPEGTSOutPath string `json:"mpegts_out_path"` +} + type RelayPushConfig struct { Enable bool `json:"enable"` AddrList []string `json:"addr_list"` diff --git a/pkg/logic/entry.go b/pkg/logic/entry.go index 8b4c1ae..f0b4284 100644 --- a/pkg/logic/entry.go +++ b/pkg/logic/entry.go @@ -34,6 +34,9 @@ var ( func Entry(confFile string) { LoadConfAndInitLog(confFile) + if dir, err := os.Getwd(); err == nil { + nazalog.Infof("wd: %s", dir) + } nazalog.Infof("args: %s", strings.Join(os.Args, " ")) nazalog.Infof("bininfo: %s", bininfo.StringifySingleLine()) nazalog.Infof("version: %s", base.LALFullInfo) @@ -45,6 +48,15 @@ func Entry(confFile string) { hls.SetUseMemoryAsDiskFlag(true) } + if config.RecordConfig.EnableFLV { + if err := os.MkdirAll(config.RecordConfig.FLVOutPath, 0777); err != nil { + nazalog.Errorf("record flv mkdir error. path=%s, err=%+v", config.RecordConfig.FLVOutPath, err) + } + if err := os.MkdirAll(config.RecordConfig.MPEGTSOutPath, 0777); err != nil { + nazalog.Errorf("record mpegts mkdir error. path=%s, err=%+v", config.RecordConfig.MPEGTSOutPath, err) + } + } + sm = NewServerManager() if config.PProfConfig.Enable { @@ -138,6 +150,7 @@ func LoadConfAndInitLog(confFile string) *Config { "hls", "httpts", "rtsp", + "record", "relay_push", "relay_pull", "http_api", diff --git a/pkg/logic/group.go b/pkg/logic/group.go index 31c23ca..2d1af95 100644 --- a/pkg/logic/group.go +++ b/pkg/logic/group.go @@ -11,8 +11,12 @@ package logic import ( "encoding/json" "fmt" + "path/filepath" "strings" "sync" + "time" + + "github.com/q191201771/lal/pkg/mpegts" "github.com/q191201771/lal/pkg/remux" @@ -46,30 +50,36 @@ type Group struct { mutex sync.Mutex // stat base.StatGroup - // + // pub rtmpPubSession *rtmp.ServerSession rtspPubSession *rtsp.PubSession - // + // pull pullEnable bool pullURL string pullProxy *pullProxy - // + // sub rtmpSubSessionSet map[*rtmp.ServerSession]struct{} httpflvSubSessionSet map[*httpflv.SubSession]struct{} httptsSubSessionSet map[*httpts.SubSession]struct{} rtspSubSessionSet map[*rtsp.SubSession]struct{} - // + // push url2PushProxy map[string]*pushProxy - // + // hls hlsMuxer *hls.Muxer + + recordFLV *httpflv.FLVFileWriter + recordMPEGTS *mpegts.FileWriter + // rtmp pub/pull使用 gopCache *GOPCache httpflvGopCache *GOPCache + // rtsp pub使用 asc []byte vps []byte sps []byte pps []byte + // tickCount uint32 } @@ -482,6 +492,12 @@ func (group *Group) OnTSPackets(rawFrame []byte, boundary bool) { session.WriteRawPacket(rawFrame) } } + + if group.recordMPEGTS != nil { + if err := group.recordMPEGTS.Write(rawFrame); err != nil { + nazalog.Errorf("[%s] record mpegts write error. err=%+v", group.UniqueKey, err) + } + } } // rtmp.PubSession or rtmp.PullSession @@ -796,7 +812,14 @@ func (group *Group) broadcastRTMP(msg base.RTMPMsg) { session.WriteRawPacket(lrm2ft.Get()) } - // # 5. 缓存关键信息,以及gop + // # 5. 录制flv文件 + if group.recordFLV != nil { + if err := group.recordFLV.WriteRaw(lrm2ft.Get()); err != nil { + nazalog.Errorf("[%s] record flv write error. err=%+v", group.UniqueKey, err) + } + } + + // # 6. 缓存关键信息,以及gop if config.RTMPConfig.Enable { group.gopCache.Feed(msg, lcd.Get) } @@ -804,7 +827,7 @@ func (group *Group) broadcastRTMP(msg base.RTMPMsg) { group.httpflvGopCache.Feed(msg, lrm2ft.Get) } - // # 6. 记录stat + // # 7. 记录stat if group.stat.AudioCodec == "" { if msg.IsAACSeqHeader() { group.stat.AudioCodec = base.AudioCodecAAC @@ -986,6 +1009,48 @@ func (group *Group) addIn() { if config.RelayPushConfig.Enable { group.pushIfNeeded() } + + now := time.Now().Unix() + if config.RecordConfig.EnableFLV { + filename := fmt.Sprintf("%s-%d.flv", group.streamName, now) + filenameWithPath := filepath.Join(config.RecordConfig.FLVOutPath, filename) + if group.recordFLV != nil { + nazalog.Errorf("[%s] record flv but already exist. new filename=%s, old filename=%s", + group.UniqueKey, filenameWithPath, group.recordFLV.Name()) + if err := group.recordFLV.Dispose(); err != nil { + nazalog.Errorf("[%s] record flv dispose error. err=%+v", group.UniqueKey, err) + } + } + group.recordFLV = &httpflv.FLVFileWriter{} + if err := group.recordFLV.Open(filenameWithPath); err != nil { + nazalog.Errorf("[%s] record flv open file failed. filename=%s, err=%+v", + group.UniqueKey, filenameWithPath, err) + group.recordFLV = nil + } + if err := group.recordFLV.WriteFLVHeader(); err != nil { + nazalog.Errorf("[%s] record flv write flv header failed. filename=%s, err=%+v", + group.UniqueKey, filenameWithPath, err) + group.recordFLV = nil + } + } + + if config.RecordConfig.EnableMPEGTS { + filename := fmt.Sprintf("%s-%d.ts", group.streamName, now) + filenameWithPath := filepath.Join(config.RecordConfig.MPEGTSOutPath, filename) + if group.recordMPEGTS != nil { + nazalog.Errorf("[%s] record mpegts but already exist. new filename=%s, old filename=%s", + group.UniqueKey, filenameWithPath, group.recordMPEGTS.Name()) + if err := group.recordMPEGTS.Dispose(); err != nil { + nazalog.Errorf("[%s] record mpegts dispose error. err=%+v", group.UniqueKey, err) + } + } + group.recordMPEGTS = &mpegts.FileWriter{} + if err := group.recordMPEGTS.Create(filenameWithPath); err != nil { + nazalog.Errorf("[%s] record mpegts open file failed. filename=%s, err=%+v", + group.UniqueKey, filenameWithPath, err) + group.recordFLV = nil + } + } } func (group *Group) delIn() { @@ -1002,6 +1067,24 @@ func (group *Group) delIn() { } } + if config.RecordConfig.EnableFLV { + if group.recordFLV != nil { + if err := group.recordFLV.Dispose(); err != nil { + nazalog.Errorf("[%s] record flv dispose error. err=%+v", group.UniqueKey, err) + } + group.recordFLV = nil + } + } + + if config.RecordConfig.EnableMPEGTS { + if group.recordMPEGTS != nil { + if err := group.recordMPEGTS.Dispose(); err != nil { + nazalog.Errorf("[%s] record mpegts dispose error. err=%+v", group.UniqueKey, err) + } + group.recordMPEGTS = nil + } + } + group.gopCache.Clear() group.httpflvGopCache.Clear() } diff --git a/pkg/mpegts/file_writer.go b/pkg/mpegts/file_writer.go new file mode 100644 index 0000000..09f4c12 --- /dev/null +++ b/pkg/mpegts/file_writer.go @@ -0,0 +1,42 @@ +// Copyright 2019, Chef. All rights reserved. +// https://github.com/q191201771/lal +// +// Use of this source code is governed by a MIT-style license +// that can be found in the License file. +// +// Author: Chef (191201771@qq.com) + +package mpegts + +import "os" + +type FileWriter struct { + fp *os.File +} + +func (fw *FileWriter) Create(filename string) (err error) { + fw.fp, err = os.Create(filename) + return +} + +func (fw *FileWriter) Write(b []byte) (err error) { + if fw.fp == nil { + return ErrMPEGTS + } + _, err = fw.fp.Write(b) + return +} + +func (fw *FileWriter) Dispose() error { + if fw.fp == nil { + return ErrMPEGTS + } + return fw.fp.Close() +} + +func (fw *FileWriter) Name() string { + if fw.fp == nil { + return "" + } + return fw.fp.Name() +} diff --git a/pkg/mpegts/mpegts.go b/pkg/mpegts/mpegts.go index 4a1592e..2276f85 100644 --- a/pkg/mpegts/mpegts.go +++ b/pkg/mpegts/mpegts.go @@ -8,8 +8,12 @@ package mpegts +import "errors" + // MPEG: Moving Picture Experts Group +var ErrMPEGTS = errors.New("lal.mpegts: fxxk") + // 每个TS文件都以固定的PAT,PMT开始 var FixedFragmentHeader = []byte{ /* TS */