mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-09-11 00:22:56 -05:00
feature: provide HDMI status in /api/vm/hdmi (#859)
* feature: provide HDMI status in /api/vm/hdmi * fix: return false when HDMI idle in /api/vm/hdmi
This commit is contained in:
@@ -157,7 +157,7 @@ printf '0\n' > "$STAGE/kvm/now_fps"
|
||||
printf '60\n' > "$STAGE/kvm/qlty"
|
||||
printf '1920\n' > "$STAGE/kvm/width"
|
||||
printf '1080\n' > "$STAGE/kvm/height"
|
||||
printf '1\n' > "$STAGE/kvm/state"
|
||||
printf '0\n' > "$STAGE/kvm/state"
|
||||
printf 'mjpeg\n' > "$STAGE/kvm/type"
|
||||
printf '0' > "$STAGE/kvm/res"
|
||||
|
||||
|
||||
@@ -116,6 +116,16 @@ func (k *KvmVision) SetHDMI(enable bool) int {
|
||||
return result
|
||||
}
|
||||
|
||||
func (k *KvmVision) HasHDMISignal() bool {
|
||||
k.mutex.RLock()
|
||||
defer k.mutex.RUnlock()
|
||||
if k.closed {
|
||||
return false
|
||||
}
|
||||
|
||||
return C.kvmv_hdmi_signal_active() != 0
|
||||
}
|
||||
|
||||
func (k *KvmVision) SetGop(gop uint8) {
|
||||
k.mutex.RLock()
|
||||
defer k.mutex.RUnlock()
|
||||
|
||||
Binary file not shown.
@@ -21,6 +21,7 @@ require (
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/unrolled/secure v1.15.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/sys v0.41.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -79,7 +80,6 @@ require (
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.35.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
||||
@@ -66,6 +66,7 @@ void set_h264_gop(uint8_t _gop);
|
||||
void set_frame_detact(uint8_t _frame_detact);
|
||||
void kvmv_deinit();
|
||||
uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
uint8_t kvmv_hdmi_signal_active(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ type GetOLEDRsp struct {
|
||||
|
||||
type GetGetHdmiStateRsp struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Signal bool `json:"signal"`
|
||||
IdleTimeout int `json:"idleTimeout"`
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ var (
|
||||
setHDMI = func(enabled bool) { common.GetKvmVision().SetHDMI(enabled) }
|
||||
isHdmiDisabled = utils.IsHdmiDisabled
|
||||
getHdmiIdleTimeout = utils.GetHDMIIdleTimeout
|
||||
getHdmiSignal = func() bool { return common.GetKvmVision().HasHDMISignal() }
|
||||
)
|
||||
|
||||
func (s *Service) ResetHdmi(c *gin.Context) {
|
||||
@@ -63,10 +64,12 @@ func (s *Service) DisableHdmi(c *gin.Context) {
|
||||
|
||||
func (s *Service) GetHdmiState(c *gin.Context) {
|
||||
var rsp proto.Response
|
||||
enabled := !isHdmiDisabled()
|
||||
|
||||
rsp.OkRspWithData(c, &proto.GetGetHdmiStateRsp{
|
||||
Enabled: !utils.IsHdmiDisabled(),
|
||||
IdleTimeout: utils.GetHDMIIdleTimeout(),
|
||||
Enabled: enabled,
|
||||
Signal: enabled && getHdmiSignal(),
|
||||
IdleTimeout: getHdmiIdleTimeout(),
|
||||
})
|
||||
|
||||
log.Debug("get hdmi state")
|
||||
|
||||
@@ -74,9 +74,10 @@ void set_h264_gop(uint8_t _gop);
|
||||
void set_frame_detact(uint8_t _frame_detact);
|
||||
void kvmv_deinit();
|
||||
uint8_t kvmv_hdmi_control(uint8_t _en);
|
||||
uint8_t kvmv_hdmi_signal_active(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // KVM_VISION_H_
|
||||
#endif // KVM_VISION_H_
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "vi_state_shared.hpp"
|
||||
#include "internal/vi_state_writer.hpp"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define default_venc_chn 1
|
||||
|
||||
#define VENC_MJPEG 0
|
||||
@@ -43,6 +46,7 @@
|
||||
#define vi_height_path "/kvmapp/kvm/height"
|
||||
#define hdmi_mode_path "/etc/kvm/hdmi_mode"
|
||||
#define hdmi_state_path "/proc/lt_int"
|
||||
#define hdmi_signal_file_path "/kvmapp/kvm/state"
|
||||
#define watchdog_mode_path "/etc/kvm/watchdog"
|
||||
#define watchdog_temp_path "/tmp/watchdog"
|
||||
#define watchdog_file "/tmp/nanokvm_wd"
|
||||
@@ -53,6 +57,7 @@
|
||||
#define LT6911_WRITE 0x00
|
||||
|
||||
pthread_mutex_t vi_mutex;
|
||||
pthread_mutex_t hdmi_signal_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static char NanoKVM_edit[] = {
|
||||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x41,0x0C,0x33,0xC2,0x66,0xBA,0x00,0x00,
|
||||
@@ -77,7 +82,6 @@ typedef struct {
|
||||
uint16_t vpss_height = default_vpss_height;
|
||||
uint8_t venc_type;
|
||||
uint16_t qlty;
|
||||
uint8_t cam_state = 0;
|
||||
uint8_t stream_stop;
|
||||
uint8_t frame_detact = 0;
|
||||
uint8_t display;
|
||||
@@ -125,6 +129,83 @@ uint8_t kvmv_data_buffer_index = 0;
|
||||
uint8_t debug_en = 0;
|
||||
uint8_t last_vi_state_code = 0;
|
||||
uint32_t last_vi_state_refresh_ms = 0;
|
||||
uint8_t hdmi_capture_enabled = 0;
|
||||
uint8_t hdmi_signal_active = 0;
|
||||
|
||||
void debug(const char *format, ...);
|
||||
|
||||
static bool write_hdmi_signal_file(uint8_t active)
|
||||
{
|
||||
char temp_path[] = "/kvmapp/kvm/.state.tmp.XXXXXX";
|
||||
const char *state = active != 0 ? "1\n" : "0\n";
|
||||
const size_t state_size = 2;
|
||||
int fd = mkstemp(temp_path);
|
||||
if(fd < 0){
|
||||
debug("[hdmi] failed to create state file: %d\n", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
int failure = 0;
|
||||
if(fchmod(fd, 0644) != 0){
|
||||
failure = errno;
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
while(failure == 0 && written < state_size){
|
||||
ssize_t result = write(fd, state + written, state_size - written);
|
||||
if(result > 0){
|
||||
written += (size_t)result;
|
||||
} else if(result < 0 && errno == EINTR){
|
||||
continue;
|
||||
} else {
|
||||
failure = result < 0 ? errno : EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if(failure == 0 && fsync(fd) != 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(close(fd) != 0 && failure == 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(failure == 0 && rename(temp_path, hdmi_signal_file_path) != 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(failure != 0){
|
||||
unlink(temp_path);
|
||||
debug("[hdmi] failed to publish state file: %d\n", failure);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void set_hdmi_signal_state(uint8_t active)
|
||||
{
|
||||
pthread_mutex_lock(&hdmi_signal_mutex);
|
||||
uint8_t enabled = __atomic_load_n(&hdmi_capture_enabled, __ATOMIC_ACQUIRE);
|
||||
uint8_t next = enabled != 0 && active != 0 ? 1 : 0;
|
||||
uint8_t previous = __atomic_exchange_n(&hdmi_signal_active, next, __ATOMIC_ACQ_REL);
|
||||
if(previous != next){
|
||||
write_hdmi_signal_file(next);
|
||||
}
|
||||
pthread_mutex_unlock(&hdmi_signal_mutex);
|
||||
}
|
||||
|
||||
static void set_hdmi_capture_enabled(uint8_t enabled)
|
||||
{
|
||||
pthread_mutex_lock(&hdmi_signal_mutex);
|
||||
__atomic_store_n(&hdmi_capture_enabled, enabled != 0 ? 1 : 0, __ATOMIC_RELEASE);
|
||||
__atomic_store_n(&hdmi_signal_active, 0, __ATOMIC_RELEASE);
|
||||
write_hdmi_signal_file(0);
|
||||
pthread_mutex_unlock(&hdmi_signal_mutex);
|
||||
}
|
||||
|
||||
static void set_hdmi_detection_state(uint8_t active)
|
||||
{
|
||||
__atomic_store_n(&kvmv_cfg.hdmi_cable_state, active != 0 ? 1 : 0, __ATOMIC_RELEASE);
|
||||
set_hdmi_signal_state(active);
|
||||
}
|
||||
|
||||
void debug(const char *format, ...)
|
||||
{
|
||||
if(debug_en){
|
||||
@@ -136,8 +217,9 @@ uint8_t refresh_vi_state()
|
||||
{
|
||||
// The driver read blocks for about a second; mark the deadline before it so
|
||||
// that the publication cadence measures wall-clock time, not read time plus it.
|
||||
last_vi_state_refresh_ms = vi_state_shared::monotonic_ms();
|
||||
last_vi_state_refresh_ms = vi_state_shared::monotonic_ms();
|
||||
last_vi_state_code = vi_state_shared::refresh();
|
||||
set_hdmi_detection_state(last_vi_state_code == 1 || last_vi_state_code >= 3);
|
||||
return last_vi_state_code;
|
||||
}
|
||||
|
||||
@@ -1176,7 +1258,7 @@ void* vi_subsystem_detection(void * arg)
|
||||
if(lt6911_get_hdmi_res()){
|
||||
// hdmi get res
|
||||
debug("[hdmi] C HDMI cable insertion!\n");
|
||||
kvmv_cfg.hdmi_cable_state = 1;
|
||||
set_hdmi_detection_state(1);
|
||||
kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
|
||||
if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1;
|
||||
else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){
|
||||
@@ -1188,7 +1270,7 @@ void* vi_subsystem_detection(void * arg)
|
||||
} else {
|
||||
// HDMI res = 0*0/x*0
|
||||
debug("[hdmi] C HDMI cable unplugged!\n");
|
||||
kvmv_cfg.hdmi_cable_state = 0;
|
||||
set_hdmi_detection_state(0);
|
||||
}
|
||||
lt6911_disable();
|
||||
}
|
||||
@@ -1201,7 +1283,7 @@ void* vi_subsystem_detection(void * arg)
|
||||
if(lt6911_get_hdmi_res()){
|
||||
// hdmi get res
|
||||
debug("[hdmi] UXC HDMI cable insertion!\n");
|
||||
kvmv_cfg.hdmi_cable_state = 1;
|
||||
set_hdmi_detection_state(1);
|
||||
kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
|
||||
if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1;
|
||||
else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){
|
||||
@@ -1213,7 +1295,7 @@ void* vi_subsystem_detection(void * arg)
|
||||
} else {
|
||||
// HDMI res = 0*0/x*0
|
||||
debug("[hdmi] UXC HDMI cable unplugged!\n");
|
||||
kvmv_cfg.hdmi_cable_state = 0;
|
||||
set_hdmi_detection_state(0);
|
||||
}
|
||||
lt6911_disable();
|
||||
}
|
||||
@@ -1226,7 +1308,7 @@ void* vi_subsystem_detection(void * arg)
|
||||
if(lt6911_get_hdmi_res()){
|
||||
// hdmi get res
|
||||
debug("[hdmi] D HDMI cable insertion!\n");
|
||||
kvmv_cfg.hdmi_cable_state = 1;
|
||||
set_hdmi_detection_state(1);
|
||||
kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height);
|
||||
if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1;
|
||||
else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){
|
||||
@@ -1238,7 +1320,7 @@ void* vi_subsystem_detection(void * arg)
|
||||
} else {
|
||||
// HDMI res = 0*0/x*0
|
||||
debug("[hdmi] D HDMI cable unplugged!\n");
|
||||
kvmv_cfg.hdmi_cable_state = 0;
|
||||
set_hdmi_detection_state(0);
|
||||
}
|
||||
lt6911_disable();
|
||||
}
|
||||
@@ -1639,7 +1721,7 @@ void kvmv_init(uint8_t _debug_info_en)
|
||||
|
||||
uint8_t check_kvmv(uint8_t _try_num)
|
||||
{
|
||||
if(kvmv_cfg.hdmi_cable_state == 0){
|
||||
if(__atomic_load_n(&kvmv_cfg.hdmi_cable_state, __ATOMIC_ACQUIRE) == 0){
|
||||
debug("[kvmv]HDMI Cable not exist!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1788,17 +1870,8 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql
|
||||
}
|
||||
}
|
||||
}
|
||||
if(kvmv_cfg.cam_state == 0) {
|
||||
kvmv_cfg.cam_state = 1;
|
||||
kvmv_cfg.hdmi_cable_state = 1;
|
||||
system("echo 1 > /kvmapp/kvm/state");
|
||||
}
|
||||
} else {
|
||||
if(kvmv_cfg.cam_state == 1) {
|
||||
kvmv_cfg.cam_state = 0;
|
||||
system("echo 0 > /kvmapp/kvm/state");
|
||||
}
|
||||
delete img;
|
||||
delete img;
|
||||
debug("[kvmv]can`t get img...\n");
|
||||
continue;
|
||||
// pthread_mutex_unlock(&vi_mutex);
|
||||
@@ -1905,6 +1978,7 @@ void free_all_kvmv_data()
|
||||
|
||||
void kvmv_deinit()
|
||||
{
|
||||
set_hdmi_capture_enabled(0);
|
||||
pthread_mutex_destroy(&vi_mutex);
|
||||
kvmv_cfg.try_exit_thread = 1;
|
||||
cam->close();
|
||||
@@ -1914,6 +1988,10 @@ void kvmv_deinit()
|
||||
|
||||
uint8_t kvmv_hdmi_control(uint8_t _en)
|
||||
{
|
||||
if(_en == 0){
|
||||
set_hdmi_capture_enabled(0);
|
||||
}
|
||||
|
||||
if(kvmv_cfg.hw_version == 0){
|
||||
|
||||
FILE *fp;
|
||||
@@ -1935,6 +2013,9 @@ uint8_t kvmv_hdmi_control(uint8_t _en)
|
||||
}
|
||||
if(kvmv_cfg.hw_version != 'p'){
|
||||
debug("[kvmv]Hardware not support!\n");
|
||||
if(_en != 0){
|
||||
set_hdmi_capture_enabled(1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if(access("/sys/class/gpio/gpio451/value", F_OK) != 0){
|
||||
@@ -1954,7 +2035,15 @@ uint8_t kvmv_hdmi_control(uint8_t _en)
|
||||
// Reopening it here or from kvmv_read_img can exhaust the carveout
|
||||
// heap while the HDMI detector is transitioning.
|
||||
kvmv_cfg.fresh_frame_count = fresh_frame_discard_count;
|
||||
set_hdmi_capture_enabled(1);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t kvmv_hdmi_signal_active()
|
||||
{
|
||||
uint8_t enabled = __atomic_load_n(&hdmi_capture_enabled, __ATOMIC_ACQUIRE);
|
||||
uint8_t active = __atomic_load_n(&hdmi_signal_active, __ATOMIC_ACQUIRE);
|
||||
return enabled != 0 && active != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
#include "config.h"
|
||||
#include "system_init.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
using namespace maix;
|
||||
using namespace maix::sys;
|
||||
|
||||
extern kvm_sys_state_t kvm_sys_state;
|
||||
extern kvm_oled_state_t kvm_oled_state;
|
||||
|
||||
static void write_hdmi_state(uint8_t active)
|
||||
{
|
||||
char temp_path[] = "/kvmapp/kvm/.state.init.XXXXXX";
|
||||
const char *state = active != 0 ? "1\n" : "0\n";
|
||||
int fd = mkstemp(temp_path);
|
||||
if(fd < 0){
|
||||
printf("failed to create HDMI state file: %d\n", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
int failure = 0;
|
||||
if(fchmod(fd, 0644) != 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(failure == 0 && write(fd, state, 2) != 2){
|
||||
failure = errno != 0 ? errno : EIO;
|
||||
}
|
||||
if(failure == 0 && fsync(fd) != 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(close(fd) != 0 && failure == 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(failure == 0 && rename(temp_path, "/kvmapp/kvm/state") != 0){
|
||||
failure = errno;
|
||||
}
|
||||
if(failure != 0){
|
||||
unlink(temp_path);
|
||||
printf("failed to publish HDMI state file: %d\n", failure);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t get_hdmi_version()
|
||||
{
|
||||
FILE *fp;
|
||||
@@ -101,7 +136,7 @@ void new_app_init(void)
|
||||
system("echo 2000 > /kvmapp/kvm/qlty");
|
||||
system("echo 720 > /kvmapp/kvm/res");
|
||||
system("echo h264 > /kvmapp/kvm/type");
|
||||
system("echo 0 > /kvmapp/kvm/state");
|
||||
write_hdmi_state(0);
|
||||
system("touch /etc/kvm/frame_detact");
|
||||
|
||||
// rm jpg_stream & kvm_stream
|
||||
|
||||
Reference in New Issue
Block a user