mirror of https://github.com/jetkvm/kvm.git
feat: add video quality factor config
This commit is contained in:
parent
cf781262f6
commit
58c8c41c05
|
|
@ -106,6 +106,7 @@ type Config struct {
|
||||||
NetworkConfig *types.NetworkConfig `json:"network_config"`
|
NetworkConfig *types.NetworkConfig `json:"network_config"`
|
||||||
DefaultLogLevel string `json:"default_log_level"`
|
DefaultLogLevel string `json:"default_log_level"`
|
||||||
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
||||||
|
VideoQualityFactor float64 `json:"video_quality_factor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetDisplayRotation() uint16 {
|
func (c *Config) GetDisplayRotation() uint16 {
|
||||||
|
|
|
||||||
|
|
@ -405,8 +405,8 @@ char *jetkvm_video_log_status() {
|
||||||
return (char *)videoc_log_status();
|
return (char *)videoc_log_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
int jetkvm_video_init() {
|
int jetkvm_video_init(float factor) {
|
||||||
return video_init();
|
return video_init(factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jetkvm_video_shutdown() {
|
void jetkvm_video_shutdown() {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const char *jetkvm_ui_get_lvgl_version();
|
||||||
|
|
||||||
const char *jetkvm_ui_event_code_to_name(int code);
|
const char *jetkvm_ui_event_code_to_name(int code);
|
||||||
|
|
||||||
int jetkvm_video_init();
|
int jetkvm_video_init(float quality_factor);
|
||||||
void jetkvm_video_shutdown();
|
void jetkvm_video_shutdown();
|
||||||
void jetkvm_video_start();
|
void jetkvm_video_start();
|
||||||
void jetkvm_video_stop();
|
void jetkvm_video_stop();
|
||||||
|
|
|
||||||
|
|
@ -220,10 +220,15 @@ static int32_t buf_init()
|
||||||
|
|
||||||
pthread_t *format_thread = NULL;
|
pthread_t *format_thread = NULL;
|
||||||
|
|
||||||
int video_init()
|
int video_init(float factor)
|
||||||
{
|
{
|
||||||
detect_sleep_mode();
|
detect_sleep_mode();
|
||||||
|
|
||||||
|
if (factor < 0 || factor > 1) {
|
||||||
|
factor = 1.0f;
|
||||||
|
}
|
||||||
|
quality_factor = factor;
|
||||||
|
|
||||||
if (RK_MPI_SYS_Init() != RK_SUCCESS)
|
if (RK_MPI_SYS_Init() != RK_SUCCESS)
|
||||||
{
|
{
|
||||||
log_error("RK_MPI_SYS_Init failed");
|
log_error("RK_MPI_SYS_Init failed");
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
* @return int 0 on success, -1 on failure
|
* @return int 0 on success, -1 on failure
|
||||||
*/
|
*/
|
||||||
int video_init();
|
int video_init(float quality_factor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Shutdown the video subsystem
|
* @brief Shutdown the video subsystem
|
||||||
|
|
|
||||||
|
|
@ -129,11 +129,13 @@ func uiTick() {
|
||||||
C.jetkvm_ui_tick()
|
C.jetkvm_ui_tick()
|
||||||
}
|
}
|
||||||
|
|
||||||
func videoInit() error {
|
func videoInit(factor float64) error {
|
||||||
cgoLock.Lock()
|
cgoLock.Lock()
|
||||||
defer cgoLock.Unlock()
|
defer cgoLock.Unlock()
|
||||||
|
|
||||||
ret := C.jetkvm_video_init()
|
factorC := C.float(factor)
|
||||||
|
|
||||||
|
ret := C.jetkvm_video_init(factorC)
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return fmt.Errorf("failed to initialize video: %d", ret)
|
return fmt.Errorf("failed to initialize video: %d", ret)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ type Native struct {
|
||||||
systemVersion *semver.Version
|
systemVersion *semver.Version
|
||||||
appVersion *semver.Version
|
appVersion *semver.Version
|
||||||
displayRotation uint16
|
displayRotation uint16
|
||||||
|
defaultQualityFactor float64
|
||||||
onVideoStateChange func(state VideoState)
|
onVideoStateChange func(state VideoState)
|
||||||
onVideoFrameReceived func(frame []byte, duration time.Duration)
|
onVideoFrameReceived func(frame []byte, duration time.Duration)
|
||||||
onIndevEvent func(event string)
|
onIndevEvent func(event string)
|
||||||
|
|
@ -22,12 +23,14 @@ type Native struct {
|
||||||
sleepModeSupported bool
|
sleepModeSupported bool
|
||||||
videoLock sync.Mutex
|
videoLock sync.Mutex
|
||||||
screenLock sync.Mutex
|
screenLock sync.Mutex
|
||||||
|
extraLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type NativeOptions struct {
|
type NativeOptions struct {
|
||||||
SystemVersion *semver.Version
|
SystemVersion *semver.Version
|
||||||
AppVersion *semver.Version
|
AppVersion *semver.Version
|
||||||
DisplayRotation uint16
|
DisplayRotation uint16
|
||||||
|
DefaultQualityFactor float64
|
||||||
OnVideoStateChange func(state VideoState)
|
OnVideoStateChange func(state VideoState)
|
||||||
OnVideoFrameReceived func(frame []byte, duration time.Duration)
|
OnVideoFrameReceived func(frame []byte, duration time.Duration)
|
||||||
OnIndevEvent func(event string)
|
OnIndevEvent func(event string)
|
||||||
|
|
@ -65,6 +68,11 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
|
|
||||||
sleepModeSupported := isSleepModeSupported()
|
sleepModeSupported := isSleepModeSupported()
|
||||||
|
|
||||||
|
defaultQualityFactor := opts.DefaultQualityFactor
|
||||||
|
if defaultQualityFactor < 0 || defaultQualityFactor > 1 {
|
||||||
|
defaultQualityFactor = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
return &Native{
|
return &Native{
|
||||||
ready: make(chan struct{}),
|
ready: make(chan struct{}),
|
||||||
l: nativeLogger,
|
l: nativeLogger,
|
||||||
|
|
@ -72,6 +80,7 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
systemVersion: opts.SystemVersion,
|
systemVersion: opts.SystemVersion,
|
||||||
appVersion: opts.AppVersion,
|
appVersion: opts.AppVersion,
|
||||||
displayRotation: opts.DisplayRotation,
|
displayRotation: opts.DisplayRotation,
|
||||||
|
defaultQualityFactor: defaultQualityFactor,
|
||||||
onVideoStateChange: onVideoStateChange,
|
onVideoStateChange: onVideoStateChange,
|
||||||
onVideoFrameReceived: onVideoFrameReceived,
|
onVideoFrameReceived: onVideoFrameReceived,
|
||||||
onIndevEvent: onIndevEvent,
|
onIndevEvent: onIndevEvent,
|
||||||
|
|
@ -97,7 +106,7 @@ func (n *Native) Start() {
|
||||||
n.initUI()
|
n.initUI()
|
||||||
go n.tickUI()
|
go n.tickUI()
|
||||||
|
|
||||||
if err := videoInit(); err != nil {
|
if err := videoInit(n.defaultQualityFactor); err != nil {
|
||||||
n.l.Error().Err(err).Msg("failed to initialize video")
|
n.l.Error().Err(err).Msg("failed to initialize video")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
|
||||||
|
|
@ -9,6 +11,8 @@ const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mod
|
||||||
// DefaultEDID is the default EDID for the video stream.
|
// DefaultEDID is the default EDID for the video stream.
|
||||||
const DefaultEDID = "00ffffffffffff0052620188008888881c150103800000780a0dc9a05747982712484c00000001010101010101010101010101010101023a801871382d40582c4500c48e2100001e011d007251d01e206e285500c48e2100001e000000fc00543734392d6648443732300a20000000fd00147801ff1d000a202020202020017b"
|
const DefaultEDID = "00ffffffffffff0052620188008888881c150103800000780a0dc9a05747982712484c00000001010101010101010101010101010101023a801871382d40582c4500c48e2100001e011d007251d01e206e285500c48e2100001e000000fc00543734392d6648443732300a20000000fd00147801ff1d000a202020202020017b"
|
||||||
|
|
||||||
|
var extraLockTimeout = 5 * time.Second
|
||||||
|
|
||||||
// VideoState is the state of the video stream.
|
// VideoState is the state of the video stream.
|
||||||
type VideoState struct {
|
type VideoState struct {
|
||||||
Ready bool `json:"ready"`
|
Ready bool `json:"ready"`
|
||||||
|
|
@ -69,12 +73,32 @@ func (n *Native) VideoSleepModeSupported() bool {
|
||||||
return n.sleepModeSupported
|
return n.sleepModeSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// useExtraLock uses the extra lock to execute a function.
|
||||||
|
// if the lock is currently held by another goroutine, returns an error.
|
||||||
|
//
|
||||||
|
// it's used to ensure that only one change is made to the video stream at a time.
|
||||||
|
// as the change usually requires to restart video streaming
|
||||||
|
// TODO: check video streaming status instead of using a hardcoded timeout
|
||||||
|
func (n *Native) useExtraLock(fn func() error) error {
|
||||||
|
if !n.extraLock.TryLock() {
|
||||||
|
return fmt.Errorf("the previous change hasn't been completed yet")
|
||||||
|
}
|
||||||
|
err := fn()
|
||||||
|
if err == nil {
|
||||||
|
time.Sleep(extraLockTimeout)
|
||||||
|
}
|
||||||
|
n.extraLock.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// VideoSetQualityFactor sets the quality factor for the video stream.
|
// VideoSetQualityFactor sets the quality factor for the video stream.
|
||||||
func (n *Native) VideoSetQualityFactor(factor float64) error {
|
func (n *Native) VideoSetQualityFactor(factor float64) error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
||||||
return videoSetStreamQualityFactor(factor)
|
return n.useExtraLock(func() error {
|
||||||
|
return videoSetStreamQualityFactor(factor)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoGetQualityFactor gets the quality factor for the video stream.
|
// VideoGetQualityFactor gets the quality factor for the video stream.
|
||||||
|
|
@ -94,7 +118,9 @@ func (n *Native) VideoSetEDID(edid string) error {
|
||||||
edid = DefaultEDID
|
edid = DefaultEDID
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoSetEDID(edid)
|
return n.useExtraLock(func() error {
|
||||||
|
return videoSetEDID(edid)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// VideoGetEDID gets the EDID for the video stream.
|
// VideoGetEDID gets the EDID for the video stream.
|
||||||
|
|
|
||||||
17
native.go
17
native.go
|
|
@ -17,9 +17,10 @@ var (
|
||||||
|
|
||||||
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
||||||
nativeInstance = native.NewNative(native.NativeOptions{
|
nativeInstance = native.NewNative(native.NativeOptions{
|
||||||
SystemVersion: systemVersion,
|
SystemVersion: systemVersion,
|
||||||
AppVersion: appVersion,
|
AppVersion: appVersion,
|
||||||
DisplayRotation: config.GetDisplayRotation(),
|
DisplayRotation: config.GetDisplayRotation(),
|
||||||
|
DefaultQualityFactor: config.VideoQualityFactor,
|
||||||
OnVideoStateChange: func(state native.VideoState) {
|
OnVideoStateChange: func(state native.VideoState) {
|
||||||
lastVideoState = state
|
lastVideoState = state
|
||||||
triggerVideoStateUpdate()
|
triggerVideoStateUpdate()
|
||||||
|
|
@ -60,9 +61,13 @@ func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
|
||||||
})
|
})
|
||||||
|
|
||||||
nativeInstance.Start()
|
nativeInstance.Start()
|
||||||
if err := nativeInstance.VideoSetEDID(config.EdidString); err != nil {
|
go func() {
|
||||||
nativeLogger.Warn().Err(err).Msg("error setting EDID")
|
for {
|
||||||
}
|
if err := nativeInstance.VideoSetEDID(config.EdidString); err != nil {
|
||||||
|
nativeLogger.Warn().Err(err).Msg("error setting EDID")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
if os.Getenv("JETKVM_CRASH_TESTING") == "1" {
|
if os.Getenv("JETKVM_CRASH_TESTING") == "1" {
|
||||||
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
|
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue