mirror of
https://github.com/pion/mediadevices.git
synced 2025-09-27 12:52:20 +08:00
113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
// Package mmal implements a hardware accelerated H264 encoder for raspberry pi.
|
|
// This package requires libmmal headers and libraries to be built.
|
|
// Reference: https://github.com/raspberrypi/userland/tree/master/interface/mmal
|
|
package mmal
|
|
|
|
// #cgo pkg-config: mmal
|
|
// #include "bridge.h"
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"io"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/pion/mediadevices/pkg/codec"
|
|
"github.com/pion/mediadevices/pkg/io/video"
|
|
"github.com/pion/mediadevices/pkg/prop"
|
|
)
|
|
|
|
type encoder struct {
|
|
engine C.Encoder
|
|
r video.Reader
|
|
mu sync.Mutex
|
|
closed bool
|
|
cntr int
|
|
}
|
|
|
|
func statusToErr(status *C.Status) error {
|
|
return fmt.Errorf("(status = %d) %s", int(status.code), C.GoString(status.msg))
|
|
}
|
|
|
|
func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
|
|
if params.KeyFrameInterval == 0 {
|
|
params.KeyFrameInterval = 60
|
|
}
|
|
|
|
if params.BitRate == 0 {
|
|
params.BitRate = 300000
|
|
}
|
|
|
|
e := encoder{
|
|
r: video.ToI420(r),
|
|
}
|
|
status := C.enc_new(C.Params{
|
|
width: C.int(p.Width),
|
|
height: C.int(p.Height),
|
|
bitrate: C.uint(params.BitRate),
|
|
key_frame_interval: C.uint(params.KeyFrameInterval),
|
|
}, &e.engine)
|
|
if status.code != 0 {
|
|
return nil, statusToErr(&status)
|
|
}
|
|
|
|
return &e, nil
|
|
}
|
|
|
|
func (e *encoder) Read() ([]byte, func(), error) {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
if e.closed {
|
|
return nil, func() {}, io.EOF
|
|
}
|
|
|
|
img, _, err := e.r.Read()
|
|
if err != nil {
|
|
return nil, func() {}, err
|
|
}
|
|
imgReal := img.(*image.YCbCr)
|
|
var y, cb, cr C.Slice
|
|
y.data = (*C.uchar)(&imgReal.Y[0])
|
|
y.len = C.int(len(imgReal.Y))
|
|
cb.data = (*C.uchar)(&imgReal.Cb[0])
|
|
cb.len = C.int(len(imgReal.Cb))
|
|
cr.data = (*C.uchar)(&imgReal.Cr[0])
|
|
cr.len = C.int(len(imgReal.Cr))
|
|
|
|
var encodedBuffer *C.MMAL_BUFFER_HEADER_T
|
|
status := C.enc_encode(&e.engine, y, cb, cr, &encodedBuffer)
|
|
if status.code != 0 {
|
|
return nil, func() {}, 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)
|
|
|
|
return encoded, func() {}, err
|
|
}
|
|
|
|
func (e *encoder) SetBitRate(b int) error {
|
|
panic("SetBitRate is not implemented")
|
|
}
|
|
|
|
func (e *encoder) ForceKeyFrame() error {
|
|
panic("ForceKeyFrame is not implemented")
|
|
}
|
|
|
|
func (e *encoder) Close() error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
if e.closed {
|
|
return nil
|
|
}
|
|
|
|
e.closed = true
|
|
C.enc_close(&e.engine)
|
|
return nil
|
|
}
|