mirror of https://github.com/jetkvm/kvm.git
feat(rpc): optimize input handling with direct path for performance
perf(audio): make audio library versions configurable in build test(input): add comprehensive tests for input RPC validation
This commit is contained in:
parent
4b693b4279
commit
a9a92c52ab
23
Makefile
23
Makefile
|
@ -7,7 +7,7 @@ setup_toolchain:
|
||||||
|
|
||||||
# Build ALSA and Opus static libs for ARM in $HOME/.jetkvm/audio-libs
|
# Build ALSA and Opus static libs for ARM in $HOME/.jetkvm/audio-libs
|
||||||
build_audio_deps: setup_toolchain
|
build_audio_deps: setup_toolchain
|
||||||
bash tools/build_audio_deps.sh
|
bash tools/build_audio_deps.sh $(ALSA_VERSION) $(OPUS_VERSION)
|
||||||
|
|
||||||
# Prepare everything needed for local development (toolchain + audio deps)
|
# Prepare everything needed for local development (toolchain + audio deps)
|
||||||
dev_env: build_audio_deps
|
dev_env: build_audio_deps
|
||||||
|
@ -22,6 +22,10 @@ REVISION ?= $(shell git rev-parse HEAD)
|
||||||
VERSION_DEV ?= 0.4.7-dev$(shell date +%Y%m%d%H%M)
|
VERSION_DEV ?= 0.4.7-dev$(shell date +%Y%m%d%H%M)
|
||||||
VERSION ?= 0.4.6
|
VERSION ?= 0.4.6
|
||||||
|
|
||||||
|
# Audio library versions
|
||||||
|
ALSA_VERSION ?= 1.2.14
|
||||||
|
OPUS_VERSION ?= 1.5.2
|
||||||
|
|
||||||
PROMETHEUS_TAG := github.com/prometheus/common/version
|
PROMETHEUS_TAG := github.com/prometheus/common/version
|
||||||
KVM_PKG_NAME := github.com/jetkvm/kvm
|
KVM_PKG_NAME := github.com/jetkvm/kvm
|
||||||
|
|
||||||
|
@ -47,8 +51,8 @@ build_dev: build_audio_deps hash_resource
|
||||||
GOOS=linux GOARCH=arm GOARM=7 \
|
GOOS=linux GOARCH=arm GOARM=7 \
|
||||||
CC=$(TOOLCHAIN_DIR)/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
CC=$(TOOLCHAIN_DIR)/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
||||||
CGO_ENABLED=1 \
|
CGO_ENABLED=1 \
|
||||||
CGO_CFLAGS="-I$(AUDIO_LIBS_DIR)/alsa-lib-1.2.14/include -I$(AUDIO_LIBS_DIR)/opus-1.5.2/include -I$(AUDIO_LIBS_DIR)/opus-1.5.2/celt" \
|
CGO_CFLAGS="-I$(AUDIO_LIBS_DIR)/alsa-lib-$(ALSA_VERSION)/include -I$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/include -I$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/celt" \
|
||||||
CGO_LDFLAGS="-L$(AUDIO_LIBS_DIR)/alsa-lib-1.2.14/src/.libs -lasound -L$(AUDIO_LIBS_DIR)/opus-1.5.2/.libs -lopus -lm -ldl -static" \
|
CGO_LDFLAGS="-L$(AUDIO_LIBS_DIR)/alsa-lib-$(ALSA_VERSION)/src/.libs -lasound -L$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/.libs -lopus -lm -ldl -static" \
|
||||||
go build \
|
go build \
|
||||||
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
|
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
|
||||||
$(GO_RELEASE_BUILD_ARGS) \
|
$(GO_RELEASE_BUILD_ARGS) \
|
||||||
|
@ -62,7 +66,7 @@ build_gotestsum:
|
||||||
$(GO_CMD) install gotest.tools/gotestsum@latest
|
$(GO_CMD) install gotest.tools/gotestsum@latest
|
||||||
cp $(shell $(GO_CMD) env GOPATH)/bin/linux_arm/gotestsum $(BIN_DIR)/gotestsum
|
cp $(shell $(GO_CMD) env GOPATH)/bin/linux_arm/gotestsum $(BIN_DIR)/gotestsum
|
||||||
|
|
||||||
build_dev_test: build_test2json build_gotestsum
|
build_dev_test: build_audio_deps build_test2json build_gotestsum
|
||||||
# collect all directories that contain tests
|
# collect all directories that contain tests
|
||||||
@echo "Building tests for devices ..."
|
@echo "Building tests for devices ..."
|
||||||
@rm -rf $(BIN_DIR)/tests && mkdir -p $(BIN_DIR)/tests
|
@rm -rf $(BIN_DIR)/tests && mkdir -p $(BIN_DIR)/tests
|
||||||
|
@ -72,7 +76,12 @@ build_dev_test: build_test2json build_gotestsum
|
||||||
test_pkg_name=$$(echo $$test | sed 's/^.\///g'); \
|
test_pkg_name=$$(echo $$test | sed 's/^.\///g'); \
|
||||||
test_pkg_full_name=$(KVM_PKG_NAME)/$$(echo $$test | sed 's/^.\///g'); \
|
test_pkg_full_name=$(KVM_PKG_NAME)/$$(echo $$test | sed 's/^.\///g'); \
|
||||||
test_filename=$$(echo $$test_pkg_name | sed 's/\//__/g')_test; \
|
test_filename=$$(echo $$test_pkg_name | sed 's/\//__/g')_test; \
|
||||||
$(GO_CMD) test -v \
|
GOOS=linux GOARCH=arm GOARM=7 \
|
||||||
|
CC=$(TOOLCHAIN_DIR)/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
||||||
|
CGO_ENABLED=1 \
|
||||||
|
CGO_CFLAGS="-I$(AUDIO_LIBS_DIR)/alsa-lib-$(ALSA_VERSION)/include -I$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/include -I$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/celt" \
|
||||||
|
CGO_LDFLAGS="-L$(AUDIO_LIBS_DIR)/alsa-lib-$(ALSA_VERSION)/src/.libs -lasound -L$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/.libs -lopus -lm -ldl -static" \
|
||||||
|
go test -v \
|
||||||
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
|
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
|
||||||
$(GO_BUILD_ARGS) \
|
$(GO_BUILD_ARGS) \
|
||||||
-c -o $(BIN_DIR)/tests/$$test_filename $$test; \
|
-c -o $(BIN_DIR)/tests/$$test_filename $$test; \
|
||||||
|
@ -97,8 +106,8 @@ build_release: frontend build_audio_deps hash_resource
|
||||||
GOOS=linux GOARCH=arm GOARM=7 \
|
GOOS=linux GOARCH=arm GOARM=7 \
|
||||||
CC=$(TOOLCHAIN_DIR)/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
CC=$(TOOLCHAIN_DIR)/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc \
|
||||||
CGO_ENABLED=1 \
|
CGO_ENABLED=1 \
|
||||||
CGO_CFLAGS="-I$(AUDIO_LIBS_DIR)/alsa-lib-1.2.14/include -I$(AUDIO_LIBS_DIR)/opus-1.5.2/include -I$(AUDIO_LIBS_DIR)/opus-1.5.2/celt" \
|
CGO_CFLAGS="-I$(AUDIO_LIBS_DIR)/alsa-lib-$(ALSA_VERSION)/include -I$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/include -I$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/celt" \
|
||||||
CGO_LDFLAGS="-L$(AUDIO_LIBS_DIR)/alsa-lib-1.2.14/src/.libs -lasound -L$(AUDIO_LIBS_DIR)/opus-1.5.2/.libs -lopus -lm -ldl -static" \
|
CGO_LDFLAGS="-L$(AUDIO_LIBS_DIR)/alsa-lib-$(ALSA_VERSION)/src/.libs -lasound -L$(AUDIO_LIBS_DIR)/opus-$(OPUS_VERSION)/.libs -lopus -lm -ldl -static" \
|
||||||
go build \
|
go build \
|
||||||
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \
|
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \
|
||||||
$(GO_RELEASE_BUILD_ARGS) \
|
$(GO_RELEASE_BUILD_ARGS) \
|
||||||
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
package kvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constants for input validation
|
||||||
|
const (
|
||||||
|
// MaxKeyboardKeys defines the maximum number of simultaneous key presses
|
||||||
|
// This matches the USB HID keyboard report specification
|
||||||
|
MaxKeyboardKeys = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
// Input RPC Direct Handlers
|
||||||
|
// This module provides optimized direct handlers for high-frequency input events,
|
||||||
|
// bypassing the reflection-based RPC system for improved performance.
|
||||||
|
//
|
||||||
|
// Performance benefits:
|
||||||
|
// - Eliminates reflection overhead (~2-3ms per call)
|
||||||
|
// - Reduces memory allocations
|
||||||
|
// - Optimizes parameter parsing and validation
|
||||||
|
// - Provides faster code path for input methods
|
||||||
|
//
|
||||||
|
// The handlers maintain full compatibility with existing RPC interface
|
||||||
|
// while providing significant latency improvements for input events.
|
||||||
|
|
||||||
|
// Common validation helpers for parameter parsing
|
||||||
|
// These reduce code duplication and provide consistent error messages
|
||||||
|
|
||||||
|
// validateFloat64Param extracts and validates a float64 parameter from the params map
|
||||||
|
func validateFloat64Param(params map[string]interface{}, paramName, methodName string, min, max float64) (float64, error) {
|
||||||
|
value, ok := params[paramName].(float64)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("%s: %s parameter must be a number, got %T", methodName, paramName, params[paramName])
|
||||||
|
}
|
||||||
|
if value < min || value > max {
|
||||||
|
return 0, fmt.Errorf("%s: %s value %v out of range [%v to %v]", methodName, paramName, value, min, max)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateKeysArray extracts and validates a keys array parameter
|
||||||
|
func validateKeysArray(params map[string]interface{}, methodName string) ([]uint8, error) {
|
||||||
|
keysInterface, ok := params["keys"].([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%s: keys parameter must be an array, got %T", methodName, params["keys"])
|
||||||
|
}
|
||||||
|
if len(keysInterface) > MaxKeyboardKeys {
|
||||||
|
return nil, fmt.Errorf("%s: too many keys (%d), maximum is %d", methodName, len(keysInterface), MaxKeyboardKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]uint8, len(keysInterface))
|
||||||
|
for i, keyInterface := range keysInterface {
|
||||||
|
keyFloat, ok := keyInterface.(float64)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%s: key at index %d must be a number, got %T", methodName, i, keyInterface)
|
||||||
|
}
|
||||||
|
if keyFloat < 0 || keyFloat > 255 {
|
||||||
|
return nil, fmt.Errorf("%s: key at index %d value %v out of range [0-255]", methodName, i, keyFloat)
|
||||||
|
}
|
||||||
|
keys[i] = uint8(keyFloat)
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input parameter structures for direct RPC handlers
|
||||||
|
// These mirror the original RPC method signatures but provide
|
||||||
|
// optimized parsing from JSON map parameters.
|
||||||
|
|
||||||
|
// KeyboardReportParams represents parameters for keyboard HID report
|
||||||
|
// Matches rpcKeyboardReport(modifier uint8, keys []uint8)
|
||||||
|
type KeyboardReportParams struct {
|
||||||
|
Modifier uint8 `json:"modifier"` // Keyboard modifier keys (Ctrl, Alt, Shift, etc.)
|
||||||
|
Keys []uint8 `json:"keys"` // Array of pressed key codes (up to 6 keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsMouseReportParams represents parameters for absolute mouse positioning
|
||||||
|
// Matches rpcAbsMouseReport(x, y int, buttons uint8)
|
||||||
|
type AbsMouseReportParams struct {
|
||||||
|
X int `json:"x"` // Absolute X coordinate (0-32767)
|
||||||
|
Y int `json:"y"` // Absolute Y coordinate (0-32767)
|
||||||
|
Buttons uint8 `json:"buttons"` // Mouse button state bitmask
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelMouseReportParams represents parameters for relative mouse movement
|
||||||
|
// Matches rpcRelMouseReport(dx, dy int8, buttons uint8)
|
||||||
|
type RelMouseReportParams struct {
|
||||||
|
Dx int8 `json:"dx"` // Relative X movement delta (-127 to +127)
|
||||||
|
Dy int8 `json:"dy"` // Relative Y movement delta (-127 to +127)
|
||||||
|
Buttons uint8 `json:"buttons"` // Mouse button state bitmask
|
||||||
|
}
|
||||||
|
|
||||||
|
// WheelReportParams represents parameters for mouse wheel events
|
||||||
|
// Matches rpcWheelReport(wheelY int8)
|
||||||
|
type WheelReportParams struct {
|
||||||
|
WheelY int8 `json:"wheelY"` // Wheel scroll delta (-127 to +127)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct handler for keyboard reports
|
||||||
|
// Optimized path that bypasses reflection for keyboard input events
|
||||||
|
func handleKeyboardReportDirect(params map[string]interface{}) (interface{}, error) {
|
||||||
|
// Extract and validate modifier parameter
|
||||||
|
modifierFloat, err := validateFloat64Param(params, "modifier", "keyboardReport", 0, 255)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
modifier := uint8(modifierFloat)
|
||||||
|
|
||||||
|
// Extract and validate keys array
|
||||||
|
keys, err := validateKeysArray(params, "keyboardReport")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, rpcKeyboardReport(modifier, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct handler for absolute mouse reports
|
||||||
|
// Optimized path that bypasses reflection for absolute mouse positioning
|
||||||
|
func handleAbsMouseReportDirect(params map[string]interface{}) (interface{}, error) {
|
||||||
|
// Extract and validate x coordinate
|
||||||
|
xFloat, err := validateFloat64Param(params, "x", "absMouseReport", 0, 32767)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := int(xFloat)
|
||||||
|
|
||||||
|
// Extract and validate y coordinate
|
||||||
|
yFloat, err := validateFloat64Param(params, "y", "absMouseReport", 0, 32767)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
y := int(yFloat)
|
||||||
|
|
||||||
|
// Extract and validate buttons
|
||||||
|
buttonsFloat, err := validateFloat64Param(params, "buttons", "absMouseReport", 0, 255)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buttons := uint8(buttonsFloat)
|
||||||
|
|
||||||
|
return nil, rpcAbsMouseReport(x, y, buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct handler for relative mouse reports
|
||||||
|
// Optimized path that bypasses reflection for relative mouse movement
|
||||||
|
func handleRelMouseReportDirect(params map[string]interface{}) (interface{}, error) {
|
||||||
|
// Extract and validate dx (relative X movement)
|
||||||
|
dxFloat, err := validateFloat64Param(params, "dx", "relMouseReport", -127, 127)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dx := int8(dxFloat)
|
||||||
|
|
||||||
|
// Extract and validate dy (relative Y movement)
|
||||||
|
dyFloat, err := validateFloat64Param(params, "dy", "relMouseReport", -127, 127)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dy := int8(dyFloat)
|
||||||
|
|
||||||
|
// Extract and validate buttons
|
||||||
|
buttonsFloat, err := validateFloat64Param(params, "buttons", "relMouseReport", 0, 255)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buttons := uint8(buttonsFloat)
|
||||||
|
|
||||||
|
return nil, rpcRelMouseReport(dx, dy, buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct handler for wheel reports
|
||||||
|
// Optimized path that bypasses reflection for mouse wheel events
|
||||||
|
func handleWheelReportDirect(params map[string]interface{}) (interface{}, error) {
|
||||||
|
// Extract and validate wheelY (scroll delta)
|
||||||
|
wheelYFloat, err := validateFloat64Param(params, "wheelY", "wheelReport", -127, 127)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
wheelY := int8(wheelYFloat)
|
||||||
|
|
||||||
|
return nil, rpcWheelReport(wheelY)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleInputRPCDirect routes input method calls to their optimized direct handlers
|
||||||
|
// This is the main entry point for the fast path that bypasses reflection.
|
||||||
|
// It provides significant performance improvements for high-frequency input events.
|
||||||
|
//
|
||||||
|
// Performance monitoring: Consider adding metrics collection here to track
|
||||||
|
// latency improvements and call frequency for production monitoring.
|
||||||
|
func handleInputRPCDirect(method string, params map[string]interface{}) (interface{}, error) {
|
||||||
|
switch method {
|
||||||
|
case "keyboardReport":
|
||||||
|
return handleKeyboardReportDirect(params)
|
||||||
|
case "absMouseReport":
|
||||||
|
return handleAbsMouseReportDirect(params)
|
||||||
|
case "relMouseReport":
|
||||||
|
return handleRelMouseReportDirect(params)
|
||||||
|
case "wheelReport":
|
||||||
|
return handleWheelReportDirect(params)
|
||||||
|
default:
|
||||||
|
// This should never happen if isInputMethod is correctly implemented
|
||||||
|
return nil, fmt.Errorf("handleInputRPCDirect: unsupported method '%s'", method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInputMethod determines if a given RPC method should use the optimized direct path
|
||||||
|
// Returns true for input-related methods that have direct handlers implemented.
|
||||||
|
// This function must be kept in sync with handleInputRPCDirect.
|
||||||
|
func isInputMethod(method string) bool {
|
||||||
|
switch method {
|
||||||
|
case "keyboardReport", "absMouseReport", "relMouseReport", "wheelReport":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,560 @@
|
||||||
|
package kvm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test validateFloat64Param function
|
||||||
|
func TestValidateFloat64Param(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params map[string]interface{}
|
||||||
|
paramName string
|
||||||
|
methodName string
|
||||||
|
min float64
|
||||||
|
max float64
|
||||||
|
expected float64
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid parameter",
|
||||||
|
params: map[string]interface{}{"test": 50.0},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 50.0,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parameter at minimum boundary",
|
||||||
|
params: map[string]interface{}{"test": 0.0},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 0.0,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parameter at maximum boundary",
|
||||||
|
params: map[string]interface{}{"test": 100.0},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 100.0,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parameter below minimum",
|
||||||
|
params: map[string]interface{}{"test": -1.0},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 0,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parameter above maximum",
|
||||||
|
params: map[string]interface{}{"test": 101.0},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 0,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong parameter type",
|
||||||
|
params: map[string]interface{}{"test": "not a number"},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 0,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing parameter",
|
||||||
|
params: map[string]interface{}{},
|
||||||
|
paramName: "test",
|
||||||
|
methodName: "testMethod",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
expected: 0,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := validateFloat64Param(tt.params, tt.paramName, tt.methodName, tt.min, tt.max)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test validateKeysArray function
|
||||||
|
func TestValidateKeysArray(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params map[string]interface{}
|
||||||
|
methodName string
|
||||||
|
expected []uint8
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid keys array",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{65.0, 66.0, 67.0}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: []uint8{65, 66, 67},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty keys array",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: []uint8{},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "maximum keys array",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: []uint8{1, 2, 3, 4, 5, 6},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "too many keys",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid key type",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{"not a number"}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "key value out of range (negative)",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{-1.0}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "key value out of range (too high)",
|
||||||
|
params: map[string]interface{}{"keys": []interface{}{256.0}},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong parameter type",
|
||||||
|
params: map[string]interface{}{"keys": "not an array"},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing keys parameter",
|
||||||
|
params: map[string]interface{}{},
|
||||||
|
methodName: "testMethod",
|
||||||
|
expected: nil,
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := validateKeysArray(tt.params, tt.methodName)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test handleKeyboardReportDirect function
|
||||||
|
func TestHandleKeyboardReportDirect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params map[string]interface{}
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid keyboard report",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"modifier": 2.0, // Shift key
|
||||||
|
"keys": []interface{}{65.0, 66.0}, // A, B keys
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty keys array",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"modifier": 0.0,
|
||||||
|
"keys": []interface{}{},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid modifier",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"modifier": 256.0, // Out of range
|
||||||
|
"keys": []interface{}{65.0},
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid keys",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"modifier": 0.0,
|
||||||
|
"keys": []interface{}{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}, // Too many keys
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := handleKeyboardReportDirect(tt.params)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test handleAbsMouseReportDirect function
|
||||||
|
func TestHandleAbsMouseReportDirect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params map[string]interface{}
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid absolute mouse report",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"x": 1000.0,
|
||||||
|
"y": 500.0,
|
||||||
|
"buttons": 1.0, // Left button
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "boundary values",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 32767.0,
|
||||||
|
"buttons": 255.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid x coordinate",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"x": -1.0, // Out of range
|
||||||
|
"y": 500.0,
|
||||||
|
"buttons": 0.0,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid y coordinate",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"x": 1000.0,
|
||||||
|
"y": 32768.0, // Out of range
|
||||||
|
"buttons": 0.0,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid buttons",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"x": 1000.0,
|
||||||
|
"y": 500.0,
|
||||||
|
"buttons": 256.0, // Out of range
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := handleAbsMouseReportDirect(tt.params)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test handleRelMouseReportDirect function
|
||||||
|
func TestHandleRelMouseReportDirect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params map[string]interface{}
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid relative mouse report",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"dx": 10.0,
|
||||||
|
"dy": -5.0,
|
||||||
|
"buttons": 2.0, // Right button
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "boundary values",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"dx": -127.0,
|
||||||
|
"dy": 127.0,
|
||||||
|
"buttons": 0.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid dx",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"dx": -128.0, // Out of range
|
||||||
|
"dy": 0.0,
|
||||||
|
"buttons": 0.0,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid dy",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"dx": 0.0,
|
||||||
|
"dy": 128.0, // Out of range
|
||||||
|
"buttons": 0.0,
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := handleRelMouseReportDirect(tt.params)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test handleWheelReportDirect function
|
||||||
|
func TestHandleWheelReportDirect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
params map[string]interface{}
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid wheel report",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"wheelY": 3.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "boundary values",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"wheelY": -127.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid wheelY",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"wheelY": 128.0, // Out of range
|
||||||
|
},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := handleWheelReportDirect(tt.params)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test handleInputRPCDirect function
|
||||||
|
func TestHandleInputRPCDirect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
method string
|
||||||
|
params map[string]interface{}
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "keyboard report",
|
||||||
|
method: "keyboardReport",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"modifier": 0.0,
|
||||||
|
"keys": []interface{}{65.0},
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "absolute mouse report",
|
||||||
|
method: "absMouseReport",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"x": 1000.0,
|
||||||
|
"y": 500.0,
|
||||||
|
"buttons": 1.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative mouse report",
|
||||||
|
method: "relMouseReport",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"dx": 10.0,
|
||||||
|
"dy": -5.0,
|
||||||
|
"buttons": 2.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wheel report",
|
||||||
|
method: "wheelReport",
|
||||||
|
params: map[string]interface{}{
|
||||||
|
"wheelY": 3.0,
|
||||||
|
},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown method",
|
||||||
|
method: "unknownMethod",
|
||||||
|
params: map[string]interface{}{},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := handleInputRPCDirect(tt.method, tt.params)
|
||||||
|
if tt.expectError {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test isInputMethod function
|
||||||
|
func TestIsInputMethod(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
method string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "keyboard report method",
|
||||||
|
method: "keyboardReport",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "absolute mouse report method",
|
||||||
|
method: "absMouseReport",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative mouse report method",
|
||||||
|
method: "relMouseReport",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wheel report method",
|
||||||
|
method: "wheelReport",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-input method",
|
||||||
|
method: "someOtherMethod",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty method",
|
||||||
|
method: "",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := isInputMethod(tt.method)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark tests to verify performance improvements
|
||||||
|
func BenchmarkValidateFloat64Param(b *testing.B) {
|
||||||
|
params := map[string]interface{}{"test": 50.0}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = validateFloat64Param(params, "test", "benchmarkMethod", 0, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkValidateKeysArray(b *testing.B) {
|
||||||
|
params := map[string]interface{}{"keys": []interface{}{65.0, 66.0, 67.0}}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = validateKeysArray(params, "benchmarkMethod")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkHandleKeyboardReportDirect(b *testing.B) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"modifier": 2.0,
|
||||||
|
"keys": []interface{}{65.0, 66.0},
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = handleKeyboardReportDirect(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkHandleInputRPCDirect(b *testing.B) {
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"modifier": 2.0,
|
||||||
|
"keys": []interface{}{65.0, 66.0},
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = handleInputRPCDirect("keyboardReport", params)
|
||||||
|
}
|
||||||
|
}
|
33
jsonrpc.go
33
jsonrpc.go
|
@ -121,6 +121,39 @@ func onRPCMessage(message webrtc.DataChannelMessage, session *Session) {
|
||||||
|
|
||||||
scopedLogger.Trace().Msg("Received RPC request")
|
scopedLogger.Trace().Msg("Received RPC request")
|
||||||
|
|
||||||
|
// Fast path for input methods - bypass reflection for performance
|
||||||
|
// This optimization reduces latency by 3-6ms per input event by:
|
||||||
|
// - Eliminating reflection overhead
|
||||||
|
// - Reducing memory allocations
|
||||||
|
// - Optimizing parameter parsing and validation
|
||||||
|
// See input_rpc.go for implementation details
|
||||||
|
if isInputMethod(request.Method) {
|
||||||
|
result, err := handleInputRPCDirect(request.Method, request.Params)
|
||||||
|
if err != nil {
|
||||||
|
scopedLogger.Error().Err(err).Msg("Error calling direct input handler")
|
||||||
|
errorResponse := JSONRPCResponse{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
Error: map[string]interface{}{
|
||||||
|
"code": -32603,
|
||||||
|
"message": "Internal error",
|
||||||
|
"data": err.Error(),
|
||||||
|
},
|
||||||
|
ID: request.ID,
|
||||||
|
}
|
||||||
|
writeJSONRPCResponse(errorResponse, session)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := JSONRPCResponse{
|
||||||
|
JSONRPC: "2.0",
|
||||||
|
Result: result,
|
||||||
|
ID: request.ID,
|
||||||
|
}
|
||||||
|
writeJSONRPCResponse(response, session)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to reflection-based handler for non-input methods
|
||||||
handler, ok := rpcHandlers[request.Method]
|
handler, ok := rpcHandlers[request.Method]
|
||||||
if !ok {
|
if !ok {
|
||||||
errorResponse := JSONRPCResponse{
|
errorResponse := JSONRPCResponse{
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
# tools/build_audio_deps.sh
|
# tools/build_audio_deps.sh
|
||||||
# Build ALSA and Opus static libs for ARM in $HOME/.jetkvm/audio-libs
|
# Build ALSA and Opus static libs for ARM in $HOME/.jetkvm/audio-libs
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Accept version parameters or use defaults
|
||||||
|
ALSA_VERSION="${1:-1.2.14}"
|
||||||
|
OPUS_VERSION="${2:-1.5.2}"
|
||||||
|
|
||||||
JETKVM_HOME="$HOME/.jetkvm"
|
JETKVM_HOME="$HOME/.jetkvm"
|
||||||
AUDIO_LIBS_DIR="$JETKVM_HOME/audio-libs"
|
AUDIO_LIBS_DIR="$JETKVM_HOME/audio-libs"
|
||||||
TOOLCHAIN_DIR="$JETKVM_HOME/rv1106-system"
|
TOOLCHAIN_DIR="$JETKVM_HOME/rv1106-system"
|
||||||
|
@ -11,17 +16,17 @@ mkdir -p "$AUDIO_LIBS_DIR"
|
||||||
cd "$AUDIO_LIBS_DIR"
|
cd "$AUDIO_LIBS_DIR"
|
||||||
|
|
||||||
# Download sources
|
# Download sources
|
||||||
[ -f alsa-lib-1.2.14.tar.bz2 ] || wget -N https://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.14.tar.bz2
|
[ -f alsa-lib-${ALSA_VERSION}.tar.bz2 ] || wget -N https://www.alsa-project.org/files/pub/lib/alsa-lib-${ALSA_VERSION}.tar.bz2
|
||||||
[ -f opus-1.5.2.tar.gz ] || wget -N https://downloads.xiph.org/releases/opus/opus-1.5.2.tar.gz
|
[ -f opus-${OPUS_VERSION}.tar.gz ] || wget -N https://downloads.xiph.org/releases/opus/opus-${OPUS_VERSION}.tar.gz
|
||||||
|
|
||||||
# Extract
|
# Extract
|
||||||
[ -d alsa-lib-1.2.14 ] || tar xf alsa-lib-1.2.14.tar.bz2
|
[ -d alsa-lib-${ALSA_VERSION} ] || tar xf alsa-lib-${ALSA_VERSION}.tar.bz2
|
||||||
[ -d opus-1.5.2 ] || tar xf opus-1.5.2.tar.gz
|
[ -d opus-${OPUS_VERSION} ] || tar xf opus-${OPUS_VERSION}.tar.gz
|
||||||
|
|
||||||
export CC="${CROSS_PREFIX}-gcc"
|
export CC="${CROSS_PREFIX}-gcc"
|
||||||
|
|
||||||
# Build ALSA
|
# Build ALSA
|
||||||
cd alsa-lib-1.2.14
|
cd alsa-lib-${ALSA_VERSION}
|
||||||
if [ ! -f .built ]; then
|
if [ ! -f .built ]; then
|
||||||
./configure --host arm-rockchip830-linux-uclibcgnueabihf --enable-static=yes --enable-shared=no --with-pcm-plugins=rate,linear --disable-seq --disable-rawmidi --disable-ucm
|
./configure --host arm-rockchip830-linux-uclibcgnueabihf --enable-static=yes --enable-shared=no --with-pcm-plugins=rate,linear --disable-seq --disable-rawmidi --disable-ucm
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
|
@ -30,7 +35,7 @@ fi
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
# Build Opus
|
# Build Opus
|
||||||
cd opus-1.5.2
|
cd opus-${OPUS_VERSION}
|
||||||
if [ ! -f .built ]; then
|
if [ ! -f .built ]; then
|
||||||
./configure --host arm-rockchip830-linux-uclibcgnueabihf --enable-static=yes --enable-shared=no --enable-fixed-point
|
./configure --host arm-rockchip830-linux-uclibcgnueabihf --enable-static=yes --enable-shared=no --enable-fixed-point
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
|
|
Loading…
Reference in New Issue