Skip to content

Commit

Permalink
Update codec.Reader interface to return byte slice
Browse files Browse the repository at this point in the history
  • Loading branch information
lherman-cs committed Oct 29, 2020
1 parent c8547c4 commit f4a4edc
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 228 deletions.
12 changes: 6 additions & 6 deletions mediadevices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,11 @@ type mockVideoCodec struct {
closed chan struct{}
}

func (m *mockVideoCodec) Read(b []byte) (int, error) {
func (m *mockVideoCodec) Read() ([]byte, error) {
if _, err := m.r.Read(); err != nil {
return 0, err
return nil, err
}
return len(b), nil
return make([]byte, 20), nil
}

func (m *mockVideoCodec) Close() error { return nil }
Expand All @@ -216,11 +216,11 @@ type mockAudioCodec struct {
closed chan struct{}
}

func (m *mockAudioCodec) Read(b []byte) (int, error) {
func (m *mockAudioCodec) Read() ([]byte, error) {
if _, err := m.r.Read(); err != nil {
return 0, err
return nil, err
}
return len(b), nil
return make([]byte, 20), nil
}
func (m *mockAudioCodec) Close() error { return nil }

Expand Down
5 changes: 2 additions & 3 deletions pkg/codec/codec.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package codec

import (
"io"

"github.com/pion/mediadevices/pkg/io/audio"
"github.com/pion/mediadevices/pkg/io/video"
"github.com/pion/mediadevices/pkg/prop"
Expand Down Expand Up @@ -60,7 +58,8 @@ type VideoEncoderBuilder interface {

// ReadCloser is an io.ReadCloser with methods for rate limiting: SetBitRate and ForceKeyFrame
type ReadCloser interface {
io.ReadCloser
Read() ([]byte, error)
Close() error
// SetBitRate sets current target bitrate, lower bitrate means smaller data will be transmitted
// but this also means that the quality will also be lower.
SetBitRate(int) error
Expand Down
24 changes: 5 additions & 19 deletions pkg/codec/mmal/mmal.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ import (
"unsafe"

"github.com/pion/mediadevices/pkg/codec"
mio "github.com/pion/mediadevices/pkg/io"
"github.com/pion/mediadevices/pkg/io/video"
"github.com/pion/mediadevices/pkg/prop"
)

type encoder struct {
engine C.Encoder
buff []byte
r video.Reader
mu sync.Mutex
closed bool
Expand Down Expand Up @@ -57,25 +55,17 @@ func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser,
return &e, nil
}

func (e *encoder) Read(p []byte) (int, error) {
func (e *encoder) Read() ([]byte, error) {
e.mu.Lock()
defer e.mu.Unlock()

if e.closed {
return 0, io.EOF
}

if e.buff != nil {
n, err := mio.Copy(p, e.buff)
if err == nil {
e.buff = nil
}
return n, err
return nil, io.EOF
}

img, err := e.r.Read()
if err != nil {
return 0, err
return nil, err
}
imgReal := img.(*image.YCbCr)
var y, cb, cr C.Slice
Expand All @@ -89,19 +79,15 @@ func (e *encoder) Read(p []byte) (int, error) {
var encodedBuffer *C.MMAL_BUFFER_HEADER_T
status := C.enc_encode(&e.engine, y, cb, cr, &encodedBuffer)
if status.code != 0 {
return 0, statusToErr(&status)
return nil, statusToErr(&status)
}

// GoBytes copies the C array to Go slice. After this, it's safe to release the C array
encoded := C.GoBytes(unsafe.Pointer(encodedBuffer.data), C.int(encodedBuffer.length))
// Release the buffer so that mmal can reuse this memory
C.mmal_buffer_header_release(encodedBuffer)

n, err := mio.Copy(p, encoded)
if err != nil {
e.buff = encoded
}
return n, err
return encoded, err
}

func (e *encoder) SetBitRate(b int) error {
Expand Down
26 changes: 5 additions & 21 deletions pkg/codec/openh264/openh264.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ import (
"unsafe"

"github.com/pion/mediadevices/pkg/codec"
mio "github.com/pion/mediadevices/pkg/io"
"github.com/pion/mediadevices/pkg/io/video"
"github.com/pion/mediadevices/pkg/prop"
)

type encoder struct {
engine *C.Encoder
r video.Reader
buff []byte

mu sync.Mutex
closed bool
Expand Down Expand Up @@ -52,26 +50,17 @@ func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser,
}, nil
}

func (e *encoder) Read(p []byte) (n int, err error) {
func (e *encoder) Read() ([]byte, error) {
e.mu.Lock()
defer e.mu.Unlock()

if e.closed {
return 0, io.EOF
}

if e.buff != nil {
n, err = mio.Copy(p, e.buff)
if err == nil {
e.buff = nil
}

return n, err
return nil, io.EOF
}

img, err := e.r.Read()
if err != nil {
return 0, err
return nil, err
}

yuvImg := img.(*image.YCbCr)
Expand All @@ -85,16 +74,11 @@ func (e *encoder) Read(p []byte) (n int, err error) {
width: C.int(bounds.Max.X - bounds.Min.X),
}, &rv)
if err := errResult(rv); err != nil {
return 0, fmt.Errorf("failed in encoding: %v", err)
return nil, fmt.Errorf("failed in encoding: %v", err)
}

encoded := C.GoBytes(unsafe.Pointer(s.data), s.data_len)
n, err = mio.Copy(p, encoded)
if err != nil {
e.buff = encoded
}

return n, err
return encoded, nil
}

func (e *encoder) SetBitRate(b int) error {
Expand Down
21 changes: 8 additions & 13 deletions pkg/codec/opus/opus.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,27 +72,22 @@ func newEncoder(r audio.Reader, p prop.Media, params Params) (codec.ReadCloser,
return &e, nil
}

func (e *encoder) Read(p []byte) (int, error) {
func (e *encoder) Read() ([]byte, error) {
buff, err := e.reader.Read()
if err != nil {
return 0, err
return nil, err
}

encoded := make([]byte, 1024)
switch b := buff.(type) {
case *wave.Int16Interleaved:
n, err := e.engine.Encode(b.Data, p)
if err != nil {
return n, err
}
return n, nil
n, err := e.engine.Encode(b.Data, encoded)
return encoded[:n:n], err
case *wave.Float32Interleaved:
n, err := e.engine.EncodeFloat32(b.Data, p)
if err != nil {
return n, err
}
return n, nil
n, err := e.engine.EncodeFloat32(b.Data, encoded)
return encoded[:n:n], err
default:
return 0, errors.New("unknown type of audio buffer")
return nil, errors.New("unknown type of audio buffer")
}
}

Expand Down
55 changes: 22 additions & 33 deletions pkg/codec/vaapi/vp8.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ const (

type encoderVP8 struct {
r video.Reader
buf []byte
frame []byte

fdDRI C.int
Expand Down Expand Up @@ -297,25 +296,17 @@ func newVP8Encoder(r video.Reader, p prop.Media, params ParamsVP8) (codec.ReadCl
return e, nil
}

func (e *encoderVP8) Read(p []byte) (int, error) {
func (e *encoderVP8) Read() ([]byte, error) {
e.mu.Lock()
defer e.mu.Unlock()

if e.closed {
return 0, io.EOF
}

if e.buf != nil {
n, err := mio.Copy(p, e.buf)
if err == nil {
e.buf = nil
}
return n, err
return nil, io.EOF
}

img, err := e.r.Read()
if err != nil {
return 0, err
return nil, err
}
yuvImg := img.(*image.YCbCr)

Expand Down Expand Up @@ -357,7 +348,7 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
}
}
if e.picParam.reconstructed_frame == C.VA_INVALID_SURFACE {
return 0, errors.New("no available surface")
return nil, errors.New("no available surface")
}

C.setForceKFFlagVP8(&e.picParam, 0)
Expand Down Expand Up @@ -425,7 +416,7 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
C.size_t(uintptr(p.src)),
&id,
); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to create buffer: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to create buffer: %s", C.GoString(C.vaErrorStr(s)))
}
buffs = append(buffs, id)
}
Expand All @@ -435,17 +426,17 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
e.display, e.ctxID,
e.surfs[surfaceVP8Input],
); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to begin picture: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to begin picture: %s", C.GoString(C.vaErrorStr(s)))
}

// Upload image
var vaImg C.VAImage
var rawBuf unsafe.Pointer
if s := C.vaDeriveImage(e.display, e.surfs[surfaceVP8Input], &vaImg); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to derive image: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to derive image: %s", C.GoString(C.vaErrorStr(s)))
}
if s := C.vaMapBuffer(e.display, vaImg.buf, &rawBuf); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to map buffer: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to map buffer: %s", C.GoString(C.vaErrorStr(s)))
}
// TODO: use vaImg.pitches to support padding
C.memcpy(
Expand All @@ -461,49 +452,49 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
unsafe.Pointer(&yuvImg.Cr[0]), C.size_t(len(yuvImg.Cr)),
)
if s := C.vaUnmapBuffer(e.display, vaImg.buf); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to unmap buffer: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to unmap buffer: %s", C.GoString(C.vaErrorStr(s)))
}
if s := C.vaDestroyImage(e.display, vaImg.image_id); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to destroy image: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to destroy image: %s", C.GoString(C.vaErrorStr(s)))
}

if s := C.vaRenderPicture(
e.display, e.ctxID,
&buffs[1], // 0 is for ouput
C.int(len(buffs)-1),
); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to render picture: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to render picture: %s", C.GoString(C.vaErrorStr(s)))
}
if s := C.vaEndPicture(
e.display, e.ctxID,
); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to end picture: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to end picture: %s", C.GoString(C.vaErrorStr(s)))
}

