Add pool optimization to frame decoders

This commit is contained in:
Lukas Herman
2020-10-30 21:12:19 -07:00
parent 640eeb0cc0
commit 7df20d004e
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"
)
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))
return img, func() {}, err
}
}

View File

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

View File

@@ -5,7 +5,8 @@ import (
"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
cbi := yi + 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,
Rect: image.Rect(0, 0, width, height),
}, 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
ci := yi + width*height/2
@@ -48,4 +51,5 @@ func decodeNV21(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio420,
Rect: image.Rect(0, 0, width, height),
}, 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);
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
ci := yi / 2
fi := yi + 2*ci
@@ -42,9 +43,11 @@ func decodeYUY2(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio422,
Rect: image.Rect(0, 0, width, height),
}, 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
ci := yi / 2
fi := yi + 2*ci
@@ -74,4 +77,5 @@ func decodeUYVY(frame []byte, width, height int) (image.Image, func(), error) {
SubsampleRatio: image.YCbCrSubsampleRatio422,
Rect: image.Rect(0, 0, width, height),
}, func() {}, nil
}
}

View File

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

View File

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