Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VP9 encoder option #445

Merged
merged 1 commit into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ encoder:
# so we keep the frame buffer roughly half of that size or 2 RTC packets per frame
frame: 10
video:
# h264, vpx (VP8)
# h264, vpx (vp8) or vp9
codec: h264
# Threaded encoder if supported, 0 - auto, 1 - nope, >1 - multi-threaded
threads: 0
Expand Down
12 changes: 9 additions & 3 deletions pkg/encoder/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type VideoCodec string
const (
H264 VideoCodec = "h264"
VP8 VideoCodec = "vp8"
VP9 VideoCodec = "vp9"
VPX VideoCodec = "vpx"
)

Expand All @@ -48,13 +49,18 @@ const (
func NewVideoEncoder(w, h, dw, dh int, scale float64, conf config.Video, log *logger.Logger) (*Video, error) {
var enc Encoder
var err error
switch VideoCodec(conf.Codec) {
codec := VideoCodec(conf.Codec)
switch codec {
case H264:
opts := h264.Options(conf.H264)
enc, err = h264.NewEncoder(dw, dh, conf.Threads, &opts)
case VP8, VPX:
case VP8, VP9, VPX:
opts := vpx.Options(conf.Vpx)
enc, err = vpx.NewEncoder(dw, dh, &opts)
v := 8
if codec == VP9 {
v = 9
}
enc, err = vpx.NewEncoder(dw, dh, conf.Threads, v, &opts)
default:
err = fmt.Errorf("unsupported codec: %v", conf.Codec)
}
Expand Down
26 changes: 21 additions & 5 deletions pkg/encoder/vpx/libvpx.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package vpx
#include <string.h>

#define VP8_FOURCC 0x30385056
#define VP9_FOURCC 0x30395056

typedef struct VpxInterface {
const char *const name;
Expand Down Expand Up @@ -42,7 +43,10 @@ FrameBuffer get_frame_buffer(vpx_codec_ctx_t *codec, vpx_codec_iter_t *iter) {
return fb;
}

const VpxInterface vpx_encoders[] = {{ "vp8", VP8_FOURCC, &vpx_codec_vp8_cx }};
const VpxInterface vpx_encoders[] = {
{ "vp8", VP8_FOURCC, &vpx_codec_vp8_cx },
{ "vp9", VP9_FOURCC, &vpx_codec_vp9_cx },
};

int vpx_img_plane_width(const vpx_image_t *img, int plane) {
if (plane > 0 && img->x_chroma_shift > 0)
Expand Down Expand Up @@ -85,6 +89,7 @@ type Vpx struct {
codecCtx C.vpx_codec_ctx_t
kfi C.int
flipped bool
v int
}

func (vpx *Vpx) SetFlip(b bool) { vpx.flipped = b }
Expand All @@ -96,8 +101,12 @@ type Options struct {
KeyframeInterval uint
}

func NewEncoder(w, h int, opts *Options) (*Vpx, error) {
encoder := &C.vpx_encoders[0]
func NewEncoder(w, h int, th int, version int, opts *Options) (*Vpx, error) {
idx := 0
if version == 9 {
idx = 1
}
encoder := &C.vpx_encoders[idx]
if encoder == nil {
return nil, fmt.Errorf("couldn't get the encoder")
}
Expand All @@ -112,6 +121,7 @@ func NewEncoder(w, h int, opts *Options) (*Vpx, error) {
vpx := Vpx{
frameCount: C.int(0),
kfi: C.int(opts.KeyframeInterval),
v: version,
}

if C.vpx_img_alloc(&vpx.image, C.VPX_IMG_FMT_I420, C.uint(w), C.uint(h), 1) == nil {
Expand All @@ -125,8 +135,12 @@ func NewEncoder(w, h int, opts *Options) (*Vpx, error) {

cfg.g_w = C.uint(w)
cfg.g_h = C.uint(h)
if th != 0 {
cfg.g_threads = C.uint(th)
}
cfg.g_lag_in_frames = 0
cfg.rc_target_bitrate = C.uint(opts.Bitrate)
cfg.g_error_resilient = 1
cfg.g_error_resilient = C.VPX_ERROR_RESILIENT_DEFAULT

if C.call_vpx_codec_enc_init(&vpx.codecCtx, encoder, &cfg) != 0 {
return nil, fmt.Errorf("failed to initialize encoder")
Expand Down Expand Up @@ -160,7 +174,9 @@ func (vpx *Vpx) Encode(yuv []byte) []byte {
return C.GoBytes(fb.ptr, fb.size)
}

func (vpx *Vpx) Info() string { return fmt.Sprintf("vpx: %v", C.GoString(C.vpx_codec_version_str())) }
func (vpx *Vpx) Info() string {
return fmt.Sprintf("vpx (%v): %v", vpx.v, C.GoString(C.vpx_codec_version_str()))
}

func (vpx *Vpx) IntraRefresh() {
// !to implement
Expand Down
2 changes: 2 additions & 0 deletions pkg/network/webrtc/webrtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ func newTrack(id string, label string, codec string) (*webrtc.TrackLocalStaticSa
mime = webrtc.MimeTypeH264
case "vpx", "vp8":
mime = webrtc.MimeTypeVP8
case "vp9":
mime = webrtc.MimeTypeVP9
}
}
if mime == "" {
Expand Down
12 changes: 7 additions & 5 deletions pkg/worker/room/room_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,15 @@ func TestMain(m *testing.M) {

func TestRoom(t *testing.T) {
tests := []testParams{
{game: alwas, codecs: []codec{encoder.H264}, frames: 300},
{game: alwas, codecs: []codec{encoder.H264, encoder.VP8, encoder.VP9}, frames: 300},
}

for _, test := range tests {
room := room(conf{codec: test.codecs[0], game: test.game})
room.WaitFrame(test.frames)
room.Close()
for _, codec := range test.codecs {
room := room(conf{codec: codec, game: test.game})
room.WaitFrame(test.frames)
room.Close()
}
}
}

Expand Down Expand Up @@ -245,7 +247,7 @@ func room(cfg conf) testRoom {
func BenchmarkRoom(b *testing.B) {
benches := []testParams{
// warm up
{system: "gba", game: sushi, codecs: []codec{encoder.VP8}, frames: 50},
{system: "gba", game: sushi, codecs: []codec{encoder.VP8, encoder.VP9}, frames: 50},
{system: "gba", game: sushi, codecs: []codec{encoder.VP8, encoder.H264}, frames: 100},
{system: "nes", game: alwas, codecs: []codec{encoder.VP8, encoder.H264}, frames: 100},
}
Expand Down