Compare commits

...

1 Commits
rtp ... pool

Author SHA1 Message Date
Lukas Herman
7df20d004e Add pool optimization to frame decoders 2020-10-30 21:12:19 -07:00
7 changed files with 209 additions and 166 deletions

8
pkg/frame/buffer.go Normal file
View File

@@ -0,0 +1,8 @@
package frame
import "image"
type buffer struct {
image image.Image
raw []uint8
}

View File

@@ -6,7 +6,9 @@ import (
"image/jpeg" "image/jpeg"
) )
func decodeMJPEG(frame []byte, width, height int) (image.Image, func(), error) { func decodeMJPEG() decoderFunc {
return func(frame []byte, width, height int) (image.Image, func(), error) {
img, err := jpeg.Decode(bytes.NewReader(frame)) img, err := jpeg.Decode(bytes.NewReader(frame))
return img, func() {}, err return img, func() {}, err
}
} }

View File

@@ -5,22 +5,22 @@ import (
) )
func NewDecoder(f Format) (Decoder, error) { func NewDecoder(f Format) (Decoder, error) {
var decoder decoderFunc var buildDecoder func() decoderFunc
switch f { switch f {
case FormatI420: case FormatI420:
decoder = decodeI420 buildDecoder = decodeI420
case FormatNV21: case FormatNV21:
decoder = decodeNV21 buildDecoder = decodeNV21
case FormatYUY2: case FormatYUY2:
decoder = decodeYUY2 buildDecoder = decodeYUY2
case FormatUYVY: case FormatUYVY:
decoder = decodeUYVY buildDecoder = decodeUYVY
case FormatMJPEG: case FormatMJPEG:
decoder = decodeMJPEG buildDecoder = decodeMJPEG
default: default:
return nil, fmt.Errorf("%s is not supported", f) return nil, fmt.Errorf("%s is not supported", f)
} }
return decoder, nil return buildDecoder(), nil
} }

View File

@@ -5,7 +5,8 @@ import (
"image" "image"
) )
func decodeI420(frame []byte, width, height int) (image.Image, func(), error) { func decodeI420() decoderFunc {
return func(frame []byte, width, height int) (image.Image, func(), error) {
yi := width * height yi := width * height
cbi := yi + width*height/4 cbi := yi + width*height/4
cri := cbi + width*height/4 cri := cbi + width*height/4
@@ -23,9 +24,11 @@ func decodeI420(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio420, SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
}, func() {}, nil }, func() {}, nil
}
} }
func decodeNV21(frame []byte, width, height int) (image.Image, func(), error) { func decodeNV21() decoderFunc {
return func(frame []byte, width, height int) (image.Image, func(), error) {
yi := width * height yi := width * height
ci := yi + width*height/2 ci := yi + width*height/2
@@ -48,4 +51,5 @@ func decodeNV21(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio420, SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
}, func() {}, nil }, func() {}, nil
}
} }

View File

@@ -12,7 +12,8 @@ import (
// void decodeUYVYCGO(uint8_t* y, uint8_t* cb, uint8_t* cr, uint8_t* uyvy, int width, int height); // void decodeUYVYCGO(uint8_t* y, uint8_t* cb, uint8_t* cr, uint8_t* uyvy, int width, int height);
import "C" import "C"
func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) { func decodeYUY2() decoderFunc {
return func(frame []byte, width, height int) (image.Image, func(), error) {
yi := width * height yi := width * height
ci := yi / 2 ci := yi / 2
fi := yi + 2*ci fi := yi + 2*ci
@@ -42,9 +43,11 @@ func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio422, SubsampleRatio: image.YCbCrSubsampleRatio422,
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
}, func() {}, nil }, func() {}, nil
}
} }
func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) { func decodeUYVY() decoderFunc {
return func(frame []byte, width, height int) (image.Image, func(), error) {
yi := width * height yi := width * height
ci := yi / 2 ci := yi / 2
fi := yi + 2*ci fi := yi + 2*ci
@@ -74,4 +77,5 @@ func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio422, SubsampleRatio: image.YCbCrSubsampleRatio422,
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
}, func() {}, nil }, func() {}, nil
}
} }

View File

@@ -5,20 +5,37 @@ package frame
import ( import (
"fmt" "fmt"
"image" "image"
"sync"
) )
func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) { func decodeYUY2() decoderFunc {
pool := sync.Pool{
New: func() interface{} {
return &buffer{
image: &image.YCbCr{},
}
},
}
return func(frame []byte, width, height int) (image.Image, func(), error) {
buff := pool.Get().(*buffer)
yi := width * height yi := width * height
ci := yi / 2 ci := yi / 2
fi := yi + 2*ci fi := yi + 2*ci
if len(frame) != fi { if len(frame) != fi {
pool.Put(buff)
return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi) return nil, func() {}, fmt.Errorf("frame length (%d) less than expected (%d)", len(frame), fi)
} }
y := make([]byte, yi) if len(buff.raw) < fi {
cb := make([]byte, ci) need := fi - len(buff.raw)
cr := make([]byte, ci) buff.raw = append(buff.raw, make([]uint8, need)...)
}
y := buff.raw[:yi:yi]
cb := buff.raw[yi : yi+ci : yi+ci]
cr := buff.raw[yi+ci : fi : fi]
fast := 0 fast := 0
slow := 0 slow := 0
@@ -31,18 +48,23 @@ func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) {
slow++ slow++
} }
return &image.YCbCr{ img := buff.image.(*image.YCbCr)
Y: y, img.Y = y
YStride: width, img.YStride = width
Cb: cb, img.Cb = cb
Cr: cr, img.Cr = cr
CStride: width / 2, img.CStride = width / 2
SubsampleRatio: image.YCbCrSubsampleRatio422, img.SubsampleRatio = image.YCbCrSubsampleRatio422
Rect: image.Rect(0, 0, width, height), img.Rect.Min.X = 0
}, func() {}, nil img.Rect.Min.Y = 0
img.Rect.Max.X = width
img.Rect.Max.Y = height
return img, func() { pool.Put(buff) }, nil
}
} }
func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) { func decodeUYVY() decoderFunc {
return func(frame []byte, width, height int) (image.Image, func(), error) {
yi := width * height yi := width * height
ci := yi / 2 ci := yi / 2
fi := yi + 2*ci fi := yi + 2*ci
@@ -75,4 +97,5 @@ func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio422, SubsampleRatio: image.YCbCrSubsampleRatio422,
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
}, func() {}, nil }, func() {}, nil
}
} }

View File

@@ -27,7 +27,7 @@ func TestDecodeYUY2(t *testing.T) {
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
} }
img, _, err := decodeYUY2(input, width, height) img, _, err := decodeYUY2()(input, width, height)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -56,7 +56,7 @@ func TestDecodeUYVY(t *testing.T) {
Rect: image.Rect(0, 0, width, height), Rect: image.Rect(0, 0, width, height),
} }
img, _, err := decodeUYVY(input, width, height) img, _, err := decodeUYVY()(input, width, height)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -76,11 +76,13 @@ func BenchmarkDecodeYUY2(b *testing.B) {
sz := sz sz := sz
b.Run(fmt.Sprintf("%dx%d", sz.width, sz.height), func(b *testing.B) { b.Run(fmt.Sprintf("%dx%d", sz.width, sz.height), func(b *testing.B) {
input := make([]byte, sz.width*sz.height*2) input := make([]byte, sz.width*sz.height*2)
decode := decodeYUY2()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, _, err := decodeYUY2(input, sz.width, sz.height) _, release, err := decode(input, sz.width, sz.height)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
release()
} }
}) })
} }