mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-09-11 00:22:56 -05:00
+ Add watchdog. + Add unsupported resolution output prompt. + Refactor automatic resolution detection function. + Add support for UE chip. + Fix the issue where the UE chip server cannot start.
1867 lines
57 KiB
C++
1867 lines
57 KiB
C++
/**
|
||
* 待解决的问题:
|
||
* // 分辨率跟随输出
|
||
* // 自动切换分辨率
|
||
* // HDMI分辨率问题
|
||
* // H264输入空图,VENC容易炸问题
|
||
* // MJPEG/H264切换时卡退
|
||
* // 再加一条手动清空全部内存
|
||
* // deinit时free全部内存
|
||
* // free 错内存时会炸的问题
|
||
*/
|
||
#include "kvm_vision.h"
|
||
|
||
#define default_venc_chn 1
|
||
|
||
#define VENC_MJPEG 0
|
||
#define VENC_H264 1
|
||
|
||
#define KVMV_MAX_TRY_NUM 2
|
||
#define vi_min_width 32
|
||
#define vi_min_height 3
|
||
#define vi_max_width 1920
|
||
#define vi_max_height 1080
|
||
|
||
#define default_vi_width 1920
|
||
#define default_vi_height 1080
|
||
#define default_vpss_width 1920
|
||
#define default_vpss_height 1080
|
||
#define default_venc_type VENC_MJPEG
|
||
#define default_mjpeg_qlty 60
|
||
#define default_h264_qlty 1000
|
||
#define default_h264_gop 30
|
||
|
||
#define kvmv_data_buffer_size 4
|
||
|
||
#define vi_width_path "/kvmapp/kvm/width"
|
||
#define vi_height_path "/kvmapp/kvm/height"
|
||
#define hdmi_mode_path "/etc/kvm/hdmi_mode"
|
||
#define hdmi_state_path "/proc/lt_int"
|
||
#define watchdog_mode_path "/etc/kvm/watchdog"
|
||
#define watchdog_file "/tmp/nanokvm_wd"
|
||
|
||
#define LT6911_ADDR 0x2B
|
||
#define LT6911_READ 0xFF
|
||
#define LT6911_WRITE 0x00
|
||
|
||
pthread_mutex_t vi_mutex;
|
||
|
||
static char NanoKVM_edit[] = {
|
||
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x41,0x0C,0x33,0xC2,0x66,0xBA,0x00,0x00,
|
||
0x2B,0x1F,0x01,0x04,0xA5,0x50,0x22,0x78,0x3B,0xCC,0xE5,0xAB,0x51,0x48,0xA6,0x26,
|
||
0x0C,0x50,0x54,0xBF,0xEF,0x00,0xD1,0xC0,0xB3,0x00,0x95,0x00,0x81,0x80,0x81,0x40,
|
||
0x81,0xC0,0x01,0x01,0x01,0x01,0xD8,0x59,0x00,0x60,0xA3,0x38,0x28,0x40,0xA0,0x10,
|
||
0x3A,0x10,0x20,0x4F,0x31,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF,0x00,0x55,0x4B,0x30,
|
||
0x32,0x31,0x34,0x33,0x30,0x34,0x37,0x37,0x31,0x38,0x00,0x00,0x00,0xFC,0x00,0x50,
|
||
0x48,0x4C,0x20,0x33,0x34,0x32,0x45,0x32,0x0A,0x20,0x20,0x20,0x00,0x00,0x00,0xFD,
|
||
0x00,0x30,0x4B,0x63,0x63,0x1E,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x01,0x8E
|
||
};
|
||
|
||
using namespace maix;
|
||
using namespace maix::sys;
|
||
using namespace maix::peripheral;
|
||
i2c::I2C LT6911_i2c(4, i2c::Mode::MASTER);
|
||
|
||
typedef struct {
|
||
uint16_t vi_width = default_vi_width;
|
||
uint16_t vi_height = default_vi_height;
|
||
uint16_t vpss_width = default_vpss_width;
|
||
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;
|
||
uint8_t display;
|
||
uint8_t reinit_flag = 1;
|
||
uint8_t reopen_cam_flag = 0;
|
||
uint8_t hdmi_cable_state = 0;
|
||
uint8_t try_exit_thread = 0;
|
||
uint8_t thread_is_running = 0;
|
||
uint8_t Auto_res = 0;
|
||
uint8_t hdmi_version = 0;
|
||
uint8_t hw_version = 0;
|
||
uint8_t hdmi_stop_flag = 0;
|
||
uint8_t hdmi_reading_flag = 0;
|
||
uint8_t hdmi_mode = 0;
|
||
uint8_t hdmi_res_type = 0;
|
||
uint8_t hdmi_res_err = 0;
|
||
uint8_t vi_detect_state = 0;
|
||
uint8_t venc_auto_recyc = 0;
|
||
} kvmv_cfg_t;
|
||
|
||
typedef struct {
|
||
uint8_t* p_img_data = NULL;
|
||
uint8_t img_data_type = 0;
|
||
uint32_t img_data_size = 0;
|
||
} kvmv_data_t;
|
||
|
||
typedef struct {
|
||
uint8_t mmf_venc_chn;
|
||
uint8_t enc_h264_running;
|
||
uint8_t enc_h264_init;
|
||
mmf_venc_cfg_t kvm_venc_cfg;
|
||
} kvm_venc_t;
|
||
|
||
camera::Camera *cam = new camera::Camera(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
|
||
// camera::Camera *cam = new camera::Camera(320, 240, image::FMT_RGB888);
|
||
|
||
kvmv_cfg_t kvmv_cfg;
|
||
|
||
kvmv_data_t kvmv_data_buffer[kvmv_data_buffer_size];
|
||
kvmv_data_t kvmv_SPS_buffer = {0};
|
||
kvmv_data_t kvmv_PPS_buffer = {0};
|
||
|
||
uint8_t kvmv_data_buffer_index = 0;
|
||
|
||
uint8_t debug_en = 0;
|
||
void debug(const char *format, ...)
|
||
{
|
||
if(debug_en){
|
||
printf(format);
|
||
}
|
||
}
|
||
|
||
uint8_t to_roll(int8_t _input)
|
||
{
|
||
if(_input < 0) return _input + kvmv_data_buffer_size;
|
||
if(_input >= kvmv_data_buffer_size) return _input - kvmv_data_buffer_size;
|
||
return _input;
|
||
}
|
||
|
||
int maxmin_data(int _max, int _min, int _data)
|
||
{
|
||
if(_data > _max) return _max;
|
||
if(_data < _min) return _min;
|
||
return _data;
|
||
}
|
||
|
||
kvmv_data_t* get_save_buffer()
|
||
{
|
||
kvmv_data_buffer_index = to_roll(kvmv_data_buffer_index + 1);
|
||
// debug("[kvmv]kvmv_data_buffer_index = %d\n", kvmv_data_buffer_index);
|
||
// debug("[kvmv]kvmv_data_buffer.p_img_data = %d\n", kvmv_data_buffer[kvmv_data_buffer_index].p_img_data);
|
||
if(kvmv_data_buffer[kvmv_data_buffer_index].p_img_data == NULL){
|
||
return &kvmv_data_buffer[kvmv_data_buffer_index];
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
// ====HDMI RES==================================================
|
||
|
||
uint16_t hdmi_res_list[][2] = {
|
||
{1920, 1080},
|
||
{1600, 900},
|
||
{1440, 1080},
|
||
{1440, 900},
|
||
{1280, 1024},
|
||
{1280, 960},
|
||
{1280, 800},
|
||
{1280, 720},
|
||
{1152, 864},
|
||
{1024, 768},
|
||
{800, 600},
|
||
};
|
||
|
||
uint16_t hdmi_unsupported_res_list[][2] = {
|
||
{1680, 1050},
|
||
{1440, 1050},
|
||
{1400, 1050},
|
||
{1368, 768},
|
||
{1366, 768},
|
||
{720, 576},
|
||
};
|
||
|
||
/* return 0 : normal res;
|
||
/* return 1 : new res;
|
||
* return 2 : unsupport res;
|
||
* return 3 : unknow res;
|
||
*/
|
||
uint8_t check_res(uint16_t _width, uint16_t _height)
|
||
{
|
||
uint8_t i;
|
||
uint8_t ret;
|
||
|
||
for(i = 0; i < sizeof(hdmi_res_list)/4; i++){
|
||
if(_width == hdmi_res_list[i][0] && _height == hdmi_res_list[i][1]) return NORMAL_RES;
|
||
}
|
||
for(i = 0; i < sizeof(hdmi_unsupported_res_list)/4; i++){
|
||
if(_width == hdmi_unsupported_res_list[i][0] && _height == hdmi_unsupported_res_list[i][1]) return UNSUPPORT_RES;
|
||
}
|
||
return UNKNOWN_RES;
|
||
}
|
||
|
||
void write_res_to_file(uint16_t _width, uint16_t _height)
|
||
{
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo %d > %s", _width, vi_width_path);
|
||
system(Cmd);
|
||
sprintf(Cmd, "echo %d > %s", _height, vi_height_path);
|
||
system(Cmd);
|
||
system("sync");
|
||
}
|
||
|
||
/* return 0 : VI not init;
|
||
* return 1 : HDMI and CSI status are normal;
|
||
* return 2 : HDMI abnormal;
|
||
* return 3 : CSI abnormal: width too small;
|
||
* return 4 : CSI abnormal: width too large;
|
||
* return 5 : CSI abnormal: height too small;
|
||
* return 6 : CSI abnormal: height too large;
|
||
* return 7 : CSI abnormal: Unknown reason;
|
||
*/
|
||
uint8_t get_vi_state()
|
||
{
|
||
char VI_State[10]={0};
|
||
char cmd[100] = "cat /proc/cvitek/vi_dbg | grep -A 17 VIDevFPS | awk '{print $3}'";
|
||
uint8_t FPS[2];
|
||
uint8_t VIWHGTLSCnt[4];
|
||
FILE* fp = popen( cmd, "r" );
|
||
uint8_t ret = 0;
|
||
|
||
if (fgets(VI_State, sizeof(VI_State), fp) != NULL){
|
||
FPS[0] = atoi(VI_State);
|
||
// debug("VIDevFPS = %d\n", FPS[0]);
|
||
} else {
|
||
pclose(fp);
|
||
return ret; // VI not init;
|
||
}
|
||
if (fgets(VI_State, sizeof(VI_State), fp) != NULL){
|
||
FPS[1] = atoi(VI_State);
|
||
// debug("VIFPS = %d\n", FPS[1]);
|
||
}
|
||
if (FPS[0] == 0){
|
||
ret = 2; // HDMI not OK;
|
||
} else if (FPS[1] == 0){
|
||
ret = 3; // HDMI OK ; CSI not;
|
||
} else {
|
||
ret = 1; // HDMI CSI OK;
|
||
}
|
||
if(ret == 3){
|
||
// Ignore other information
|
||
uint8_t count = 0;
|
||
for(count = 0; count < 13; count ++){
|
||
fgets(VI_State, sizeof(VI_State), fp);
|
||
}
|
||
// Check if the resolution might be set incorrectly
|
||
for(count = 0; count < 4; count ++){
|
||
if (fgets(VI_State, sizeof(VI_State), fp) != NULL){
|
||
// debug("VI_State = %s", VI_State);
|
||
VIWHGTLSCnt[count] = atoi(VI_State);
|
||
// printf("count = %d, val = %d\n", count, atoi(VI_State));
|
||
}
|
||
}
|
||
|
||
if(VIWHGTLSCnt[0] != 0) ret = 3; // The vi width setting value is too small
|
||
else if(VIWHGTLSCnt[1] != 0) ret = 4; // The vi width setting value is too large
|
||
else if(VIWHGTLSCnt[2] != 0) ret = 5; // The vi height setting value is too small
|
||
else if(VIWHGTLSCnt[3] != 0) ret = 6; // The vi height setting value is too large
|
||
else ret = 7; // printf("[kvmv] Unexpected situation\n");
|
||
}
|
||
pclose(fp);
|
||
return ret;
|
||
}
|
||
|
||
int set_hdmi_mode(uint8_t _hdmi_mode)
|
||
{
|
||
if(_hdmi_mode >= 0 && _hdmi_mode <= 2){
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo %d > %s", _hdmi_mode, hdmi_mode_path);
|
||
system(Cmd);
|
||
return 1;
|
||
} else {
|
||
debug("[kvmv] Incorrect HDMI mode.\n");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
int get_hdmi_mode(void)
|
||
{
|
||
if(access(hdmi_mode_path, F_OK) == 0){
|
||
// exist
|
||
FILE *fp;
|
||
int file_size;
|
||
uint8_t tmp8;
|
||
uint8_t RW_Data[2];
|
||
|
||
fp = fopen(hdmi_mode_path, "r");
|
||
fread(RW_Data, sizeof(char), 1, fp);
|
||
fclose(fp);
|
||
RW_Data[2] = 0;
|
||
tmp8 = atoi((char*)RW_Data);
|
||
if(tmp8 > 2) {
|
||
tmp8 = 0;
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo 0 > %s", hdmi_mode_path);
|
||
system(Cmd);
|
||
}
|
||
if(tmp8 != kvmv_cfg.hdmi_mode){
|
||
kvmv_cfg.hdmi_mode = tmp8;
|
||
debug("[kvmv] hdmi mode = %d\n", kvmv_cfg.hdmi_mode);
|
||
return 1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
kvmv_cfg.hdmi_mode = 0;
|
||
return 0;
|
||
}
|
||
|
||
uint8_t watchdog_sf_is_open()
|
||
{
|
||
if(access(watchdog_mode_path, F_OK) == 0) return 1;
|
||
else return 0;
|
||
}
|
||
|
||
int vision_update_watchdog()
|
||
{
|
||
FILE *file;
|
||
|
||
file = fopen(watchdog_file, "w");
|
||
if (file == NULL) {
|
||
debug("[kvmv] watchdog open error\n");
|
||
return -1;
|
||
}
|
||
// fprintf(file, "%s", 'v');
|
||
fclose(file);
|
||
return 1;
|
||
}
|
||
|
||
int get_manual_resolution(void)
|
||
{
|
||
uint8_t RW_Data[35];
|
||
FILE *fp;
|
||
int file_size;
|
||
uint16_t tmp_width, tmp_height;
|
||
int res = 0;
|
||
|
||
// get res
|
||
if(access("/kvmapp/kvm/width", F_OK) == 0){
|
||
fp = fopen("/kvmapp/kvm/width", "r");
|
||
fseek(fp, 0, SEEK_END);
|
||
file_size = ftell(fp);
|
||
fseek(fp, 0, SEEK_SET);
|
||
fread(RW_Data, sizeof(char), file_size, fp);
|
||
fclose(fp);
|
||
RW_Data[file_size] = 0;
|
||
tmp_width = atoi((char*)RW_Data);
|
||
} else {
|
||
tmp_width = 1920;
|
||
}
|
||
if(access("/kvmapp/kvm/height", F_OK) == 0){
|
||
fp = fopen("/kvmapp/kvm/height", "r");
|
||
fseek(fp, 0, SEEK_END);
|
||
file_size = ftell(fp);
|
||
fseek(fp, 0, SEEK_SET);
|
||
fread(RW_Data, sizeof(char), file_size, fp);
|
||
fclose(fp);
|
||
RW_Data[file_size] = 0;
|
||
tmp_height = atoi((char*)RW_Data);
|
||
} else {
|
||
tmp_height = 1080;
|
||
}
|
||
|
||
// res min limit
|
||
if(tmp_width < vi_min_width){
|
||
tmp_width = vi_min_width;
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo %d > %s", vi_min_width, vi_width_path);
|
||
system(Cmd);
|
||
}
|
||
if(tmp_height < vi_min_height){
|
||
tmp_height = vi_min_height;
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo %d > %s", vi_min_height, vi_height_path);
|
||
system(Cmd);
|
||
}
|
||
// res max limit
|
||
if(tmp_width > vi_max_width){
|
||
tmp_width = vi_max_width;
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo %d > %s", vi_max_width, vi_width_path);
|
||
system(Cmd);
|
||
}
|
||
if(tmp_height > vi_max_height){
|
||
tmp_height = vi_max_height;
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo %d > %s", vi_max_height, vi_height_path);
|
||
system(Cmd);
|
||
}
|
||
|
||
// res change ?
|
||
if(kvmv_cfg.vi_width != tmp_width){
|
||
kvmv_cfg.vi_width = tmp_width;
|
||
printf("[kvmk] get new width = %d\n", kvmv_cfg.vi_width);
|
||
res = 1;
|
||
}
|
||
if(kvmv_cfg.vi_height != tmp_height){
|
||
kvmv_cfg.vi_height = tmp_height;
|
||
printf("[kvmk] get new height = %d\n", kvmv_cfg.vi_height);
|
||
res = 1;
|
||
}
|
||
return res;
|
||
}
|
||
|
||
uint8_t auto_try_res()
|
||
{
|
||
char Cmd[100]={0};
|
||
uint8_t err_code;
|
||
uint8_t auto_trying_times = 0;
|
||
|
||
for (auto_trying_times = 0; auto_trying_times < sizeof(hdmi_res_list)/4; auto_trying_times++){
|
||
err_code = get_vi_state();
|
||
switch(err_code){
|
||
case 0:
|
||
// shouldn't be possible to run here
|
||
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
|
||
printf("[kvmv] VI not init\n");
|
||
break;
|
||
case 1:
|
||
printf("[kvmv] VI subsystem is normal\n");
|
||
return 1;
|
||
break;
|
||
case 2:
|
||
// HDMI not detected or resolution not supported; interval checks will continue
|
||
printf("[kvmv] Cannot obtain HDMI input\n");
|
||
auto_trying_times--;
|
||
break;
|
||
case 3: // width too small
|
||
case 4: // width too large
|
||
case 5: // height too small
|
||
case 6: // height too large
|
||
// CSI abnormal due to resolution error
|
||
// The test list is short; sequential testing can be performed
|
||
printf("[kvmv] Trying %d * %d res ..\n", hdmi_res_list[auto_trying_times][0], hdmi_res_list[auto_trying_times][1]);
|
||
sprintf(Cmd, "echo %d > %s", hdmi_res_list[auto_trying_times][0], vi_width_path);
|
||
system(Cmd);
|
||
sprintf(Cmd, "echo %d > %s", hdmi_res_list[auto_trying_times][1], vi_height_path);
|
||
system(Cmd);
|
||
|
||
kvmv_cfg.vi_width = hdmi_res_list[auto_trying_times][0];
|
||
kvmv_cfg.vi_height = hdmi_res_list[auto_trying_times][1];
|
||
printf("[kvmv] restart cam...\n");
|
||
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
|
||
time::sleep_ms(50);
|
||
break;
|
||
case 7: // Unknown reason
|
||
printf("[kvmv] CSI abnormal: Unknown reason\n");
|
||
break;
|
||
}
|
||
}
|
||
if (get_vi_state() == 1) return 1;
|
||
if (get_vi_state() == 2) return 2;
|
||
else return 0;
|
||
}
|
||
|
||
/* return :
|
||
* 0 : error
|
||
* 1 : out of mem
|
||
* 2 : normal
|
||
*/
|
||
uint8_t chack_ion()
|
||
{
|
||
// cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep "usage rate:" | awk -F '[:%]' '{print $2}'
|
||
uint8_t RW_Data[10];
|
||
uint8_t ATOI_Data[3] = {0};
|
||
uint8_t ion_usage_rate;
|
||
char Cmd[150]={0};
|
||
// sprintf( Cmd, "cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep \"usage rate:\" | awk -F '[:%]' '{print $2}'");
|
||
sprintf( Cmd, "cat /sys/kernel/debug/ion/cvi_carveout_heap_dump/summary | grep \"usage rate:\" | awk '{print $2}'");
|
||
FILE* fp = popen( Cmd, "r" );
|
||
if ( NULL == fp )
|
||
{
|
||
pclose(fp);
|
||
return 0;
|
||
}
|
||
fgets((char*)RW_Data, 8, fp);
|
||
pclose(fp);
|
||
RW_Data[8] = 0;
|
||
if (RW_Data[6] == '&') return 1;
|
||
else {
|
||
ATOI_Data[0] = RW_Data[5];
|
||
ATOI_Data[1] = RW_Data[6];
|
||
}
|
||
ion_usage_rate = atoi((char*)ATOI_Data);
|
||
|
||
if(ion_usage_rate >= 95) return 1;
|
||
else return 2;
|
||
}
|
||
|
||
void lt6911_enable()
|
||
{
|
||
uint8_t buf[2];
|
||
buf[0] = 0xff;
|
||
buf[1] = 0x80;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0xee;
|
||
buf[1] = 0x01;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
if(kvmv_cfg.hdmi_version != 0){
|
||
// disable watchdog
|
||
buf[0] = 0x10;
|
||
buf[1] = 0x00;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
}
|
||
}
|
||
|
||
void lt6911_disable()
|
||
{
|
||
uint8_t buf[2];
|
||
buf[0] = 0xff;
|
||
buf[1] = 0x80;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0xee;
|
||
buf[1] = 0x00;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
}
|
||
|
||
void lt6911_get_hdmi_errer()
|
||
{
|
||
uint8_t buf[6];
|
||
|
||
buf[0] = 0xff;
|
||
buf[1] = 0xC0;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0x20;
|
||
buf[1] = 0x01;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
time::sleep_ms(100);
|
||
|
||
buf[0] = 0x24;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
|
||
maix::Bytes *dat = LT6911_i2c.readfrom(LT6911_ADDR, 6);
|
||
|
||
buf[0] = 0x20;
|
||
buf[1] = 0x07;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
for(int i = 0; i < 6; i++){
|
||
buf[i] = (uint8_t)dat->data[i];
|
||
}
|
||
delete dat;
|
||
|
||
debug("hdmi_errer_code = %x, %x, %x, %x, %x, %x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
|
||
}
|
||
|
||
uint8_t lt6911_get_hdmi_res()
|
||
{
|
||
uint8_t buf[2];
|
||
uint8_t revbuf[4];
|
||
uint16_t Vactive;
|
||
uint16_t Hactive;
|
||
|
||
|
||
if(kvmv_cfg.hdmi_version == 0){
|
||
// LT6911C
|
||
buf[0] = 0xff;
|
||
buf[1] = 0xd2;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0x83;
|
||
buf[1] = 0x11;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
time::sleep_ms(5);
|
||
|
||
// Vactive
|
||
buf[0] = 0x96;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 2);
|
||
|
||
// Hactive
|
||
buf[0] = 0x8b;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat1 = LT6911_i2c.readfrom(LT6911_ADDR, 2);
|
||
|
||
revbuf[0] = (uint8_t)dat0->data[0];
|
||
revbuf[1] = (uint8_t)dat0->data[1];
|
||
revbuf[2] = (uint8_t)dat1->data[0];
|
||
revbuf[3] = (uint8_t)dat1->data[1];
|
||
|
||
Vactive = (revbuf[0] << 8)|revbuf[1];
|
||
Hactive = (revbuf[2] << 8)|revbuf[3];
|
||
Hactive *= 2;
|
||
|
||
debug("[hdmi]HDMI res modification event\n");
|
||
debug("[hdmi]new res: %d * %d\n", Hactive, Vactive);
|
||
|
||
delete dat0;
|
||
delete dat1;
|
||
|
||
if (Vactive != 0 && Hactive != 0){
|
||
return 1;
|
||
} else {
|
||
// system("echo 0 > %s", vi_height_path);
|
||
// system("echo 0 > %s", vi_width_path);
|
||
return 0;
|
||
}
|
||
} else if (kvmv_cfg.hdmi_version == 1){
|
||
// LT6911UXC
|
||
buf[0] = 0xff;
|
||
buf[1] = 0x86;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0xff;
|
||
buf[1] = 0x86;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
// HDMI signal disappear/stable
|
||
buf[0] = 0xA3;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 1);
|
||
|
||
revbuf[0] = (uint8_t)dat0->data[0];
|
||
delete dat0;
|
||
|
||
debug("[hdmi]HDMI-UXC res modification event\n");
|
||
|
||
if(revbuf[0] == 0x55) return 1;
|
||
else if(revbuf[0] == 0x88) return 0;
|
||
else return 0;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
void lt6911_get_hdmi_clk()
|
||
{
|
||
uint8_t buf[2];
|
||
uint8_t revbuf[3];
|
||
uint32_t clk;
|
||
|
||
buf[0] = 0xff;
|
||
buf[1] = 0xa0;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0x34;
|
||
buf[1] = 0x0b;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
time::sleep_ms(50);
|
||
|
||
// clk
|
||
buf[0] = 0xff;
|
||
buf[1] = 0xb8;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
buf[0] = 0xb1;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 3);
|
||
|
||
revbuf[0] = (uint8_t)dat0->data[0];
|
||
revbuf[1] = (uint8_t)dat0->data[1];
|
||
revbuf[2] = (uint8_t)dat0->data[2];
|
||
revbuf[0] &= 0x07;
|
||
|
||
clk = revbuf[0];
|
||
clk <<= 8;
|
||
clk |= revbuf[1];
|
||
clk <<= 8;
|
||
clk |= revbuf[2];
|
||
|
||
debug("[hdmi]HDMI CLK = %d\n", clk);
|
||
|
||
delete dat0;
|
||
}
|
||
|
||
uint8_t lt6911_get_csi_res(uint16_t *p_width, uint16_t *p_height)
|
||
{
|
||
uint8_t buf[2];
|
||
uint8_t revbuf[4];
|
||
uint8_t res_type;
|
||
static uint16_t old_Vactive;
|
||
static uint16_t old_Hactive;
|
||
uint16_t Vactive;
|
||
uint16_t Hactive;
|
||
|
||
if(kvmv_cfg.hdmi_version == 0){
|
||
// LT6911C
|
||
buf[0] = 0xff;
|
||
buf[1] = 0xc2;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
// Vactive
|
||
buf[0] = 0x06;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 2);
|
||
|
||
// Hactive
|
||
buf[0] = 0x38;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat1 = LT6911_i2c.readfrom(LT6911_ADDR, 2);
|
||
|
||
revbuf[0] = (uint8_t)dat0->data[0];
|
||
revbuf[1] = (uint8_t)dat0->data[1];
|
||
revbuf[2] = (uint8_t)dat1->data[0];
|
||
revbuf[3] = (uint8_t)dat1->data[1];
|
||
|
||
delete dat0;
|
||
delete dat1;
|
||
|
||
Vactive = (revbuf[0] << 8)|revbuf[1];
|
||
Hactive = (revbuf[2] << 8)|revbuf[3];
|
||
} else if(kvmv_cfg.hdmi_version == 1) {
|
||
// LT6911UXC
|
||
debug("[hdmi]UXC get csi res\n");
|
||
buf[0] = 0xff;
|
||
buf[1] = 0x85;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
|
||
// Vactive
|
||
buf[0] = 0xF0;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat0 = LT6911_i2c.readfrom(LT6911_ADDR, 2);
|
||
|
||
// Hactive
|
||
buf[0] = 0xEA;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
maix::Bytes *dat1 = LT6911_i2c.readfrom(LT6911_ADDR, 2);
|
||
|
||
revbuf[0] = (uint8_t)dat0->data[0];
|
||
revbuf[1] = (uint8_t)dat0->data[1];
|
||
revbuf[2] = (uint8_t)dat1->data[0];
|
||
revbuf[3] = (uint8_t)dat1->data[1];
|
||
|
||
delete dat0;
|
||
delete dat1;
|
||
|
||
Vactive = (revbuf[0] << 8)|revbuf[1];
|
||
Hactive = (revbuf[2] << 8)|revbuf[3];
|
||
} else {
|
||
return UNKNOWN_RES;
|
||
}
|
||
|
||
res_type = check_res(Hactive, Vactive);
|
||
if(res_type == NORMAL_RES)
|
||
{
|
||
if(old_Hactive != Hactive || old_Vactive != Vactive){
|
||
old_Hactive = Hactive;
|
||
old_Vactive = Vactive;
|
||
// p_kvm_cfg->width = Hactive;
|
||
// p_kvm_cfg->height = Vactive;
|
||
*p_width = Hactive;
|
||
*p_height = Vactive;
|
||
res_type = NEW_RES;
|
||
}
|
||
}
|
||
|
||
switch (res_type)
|
||
{
|
||
case NORMAL_RES:
|
||
printf("[hdmi] get res : %d * %d\n", Hactive, Vactive);
|
||
break;
|
||
case NEW_RES:
|
||
printf("[hdmi] get new res : %d * %d\n", Hactive, Vactive);
|
||
write_res_to_file(Hactive, Vactive);
|
||
break;
|
||
case UNSUPPORT_RES:
|
||
printf("[hdmi] get unsupport res : %d * %d\n", Hactive, Vactive);
|
||
break;
|
||
case UNKNOWN_RES:
|
||
printf("[hdmi] get unknown res : %d * %d\n", Hactive, Vactive);
|
||
break;
|
||
}
|
||
|
||
return res_type;
|
||
}
|
||
|
||
void lt6911_write_reg(uint8_t reg, uint8_t val)
|
||
{
|
||
uint8_t buf[2];
|
||
buf[0] = reg;
|
||
buf[1] = val;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 2);
|
||
}
|
||
|
||
void lt6911_read_reg(uint8_t reg)
|
||
{
|
||
uint8_t buf[16];
|
||
buf[0] = reg;
|
||
LT6911_i2c.writeto(LT6911_ADDR, buf, 1);
|
||
|
||
maix::Bytes *dat = LT6911_i2c.readfrom(LT6911_ADDR, 16);
|
||
|
||
for(int i = 0; i < 16; i++){
|
||
buf[i] = (uint8_t)dat->data[i];
|
||
}
|
||
|
||
delete dat;
|
||
|
||
debug("[hdmi]%3x %3x %3x %3x %3x %3x %3x %3x |%3x %3x %3x %3x %3x %3x %3x %3x \n", \
|
||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], \
|
||
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
|
||
}
|
||
|
||
uint8_t lt6911_read_one_reg(uint8_t reg)
|
||
{
|
||
uint8_t ret;
|
||
ret = reg;
|
||
LT6911_i2c.writeto(LT6911_ADDR, &ret, 1);
|
||
|
||
maix::Bytes *dat = LT6911_i2c.readfrom(LT6911_ADDR, 1);
|
||
|
||
ret = (uint8_t)dat->data[0];
|
||
|
||
delete dat;
|
||
return ret;
|
||
}
|
||
|
||
void lt6911_write_edid(void)
|
||
{
|
||
uint8_t i, j;
|
||
uint8_t buf[2];
|
||
lt6911_enable();
|
||
|
||
// to 90
|
||
lt6911_write_reg(0xff, 0x90);
|
||
buf[0] = lt6911_read_one_reg(0x02);
|
||
buf[0] &= 0xDF;
|
||
lt6911_write_reg(0x02, buf[0]);
|
||
buf[0] |= 0x20;
|
||
lt6911_write_reg(0x02, buf[0]);
|
||
|
||
// to 80
|
||
lt6911_write_reg(0xff, 0x80);
|
||
// wren enable
|
||
lt6911_write_reg(0x5A, 0x86);
|
||
lt6911_write_reg(0x5A, 0x82);
|
||
|
||
for(i = 0; i < 16; i++){
|
||
// 写wren命令(为一个pulse,此时不需要考虑wrrd_mode,spi_paddr[1:0]的值)
|
||
lt6911_write_reg(0x5A, 0x86);
|
||
lt6911_write_reg(0x5A, 0x82);
|
||
|
||
// 配置spi_len[3:0]= 15,可配置,spi内部加1,即配置一次写入16个字节
|
||
lt6911_write_reg(0x5E, 0xEF);
|
||
lt6911_write_reg(0x5A, 0xA2);
|
||
lt6911_write_reg(0x5A, 0x82);
|
||
|
||
lt6911_write_reg(0x58, 0x01);
|
||
|
||
if(i < 8) {
|
||
for(j = 0; j < 16; j++){
|
||
lt6911_write_reg(0x59, NanoKVM_edit[i*16+j]);
|
||
}
|
||
} else {
|
||
for(j = 0; j < 16; j++){
|
||
lt6911_write_reg(0x59, 0x00);
|
||
}
|
||
}
|
||
|
||
// 把fifo数据写到flash(当wrrd_mode = 1,spi_paddr= 2’b10,addr[23:0](地址在写入过程中需要保持不变)准备好的情况下,给一个spi_sta的pulse,就开始写入flash)
|
||
lt6911_write_reg(0x5B, 0x00);
|
||
lt6911_write_reg(0x5C, 0x21);
|
||
lt6911_write_reg(0x5D, i*16);
|
||
lt6911_write_reg(0x5E, 0xE0);
|
||
lt6911_write_reg(0x5A, 0x92);
|
||
lt6911_write_reg(0x5A, 0x82);
|
||
|
||
}
|
||
|
||
lt6911_write_reg(0x5A, 0x8A);
|
||
lt6911_write_reg(0x5A, 0x82);
|
||
|
||
lt6911_disable();
|
||
debug("[hdmi]lt6911_write_edid OK\n");
|
||
}
|
||
|
||
void lt6911_read_edid(void)
|
||
{
|
||
uint8_t i, j;
|
||
lt6911_enable();
|
||
|
||
// to 80
|
||
lt6911_write_reg(0xff, 0x80);
|
||
lt6911_write_reg(0xEE, 0x01);
|
||
|
||
// configure parameter
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
lt6911_write_reg(0x5E, 0xC0);
|
||
lt6911_write_reg(0x58, 0x00);
|
||
lt6911_write_reg(0x59, 0x51);
|
||
lt6911_write_reg(0x5A, 0x90);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
|
||
for(i = 0; i < 16; i++){
|
||
// to 90
|
||
lt6911_write_reg(0xff, 0x90);
|
||
// fifo rst_n
|
||
lt6911_write_reg(0x02, 0xdf);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x02, 0xff);
|
||
|
||
// wren
|
||
// to 80
|
||
lt6911_write_reg(0xff, 0x80);
|
||
// fifo rst_n
|
||
lt6911_write_reg(0x5A, 0x84);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
|
||
// flash to fifo
|
||
lt6911_write_reg(0x5E, 0x6F);
|
||
lt6911_write_reg(0x5A, 0xA0);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
lt6911_write_reg(0x5B, 0x00);
|
||
lt6911_write_reg(0x5C, 0x21);
|
||
lt6911_write_reg(0x5D, 16*i);
|
||
lt6911_write_reg(0x5A, 0x90);
|
||
time::sleep_ms(5);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
lt6911_write_reg(0x58, 0x01);
|
||
|
||
// for(j = 0; j < 16; j++){
|
||
debug("[hdmi] EDID: ");
|
||
lt6911_read_reg(0x5F);
|
||
// }
|
||
time::sleep_ms(10);
|
||
}
|
||
|
||
// wrdi
|
||
lt6911_write_reg(0x5A, 0x88);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
|
||
lt6911_disable();
|
||
debug("[hdmi]lt6911_read_edid OK\n");
|
||
}
|
||
|
||
void lt6911_read_fw(void)
|
||
{
|
||
uint32_t i, j;
|
||
uint8_t buf[3];
|
||
lt6911_enable();
|
||
|
||
// to 80
|
||
lt6911_write_reg(0xff, 0x80);
|
||
lt6911_write_reg(0xEE, 0x01);
|
||
|
||
// configure parameter
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
lt6911_write_reg(0x5E, 0xC0);
|
||
lt6911_write_reg(0x58, 0x00);
|
||
lt6911_write_reg(0x59, 0x51);
|
||
lt6911_write_reg(0x5A, 0x90);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
|
||
for(i = 0x1; i < 50000; i++){
|
||
buf[0] = ((i*16) & 0xFF0000) >> 16;
|
||
buf[1] = ((i*16) & 0xFF00) >> 8;
|
||
buf[2] = ((i*16) & 0xFF);
|
||
// to 90
|
||
lt6911_write_reg(0xff, 0x90);
|
||
// fifo rst_n
|
||
lt6911_write_reg(0x02, 0xdf);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x02, 0xff);
|
||
|
||
// wren
|
||
// to 80
|
||
lt6911_write_reg(0xff, 0x80);
|
||
// fifo rst_n
|
||
lt6911_write_reg(0x5A, 0x84);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
|
||
// flash to fifo
|
||
lt6911_write_reg(0x5E, 0x6F);
|
||
lt6911_write_reg(0x5A, 0xA0);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
lt6911_write_reg(0x5B, buf[0]);
|
||
lt6911_write_reg(0x5C, buf[1]);
|
||
lt6911_write_reg(0x5D, buf[2]);
|
||
lt6911_write_reg(0x5A, 0x90);
|
||
time::sleep_ms(5);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
lt6911_write_reg(0x58, 0x01);
|
||
|
||
// for(j = 0; j < 16; j++){
|
||
debug("[hdmi]REG %6x: ", i*16);
|
||
lt6911_read_reg(0x5F);
|
||
// }
|
||
time::sleep_ms(10);
|
||
}
|
||
|
||
// wrdi
|
||
lt6911_write_reg(0x5A, 0x88);
|
||
time::sleep_ms(1);
|
||
lt6911_write_reg(0x5A, 0x80);
|
||
|
||
lt6911_disable();
|
||
debug("[hdmi]lt6911_read_edid OK\n");
|
||
}
|
||
|
||
// ==============================================================
|
||
|
||
void* watchdog_sf_feed(void * arg)
|
||
{
|
||
while(true)
|
||
{
|
||
if(kvmv_cfg.try_exit_thread == 1)
|
||
break;
|
||
time::sleep_ms(500);
|
||
if (watchdog_sf_is_open()){
|
||
if (chack_ion() == 1){
|
||
debug("[kvmv] Ion memory is full reboot now!\n");
|
||
system("reboot");
|
||
}
|
||
debug("[kvmv] watchdog_sf_feed now!\n");
|
||
vision_update_watchdog();
|
||
}
|
||
}
|
||
}
|
||
|
||
void get_hdmi_version()
|
||
{
|
||
FILE *fp;
|
||
uint8_t RW_Data[2];
|
||
system("/kvmapp/system/init.d/S15kvmhwd get_hdmi_version");
|
||
if(access("/etc/kvm/hdmi_version", F_OK) == 0){
|
||
fp = fopen("/etc/kvm/hdmi_version", "r");
|
||
fread(RW_Data, sizeof(char), 2, fp);
|
||
fclose(fp);
|
||
if(RW_Data[0] == 'u'){
|
||
// 6911uxc
|
||
if(RW_Data[1] == 'e'){
|
||
kvmv_cfg.hdmi_version = 2;
|
||
debug("[hdmi]HDMI-UE exist!\n");
|
||
set_hdmi_mode(1);
|
||
} else if(RW_Data[1] == 'x') {
|
||
kvmv_cfg.hdmi_version = 1;
|
||
debug("[hdmi]HDMI-UX exist!\n");
|
||
} else {
|
||
kvmv_cfg.hdmi_version = 1;
|
||
debug("[hdmi]Incomplete version number, set to 'ux'\n");
|
||
}
|
||
} else {
|
||
// 6911c
|
||
kvmv_cfg.hdmi_version = 0;
|
||
debug("[hdmi]HDMI-C exist!\n");
|
||
}
|
||
RW_Data[0] = 0;
|
||
} else {
|
||
kvmv_cfg.hdmi_version = 0;
|
||
}
|
||
}
|
||
|
||
void* vi_subsystem_detection(void * arg)
|
||
{
|
||
uint64_t __attribute__((unused)) int_time;
|
||
|
||
FILE *fp;
|
||
uint8_t RW_Data[2];
|
||
uint8_t file_size;
|
||
uint8_t tmp8;
|
||
uint8_t rising_times = 0;
|
||
uint8_t falling_times = 0;
|
||
uint8_t cam_need_restart = 0;
|
||
kvmv_cfg.thread_is_running = 1;
|
||
if(access("/proc/lt_int", F_OK) != 0){
|
||
time::sleep_ms(10);
|
||
debug("[hdmi]/proc/lt_int not ok\n");
|
||
}
|
||
|
||
get_hdmi_version();
|
||
|
||
// while(!app::need_exit())
|
||
uint8_t while_count_detect_res = 0;
|
||
while(true)
|
||
{
|
||
if(kvmv_cfg.try_exit_thread == 1)
|
||
break;
|
||
|
||
uint8_t get_new_hdmi_mode = get_hdmi_mode();
|
||
uint8_t try_res;
|
||
uint8_t err_code;
|
||
|
||
switch (kvmv_cfg.hdmi_mode){
|
||
case 0:
|
||
// Switching to Mode 0 requires restarting HDMI (effective only for PCIe version)
|
||
// Handling of automatic detection situations
|
||
if(get_new_hdmi_mode == 1){
|
||
kvmv_cfg.vi_detect_state = 0;
|
||
// reset hdmi_state
|
||
char Cmd[100]={0};
|
||
sprintf(Cmd, "echo 0 > %s", hdmi_state_path);
|
||
system(Cmd);
|
||
// reset hdmi
|
||
kvmv_hdmi_control(0);
|
||
time::sleep_ms(10);
|
||
kvmv_hdmi_control(1);
|
||
}
|
||
// Initialize VI with I2C information after HDMI insertion
|
||
fp = fopen(hdmi_state_path, "r+");
|
||
if(fp != NULL){
|
||
// fseek(fp, 0, SEEK_END);
|
||
// file_size = ftell(fp);
|
||
// fseek(fp, 0, SEEK_SET);
|
||
fread(RW_Data, sizeof(char), 2, fp);
|
||
tmp8 = atoi((char*)RW_Data);
|
||
// debug("[hdmi]UXC tmp8 = %d\n", tmp8);
|
||
if(tmp8 != 0){
|
||
// reset hdmi_state
|
||
fputs("0", fp);
|
||
// count edge ints
|
||
rising_times = tmp8%10; // RISING times
|
||
falling_times = tmp8/10; // FALLING times
|
||
|
||
if(kvmv_cfg.hdmi_stop_flag != 1){
|
||
kvmv_cfg.hdmi_reading_flag = 1;
|
||
if(kvmv_cfg.hdmi_version == 0){
|
||
// LT6911C
|
||
if(rising_times != 0){
|
||
lt6911_enable();
|
||
if(lt6911_get_hdmi_res()){
|
||
// hdmi get res
|
||
debug("[hdmi] C HDMI cable insertion!\n");
|
||
kvmv_cfg.hdmi_cable_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){
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
if(auto_try_res() == 1){
|
||
printf("[hdmi] auto get res\n");
|
||
kvmv_cfg.hdmi_res_err = NORMAL_RES;
|
||
} else {
|
||
// Potential deadlock may occur
|
||
kvmv_cfg.hdmi_res_err = ERROR_RES;
|
||
}
|
||
kvmv_cfg.vi_detect_state = 0;
|
||
}
|
||
} else {
|
||
// HDMI res = 0*0/x*0
|
||
debug("[hdmi] C HDMI cable unplugged!\n");
|
||
kvmv_cfg.hdmi_cable_state = 0;
|
||
}
|
||
lt6911_disable();
|
||
}
|
||
} else if (kvmv_cfg.hdmi_version == 1){
|
||
// LT6911UXC
|
||
debug("[hdmi] UXC int\n");
|
||
if(falling_times != 0){
|
||
debug("[hdmi] UXC int && \n");
|
||
lt6911_enable();
|
||
if(lt6911_get_hdmi_res()){
|
||
// hdmi get res
|
||
debug("[hdmi] UXC HDMI cable insertion!\n");
|
||
kvmv_cfg.hdmi_cable_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){
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
if(auto_try_res() == 1){
|
||
printf("[hdmi] auto get res\n");
|
||
kvmv_cfg.hdmi_res_err = NORMAL_RES;
|
||
} else {
|
||
// Potential deadlock may occur
|
||
kvmv_cfg.hdmi_res_err = ERROR_RES;
|
||
}
|
||
kvmv_cfg.vi_detect_state = 0;
|
||
}
|
||
} else {
|
||
// HDMI res = 0*0/x*0
|
||
debug("[hdmi] UXC HDMI cable unplugged!\n");
|
||
kvmv_cfg.hdmi_cable_state = 0;
|
||
}
|
||
lt6911_disable();
|
||
}
|
||
} else {
|
||
debug("[hdmi] Chip not supported for reading \n");
|
||
}
|
||
kvmv_cfg.hdmi_reading_flag = 0;
|
||
}
|
||
}
|
||
fclose(fp);
|
||
}
|
||
break;
|
||
/* Mode 1 & 2 will disable API access to the image during detection,
|
||
and will output -4: Modifying image resolution, please wait.*/
|
||
case 1: // Automatically trying common resolutions
|
||
/* kvmv_cfg.vi_detect_state :
|
||
* 0: HDMI standard mode, detection program does not interfere with the camera
|
||
* 1: Preparing / Testing in progress
|
||
* 2: Test completed: Suitable resolution found,
|
||
*/
|
||
|
||
if(get_new_hdmi_mode == 1){
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
}
|
||
if(kvmv_cfg.vi_detect_state == 1){
|
||
|
||
try_res = auto_try_res();
|
||
if (try_res == 1) {
|
||
kvmv_cfg.vi_detect_state = 2;
|
||
} else if (try_res == 0) {
|
||
printf("[kvmv] Suitable resolution not found, switching to manual input mode automatically\n");
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
set_hdmi_mode(2);
|
||
} else if (try_res == 2) {
|
||
// Cannot obtain HDMI input / No signal on HDMI
|
||
}
|
||
} else if (kvmv_cfg.vi_detect_state == 2){
|
||
// Low-frequency detection of HDMI status, no log output
|
||
printf("[kvmv] kvmv_cfg.vi_detect_state == 2\n");
|
||
err_code = get_vi_state();
|
||
if (err_code != 1) {
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
}
|
||
time::sleep_ms(1000);
|
||
} else {
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
}
|
||
break;
|
||
case 2:
|
||
// Manually initialize VI.
|
||
while_count_detect_res = (while_count_detect_res + 1)%100;
|
||
if(while_count_detect_res == 1){
|
||
if (kvmv_cfg.vi_detect_state == 1){
|
||
// detect_res
|
||
if (get_manual_resolution()) {
|
||
debug("[kvmv] restart cam...\n");
|
||
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
|
||
}
|
||
|
||
// dbg info
|
||
err_code = get_vi_state();
|
||
switch(err_code){
|
||
case 0:
|
||
debug("[kvmv] VI not init\n");
|
||
break;
|
||
case 1:
|
||
debug("[kvmv] HDMI and CSI status are normal\n");
|
||
kvmv_cfg.vi_detect_state = 2;
|
||
break;
|
||
case 2:
|
||
debug("[kvmv] HDMI abnormal\n");
|
||
break;
|
||
case 3:
|
||
debug("[kvmv] CSI abnormal: width too small\n");
|
||
break;
|
||
case 4:
|
||
debug("[kvmv] CSI abnormal: width too large\n");
|
||
break;
|
||
case 5:
|
||
debug("[kvmv] CSI abnormal: height too small\n");
|
||
break;
|
||
case 6:
|
||
debug("[kvmv] CSI abnormal: height too large\n");
|
||
break;
|
||
case 7:
|
||
debug("[kvmv] CSI abnormal: Unknown reason\n");
|
||
break;
|
||
}
|
||
} else if (kvmv_cfg.vi_detect_state == 2){
|
||
// detection of HDMI status, no log output
|
||
err_code = get_vi_state();
|
||
if (err_code != 1) kvmv_cfg.vi_detect_state = 1;
|
||
} else {
|
||
kvmv_cfg.vi_detect_state = 1;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
debug("Non-existent hdmi state = %d\n", kvmv_cfg.hdmi_mode);
|
||
break;
|
||
}
|
||
|
||
time::sleep_ms(10);
|
||
}
|
||
kvmv_cfg.thread_is_running = 0;
|
||
}
|
||
|
||
int sync_vi_res()
|
||
{
|
||
int res = 0;
|
||
uint8_t RW_Data[35];
|
||
FILE *fp;
|
||
int file_size;
|
||
uint16_t tmp16;
|
||
|
||
// vi_width:
|
||
if (access(vi_width_path, F_OK) != 0){
|
||
kvmv_cfg.vi_width = default_vi_width;
|
||
kvmv_cfg.vi_height = default_vi_height;
|
||
res = -1;
|
||
return res;
|
||
} else {
|
||
fp = fopen(vi_width_path, "r");
|
||
fseek(fp, 0, SEEK_END);
|
||
file_size = ftell(fp);
|
||
fseek(fp, 0, SEEK_SET);
|
||
fread(RW_Data, sizeof(char), file_size, fp);
|
||
fclose(fp);
|
||
RW_Data[file_size] = 0;
|
||
tmp16 = atoi((char*)RW_Data);
|
||
if(tmp16 != kvmv_cfg.vi_width){
|
||
kvmv_cfg.vi_width = tmp16;
|
||
debug("[hdmi] Get new HDMI width = %d\r\n", kvmv_cfg.vi_width);
|
||
res = 1;
|
||
}
|
||
}
|
||
// vi_height:
|
||
if (access(vi_height_path, F_OK) != 0){
|
||
kvmv_cfg.vi_height = default_vi_height;
|
||
res = -1;
|
||
return res;
|
||
} else {
|
||
fp = fopen(vi_height_path, "r");
|
||
fseek(fp, 0, SEEK_END);
|
||
file_size = ftell(fp);
|
||
fseek(fp, 0, SEEK_SET);
|
||
fread(RW_Data, sizeof(char), file_size, fp);
|
||
fclose(fp);
|
||
RW_Data[file_size] = 0;
|
||
tmp16 = atoi((char*)RW_Data);
|
||
if(tmp16 != kvmv_cfg.vi_height){
|
||
kvmv_cfg.vi_height = tmp16;
|
||
debug("[hdmi] Get new HDMI height = %d\r\n", kvmv_cfg.vi_height);
|
||
res = 1;
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
|
||
void jpg_dump(kvmv_data_t* dump_to, image::Image *raw)
|
||
{
|
||
dump_to->p_img_data = (uint8_t *)malloc(raw->data_size());
|
||
dump_to->img_data_size = raw->data_size();
|
||
dump_to->img_data_type = VENC_MJPEG;
|
||
memcpy(dump_to->p_img_data, (uint8_t *)raw->data(), raw->data_size());
|
||
}
|
||
|
||
uint8_t kvmvenc_gop = default_h264_gop;
|
||
kvm_venc_t kvm_venc;
|
||
mmf_venc_cfg_t cfg;
|
||
void init_venc_h264(uint16_t _width, uint16_t _height, uint16_t _qlty)
|
||
{
|
||
cfg.type = 2; //1, h265, 2, h264
|
||
cfg.w = _width;
|
||
cfg.h = _height;
|
||
cfg.fmt = mmf_invert_format_to_mmf(image::Format::FMT_YVU420SP);
|
||
cfg.jpg_quality = 0; // unused
|
||
cfg.gop = kvmvenc_gop;
|
||
cfg.intput_fps = 60;
|
||
cfg.output_fps = 60;
|
||
cfg.bitrate = _qlty; // 码率
|
||
|
||
kvm_venc.mmf_venc_chn = default_venc_chn;
|
||
kvm_venc.enc_h264_init = 0;
|
||
kvm_venc.kvm_venc_cfg = cfg;
|
||
|
||
// if(mmf_vdec_is_used(kvm_venc.mmf_venc_chn)){
|
||
mmf_del_venc_channel(kvm_venc.mmf_venc_chn);
|
||
// }
|
||
if (0 != mmf_add_venc_channel(kvm_venc.mmf_venc_chn, &kvm_venc.kvm_venc_cfg)) {
|
||
err::check_raise(err::ERR_RUNTIME, "mmf venc init failed!");
|
||
}
|
||
kvm_venc.enc_h264_init = 1;
|
||
|
||
// init_kvm_h264_stream(&kvm_h264_stream, mmf_stream_buf);
|
||
// init_h264_stream_struct(&kvm_h264_stream);
|
||
}
|
||
|
||
int h264_stream_dump(kvmv_data_t* dump_to, mmf_stream_t* dump_from)
|
||
{
|
||
static int8_t I_Frame_index = -1;
|
||
// debug("[kvmv]dump_from->count = %d\n", dump_from->count);
|
||
if (dump_from->count == 3) {
|
||
// debug("[kvmv]dump I-Frame\r\n");
|
||
if(kvmv_SPS_buffer.p_img_data != NULL || kvmv_PPS_buffer.p_img_data != NULL)
|
||
return IMG_BUFFER_FULL;
|
||
|
||
kvmv_SPS_buffer.p_img_data = (uint8_t *)malloc(dump_from->data_size[0]);
|
||
kvmv_PPS_buffer.p_img_data = (uint8_t *)malloc(dump_from->data_size[1]);
|
||
dump_to->p_img_data = (uint8_t *)malloc(dump_from->data_size[2]);
|
||
|
||
kvmv_SPS_buffer.img_data_size = dump_from->data_size[0];
|
||
kvmv_PPS_buffer.img_data_size = dump_from->data_size[1];
|
||
dump_to->img_data_size = dump_from->data_size[2];
|
||
|
||
kvmv_SPS_buffer.img_data_type = IMG_H264_TYPE_SPS;
|
||
kvmv_PPS_buffer.img_data_type = IMG_H264_TYPE_PPS;
|
||
dump_to->img_data_type = IMG_H264_TYPE_IF;
|
||
|
||
memcpy(kvmv_SPS_buffer.p_img_data, dump_from->data[0], dump_from->data_size[0]);
|
||
memcpy(kvmv_PPS_buffer.p_img_data, dump_from->data[1], dump_from->data_size[1]);
|
||
memcpy(dump_to->p_img_data, dump_from->data[2], dump_from->data_size[2]);
|
||
|
||
debug("[kvmv]SPS size = %d\n", kvmv_SPS_buffer.img_data_size);
|
||
debug("[kvmv]PPS size = %d\n", kvmv_PPS_buffer.img_data_size);
|
||
debug("[kvmv]I-Frame size = %d\n", dump_to->img_data_size);
|
||
|
||
return IMG_H264_TYPE_IF;
|
||
|
||
} else if (dump_from->count == 1) {
|
||
// debug("[kvmv]dump P-Frame\r\n");
|
||
I_Frame_index = -1;
|
||
dump_to->p_img_data = (uint8_t *)malloc(dump_from->data_size[0]);
|
||
dump_to->img_data_size = dump_from->data_size[0];
|
||
dump_to->img_data_type = IMG_H264_TYPE_PF;
|
||
memcpy(dump_to->p_img_data, dump_from->data[0], dump_from->data_size[0]);
|
||
return IMG_H264_TYPE_PF;
|
||
} else {
|
||
debug("[kvmv]venc error!\r\n");
|
||
return IMG_VENC_ERROR;
|
||
}
|
||
}
|
||
|
||
void set_h264_gop(uint8_t _gop)
|
||
{
|
||
kvm_venc.enc_h264_init = 0; // call
|
||
kvmvenc_gop = maxmin_data(100, 10, (int)_gop);
|
||
}
|
||
|
||
// uint8_t get_h264_gop(void)
|
||
// {
|
||
// return kvm_venc.kvm_venc_cfg.gop;
|
||
// }
|
||
|
||
// uint8_t get_hdmi_width(void)
|
||
// {
|
||
// return kvm_venc.kvm_venc_cfg.gop;
|
||
// }
|
||
|
||
int8_t raw_to_h264(image::Image *raw, kvmv_data_t* ret_stream, uint16_t _qlty)
|
||
{
|
||
uint64_t __attribute__((unused)) start_time;
|
||
uint64_t __attribute__((unused)) frame_time;
|
||
int8_t ret = 0;
|
||
static uint8_t P_Frame_Count = 0;
|
||
// start_time = time::time_ms();
|
||
// log::info("getimg: %d \r\n", (int)(time::time_ms()));
|
||
mmf_stream_t _stream = {0};
|
||
if(kvm_venc.enc_h264_init != 1 || raw->width() != kvm_venc.kvm_venc_cfg.w || raw->height() != kvm_venc.kvm_venc_cfg.h || _qlty != kvm_venc.kvm_venc_cfg.bitrate){
|
||
debug("[kvmv]init_venc_h264 enc_h264_init = %d; raw->width() = %d | %d raw->height() = %d | %d \n",
|
||
kvm_venc.enc_h264_init,
|
||
raw->width(), kvm_venc.kvm_venc_cfg.w,
|
||
raw->height(), kvm_venc.kvm_venc_cfg.h);
|
||
|
||
init_venc_h264(raw->width(), raw->height(), _qlty);
|
||
debug("[kvmv]init_venc_h264 finish enc_h264_init = %d; raw->width() = %d | %d raw->height() = %d | %d \n",
|
||
kvm_venc.enc_h264_init,
|
||
raw->width(), kvm_venc.kvm_venc_cfg.w,
|
||
raw->height(), kvm_venc.kvm_venc_cfg.h);
|
||
// if(kvm_venc.enc_h264_init == 1){
|
||
// init_venc_h264(raw->width(), raw->height(), _qlty);
|
||
// } else {
|
||
// init_venc_h264(default_vpss_width, default_vpss_height, default_h264_qlty);
|
||
// }
|
||
}
|
||
// log::info("init(): %d \r\n", (int)(time::time_ms() - start_time));
|
||
if (mmf_venc_push(kvm_venc.mmf_venc_chn, (uint8_t *)raw->data(), raw->width(), raw->height(), mmf_invert_format_to_mmf(raw->format()))) {
|
||
mmf_del_venc_channel(kvm_venc.mmf_venc_chn);
|
||
kvm_venc.enc_h264_init = 0;
|
||
// rtmp->unlock();
|
||
debug("[kvmv]mmf venc push failed!\n");
|
||
// err::check_raise(err::ERR_RUNTIME, "mmf venc push failed!\r\n");
|
||
return -1;
|
||
}
|
||
// log::info("push(): %d \r\n", (int)(time::time_ms() - start_time));
|
||
if (mmf_venc_pop(kvm_venc.mmf_venc_chn, &_stream)) {
|
||
// log::error("mmf_venc_pop failed\n");
|
||
mmf_venc_free(kvm_venc.mmf_venc_chn);
|
||
mmf_del_venc_channel(kvm_venc.mmf_venc_chn);
|
||
kvm_venc.enc_h264_init = 0;
|
||
debug("[kvmv]mmf venc push failed!\n");
|
||
// rtmp->unlock();
|
||
return -1;
|
||
}
|
||
// log::info("pop(): %d \r\n", (int)(time::time_ms() - start_time));
|
||
ret = h264_stream_dump(ret_stream, &_stream);
|
||
mmf_venc_free(kvm_venc.mmf_venc_chn);
|
||
// log::info("dump(): %d \r\n", (int)(time::time_ms() - start_time));
|
||
|
||
// debug("[kvmv]_stream.data[0][4] = %d;\n", _stream.data[0][4]);
|
||
debug("[kvmv]Frame size = %d;\n", ret_stream->img_data_size);
|
||
|
||
if(ret == IMG_H264_TYPE_IF){
|
||
debug("[kvmv]================ GOP = %d ================\n", kvm_venc.kvm_venc_cfg.gop);
|
||
debug("[kvmv]SPS; PPS; I-Frame, I-Frame size = %d\n", ret_stream->img_data_size);
|
||
P_Frame_Count = 0;
|
||
|
||
} else if(ret == IMG_H264_TYPE_PF){
|
||
debug("[kvmv]P-Frame size = %d, P-count = %d\n", ret_stream->img_data_size, P_Frame_Count);
|
||
P_Frame_Count++;
|
||
}
|
||
debug("[kvmv]dump ret = %d\n", ret);
|
||
return ret;
|
||
}
|
||
|
||
void kvmv_init(uint8_t _debug_info_en)
|
||
{
|
||
pthread_t thread;
|
||
pthread_mutex_init(&vi_mutex, NULL);
|
||
if(_debug_info_en == 0) debug_en = 0;
|
||
else debug_en = 1;
|
||
|
||
// debug("[kvmv]kvmv_init - 1\r\n");
|
||
|
||
cam->hmirror(1);
|
||
cam->vflip(1);
|
||
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
|
||
for(int i = 0; i < kvmv_data_buffer_size; i++){
|
||
kvmv_data_buffer[i].p_img_data = NULL;
|
||
}
|
||
kvmv_SPS_buffer.p_img_data = NULL;
|
||
kvmv_PPS_buffer.p_img_data = NULL;
|
||
|
||
kvmv_cfg.try_exit_thread = 0;
|
||
// debug("[kvmv]kvmv_init - 2\r\n");
|
||
|
||
if(kvmv_cfg.thread_is_running == 1){
|
||
debug("[kvmv]thread is running!\r\n");
|
||
} else {
|
||
if (0 != pthread_create(&thread, NULL, vi_subsystem_detection, NULL)) {
|
||
debug("[kvmv]create vi_subsystem_detection thread failed!\r\n");
|
||
// return -1;
|
||
}
|
||
|
||
if (0 != pthread_create(&thread, NULL, watchdog_sf_feed, NULL)) {
|
||
debug("[kvmv]create watchdog_sf_feed thread failed!\r\n");
|
||
// return -1;
|
||
}
|
||
}
|
||
// debug("[kvmv]kvmv_init - 3\r\n");
|
||
}
|
||
|
||
uint8_t check_kvmv(uint8_t _try_num)
|
||
{
|
||
if(kvmv_cfg.hdmi_cable_state == 0){
|
||
debug("[kvmv]HDMI Cable not exist!\n");
|
||
return 0;
|
||
}
|
||
if(_try_num >= KVMV_MAX_TRY_NUM){
|
||
debug("[kvmv]try_num >= KVMV_MAX_TRY_NUM!\n");
|
||
return 0;
|
||
}
|
||
// if(sync_vi_res() != 0){
|
||
if(kvmv_cfg.reopen_cam_flag == 1){
|
||
// vi size changed
|
||
kvmv_cfg.reopen_cam_flag = 0;
|
||
// cam->open(kvmv_cfg.vpss_width, kvmv_cfg.vpss_height, image::FMT_YVU420SP, 3);
|
||
cam->restart(default_vpss_width, default_vpss_height, image::FMT_YVU420SP);
|
||
debug("[kvmv]vi size changed, try again\n");
|
||
return 1;
|
||
}
|
||
debug("[kvmv]just try again\n");
|
||
return 1;
|
||
}
|
||
|
||
void set_venc_auto_recyc(uint8_t _enable)
|
||
{
|
||
if(_enable) kvmv_cfg.venc_auto_recyc = 1;
|
||
else kvmv_cfg.venc_auto_recyc = 0;
|
||
}
|
||
|
||
/**********************************************************************************
|
||
* @name kvmv_read_img
|
||
* @author Sipeed BuGu
|
||
* @date 2024/10/25
|
||
* @version R1.0
|
||
* @brief Acquire the encoded image with auto init
|
||
* @param _width @input: Output image width
|
||
* @param _height @input: Output image height
|
||
* @param _type @input: Encode type
|
||
* @param _qlty @input: MJPEG: (50-100) | H264: (500-10000)
|
||
* @param _pp_kvm_data @output: Encode data
|
||
* @param _p_kvmv_data_size @output: Encode data size
|
||
* @return
|
||
-7: HDMI INPUT RES ERROR
|
||
-6: Unsupported resolution, please modify it in the host settings.
|
||
-5: Retrieving image, please wait
|
||
-4: Modifying image resolution, please wait
|
||
-3: img buffer full
|
||
-2: VENC Errorl
|
||
-1: No images were acquired
|
||
0: Acquire MJPEG encoded images
|
||
1: Acquire H264 encoded images(SPS)
|
||
2: Acquire H264 encoded images(PPS)
|
||
3: Acquire H264 encoded images(I)
|
||
4: Acquire H264 encoded images(P)
|
||
**********************************************************************************/
|
||
int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _qlty, uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size)
|
||
{
|
||
// uint64_t __attribute__((unused)) start_time = time::time_ms();
|
||
debug("[kvmv]kvmv_read_img type = %d...\n", _type);
|
||
struct timespec ts;
|
||
clock_gettime(CLOCK_REALTIME, &ts);
|
||
ts.tv_sec += 1;
|
||
// pthread_mutex_lock(&vi_mutex); // Add lock
|
||
int mutex_res = pthread_mutex_timedlock(&vi_mutex, &ts);
|
||
if(mutex_res != 0){
|
||
return -5;
|
||
}
|
||
if (kvmv_cfg.hdmi_res_err == ERROR_RES){
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return -7;
|
||
}
|
||
if (kvmv_cfg.hdmi_res_type == UNSUPPORT_RES){
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return -6;
|
||
}
|
||
if (kvmv_cfg.vi_detect_state == 1){
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return -4;
|
||
}
|
||
uint8_t try_num = 0;
|
||
do {
|
||
if(kvmv_cfg.vpss_width != _width || kvmv_cfg.vpss_height != _height){
|
||
if(_width == 0 || _height == 0){
|
||
// Follow the HDMI output
|
||
kvmv_cfg.vpss_width = kvmv_cfg.vi_width;
|
||
kvmv_cfg.vpss_height = kvmv_cfg.vi_height;
|
||
|
||
if(kvmv_cfg.Auto_res == 0){
|
||
kvmv_cfg.Auto_res = 1;
|
||
cam->set_resolution(kvmv_cfg.vpss_width, kvmv_cfg.vpss_height);
|
||
kvmv_cfg.reinit_flag = 1;
|
||
}
|
||
|
||
} else {
|
||
kvmv_cfg.Auto_res = 0;
|
||
// Set the output
|
||
kvmv_cfg.vpss_width = _width;
|
||
kvmv_cfg.vpss_height = _height;
|
||
|
||
cam->set_resolution(kvmv_cfg.vpss_width, kvmv_cfg.vpss_height);
|
||
kvmv_cfg.reinit_flag = 1;
|
||
}
|
||
}
|
||
|
||
//
|
||
if (kvmv_cfg.reinit_flag == 1) {
|
||
cam->hmirror(1);
|
||
cam->vflip(1);
|
||
kvmv_cfg.reinit_flag = 0;
|
||
}
|
||
// debug("[kvmv]befor read img: %d \r\n", (int)(time::time_ms() - start_time));
|
||
image::Image *img = cam->read();
|
||
// debug("[kvmv]read img: %d \r\n", (int)(time::time_ms() - start_time));
|
||
if(img != NULL){
|
||
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;
|
||
debug("[kvmv]can`t get img...\n");
|
||
continue;
|
||
// pthread_mutex_unlock(&vi_mutex);
|
||
// return IMG_NOT_EXIST;
|
||
}
|
||
// debug("[kvmv]cheak img null?: %d \r\n", (int)(time::time_ms() - start_time));
|
||
|
||
// img exist
|
||
// Encode
|
||
if(kvmv_cfg.venc_type == VENC_MJPEG && kvmv_cfg.venc_type != _type){
|
||
if(kvmv_cfg.venc_auto_recyc == 1){
|
||
mmf_enc_jpg_deinit(0);
|
||
}
|
||
kvm_venc.enc_h264_init = 1;
|
||
}
|
||
if(kvmv_cfg.venc_type == VENC_H264 && kvmv_cfg.venc_type != _type){
|
||
if(kvmv_cfg.venc_auto_recyc == 1){
|
||
mmf_del_venc_channel(kvm_venc.mmf_venc_chn);
|
||
}
|
||
kvm_venc.enc_h264_init = 0;
|
||
}
|
||
|
||
kvmv_cfg.venc_type = _type;
|
||
|
||
if(kvmv_cfg.venc_type == VENC_MJPEG){
|
||
image::Image *jpg = img->to_jpeg(maxmin_data(99, 51, (int)_qlty));
|
||
kvmv_data_t* p_kvmv_data = get_save_buffer();
|
||
if(p_kvmv_data == NULL){
|
||
// buffer full
|
||
delete jpg;
|
||
delete img;
|
||
debug("[kvmv]jpg buffer full\n");
|
||
*_pp_kvm_data = NULL;
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return IMG_BUFFER_FULL;
|
||
}
|
||
jpg_dump(p_kvmv_data, jpg);
|
||
delete jpg;
|
||
delete img;
|
||
*_pp_kvm_data = p_kvmv_data->p_img_data;
|
||
*_p_kvmv_data_size = p_kvmv_data->img_data_size;
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return IMG_MJPEG_TYPE;
|
||
} else if (kvmv_cfg.venc_type == VENC_H264){
|
||
int ret;
|
||
kvmv_data_t* p_kvmv_data = get_save_buffer();
|
||
if(p_kvmv_data == NULL){
|
||
// buffer full
|
||
delete img;
|
||
*_pp_kvm_data = NULL;
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return IMG_BUFFER_FULL;
|
||
}
|
||
// debug("[kvmv]get_save_buffer: %d \r\n", (int)(time::time_ms() - start_time));
|
||
ret = raw_to_h264(img, p_kvmv_data, maxmin_data(10000, 500, (int)_qlty));
|
||
// debug("[kvmv]venc raw_to_h264: %d \r\n", (int)(time::time_ms() - start_time));
|
||
delete img;
|
||
*_pp_kvm_data = p_kvmv_data->p_img_data;
|
||
*_p_kvmv_data_size = p_kvmv_data->img_data_size;
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return ret;
|
||
}
|
||
} while (check_kvmv(try_num++));
|
||
// debug("[kvmv]return: %d \r\n", (int)(time::time_ms() - start_time));
|
||
*_pp_kvm_data = NULL;
|
||
pthread_mutex_unlock(&vi_mutex);
|
||
return IMG_NOT_EXIST;
|
||
}
|
||
|
||
int kvmv_get_sps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size)
|
||
{
|
||
if(kvmv_SPS_buffer.p_img_data == NULL){
|
||
return IMG_NOT_EXIST;
|
||
} else {
|
||
*_pp_kvm_data = kvmv_SPS_buffer.p_img_data;
|
||
*_p_kvmv_data_size = kvmv_SPS_buffer.img_data_size;
|
||
return IMG_H264_TYPE_SPS;
|
||
}
|
||
return IMG_NOT_EXIST;
|
||
}
|
||
|
||
int kvmv_get_pps_frame(uint8_t** _pp_kvm_data, uint32_t* _p_kvmv_data_size)
|
||
{
|
||
if(kvmv_PPS_buffer.p_img_data == NULL){
|
||
return IMG_NOT_EXIST;
|
||
} else {
|
||
*_pp_kvm_data = kvmv_PPS_buffer.p_img_data;
|
||
*_p_kvmv_data_size = kvmv_PPS_buffer.img_data_size;
|
||
return IMG_H264_TYPE_PPS;
|
||
}
|
||
return IMG_NOT_EXIST;
|
||
}
|
||
|
||
int free_kvmv_data(uint8_t ** _pp_kvm_data)
|
||
{
|
||
// debug("[kvmv]free_kvmv_data - 1\r\n");
|
||
for(int i = 0; i < kvmv_data_buffer_size; i++){
|
||
if(*_pp_kvm_data == kvmv_data_buffer[i].p_img_data){
|
||
// debug("[kvmv]free buffer : %d\n", *_pp_kvm_data);
|
||
if (*_pp_kvm_data != NULL){
|
||
// debug("[kvmv]free_kvmv_data - 2\r\n");
|
||
free(*_pp_kvm_data);
|
||
// debug("[kvmv]free_kvmv_data - 3\r\n");
|
||
kvmv_data_buffer[i].p_img_data = NULL;
|
||
uint8_t _type = kvmv_data_buffer[i].img_data_type;
|
||
if(_type == IMG_H264_TYPE_IF){
|
||
free(kvmv_SPS_buffer.p_img_data);
|
||
free(kvmv_PPS_buffer.p_img_data);
|
||
kvmv_SPS_buffer.p_img_data = NULL;
|
||
kvmv_PPS_buffer.p_img_data = NULL;
|
||
}
|
||
return _type;
|
||
} else {
|
||
return IMG_NOT_EXIST;
|
||
}
|
||
}
|
||
}
|
||
return IMG_NOT_EXIST;
|
||
}
|
||
|
||
void free_all_kvmv_data()
|
||
{
|
||
for(int i = 0; i <= kvmv_data_buffer_size; i++){
|
||
if(kvmv_data_buffer[i].p_img_data != NULL){
|
||
free(kvmv_data_buffer[i].p_img_data);
|
||
kvmv_data_buffer[i].p_img_data = NULL;
|
||
}
|
||
}
|
||
if(kvmv_SPS_buffer.p_img_data != NULL) free(kvmv_SPS_buffer.p_img_data);
|
||
if(kvmv_PPS_buffer.p_img_data != NULL) free(kvmv_PPS_buffer.p_img_data);
|
||
kvmv_SPS_buffer.p_img_data = NULL;
|
||
kvmv_PPS_buffer.p_img_data = NULL;
|
||
}
|
||
|
||
void kvmv_deinit()
|
||
{
|
||
pthread_mutex_destroy(&vi_mutex);
|
||
kvmv_cfg.try_exit_thread = 1;
|
||
cam->close();
|
||
mmf_deinit();
|
||
free_all_kvmv_data();
|
||
}
|
||
|
||
uint8_t kvmv_hdmi_control(uint8_t _en)
|
||
{
|
||
if(kvmv_cfg.hw_version == 0){
|
||
|
||
FILE *fp;
|
||
uint8_t RW_Data[2];
|
||
if(access("/etc/kvm/hw", F_OK) == 0){
|
||
fp = fopen("/etc/kvm/hw", "r");
|
||
fread(RW_Data, sizeof(char), 1, fp);
|
||
fclose(fp);
|
||
switch(RW_Data[0]){
|
||
case 'a':
|
||
case 'b':
|
||
case 'p':
|
||
kvmv_cfg.hw_version = RW_Data[0];
|
||
break;
|
||
default :
|
||
kvmv_cfg.hw_version = 'a';
|
||
}
|
||
}
|
||
}
|
||
if(kvmv_cfg.hw_version != 'p'){
|
||
debug("[kvmv]Hardware not support!\n");
|
||
return -1;
|
||
}
|
||
if(access("/sys/class/gpio/gpio451/value", F_OK) != 0){
|
||
system("echo 451 > /sys/class/gpio/export");
|
||
system("echo out > /sys/class/gpio/gpio451/direction");
|
||
}
|
||
if(_en == 0){
|
||
kvmv_cfg.hdmi_stop_flag = 1;
|
||
while(kvmv_cfg.hdmi_reading_flag == 1) time::sleep_ms(10);
|
||
system("echo 0 > /sys/class/gpio/gpio451/value");
|
||
return 0;
|
||
} else {
|
||
kvmv_cfg.hdmi_stop_flag = 0;
|
||
system("echo 1 > /sys/class/gpio/gpio451/value");
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
|
||
/*
|
||
todo list:
|
||
- [x] 添加芯片型号
|
||
- [-] 写一个不支持的分辨率列表,如果不支持就发出错误提醒,不输出/不获取图像
|
||
- mode0:
|
||
- 获取到支持的分辨率:直接初始化
|
||
- 不支持的分辨率:输出对应错误
|
||
- 不在列表的分辨率:自动进入模式1
|
||
- mode1:
|
||
- 根据/proc/cvitek
|
||
- [ ] 写一个文档说明获取USB HDMI ETH WiFi的状态
|
||
- [ ] mode0:添加功能,获取到一个错误的res自动切换为探索模式
|
||
- [ ] add mode3:仅i2c获取模式
|
||
- [ ] 创建守护server进程
|
||
- [ ] mDNS默认配置问题
|
||
- [ ] 关于看门狗:kvm_vision和kvm_system相互在/tmp里狗叫,同时vision中添加检测是否read卡死,system中检测ion是否够用,否则重启
|
||
VI_SDK_IOC_S_CTRL - vi_sdk_enable_chn NG, No buffer space available ion是否够用也放在vision里吧,前面是log
|
||
|
||
*/ |