// Load encoded data
for retry := 3; retry >= 0; retry-- {
if s := C.vaSyncSurface(e.display, e.picParam.reconstructed_frame); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to sync surface: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to sync surface: %s", C.GoString(C.vaErrorStr(s)))
}
var surfStat C.VASurfaceStatus
if s := C.vaQuerySurfaceStatus(
e.display, e.picParam.reconstructed_frame, &surfStat,
); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to query surface status: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to query surface status: %s", C.GoString(C.vaErrorStr(s)))
}
if surfStat == C.VASurfaceReady {
break
}
if retry == 0 {
return 0, fmt.Errorf("failed to sync surface: %d", surfStat)
return nil, fmt.Errorf("failed to sync surface: %d", surfStat)
}
}
var seg *C.VACodedBufferSegment
if s := C.vaMapBufferSeg(e.display, buffs[0], &seg); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to map buffer: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to map buffer: %s", C.GoString(C.vaErrorStr(s)))
}
if seg.status&C.VA_CODED_BUF_STATUS_SLICE_OVERFLOW_MASK != 0 {
return 0, errors.New("buffer size too small")
return nil, errors.New("buffer size too small")
}

if cap(e.frame) < int(seg.size) {
Expand All @@ -516,13 +507,13 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
)

if s := C.vaUnmapBuffer(e.display, buffs[0]); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to unmap buffer: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to unmap buffer: %s", C.GoString(C.vaErrorStr(s)))
}

// Destroy buffers
for _, b := range buffs {
if s := C.vaDestroyBuffer(e.display, b); s != C.VA_STATUS_SUCCESS {
return 0, fmt.Errorf("failed to destroy buffer: %s", C.GoString(C.vaErrorStr(s)))
return nil, fmt.Errorf("failed to destroy buffer: %s", C.GoString(C.vaErrorStr(s)))
}
}

Expand All @@ -545,11 +536,9 @@ func (e *encoderVP8) Read(p []byte) (int, error) {
e.picParam.ref_last_frame = e.picParam.reconstructed_frame
C.setRefreshLastFlagVP8(&e.picParam, 1)

n, err := mio.Copy(p, e.frame)
if err != nil {
e.buf = e.frame
}
return n, err
encoded := make([]byte, len(e.frame))
copy(encoded, e.frame)
return encoded, err
}

func (e *encoderVP8) SetBitRate(b int) error {
Expand Down
Loading

0 comments on commit f4a4edc

Please sign in to comment.