mirror of
https://github.com/pion/mediadevices.git
synced 2026-04-22 15:57:27 +08:00
cd6aaa1393
Also make the ReadCloser an Controllable allows to uncouple the controller implementation from the encoder. This is not needed for the 2 codec controller already implemented (openh264 and vpx) but is more future proof in case it required for other codecs.
116 lines
2.6 KiB
Go
116 lines
2.6 KiB
Go
package openh264
|
|
|
|
// #include <string.h>
|
|
// #include <openh264/codec_api.h>
|
|
// #include <errno.h>
|
|
// #include "bridge.hpp"
|
|
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
|
|
}
|
|
|
|
func newEncoder(r video.Reader, p prop.Media, params Params) (codec.ReadCloser, error) {
|
|
if params.BitRate == 0 {
|
|
params.BitRate = 100000
|
|
}
|
|
|
|
var rv C.int
|
|
cEncoder := C.enc_new(C.EncoderOptions{
|
|
width: C.int(p.Width),
|
|
height: C.int(p.Height),
|
|
target_bitrate: C.int(params.BitRate),
|
|
max_fps: C.float(p.FrameRate),
|
|
usage_type: C.EUsageType(params.UsageType),
|
|
rc_mode: C.RC_MODES(params.RCMode),
|
|
enable_frame_skip: C.bool(params.EnableFrameSkip),
|
|
max_nal_size: C.uint(params.MaxNalSize),
|
|
intra_period: C.uint(params.IntraPeriod),
|
|
multiple_thread_idc: C.int(params.MultipleThreadIdc),
|
|
slice_num: C.uint(params.SliceNum),
|
|
slice_mode: C.SliceModeEnum(params.SliceMode),
|
|
slice_size_constraint: C.uint(params.SliceSizeConstraint),
|
|
}, &rv)
|
|
if err := errResult(rv); err != nil {
|
|
return nil, fmt.Errorf("failed in creating encoder: %v", err)
|
|
}
|
|
|
|
return &encoder{
|
|
engine: cEncoder,
|
|
r: video.ToI420(r),
|
|
}, 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
|
|
}
|
|
|
|
yuvImg := img.(*image.YCbCr)
|
|
bounds := yuvImg.Bounds()
|
|
var rv C.int
|
|
s := C.enc_encode(e.engine, C.Frame{
|
|
y: unsafe.Pointer(&yuvImg.Y[0]),
|
|
u: unsafe.Pointer(&yuvImg.Cb[0]),
|
|
v: unsafe.Pointer(&yuvImg.Cr[0]),
|
|
ystride: C.int(yuvImg.YStride),
|
|
cstride: C.int(yuvImg.CStride),
|
|
height: C.int(bounds.Max.Y - bounds.Min.Y),
|
|
width: C.int(bounds.Max.X - bounds.Min.X),
|
|
}, &rv)
|
|
if err := errResult(rv); err != nil {
|
|
return nil, func() {}, fmt.Errorf("failed in encoding: %v", err)
|
|
}
|
|
|
|
encoded := C.GoBytes(unsafe.Pointer(s.data), s.data_len)
|
|
return encoded, func() {}, nil
|
|
}
|
|
|
|
func (e *encoder) ForceKeyFrame() error {
|
|
e.engine.force_key_frame = C.int(1)
|
|
return nil
|
|
}
|
|
|
|
func (e *encoder) Controller() codec.EncoderController {
|
|
return e
|
|
}
|
|
|
|
func (e *encoder) Close() error {
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
|
|
if e.closed {
|
|
return nil
|
|
}
|
|
|
|
e.closed = true
|
|
|
|
var rv C.int
|
|
C.enc_free(e.engine, &rv)
|
|
return errResult(rv)
|
|
}
|