From c382595f9218f2cabe536a04aedc34e6a44d3a16 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Thu, 24 Oct 2024 11:13:24 +0900 Subject: [PATCH 01/18] =?UTF-8?q?oggwriter=20=E3=82=92=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 1 + go.mod | 13 +++---- go.sum | 14 ++++++++ handler.go | 7 ---- oggwriter.go | 42 +++++++++++++++------- patch/oggwriter.go.patch | 72 +++++++++++--------------------------- patch/util.go.patch | 11 ++++++ pion/oggwriter.go | 26 ++++++++++---- pion/util.go | 75 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 178 insertions(+), 83 deletions(-) create mode 100644 patch/util.go.patch create mode 100644 pion/util.go diff --git a/Makefile b/Makefile index 806b282..7a9071c 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ all: patch patch: patch -o oggwriter.go ./pion/oggwriter.go ./patch/oggwriter.go.patch + patch -o util.go ./pion/util.go ./patch/util.go.patch test: diff --git a/go.mod b/go.mod index 5cdb61c..c5e3d95 100644 --- a/go.mod +++ b/go.mod @@ -8,12 +8,12 @@ require ( github.com/labstack/echo-contrib v0.17.1 github.com/labstack/echo/v4 v4.12.0 github.com/pion/randutil v0.1.0 - github.com/pion/rtp v1.8.6 + github.com/pion/rtp v1.8.9 github.com/rs/zerolog v1.32.0 github.com/stretchr/testify v1.9.0 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f - golang.org/x/net v0.24.0 - golang.org/x/sync v0.7.0 + golang.org/x/net v0.29.0 + golang.org/x/sync v0.8.0 google.golang.org/api v0.176.1 google.golang.org/grpc v1.63.2 google.golang.org/protobuf v1.33.0 @@ -46,6 +46,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/pion/webrtc/v4 v4.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.19.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -59,10 +60,10 @@ require ( go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/oauth2 v0.19.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect diff --git a/go.sum b/go.sum index 7bb725a..f640911 100644 --- a/go.sum +++ b/go.sum @@ -203,6 +203,10 @@ github.com/pion/rtp v1.8.5 h1:uYzINfaK+9yWs7r537z/Rc1SvT8ILjBcmDOpJcTB+OU= github.com/pion/rtp v1.8.5/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw= github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk= +github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/webrtc/v4 v4.0.1 h1:6Unwc6JzoTsjxetcAIoWH81RUM4K5dBc1BbJGcF9WVE= +github.com/pion/webrtc/v4 v4.0.1/go.mod h1:SfNn8CcFxR6OUVjLXVslAQ3a3994JhyE3Hw1jAuqEto= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -308,6 +312,8 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4= golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= @@ -362,6 +368,8 @@ golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= @@ -384,6 +392,8 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -408,6 +418,8 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -416,6 +428,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/handler.go b/handler.go index cec4245..dd72682 100644 --- a/handler.go +++ b/handler.go @@ -240,13 +240,6 @@ func opus2ogg(ctx context.Context, opusReader io.Reader, oggWriter io.Writer, sa } defer o.Close() - if err := o.writeHeaders(); err != nil { - if w, ok := oggWriter.(*io.PipeWriter); ok { - w.CloseWithError(err) - } - return err - } - for { buf := make([]byte, FrameSize) n, err := opusReader.Read(buf) diff --git a/oggwriter.go b/oggwriter.go index d0f95a7..0504a86 100644 --- a/oggwriter.go +++ b/oggwriter.go @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package oggwriter implements OGG media container writer package suzu import ( @@ -6,7 +10,6 @@ import ( "io" "os" - "github.com/pion/randutil" "github.com/pion/rtp" "github.com/pion/rtp/codecs" ) @@ -19,7 +22,6 @@ const ( idPageSignature = "OpusHead" commentPageSignature = "OpusTags" pageHeaderSignature = "OggS" - vendorName = "pion" ) var ( @@ -65,7 +67,7 @@ func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, stream: out, sampleRate: sampleRate, channelCount: channelCount, - serial: randutil.NewMathRandomGenerator().Uint32(), + serial: RandUint32(), checksumTable: generateChecksumTable(), // Timestamp and Granule MUST start from 1 @@ -73,6 +75,9 @@ func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, previousTimestamp: 1, previousGranulePosition: 1, } + if err := writer.writeHeaders(); err != nil { + return nil, err + } return writer, nil } @@ -80,6 +85,7 @@ func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, /* ref: https://tools.ietf.org/html/rfc7845.html https://git.xiph.org/?p=opus-tools.git;a=blob;f=src/opus_header.c#l219 + Page 0 Pages 1 ... n Pages (n+1) ... +------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +-- | | | | | | | | | | | | | @@ -95,6 +101,7 @@ func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, | ID header is contained on a single page | 'Beginning Of Stream' + Figure 1: Example Packet Organization for a Logical Ogg Opus Stream */ @@ -119,11 +126,11 @@ func (i *OggWriter) writeHeaders() error { i.pageIndex++ // Comment Header - oggCommentHeader := make([]byte, (8 + len(vendorName) + 4 + 4)) - copy(oggCommentHeader[0:], commentPageSignature) // Magic Signature 'OpusTags' - binary.LittleEndian.PutUint32(oggCommentHeader[8:], uint32(len(vendorName))) // Vendor Length - copy(oggCommentHeader[12:], vendorName) // Vendor name 'pion' - binary.LittleEndian.PutUint32(oggCommentHeader[16:], 0) // User Comment List Length + oggCommentHeader := make([]byte, 21) + copy(oggCommentHeader[0:], commentPageSignature) // Magic Signature 'OpusTags' + binary.LittleEndian.PutUint32(oggCommentHeader[8:], 5) // Vendor Length + copy(oggCommentHeader[12:], "pion") // Vendor name 'pion' + binary.LittleEndian.PutUint32(oggCommentHeader[17:], 0) // User Comment List Length // RFC specifies that the page where the CommentHeader completes should have a granule position of 0 data = i.createPage(oggCommentHeader, pageHeaderTypeContinuationOfStream, 0, i.pageIndex) @@ -141,7 +148,9 @@ const ( func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uint64, pageIndex uint32) []byte { i.lastPayloadSize = len(payload) - page := make([]byte, pageHeaderSize+1+i.lastPayloadSize) + nSegments := (len(payload) / 255) + 1 // A segment can be at most 255 bytes long. + + page := make([]byte, pageHeaderSize+i.lastPayloadSize+nSegments) copy(page[0:], pageHeaderSignature) // page headers starts with 'OggS' page[4] = 0 // Version @@ -149,14 +158,23 @@ func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uin binary.LittleEndian.PutUint64(page[6:], granulePos) // granule position binary.LittleEndian.PutUint32(page[14:], i.serial) // Bitstream serial number binary.LittleEndian.PutUint32(page[18:], pageIndex) // Page sequence number - page[26] = 1 // Number of segments in page, giving always 1 segment - page[27] = uint8(i.lastPayloadSize) // Segment Table inserting at 27th position since page header length is 27 - copy(page[28:], payload) // inserting at 28th since Segment Table(1) + header length(27) + page[26] = uint8(nSegments) // Number of segments in page. + + // Filling segment table with the lacing values. + // First (nSegments - 1) values will always be 255. + for i := 0; i < nSegments-1; i++ { + page[pageHeaderSize+i] = 255 + } + // The last value will be the remainder. + page[pageHeaderSize+nSegments-1] = uint8(len(payload) % 255) + + copy(page[pageHeaderSize+nSegments:], payload) // Payload goes after the segment table, so at pageHeaderSize+nSegments. var checksum uint32 for index := range page { checksum = (checksum << 8) ^ i.checksumTable[byte(checksum>>24)^page[index]] } + binary.LittleEndian.PutUint32(page[22:], checksum) // Checksum - generating for page data and inserting at 22th position into 32 bits return page diff --git a/patch/oggwriter.go.patch b/patch/oggwriter.go.patch index 093a6d6..fe84ef7 100644 --- a/patch/oggwriter.go.patch +++ b/patch/oggwriter.go.patch @@ -1,60 +1,28 @@ ---- oggwriter.go.org 2023-01-17 12:52:08 -+++ oggwriter.go 2023-01-17 16:18:23 -@@ -1,5 +1,4 @@ --// Package oggwriter implements OGG media container writer +--- oggwriter.go.org 2024-10-24 10:52:25 ++++ oggwriter.go 2024-10-24 10:49:11 +@@ -2,7 +2,7 @@ + // SPDX-License-Identifier: MIT + + // Package oggwriter implements OGG media container writer -package oggwriter +package suzu import ( "encoding/binary" -@@ -20,6 +19,7 @@ - idPageSignature = "OpusHead" - commentPageSignature = "OpusTags" - pageHeaderSignature = "OggS" -+ vendorName = "pion" - ) - - var ( -@@ -73,9 +73,6 @@ - previousTimestamp: 1, - previousGranulePosition: 1, - } -- if err := writer.writeHeaders(); err != nil { -- return nil, err -- } +@@ -12,7 +12,6 @@ - return writer, nil - } -@@ -83,7 +80,6 @@ - /* - ref: https://tools.ietf.org/html/rfc7845.html - https://git.xiph.org/?p=opus-tools.git;a=blob;f=src/opus_header.c#l219 -- - Page 0 Pages 1 ... n Pages (n+1) ... - +------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +-- - | | | | | | | | | | | | | -@@ -99,7 +95,6 @@ - | ID header is contained on a single page - | - 'Beginning Of Stream' -- - Figure 1: Example Packet Organization for a Logical Ogg Opus Stream - */ - -@@ -124,11 +119,11 @@ - i.pageIndex++ + "github.com/pion/rtp" + "github.com/pion/rtp/codecs" +- "github.com/pion/webrtc/v4/internal/util" + ) - // Comment Header -- oggCommentHeader := make([]byte, 21) -- copy(oggCommentHeader[0:], commentPageSignature) // Magic Signature 'OpusTags' -- binary.LittleEndian.PutUint32(oggCommentHeader[8:], 5) // Vendor Length -- copy(oggCommentHeader[12:], "pion") // Vendor name 'pion' -- binary.LittleEndian.PutUint32(oggCommentHeader[17:], 0) // User Comment List Length -+ oggCommentHeader := make([]byte, (8 + len(vendorName) + 4 + 4)) -+ copy(oggCommentHeader[0:], commentPageSignature) // Magic Signature 'OpusTags' -+ binary.LittleEndian.PutUint32(oggCommentHeader[8:], uint32(len(vendorName))) // Vendor Length -+ copy(oggCommentHeader[12:], vendorName) // Vendor name 'pion' -+ binary.LittleEndian.PutUint32(oggCommentHeader[16:], 0) // User Comment List Length + const ( +@@ -68,7 +67,7 @@ + stream: out, + sampleRate: sampleRate, + channelCount: channelCount, +- serial: util.RandUint32(), ++ serial: RandUint32(), + checksumTable: generateChecksumTable(), - // RFC specifies that the page where the CommentHeader completes should have a granule position of 0 - data = i.createPage(oggCommentHeader, pageHeaderTypeContinuationOfStream, 0, i.pageIndex) + // Timestamp and Granule MUST start from 1 diff --git a/patch/util.go.patch b/patch/util.go.patch new file mode 100644 index 0000000..2a923c6 --- /dev/null +++ b/patch/util.go.patch @@ -0,0 +1,11 @@ +--- util.go.org 2024-10-24 10:53:48 ++++ util.go 2024-10-24 10:50:15 +@@ -2,7 +2,7 @@ + // SPDX-License-Identifier: MIT + + // Package util provides auxiliary functions internally used in webrtc package +-package util ++package suzu + + import ( + "errors" diff --git a/pion/oggwriter.go b/pion/oggwriter.go index 7c179e3..0852e38 100644 --- a/pion/oggwriter.go +++ b/pion/oggwriter.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package oggwriter implements OGG media container writer package oggwriter @@ -7,9 +10,9 @@ import ( "io" "os" - "github.com/pion/randutil" "github.com/pion/rtp" "github.com/pion/rtp/codecs" + "github.com/pion/webrtc/v4/internal/util" ) const ( @@ -65,7 +68,7 @@ func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, stream: out, sampleRate: sampleRate, channelCount: channelCount, - serial: randutil.NewMathRandomGenerator().Uint32(), + serial: util.RandUint32(), checksumTable: generateChecksumTable(), // Timestamp and Granule MUST start from 1 @@ -146,7 +149,9 @@ const ( func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uint64, pageIndex uint32) []byte { i.lastPayloadSize = len(payload) - page := make([]byte, pageHeaderSize+1+i.lastPayloadSize) + nSegments := (len(payload) / 255) + 1 // A segment can be at most 255 bytes long. + + page := make([]byte, pageHeaderSize+i.lastPayloadSize+nSegments) copy(page[0:], pageHeaderSignature) // page headers starts with 'OggS' page[4] = 0 // Version @@ -154,14 +159,23 @@ func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uin binary.LittleEndian.PutUint64(page[6:], granulePos) // granule position binary.LittleEndian.PutUint32(page[14:], i.serial) // Bitstream serial number binary.LittleEndian.PutUint32(page[18:], pageIndex) // Page sequence number - page[26] = 1 // Number of segments in page, giving always 1 segment - page[27] = uint8(i.lastPayloadSize) // Segment Table inserting at 27th position since page header length is 27 - copy(page[28:], payload) // inserting at 28th since Segment Table(1) + header length(27) + page[26] = uint8(nSegments) // Number of segments in page. + + // Filling segment table with the lacing values. + // First (nSegments - 1) values will always be 255. + for i := 0; i < nSegments-1; i++ { + page[pageHeaderSize+i] = 255 + } + // The last value will be the remainder. + page[pageHeaderSize+nSegments-1] = uint8(len(payload) % 255) + + copy(page[pageHeaderSize+nSegments:], payload) // Payload goes after the segment table, so at pageHeaderSize+nSegments. var checksum uint32 for index := range page { checksum = (checksum << 8) ^ i.checksumTable[byte(checksum>>24)^page[index]] } + binary.LittleEndian.PutUint32(page[22:], checksum) // Checksum - generating for page data and inserting at 22th position into 32 bits return page diff --git a/pion/util.go b/pion/util.go new file mode 100644 index 0000000..966a623 --- /dev/null +++ b/pion/util.go @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package util provides auxiliary functions internally used in webrtc package +package util + +import ( + "errors" + "strings" + + "github.com/pion/randutil" +) + +const ( + runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +) + +// Use global random generator to properly seed by crypto grade random. +var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals + +// MathRandAlpha generates a mathematical random alphabet sequence of the requested length. +func MathRandAlpha(n int) string { + return globalMathRandomGenerator.GenerateString(n, runesAlpha) +} + +// RandUint32 generates a mathematical random uint32. +func RandUint32() uint32 { + return globalMathRandomGenerator.Uint32() +} + +// FlattenErrs flattens multiple errors into one +func FlattenErrs(errs []error) error { + errs2 := []error{} + for _, e := range errs { + if e != nil { + errs2 = append(errs2, e) + } + } + if len(errs2) == 0 { + return nil + } + return multiError(errs2) +} + +type multiError []error //nolint:errname + +func (me multiError) Error() string { + var errstrings []string + + for _, err := range me { + if err != nil { + errstrings = append(errstrings, err.Error()) + } + } + + if len(errstrings) == 0 { + return "multiError must contain multiple error but is empty" + } + + return strings.Join(errstrings, "\n") +} + +func (me multiError) Is(err error) bool { + for _, e := range me { + if errors.Is(e, err) { + return true + } + if me2, ok := e.(multiError); ok { //nolint:errorlint + if me2.Is(err) { + return true + } + } + } + return false +} From 070dd7a97575b54e32180f32d9a393d87b51af19 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Fri, 8 Nov 2024 16:26:47 +0900 Subject: [PATCH 02/18] =?UTF-8?q?audio=20streaming=20header=20=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler_test.go | 26 ++++++++++++++++++++++++++ testdata/header.jsonl | 9 +++++++++ 2 files changed, 35 insertions(+) create mode 100644 testdata/header.jsonl diff --git a/handler_test.go b/handler_test.go index 24f0535..b743291 100644 --- a/handler_test.go +++ b/handler_test.go @@ -51,6 +51,32 @@ func TestOpusPacketReader(t *testing.T) { } }) + t.Run("audio streaming header", func(t *testing.T) { + d := time.Duration(100) * time.Millisecond + r := readDumpFile(t, "testdata/header.jsonl", 0) + defer r.Close() + + c := Config{ + AudioStreamingHeader: true, + } + reader := NewOpusReader(c, d, r) + + for { + buf := make([]byte, FrameSize) + _, err := reader.Read(buf) + if err != nil { + assert.ErrorIs(t, err, io.EOF) + break + } + // seqNum + assert.Equal(t, buf[8:16], []byte{0, 0, 0, 0, 0, 0, 0, 0}) + // length + assert.Equal(t, buf[16:20], []byte{0, 0, 0, 3}) + assert.Equal(t, buf[20:23], []byte{252, 255, 254}) + + } + }) + t.Run("read error", func(t *testing.T) { d := time.Duration(100) * time.Millisecond errPacketRead := errors.New("packet read error") diff --git a/testdata/header.jsonl b/testdata/header.jsonl new file mode 100644 index 0000000..30a61f1 --- /dev/null +++ b/testdata/header.jsonl @@ -0,0 +1,9 @@ +{"timestamp":1667274760504,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfXwsAAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760521,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfYBSgAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760544,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfYXwAAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760562,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfYpVAAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760585,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfY/ygAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760603,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfZRXgAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760626,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfZn1AAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760643,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfZ4bgAAAAAAAAAAAAAAAP8//4="} +{"timestamp":1667274760666,"channel_id":"sora","connection_id":"JG6CSF8P6D3PS61FW1S4KGK8FM","language_code":"ja-JP","sample_rate":48000,"channel_count":2,"payload":"AAXsYKfaO5AAAAAAAAAAAAAAAAP8//4="} From cf4b0527e4e330bee7aa8b74167058e4d2822bb8 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Mon, 11 Nov 2024 15:31:02 +0900 Subject: [PATCH 03/18] =?UTF-8?q?writeHeaders=20=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/handler.go b/handler.go index f16a1db..053e43b 100644 --- a/handler.go +++ b/handler.go @@ -342,13 +342,6 @@ func opus2ogg(ctx context.Context, opusReader io.Reader, oggWriter io.Writer, sa } defer o.Close() - if err := o.writeHeaders(); err != nil { - if w, ok := oggWriter.(*io.PipeWriter); ok { - w.CloseWithError(err) - } - return err - } - var r io.Reader if c.AudioStreamingHeader { r, err = readPacketWithHeader(opusReader) From ce60b0c0ba39017bbf88796e762b0f0c9dcb9cb6 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Mon, 11 Nov 2024 16:54:52 +0900 Subject: [PATCH 04/18] =?UTF-8?q?patch=20=E3=82=92=E5=BD=93=E3=81=A6?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9ae683c..f3f9366 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,6 +22,9 @@ jobs: - run: go fmt . + - name: Patch + run: make patch + - uses: dominikh/staticcheck-action@v1.3.1 with: version: "2023.1.6" From 9aa55e5a2233476fa900d46f327eb6f438f6c1a4 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Mon, 11 Nov 2024 17:07:00 +0900 Subject: [PATCH 05/18] =?UTF-8?q?Revert=20"patch=20=E3=82=92=E5=BD=93?= =?UTF-8?q?=E3=81=A6=E3=82=8B"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ce60b0c0ba39017bbf88796e762b0f0c9dcb9cb6. --- .github/workflows/ci.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f3f9366..9ae683c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,9 +22,6 @@ jobs: - run: go fmt . - - name: Patch - run: make patch - - uses: dominikh/staticcheck-action@v1.3.1 with: version: "2023.1.6" From b8d044e0de8fdc9a72e948880a5e3963598a1576 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Mon, 11 Nov 2024 17:07:29 +0900 Subject: [PATCH 06/18] =?UTF-8?q?util.go=20=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- util.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 util.go diff --git a/util.go b/util.go new file mode 100644 index 0000000..6a38f1e --- /dev/null +++ b/util.go @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package util provides auxiliary functions internally used in webrtc package +package suzu + +import ( + "errors" + "strings" + + "github.com/pion/randutil" +) + +const ( + runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +) + +// Use global random generator to properly seed by crypto grade random. +var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals + +// MathRandAlpha generates a mathematical random alphabet sequence of the requested length. +func MathRandAlpha(n int) string { + return globalMathRandomGenerator.GenerateString(n, runesAlpha) +} + +// RandUint32 generates a mathematical random uint32. +func RandUint32() uint32 { + return globalMathRandomGenerator.Uint32() +} + +// FlattenErrs flattens multiple errors into one +func FlattenErrs(errs []error) error { + errs2 := []error{} + for _, e := range errs { + if e != nil { + errs2 = append(errs2, e) + } + } + if len(errs2) == 0 { + return nil + } + return multiError(errs2) +} + +type multiError []error //nolint:errname + +func (me multiError) Error() string { + var errstrings []string + + for _, err := range me { + if err != nil { + errstrings = append(errstrings, err.Error()) + } + } + + if len(errstrings) == 0 { + return "multiError must contain multiple error but is empty" + } + + return strings.Join(errstrings, "\n") +} + +func (me multiError) Is(err error) bool { + for _, e := range me { + if errors.Is(e, err) { + return true + } + if me2, ok := e.(multiError); ok { //nolint:errorlint + if me2.Is(err) { + return true + } + } + } + return false +} From b7ee34d07c0e5a4e4bdd1f9a7145866ea7771380 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Mon, 11 Nov 2024 20:24:22 +0900 Subject: [PATCH 07/18] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E3=81=9F?= =?UTF-8?q?=E3=82=81=E5=89=8A=E9=99=A4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/handler.go b/handler.go index f16a1db..282f47b 100644 --- a/handler.go +++ b/handler.go @@ -267,13 +267,6 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { continue } - // payload が足りないのでさらに読み込む - if length < (20 + payloadLength) { - // 前の payload へ追加して次へ - payload = append(payload, p...) - continue - } - // 次の frame が含まれている場合 if length > (20 + payloadLength) { if _, err := w.Write(p[:payloadLength]); err != nil { @@ -319,8 +312,6 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { break } } - - continue } } else { // ヘッダー分に足りなければ次の読み込みへ From 35ffc8264528048bf143f0df0825ced885ba5395 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Mon, 11 Nov 2024 20:28:44 +0900 Subject: [PATCH 08/18] =?UTF-8?q?=E3=82=B3=E3=82=B9=E3=83=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/handler.go b/handler.go index 282f47b..a7967e6 100644 --- a/handler.go +++ b/handler.go @@ -231,6 +231,10 @@ func (s *Server) createSpeechHandler(serviceType string, onResultFunc func(conte } } +const ( + HeaderLength = 20 +) + func readPacketWithHeader(reader io.Reader) (io.Reader, error) { r, w := io.Pipe() @@ -240,7 +244,7 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { var payload []byte for { - buf := make([]byte, 20+0xffff) + buf := make([]byte, HeaderLength+0xffff) n, err := reader.Read(buf) if err != nil { w.CloseWithError(err) @@ -252,12 +256,12 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { if length > 20 { // timestamp(64), sequence number(64), length(32) - h := payload[0:20] - p := payload[20:length] + h := payload[:HeaderLength] + p := payload[HeaderLength:] - payloadLength = int(binary.BigEndian.Uint32(h[16:20])) + payloadLength = int(binary.BigEndian.Uint32(h[16:HeaderLength])) - if length == (20 + payloadLength) { + if length == (HeaderLength + payloadLength) { if _, err := w.Write(p); err != nil { w.CloseWithError(err) return @@ -268,7 +272,7 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { } // 次の frame が含まれている場合 - if length > (20 + payloadLength) { + if length > (HeaderLength + payloadLength) { if _, err := w.Write(p[:payloadLength]); err != nil { w.CloseWithError(err) return @@ -279,14 +283,14 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { // 次の payload がすでにある場合の処理 for { - if length > 20 { - h = payload[0:20] - p = payload[20:length] + if length > HeaderLength { + h = payload[:HeaderLength] + p = payload[HeaderLength:] - payloadLength = int(binary.BigEndian.Uint32(h[16:20])) + payloadLength = int(binary.BigEndian.Uint32(h[16:HeaderLength])) // すでに次の payload が全てある場合 - if length == (20 + payloadLength) { + if length == (HeaderLength + payloadLength) { if _, err := w.Write(p); err != nil { w.CloseWithError(err) return @@ -296,7 +300,7 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { continue } - if length > (20 + payloadLength) { + if length > (HeaderLength + payloadLength) { if _, err := w.Write(p[:payloadLength]); err != nil { w.CloseWithError(err) return From 6ff65aaa203d59e5273324d28ca4d47370ee2544 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 10:33:29 +0900 Subject: [PATCH 09/18] =?UTF-8?q?=E5=87=A6=E7=90=86=E3=82=92=E3=81=BE?= =?UTF-8?q?=E3=81=A8=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler.go | 109 ++++++++++++++++++++++++----------------------------- 1 file changed, 49 insertions(+), 60 deletions(-) diff --git a/handler.go b/handler.go index a7967e6..1313724 100644 --- a/handler.go +++ b/handler.go @@ -254,72 +254,61 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { payload = append(payload, buf[:n]...) length += n - if length > 20 { - // timestamp(64), sequence number(64), length(32) - h := payload[:HeaderLength] - p := payload[HeaderLength:] + // ヘッダー分のデータが揃っていないので、次の読み込みへ + if length < HeaderLength { + continue + } + + // timestamp(64), sequence number(64), length(32) + h := payload[:HeaderLength] + p := payload[HeaderLength:] + + payloadLength = int(binary.BigEndian.Uint32(h[16:HeaderLength])) + + // payload が足りないので、次の読み込みへ + if length < (HeaderLength + payloadLength) { + continue + } + + if _, err := w.Write(p[:payloadLength]); err != nil { + w.CloseWithError(err) + return + } + + payload = p[payloadLength:] + length = len(payload) + + // 全てのデータを書き込んだ場合は次の読み込みへ + if length == 0 { + continue + } + + // 次の frame が含まれている場合 + for { + // ヘッダー分のデータが揃っていないので、次の読み込みへ + if length < HeaderLength { + break + } + + h = payload[:HeaderLength] + p = payload[HeaderLength:] payloadLength = int(binary.BigEndian.Uint32(h[16:HeaderLength])) - if length == (HeaderLength + payloadLength) { - if _, err := w.Write(p); err != nil { - w.CloseWithError(err) - return - } - payload = []byte{} - length = 0 - continue + // payload が足りないので、次の読み込みへ + if length < (HeaderLength + payloadLength) { + break } - // 次の frame が含まれている場合 - if length > (HeaderLength + payloadLength) { - if _, err := w.Write(p[:payloadLength]); err != nil { - w.CloseWithError(err) - return - } - // 次の payload 処理へ - payload = p[payloadLength:] - length = len(payload) - - // 次の payload がすでにある場合の処理 - for { - if length > HeaderLength { - h = payload[:HeaderLength] - p = payload[HeaderLength:] - - payloadLength = int(binary.BigEndian.Uint32(h[16:HeaderLength])) - - // すでに次の payload が全てある場合 - if length == (HeaderLength + payloadLength) { - if _, err := w.Write(p); err != nil { - w.CloseWithError(err) - return - } - payload = []byte{} - length = 0 - continue - } - - if length > (HeaderLength + payloadLength) { - if _, err := w.Write(p[:payloadLength]); err != nil { - w.CloseWithError(err) - return - } - - // 次の payload 処理へ - payload = p[payloadLength:] - length = len(payload) - continue - } - } else { - // payload が足りないので、次の読み込みへ - break - } - } + // データが足りているので payloadLength まで書き込む + if _, err := w.Write(p[:payloadLength]); err != nil { + w.CloseWithError(err) + return } - } else { - // ヘッダー分に足りなければ次の読み込みへ - continue + + // 残りの処理へ + payload = p[payloadLength:] + length = len(payload) } } }() From 0f31a8e5af5392077548b47da614db9c81f59003 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 10:59:01 +0900 Subject: [PATCH 10/18] =?UTF-8?q?=E5=AE=9A=E6=95=B0=E3=81=AB=E3=81=BE?= =?UTF-8?q?=E3=81=A8=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/handler.go b/handler.go index 1313724..eddb06b 100644 --- a/handler.go +++ b/handler.go @@ -16,7 +16,9 @@ import ( ) const ( - FrameSize = 1024 * 10 + FrameSize = 1024 * 10 + HeaderLength = 20 + MaxPayloadLength = 0xffff ) var ( @@ -231,9 +233,7 @@ func (s *Server) createSpeechHandler(serviceType string, onResultFunc func(conte } } -const ( - HeaderLength = 20 -) +const () func readPacketWithHeader(reader io.Reader) (io.Reader, error) { r, w := io.Pipe() @@ -244,7 +244,7 @@ func readPacketWithHeader(reader io.Reader) (io.Reader, error) { var payload []byte for { - buf := make([]byte, HeaderLength+0xffff) + buf := make([]byte, HeaderLength+MaxPayloadLength) n, err := reader.Read(buf) if err != nil { w.CloseWithError(err) From 19a56bff3d3ce7ea5569b8d03aa69a2aee26e538 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 12:41:45 +0900 Subject: [PATCH 11/18] =?UTF-8?q?=E5=8F=97=E4=BF=A1=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=81=9F=E3=83=87=E3=83=BC=E3=82=BF=E9=95=B7=E3=81=94=E3=81=A8?= =?UTF-8?q?=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler_test.go | 244 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 206 insertions(+), 38 deletions(-) diff --git a/handler_test.go b/handler_test.go index b743291..b7192ad 100644 --- a/handler_test.go +++ b/handler_test.go @@ -29,15 +29,15 @@ func (e *ErrReadCloser) Close() error { } func TestOpusPacketReader(t *testing.T) { + c := Config{ + AudioStreamingHeader: false, + } t.Run("success", func(t *testing.T) { d := time.Duration(100) * time.Millisecond r := readDumpFile(t, "testdata/000.jsonl", 0) defer r.Close() - c := Config{ - AudioStreamingHeader: false, - } reader := NewOpusReader(c, d, r) for { @@ -51,41 +51,12 @@ func TestOpusPacketReader(t *testing.T) { } }) - t.Run("audio streaming header", func(t *testing.T) { - d := time.Duration(100) * time.Millisecond - r := readDumpFile(t, "testdata/header.jsonl", 0) - defer r.Close() - - c := Config{ - AudioStreamingHeader: true, - } - reader := NewOpusReader(c, d, r) - - for { - buf := make([]byte, FrameSize) - _, err := reader.Read(buf) - if err != nil { - assert.ErrorIs(t, err, io.EOF) - break - } - // seqNum - assert.Equal(t, buf[8:16], []byte{0, 0, 0, 0, 0, 0, 0, 0}) - // length - assert.Equal(t, buf[16:20], []byte{0, 0, 0, 3}) - assert.Equal(t, buf[20:23], []byte{252, 255, 254}) - - } - }) - t.Run("read error", func(t *testing.T) { d := time.Duration(100) * time.Millisecond errPacketRead := errors.New("packet read error") r := NewErrReadCloser(errPacketRead) - c := Config{ - AudioStreamingHeader: false, - } reader := NewOpusReader(c, d, &r) for { @@ -104,9 +75,6 @@ func TestOpusPacketReader(t *testing.T) { r := readDumpFile(t, "testdata/dump.jsonl", 0) r.Close() - c := Config{ - AudioStreamingHeader: false, - } reader := NewOpusReader(c, d, r) for { @@ -127,9 +95,6 @@ func TestOpusPacketReader(t *testing.T) { r.Close() }() - c := Config{ - AudioStreamingHeader: false, - } reader := NewOpusReader(c, d, r) for { @@ -143,3 +108,206 @@ func TestOpusPacketReader(t *testing.T) { }) } + +func TestReadPacketWithHeader(t *testing.T) { + testCaces := []struct { + Name string + Data [][]byte + Expect [][]byte + }{ + { + Name: "success", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 254, + }, + { + 0, 5, 236, 96, 167, 215, 194, 193, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 255, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + { + 252, 255, 255, + }, + }, + }, + { + Name: "multiple data", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 254, + 0, 5, 236, 96, 167, 215, 194, 193, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 255, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + { + 252, 255, 255, + }, + }, + }, + { + Name: "split data", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + }, + { + 252, 255, 254, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + }, + }, + { + Name: "split data", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + { + 0, 0, 0, 3, + 252, 255, 254, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + }, + }, + { + Name: "split data", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 254, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + }, + }, + { + Name: "split data", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + { + 0, 0, 0, 3, + 252, 255, 254, + 0, 5, 236, 96, 167, 215, 194, 193, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 255, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + { + 252, 255, 255, + }, + }, + }, + { + Name: "split data", + Data: [][]byte{ + { + 0, 5, 236, 96, 167, 215, 194, 192, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + 252, 255, 254, + 0, 5, 236, 96, 167, 215, 194, 193, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, + }, + { + 252, 255, 255, + }, + }, + Expect: [][]byte{ + { + 252, 255, 254, + }, + { + 252, 255, 255, + }, + }, + }, + } + + for _, tc := range testCaces { + t.Run(tc.Name, func(t *testing.T) { + reader, writer := io.Pipe() + defer reader.Close() + + go func() { + defer writer.Close() + for _, data := range tc.Data { + _, err := writer.Write(data) + if err != nil { + if assert.ErrorIs(t, err, io.EOF) { + break + } + t.Error(t, err) + return + } + } + }() + + r, err := readPacketWithHeader(reader) + assert.NoError(t, err) + + i := 0 + for { + buf := make([]byte, HeaderLength+MaxPayloadLength) + n, err := r.Read(buf) + if err != nil { + if assert.ErrorIs(t, err, io.EOF) { + break + } + t.Error(t, err) + return + } + + assert.Equal(t, tc.Expect[i], buf[:n]) + + i += 1 + } + + }) + } +} From fd5567ea628bd5faaa85fca04deb00cd0e16c9b8 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 14:31:15 +0900 Subject: [PATCH 12/18] =?UTF-8?q?third=5Fparty=20=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=83=AC=E3=82=AF=E3=83=88=E3=83=AA=E3=82=92=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 4 ++-- {pion => third_party/pion}/oggwriter.go | 0 {pion => third_party/pion}/util.go | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename {pion => third_party/pion}/oggwriter.go (100%) rename {pion => third_party/pion}/util.go (100%) diff --git a/Makefile b/Makefile index 7a9071c..aaf5940 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ all: patch go build -o bin/suzu cmd/suzu/main.go patch: - patch -o oggwriter.go ./pion/oggwriter.go ./patch/oggwriter.go.patch - patch -o util.go ./pion/util.go ./patch/util.go.patch + patch -o oggwriter.go ./third_party/pion/oggwriter.go ./patch/oggwriter.go.patch + patch -o util.go ./third_party/pion/util.go ./patch/util.go.patch test: diff --git a/pion/oggwriter.go b/third_party/pion/oggwriter.go similarity index 100% rename from pion/oggwriter.go rename to third_party/pion/oggwriter.go diff --git a/pion/util.go b/third_party/pion/util.go similarity index 100% rename from pion/util.go rename to third_party/pion/util.go From 5d207577bee46974042f26c558948d9928857653 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 14:31:20 +0900 Subject: [PATCH 13/18] =?UTF-8?q?Revert=20"util.go=20=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=81=99=E3=82=8B"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b8d044e0de8fdc9a72e948880a5e3963598a1576. --- util.go | 75 --------------------------------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 util.go diff --git a/util.go b/util.go deleted file mode 100644 index 6a38f1e..0000000 --- a/util.go +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -// Package util provides auxiliary functions internally used in webrtc package -package suzu - -import ( - "errors" - "strings" - - "github.com/pion/randutil" -) - -const ( - runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -) - -// Use global random generator to properly seed by crypto grade random. -var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals - -// MathRandAlpha generates a mathematical random alphabet sequence of the requested length. -func MathRandAlpha(n int) string { - return globalMathRandomGenerator.GenerateString(n, runesAlpha) -} - -// RandUint32 generates a mathematical random uint32. -func RandUint32() uint32 { - return globalMathRandomGenerator.Uint32() -} - -// FlattenErrs flattens multiple errors into one -func FlattenErrs(errs []error) error { - errs2 := []error{} - for _, e := range errs { - if e != nil { - errs2 = append(errs2, e) - } - } - if len(errs2) == 0 { - return nil - } - return multiError(errs2) -} - -type multiError []error //nolint:errname - -func (me multiError) Error() string { - var errstrings []string - - for _, err := range me { - if err != nil { - errstrings = append(errstrings, err.Error()) - } - } - - if len(errstrings) == 0 { - return "multiError must contain multiple error but is empty" - } - - return strings.Join(errstrings, "\n") -} - -func (me multiError) Is(err error) bool { - for _, e := range me { - if errors.Is(e, err) { - return true - } - if me2, ok := e.(multiError); ok { //nolint:errorlint - if me2.Is(err) { - return true - } - } - } - return false -} From 371ef64b7d70197a704a4443f695b75b1cf5b05a Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 14:34:37 +0900 Subject: [PATCH 14/18] =?UTF-8?q?staticcheck=20=E5=89=8D=E3=81=AB=20patch?= =?UTF-8?q?=20=E3=82=92=E5=BD=93=E3=81=A6=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9ae683c..f3f9366 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,6 +22,9 @@ jobs: - run: go fmt . + - name: Patch + run: make patch + - uses: dominikh/staticcheck-action@v1.3.1 with: version: "2023.1.6" From 7a297b994469bca24339f40228c0747cef453dfc Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 15:28:37 +0900 Subject: [PATCH 15/18] =?UTF-8?q?staticcheck=20=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 4 ++-- {third_party => _third_party}/pion/oggwriter.go | 0 {third_party => _third_party}/pion/util.go | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename {third_party => _third_party}/pion/oggwriter.go (100%) rename {third_party => _third_party}/pion/util.go (100%) diff --git a/Makefile b/Makefile index aaf5940..4a92547 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ all: patch go build -o bin/suzu cmd/suzu/main.go patch: - patch -o oggwriter.go ./third_party/pion/oggwriter.go ./patch/oggwriter.go.patch - patch -o util.go ./third_party/pion/util.go ./patch/util.go.patch + patch -o oggwriter.go ./_third_party/pion/oggwriter.go ./patch/oggwriter.go.patch + patch -o util.go ./_third_party/pion/util.go ./patch/util.go.patch test: diff --git a/third_party/pion/oggwriter.go b/_third_party/pion/oggwriter.go similarity index 100% rename from third_party/pion/oggwriter.go rename to _third_party/pion/oggwriter.go diff --git a/third_party/pion/util.go b/_third_party/pion/util.go similarity index 100% rename from third_party/pion/util.go rename to _third_party/pion/util.go From 6cdb1014e35982559562bc3da132fcfc543bb362 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 16:09:55 +0900 Subject: [PATCH 16/18] =?UTF-8?q?=E5=A4=89=E6=9B=B4=E5=B1=A5=E6=AD=B4?= =?UTF-8?q?=E3=82=92=E6=9B=B4=E6=96=B0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index e3484e8..96dff15 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ ## develop +- [FIX] 高ビットレートの音声データの場合に、解析結果が送られてこない不具合を修正する + - @Hexa + ### misc ## 2024.4.0 From 794849d53beee7a92fa9ddbf9f1e2339c9a58a5a Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 16:22:46 +0900 Subject: [PATCH 17/18] =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E4=B8=8A=E3=81=92=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3f841c8..0cf8b76 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2024.4.0 +2024.5.0 From b64a83edd49fe770590a15a054b2d4dedd5177d4 Mon Sep 17 00:00:00 2001 From: Yoshida Hiroshi Date: Tue, 12 Nov 2024 16:23:50 +0900 Subject: [PATCH 18/18] =?UTF-8?q?=E5=A4=89=E6=9B=B4=E5=B1=A5=E6=AD=B4?= =?UTF-8?q?=E3=82=92=E6=9B=B4=E6=96=B0=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 96dff15..b7f0741 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,11 +11,13 @@ ## develop +### misc + +## 2024.5.0 + - [FIX] 高ビットレートの音声データの場合に、解析結果が送られてこない不具合を修正する - @Hexa -### misc - ## 2024.4.0 - [ADD] audio streaming header に対応する