videosink: Add options for JPEG and PNG encoders

Expose the quality settings for the JPEG encoder and the compression
level of the PNG encoder via the Options structure.

Signed-off-by: Michael Hanselmann <public@hansmi.ch>
pull/30/head
Michael Hanselmann 4 years ago
parent 42ee8553ed
commit e989a2f6ad

@ -21,12 +21,16 @@ import (
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
"image/jpeg"
"image/png"
"net/http" "net/http"
"sync" "sync"
"periph.io/x/conn/v3/display" "periph.io/x/conn/v3/display"
) )
const defaultJPEGQuality = 95
// Options for videosink devices. // Options for videosink devices.
type Options struct { type Options struct {
// Width and height of the image buffer. // Width and height of the image buffer.
@ -35,11 +39,21 @@ type Options struct {
// Format specifies the image format to send to clients. // Format specifies the image format to send to clients.
Format ImageFormat Format ImageFormat
// TODO: Add options for JPEG and PNG encoder settings // JPEG controls options for the JPEG encoder.
JPEG jpeg.Options
// PNG controls options for the PNG encoder.
PNG struct {
// CompressionLevel is the amount of compression applied by the PNG
// encoder. Defaults to png.DefaultCompression.
CompressionLevel png.CompressionLevel
}
} }
type Display struct { type Display struct {
defaultFormat ImageFormat defaultFormat ImageFormat
jpegOptions jpeg.Options
pngCompressionLevel png.CompressionLevel
mu sync.Mutex mu sync.Mutex
buffer *image.RGBA buffer *image.RGBA
@ -58,12 +72,21 @@ func New(opt *Options) *Display {
// draw operation makes it opaque. // draw operation makes it opaque.
draw.Draw(buffer, buffer.Bounds(), image.Black, image.Point{}, draw.Src) draw.Draw(buffer, buffer.Bounds(), image.Black, image.Point{}, draw.Src)
return &Display{ d := &Display{
jpegOptions: opt.JPEG,
pngCompressionLevel: opt.PNG.CompressionLevel,
buffer: buffer, buffer: buffer,
clients: map[*client]struct{}{}, clients: map[*client]struct{}{},
snapshot: map[imageConfig][]byte{}, snapshot: map[imageConfig][]byte{},
defaultFormat: opt.Format, defaultFormat: opt.Format,
} }
if d.jpegOptions.Quality == 0 {
d.jpegOptions.Quality = defaultJPEGQuality
}
return d
} }
// String returns the name of the device. // String returns the name of the device.

@ -5,15 +5,10 @@
package videosink package videosink
import ( import (
"image/jpeg"
"image/png" "image/png"
"sync" "sync"
) )
var jpegOptions = jpeg.Options{
Quality: 95,
}
type pngEncoderBufferPool sync.Pool type pngEncoderBufferPool sync.Pool
func (p *pngEncoderBufferPool) Get() *png.EncoderBuffer { func (p *pngEncoderBufferPool) Get() *png.EncoderBuffer {
@ -26,21 +21,33 @@ func (p *pngEncoderBufferPool) Put(buf *png.EncoderBuffer) {
} }
type pngEncoderManager struct { type pngEncoderManager struct {
once sync.Once mu sync.Mutex
pool pngEncoderBufferPool pool pngEncoderBufferPool
enc *png.Encoder enc map[png.CompressionLevel]*png.Encoder
} }
var pngEncoder pngEncoderManager var pngEncoder pngEncoderManager
// get returns a PNG encoder with a globally shared buffer pool. // get returns a PNG encoder with a globally shared buffer pool.
func (m *pngEncoderManager) get() *png.Encoder { func (m *pngEncoderManager) get(level png.CompressionLevel) *png.Encoder {
m.once.Do(func() { m.mu.Lock()
m.enc = &png.Encoder{ defer m.mu.Unlock()
CompressionLevel: png.BestSpeed,
enc := m.enc[level]
if enc == nil {
if m.enc == nil {
// The vast majority of use cases will involve exactly one
// compression level.
m.enc = make(map[png.CompressionLevel]*png.Encoder, 1)
}
enc = &png.Encoder{
CompressionLevel: level,
BufferPool: &m.pool, BufferPool: &m.pool,
} }
})
return m.enc m.enc[level] = enc
}
return enc
} }

@ -80,12 +80,12 @@ func (d *Display) encodeBufferLocked(format ImageFormat) ([]byte, error) {
switch format { switch format {
case PNG: case PNG:
if err := pngEncoder.get().Encode(buf, d.buffer); err != nil { if err := pngEncoder.get(d.pngCompressionLevel).Encode(buf, d.buffer); err != nil {
return nil, err return nil, err
} }
case JPEG: case JPEG:
if err := jpeg.Encode(buf, d.buffer, &jpegOptions); err != nil { if err := jpeg.Encode(buf, d.buffer, &d.jpegOptions); err != nil {
return nil, err return nil, err
} }

Loading…
Cancel
Save