From 3459c7e8d606afef8c077bdc80cf40e2d2ac6fe6 Mon Sep 17 00:00:00 2001 From: Sergey Stepanov Date: Thu, 15 Feb 2024 04:10:49 +0300 Subject: [PATCH] Add VP9 encoder option --- pkg/config/config.yaml | 2 +- pkg/encoder/encoder.go | 12 +++++++++--- pkg/encoder/vpx/libvpx.go | 26 +++++++++++++++++++++----- pkg/network/webrtc/webrtc.go | 2 ++ pkg/worker/room/room_test.go | 12 +++++++----- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml index c532fbe1d..27f1eaac2 100644 --- a/pkg/config/config.yaml +++ b/pkg/config/config.yaml @@ -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 diff --git a/pkg/encoder/encoder.go b/pkg/encoder/encoder.go index b8ef829ae..a323290b3 100644 --- a/pkg/encoder/encoder.go +++ b/pkg/encoder/encoder.go @@ -37,6 +37,7 @@ type VideoCodec string const ( H264 VideoCodec = "h264" VP8 VideoCodec = "vp8" + VP9 VideoCodec = "vp9" VPX VideoCodec = "vpx" ) @@ -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) } diff --git a/pkg/encoder/vpx/libvpx.go b/pkg/encoder/vpx/libvpx.go index 8de6ff409..c175c14f2 100644 --- a/pkg/encoder/vpx/libvpx.go +++ b/pkg/encoder/vpx/libvpx.go @@ -12,6 +12,7 @@ package vpx #include #define VP8_FOURCC 0x30385056 +#define VP9_FOURCC 0x30395056 typedef struct VpxInterface { const char *const name; @@ -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) @@ -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 } @@ -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") } @@ -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 { @@ -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") @@ -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 diff --git a/pkg/network/webrtc/webrtc.go b/pkg/network/webrtc/webrtc.go index 0f67ba7d9..25612d06e 100644 --- a/pkg/network/webrtc/webrtc.go +++ b/pkg/network/webrtc/webrtc.go @@ -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 == "" { diff --git a/pkg/worker/room/room_test.go b/pkg/worker/room/room_test.go index c1459e0ab..2be732d5a 100644 --- a/pkg/worker/room/room_test.go +++ b/pkg/worker/room/room_test.go @@ -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() + } } } @@ -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}, }