Compare commits

...

5 Commits

Author SHA1 Message Date
ytx@queuingsystem.cn 575fe52b9a feat(database): 为Camera结构体添加Enabled字段并实现兼容查询
refactor(web): 增强摄像头管理表单和卡片显示功能

移除Java集成示例代码,专注于Go实现
```

这个提交消息:
1. 使用中文描述变更内容
2. 分为三个主要部分:
   - 数据库功能增强:添加Enabled字段并实现兼容查询
   - 前端重构:改进摄像头管理界面
   - 清理:移除不再需要的Java示例代码
3. 遵循了类型+简要描述的格式
4. 对数据库变更使用了feat类型,对界面改进使用了refactor类型
5. 保持了简洁性,同时涵盖了主要变更点
6 months ago
karlkyo 1a617370d6 feat(数据库): 增强数据库集成功能并改进空值处理
- 在storageConfig.go中添加数据库启用状态同步逻辑
- 修改Camera结构体字段为sql.Null类型以更好处理数据库空值
- 新增DatabaseGetCameras和DatabaseGetCamera方法用于摄像头查询
- 在apiHTTPCamera.go中添加空值处理辅助函数
- 重构Java集成API以使用新的数据库结构和空值处理
- 更新config.json中的数据库凭证
6 months ago
ytx@queuingsystem.cn 6a473d0732 feat(integration): 添加Java项目集成支持
1. 新增Java集成API端点(/api/java/*)用于摄像头和流管理
2. 实现摄像头管理前端组件和降级显示逻辑
3. 添加Java客户端示例代码和集成文档
4. 更新数据库配置使用生产环境参数

新增Java集成功能使Java项目可以通过REST API与RTSPtoWeb服务交互,包括:
- 摄像头列表/详情查询
- 视频流管理
- 系统状态监控
- 播放URL生成

同时提供完整的集成文档(JAVA_INTEGRATION.md)和API演示页面,便于开发者快速集成。
6 months ago
karlkyo bbccd98711 feat(API): 新增按单位代码获取摄像头列表接口
新增/cameras/unit/:unitcode接口,支持根据单位代码过滤摄像头列表
适配qsc_camera表结构调整数据库模型和API响应格式
添加API使用文档说明各接口调用方式
6 months ago
ytx@queuingsystem.cn 2a37fbf7be feat(数据库): 添加MySQL和SQL Server数据库支持并实现摄像头管理功能
实现数据库集成功能,支持MySQL和SQL Server作为后端存储。主要变更包括:
1. 新增数据库配置结构和初始化逻辑
2. 添加摄像头管理API接口和Web页面
3. 实现流配置与数据库的同步机制
4. 新增数据库初始化SQL脚本和文档
5. 优化日志系统和启动流程

同时更新了前端界面,完成中文本地化适配,并添加了相关API文档。
7 months ago

@ -0,0 +1,23 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
},
{
"name": "Launch Test",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${fileDirname}"
}
]
}

@ -0,0 +1,371 @@
# 摄像头管理 API 使用说明
本文档说明如何使用摄像头管理相关的 REST API 接口。
## 基础配置
### 服务器地址
```
http://localhost:8083 # 默认地址,根据实际配置调整
```
### 认证
如果启用了 HTTP 认证,需要在请求头中添加 Basic Auth
```
Authorization: Basic <base64(username:password)>
```
## API 接口列表
### 1. 获取所有摄像头列表
**请求方式:** `GET`
**请求路径:** `/cameras`
**描述:** 获取所有未删除的摄像头列表
**响应示例:**
```json
{
"cameras": [
{
"camera_id": 1,
"ip": "192.168.1.100",
"port": 554,
"username": "admin",
"password": "password",
"url": "rtsp://admin:password@192.168.1.100:554/stream1",
"camera_produce": "海康威视",
"camera_name": "前门摄像头",
"device_type": "网络摄像头",
"unit_code": "UNIT001",
"nvr_produce": "海康威视",
"nvr_path": "/stream1",
"play_back": "支持",
"del_flag": "0",
"create_by": "admin",
"create_time": "2024-01-01T10:00:00Z",
"update_by": "admin",
"update_time": "2024-01-01T10:00:00Z",
"user_id": 1,
"dept_id": 1,
"stream_status": "online"
}
],
"total": 1
}
```
### 2. 根据单位代码获取摄像头列表
**请求方式:** `GET`
**请求路径:** `/cameras/unit/{unitcode}`
**描述:** 根据单位代码获取对应的摄像头列表
**路径参数:**
- `unitcode`: 单位代码(必填)
**响应示例:**
```json
{
"cameras": [
{
"camera_id": 1,
"ip": "192.168.1.100",
"port": 554,
"username": "admin",
"password": "password",
"url": "rtsp://admin:password@192.168.1.100:554/stream1",
"camera_produce": "海康威视",
"camera_name": "前门摄像头",
"device_type": "网络摄像头",
"unit_code": "UNIT001",
"nvr_produce": "海康威视",
"nvr_path": "/stream1",
"play_back": "支持",
"del_flag": "0",
"create_by": "admin",
"create_time": "2024-01-01T10:00:00Z",
"update_by": "admin",
"update_time": "2024-01-01T10:00:00Z",
"user_id": 1,
"dept_id": 1,
"stream_status": "online"
}
],
"total": 1,
"unit_code": "UNIT001"
}
```
### 3. 其他摄像头管理接口
- `POST /camera/add` - 添加摄像头
- `GET /camera/{uuid}` - 获取单个摄像头信息
- `PUT /camera/{uuid}` - 更新摄像头信息
- `DELETE /camera/{uuid}` - 删除摄像头
- `POST /cameras/refresh` - 刷新摄像头列表
- `GET /database/status` - 获取数据库状态
## 前端调用示例
### JavaScript (原生)
```javascript
// 获取所有摄像头
async function getAllCameras() {
try {
const response = await fetch('http://localhost:8083/cameras', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
// 如果需要认证,添加以下头部
// 'Authorization': 'Basic ' + btoa('username:password')
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('所有摄像头:', data.cameras);
return data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
}
}
// 根据单位代码获取摄像头
async function getCamerasByUnitCode(unitCode) {
try {
const response = await fetch(`http://localhost:8083/cameras/unit/${unitCode}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
// 如果需要认证,添加以下头部
// 'Authorization': 'Basic ' + btoa('username:password')
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(`单位 ${unitCode} 的摄像头:`, data.cameras);
return data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
}
}
// 使用示例
getAllCameras();
getCamerasByUnitCode('UNIT001');
```
### jQuery
```javascript
// 获取所有摄像头
function getAllCameras() {
$.ajax({
url: 'http://localhost:8083/cameras',
type: 'GET',
dataType: 'json',
// 如果需要认证
// beforeSend: function(xhr) {
// xhr.setRequestHeader('Authorization', 'Basic ' + btoa('username:password'));
// },
success: function(data) {
console.log('所有摄像头:', data.cameras);
// 处理数据
},
error: function(xhr, status, error) {
console.error('获取摄像头列表失败:', error);
}
});
}
// 根据单位代码获取摄像头
function getCamerasByUnitCode(unitCode) {
$.ajax({
url: `http://localhost:8083/cameras/unit/${unitCode}`,
type: 'GET',
dataType: 'json',
// 如果需要认证
// beforeSend: function(xhr) {
// xhr.setRequestHeader('Authorization', 'Basic ' + btoa('username:password'));
// },
success: function(data) {
console.log(`单位 ${unitCode} 的摄像头:`, data.cameras);
// 处理数据
},
error: function(xhr, status, error) {
console.error('获取摄像头列表失败:', error);
}
});
}
```
### Axios
```javascript
import axios from 'axios';
// 配置基础URL和认证
const api = axios.create({
baseURL: 'http://localhost:8083',
headers: {
'Content-Type': 'application/json',
// 如果需要认证
// 'Authorization': 'Basic ' + btoa('username:password')
}
});
// 获取所有摄像头
export const getAllCameras = async () => {
try {
const response = await api.get('/cameras');
return response.data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
throw error;
}
};
// 根据单位代码获取摄像头
export const getCamerasByUnitCode = async (unitCode) => {
try {
const response = await api.get(`/cameras/unit/${unitCode}`);
return response.data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
throw error;
}
};
```
### Vue.js 组件示例
```vue
<template>
<div>
<h2>摄像头管理</h2>
<!-- 单位代码选择 -->
<div>
<label>选择单位代码:</label>
<select v-model="selectedUnitCode" @change="loadCamerasByUnit">
<option value="">全部</option>
<option value="UNIT001">UNIT001</option>
<option value="UNIT002">UNIT002</option>
</select>
</div>
<!-- 摄像头列表 -->
<div v-if="loading">加载中...</div>
<div v-else>
<h3>摄像头列表 (共 {{ cameras.length }} 个)</h3>
<div v-for="camera in cameras" :key="camera.camera_id" class="camera-item">
<h4>{{ camera.camera_name }}</h4>
<p>IP: {{ camera.ip }}:{{ camera.port }}</p>
<p>单位代码: {{ camera.unit_code }}</p>
<p>状态: <span :class="camera.stream_status">{{ camera.stream_status }}</span></p>
</div>
</div>
</div>
</template>
<script>
import { getAllCameras, getCamerasByUnitCode } from './api';
export default {
name: 'CameraManager',
data() {
return {
cameras: [],
selectedUnitCode: '',
loading: false
};
},
mounted() {
this.loadAllCameras();
},
methods: {
async loadAllCameras() {
this.loading = true;
try {
const data = await getAllCameras();
this.cameras = data.cameras;
} catch (error) {
console.error('加载摄像头失败:', error);
} finally {
this.loading = false;
}
},
async loadCamerasByUnit() {
if (!this.selectedUnitCode) {
this.loadAllCameras();
return;
}
this.loading = true;
try {
const data = await getCamerasByUnitCode(this.selectedUnitCode);
this.cameras = data.cameras;
} catch (error) {
console.error('加载摄像头失败:', error);
} finally {
this.loading = false;
}
}
}
};
</script>
<style scoped>
.camera-item {
border: 1px solid #ddd;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
.online {
color: green;
font-weight: bold;
}
.offline {
color: red;
font-weight: bold;
}
</style>
```
## 错误处理
### 常见错误码
- `400 Bad Request` - 请求参数错误
- `401 Unauthorized` - 认证失败
- `500 Internal Server Error` - 服务器内部错误
- `503 Service Unavailable` - 数据库未启用
### 错误响应格式
```json
{
"error": "错误描述信息"
}
```
## 注意事项
1. **数据库配置**:确保在 `config.json` 中正确配置了数据库连接信息
2. **认证**:如果启用了 HTTP 认证,所有请求都需要包含认证头部
3. **CORS**:如果前端和后端不在同一域名下,需要处理跨域问题
4. **流状态**`stream_status` 字段表示摄像头的实时流状态online/offline
5. **单位代码**`unit_code` 字段用于按组织单位过滤摄像头
6. **删除标记**:只返回 `del_flag != '1'` 的摄像头(未删除的摄像头)

@ -0,0 +1,275 @@
# 数据库集成功能说明
本项目已扩展支持MySQL和SQL Server数据库用于管理摄像头信息实现动态配置和实时预览功能。
## 功能特性
- ✅ 支持MySQL和SQL Server数据库
- ✅ 摄像头信息的增删改查
- ✅ Web界面管理摄像头
- ✅ 实时预览新增摄像头
- ✅ 数据库与配置文件双模式支持
- ✅ 自动数据库表创建
- ✅ 数据库连接状态监控
## 配置说明
### 1. 配置文件设置
`config.json` 中添加数据库配置:
```json
{
"server": {
"database_enabled": true,
// ... 其他服务器配置
},
"database": {
"enabled": true,
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "password",
"database": "rtsp_cameras",
"table_name": "cameras"
}
}
```
### 2. 数据库类型配置
#### MySQL配置
```json
"database": {
"enabled": true,
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "password",
"database": "rtsp_cameras",
"table_name": "cameras"
}
```
#### SQL Server配置
```json
"database": {
"enabled": true,
"type": "sqlserver",
"host": "localhost",
"port": 1433,
"username": "sa",
"password": "YourPassword123",
"database": "rtsp_cameras",
"table_name": "cameras"
}
```
## 数据库初始化
### 1. 使用提供的SQL脚本
执行 `database_init.sql` 文件来创建数据库和表:
```bash
# MySQL
mysql -u root -p < database_init.sql
# SQL Server
sqlcmd -S localhost -U sa -P YourPassword123 -i database_init.sql
```
### 2. 手动创建MySQL
```sql
CREATE DATABASE rtsp_cameras CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE rtsp_cameras;
CREATE TABLE cameras (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
ip VARCHAR(45) NOT NULL,
username VARCHAR(100) DEFAULT NULL,
password VARCHAR(255) DEFAULT NULL,
rtsp_url VARCHAR(500) NOT NULL,
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
```
### 3. 手动创建SQL Server
```sql
CREATE DATABASE rtsp_cameras;
GO
USE rtsp_cameras;
GO
CREATE TABLE cameras (
id NVARCHAR(36) PRIMARY KEY,
name NVARCHAR(255) NOT NULL,
ip NVARCHAR(45) NOT NULL,
username NVARCHAR(100) NULL,
password NVARCHAR(255) NULL,
rtsp_url NVARCHAR(500) NOT NULL,
enabled BIT DEFAULT 1,
created_at DATETIME2 DEFAULT GETDATE(),
updated_at DATETIME2 DEFAULT GETDATE()
);
```
## API接口
### 摄像头管理接口
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/cameras` | 获取所有摄像头列表 |
| POST | `/camera/add` | 添加新摄像头 |
| GET | `/camera/{id}` | 获取单个摄像头信息 |
| PUT | `/camera/{id}` | 更新摄像头信息 |
| DELETE | `/camera/{id}` | 删除摄像头 |
| POST | `/cameras/refresh` | 刷新摄像头列表 |
| GET | `/database/status` | 获取数据库状态 |
### 请求示例
#### 添加摄像头
```bash
curl -X POST http://localhost:8083/camera/add \
-H "Content-Type: application/json" \
-d '{
"name": "测试摄像头",
"ip": "192.168.1.100",
"username": "admin",
"password": "admin123",
"rtsp_url": "rtsp://192.168.1.100:554/stream1",
"enabled": true
}'
```
#### 更新摄像头
```bash
curl -X PUT http://localhost:8083/camera/{camera_id} \
-H "Content-Type: application/json" \
-d '{
"name": "更新的摄像头名称",
"ip": "192.168.1.101",
"username": "admin",
"password": "newpassword",
"rtsp_url": "rtsp://192.168.1.101:554/stream1",
"enabled": true
}'
```
## Web界面使用
### 1. 访问摄像头管理页面
打开浏览器访问:`http://localhost:8083/pages/cameras`
### 2. 功能说明
- **摄像头列表**:显示所有摄像头的基本信息和状态
- **添加摄像头**:点击"添加摄像头"按钮,填写摄像头信息
- **编辑摄像头**:点击摄像头卡片上的"编辑"按钮
- **删除摄像头**:点击摄像头卡片上的"删除"按钮
- **实时预览**:点击摄像头卡片上的"预览"按钮
- **刷新列表**:点击"刷新"按钮重新加载摄像头列表
- **数据库状态**:页面顶部显示数据库连接状态
## 运行模式
### 1. 数据库模式
`database.enabled = true` 时:
- 摄像头信息从数据库加载
- 所有配置变更保存到数据库
- 配置文件中的streams配置被忽略
### 2. 配置文件模式
`database.enabled = false` 时:
- 摄像头信息从config.json加载
- 配置变更保存到配置文件
- 数据库功能不可用
### 3. 混合模式
可以在运行时动态切换模式,但建议重启应用以确保配置生效。
## 依赖包
项目添加了以下Go依赖包
```go
// MySQL驱动
github.com/go-sql-driver/mysql v1.7.1
// SQL Server驱动
github.com/denisenkom/go-mssqldb v0.12.3
// UUID生成
github.com/google/uuid v1.3.0
```
## 安装依赖
```bash
go mod tidy
```
## 编译运行
```bash
# 编译
go build -o rtsp-to-web
# 运行
./rtsp-to-web -config config.json
```
## 故障排除
### 1. 数据库连接失败
- 检查数据库服务是否运行
- 验证连接参数(主机、端口、用户名、密码)
- 确认数据库存在
- 检查防火墙设置
### 2. 表不存在错误
- 运行数据库初始化脚本
- 检查数据库权限
- 确认表名配置正确
### 3. 摄像头无法预览
- 检查RTSP URL是否正确
- 验证摄像头网络连接
- 确认用户名密码正确
- 检查摄像头是否支持RTSP协议
### 4. Web界面无法访问
- 确认HTTP服务端口配置
- 检查防火墙设置
- 验证demo模式是否启用
## 注意事项
1. **密码安全**:数据库中的密码以明文存储,生产环境建议加密
2. **并发访问**:多个实例同时访问数据库时注意数据一致性
3. **备份恢复**:定期备份数据库数据
4. **性能优化**:大量摄像头时考虑数据库索引优化
5. **网络安全**生产环境建议使用HTTPS和数据库SSL连接
## 更新日志
- v1.0.0: 初始版本支持MySQL和SQL Server
- 添加Web界面管理功能
- 支持实时预览和动态配置

@ -0,0 +1,512 @@
# Java项目集成RTSPtoWeb指南
本文档说明如何将RTSPtoWeb项目集成到Java项目中实现摄像头管理和视频流展示功能。
## 集成方案
### 方案一:嵌入式集成(推荐)
将RTSPtoWeb的前端页面嵌入到Java Web项目中通过iframe或直接集成HTML。
#### 1. 嵌入整个管理界面
```html
<!-- 嵌入RTSPtoWeb完整界面 -->
<iframe src="http://localhost:8083"
width="100%"
height="800px"
frameborder="0">
</iframe>
```
#### 2. 嵌入特定功能页面
```html
<!-- 仅嵌入摄像头管理页面 -->
<iframe src="http://localhost:8083/pages/cameras"
width="100%"
height="600px"
frameborder="0">
</iframe>
<!-- 仅嵌入视频流列表 -->
<iframe src="http://localhost:8083/"
width="100%"
height="600px"
frameborder="0">
</iframe>
```
### 方案二API集成
通过REST API调用RTSPtoWeb的功能在Java项目中实现自定义界面。
#### Java HTTP客户端示例
```java
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
public class RTSPtoWebClient {
private final String baseUrl;
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
public RTSPtoWebClient(String baseUrl) {
this.baseUrl = baseUrl;
this.httpClient = HttpClient.newHttpClient();
this.objectMapper = new ObjectMapper();
}
// 获取所有摄像头
public JsonNode getAllCameras() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/cameras"))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to get cameras: " + response.statusCode());
}
}
// 根据单位代码获取摄像头
public JsonNode getCamerasByUnitCode(String unitCode) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/cameras/unit/" + unitCode))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to get cameras by unit: " + response.statusCode());
}
}
// 获取特定摄像头信息
public JsonNode getCamera(String cameraId) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/camera/" + cameraId))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to get camera: " + response.statusCode());
}
}
// 添加摄像头
public JsonNode addCamera(CameraData cameraData) throws Exception {
String jsonBody = objectMapper.writeValueAsString(cameraData);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/camera/add"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to add camera: " + response.statusCode());
}
}
// 更新摄像头
public JsonNode updateCamera(String cameraId, CameraData cameraData) throws Exception {
String jsonBody = objectMapper.writeValueAsString(cameraData);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/camera/" + cameraId))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to update camera: " + response.statusCode());
}
}
// 删除摄像头
public boolean deleteCamera(String cameraId) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/camera/" + cameraId))
.DELETE()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
return response.statusCode() == 200;
}
// 刷新摄像头状态
public JsonNode refreshCameras() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/cameras/refresh"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{}"))
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to refresh cameras: " + response.statusCode());
}
}
// 获取视频流列表
public JsonNode getStreams() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/streams"))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return objectMapper.readTree(response.body());
} else {
throw new RuntimeException("Failed to get streams: " + response.statusCode());
}
}
// 生成播放URL
public String getPlayUrl(String cameraId, int channel, String type) {
switch (type.toLowerCase()) {
case "hls":
return baseUrl + "/stream/" + cameraId + "/channel/" + channel + "/hls/live/index.m3u8";
case "webrtc":
return baseUrl + "/pages/player/webrtc/" + cameraId + "/" + channel;
case "mse":
return baseUrl + "/pages/player/mse/" + cameraId + "/" + channel;
default:
return baseUrl + "/pages/player/all/" + cameraId + "/" + channel;
}
}
}
// 摄像头数据模型
class CameraData {
public String name;
public String ip;
public int port = 554;
public String username;
public String password;
public String rtsp_url;
public String camera_produce;
public String device_type;
public String unit_code;
public String nvr_produce;
public String nvr_path;
public String play_back;
public boolean enabled = true;
public int user_id;
public int dept_id;
// 构造函数、getter和setter方法
public CameraData() {}
public CameraData(String name, String ip, String username, String password, String rtspUrl) {
this.name = name;
this.ip = ip;
this.username = username;
this.password = password;
this.rtsp_url = rtspUrl;
}
}
```
#### Spring Boot Controller示例
```java
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import com.fasterxml.jackson.databind.JsonNode;
@Controller
@RequestMapping("/camera")
public class CameraController {
private final RTSPtoWebClient rtspClient;
public CameraController() {
this.rtspClient = new RTSPtoWebClient("http://localhost:8083");
}
// 摄像头管理页面
@GetMapping("/management")
public String cameraManagement(Model model) {
try {
JsonNode cameras = rtspClient.getAllCameras();
model.addAttribute("cameras", cameras);
} catch (Exception e) {
model.addAttribute("error", "获取摄像头列表失败: " + e.getMessage());
}
return "camera/management";
}
// 根据单位获取摄像头
@GetMapping("/unit/{unitCode}")
@ResponseBody
public JsonNode getCamerasByUnit(@PathVariable String unitCode) {
try {
return rtspClient.getCamerasByUnitCode(unitCode);
} catch (Exception e) {
throw new RuntimeException("获取摄像头失败", e);
}
}
// 视频监控页面
@GetMapping("/monitor")
public String videoMonitor(Model model) {
try {
JsonNode cameras = rtspClient.getAllCameras();
model.addAttribute("cameras", cameras);
} catch (Exception e) {
model.addAttribute("error", "获取摄像头列表失败: " + e.getMessage());
}
return "camera/monitor";
}
// 添加摄像头
@PostMapping("/add")
@ResponseBody
public JsonNode addCamera(@RequestBody CameraData cameraData) {
try {
return rtspClient.addCamera(cameraData);
} catch (Exception e) {
throw new RuntimeException("添加摄像头失败", e);
}
}
// 获取播放URL
@GetMapping("/{cameraId}/play-url")
@ResponseBody
public String getPlayUrl(@PathVariable String cameraId,
@RequestParam(defaultValue = "0") int channel,
@RequestParam(defaultValue = "hls") String type) {
return rtspClient.getPlayUrl(cameraId, channel, type);
}
}
```
### 方案三前端JavaScript集成
在Java项目的前端页面中直接使用RTSPtoWeb提供的JavaScript API。
```html
<!DOCTYPE html>
<html>
<head>
<title>视频监控系统</title>
<script src="http://localhost:8083/static/js/camera-manager.js"></script>
</head>
<body>
<div id="camera-container"></div>
<script>
// 使用RTSPtoWeb提供的全局API
async function loadCameras() {
try {
const data = await window.RTSPtoWebAPI.getCameras();
displayCameras(data.cameras);
} catch (error) {
console.error('加载摄像头失败:', error);
}
}
function displayCameras(cameras) {
const container = document.getElementById('camera-container');
container.innerHTML = '';
cameras.forEach(camera => {
const div = document.createElement('div');
div.innerHTML = `
<h3>${camera.name}</h3>
<p>IP: ${camera.ip}</p>
<p>状态: ${camera.status}</p>
<button onclick="playCamera('${camera.id}', 'hls')">HLS播放</button>
<button onclick="playCamera('${camera.id}', 'webrtc')">WebRTC播放</button>
`;
container.appendChild(div);
});
}
function playCamera(cameraId, type) {
const url = window.RTSPtoWebAPI.getPlayUrl(cameraId, 0, type);
window.open(url, '_blank');
}
// 页面加载时获取摄像头列表
document.addEventListener('DOMContentLoaded', loadCameras);
</script>
</body>
</html>
```
## 部署配置
### 1. RTSPtoWeb服务配置
确保RTSPtoWeb服务正常运行
```bash
# 启动RTSPtoWeb服务
cd RTSPtoWeb
go run . --config config.json
```
### 2. 跨域配置
如果Java项目和RTSPtoWeb运行在不同端口需要配置CORS
在RTSPtoWeb的`apiHTTPServer.go`中添加CORS中间件
```go
func CORSMiddleware() gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Header("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
}
```
### 3. 反向代理配置
使用Nginx反向代理统一端口
```nginx
server {
listen 80;
server_name your-domain.com;
# Java应用
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# RTSPtoWeb API
location /rtsp-api/ {
proxy_pass http://localhost:8083/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket支持
location /rtsp-api/stream/ {
proxy_pass http://localhost:8083/stream/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
```
## API接口文档
### 摄像头管理API
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/cameras` | 获取所有摄像头列表 |
| GET | `/cameras/unit/{unitcode}` | 根据单位代码获取摄像头 |
| GET | `/camera/{id}` | 获取特定摄像头信息 |
| POST | `/camera/add` | 添加新摄像头 |
| PUT | `/camera/{id}` | 更新摄像头信息 |
| DELETE | `/camera/{id}` | 删除摄像头 |
| POST | `/cameras/refresh` | 刷新摄像头状态 |
### 视频流API
| 方法 | 路径 | 描述 |
|------|------|------|
| GET | `/streams` | 获取所有视频流列表 |
| GET | `/stream/{id}/channel/{channel}/hls/live/index.m3u8` | HLS播放地址 |
| GET | `/pages/player/webrtc/{id}/{channel}` | WebRTC播放页面 |
| GET | `/pages/player/mse/{id}/{channel}` | MSE播放页面 |
| GET | `/pages/player/all/{id}/{channel}` | 通用播放页面 |
## 注意事项
1. **数据库配置**确保RTSPtoWeb的数据库配置正确摄像头数据存储在数据库中。
2. **网络配置**确保Java项目能够访问RTSPtoWeb服务的端口默认8083
3. **安全考虑**:在生产环境中,建议配置认证和授权机制。
4. **性能优化**:对于大量摄像头的场景,建议实现分页和缓存机制。
5. **错误处理**:实现完善的错误处理和重试机制。
6. **监控告警**:建议添加服务健康检查和告警机制。
## 示例项目结构
```
java-rtsp-integration/
├── src/main/java/
│ ├── controller/
│ │ ├── CameraController.java
│ │ └── VideoController.java
│ ├── service/
│ │ ├── RTSPtoWebClient.java
│ │ └── CameraService.java
│ └── model/
│ └── CameraData.java
├── src/main/resources/
│ ├── templates/
│ │ ├── camera/
│ │ │ ├── management.html
│ │ │ └── monitor.html
│ │ └── layout.html
│ └── static/
│ ├── css/
│ └── js/
└── pom.xml
```
通过以上集成方案可以在Java项目中完整地使用RTSPtoWeb的摄像头管理和视频流功能。

Binary file not shown.

@ -10,6 +10,9 @@ import (
)
func main() {
println("Starting RTSPtoWeb server...")
// Set correct log level after Storage initialization
SetLogLevel()
log.WithFields(logrus.Fields{
"module": "main",
"func": "main",

@ -0,0 +1,542 @@
package main
import (
"database/sql"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
// getStringValue 获取sql.NullString的值
func getStringValue(ns sql.NullString) string {
if ns.Valid {
return ns.String
}
return ""
}
// getInt64Value 获取sql.NullInt64的值
func getInt64Value(ni sql.NullInt64) int64 {
if ni.Valid {
return ni.Int64
}
return 0
}
// getTimeValue 获取sql.NullTime的值
func getTimeValue(nt sql.NullTime) time.Time {
if nt.Valid {
return nt.Time
}
return time.Time{}
}
// CameraRequest 摄像头请求结构
type CameraRequest struct {
Name string `json:"name" binding:"required"`
IP string `json:"ip" binding:"required"`
Username string `json:"username"`
Password string `json:"password"`
RTSPURL string `json:"rtsp_url" binding:"required"`
Enabled bool `json:"enabled"`
}
// CameraResponse 摄像头响应结构 - 适配qsc_camera表
type CameraResponse struct {
CameraID int `json:"camera_id"`
IP string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"`
URL string `json:"url"`
CameraProduce string `json:"camera_produce"`
CameraName string `json:"camera_name"`
DeviceType string `json:"device_type"`
UnitCode string `json:"unit_code"`
NvrProduce string `json:"nvr_produce"`
NvrPath string `json:"nvr_path"`
PlayBack string `json:"play_back"`
DelFlag string `json:"del_flag"`
CreateBy string `json:"create_by"`
CreateTime time.Time `json:"create_time"`
UpdateBy string `json:"update_by"`
UpdateTime time.Time `json:"update_time"`
UserID int64 `json:"user_id"`
DeptID int64 `json:"dept_id"`
StreamStatus string `json:"stream_status"`
}
// HTTPAPIServerCameras 获取所有摄像头
func HTTPAPIServerCameras(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
cameras, err := Storage.dbManager.GetAllCameras()
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameras",
"call": "GetAllCameras",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// 转换为响应格式
var responses []CameraResponse
for _, camera := range cameras {
// 检查流状态
streamID := fmt.Sprintf("%d", camera.CameraID)
streamStatus := "offline"
if _, exists := Storage.Streams[streamID]; exists {
if Storage.StreamChannelExist(streamID, "0") {
streamStatus = "online"
}
}
response := CameraResponse{
CameraID: camera.CameraID,
IP: camera.IP,
Port: camera.Port,
Username: getStringValue(camera.Username),
Password: getStringValue(camera.Password),
URL: camera.URL,
CameraProduce: getStringValue(camera.CameraProduce),
CameraName: getStringValue(camera.CameraName),
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
NvrProduce: getStringValue(camera.NvrProduce),
NvrPath: getStringValue(camera.NvrPath),
PlayBack: getStringValue(camera.PlayBack),
DelFlag: camera.DelFlag,
CreateBy: getStringValue(camera.CreateBy),
CreateTime: getTimeValue(camera.CreateTime),
UpdateBy: getStringValue(camera.UpdateBy),
UpdateTime: getTimeValue(camera.UpdateTime),
UserID: getInt64Value(camera.UserID),
DeptID: getInt64Value(camera.DeptID),
StreamStatus: streamStatus,
}
responses = append(responses, response)
}
c.JSON(http.StatusOK, gin.H{
"cameras": responses,
"total": len(responses),
})
}
// HTTPAPIServerCamerasByUnitCode 根据unitcode获取摄像头列表
func HTTPAPIServerCamerasByUnitCode(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
unitCode := c.Param("unitcode")
if unitCode == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Unit code is required",
})
return
}
cameras, err := Storage.dbManager.GetCamerasByUnitCode(unitCode)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCamerasByUnitCode",
"call": "GetCamerasByUnitCode",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// 转换为响应格式
var responses []CameraResponse
for _, camera := range cameras {
// 检查流状态
streamID := fmt.Sprintf("%d", camera.CameraID)
streamStatus := "offline"
if _, exists := Storage.Streams[streamID]; exists {
if Storage.StreamChannelExist(streamID, "0") {
streamStatus = "online"
}
}
response := CameraResponse{
CameraID: camera.CameraID,
IP: camera.IP,
Port: camera.Port,
Username: getStringValue(camera.Username),
Password: getStringValue(camera.Password),
URL: camera.URL,
CameraProduce: getStringValue(camera.CameraProduce),
CameraName: getStringValue(camera.CameraName),
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
NvrProduce: getStringValue(camera.NvrProduce),
NvrPath: getStringValue(camera.NvrPath),
PlayBack: getStringValue(camera.PlayBack),
DelFlag: camera.DelFlag,
CreateBy: getStringValue(camera.CreateBy),
CreateTime: getTimeValue(camera.CreateTime),
UpdateBy: getStringValue(camera.UpdateBy),
UpdateTime: getTimeValue(camera.UpdateTime),
UserID: getInt64Value(camera.UserID),
DeptID: getInt64Value(camera.DeptID),
StreamStatus: streamStatus,
}
responses = append(responses, response)
}
c.JSON(http.StatusOK, gin.H{
"cameras": responses,
"total": len(responses),
"unit_code": unitCode,
})
}
// HTTPAPIServerCameraAdd 添加新摄像头
func HTTPAPIServerCameraAdd(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
var req CameraRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
// 创建摄像头记录 - 适配qsc_camera表
camera := Camera{
IP: req.IP,
Port: 554, // 默认RTSP端口
Username: sql.NullString{String: req.Username, Valid: req.Username != ""},
Password: sql.NullString{String: req.Password, Valid: req.Password != ""},
URL: req.RTSPURL,
CameraName: sql.NullString{String: req.Name, Valid: true},
DeviceType: sql.NullString{String: "网络摄像头", Valid: true},
UnitCode: sql.NullString{String: "DEFAULT", Valid: true}, // 默认单位代码
DelFlag: "0",
CreateBy: sql.NullString{String: "admin", Valid: true},
CreateTime: sql.NullTime{Time: time.Now(), Valid: true},
UpdateBy: sql.NullString{String: "admin", Valid: true},
UpdateTime: sql.NullTime{Time: time.Now(), Valid: true},
UserID: sql.NullInt64{Int64: 1, Valid: true},
DeptID: sql.NullInt64{Int64: 1, Valid: true},
}
// 保存到数据库
err := Storage.dbManager.CreateCamera(&camera)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraAdd",
"call": "CreateCamera",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// 检查流是否已存在
streamID := fmt.Sprintf("%d", camera.CameraID)
if _, exists := Storage.Streams[streamID]; exists {
c.JSON(http.StatusConflict, gin.H{
"error": "Stream already exists",
})
return
}
// 转换为流配置并添加到内存
stream := CameraToStream(camera)
err = Storage.StreamAdd(streamID, stream)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraAdd",
"call": "StreamAdd",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusCreated, gin.H{
"message": "Camera added successfully",
"camera_id": camera.CameraID,
})
}
// HTTPAPIServerCameraUpdate 更新摄像头
func HTTPAPIServerCameraUpdate(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
cameraID := c.Param("uuid")
if cameraID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Camera ID is required",
})
return
}
var req CameraRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
// 检查摄像头是否存在
existingCamera, err := Storage.dbManager.GetCameraByID(cameraID)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraUpdate",
"call": "GetCameraByID",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
if existingCamera == nil {
c.JSON(http.StatusNotFound, gin.H{
"error": "Camera not found",
})
return
}
// 更新摄像头信息 - 适配qsc_camera表
updatedCamera := *existingCamera // 复制现有摄像头信息
updatedCamera.IP = req.IP
updatedCamera.Username = sql.NullString{String: req.Username, Valid: req.Username != ""}
updatedCamera.Password = sql.NullString{String: req.Password, Valid: req.Password != ""}
updatedCamera.URL = req.RTSPURL
updatedCamera.CameraName = sql.NullString{String: req.Name, Valid: true}
updatedCamera.UpdateBy = sql.NullString{String: "admin", Valid: true}
updatedCamera.UpdateTime = sql.NullTime{Time: time.Now(), Valid: true}
// 更新数据库
err = Storage.dbManager.UpdateCamera(&updatedCamera)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraUpdate",
"call": "UpdateCamera",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// 更新内存中的流配置
stream := CameraToStream(updatedCamera)
err = Storage.StreamEdit(cameraID, stream)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraUpdate",
"call": "StreamEdit",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "Camera updated successfully",
})
}
// HTTPAPIServerCameraDelete 删除摄像头
func HTTPAPIServerCameraDelete(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
cameraID := c.Param("uuid")
if cameraID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Camera ID is required",
})
return
}
// 检查摄像头是否存在
existingCamera, err := Storage.dbManager.GetCameraByID(cameraID)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraDelete",
"call": "GetCameraByID",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
if existingCamera == nil {
c.JSON(http.StatusNotFound, gin.H{
"error": "Camera not found",
})
return
}
// 从内存中删除流
err = Storage.StreamDelete(cameraID)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraDelete",
"call": "StreamDelete",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "Camera deleted successfully",
})
}
// HTTPAPIServerCameraGet 获取单个摄像头信息
func HTTPAPIServerCameraGet(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
cameraID := c.Param("uuid")
if cameraID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Camera ID is required",
})
return
}
camera, err := Storage.dbManager.GetCameraByID(cameraID)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraGet",
"call": "GetCameraByID",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
if camera == nil {
c.JSON(http.StatusNotFound, gin.H{
"error": "Camera not found",
})
return
}
// 检查流状态
status := "offline"
streamID := fmt.Sprintf("%d", camera.CameraID)
if stream, exists := Storage.Streams[streamID]; exists {
if len(stream.Channels) > 0 {
for _, channel := range stream.Channels {
if channel.runLock {
status = "online"
break
}
}
}
}
response := CameraResponse{
CameraID: camera.CameraID,
CameraName: getStringValue(camera.CameraName),
IP: camera.IP,
Port: camera.Port,
Username: getStringValue(camera.Username),
// Password: camera.Password, // 不返回密码
URL: camera.URL,
CameraProduce: getStringValue(camera.CameraProduce),
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
NvrProduce: getStringValue(camera.NvrProduce),
StreamStatus: status,
CreateTime: getTimeValue(camera.CreateTime),
UpdateTime: getTimeValue(camera.UpdateTime),
}
c.JSON(http.StatusOK, response)
}
// HTTPAPIServerCameraRefresh 刷新摄像头列表(从数据库重新加载)
func HTTPAPIServerCameraRefresh(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
err := Storage.RefreshStreamsFromDatabase()
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCameraRefresh",
"call": "RefreshStreamsFromDatabase",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "Cameras refreshed successfully",
})
}
// HTTPAPIServerDatabaseStatus 获取数据库状态
func HTTPAPIServerDatabaseStatus(c *gin.Context) {
status := Storage.GetDatabaseStatus()
c.JSON(http.StatusOK, status)
}

@ -0,0 +1,342 @@
package main
import (
"fmt"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
// JavaAPIResponse 标准化的Java API响应格式
type JavaAPIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
Success bool `json:"success"`
}
// CameraListResponse 摄像头列表响应
type CameraListResponse struct {
Total int `json:"total"`
Cameras []Camera `json:"cameras"`
}
// StreamListResponse 流列表响应
type StreamListResponse struct {
Total int `json:"total"`
Streams []Stream `json:"streams"`
}
// JavaCamera 摄像头信息结构 - 用于Java集成API
type JavaCamera struct {
ID string `json:"id"`
Name string `json:"name"`
IP string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"` // 敏感信息可选返回
RTSPURL string `json:"rtsp_url"`
Status string `json:"status"`
Enabled bool `json:"enabled"`
DeviceType string `json:"device_type"`
UnitCode string `json:"unit_code"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PlayURLs PlayURLs `json:"play_urls"`
}
// Stream 流信息结构
type Stream struct {
ID string `json:"id"`
Name string `json:"name"`
Channels map[string]string `json:"channels"`
PlayURLs PlayURLs `json:"play_urls"`
}
// PlayURLs 播放地址结构
type PlayURLs struct {
HLS string `json:"hls"`
WebRTC string `json:"webrtc"`
MSE string `json:"mse"`
All string `json:"all"`
}
// HTTPJavaAPICameras Java项目专用的摄像头列表API
func HTTPJavaAPICameras(c *gin.Context) {
if !Storage.ServerHTTPDemo() {
c.JSON(http.StatusForbidden, JavaAPIResponse{
Code: 403,
Message: "Demo mode is disabled",
Success: false,
})
return
}
// 获取查询参数
unitCode := c.Query("unit_code")
status := c.Query("status")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
var cameras []JavaCamera
var total int
// 如果数据库启用,从数据库获取
if Storage.Server.DatabaseEnabled {
dbCameras, err := Storage.DatabaseGetCameras()
if err != nil {
c.JSON(http.StatusInternalServerError, JavaAPIResponse{
Code: 500,
Message: fmt.Sprintf("Database error: %v", err),
Success: false,
})
return
}
// 转换数据库摄像头为API格式
for _, dbCamera := range dbCameras {
camera := JavaCamera{
ID: fmt.Sprintf("%d", dbCamera.CameraID),
Name: getStringValue(dbCamera.CameraName),
IP: dbCamera.IP,
Port: dbCamera.Port,
Username: getStringValue(dbCamera.Username),
RTSPURL: dbCamera.URL,
Status: "online", // 默认状态
Enabled: dbCamera.DelFlag == "0", // 删除标志为0表示启用
DeviceType: getStringValue(dbCamera.DeviceType),
UnitCode: getStringValue(dbCamera.UnitCode),
CreatedAt: getTimeValue(dbCamera.CreateTime),
UpdatedAt: getTimeValue(dbCamera.UpdateTime),
PlayURLs: generatePlayURLs(fmt.Sprintf("%d", dbCamera.CameraID)),
}
// 过滤条件
if unitCode != "" && camera.UnitCode != unitCode {
continue
}
if status != "" && camera.Status != status {
continue
}
cameras = append(cameras, camera)
}
} else {
// 从流配置获取
streamsList := Storage.Streams
for streamID := range streamsList {
camera := JavaCamera{
ID: streamID,
Name: fmt.Sprintf("Stream %s", streamID),
Status: "online",
Enabled: true,
PlayURLs: generatePlayURLs(streamID),
}
cameras = append(cameras, camera)
}
}
total = len(cameras)
// 分页处理
start := (page - 1) * size
end := start + size
if start > total {
cameras = []JavaCamera{}
} else if end > total {
cameras = cameras[start:]
} else {
cameras = cameras[start:end]
}
response := JavaAPIResponse{
Code: 200,
Message: "Success",
Success: true,
Data: map[string]interface{}{
"total": total,
"cameras": cameras,
},
}
c.JSON(http.StatusOK, response)
}
// HTTPJavaAPIStreams Java项目专用的流列表API
func HTTPJavaAPIStreams(c *gin.Context) {
if !Storage.ServerHTTPDemo() {
c.JSON(http.StatusForbidden, JavaAPIResponse{
Code: 403,
Message: "Demo mode is disabled",
Success: false,
})
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
streamsList := Storage.Streams
var streams []Stream
for streamID, streamData := range streamsList {
stream := Stream{
ID: streamID,
Name: fmt.Sprintf("Stream %s", streamID),
Channels: make(map[string]string),
PlayURLs: generatePlayURLs(streamID),
}
// 获取通道信息
if len(streamData.Channels) > 0 {
for channelID := range streamData.Channels {
stream.Channels[channelID] = fmt.Sprintf("Channel %s", channelID)
}
}
streams = append(streams, stream)
}
total := len(streams)
// 分页处理
start := (page - 1) * size
end := start + size
if start > total {
streams = []Stream{}
} else if end > total {
streams = streams[start:]
} else {
streams = streams[start:end]
}
response := JavaAPIResponse{
Code: 200,
Message: "Success",
Success: true,
Data: StreamListResponse{
Total: total,
Streams: streams,
},
}
c.JSON(http.StatusOK, response)
}
// HTTPJavaAPICameraDetail 获取摄像头详情
func HTTPJavaAPICameraDetail(c *gin.Context) {
cameraID := c.Param("id")
if cameraID == "" {
c.JSON(http.StatusBadRequest, JavaAPIResponse{
Code: 400,
Message: "Camera ID is required",
Success: false,
})
return
}
if Storage.Server.DatabaseEnabled {
camera, err := Storage.DatabaseGetCamera(cameraID)
if err != nil {
c.JSON(http.StatusNotFound, JavaAPIResponse{
Code: 404,
Message: "Camera not found",
Success: false,
})
return
}
apiCamera := JavaCamera{
ID: fmt.Sprintf("%d", camera.CameraID),
Name: getStringValue(camera.CameraName),
IP: camera.IP,
Port: camera.Port,
Username: getStringValue(camera.Username),
RTSPURL: camera.URL,
Status: "online",
Enabled: camera.DelFlag == "0",
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
CreatedAt: getTimeValue(camera.CreateTime),
UpdatedAt: getTimeValue(camera.UpdateTime),
PlayURLs: generatePlayURLs(fmt.Sprintf("%d", camera.CameraID)),
}
c.JSON(http.StatusOK, JavaAPIResponse{
Code: 200,
Message: "Success",
Success: true,
Data: apiCamera,
})
} else {
// 从流配置获取
if !Storage.StreamExist(cameraID) {
c.JSON(http.StatusNotFound, JavaAPIResponse{
Code: 404,
Message: "Stream not found",
Success: false,
})
return
}
camera := JavaCamera{
ID: cameraID,
Name: fmt.Sprintf("Stream %s", cameraID),
Status: "online",
Enabled: true,
PlayURLs: generatePlayURLs(cameraID),
}
c.JSON(http.StatusOK, JavaAPIResponse{
Code: 200,
Message: "Success",
Success: true,
Data: camera,
})
}
}
// HTTPJavaAPISystemInfo 获取系统信息
func HTTPJavaAPISystemInfo(c *gin.Context) {
systemInfo := map[string]interface{}{
"version": "1.0.0",
"database_enabled": Storage.Server.DatabaseEnabled,
"demo_enabled": Storage.ServerHTTPDemo(),
"total_streams": len(Storage.Streams),
"server_time": time.Now().Format(time.RFC3339),
}
if Storage.Server.DatabaseEnabled {
cameras, err := Storage.DatabaseGetCameras()
if err == nil {
systemInfo["total_cameras"] = len(cameras)
onlineCount := 0
for _, camera := range cameras {
if camera.DelFlag == "0" { // 删除标志为0表示在线
onlineCount++
}
}
systemInfo["online_cameras"] = onlineCount
}
}
c.JSON(http.StatusOK, JavaAPIResponse{
Code: 200,
Message: "Success",
Success: true,
Data: systemInfo,
})
}
// generatePlayURLs 生成播放地址
func generatePlayURLs(streamID string) PlayURLs {
baseURL := "http://localhost:8083" // 可以从配置获取
return PlayURLs{
HLS: fmt.Sprintf("%s/stream/%s/channel/0/hls/live/index.m3u8", baseURL, streamID),
WebRTC: fmt.Sprintf("%s/pages/player/webrtc/%s/0", baseURL, streamID),
MSE: fmt.Sprintf("%s/pages/player/mse/%s/0", baseURL, streamID),
All: fmt.Sprintf("%s/pages/player/all/%s/0", baseURL, streamID),
}
}

@ -0,0 +1,14 @@
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// HTTPAPICameraManagement 摄像头管理页面
func HTTPAPICameraManagement(c *gin.Context) {
c.HTML(http.StatusOK, "cameras.tmpl", gin.H{
"title": "摄像头管理",
})
}

@ -1,12 +1,18 @@
package main
import (
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
"github.com/sirupsen/logrus"
)
@ -57,7 +63,9 @@ func HTTPAPIServer() {
public.Any("/pages/multiview/full", HTTPAPIFullScreenMultiView)
public.GET("/pages/documentation", HTTPAPIServerDocumentation)
public.GET("/pages/player/all/:uuid/:channel", HTTPAPIPlayAll)
public.GET("/pages/cameras", HTTPAPICameraManagement)
public.StaticFS("/static", http.Dir(Storage.ServerHTTPDir()+"/static"))
public.GET("/docs/*filepath", HTTPAPIMarkdownRenderer)
}
/*
@ -78,6 +86,28 @@ func HTTPAPIServer() {
privat.POST("/streams/multi/control/add", HTTPAPIServerStreamsMultiControlAdd)
privat.POST("/streams/multi/control/delete", HTTPAPIServerStreamsMultiControlDelete)
/*
Camera Management elements
*/
privat.GET("/cameras", HTTPAPIServerCameras)
privat.GET("/cameras/unit/:unitcode", HTTPAPIServerCamerasByUnitCode)
privat.POST("/camera/add", HTTPAPIServerCameraAdd)
privat.GET("/camera/:uuid", HTTPAPIServerCameraGet)
privat.PUT("/camera/:uuid", HTTPAPIServerCameraUpdate)
privat.DELETE("/camera/:uuid", HTTPAPIServerCameraDelete)
privat.POST("/cameras/refresh", HTTPAPIServerCameraRefresh)
privat.GET("/database/status", HTTPAPIServerDatabaseStatus)
/*
Java Integration API elements
*/
public.GET("/api/java/cameras", HTTPJavaAPICameras)
public.GET("/api/java/streams", HTTPJavaAPIStreams)
public.GET("/api/java/camera/:id", HTTPJavaAPICameraDetail)
public.GET("/api/java/system/info", HTTPJavaAPISystemInfo)
/*
Stream Channel elements
*/
@ -286,6 +316,83 @@ func HTTPAPIFullScreenMultiView(c *gin.Context) {
})
}
// HTTPAPIMarkdownRenderer renders markdown files as HTML
func HTTPAPIMarkdownRenderer(c *gin.Context) {
filePath := c.Param("filepath")
// Remove leading slash
if strings.HasPrefix(filePath, "/") {
filePath = filePath[1:]
}
// Construct full file path
fullPath := filepath.Join("./docs", filePath)
// Check if file exists and has .md extension
if !strings.HasSuffix(filePath, ".md") {
c.String(http.StatusNotFound, "只支持 .md 文件")
return
}
// Read markdown file
markdownBytes, err := ioutil.ReadFile(fullPath)
if err != nil {
c.String(http.StatusNotFound, "文件未找到")
return
}
// Create markdown parser with extensions
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
p := parser.NewWithExtensions(extensions)
doc := p.Parse(markdownBytes)
// Create HTML renderer with extensions
htmlFlags := html.CommonFlags | html.HrefTargetBlank
opts := html.RendererOptions{Flags: htmlFlags}
renderer := html.NewRenderer(opts)
// Render markdown to HTML
htmlContent := markdown.Render(doc, renderer)
// Create HTML content by concatenating strings
htmlStart := `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API </title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.min.css">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.markdown-body { line-height: 1.6; }
.markdown-body h1, .markdown-body h2, .markdown-body h3 { color: #333; margin-top: 24px; margin-bottom: 16px; }
.markdown-body code { background-color: #f6f8fa; padding: 2px 4px; border-radius: 3px; }
.markdown-body pre { background-color: #f6f8fa; padding: 16px; border-radius: 6px; overflow: auto; }
.markdown-body table { border-collapse: collapse; width: 100%; }
.markdown-body th, .markdown-body td { border: 1px solid #dfe2e5; padding: 6px 13px; }
.markdown-body th { background-color: #f6f8fa; font-weight: 600; }
</style>
</head>
<body>
<div class="container">
<nav class="mb-4">
<a href="/" class="btn btn-secondary"></a>
<a href="/pages/documentation" class="btn btn-secondary ml-2"></a>
</nav>
<div class="markdown-body">`
htmlEnd := ` </div>
</div>
</body>
</html>`
// Combine HTML parts with markdown content
finalHTML := htmlStart + string(htmlContent) + htmlEnd
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusOK, finalHTML)
}
// CrossOrigin Access-Control-Allow-Origin any methods
func CrossOrigin() gin.HandlerFunc {
return func(c *gin.Context) {

@ -1,5 +1,6 @@
{
"server": {
"database_enabled": true,
"debug": true,
"http_debug": false,
"http_demo": true,
@ -23,16 +24,14 @@
"audio": true
}
},
"streams": {
"27aec28e-6181-4753-9acd-0456a75f0289": {
"channels": {
"0": {
"url": "rtmp://171.25.232.10/12d525bc9f014e209c1280bc0d46a87e",
"debug": false,
"audio": true
}
},
"name": "111111111"
}
"database": {
"enabled": true,
"type": "mysql",
"host": "47.96.78.103",
"port": 6330,
"username": "JgzY",
"password": "JgzY@^&-)XswL",
"database": "ziyun",
"table_name": "qsc_camera"
}
}

@ -0,0 +1,398 @@
package main
import (
"database/sql"
"fmt"
"strconv"
"time"
_ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql"
)
// DatabaseConfig 数据库配置结构
type DatabaseConfig struct {
Enabled bool `json:"enabled"`
Type string `json:"type"` // mysql 或 sqlserver
Host string `json:"host"`
Port int `json:"port"`
Database string `json:"database"`
Username string `json:"username"`
Password string `json:"password"`
SSLMode string `json:"ssl_mode,omitempty"` // for mysql
}
// Camera 摄像头数据库模型 - 适配qsc_camera表
type Camera struct {
CameraID int `json:"camera_id" db:"camera_id"`
IP string `json:"ip" db:"ip"`
Port int `json:"port" db:"port"`
Username sql.NullString `json:"username" db:"username"`
Password sql.NullString `json:"password" db:"password"`
URL string `json:"url" db:"url"`
CameraProduce sql.NullString `json:"camera_produce" db:"camera_produce"`
CameraName sql.NullString `json:"camera_name" db:"camera_name"`
DeviceType sql.NullString `json:"device_type" db:"device_type"`
UnitCode sql.NullString `json:"unit_code" db:"unit_code"`
NvrProduce sql.NullString `json:"nvr_produce" db:"nvr_produce"`
NvrPath sql.NullString `json:"nvr_path" db:"nvr_path"`
PlayBack sql.NullString `json:"play_back" db:"play_back"`
Enabled bool `json:"enabled" db:"enabled"` // 兼容字段如果数据库中没有此列则默认为true
DelFlag string `json:"del_flag" db:"del_flag"`
CreateBy sql.NullString `json:"create_by" db:"create_by"`
CreateTime sql.NullTime `json:"create_time" db:"create_time"`
UpdateBy sql.NullString `json:"update_by" db:"update_by"`
UpdateTime sql.NullTime `json:"update_time" db:"update_time"`
UserID sql.NullInt64 `json:"user_id" db:"user_id"`
DeptID sql.NullInt64 `json:"dept_id" db:"dept_id"`
}
// DatabaseManager 数据库管理器
type DatabaseManager struct {
db *sql.DB
config DatabaseConfig
}
// NewDatabaseManager 创建数据库管理器
func NewDatabaseManager(config DatabaseConfig) (*DatabaseManager, error) {
if !config.Enabled {
return nil, nil
}
var dsn string
switch config.Type {
case "mysql":
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
config.Username, config.Password, config.Host, config.Port, config.Database)
if config.SSLMode != "" {
dsn += "&tls=" + config.SSLMode
}
case "sqlserver":
dsn = fmt.Sprintf("server=%s;port=%d;database=%s;user id=%s;password=%s",
config.Host, config.Port, config.Database, config.Username, config.Password)
default:
return nil, fmt.Errorf("unsupported database type: %s", config.Type)
}
db, err := sql.Open(config.Type, dsn)
if err != nil {
return nil, fmt.Errorf("failed to open database: %v", err)
}
// 测试连接
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("failed to ping database: %v", err)
}
// 设置连接池参数
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
dm := &DatabaseManager{
db: db,
config: config,
}
// 初始化数据库表
if err := dm.initTables(); err != nil {
return nil, fmt.Errorf("failed to initialize tables: %v", err)
}
return dm, nil
}
// initTables 初始化数据库表
func (dm *DatabaseManager) initTables() error {
var createTableSQL string
switch dm.config.Type {
case "mysql":
createTableSQL = `
CREATE TABLE IF NOT EXISTS cameras (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
ip VARCHAR(45) NOT NULL,
username VARCHAR(100),
password VARCHAR(100),
rtsp_url TEXT NOT NULL,
enabled BOOLEAN DEFAULT TRUE,
on_demand BOOLEAN DEFAULT FALSE,
audio BOOLEAN DEFAULT TRUE,
debug BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_enabled (enabled),
INDEX idx_ip (ip)
)`
case "sqlserver":
createTableSQL = `
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='cameras' AND xtype='U')
CREATE TABLE cameras (
id NVARCHAR(36) PRIMARY KEY,
name NVARCHAR(255) NOT NULL,
ip NVARCHAR(45) NOT NULL,
username NVARCHAR(100),
password NVARCHAR(100),
rtsp_url NTEXT NOT NULL,
enabled BIT DEFAULT 1,
on_demand BIT DEFAULT 0,
audio BIT DEFAULT 1,
debug BIT DEFAULT 0,
created_at DATETIME2 DEFAULT GETDATE(),
updated_at DATETIME2 DEFAULT GETDATE()
)`
}
_, err := dm.db.Exec(createTableSQL)
return err
}
// GetAllCameras 获取所有摄像头列表 - 适配qsc_camera表
func (dm *DatabaseManager) GetAllCameras() ([]Camera, error) {
// 首先尝试查询包含enabled列的SQL
query := `SELECT camera_id, ip, port, username, password, url, camera_produce,
camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, enabled,
del_flag, create_by, create_time, update_by, update_time, user_id, dept_id
FROM qsc_camera WHERE del_flag != '1'`
rows, err := dm.db.Query(query)
if err != nil {
// 如果查询失败可能是因为enabled列不存在尝试不包含enabled列的查询
query = `SELECT camera_id, ip, port, username, password, url, camera_produce,
camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back,
del_flag, create_by, create_time, update_by, update_time, user_id, dept_id
FROM qsc_camera WHERE del_flag != '1'`
rows, err = dm.db.Query(query)
if err != nil {
return nil, err
}
}
defer rows.Close()
var cameras []Camera
for rows.Next() {
var camera Camera
// 检查列数来决定如何扫描
columns, _ := rows.Columns()
if len(columns) == 21 { // 包含enabled列
err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username,
&camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName,
&camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath,
&camera.PlayBack, &camera.Enabled, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime,
&camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID)
if err != nil {
return nil, err
}
} else { // 不包含enabled列
camera.Enabled = true // 默认启用
err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username,
&camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName,
&camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath,
&camera.PlayBack, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime,
&camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID)
if err != nil {
return nil, err
}
}
cameras = append(cameras, camera)
}
return cameras, nil
}
// GetCamerasByUnitCode 根据unitcode获取摄像头列表 - 适配qsc_camera表
func (dm *DatabaseManager) GetCamerasByUnitCode(unitCode string) ([]Camera, error) {
// 首先尝试查询包含enabled列的SQL
query := `SELECT camera_id, ip, port, username, password, url, camera_produce,
camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, enabled,
del_flag, create_by, create_time, update_by, update_time, user_id, dept_id
FROM qsc_camera WHERE unit_code = ? AND del_flag != '1'`
rows, err := dm.db.Query(query, unitCode)
if err != nil {
// 如果查询失败可能是因为enabled列不存在尝试不包含enabled列的查询
query = `SELECT camera_id, ip, port, username, password, url, camera_produce,
camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back,
del_flag, create_by, create_time, update_by, update_time, user_id, dept_id
FROM qsc_camera WHERE unit_code = ? AND del_flag != '1'`
rows, err = dm.db.Query(query, unitCode)
if err != nil {
return nil, err
}
}
defer rows.Close()
var cameras []Camera
for rows.Next() {
var camera Camera
// 检查列数来决定如何扫描
columns, _ := rows.Columns()
if len(columns) == 21 { // 包含enabled列
err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username,
&camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName,
&camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath,
&camera.PlayBack, &camera.Enabled, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime,
&camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID)
if err != nil {
return nil, err
}
} else { // 不包含enabled列
camera.Enabled = true // 默认启用
err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username,
&camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName,
&camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath,
&camera.PlayBack, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime,
&camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID)
if err != nil {
return nil, err
}
}
cameras = append(cameras, camera)
}
return cameras, nil
}
// GetCameraByID 根据ID获取摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) GetCameraByID(id string) (*Camera, error) {
query := `SELECT camera_id, ip, port, username, password, url, camera_produce,
camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back,
del_flag, create_by, create_time, update_by, update_time, user_id, dept_id
FROM qsc_camera WHERE camera_id = ? AND del_flag = '0'`
var camera Camera
err := dm.db.QueryRow(query, id).Scan(&camera.CameraID, &camera.IP, &camera.Port,
&camera.Username, &camera.Password, &camera.URL, &camera.CameraProduce,
&camera.CameraName, &camera.DeviceType, &camera.UnitCode, &camera.NvrProduce,
&camera.NvrPath, &camera.PlayBack, &camera.DelFlag, &camera.CreateBy,
&camera.CreateTime, &camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, err
}
return &camera, nil
}
// CreateCamera 创建摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) CreateCamera(camera *Camera) error {
camera.CreateTime = sql.NullTime{Time: time.Now(), Valid: true}
camera.UpdateTime = sql.NullTime{Time: time.Now(), Valid: true}
query := `INSERT INTO qsc_camera (ip, port, username, password, url, camera_produce,
camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back,
del_flag, create_by, create_time, update_by, update_time, user_id, dept_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
_, err := dm.db.Exec(query, camera.IP, camera.Port, camera.Username, camera.Password,
camera.URL, camera.CameraProduce, camera.CameraName, camera.DeviceType,
camera.UnitCode, camera.NvrProduce, camera.NvrPath, camera.PlayBack,
camera.DelFlag, camera.CreateBy, camera.CreateTime, camera.UpdateBy,
camera.UpdateTime, camera.UserID, camera.DeptID)
return err
}
// UpdateCamera 更新摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) UpdateCamera(camera *Camera) error {
camera.UpdateTime = sql.NullTime{Time: time.Now(), Valid: true}
query := `UPDATE qsc_camera SET ip=?, port=?, username=?, password=?, url=?,
camera_produce=?, camera_name=?, device_type=?, unit_code=?, nvr_produce=?,
nvr_path=?, play_back=?, update_by=?, update_time=? WHERE camera_id=?`
_, err := dm.db.Exec(query, camera.IP, camera.Port, camera.Username, camera.Password,
camera.URL, camera.CameraProduce, camera.CameraName, camera.DeviceType,
camera.UnitCode, camera.NvrProduce, camera.NvrPath, camera.PlayBack,
camera.UpdateBy, camera.UpdateTime, camera.CameraID)
return err
}
// DeleteCamera 删除摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) DeleteCamera(id string) error {
query := `UPDATE qsc_camera SET del_flag = '1' WHERE camera_id = ?`
_, err := dm.db.Exec(query, id)
return err
}
// Close 关闭数据库连接
func (dm *DatabaseManager) Close() error {
if dm.db != nil {
return dm.db.Close()
}
return nil
}
// CameraToStream 将摄像头转换为流配置 - 适配qsc_camera表
func CameraToStream(camera Camera) StreamST {
// 构建RTSP URL
rtspURL := camera.URL
if rtspURL == "" {
// 如果URL为空根据IP、端口、用户名密码构建RTSP URL
if camera.Username.Valid && camera.Password.Valid && camera.Username.String != "" && camera.Password.String != "" {
rtspURL = fmt.Sprintf("rtsp://%s:%s@%s:%d/stream1",
camera.Username.String, camera.Password.String, camera.IP, camera.Port)
} else {
rtspURL = fmt.Sprintf("rtsp://%s:%d/stream1", camera.IP, camera.Port)
}
}
cameraName := "Unknown Camera"
if camera.CameraName.Valid {
cameraName = camera.CameraName.String
}
return StreamST{
Name: cameraName,
Channels: map[string]ChannelST{
"0": {
Name: cameraName,
URL: rtspURL,
OnDemand: false, // 根据需要设置
Debug: false, // 根据需要设置
Audio: true, // 根据需要设置
},
},
}
}
// StreamToCamera 将流配置转换为摄像头 - 适配qsc_camera表
func StreamToCamera(id string, stream StreamST) Camera {
// 创建基础摄像头对象 - 适配qsc_camera表
cameraID, _ := strconv.Atoi(id)
camera := Camera{
CameraID: cameraID,
CameraName: sql.NullString{String: stream.Name, Valid: true},
IP: "", // 从URL中解析
Port: 554, // 默认RTSP端口
Username: sql.NullString{String: "", Valid: false}, // 从URL中解析
Password: sql.NullString{String: "", Valid: false}, // 从URL中解析
URL: "", // 将从第一个通道获取
DeviceType: sql.NullString{String: "网络摄像头", Valid: true},
UnitCode: sql.NullString{String: "DEFAULT", Valid: true},
DelFlag: "0",
CreateBy: sql.NullString{String: "system", Valid: true},
CreateTime: sql.NullTime{Time: time.Now(), Valid: true},
UpdateBy: sql.NullString{String: "system", Valid: true},
UpdateTime: sql.NullTime{Time: time.Now(), Valid: true},
UserID: sql.NullInt64{Int64: 1, Valid: true},
DeptID: sql.NullInt64{Int64: 1, Valid: true},
}
// 获取第一个通道的配置
if len(stream.Channels) > 0 {
for _, channel := range stream.Channels {
camera.URL = channel.URL
break
}
}
return camera
}

@ -0,0 +1,101 @@
-- MySQL数据库初始化脚本
-- 创建数据库
CREATE DATABASE IF NOT EXISTS rtsp_cameras CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用数据库
USE rtsp_cameras;
-- 创建摄像头表
CREATE TABLE IF NOT EXISTS cameras (
id VARCHAR(36) PRIMARY KEY COMMENT '摄像头唯一标识符',
name VARCHAR(255) NOT NULL COMMENT '摄像头名称',
ip VARCHAR(45) NOT NULL COMMENT 'IP地址',
username VARCHAR(100) DEFAULT NULL COMMENT '用户名',
password VARCHAR(255) DEFAULT NULL COMMENT '密码',
rtsp_url VARCHAR(500) NOT NULL COMMENT 'RTSP URL',
enabled BOOLEAN DEFAULT TRUE COMMENT '是否启用',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_name (name),
INDEX idx_ip (ip),
INDEX idx_enabled (enabled),
INDEX idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='摄像头信息表';
-- 插入示例数据
INSERT INTO cameras (id, name, ip, username, password, rtsp_url, enabled) VALUES
('27aec28e-6181-4753-9acd-0456a75f0289', '测试摄像头1', '192.168.1.100', 'admin', 'admin123', 'rtsp://192.168.1.100:554/stream1', TRUE),
('38bfd39f-7292-5864-0bde-1567b86f1390', '测试摄像头2', '192.168.1.101', 'admin', 'admin123', 'rtsp://192.168.1.101:554/stream1', TRUE)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
ip = VALUES(ip),
username = VALUES(username),
password = VALUES(password),
rtsp_url = VALUES(rtsp_url),
enabled = VALUES(enabled),
updated_at = CURRENT_TIMESTAMP;
-- SQL Server数据库初始化脚本注释版本
/*
-- 创建数据库
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'rtsp_cameras')
BEGIN
CREATE DATABASE rtsp_cameras;
END
GO
-- 使用数据库
USE rtsp_cameras;
GO
-- 创建摄像头表
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='cameras' AND xtype='U')
BEGIN
CREATE TABLE cameras (
id NVARCHAR(36) PRIMARY KEY,
name NVARCHAR(255) NOT NULL,
ip NVARCHAR(45) NOT NULL,
username NVARCHAR(100) NULL,
password NVARCHAR(255) NULL,
rtsp_url NVARCHAR(500) NOT NULL,
enabled BIT DEFAULT 1,
created_at DATETIME2 DEFAULT GETDATE(),
updated_at DATETIME2 DEFAULT GETDATE()
);
-- 创建索引
CREATE INDEX idx_cameras_name ON cameras(name);
CREATE INDEX idx_cameras_ip ON cameras(ip);
CREATE INDEX idx_cameras_enabled ON cameras(enabled);
CREATE INDEX idx_cameras_created_at ON cameras(created_at);
END
GO
-- 创建更新触发器
IF NOT EXISTS (SELECT * FROM sys.triggers WHERE name = 'tr_cameras_update')
BEGIN
EXEC('CREATE TRIGGER tr_cameras_update ON cameras
AFTER UPDATE
AS
BEGIN
UPDATE cameras
SET updated_at = GETDATE()
WHERE id IN (SELECT id FROM inserted);
END');
END
GO
-- 插入示例数据
IF NOT EXISTS (SELECT * FROM cameras WHERE id = '27aec28e-6181-4753-9acd-0456a75f0289')
BEGIN
INSERT INTO cameras (id, name, ip, username, password, rtsp_url, enabled) VALUES
('27aec28e-6181-4753-9acd-0456a75f0289', N'测试摄像头1', N'192.168.1.100', N'admin', N'admin123', N'rtsp://192.168.1.100:554/stream1', 1);
END
IF NOT EXISTS (SELECT * FROM cameras WHERE id = '38bfd39f-7292-5864-0bde-1567b86f1390')
BEGIN
INSERT INTO cameras (id, name, ip, username, password, rtsp_url, enabled) VALUES
('38bfd39f-7292-5864-0bde-1567b86f1390', N'测试摄像头2', N'192.168.1.101', N'admin', N'admin123', N'rtsp://192.168.1.101:554/stream1', 1);
END
GO
*/

@ -0,0 +1,64 @@
package main
import (
"database/sql"
"fmt"
"os"
_ "github.com/go-sql-driver/mysql"
"github.com/sirupsen/logrus"
)
var db *sql.DB
type DBConfig struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Database string `json:"database"`
}
func initDB() error {
cfg := DBConfig{
Host: os.Getenv("DB_HOST"),
Port: 3306,
User: os.Getenv("DB_USER"),
Password: os.Getenv("DB_PASSWORD"),
Database: os.Getenv("DB_NAME"),
}
if cfg.Host == "" {
cfg.Host = "localhost"
}
if cfg.User == "" {
return fmt.Errorf("database user not configured")
}
if cfg.Password == "" {
return fmt.Errorf("database password not configured")
}
if cfg.Database == "" {
cfg.Database = "rtsp_config"
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database)
var err error
db, err = sql.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("failed to connect to database: %v", err)
}
err = db.Ping()
if err != nil {
return fmt.Errorf("failed to ping database: %v", err)
}
log.WithFields(logrus.Fields{
"module": "database",
"func": "initDB",
}).Info("Database connection established")
return nil
}

@ -1,31 +1,31 @@
# RTSPtoWeb API
* [Streams](#streams)
* [List streams](#list-streams)
* [Add a stream](#add-a-stream)
* [Update a stream](#update-a-stream)
* [Reload a stream](#reload-a-stream)
* [Get stream info](#get-stream-info)
* [Delete a stream](#delete-a-stream)
* [Channels](#channels)
* [Add a channel to a stream](#add-a-channel-to-a-stream)
* [Update a stream channel](#update-a-stream-channel)
* [Reload a stream channel](#reload-a-stream-channel)
* [Get stream channel info](#get-stream-channel-info)
* [Get stream channel codec](#get-stream-channel-codec)
* [Delete a stream channel](#delete-a-stream-channel)
* [Video endpoints](#video-endpoints)
* [流管理](#streams)
* [列出流](#list-streams)
* [添加流](#add-a-stream)
* [更新流](#update-a-stream)
* [重新加载流](#reload-a-stream)
* [获取流信息](#get-stream-info)
* [删除流](#delete-a-stream)
* [通道管理](#channels)
* [向流添加通道](#add-a-channel-to-a-stream)
* [更新流通道](#update-a-stream-channel)
* [重新加载流通道](#reload-a-stream-channel)
* [获取流通道信息](#get-stream-channel-info)
* [获取流通道编解码器](#get-stream-channel-codec)
* [删除流通道](#delete-a-stream-channel)
* [视频端点](#video-endpoints)
* [HLS](#hls)
* [HLS-LL](#hls-ll)
* [MSE](#mse)
* [WebRTC](#webrtc)
* [RTSP](#rtsp)
## Streams
## 流管理
### List streams
### 列出流
#### Request
#### 请求
`GET /streams`
@ -33,7 +33,7 @@
curl http://demo:demo@127.0.0.1:8083/streams
```
#### Response
#### 响应
```json
{
@ -81,9 +81,9 @@ curl http://demo:demo@127.0.0.1:8083/streams
}
```
### Add a stream
### 添加流
#### Request
#### 请求
`POST /stream/{STREAM_ID}/add`
@ -113,7 +113,7 @@ curl \
http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/add
```
#### Response
#### 响应
```json
{
@ -122,9 +122,9 @@ curl \
}
```
### Update a stream
### 更新流
#### Request
#### 请求
`POST /stream/{STREAM_ID}/edit`
@ -154,7 +154,7 @@ curl \
http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/edit
```
#### Response
#### 响应
```json
{
@ -163,9 +163,9 @@ curl \
}
```
### Reload a stream
### 重新加载流
#### Request
#### 请求
`GET /stream/{STREAM_ID}/reload`
@ -173,7 +173,7 @@ curl \
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/reload
```
#### Response
#### 响应
```json
{
@ -182,9 +182,9 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/reload
}
```
### Get stream info
### 获取流信息
#### Request
#### 请求
`GET /stream/{STREAM_ID}/info`
@ -192,7 +192,7 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/reload
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/info
```
#### Response
#### 响应
```json
{
@ -219,9 +219,9 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/info
}
```
### Delete a stream
### 删除流
#### Request
#### 请求
`GET /stream/{STREAM_ID}/delete`
@ -229,7 +229,7 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/info
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/delete
```
#### Response
#### 响应
```json
{
@ -238,11 +238,11 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/delete
}
```
## Channels
## 通道管理
### Add a channel to a stream
### 向流添加通道
#### Request
#### 请求
`POST /stream/{STREAM_ID}/channel/{CHANNEL_ID}/add`
@ -260,7 +260,7 @@ curl \
http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/add
```
#### Response
#### 响应
```json
{
@ -269,9 +269,9 @@ curl \
}
```
### Update a stream channel
### 更新流通道
#### Request
#### 请求
`POST /stream/{STREAM_ID}/channel/{CHANNEL_ID}/edit`
@ -289,7 +289,7 @@ curl \
http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/edit
```
#### Response
#### 响应
```json
{
@ -298,9 +298,9 @@ curl \
}
```
### Reload a stream channel
### 重新加载流通道
#### Request
#### 请求
`GET /stream/{STREAM_ID}/channel/{CHANNEL_ID}/reload`
@ -308,7 +308,7 @@ curl \
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/reload
```
#### Response
#### 响应
```json
{
@ -317,9 +317,9 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/rel
}
```
### Get stream channel info
### 获取流通道信息
#### Request
#### 请求
`GET /stream/{STREAM_ID}/channel/{CHANNEL_ID}/info`
@ -327,7 +327,7 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/rel
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/info
```
#### Response
#### 响应
```json
{
@ -342,16 +342,16 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/inf
}
```
### Get stream channel codec
### 获取流通道编解码器
#### Request
#### 请求
`GET /stream/{STREAM_ID}/{CHANNEL_ID}/codec`
```bash
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/{CHANNEL_ID}/codec
```
#### Response
#### 响应
```json
{
"status": 1,
@ -387,9 +387,9 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/{CHANNEL_ID}/codec
}
```
### Delete a stream channel
### 删除流通道
#### Request
#### 请求
`GET /stream/{STREAM_ID}/channel/{CHANNEL_ID}/delete`
@ -397,7 +397,7 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/{CHANNEL_ID}/codec
curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/delete
```
#### Response
#### 响应
```json
{
"status": 1,
@ -405,7 +405,7 @@ curl http://demo:demo@127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/del
}
```
## Video endpoints
## 视频端点
### HLS
@ -439,7 +439,7 @@ ffplay http://127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/hlsll/live/
ws://127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/mse?uuid={STREAM_ID}&channel={CHANNEL_ID}
```
NOTE: Use `wss` for a secure connection.
注意:使用 `wss` 进行安全连接。
### WebRTC
@ -451,11 +451,11 @@ http://127.0.0.1:8083/stream/{STREAM_ID}/channel/{CHANNEL_ID}/webrtc
#### Request
The request is an HTTP `POST` with a FormData parameter `data` that is a base64 encoded SDP offer (e.g. `v=0...`) from a WebRTC client.
请求是一个 HTTP `POST`,包含 FormData 参数 `data`,该参数是来自 WebRTC 客户端的 base64 编码的 SDP offer例如 `v=0...`)。
#### Response
#### 响应
The response is a base64 encoded SDP Answer.
响应是一个 base64 编码的 SDP Answer。
### RTSP

@ -4,9 +4,12 @@ go 1.19
require (
github.com/deepch/vdk v0.0.20
github.com/denisenkom/go-mssqldb v0.12.3
github.com/gin-gonic/autotls v0.0.5
github.com/gin-gonic/gin v1.9.0
github.com/go-sql-driver/mysql v1.7.1
github.com/gobwas/ws v1.1.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-version v1.6.0
github.com/imdario/mergo v0.3.15
github.com/liip/sheriff v0.11.1
@ -23,7 +26,9 @@ require (
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/goccy/go-json v0.10.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.2 // indirect

@ -1,3 +1,6 @@
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.3 h1:pf6fGl5eqWYKkx1RcD4qpuX+BIUaduv/wTm5ekWJ80M=
github.com/bytedance/sonic v1.8.3/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@ -9,6 +12,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepch/vdk v0.0.20 h1:GNQjfgapEEzaownzfRWYdQw+SqchW2yC1ha+/d0Bl24=
github.com/deepch/vdk v0.0.20/go.mod h1:774MjElr4PMgmXuTi9HXrK0nM0phMXE80W/g+vyTpAc=
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -24,6 +30,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@ -33,6 +41,10 @@ github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -42,6 +54,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk=
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -77,6 +91,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -126,6 +141,7 @@ github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
github.com/pion/webrtc/v3 v3.1.58 h1:husXqiKQuk6gbOqJlPHs185OskAyxUW6iAEgHghgCrc=
github.com/pion/webrtc/v3 v3.1.58/go.mod h1:jJdqoqGBlZiE3y8Z1tg1fjSkyEDCZLL+foypUBn0Lhk=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
@ -157,7 +173,9 @@ golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
@ -172,6 +190,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
@ -253,9 +272,11 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

@ -0,0 +1,764 @@
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 1148416 bytes. Error detail: Chunk::new
# Possible reasons:
# The system is out of physical RAM or swap space
# This process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# JVM is running with Unscaled Compressed Oops mode in which the Java heap is
# placed in the first 4GB address space. The Java Heap base address is the
# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress
# to set the Java Heap base and to place the Java Heap above 4GB virtual address.
# This output file may be truncated or incomplete.
#
# Out of Memory Error (arena.cpp:168), pid=11624, tid=17036
#
# JRE version: OpenJDK Runtime Environment Temurin-21.0.7+6 (21.0.7+6) (build 21.0.7+6-LTS)
# Java VM: OpenJDK 64-Bit Server VM Temurin-21.0.7+6 (21.0.7+6-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, parallel gc, windows-amd64)
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
--------------- S U M M A R Y ------------
Command Line: --add-modules=ALL-SYSTEM --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/sun.nio.fs=ALL-UNNAMED -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Djava.import.generatesMetadataFilesAtProjectRoot=false -DDetectVMInstallationsJob.disabled=true -Dfile.encoding=utf8 -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -Xlog:disable -javaagent:c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\lombok\lombok-1.18.36.jar c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\server\plugins\org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar -configuration c:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_ss_win -data c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java\ss_ws --pipe=\\.\pipe\lsp-32e6cc9e4aa2f1db0c4c3ebf64e64d1e-sock
Host: Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz, 4 cores, 7G, Windows 10 , 64 bit Build 19041 (10.0.19041.5438)
Time: Thu Jun 5 11:45:39 2025 Windows 10 , 64 bit Build 19041 (10.0.19041.5438) elapsed time: 33.381140 seconds (0d 0h 0m 33s)
--------------- T H R E A D ---------------
Current thread (0x0000027c97e52310): JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=17036, stack(0x000000bc70000000,0x000000bc70100000) (1024K)]
Current CompileTask:
C2:33381 4484 4 org.eclipse.jdt.internal.compiler.util.JrtFileSystem$2::visitFile (72 bytes)
Stack: [0x000000bc70000000,0x000000bc70100000]
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [jvm.dll+0x6ce119]
V [jvm.dll+0x8a84a1]
V [jvm.dll+0x8aa9ce]
V [jvm.dll+0x8ab0b3]
V [jvm.dll+0x27f8a6]
V [jvm.dll+0xc507d]
V [jvm.dll+0xc55b3]
V [jvm.dll+0x3b692c]
V [jvm.dll+0x1e0029]
V [jvm.dll+0x247c42]
V [jvm.dll+0x2470cf]
V [jvm.dll+0x1c760e]
V [jvm.dll+0x25695a]
V [jvm.dll+0x254efa]
V [jvm.dll+0x3f03f6]
V [jvm.dll+0x851f6b]
V [jvm.dll+0x6cc7dd]
C [ucrtbase.dll+0x21bb2]
C [KERNEL32.DLL+0x17374]
C [ntdll.dll+0x4cc91]
--------------- P R O C E S S ---------------
Threads class SMR info:
_java_thread_list=0x0000027cded0ed30, length=29, elements={
0x0000027c83b162f0, 0x0000027c97e3ad30, 0x0000027c97e3e760, 0x0000027c97e425a0,
0x0000027c97e434b0, 0x0000027c97e4a710, 0x0000027c97e4c170, 0x0000027c97e52310,
0x0000027c97eb3930, 0x0000027cde038090, 0x0000027cde26bcf0, 0x0000027cde7fe910,
0x0000027cde21ab70, 0x0000027cde21b1d0, 0x0000027cde0b8f10, 0x0000027cdeb7e020,
0x0000027cded29710, 0x0000027cde4e6110, 0x0000027cde4e6e30, 0x0000027cde4e46d0,
0x0000027cde4e74c0, 0x0000027cde4e4040, 0x0000027cde4e4d60, 0x0000027cde4e53f0,
0x0000027cde4e5a80, 0x0000027cde4e67a0, 0x0000027cdf312910, 0x0000027cdf313cc0,
0x0000027cded09e10
}
Java Threads: ( => current thread )
0x0000027c83b162f0 JavaThread "main" [_thread_blocked, id=12444, stack(0x000000bc6f600000,0x000000bc6f700000) (1024K)]
0x0000027c97e3ad30 JavaThread "Reference Handler" daemon [_thread_blocked, id=14844, stack(0x000000bc6fa00000,0x000000bc6fb00000) (1024K)]
0x0000027c97e3e760 JavaThread "Finalizer" daemon [_thread_blocked, id=11500, stack(0x000000bc6fb00000,0x000000bc6fc00000) (1024K)]
0x0000027c97e425a0 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=12072, stack(0x000000bc6fc00000,0x000000bc6fd00000) (1024K)]
0x0000027c97e434b0 JavaThread "Attach Listener" daemon [_thread_blocked, id=13028, stack(0x000000bc6fd00000,0x000000bc6fe00000) (1024K)]
0x0000027c97e4a710 JavaThread "Service Thread" daemon [_thread_blocked, id=7368, stack(0x000000bc6fe00000,0x000000bc6ff00000) (1024K)]
0x0000027c97e4c170 JavaThread "Monitor Deflation Thread" daemon [_thread_blocked, id=14600, stack(0x000000bc6ff00000,0x000000bc70000000) (1024K)]
=>0x0000027c97e52310 JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=17036, stack(0x000000bc70000000,0x000000bc70100000) (1024K)]
0x0000027c97eb3930 JavaThread "C1 CompilerThread0" daemon [_thread_blocked, id=17028, stack(0x000000bc70100000,0x000000bc70200000) (1024K)]
0x0000027cde038090 JavaThread "Common-Cleaner" daemon [_thread_blocked, id=4300, stack(0x000000bc70200000,0x000000bc70300000) (1024K)]
0x0000027cde26bcf0 JavaThread "Notification Thread" daemon [_thread_blocked, id=5188, stack(0x000000bc70300000,0x000000bc70400000) (1024K)]
0x0000027cde7fe910 JavaThread "Active Thread: Equinox Container: 0112b3c0-050a-48d0-9b5a-65dda4b58de3" [_thread_blocked, id=9260, stack(0x000000bc70700000,0x000000bc70800000) (1024K)]
0x0000027cde21ab70 JavaThread "Framework Event Dispatcher: Equinox Container: 0112b3c0-050a-48d0-9b5a-65dda4b58de3" daemon [_thread_blocked, id=14192, stack(0x000000bc70800000,0x000000bc70900000) (1024K)]
0x0000027cde21b1d0 JavaThread "Start Level: Equinox Container: 0112b3c0-050a-48d0-9b5a-65dda4b58de3" daemon [_thread_blocked, id=13792, stack(0x000000bc70900000,0x000000bc70a00000) (1024K)]
0x0000027cde0b8f10 JavaThread "SCR Component Actor" daemon [_thread_blocked, id=2156, stack(0x000000bc70a00000,0x000000bc70b00000) (1024K)]
0x0000027cdeb7e020 JavaThread "Worker-JM" [_thread_blocked, id=15424, stack(0x000000bc70c00000,0x000000bc70d00000) (1024K)]
0x0000027cded29710 JavaThread "Worker-0" [_thread_blocked, id=4620, stack(0x000000bc70d00000,0x000000bc70e00000) (1024K)]
0x0000027cde4e6110 JavaThread "Worker-1" [_thread_blocked, id=6008, stack(0x000000bc70e00000,0x000000bc70f00000) (1024K)]
0x0000027cde4e6e30 JavaThread "JNA Cleaner" daemon [_thread_blocked, id=15640, stack(0x000000bc70b00000,0x000000bc70c00000) (1024K)]
0x0000027cde4e46d0 JavaThread "Worker-2" [_thread_blocked, id=15664, stack(0x000000bc71100000,0x000000bc71200000) (1024K)]
0x0000027cde4e74c0 JavaThread "Thread-2" daemon [_thread_in_native, id=10800, stack(0x000000bc71200000,0x000000bc71300000) (1024K)]
0x0000027cde4e4040 JavaThread "Thread-3" daemon [_thread_in_native, id=2872, stack(0x000000bc71300000,0x000000bc71400000) (1024K)]
0x0000027cde4e4d60 JavaThread "Thread-4" daemon [_thread_in_native, id=1268, stack(0x000000bc71400000,0x000000bc71500000) (1024K)]
0x0000027cde4e53f0 JavaThread "Thread-5" daemon [_thread_in_native, id=12728, stack(0x000000bc71500000,0x000000bc71600000) (1024K)]
0x0000027cde4e5a80 JavaThread "Thread-6" daemon [_thread_in_native, id=4480, stack(0x000000bc71600000,0x000000bc71700000) (1024K)]
0x0000027cde4e67a0 JavaThread "pool-2-thread-1" [_thread_blocked, id=14796, stack(0x000000bc71700000,0x000000bc71800000) (1024K)]
0x0000027cdf312910 JavaThread "WorkspaceEventsHandler" [_thread_blocked, id=9812, stack(0x000000bc71800000,0x000000bc71900000) (1024K)]
0x0000027cdf313cc0 JavaThread "pool-1-thread-1" [_thread_in_Java, id=15736, stack(0x000000bc71900000,0x000000bc71a00000) (1024K)]
0x0000027cded09e10 JavaThread "C2 CompilerThread1" daemon [_thread_in_native, id=17400, stack(0x000000bc71a00000,0x000000bc71b00000) (1024K)]
Total: 29
Other Threads:
0x0000027c97e2e510 VMThread "VM Thread" [id=14016, stack(0x000000bc6f900000,0x000000bc6fa00000) (1024K)]
0x0000027c83b80320 WatcherThread "VM Periodic Task Thread" [id=8132, stack(0x000000bc6f800000,0x000000bc6f900000) (1024K)]
0x0000027c83b34770 WorkerThread "GC Thread#0" [id=9748, stack(0x000000bc6f700000,0x000000bc6f800000) (1024K)]
0x0000027cde487bb0 WorkerThread "GC Thread#1" [id=11000, stack(0x000000bc70400000,0x000000bc70500000) (1024K)]
0x0000027cde34f330 WorkerThread "GC Thread#2" [id=1668, stack(0x000000bc70500000,0x000000bc70600000) (1024K)]
0x0000027cde34f6d0 WorkerThread "GC Thread#3" [id=16612, stack(0x000000bc70600000,0x000000bc70700000) (1024K)]
Total: 6
Threads with active compile tasks:
C2 CompilerThread0 33432 4484 4 org.eclipse.jdt.internal.compiler.util.JrtFileSystem$2::visitFile (72 bytes)
C2 CompilerThread1 33432 4486 4 org.eclipse.jdt.internal.compiler.util.JrtFileSystem$2::visitFile (10 bytes)
Total: 2
VM state: not at safepoint (normal execution)
VM Mutex/Monitor currently owned by a thread: None
Heap address: 0x00000000c0000000, size: 1024 MB, Compressed Oops mode: 32-bit
CDS archive(s) mapped at: [0x0000027c98000000-0x0000027c98ba0000-0x0000027c98ba0000), size 12189696, SharedBaseAddress: 0x0000027c98000000, ArchiveRelocationMode: 1.
Compressed class space mapped at: 0x0000027c99000000-0x0000027cd9000000, reserved size: 1073741824
Narrow klass base: 0x0000027c98000000, Narrow klass shift: 0, Narrow klass range: 0x100000000
GC Precious Log:
CardTable entry size: 512
CPUs: 4 total, 4 available
Memory: 8096M
Large Page Support: Disabled
NUMA Support: Disabled
Compressed Oops: Enabled (32-bit)
Alignments: Space 512K, Generation 512K, Heap 2M
Heap Min Capacity: 100M
Heap Initial Capacity: 100M
Heap Max Capacity: 1G
Pre-touch: Disabled
Parallel Workers: 4
Heap:
PSYoungGen total 27648K, used 24509K [0x00000000eab00000, 0x00000000ecb80000, 0x0000000100000000)
eden space 23040K, 87% used [0x00000000eab00000,0x00000000ebeb1b48,0x00000000ec180000)
from space 4608K, 94% used [0x00000000ec700000,0x00000000ecb3dc48,0x00000000ecb80000)
to space 4608K, 0% used [0x00000000ec280000,0x00000000ec280000,0x00000000ec700000)
ParOldGen total 68608K, used 13622K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 19% used [0x00000000c0000000,0x00000000c0d4d9d8,0x00000000c4300000)
Metaspace used 32723K, committed 33664K, reserved 1114112K
class space used 3221K, committed 3648K, reserved 1048576K
Card table byte_map: [0x0000027c834c0000,0x0000027c836d0000] _byte_map_base: 0x0000027c82ec0000
Marking Bits: (ParMarkBitMap*) 0x00007fffb5ad31f0
Begin Bits: [0x0000027c95d10000, 0x0000027c96d10000)
End Bits: [0x0000027c96d10000, 0x0000027c97d10000)
Polling page: 0x0000027c81b00000
Metaspace:
Usage:
Non-class: 28.81 MB used.
Class: 3.15 MB used.
Both: 31.96 MB used.
Virtual space:
Non-class space: 64.00 MB reserved, 29.31 MB ( 46%) committed, 1 nodes.
Class space: 1.00 GB reserved, 3.56 MB ( <1%) committed, 1 nodes.
Both: 1.06 GB reserved, 32.88 MB ( 3%) committed.
Chunk freelists:
Non-Class: 2.31 MB
Class: 12.41 MB
Both: 14.72 MB
MaxMetaspaceSize: unlimited
CompressedClassSpaceSize: 1.00 GB
Initial GC threshold: 21.00 MB
Current GC threshold: 35.00 MB
CDS: on
- commit_granule_bytes: 65536.
- commit_granule_words: 8192.
- virtual_space_node_default_size: 8388608.
- enlarge_chunks_in_place: 1.
- use_allocation_guard: 0.
Internal statistics:
num_allocs_failed_limit: 3.
num_arena_births: 676.
num_arena_deaths: 14.
num_vsnodes_births: 2.
num_vsnodes_deaths: 0.
num_space_committed: 526.
num_space_uncommitted: 0.
num_chunks_returned_to_freelist: 17.
num_chunks_taken_from_freelist: 1923.
num_chunk_merges: 7.
num_chunk_splits: 1275.
num_chunks_enlarged: 816.
num_inconsistent_stats: 0.
CodeHeap 'non-profiled nmethods': size=120000Kb used=2176Kb max_used=2176Kb free=117823Kb
bounds [0x0000027c8e7e0000, 0x0000027c8ea50000, 0x0000027c95d10000]
CodeHeap 'profiled nmethods': size=120000Kb used=8989Kb max_used=8989Kb free=111010Kb
bounds [0x0000027c86d10000, 0x0000027c875e0000, 0x0000027c8e240000]
CodeHeap 'non-nmethods': size=5760Kb used=1390Kb max_used=1396Kb free=4370Kb
bounds [0x0000027c8e240000, 0x0000027c8e4b0000, 0x0000027c8e7e0000]
total_blobs=5148 nmethods=4493 adapters=559
compilation: enabled
stopped_count=0, restarted_count=0
full_count=0
Compilation events (20 events):
Event: 32.948 Thread 0x0000027c97e52310 4484 4 org.eclipse.jdt.internal.compiler.util.JrtFileSystem$2::visitFile (72 bytes)
Event: 32.951 Thread 0x0000027c97eb3930 4489 3 jdk.internal.jimage.ImageReader$SharedImageReader::handleModulesSubTree (56 bytes)
Event: 32.952 Thread 0x0000027c97eb3930 nmethod 4489 0x0000027c875d2910 code [0x0000027c875d2b20, 0x0000027c875d31a0]
Event: 32.953 Thread 0x0000027cded09e10 4488 4 java.util.ArrayList::subList (20 bytes)
Event: 32.955 Thread 0x0000027cded09e10 nmethod 4488 0x0000027c8e9f5f10 code [0x0000027c8e9f60a0, 0x0000027c8e9f6228]
Event: 32.955 Thread 0x0000027cded09e10 4487 4 java.util.ArrayList$SubList::hashCode (27 bytes)
Event: 32.960 Thread 0x0000027cded09e10 nmethod 4487 0x0000027c8e9f6390 code [0x0000027c8e9f6540, 0x0000027c8e9f6850]
Event: 32.960 Thread 0x0000027cded09e10 4490 ! 4 jdk.internal.jimage.ImageReader$SharedImageReader::imageFileAttributes (46 bytes)
Event: 32.961 Thread 0x0000027cded09e10 nmethod 4490 0x0000027c8e9f6b10 code [0x0000027c8e9f6ca0, 0x0000027c8e9f6d50]
Event: 32.961 Thread 0x0000027cded09e10 4485 4 org.eclipse.jdt.internal.core.JrtPackageFragmentRoot$1::visitFile (26 bytes)
Event: 32.965 Thread 0x0000027c97eb3930 4493 3 org.eclipse.jdt.internal.compiler.parser.Scanner::scanIdentifierOrKeywordWithBoundCheck (198 bytes)
Event: 32.966 Thread 0x0000027c97eb3930 nmethod 4493 0x0000027c875d3490 code [0x0000027c875d36c0, 0x0000027c875d3d90]
Event: 32.966 Thread 0x0000027c97eb3930 4494 3 org.eclipse.jdt.internal.compiler.util.JrtFileSystem$2::preVisitDirectory (136 bytes)
Event: 32.967 Thread 0x0000027c97eb3930 nmethod 4494 0x0000027c875d3f90 code [0x0000027c875d4220, 0x0000027c875d4d08]
Event: 33.097 Thread 0x0000027cded09e10 nmethod 4485 0x0000027c8e9f6e10 code [0x0000027c8e9f7220, 0x0000027c8e9f9a90]
Event: 33.098 Thread 0x0000027cded09e10 4491 4 jdk.internal.jimage.ImageLocation::buildName (128 bytes)
Event: 33.179 Thread 0x0000027cded09e10 nmethod 4491 0x0000027c8e9fba90 code [0x0000027c8e9fbe20, 0x0000027c8e9fdfe0]
Event: 33.179 Thread 0x0000027cded09e10 4492 4 jdk.internal.jimage.ImageReader$Resource::create (17 bytes)
Event: 33.191 Thread 0x0000027cded09e10 nmethod 4492 0x0000027c8e9ff310 code [0x0000027c8e9ff500, 0x0000027c8e9ffcf0]
Event: 33.191 Thread 0x0000027cded09e10 4486 4 org.eclipse.jdt.internal.compiler.util.JrtFileSystem$2::visitFile (10 bytes)
GC Heap History (20 events):
Event: 9.072 GC heap before
{Heap before GC invocations=2 (full 0):
PSYoungGen total 29696K, used 29022K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 100% used [0x00000000eab00000,0x00000000ec400000,0x00000000ec400000)
from space 4096K, 83% used [0x00000000ec400000,0x00000000ec757880,0x00000000ec800000)
to space 4096K, 0% used [0x00000000ec800000,0x00000000ec800000,0x00000000ecc00000)
ParOldGen total 68608K, used 0K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000c4300000)
Metaspace used 8291K, committed 8640K, reserved 1114112K
class space used 852K, committed 1024K, reserved 1048576K
}
Event: 9.078 GC heap after
{Heap after GC invocations=2 (full 0):
PSYoungGen total 29696K, used 4076K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec400000)
from space 4096K, 99% used [0x00000000ec800000,0x00000000ecbfb1f8,0x00000000ecc00000)
to space 4096K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec800000)
ParOldGen total 68608K, used 1055K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 1% used [0x00000000c0000000,0x00000000c0107ea8,0x00000000c4300000)
Metaspace used 8291K, committed 8640K, reserved 1114112K
class space used 852K, committed 1024K, reserved 1048576K
}
Event: 12.327 GC heap before
{Heap before GC invocations=3 (full 0):
PSYoungGen total 29696K, used 29662K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 99% used [0x00000000eab00000,0x00000000ec3fc708,0x00000000ec400000)
from space 4096K, 99% used [0x00000000ec800000,0x00000000ecbfb1f8,0x00000000ecc00000)
to space 4096K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec800000)
ParOldGen total 68608K, used 1055K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 1% used [0x00000000c0000000,0x00000000c0107ea8,0x00000000c4300000)
Metaspace used 13081K, committed 13632K, reserved 1114112K
class space used 1341K, committed 1600K, reserved 1048576K
}
Event: 12.337 GC heap after
{Heap after GC invocations=3 (full 0):
PSYoungGen total 29696K, used 4091K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec400000)
from space 4096K, 99% used [0x00000000ec400000,0x00000000ec7fef28,0x00000000ec800000)
to space 4096K, 0% used [0x00000000ec800000,0x00000000ec800000,0x00000000ecc00000)
ParOldGen total 68608K, used 2917K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 4% used [0x00000000c0000000,0x00000000c02d9728,0x00000000c4300000)
Metaspace used 13081K, committed 13632K, reserved 1114112K
class space used 1341K, committed 1600K, reserved 1048576K
}
Event: 13.328 GC heap before
{Heap before GC invocations=4 (full 0):
PSYoungGen total 29696K, used 29598K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 99% used [0x00000000eab00000,0x00000000ec3e8ab8,0x00000000ec400000)
from space 4096K, 99% used [0x00000000ec400000,0x00000000ec7fef28,0x00000000ec800000)
to space 4096K, 0% used [0x00000000ec800000,0x00000000ec800000,0x00000000ecc00000)
ParOldGen total 68608K, used 2917K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 4% used [0x00000000c0000000,0x00000000c02d9728,0x00000000c4300000)
Metaspace used 16061K, committed 16640K, reserved 1114112K
class space used 1625K, committed 1856K, reserved 1048576K
}
Event: 13.335 GC heap after
{Heap after GC invocations=4 (full 0):
PSYoungGen total 29696K, used 4081K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec400000)
from space 4096K, 99% used [0x00000000ec800000,0x00000000ecbfc468,0x00000000ecc00000)
to space 4096K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec800000)
ParOldGen total 68608K, used 5699K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 8% used [0x00000000c0000000,0x00000000c0590ff0,0x00000000c4300000)
Metaspace used 16061K, committed 16640K, reserved 1114112K
class space used 1625K, committed 1856K, reserved 1048576K
}
Event: 15.357 GC heap before
{Heap before GC invocations=5 (full 0):
PSYoungGen total 29696K, used 29120K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 25600K, 97% used [0x00000000eab00000,0x00000000ec373d68,0x00000000ec400000)
from space 4096K, 99% used [0x00000000ec800000,0x00000000ecbfc468,0x00000000ecc00000)
to space 4096K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec800000)
ParOldGen total 68608K, used 5699K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 8% used [0x00000000c0000000,0x00000000c0590ff0,0x00000000c4300000)
Metaspace used 20218K, committed 20800K, reserved 1114112K
class space used 1924K, committed 2176K, reserved 1048576K
}
Event: 15.362 GC heap after
{Heap after GC invocations=5 (full 0):
PSYoungGen total 29184K, used 4077K [0x00000000eab00000, 0x00000000ece80000, 0x0000000100000000)
eden space 25088K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec380000)
from space 4096K, 99% used [0x00000000ec400000,0x00000000ec7fb7e0,0x00000000ec800000)
to space 5632K, 0% used [0x00000000ec900000,0x00000000ec900000,0x00000000ece80000)
ParOldGen total 68608K, used 6231K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 9% used [0x00000000c0000000,0x00000000c0615e58,0x00000000c4300000)
Metaspace used 20218K, committed 20800K, reserved 1114112K
class space used 1924K, committed 2176K, reserved 1048576K
}
Event: 17.749 GC heap before
{Heap before GC invocations=6 (full 0):
PSYoungGen total 29184K, used 23164K [0x00000000eab00000, 0x00000000ece80000, 0x0000000100000000)
eden space 25088K, 76% used [0x00000000eab00000,0x00000000ebda3940,0x00000000ec380000)
from space 4096K, 99% used [0x00000000ec400000,0x00000000ec7fb7e0,0x00000000ec800000)
to space 5632K, 0% used [0x00000000ec900000,0x00000000ec900000,0x00000000ece80000)
ParOldGen total 68608K, used 6231K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 9% used [0x00000000c0000000,0x00000000c0615e58,0x00000000c4300000)
Metaspace used 20957K, committed 21504K, reserved 1114112K
class space used 1962K, committed 2176K, reserved 1048576K
}
Event: 17.755 GC heap after
{Heap after GC invocations=6 (full 0):
PSYoungGen total 30720K, used 5364K [0x00000000eab00000, 0x00000000ece80000, 0x0000000100000000)
eden space 25088K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec380000)
from space 5632K, 95% used [0x00000000ec900000,0x00000000ece3d318,0x00000000ece80000)
to space 5632K, 0% used [0x00000000ec380000,0x00000000ec380000,0x00000000ec900000)
ParOldGen total 68608K, used 7020K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 10% used [0x00000000c0000000,0x00000000c06db368,0x00000000c4300000)
Metaspace used 20957K, committed 21504K, reserved 1114112K
class space used 1962K, committed 2176K, reserved 1048576K
}
Event: 17.755 GC heap before
{Heap before GC invocations=7 (full 1):
PSYoungGen total 30720K, used 5364K [0x00000000eab00000, 0x00000000ece80000, 0x0000000100000000)
eden space 25088K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec380000)
from space 5632K, 95% used [0x00000000ec900000,0x00000000ece3d318,0x00000000ece80000)
to space 5632K, 0% used [0x00000000ec380000,0x00000000ec380000,0x00000000ec900000)
ParOldGen total 68608K, used 7020K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 10% used [0x00000000c0000000,0x00000000c06db368,0x00000000c4300000)
Metaspace used 20957K, committed 21504K, reserved 1114112K
class space used 1962K, committed 2176K, reserved 1048576K
}
Event: 17.818 GC heap after
{Heap after GC invocations=7 (full 1):
PSYoungGen total 30720K, used 0K [0x00000000eab00000, 0x00000000ece80000, 0x0000000100000000)
eden space 25088K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec380000)
from space 5632K, 0% used [0x00000000ec900000,0x00000000ec900000,0x00000000ece80000)
to space 5632K, 0% used [0x00000000ec380000,0x00000000ec380000,0x00000000ec900000)
ParOldGen total 68608K, used 10198K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 14% used [0x00000000c0000000,0x00000000c09f5970,0x00000000c4300000)
Metaspace used 20944K, committed 21504K, reserved 1114112K
class space used 1958K, committed 2176K, reserved 1048576K
}
Event: 20.798 GC heap before
{Heap before GC invocations=8 (full 1):
PSYoungGen total 30720K, used 25087K [0x00000000eab00000, 0x00000000ece80000, 0x0000000100000000)
eden space 25088K, 99% used [0x00000000eab00000,0x00000000ec37fff8,0x00000000ec380000)
from space 5632K, 0% used [0x00000000ec900000,0x00000000ec900000,0x00000000ece80000)
to space 5632K, 0% used [0x00000000ec380000,0x00000000ec380000,0x00000000ec900000)
ParOldGen total 68608K, used 10198K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 14% used [0x00000000c0000000,0x00000000c09f5970,0x00000000c4300000)
Metaspace used 25394K, committed 26112K, reserved 1114112K
class space used 2409K, committed 2688K, reserved 1048576K
}
Event: 20.801 GC heap after
{Heap after GC invocations=8 (full 1):
PSYoungGen total 27136K, used 2247K [0x00000000eab00000, 0x00000000eca00000, 0x0000000100000000)
eden space 24576K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec300000)
from space 2560K, 87% used [0x00000000ec380000,0x00000000ec5b1d50,0x00000000ec600000)
to space 3584K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000eca00000)
ParOldGen total 68608K, used 10206K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 14% used [0x00000000c0000000,0x00000000c09f7970,0x00000000c4300000)
Metaspace used 25394K, committed 26112K, reserved 1114112K
class space used 2409K, committed 2688K, reserved 1048576K
}
Event: 23.953 GC heap before
{Heap before GC invocations=9 (full 1):
PSYoungGen total 27136K, used 26823K [0x00000000eab00000, 0x00000000eca00000, 0x0000000100000000)
eden space 24576K, 100% used [0x00000000eab00000,0x00000000ec300000,0x00000000ec300000)
from space 2560K, 87% used [0x00000000ec380000,0x00000000ec5b1d50,0x00000000ec600000)
to space 3584K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000eca00000)
ParOldGen total 68608K, used 10206K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 14% used [0x00000000c0000000,0x00000000c09f7970,0x00000000c4300000)
Metaspace used 28421K, committed 29184K, reserved 1114112K
class space used 2777K, committed 3136K, reserved 1048576K
}
Event: 23.957 GC heap after
{Heap after GC invocations=9 (full 1):
PSYoungGen total 27648K, used 3517K [0x00000000eab00000, 0x00000000eca80000, 0x0000000100000000)
eden space 24064K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec280000)
from space 3584K, 98% used [0x00000000ec680000,0x00000000ec9ef698,0x00000000eca00000)
to space 4096K, 0% used [0x00000000ec280000,0x00000000ec280000,0x00000000ec680000)
ParOldGen total 68608K, used 10214K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 14% used [0x00000000c0000000,0x00000000c09f9970,0x00000000c4300000)
Metaspace used 28421K, committed 29184K, reserved 1114112K
class space used 2777K, committed 3136K, reserved 1048576K
}
Event: 31.014 GC heap before
{Heap before GC invocations=10 (full 1):
PSYoungGen total 27648K, used 27581K [0x00000000eab00000, 0x00000000eca80000, 0x0000000100000000)
eden space 24064K, 100% used [0x00000000eab00000,0x00000000ec280000,0x00000000ec280000)
from space 3584K, 98% used [0x00000000ec680000,0x00000000ec9ef698,0x00000000eca00000)
to space 4096K, 0% used [0x00000000ec280000,0x00000000ec280000,0x00000000ec680000)
ParOldGen total 68608K, used 10214K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 14% used [0x00000000c0000000,0x00000000c09f9970,0x00000000c4300000)
Metaspace used 31607K, committed 32512K, reserved 1114112K
class space used 3097K, committed 3520K, reserved 1048576K
}
Event: 31.022 GC heap after
{Heap after GC invocations=10 (full 1):
PSYoungGen total 27648K, used 4091K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 23552K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec200000)
from space 4096K, 99% used [0x00000000ec280000,0x00000000ec67ec58,0x00000000ec680000)
to space 5120K, 0% used [0x00000000ec700000,0x00000000ec700000,0x00000000ecc00000)
ParOldGen total 68608K, used 10668K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 15% used [0x00000000c0000000,0x00000000c0a6b118,0x00000000c4300000)
Metaspace used 31607K, committed 32512K, reserved 1114112K
class space used 3097K, committed 3520K, reserved 1048576K
}
Event: 32.317 GC heap before
{Heap before GC invocations=11 (full 1):
PSYoungGen total 27648K, used 27643K [0x00000000eab00000, 0x00000000ecc00000, 0x0000000100000000)
eden space 23552K, 100% used [0x00000000eab00000,0x00000000ec200000,0x00000000ec200000)
from space 4096K, 99% used [0x00000000ec280000,0x00000000ec67ec58,0x00000000ec680000)
to space 5120K, 0% used [0x00000000ec700000,0x00000000ec700000,0x00000000ecc00000)
ParOldGen total 68608K, used 10668K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 15% used [0x00000000c0000000,0x00000000c0a6b118,0x00000000c4300000)
Metaspace used 32357K, committed 33344K, reserved 1114112K
class space used 3166K, committed 3584K, reserved 1048576K
}
Event: 32.325 GC heap after
{Heap after GC invocations=11 (full 1):
PSYoungGen total 27648K, used 4343K [0x00000000eab00000, 0x00000000ecb80000, 0x0000000100000000)
eden space 23040K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec180000)
from space 4608K, 94% used [0x00000000ec700000,0x00000000ecb3dc48,0x00000000ecb80000)
to space 4608K, 0% used [0x00000000ec280000,0x00000000ec280000,0x00000000ec700000)
ParOldGen total 68608K, used 13622K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 19% used [0x00000000c0000000,0x00000000c0d4d9d8,0x00000000c4300000)
Metaspace used 32357K, committed 33344K, reserved 1114112K
class space used 3166K, committed 3584K, reserved 1048576K
}
Dll operation events (12 events):
Event: 0.126 Loaded shared library c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\java.dll
Event: 0.656 Loaded shared library c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\zip.dll
Event: 1.219 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\instrument.dll
Event: 1.233 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\net.dll
Event: 1.239 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\nio.dll
Event: 1.249 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\zip.dll
Event: 1.443 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\jimage.dll
Event: 1.926 Loaded shared library c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\verify.dll
Event: 8.891 Loaded shared library C:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_ss_win\org.eclipse.equinox.launcher\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.1300.v20250331-1702\eclipse_11911.dll
Event: 12.430 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management.dll
Event: 12.446 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management_ext.dll
Event: 20.062 Loaded shared library C:\Temp\jna-74475\jna10110563686076366555.dll
Deoptimization events (20 events):
Event: 31.563 Thread 0x0000027cde4e46d0 Uncommon trap: trap_request=0xffffff45 fr.pc=0x0000027c8e946d4c relative=0x00000000000001ac
Event: 31.563 Thread 0x0000027cde4e46d0 Uncommon trap: reason=unstable_if action=reinterpret pc=0x0000027c8e946d4c method=jdk.internal.org.objectweb.asm.Frame.push(I)V @ 26 c2
Event: 31.563 Thread 0x0000027cde4e46d0 DEOPT PACKING pc=0x0000027c8e946d4c sp=0x000000bc711fd0b0
Event: 31.563 Thread 0x0000027cde4e46d0 DEOPT UNPACKING pc=0x0000027c8e293aa2 sp=0x000000bc711fd038 mode 2
Event: 32.334 Thread 0x0000027cdf313cc0 Uncommon trap: trap_request=0xffffff45 fr.pc=0x0000027c8e9cb9fc relative=0x00000000000011fc
Event: 32.334 Thread 0x0000027cdf313cc0 Uncommon trap: reason=unstable_if action=reinterpret pc=0x0000027c8e9cb9fc method=org.eclipse.jdt.internal.core.util.Util.splitOn(CLjava/lang/String;II)[Ljava/lang/String; @ 21 c2
Event: 32.334 Thread 0x0000027cdf313cc0 DEOPT PACKING pc=0x0000027c8e9cb9fc sp=0x000000bc719fca60
Event: 32.334 Thread 0x0000027cdf313cc0 DEOPT UNPACKING pc=0x0000027c8e293aa2 sp=0x000000bc719fca28 mode 2
Event: 32.733 Thread 0x0000027cdf313cc0 Uncommon trap: trap_request=0xffffffc6 fr.pc=0x0000027c8e9ef8ac relative=0x000000000000068c
Event: 32.733 Thread 0x0000027cdf313cc0 Uncommon trap: reason=bimorphic_or_optimized_type_check action=maybe_recompile pc=0x0000027c8e9ef8ac method=java.util.ArrayList.equalsRange(Ljava/util/List;II)Z @ 22 c2
Event: 32.733 Thread 0x0000027cdf313cc0 DEOPT PACKING pc=0x0000027c8e9ef8ac sp=0x000000bc719fcdf0
Event: 32.733 Thread 0x0000027cdf313cc0 DEOPT UNPACKING pc=0x0000027c8e293aa2 sp=0x000000bc719fcd20 mode 2
Event: 32.733 Thread 0x0000027cdf313cc0 Uncommon trap: trap_request=0xffffff76 fr.pc=0x0000027c8e9d9474 relative=0x0000000000000454
Event: 32.733 Thread 0x0000027cdf313cc0 Uncommon trap: reason=predicate action=maybe_recompile pc=0x0000027c8e9d9474 method=java.util.ArrayList.equalsRange(Ljava/util/List;II)Z @ 31 c2
Event: 32.733 Thread 0x0000027cdf313cc0 DEOPT PACKING pc=0x0000027c8e9d9474 sp=0x000000bc719fcd80
Event: 32.733 Thread 0x0000027cdf313cc0 DEOPT UNPACKING pc=0x0000027c8e293aa2 sp=0x000000bc719fcd08 mode 2
Event: 32.734 Thread 0x0000027cdf313cc0 Uncommon trap: trap_request=0xffffff76 fr.pc=0x0000027c8e9ef744 relative=0x0000000000000524
Event: 32.734 Thread 0x0000027cdf313cc0 Uncommon trap: reason=predicate action=maybe_recompile pc=0x0000027c8e9ef744 method=java.util.ArrayList.equalsRange(Ljava/util/List;II)Z @ 31 c2
Event: 32.734 Thread 0x0000027cdf313cc0 DEOPT PACKING pc=0x0000027c8e9ef744 sp=0x000000bc719fcdf0
Event: 32.734 Thread 0x0000027cdf313cc0 DEOPT UNPACKING pc=0x0000027c8e293aa2 sp=0x000000bc719fcd18 mode 2
Classes loaded (20 events):
Event: 31.464 Loading class java/lang/invoke/MethodHandleImpl$Makers$2
Event: 31.464 Loading class java/lang/invoke/MethodHandleImpl$Makers$2 done
Event: 31.464 Loading class java/lang/invoke/MethodHandleImpl$Makers$3
Event: 31.464 Loading class java/lang/invoke/MethodHandleImpl$Makers$3 done
Event: 31.468 Loading class java/lang/runtime/ObjectMethods$1
Event: 31.468 Loading class java/lang/runtime/ObjectMethods$1 done
Event: 31.531 Loading class java/util/stream/Nodes$CollectorTask$OfRef
Event: 31.532 Loading class java/util/stream/Nodes$CollectorTask
Event: 31.533 Loading class java/util/stream/Nodes$CollectorTask done
Event: 31.533 Loading class java/util/stream/Nodes$CollectorTask$OfRef done
Event: 31.533 Loading class java/util/function/LongFunction
Event: 31.533 Loading class java/util/function/LongFunction done
Event: 31.534 Loading class java/util/stream/Nodes$ConcNode
Event: 31.534 Loading class java/util/stream/Nodes$AbstractConcNode
Event: 31.534 Loading class java/util/stream/Nodes$AbstractConcNode done
Event: 31.534 Loading class java/util/stream/Nodes$ConcNode done
Event: 32.852 Loading class java/nio/channels/Channels$ReadableByteChannelImpl
Event: 32.852 Loading class java/nio/channels/Channels$ReadableByteChannelImpl done
Event: 32.853 Loading class java/nio/channels/NonWritableChannelException
Event: 32.853 Loading class java/nio/channels/NonWritableChannelException done
Classes unloaded (7 events):
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c9918b000 'java/lang/invoke/LambdaForm$MH+0x0000027c9918b000'
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c9918ac00 'java/lang/invoke/LambdaForm$MH+0x0000027c9918ac00'
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c9918a800 'java/lang/invoke/LambdaForm$MH+0x0000027c9918a800'
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c9918a400 'java/lang/invoke/LambdaForm$MH+0x0000027c9918a400'
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c9918a000 'java/lang/invoke/LambdaForm$BMH+0x0000027c9918a000'
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c99189c00 'java/lang/invoke/LambdaForm$DMH+0x0000027c99189c00'
Event: 17.770 Thread 0x0000027c97e2e510 Unloading class 0x0000027c99188c00 'java/lang/invoke/LambdaForm$DMH+0x0000027c99188c00'
Classes redefined (0 events):
No events
Internal exceptions (20 events):
Event: 29.794 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/ClassNotFoundException'{0x00000000ebe59a10}: com/sun/org/apache/xerces/internal/impl/msg/spi/XMLSerializerMessagesProvider> (0x00000000ebe59a10)
thrown [s\src\hotspot\share\classfile\systemDictionary.cpp, line 312]
Event: 29.857 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/ClassNotFoundException'{0x00000000ebe73d48}: com/sun/org/apache/xerces/internal/impl/msg/spi/XMLMessagesProvider> (0x00000000ebe73d48)
thrown [s\src\hotspot\share\classfile\systemDictionary.cpp, line 312]
Event: 30.842 Thread 0x0000027cdf313cc0 Implicit null exception at 0x0000027c8e9658f7 to 0x0000027c8e965b68
Event: 30.847 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000ec1acf88}: 'java.lang.Object java.lang.invoke.DelegatingMethodHandle$Holder.reinvoke_L(java.lang.Object, int, long, java.lang.Object, java.lang.Object, int, java.lang.Object)'> (0x00000000ec1acf88)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.248 Thread 0x0000027cde4e6110 Exception <a 'sun/nio/fs/WindowsException'{0x00000000eae9bc18}> (0x00000000eae9bc18)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 31.249 Thread 0x0000027cde4e6110 Exception <a 'sun/nio/fs/WindowsException'{0x00000000eae9d560}> (0x00000000eae9d560)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 31.252 Thread 0x0000027cde4e6110 Exception <a 'java/io/FileNotFoundException'{0x00000000eaeac030}> (0x00000000eaeac030)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 31.260 Thread 0x0000027cde4e6110 Exception <a 'java/io/FileNotFoundException'{0x00000000eaf8dc28}> (0x00000000eaf8dc28)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 31.260 Thread 0x0000027cde4e6110 Exception <a 'java/io/FileNotFoundException'{0x00000000eaf8ef30}> (0x00000000eaf8ef30)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 31.261 Thread 0x0000027cde4e6110 Exception <a 'java/io/FileNotFoundException'{0x00000000eaf8fe68}> (0x00000000eaf8fe68)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 31.462 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb20b1e0}: 'java.lang.Object java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eb20b1e0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.463 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb210328}: 'java.lang.Object java.lang.invoke.Invokers$Holder.invoke_MT(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eb210328)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.468 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb272698}: 'int java.lang.invoke.DirectMethodHandle$Holder.invokeVirtual(java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eb272698)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.469 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb276f70}: 'int java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, int, int)'> (0x00000000eb276f70)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.473 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb27e718}: 'int java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, long, long)'> (0x00000000eb27e718)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.475 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb285460}: 'int java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, float, float)'> (0x00000000eb285460)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.478 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb28c1a0}: 'int java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, double, double)'> (0x00000000eb28c1a0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.479 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb2909f0}: 'int java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(java.lang.Object, long)'> (0x00000000eb2909f0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.483 Thread 0x0000027cdf313cc0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb2af6c8}: 'int java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object, java.lang.Object)'> (0x00000000eb2af6c8)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.564 Thread 0x0000027cde4e46d0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb34e1c0}: 'java.lang.Object java.lang.invoke.DirectMethodHandle$Holder.newInvokeSpecial(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eb34e1c0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
ZGC Phase Switch (0 events):
No events
VM Operations (20 events):
Event: 29.005 Executing VM operation: Cleanup
Event: 29.021 Executing VM operation: Cleanup done
Event: 30.030 Executing VM operation: Cleanup
Event: 30.116 Executing VM operation: Cleanup done
Event: 30.167 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 30.167 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 30.214 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 30.214 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 30.884 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 30.884 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 30.885 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 30.885 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 31.014 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure)
Event: 31.022 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure) done
Event: 31.198 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 31.220 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 32.231 Executing VM operation: Cleanup
Event: 32.292 Executing VM operation: Cleanup done
Event: 32.317 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure)
Event: 32.325 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure) done
Memory protections (0 events):
No events
Nmethod flushes (0 events):
No events
Events (20 events):
Event: 12.794 Thread 0x0000027cde21b1d0 Thread added: 0x0000027cded29710
Event: 12.843 Thread 0x0000027cded29710 Thread added: 0x0000027cde4e6110
Event: 16.251 Thread 0x0000027cde21b1d0 Thread added: 0x0000027cde4e4040
Event: 17.918 Thread 0x0000027cde397580 Thread exited: 0x0000027cde397580
Event: 18.680 Thread 0x0000027cde4e4040 Thread exited: 0x0000027cde4e4040
Event: 20.101 Thread 0x0000027cde21b1d0 Thread added: 0x0000027cde4e6e30
Event: 20.547 Thread 0x0000027cded29710 Thread added: 0x0000027cde4e46d0
Event: 20.821 Thread 0x0000027c83b162f0 Thread added: 0x0000027cde4e74c0
Event: 20.821 Thread 0x0000027c83b162f0 Thread added: 0x0000027cde4e4040
Event: 20.821 Thread 0x0000027c83b162f0 Thread added: 0x0000027cde4e4d60
Event: 20.822 Thread 0x0000027c83b162f0 Thread added: 0x0000027cde4e53f0
Event: 20.822 Thread 0x0000027c83b162f0 Thread added: 0x0000027cde4e5a80
Event: 20.839 Thread 0x0000027c83b162f0 Thread added: 0x0000027cde4e67a0
Event: 23.135 Thread 0x0000027c83b162f0 Thread added: 0x0000027cdf312910
Event: 23.135 Thread 0x0000027c83b162f0 Thread added: 0x0000027cdf313cc0
Event: 31.463 Thread 0x0000027cde4e46d0 Thread added: 0x0000027cdf319210
Event: 31.535 Thread 0x0000027cdf319210 Thread exited: 0x0000027cdf319210
Event: 32.327 Thread 0x0000027c97e52310 Thread added: 0x0000027cded09e10
Event: 32.932 Thread 0x0000027cded09e10 Thread exited: 0x0000027cded09e10
Event: 32.952 Thread 0x0000027c97eb3930 Thread added: 0x0000027cded09e10
Dynamic libraries:
0x00007ff644b60000 - 0x00007ff644b6e000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\java.exe
0x00007ff804d70000 - 0x00007ff804f68000 C:\Windows\SYSTEM32\ntdll.dll
0x00007ff803fd0000 - 0x00007ff804092000 C:\Windows\System32\KERNEL32.DLL
0x00007ff802990000 - 0x00007ff802c8f000 C:\Windows\System32\KERNELBASE.dll
0x00007ff802c90000 - 0x00007ff802d90000 C:\Windows\System32\ucrtbase.dll
0x00007ffffd9d0000 - 0x00007ffffd9e8000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\jli.dll
0x00007ff8045c0000 - 0x00007ff80475d000 C:\Windows\System32\USER32.dll
0x00007ff802960000 - 0x00007ff802982000 C:\Windows\System32\win32u.dll
0x00007ff8033b0000 - 0x00007ff8033db000 C:\Windows\System32\GDI32.dll
0x00007ff8025b0000 - 0x00007ff8026ca000 C:\Windows\System32\gdi32full.dll
0x00007ff8028c0000 - 0x00007ff80295d000 C:\Windows\System32\msvcp_win.dll
0x00007ffff6560000 - 0x00007ffff67fa000 C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.19041.4355_none_60b8b9eb71f62e16\COMCTL32.dll
0x00007ff803bc0000 - 0x00007ff803c5e000 C:\Windows\System32\msvcrt.dll
0x00007ffffd8e0000 - 0x00007ffffd8fe000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\VCRUNTIME140.dll
0x00007ff804c60000 - 0x00007ff804c8f000 C:\Windows\System32\IMM32.DLL
0x00007ffffe0c0000 - 0x00007ffffe0cc000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\vcruntime140_1.dll
0x00007ffffaf30000 - 0x00007ffffafbd000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\msvcp140.dll
0x00007fffb4e20000 - 0x00007fffb5bb0000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\server\jvm.dll
0x00007ff8030e0000 - 0x00007ff80318f000 C:\Windows\System32\ADVAPI32.dll
0x00007ff804c90000 - 0x00007ff804d2f000 C:\Windows\System32\sechost.dll
0x00007ff802fb0000 - 0x00007ff8030d3000 C:\Windows\System32\RPCRT4.dll
0x00007ff802480000 - 0x00007ff8024a7000 C:\Windows\System32\bcrypt.dll
0x00007ff8033e0000 - 0x00007ff80344b000 C:\Windows\System32\WS2_32.dll
0x00007ff802270000 - 0x00007ff8022bb000 C:\Windows\SYSTEM32\POWRPROF.dll
0x00007ffff5f20000 - 0x00007ffff5f47000 C:\Windows\SYSTEM32\WINMM.dll
0x00007ffff7870000 - 0x00007ffff787a000 C:\Windows\SYSTEM32\VERSION.dll
0x00007ff802250000 - 0x00007ff802262000 C:\Windows\SYSTEM32\UMPDC.dll
0x00007ff800c80000 - 0x00007ff800c92000 C:\Windows\SYSTEM32\kernel.appcore.dll
0x00007ffffdab0000 - 0x00007ffffdaba000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\jimage.dll
0x00007fffffe00000 - 0x00007ffffffe4000 C:\Windows\SYSTEM32\DBGHELP.DLL
0x00007ffff1540000 - 0x00007ffff1574000 C:\Windows\SYSTEM32\dbgcore.DLL
0x00007ff8026d0000 - 0x00007ff802752000 C:\Windows\System32\bcryptPrimitives.dll
0x00007ffffd0c0000 - 0x00007ffffd0cf000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\instrument.dll
0x00007ffffaee0000 - 0x00007ffffaeff000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\java.dll
0x00007ff803450000 - 0x00007ff803bbd000 C:\Windows\System32\SHELL32.dll
0x00007ff8001b0000 - 0x00007ff800954000 C:\Windows\SYSTEM32\windows.storage.dll
0x00007ff8040a0000 - 0x00007ff8043f5000 C:\Windows\System32\combase.dll
0x00007ff801d70000 - 0x00007ff801d9b000 C:\Windows\SYSTEM32\Wldp.dll
0x00007ff804490000 - 0x00007ff80455d000 C:\Windows\System32\OLEAUT32.dll
0x00007ff802e20000 - 0x00007ff802ecd000 C:\Windows\System32\SHCORE.dll
0x00007ff803e50000 - 0x00007ff803ea5000 C:\Windows\System32\shlwapi.dll
0x00007ff802340000 - 0x00007ff802365000 C:\Windows\SYSTEM32\profapi.dll
0x00007ffffb2b0000 - 0x00007ffffb2c8000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\zip.dll
0x00007ffffb270000 - 0x00007ffffb280000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\net.dll
0x00007ffffcd70000 - 0x00007ffffce7a000 C:\Windows\SYSTEM32\WINHTTP.dll
0x00007ff801ad0000 - 0x00007ff801b3c000 C:\Windows\system32\mswsock.dll
0x00007ffffaf10000 - 0x00007ffffaf26000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\nio.dll
0x00007ffffb260000 - 0x00007ffffb270000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\verify.dll
0x00007ffffadb0000 - 0x00007ffffadf5000 C:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_ss_win\org.eclipse.equinox.launcher\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.1300.v20250331-1702\eclipse_11911.dll
0x00007ff803c70000 - 0x00007ff803d9b000 C:\Windows\System32\ole32.dll
0x00007ffffe270000 - 0x00007ffffe27a000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management.dll
0x00007ffffe120000 - 0x00007ffffe12b000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management_ext.dll
0x00007ff803190000 - 0x00007ff803198000 C:\Windows\System32\PSAPI.DLL
0x00007ff801cd0000 - 0x00007ff801ce8000 C:\Windows\SYSTEM32\CRYPTSP.dll
0x00007ff8013f0000 - 0x00007ff801428000 C:\Windows\system32\rsaenh.dll
0x00007ff802300000 - 0x00007ff80232e000 C:\Windows\SYSTEM32\USERENV.dll
0x00007ff801cc0000 - 0x00007ff801ccc000 C:\Windows\SYSTEM32\CRYPTBASE.dll
0x00007ff8017b0000 - 0x00007ff8017eb000 C:\Windows\SYSTEM32\IPHLPAPI.DLL
0x00007ff802e10000 - 0x00007ff802e18000 C:\Windows\System32\NSI.dll
0x00007ffffb2e0000 - 0x00007ffffb329000 C:\Temp\jna-74475\jna10110563686076366555.dll
0x00007ffffa5c0000 - 0x00007ffffa5d7000 C:\Windows\SYSTEM32\dhcpcsvc6.DLL
0x00007ffffa620000 - 0x00007ffffa63d000 C:\Windows\SYSTEM32\dhcpcsvc.DLL
dbghelp: loaded successfully - version: 4.0.5 - missing functions: none
symbol engine: initialized successfully - sym options: 0x614 - pdb path: .;c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin;C:\Windows\SYSTEM32;C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.19041.4355_none_60b8b9eb71f62e16;c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\server;C:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_ss_win\org.eclipse.equinox.launcher\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.1300.v20250331-1702;C:\Temp\jna-74475
VM Arguments:
jvm_args: --add-modules=ALL-SYSTEM --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/sun.nio.fs=ALL-UNNAMED -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Djava.import.generatesMetadataFilesAtProjectRoot=false -DDetectVMInstallationsJob.disabled=true -Dfile.encoding=utf8 -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -Xlog:disable -javaagent:c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\lombok\lombok-1.18.36.jar
java_command: c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\server\plugins\org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar -configuration c:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_ss_win -data c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java\ss_ws --pipe=\\.\pipe\lsp-32e6cc9e4aa2f1db0c4c3ebf64e64d1e-sock
java_class_path (initial): c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\server\plugins\org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar
Launcher Type: SUN_STANDARD
[Global flags]
uintx AdaptiveSizePolicyWeight = 90 {product} {command line}
intx CICompilerCount = 3 {product} {ergonomic}
uintx GCTimeRatio = 4 {product} {command line}
size_t InitialHeapSize = 104857600 {product} {command line}
size_t MaxHeapSize = 1073741824 {product} {command line}
size_t MaxNewSize = 357564416 {product} {ergonomic}
size_t MinHeapDeltaBytes = 524288 {product} {ergonomic}
size_t MinHeapSize = 104857600 {product} {command line}
size_t NewSize = 34603008 {product} {ergonomic}
uintx NonNMethodCodeHeapSize = 5832780 {pd product} {ergonomic}
uintx NonProfiledCodeHeapSize = 122912730 {pd product} {ergonomic}
size_t OldSize = 70254592 {product} {ergonomic}
uintx ProfiledCodeHeapSize = 122912730 {pd product} {ergonomic}
uintx ReservedCodeCacheSize = 251658240 {pd product} {ergonomic}
bool SegmentedCodeCache = true {product} {ergonomic}
size_t SoftMaxHeapSize = 1073741824 {manageable} {ergonomic}
bool UseCompressedOops = true {product lp64_product} {ergonomic}
bool UseLargePagesIndividualAllocation = false {pd product} {ergonomic}
bool UseParallelGC = true {product} {command line}
Logging:
Log output configuration:
#0: stdout all=off uptime,level,tags foldmultilines=false
#1: stderr all=off uptime,level,tags foldmultilines=false
Environment Variables:
JAVA_HOME=C:\Users\KKK\Desktop\java\java17
PATH=c:\Users\KKK\.trae\sdks\workspaces\70108cde\versions\node\current;c:\Users\KKK\.trae\sdks\versions\node\current;C:\Users\KKK\Desktop\PhotoTaking\PhotoTaking\PhotoTaking\bin\Debug\\adbs;C:\Users\KKK\Desktop\java\java17\bin;C:\Users\KKK\Desktop\apache-maven-3.9.9\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\Users\KKK\Desktop\java\java17\bin;C:\Users\KKK\Desktop\java\java17\jre\bin;C:\Program Files\Bandizip\;C:\Program Files\Go\bin;c:\Users\KKK\AppData\Local\Programs\cursor\resources\app\bin;C:\Program Files\dotnet\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\Users\KKK\AppData\Local\Microsoft\WindowsApps;C:\Users\KKK\AppData\Local\Programs\Windsurf\bin;C:\Users\KKK\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\KKK\AppData\Local\Programs\Trae\bin;c:\Users\KKK\AppData\Local\Programs\Trae\bin;C:\Users\KKK\go\bin;C:\Users\KKK\AppData\Local\Programs\cursor\resources\app\bin;C:\Users\KKK\.dotnet\tools;c:\Users\KKK\AppData\Local\Programs\Trae\resources\app\bin\lib
USERNAME=KKK
OS=Windows_NT
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
TMP=C:\Temp
TEMP=C:\Temp
Periodic native trim disabled
--------------- S Y S T E M ---------------
OS:
Windows 10 , 64 bit Build 19041 (10.0.19041.5438)
OS uptime: 0 days 14:50 hours
CPU: total 4 (initial active 4) (2 cores per cpu, 2 threads per core) family 6 model 61 stepping 4 microcode 0x2d, cx8, cmov, fxsr, ht, mmx, 3dnowpref, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, lzcnt, tsc, tscinvbit, avx, avx2, aes, erms, clmul, bmi1, bmi2, adx, fma, vzeroupper, clflush, rdtscp, f16c
Processor Information for the first 4 processors :
Max Mhz: 2394, Current Mhz: 2394, Mhz Limit: 2394
Memory: 4k page, system-wide physical 8096M (1147M free)
TotalPageFile size 8096M (AvailPageFile size 5M)
current process WorkingSet (physical memory assigned to process): 171M, peak: 175M
current process commit charge ("private bytes"): 254M, peak: 257M
vm_info: OpenJDK 64-Bit Server VM (21.0.7+6-LTS) for windows-amd64 JRE (21.0.7+6-LTS), built on 2025-04-15T00:00:00Z by "admin" with MS VC++ 17.7 (VS2022)
END.

@ -0,0 +1,803 @@
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (malloc) failed to allocate 1087344 bytes. Error detail: Chunk::new
# Possible reasons:
# The system is out of physical RAM or swap space
# This process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# JVM is running with Unscaled Compressed Oops mode in which the Java heap is
# placed in the first 4GB address space. The Java Heap base address is the
# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress
# to set the Java Heap base and to place the Java Heap above 4GB virtual address.
# This output file may be truncated or incomplete.
#
# Out of Memory Error (arena.cpp:168), pid=7968, tid=12472
#
# JRE version: OpenJDK Runtime Environment Temurin-21.0.7+6 (21.0.7+6) (build 21.0.7+6-LTS)
# Java VM: OpenJDK 64-Bit Server VM Temurin-21.0.7+6 (21.0.7+6-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, parallel gc, windows-amd64)
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
--------------- S U M M A R Y ------------
Command Line: --add-modules=ALL-SYSTEM --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/sun.nio.fs=ALL-UNNAMED -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Djava.import.generatesMetadataFilesAtProjectRoot=false -DDetectVMInstallationsJob.disabled=true -Dfile.encoding=utf8 -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -Xlog:disable -javaagent:c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\lombok\lombok-1.18.36.jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java -Daether.dependencyCollector.impl=bf c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\server\plugins\org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar -configuration c:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_win -data c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java\jdt_ws --pipe=\\.\pipe\lsp-fd3e28ce223fa01906c23ab150737a64-sock
Host: Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz, 4 cores, 7G, Windows 10 , 64 bit Build 19041 (10.0.19041.5438)
Time: Thu Jun 5 11:45:40 2025 Windows 10 , 64 bit Build 19041 (10.0.19041.5438) elapsed time: 31.318093 seconds (0d 0h 0m 31s)
--------------- T H R E A D ---------------
Current thread (0x000001f92b9fe1d0): JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=12472, stack(0x0000004ec7500000,0x0000004ec7600000) (1024K)]
Current CompileTask:
C2:31318 6271 4 jdk.internal.org.objectweb.asm.SymbolTable::addConstantMemberReference (147 bytes)
Stack: [0x0000004ec7500000,0x0000004ec7600000]
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [jvm.dll+0x6ce119]
V [jvm.dll+0x8a84a1]
V [jvm.dll+0x8aa9ce]
V [jvm.dll+0x8ab0b3]
V [jvm.dll+0x27f8a6]
V [jvm.dll+0xc507d]
V [jvm.dll+0xc55b3]
V [jvm.dll+0x3b692c]
V [jvm.dll+0x1e0029]
V [jvm.dll+0x247c42]
V [jvm.dll+0x2470cf]
V [jvm.dll+0x1c760e]
V [jvm.dll+0x25695a]
V [jvm.dll+0x254efa]
V [jvm.dll+0x3f03f6]
V [jvm.dll+0x851f6b]
V [jvm.dll+0x6cc7dd]
C [ucrtbase.dll+0x21bb2]
C [KERNEL32.DLL+0x17374]
C [ntdll.dll+0x4cc91]
--------------- P R O C E S S ---------------
Threads class SMR info:
_java_thread_list=0x000001f97449f950, length=36, elements={
0x000001f9155c5a20, 0x000001f92b9ed4b0, 0x000001f92b9f13d0, 0x000001f92b9f3360,
0x000001f92b9f4270, 0x000001f92b9fc950, 0x000001f92b9fd3a0, 0x000001f92b9fe1d0,
0x000001f92ba6ce60, 0x000001f917734280, 0x000001f96db03e60, 0x000001f9726fb2c0,
0x000001f96d9b7b70, 0x000001f96d8c8e30, 0x000001f92ba43960, 0x000001f96daab6d0,
0x000001f9729de3b0, 0x000001f97233cc10, 0x000001f97233ab40, 0x000001f97233d930,
0x000001f97233bef0, 0x000001f97233b1d0, 0x000001f97233c580, 0x000001f97233dfc0,
0x000001f97233b860, 0x000001f97447aee0, 0x000001f974477a60, 0x000001f97447bc00,
0x000001f97447b570, 0x000001f97447c290, 0x000001f974476d40, 0x000001f97447c920,
0x000001f9744794a0, 0x000001f974478780, 0x000001f97447a850, 0x000001f96dbf7270
}
Java Threads: ( => current thread )
0x000001f9155c5a20 JavaThread "main" [_thread_blocked, id=12692, stack(0x0000004ec6b00000,0x0000004ec6c00000) (1024K)]
0x000001f92b9ed4b0 JavaThread "Reference Handler" daemon [_thread_blocked, id=7072, stack(0x0000004ec6f00000,0x0000004ec7000000) (1024K)]
0x000001f92b9f13d0 JavaThread "Finalizer" daemon [_thread_blocked, id=10352, stack(0x0000004ec7000000,0x0000004ec7100000) (1024K)]
0x000001f92b9f3360 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=15004, stack(0x0000004ec7100000,0x0000004ec7200000) (1024K)]
0x000001f92b9f4270 JavaThread "Attach Listener" daemon [_thread_blocked, id=15720, stack(0x0000004ec7200000,0x0000004ec7300000) (1024K)]
0x000001f92b9fc950 JavaThread "Service Thread" daemon [_thread_blocked, id=6608, stack(0x0000004ec7300000,0x0000004ec7400000) (1024K)]
0x000001f92b9fd3a0 JavaThread "Monitor Deflation Thread" daemon [_thread_blocked, id=12820, stack(0x0000004ec7400000,0x0000004ec7500000) (1024K)]
=>0x000001f92b9fe1d0 JavaThread "C2 CompilerThread0" daemon [_thread_in_native, id=12472, stack(0x0000004ec7500000,0x0000004ec7600000) (1024K)]
0x000001f92ba6ce60 JavaThread "C1 CompilerThread0" daemon [_thread_blocked, id=3196, stack(0x0000004ec7600000,0x0000004ec7700000) (1024K)]
0x000001f917734280 JavaThread "Common-Cleaner" daemon [_thread_blocked, id=9208, stack(0x0000004ec7700000,0x0000004ec7800000) (1024K)]
0x000001f96db03e60 JavaThread "Notification Thread" daemon [_thread_blocked, id=16364, stack(0x0000004ec7800000,0x0000004ec7900000) (1024K)]
0x000001f9726fb2c0 JavaThread "Active Thread: Equinox Container: 628676c6-fdb3-48fc-9af4-56d955b0b6e2" [_thread_blocked, id=9228, stack(0x0000004ec7c00000,0x0000004ec7d00000) (1024K)]
0x000001f96d9b7b70 JavaThread "Refresh Thread: Equinox Container: 628676c6-fdb3-48fc-9af4-56d955b0b6e2" daemon [_thread_blocked, id=4044, stack(0x0000004ec7e00000,0x0000004ec7f00000) (1024K)]
0x000001f96d8c8e30 JavaThread "Framework Event Dispatcher: Equinox Container: 628676c6-fdb3-48fc-9af4-56d955b0b6e2" daemon [_thread_blocked, id=12004, stack(0x0000004ec7f00000,0x0000004ec8000000) (1024K)]
0x000001f92ba43960 JavaThread "Start Level: Equinox Container: 628676c6-fdb3-48fc-9af4-56d955b0b6e2" daemon [_thread_blocked, id=1736, stack(0x0000004ec7d00000,0x0000004ec7e00000) (1024K)]
0x000001f96daab6d0 JavaThread "Bundle File Closer" daemon [_thread_blocked, id=10336, stack(0x0000004ec8000000,0x0000004ec8100000) (1024K)]
0x000001f9729de3b0 JavaThread "SCR Component Actor" daemon [_thread_blocked, id=16856, stack(0x0000004ec8100000,0x0000004ec8200000) (1024K)]
0x000001f97233cc10 JavaThread "Worker-JM" [_thread_blocked, id=11496, stack(0x0000004ec8300000,0x0000004ec8400000) (1024K)]
0x000001f97233ab40 JavaThread "Worker-0" [_thread_blocked, id=9664, stack(0x0000004ec8400000,0x0000004ec8500000) (1024K)]
0x000001f97233d930 JavaThread "Worker-1: Repository registry initialization" [_thread_in_Java, id=6928, stack(0x0000004ec8500000,0x0000004ec8600000) (1024K)]
0x000001f97233bef0 JavaThread "Worker-2" [_thread_blocked, id=10200, stack(0x0000004ec8600000,0x0000004ec8700000) (1024K)]
0x000001f97233b1d0 JavaThread "Worker-3: Initialize After Load" [_thread_in_vm, id=13932, stack(0x0000004ec8700000,0x0000004ec8800000) (1024K)]
0x000001f97233c580 JavaThread "Java indexing" daemon [_thread_blocked, id=5788, stack(0x0000004ec8900000,0x0000004ec8a00000) (1024K)]
0x000001f97233dfc0 JavaThread "JNA Cleaner" daemon [_thread_blocked, id=1440, stack(0x0000004ec8200000,0x0000004ec8300000) (1024K)]
0x000001f97233b860 JavaThread "Worker-4" [_thread_blocked, id=5192, stack(0x0000004ec8c00000,0x0000004ec8d00000) (1024K)]
0x000001f97447aee0 JavaThread "Worker-5" [_thread_blocked, id=3664, stack(0x0000004ec8d00000,0x0000004ec8e00000) (1024K)]
0x000001f974477a60 JavaThread "Thread-2" daemon [_thread_in_native, id=13492, stack(0x0000004ec8800000,0x0000004ec8900000) (1024K)]
0x000001f97447bc00 JavaThread "Thread-3" daemon [_thread_in_native, id=16196, stack(0x0000004ec8e00000,0x0000004ec8f00000) (1024K)]
0x000001f97447b570 JavaThread "Thread-4" daemon [_thread_in_native, id=3760, stack(0x0000004ec8f00000,0x0000004ec9000000) (1024K)]
0x000001f97447c290 JavaThread "Thread-5" daemon [_thread_in_native, id=1628, stack(0x0000004ec9000000,0x0000004ec9100000) (1024K)]
0x000001f974476d40 JavaThread "Thread-6" daemon [_thread_in_native, id=13684, stack(0x0000004ec9100000,0x0000004ec9200000) (1024K)]
0x000001f97447c920 JavaThread "pool-2-thread-1" [_thread_blocked, id=1132, stack(0x0000004ec9200000,0x0000004ec9300000) (1024K)]
0x000001f9744794a0 JavaThread "WorkspaceEventsHandler" [_thread_blocked, id=13400, stack(0x0000004ec9400000,0x0000004ec9500000) (1024K)]
0x000001f974478780 JavaThread "pool-1-thread-1" [_thread_blocked, id=9700, stack(0x0000004ec9500000,0x0000004ec9600000) (1024K)]
0x000001f97447a850 JavaThread "Keep-Alive-Timer" daemon [_thread_blocked, id=2476, stack(0x0000004ec9300000,0x0000004ec9400000) (1024K)]
0x000001f96dbf7270 JavaThread "C2 CompilerThread1" daemon [_thread_in_native, id=8264, stack(0x0000004ec9700000,0x0000004ec9800000) (1024K)]
Total: 36
Other Threads:
0x000001f917782b80 VMThread "VM Thread" [id=16572, stack(0x0000004ec6e00000,0x0000004ec6f00000) (1024K)]
0x000001f917733750 WatcherThread "VM Periodic Task Thread" [id=14876, stack(0x0000004ec6d00000,0x0000004ec6e00000) (1024K)]
0x000001f9176e66e0 WorkerThread "GC Thread#0" [id=13616, stack(0x0000004ec6c00000,0x0000004ec6d00000) (1024K)]
0x000001f97275de10 WorkerThread "GC Thread#1" [id=2968, stack(0x0000004ec7900000,0x0000004ec7a00000) (1024K)]
0x000001f97241e9b0 WorkerThread "GC Thread#2" [id=12192, stack(0x0000004ec7a00000,0x0000004ec7b00000) (1024K)]
0x000001f97241ed50 WorkerThread "GC Thread#3" [id=17124, stack(0x0000004ec7b00000,0x0000004ec7c00000) (1024K)]
Total: 6
Threads with active compile tasks:
C2 CompilerThread0 31340 6271 4 jdk.internal.org.objectweb.asm.SymbolTable::addConstantMemberReference (147 bytes)
C2 CompilerThread1 31340 6345 4 org.eclipse.osgi.internal.container.KeyBasedLockStore::getLock (105 bytes)
Total: 2
VM state: not at safepoint (normal execution)
VM Mutex/Monitor currently owned by a thread: None
Heap address: 0x00000000c0000000, size: 1024 MB, Compressed Oops mode: 32-bit
CDS archive(s) mapped at: [0x000001f92c000000-0x000001f92cba0000-0x000001f92cba0000), size 12189696, SharedBaseAddress: 0x000001f92c000000, ArchiveRelocationMode: 1.
Compressed class space mapped at: 0x000001f92d000000-0x000001f96d000000, reserved size: 1073741824
Narrow klass base: 0x000001f92c000000, Narrow klass shift: 0, Narrow klass range: 0x100000000
GC Precious Log:
CardTable entry size: 512
CPUs: 4 total, 4 available
Memory: 8096M
Large Page Support: Disabled
NUMA Support: Disabled
Compressed Oops: Enabled (32-bit)
Alignments: Space 512K, Generation 512K, Heap 2M
Heap Min Capacity: 100M
Heap Initial Capacity: 100M
Heap Max Capacity: 1G
Pre-touch: Disabled
Parallel Workers: 4
Heap:
PSYoungGen total 23040K, used 17686K [0x00000000eab00000, 0x00000000ec680000, 0x0000000100000000)
eden space 22016K, 75% used [0x00000000eab00000,0x00000000ebb45a98,0x00000000ec080000)
from space 1024K, 100% used [0x00000000ec480000,0x00000000ec580000,0x00000000ec580000)
to space 3072K, 0% used [0x00000000ec080000,0x00000000ec080000,0x00000000ec380000)
ParOldGen total 68608K, used 23753K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 34% used [0x00000000c0000000,0x00000000c1732670,0x00000000c4300000)
Metaspace used 48166K, committed 49280K, reserved 1114112K
class space used 5175K, committed 5632K, reserved 1048576K
Card table byte_map: [0x000001f917070000,0x000001f917280000] _byte_map_base: 0x000001f916a70000
Marking Bits: (ParMarkBitMap*) 0x00007fffb5ad31f0
Begin Bits: [0x000001f9298c0000, 0x000001f92a8c0000)
End Bits: [0x000001f92a8c0000, 0x000001f92b8c0000)
Polling page: 0x000001f915550000
Metaspace:
Usage:
Non-class: 41.98 MB used.
Class: 5.05 MB used.
Both: 47.04 MB used.
Virtual space:
Non-class space: 64.00 MB reserved, 42.62 MB ( 67%) committed, 1 nodes.
Class space: 1.00 GB reserved, 5.50 MB ( <1%) committed, 1 nodes.
Both: 1.06 GB reserved, 48.12 MB ( 4%) committed.
Chunk freelists:
Non-Class: 5.05 MB
Class: 10.45 MB
Both: 15.50 MB
MaxMetaspaceSize: unlimited
CompressedClassSpaceSize: 1.00 GB
Initial GC threshold: 21.00 MB
Current GC threshold: 58.38 MB
CDS: on
- commit_granule_bytes: 65536.
- commit_granule_words: 8192.
- virtual_space_node_default_size: 8388608.
- enlarge_chunks_in_place: 1.
- use_allocation_guard: 0.
Internal statistics:
num_allocs_failed_limit: 10.
num_arena_births: 768.
num_arena_deaths: 18.
num_vsnodes_births: 2.
num_vsnodes_deaths: 0.
num_space_committed: 770.
num_space_uncommitted: 0.
num_chunks_returned_to_freelist: 28.
num_chunks_taken_from_freelist: 2655.
num_chunk_merges: 14.
num_chunk_splits: 1688.
num_chunks_enlarged: 1031.
num_inconsistent_stats: 0.
CodeHeap 'non-profiled nmethods': size=120000Kb used=3141Kb max_used=3141Kb free=116858Kb
bounds [0x000001f922390000, 0x000001f9226b0000, 0x000001f9298c0000]
CodeHeap 'profiled nmethods': size=120000Kb used=12169Kb max_used=12169Kb free=107830Kb
bounds [0x000001f91a8c0000, 0x000001f91b4b0000, 0x000001f921df0000]
CodeHeap 'non-nmethods': size=5760Kb used=1399Kb max_used=1434Kb free=4360Kb
bounds [0x000001f921df0000, 0x000001f922060000, 0x000001f922390000]
total_blobs=6452 nmethods=5752 adapters=606
compilation: enabled
stopped_count=0, restarted_count=0
full_count=0
Compilation events (20 events):
Event: 31.265 Thread 0x000001f96dbf7270 nmethod 6327 0x000001f92269d690 code [0x000001f92269d860, 0x000001f92269daf0]
Event: 31.265 Thread 0x000001f96dbf7270 6328 4 com.google.gson.internal.bind.TypeAdapters$15::read (38 bytes)
Event: 31.268 Thread 0x000001f92ba6ce60 6336 3 com.google.inject.internal.BindingImpl::getProvider (44 bytes)
Event: 31.268 Thread 0x000001f92ba6ce60 nmethod 6336 0x000001f91b499d10 code [0x000001f91b499ee0, 0x000001f91b49a190]
Event: 31.269 Thread 0x000001f96dbf7270 nmethod 6328 0x000001f92269de10 code [0x000001f92269dfe0, 0x000001f92269e270]
Event: 31.269 Thread 0x000001f96dbf7270 6337 4 java.lang.invoke.MethodHandles$Lookup::checkAccess (200 bytes)
Event: 31.274 Thread 0x000001f96dbf7270 nmethod 6337 0x000001f92269e510 code [0x000001f92269e700, 0x000001f92269eac8]
Event: 31.275 Thread 0x000001f92ba6ce60 6338 3 jdk.internal.reflect.MethodHandleAccessorFactory::slotCount (75 bytes)
Event: 31.276 Thread 0x000001f92ba6ce60 nmethod 6338 0x000001f91b49a290 code [0x000001f91b49a480, 0x000001f91b49a930]
Event: 31.279 Thread 0x000001f96dbf7270 6339 4 org.eclipse.osgi.internal.container.KeyBasedLockStore::poll (32 bytes)
Event: 31.286 Thread 0x000001f92ba6ce60 6340 ! 3 java.util.concurrent.ConcurrentHashMap::putVal (432 bytes)
Event: 31.288 Thread 0x000001f92ba6ce60 nmethod 6340 0x000001f91b49aa90 code [0x000001f91b49ae00, 0x000001f91b49c6d8]
Event: 31.288 Thread 0x000001f92ba6ce60 6342 3 java.lang.String::valueOf (14 bytes)
Event: 31.289 Thread 0x000001f92ba6ce60 nmethod 6342 0x000001f91b49d090 code [0x000001f91b49d240, 0x000001f91b49d3a8]
Event: 31.289 Thread 0x000001f92ba6ce60 6341 3 java.lang.Boolean::toString (5 bytes)
Event: 31.289 Thread 0x000001f92ba6ce60 nmethod 6341 0x000001f91b49d410 code [0x000001f91b49d5c0, 0x000001f91b49d780]
Event: 31.297 Thread 0x000001f96dbf7270 nmethod 6339 0x000001f92269ee10 code [0x000001f92269f0a0, 0x000001f92269f8c0]
Event: 31.298 Thread 0x000001f96dbf7270 6343 4 java.util.HashMap::putVal (300 bytes)
Event: 31.315 Thread 0x000001f96dbf7270 nmethod 6343 0x000001f92269ff90 code [0x000001f9226a01c0, 0x000001f9226a09c0]
Event: 31.315 Thread 0x000001f96dbf7270 6344 4 java.lang.Integer::getChars (121 bytes)
GC Heap History (20 events):
Event: 18.726 GC heap before
{Heap before GC invocations=11 (full 1):
PSYoungGen total 26112K, used 25871K [0x00000000eab00000, 0x00000000eca00000, 0x0000000100000000)
eden space 23552K, 100% used [0x00000000eab00000,0x00000000ec200000,0x00000000ec200000)
from space 2560K, 90% used [0x00000000ec300000,0x00000000ec543fd8,0x00000000ec580000)
to space 3584K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000eca00000)
ParOldGen total 68608K, used 13341K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 19% used [0x00000000c0000000,0x00000000c0d07538,0x00000000c4300000)
Metaspace used 26420K, committed 27264K, reserved 1114112K
class space used 2539K, committed 2880K, reserved 1048576K
}
Event: 18.728 GC heap after
{Heap after GC invocations=11 (full 1):
PSYoungGen total 25600K, used 2408K [0x00000000eab00000, 0x00000000ec900000, 0x0000000100000000)
eden space 23040K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec180000)
from space 2560K, 94% used [0x00000000ec680000,0x00000000ec8da2b0,0x00000000ec900000)
to space 2560K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec680000)
ParOldGen total 68608K, used 13341K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 19% used [0x00000000c0000000,0x00000000c0d07538,0x00000000c4300000)
Metaspace used 26420K, committed 27264K, reserved 1114112K
class space used 2539K, committed 2880K, reserved 1048576K
}
Event: 21.839 GC heap before
{Heap before GC invocations=12 (full 1):
PSYoungGen total 25600K, used 25448K [0x00000000eab00000, 0x00000000ec900000, 0x0000000100000000)
eden space 23040K, 100% used [0x00000000eab00000,0x00000000ec180000,0x00000000ec180000)
from space 2560K, 94% used [0x00000000ec680000,0x00000000ec8da2b0,0x00000000ec900000)
to space 2560K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec680000)
ParOldGen total 68608K, used 13341K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 19% used [0x00000000c0000000,0x00000000c0d07538,0x00000000c4300000)
Metaspace used 28751K, committed 29632K, reserved 1114112K
class space used 2837K, committed 3264K, reserved 1048576K
}
Event: 21.843 GC heap after
{Heap after GC invocations=12 (full 1):
PSYoungGen total 24064K, used 1103K [0x00000000eab00000, 0x00000000ec700000, 0x0000000100000000)
eden space 22528K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec100000)
from space 1536K, 71% used [0x00000000ec400000,0x00000000ec513cf0,0x00000000ec580000)
to space 1536K, 0% used [0x00000000ec580000,0x00000000ec580000,0x00000000ec700000)
ParOldGen total 68608K, used 15500K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 22% used [0x00000000c0000000,0x00000000c0f230d8,0x00000000c4300000)
Metaspace used 28751K, committed 29632K, reserved 1114112K
class space used 2837K, committed 3264K, reserved 1048576K
}
Event: 22.981 GC heap before
{Heap before GC invocations=13 (full 1):
PSYoungGen total 24064K, used 23631K [0x00000000eab00000, 0x00000000ec700000, 0x0000000100000000)
eden space 22528K, 100% used [0x00000000eab00000,0x00000000ec100000,0x00000000ec100000)
from space 1536K, 71% used [0x00000000ec400000,0x00000000ec513cf0,0x00000000ec580000)
to space 1536K, 0% used [0x00000000ec580000,0x00000000ec580000,0x00000000ec700000)
ParOldGen total 68608K, used 15500K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 22% used [0x00000000c0000000,0x00000000c0f230d8,0x00000000c4300000)
Metaspace used 31643K, committed 32512K, reserved 1114112K
class space used 3149K, committed 3520K, reserved 1048576K
}
Event: 22.984 GC heap after
{Heap after GC invocations=13 (full 1):
PSYoungGen total 23552K, used 1226K [0x00000000eab00000, 0x00000000ec700000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 1536K, 79% used [0x00000000ec580000,0x00000000ec6b28e0,0x00000000ec700000)
to space 1536K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec580000)
ParOldGen total 68608K, used 16424K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 23% used [0x00000000c0000000,0x00000000c100a3a0,0x00000000c4300000)
Metaspace used 31643K, committed 32512K, reserved 1114112K
class space used 3149K, committed 3520K, reserved 1048576K
}
Event: 24.159 GC heap before
{Heap before GC invocations=14 (full 1):
PSYoungGen total 23552K, used 23242K [0x00000000eab00000, 0x00000000ec700000, 0x0000000100000000)
eden space 22016K, 100% used [0x00000000eab00000,0x00000000ec080000,0x00000000ec080000)
from space 1536K, 79% used [0x00000000ec580000,0x00000000ec6b28e0,0x00000000ec700000)
to space 1536K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec580000)
ParOldGen total 68608K, used 16424K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 23% used [0x00000000c0000000,0x00000000c100a3a0,0x00000000c4300000)
Metaspace used 34055K, committed 35136K, reserved 1114112K
class space used 3442K, committed 3904K, reserved 1048576K
}
Event: 24.163 GC heap after
{Heap after GC invocations=14 (full 1):
PSYoungGen total 23552K, used 1377K [0x00000000eab00000, 0x00000000ec600000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 1536K, 89% used [0x00000000ec400000,0x00000000ec5586d8,0x00000000ec580000)
to space 512K, 0% used [0x00000000ec580000,0x00000000ec580000,0x00000000ec600000)
ParOldGen total 68608K, used 17516K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 25% used [0x00000000c0000000,0x00000000c111b268,0x00000000c4300000)
Metaspace used 34055K, committed 35136K, reserved 1114112K
class space used 3442K, committed 3904K, reserved 1048576K
}
Event: 24.306 GC heap before
{Heap before GC invocations=15 (full 1):
PSYoungGen total 23552K, used 7759K [0x00000000eab00000, 0x00000000ec600000, 0x0000000100000000)
eden space 22016K, 28% used [0x00000000eab00000,0x00000000eb13b8d8,0x00000000ec080000)
from space 1536K, 89% used [0x00000000ec400000,0x00000000ec5586d8,0x00000000ec580000)
to space 512K, 0% used [0x00000000ec580000,0x00000000ec580000,0x00000000ec600000)
ParOldGen total 68608K, used 17516K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 25% used [0x00000000c0000000,0x00000000c111b268,0x00000000c4300000)
Metaspace used 34865K, committed 35840K, reserved 1114112K
class space used 3531K, committed 3968K, reserved 1048576K
}
Event: 24.309 GC heap after
{Heap after GC invocations=15 (full 1):
PSYoungGen total 22528K, used 373K [0x00000000eab00000, 0x00000000ec600000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 512K, 72% used [0x00000000ec580000,0x00000000ec5dd4f0,0x00000000ec600000)
to space 1024K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec500000)
ParOldGen total 68608K, used 18773K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 27% used [0x00000000c0000000,0x00000000c1255688,0x00000000c4300000)
Metaspace used 34865K, committed 35840K, reserved 1114112K
class space used 3531K, committed 3968K, reserved 1048576K
}
Event: 24.309 GC heap before
{Heap before GC invocations=16 (full 2):
PSYoungGen total 22528K, used 373K [0x00000000eab00000, 0x00000000ec600000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 512K, 72% used [0x00000000ec580000,0x00000000ec5dd4f0,0x00000000ec600000)
to space 1024K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec500000)
ParOldGen total 68608K, used 18773K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 27% used [0x00000000c0000000,0x00000000c1255688,0x00000000c4300000)
Metaspace used 34865K, committed 35840K, reserved 1114112K
class space used 3531K, committed 3968K, reserved 1048576K
}
Event: 24.385 GC heap after
{Heap after GC invocations=16 (full 2):
PSYoungGen total 22528K, used 0K [0x00000000eab00000, 0x00000000ec600000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 512K, 0% used [0x00000000ec580000,0x00000000ec580000,0x00000000ec600000)
to space 1024K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec500000)
ParOldGen total 68608K, used 18065K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 26% used [0x00000000c0000000,0x00000000c11a45a0,0x00000000c4300000)
Metaspace used 34861K, committed 35840K, reserved 1114112K
class space used 3530K, committed 3968K, reserved 1048576K
}
Event: 25.420 GC heap before
{Heap before GC invocations=17 (full 2):
PSYoungGen total 22528K, used 22016K [0x00000000eab00000, 0x00000000ec600000, 0x0000000100000000)
eden space 22016K, 100% used [0x00000000eab00000,0x00000000ec080000,0x00000000ec080000)
from space 512K, 0% used [0x00000000ec580000,0x00000000ec580000,0x00000000ec600000)
to space 1024K, 0% used [0x00000000ec400000,0x00000000ec400000,0x00000000ec500000)
ParOldGen total 68608K, used 18065K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 26% used [0x00000000c0000000,0x00000000c11a45a0,0x00000000c4300000)
Metaspace used 37620K, committed 38592K, reserved 1114112K
class space used 3858K, committed 4288K, reserved 1048576K
}
Event: 25.425 GC heap after
{Heap after GC invocations=17 (full 2):
PSYoungGen total 23040K, used 1024K [0x00000000eab00000, 0x00000000ec780000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 1024K, 100% used [0x00000000ec400000,0x00000000ec500000,0x00000000ec500000)
to space 2560K, 0% used [0x00000000ec500000,0x00000000ec500000,0x00000000ec780000)
ParOldGen total 68608K, used 19869K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 28% used [0x00000000c0000000,0x00000000c1367438,0x00000000c4300000)
Metaspace used 37620K, committed 38592K, reserved 1114112K
class space used 3858K, committed 4288K, reserved 1048576K
}
Event: 26.527 GC heap before
{Heap before GC invocations=18 (full 2):
PSYoungGen total 23040K, used 23040K [0x00000000eab00000, 0x00000000ec780000, 0x0000000100000000)
eden space 22016K, 100% used [0x00000000eab00000,0x00000000ec080000,0x00000000ec080000)
from space 1024K, 100% used [0x00000000ec400000,0x00000000ec500000,0x00000000ec500000)
to space 2560K, 0% used [0x00000000ec500000,0x00000000ec500000,0x00000000ec780000)
ParOldGen total 68608K, used 19869K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 28% used [0x00000000c0000000,0x00000000c1367438,0x00000000c4300000)
Metaspace used 40446K, committed 41536K, reserved 1114112K
class space used 4187K, committed 4672K, reserved 1048576K
}
Event: 26.531 GC heap after
{Heap after GC invocations=18 (full 2):
PSYoungGen total 24064K, used 1568K [0x00000000eab00000, 0x00000000ec700000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 2048K, 76% used [0x00000000ec500000,0x00000000ec688000,0x00000000ec700000)
to space 2048K, 0% used [0x00000000ec300000,0x00000000ec300000,0x00000000ec500000)
ParOldGen total 68608K, used 20827K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 30% used [0x00000000c0000000,0x00000000c1456dd8,0x00000000c4300000)
Metaspace used 40446K, committed 41536K, reserved 1114112K
class space used 4187K, committed 4672K, reserved 1048576K
}
Event: 27.492 GC heap before
{Heap before GC invocations=19 (full 2):
PSYoungGen total 24064K, used 23584K [0x00000000eab00000, 0x00000000ec700000, 0x0000000100000000)
eden space 22016K, 100% used [0x00000000eab00000,0x00000000ec080000,0x00000000ec080000)
from space 2048K, 76% used [0x00000000ec500000,0x00000000ec688000,0x00000000ec700000)
to space 2048K, 0% used [0x00000000ec300000,0x00000000ec300000,0x00000000ec500000)
ParOldGen total 68608K, used 20827K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 30% used [0x00000000c0000000,0x00000000c1456dd8,0x00000000c4300000)
Metaspace used 43731K, committed 44736K, reserved 1114112K
class space used 4617K, committed 5056K, reserved 1048576K
}
Event: 27.496 GC heap after
{Heap after GC invocations=19 (full 2):
PSYoungGen total 23552K, used 1162K [0x00000000eab00000, 0x00000000ec580000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 1536K, 75% used [0x00000000ec300000,0x00000000ec422b48,0x00000000ec480000)
to space 1024K, 0% used [0x00000000ec480000,0x00000000ec480000,0x00000000ec580000)
ParOldGen total 68608K, used 22278K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 32% used [0x00000000c0000000,0x00000000c15c1ae8,0x00000000c4300000)
Metaspace used 43731K, committed 44736K, reserved 1114112K
class space used 4617K, committed 5056K, reserved 1048576K
}
Event: 29.894 GC heap before
{Heap before GC invocations=20 (full 2):
PSYoungGen total 23552K, used 23178K [0x00000000eab00000, 0x00000000ec580000, 0x0000000100000000)
eden space 22016K, 100% used [0x00000000eab00000,0x00000000ec080000,0x00000000ec080000)
from space 1536K, 75% used [0x00000000ec300000,0x00000000ec422b48,0x00000000ec480000)
to space 1024K, 0% used [0x00000000ec480000,0x00000000ec480000,0x00000000ec580000)
ParOldGen total 68608K, used 22278K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 32% used [0x00000000c0000000,0x00000000c15c1ae8,0x00000000c4300000)
Metaspace used 46077K, committed 47168K, reserved 1114112K
class space used 4929K, committed 5376K, reserved 1048576K
}
Event: 29.906 GC heap after
{Heap after GC invocations=20 (full 2):
PSYoungGen total 23040K, used 1024K [0x00000000eab00000, 0x00000000ec680000, 0x0000000100000000)
eden space 22016K, 0% used [0x00000000eab00000,0x00000000eab00000,0x00000000ec080000)
from space 1024K, 100% used [0x00000000ec480000,0x00000000ec580000,0x00000000ec580000)
to space 3072K, 0% used [0x00000000ec080000,0x00000000ec080000,0x00000000ec380000)
ParOldGen total 68608K, used 23753K [0x00000000c0000000, 0x00000000c4300000, 0x00000000eab00000)
object space 68608K, 34% used [0x00000000c0000000,0x00000000c1732670,0x00000000c4300000)
Metaspace used 46077K, committed 47168K, reserved 1114112K
class space used 4929K, committed 5376K, reserved 1048576K
}
Dll operation events (14 events):
Event: 0.047 Loaded shared library c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\java.dll
Event: 0.168 Loaded shared library c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\zip.dll
Event: 0.667 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\instrument.dll
Event: 0.675 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\net.dll
Event: 0.688 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\nio.dll
Event: 0.718 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\zip.dll
Event: 0.807 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\jimage.dll
Event: 1.210 Loaded shared library c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\verify.dll
Event: 7.089 Loaded shared library C:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_win\org.eclipse.equinox.launcher\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.1300.v20250331-1702\eclipse_11911.dll
Event: 14.047 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management.dll
Event: 14.053 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management_ext.dll
Event: 23.331 Loaded shared library C:\Temp\jna-74475\jna16640627127975356240.dll
Event: 26.535 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\sunmscapi.dll
Event: 26.927 Loaded shared library C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\extnet.dll
Deoptimization events (20 events):
Event: 31.238 Thread 0x000001f97233ab40 Uncommon trap: trap_request=0xffffff45 fr.pc=0x000001f922652c08 relative=0x00000000000070c8
Event: 31.238 Thread 0x000001f97233ab40 Uncommon trap: reason=unstable_if action=reinterpret pc=0x000001f922652c08 method=org.eclipse.osgi.internal.loader.ModuleClassLoader.defineClass(Ljava/lang/String;[BLorg/eclipse/osgi/internal/loader/classpath/ClasspathEntry;)Lorg/eclipse/osgi/internal/loa
Event: 31.238 Thread 0x000001f97233ab40 DEOPT PACKING pc=0x000001f922652c08 sp=0x0000004ec84fdf90
Event: 31.238 Thread 0x000001f97233ab40 DEOPT UNPACKING pc=0x000001f921e43aa2 sp=0x0000004ec84fdf00 mode 2
Event: 31.261 Thread 0x000001f97233d930 Uncommon trap: trap_request=0xffffff54 fr.pc=0x000001f92260b308 relative=0x00000000000008e8
Event: 31.261 Thread 0x000001f97233d930 Uncommon trap: reason=speculate_null_assert action=make_not_entrant pc=0x000001f92260b308 method=org.eclipse.osgi.internal.container.KeyBasedLockStore.getLock(Ljava/lang/Object;)Ljava/lang/Object; @ 71 c2
Event: 31.261 Thread 0x000001f97233d930 DEOPT PACKING pc=0x000001f92260b308 sp=0x0000004ec85fe590
Event: 31.261 Thread 0x000001f97233d930 DEOPT UNPACKING pc=0x000001f921e43aa2 sp=0x0000004ec85fe530 mode 2
Event: 31.261 Thread 0x000001f97233d930 Uncommon trap: trap_request=0xffffff45 fr.pc=0x000001f92252da94 relative=0x0000000000000834
Event: 31.261 Thread 0x000001f97233d930 Uncommon trap: reason=unstable_if action=reinterpret pc=0x000001f92252da94 method=java.util.concurrent.ConcurrentHashMap.putVal(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object; @ 97 c2
Event: 31.261 Thread 0x000001f97233d930 DEOPT PACKING pc=0x000001f92252da94 sp=0x0000004ec85fe540
Event: 31.261 Thread 0x000001f97233d930 DEOPT UNPACKING pc=0x000001f921e43aa2 sp=0x0000004ec85fe4b8 mode 2
Event: 31.276 Thread 0x000001f97233d930 Uncommon trap: trap_request=0xffffff45 fr.pc=0x000001f92250e7b8 relative=0x00000000000002d8
Event: 31.276 Thread 0x000001f97233d930 Uncommon trap: reason=unstable_if action=reinterpret pc=0x000001f92250e7b8 method=java.lang.invoke.VarHandle.accessModeType(I)Ljava/lang/invoke/MethodType; @ 6 c2
Event: 31.276 Thread 0x000001f97233d930 DEOPT PACKING pc=0x000001f92250e7b8 sp=0x0000004ec85feeb0
Event: 31.276 Thread 0x000001f97233d930 DEOPT UNPACKING pc=0x000001f921e43aa2 sp=0x0000004ec85fed68 mode 2
Event: 31.276 Thread 0x000001f97233d930 Uncommon trap: trap_request=0xffffff45 fr.pc=0x000001f9224115a0 relative=0x00000000000001c0
Event: 31.276 Thread 0x000001f97233d930 Uncommon trap: reason=unstable_if action=reinterpret pc=0x000001f9224115a0 method=java.lang.invoke.VarHandle.accessModeType(I)Ljava/lang/invoke/MethodType; @ 26 c2
Event: 31.276 Thread 0x000001f97233d930 DEOPT PACKING pc=0x000001f9224115a0 sp=0x0000004ec85feda0
Event: 31.276 Thread 0x000001f97233d930 DEOPT UNPACKING pc=0x000001f921e43aa2 sp=0x0000004ec85fec60 mode 2
Classes loaded (20 events):
Event: 30.311 Loading class sun/net/www/http/KeepAliveStreamCleaner
Event: 30.311 Loading class sun/net/www/http/KeepAliveStreamCleaner done
Event: 30.328 Loading class sun/net/www/http/KeepAliveStreamCleaner$1
Event: 30.328 Loading class sun/net/www/http/KeepAliveStreamCleaner$1 done
Event: 30.328 Loading class sun/net/www/http/KeepAliveStreamCleaner$2
Event: 30.329 Loading class sun/net/www/http/KeepAliveStreamCleaner$2 done
Event: 30.329 Loading class sun/net/www/protocol/http/HttpURLConnection$HttpInputStream
Event: 30.329 Loading class sun/net/www/protocol/http/HttpURLConnection$HttpInputStream done
Event: 30.937 Loading class sun/net/www/http/KeepAliveCache$1
Event: 30.937 Loading class sun/net/www/http/KeepAliveCache$1 done
Event: 30.937 Loading class sun/net/www/http/KeepAliveCache$ClientVector
Event: 30.937 Loading class sun/net/www/http/KeepAliveCache$ClientVector done
Event: 30.999 Loading class sun/net/www/http/KeepAliveEntry
Event: 31.002 Loading class sun/net/www/http/KeepAliveEntry done
Event: 31.253 Loading class java/util/regex/Pattern$Pos
Event: 31.253 Loading class java/util/regex/Pattern$Pos done
Event: 31.275 Loading class sun/security/provider/MD5
Event: 31.275 Loading class sun/security/provider/MD5 done
Event: 31.275 Loading class sun/security/provider/ByteArrayAccess$LE
Event: 31.276 Loading class sun/security/provider/ByteArrayAccess$LE done
Classes unloaded (9 events):
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a3800 'java/lang/invoke/LambdaForm$MH+0x000001f92d1a3800'
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a3400 'java/lang/invoke/LambdaForm$MH+0x000001f92d1a3400'
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a3000 'java/lang/invoke/LambdaForm$MH+0x000001f92d1a3000'
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a2c00 'java/lang/invoke/LambdaForm$MH+0x000001f92d1a2c00'
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a2800 'java/lang/invoke/LambdaForm$BMH+0x000001f92d1a2800'
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a2400 'java/lang/invoke/LambdaForm$DMH+0x000001f92d1a2400'
Event: 17.153 Thread 0x000001f917782b80 Unloading class 0x000001f92d1a1400 'java/lang/invoke/LambdaForm$DMH+0x000001f92d1a1400'
Event: 24.323 Thread 0x000001f917782b80 Unloading class 0x000001f92d2ab000 'java/lang/invoke/LambdaForm$MH+0x000001f92d2ab000'
Event: 24.323 Thread 0x000001f917782b80 Unloading class 0x000001f92d2aa400 'java/lang/invoke/LambdaForm$MH+0x000001f92d2aa400'
Classes redefined (0 events):
No events
Internal exceptions (20 events):
Event: 23.859 Thread 0x000001f92ba43960 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb73e428}: 'int java.lang.invoke.Invokers$Holder.invokeExact_MT(java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eb73e428)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 23.861 Thread 0x000001f92ba43960 Exception <a 'java/lang/UnsatisfiedLinkError'{0x00000000eba49cf8}: 找不到指定的程序。
> (0x00000000eba49cf8)
thrown [s\src\hotspot\share\prims\jni.cpp, line 539]
Event: 23.926 Thread 0x000001f92ba43960 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eba568c0}: 'void java.lang.invoke.Invokers$Holder.invokeExact_MT(java.lang.Object, java.lang.Object, int, java.lang.Object)'> (0x00000000eba568c0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 24.461 Thread 0x000001f9155c5a20 Exception <a 'java/io/FileNotFoundException'{0x00000000eabe56f0}> (0x00000000eabe56f0)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 24.590 Thread 0x000001f97233bef0 Exception <a 'sun/nio/fs/WindowsException'{0x00000000eb001758}> (0x00000000eb001758)
thrown [s\src\hotspot\share\prims\jni.cpp, line 520]
Event: 25.046 Thread 0x000001f9155c5a20 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb45f9e0}: 'java.lang.Object java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(java.lang.Object, long, java.lang.Object)'> (0x00000000eb45f9e0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 25.048 Thread 0x000001f9155c5a20 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb4634e8}: 'java.lang.Object java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, long, java.lang.Object)'> (0x00000000eb4634e8)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 25.048 Thread 0x000001f9155c5a20 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb4a6f28}: 'java.lang.Object java.lang.invoke.DirectMethodHandle$Holder.newInvokeSpecial(java.lang.Object, long)'> (0x00000000eb4a6f28)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 26.681 Thread 0x000001f9155c5a20 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eae4dca8}: 'void java.lang.invoke.DirectMethodHandle$Holder.invokeSpecial(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eae4dca8)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 26.682 Thread 0x000001f9155c5a20 Exception <a 'java/lang/IncompatibleClassChangeError'{0x00000000eae560c8}: Found class java.lang.Object, but interface was expected> (0x00000000eae560c8)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 840]
Event: 26.682 Thread 0x000001f9155c5a20 Exception <a 'java/lang/IncompatibleClassChangeError'{0x00000000eae57e20}: Found class java.lang.Object, but interface was expected> (0x00000000eae57e20)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 840]
Event: 27.237 Thread 0x000001f97233d930 Exception <a 'java/lang/NoSuchMethodError'{0x00000000ebb5d5a0}: 'void java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000ebb5d5a0)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 27.364 Thread 0x000001f97233d930 Exception <a 'java/lang/ExceptionInInitializerError'{0x00000000ebbef110}> (0x00000000ebbef110)
thrown [s\src\hotspot\share\oops\instanceKlass.cpp, line 1220]
Event: 27.461 Thread 0x000001f97233d930 Implicit null exception at 0x000001f9224de8fe to 0x000001f9224deb70
Event: 27.461 Thread 0x000001f97233d930 Implicit null exception at 0x000001f92244859c to 0x000001f922449460
Event: 27.464 Thread 0x000001f97233d930 Exception <a 'java/lang/NoSuchMethodError'{0x00000000ebefcc10}: 'java.lang.Object java.lang.invoke.Invokers$Holder.invokeExact_MT(java.lang.Object, int, java.lang.Object)'> (0x00000000ebefcc10)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 29.258 Thread 0x000001f97233bef0 Implicit null exception at 0x000001f92250a56c to 0x000001f92250a628
Event: 29.955 Thread 0x000001f97233d930 Exception <a 'java/lang/NoClassDefFoundError'{0x00000000eac4e408}: com/google/inject/servlet/ServletModuleTargetVisitor> (0x00000000eac4e408)
thrown [s\src\hotspot\share\classfile\systemDictionary.cpp, line 301]
Event: 31.137 Thread 0x000001f97233bef0 Exception <a 'java/lang/NoSuchMethodError'{0x00000000eb4cc990}: 'void java.lang.invoke.DirectMethodHandle$Holder.invokeStaticInit(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)'> (0x00000000eb4cc990)
thrown [s\src\hotspot\share\interpreter\linkResolver.cpp, line 773]
Event: 31.276 Thread 0x000001f97233d930 Implicit null exception at 0x000001f92250e51f to 0x000001f92250e780
ZGC Phase Switch (0 events):
No events
VM Operations (20 events):
Event: 26.663 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 26.722 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 26.766 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 26.806 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 27.138 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 27.138 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 27.491 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure)
Event: 27.496 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure) done
Event: 27.784 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 27.960 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 28.961 Executing VM operation: Cleanup
Event: 28.975 Executing VM operation: Cleanup done
Event: 29.878 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure)
Event: 29.906 Executing VM operation: ParallelGCFailedAllocation (Allocation Failure) done
Event: 30.312 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 30.328 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 30.939 Executing VM operation: HandshakeAllThreads (Deoptimize)
Event: 30.979 Executing VM operation: HandshakeAllThreads (Deoptimize) done
Event: 30.979 Executing VM operation: Cleanup
Event: 30.997 Executing VM operation: Cleanup done
Memory protections (0 events):
No events
Nmethod flushes (20 events):
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af39910
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af39e10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af3a710
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af3af90
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af3ca10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af3cd10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af48410
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af89890
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af94310
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91af97a10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91afa1790
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91aff9d10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b01f410
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b028610
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b03d290
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b03dc10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b060210
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b094f10
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b095790
Event: 24.347 Thread 0x000001f917782b80 flushing nmethod 0x000001f91b0a7710
Events (20 events):
Event: 24.233 Thread 0x000001f97233b860 Thread added: 0x000001f97447aee0
Event: 25.006 Thread 0x000001f973da69d0 Thread exited: 0x000001f973da69d0
Event: 25.143 Thread 0x000001f9155c5a20 Thread added: 0x000001f974477a60
Event: 25.144 Thread 0x000001f9155c5a20 Thread added: 0x000001f97447bc00
Event: 25.144 Thread 0x000001f9155c5a20 Thread added: 0x000001f97447b570
Event: 25.144 Thread 0x000001f9155c5a20 Thread added: 0x000001f97447c290
Event: 25.144 Thread 0x000001f9155c5a20 Thread added: 0x000001f974476d40
Event: 25.175 Thread 0x000001f9155c5a20 Thread added: 0x000001f97447c920
Event: 25.476 Thread 0x000001f92ba6ce60 Thread added: 0x000001f96da420e0
Event: 26.831 Thread 0x000001f9155c5a20 Thread added: 0x000001f9744794a0
Event: 26.832 Thread 0x000001f9155c5a20 Thread added: 0x000001f974478780
Event: 28.129 Thread 0x000001f96da420e0 Thread exited: 0x000001f96da420e0
Event: 29.304 Thread 0x000001f92b9fe1d0 Thread added: 0x000001f973da6870
Event: 29.622 Thread 0x000001f973da6870 Thread exited: 0x000001f973da6870
Event: 29.839 Thread 0x000001f92ba6ce60 Thread added: 0x000001f973da6870
Event: 30.118 Thread 0x000001f973da6870 Thread exited: 0x000001f973da6870
Event: 30.120 Thread 0x000001f97233bef0 Thread added: 0x000001f974476020
Event: 30.128 Thread 0x000001f974476020 Thread exited: 0x000001f974476020
Event: 30.937 Thread 0x000001f97233bef0 Thread added: 0x000001f97447a850
Event: 31.192 Thread 0x000001f92ba6ce60 Thread added: 0x000001f96dbf7270
Dynamic libraries:
0x00007ff644b60000 - 0x00007ff644b6e000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\java.exe
0x00007ff804d70000 - 0x00007ff804f68000 C:\Windows\SYSTEM32\ntdll.dll
0x00007ff803fd0000 - 0x00007ff804092000 C:\Windows\System32\KERNEL32.DLL
0x00007ff802990000 - 0x00007ff802c8f000 C:\Windows\System32\KERNELBASE.dll
0x00007ff802c90000 - 0x00007ff802d90000 C:\Windows\System32\ucrtbase.dll
0x00007ffffd9d0000 - 0x00007ffffd9e8000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\jli.dll
0x00007ff8045c0000 - 0x00007ff80475d000 C:\Windows\System32\USER32.dll
0x00007ff802960000 - 0x00007ff802982000 C:\Windows\System32\win32u.dll
0x00007ff8033b0000 - 0x00007ff8033db000 C:\Windows\System32\GDI32.dll
0x00007ff8025b0000 - 0x00007ff8026ca000 C:\Windows\System32\gdi32full.dll
0x00007ff8028c0000 - 0x00007ff80295d000 C:\Windows\System32\msvcp_win.dll
0x00007ffffd8e0000 - 0x00007ffffd8fe000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\VCRUNTIME140.dll
0x00007ffff6560000 - 0x00007ffff67fa000 C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.19041.4355_none_60b8b9eb71f62e16\COMCTL32.dll
0x00007ff803bc0000 - 0x00007ff803c5e000 C:\Windows\System32\msvcrt.dll
0x00007ff804c60000 - 0x00007ff804c8f000 C:\Windows\System32\IMM32.DLL
0x00007ffffe0c0000 - 0x00007ffffe0cc000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\vcruntime140_1.dll
0x00007ffffaf30000 - 0x00007ffffafbd000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\msvcp140.dll
0x00007fffb4e20000 - 0x00007fffb5bb0000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\server\jvm.dll
0x00007ff8030e0000 - 0x00007ff80318f000 C:\Windows\System32\ADVAPI32.dll
0x00007ff804c90000 - 0x00007ff804d2f000 C:\Windows\System32\sechost.dll
0x00007ff802fb0000 - 0x00007ff8030d3000 C:\Windows\System32\RPCRT4.dll
0x00007ff802480000 - 0x00007ff8024a7000 C:\Windows\System32\bcrypt.dll
0x00007ff8033e0000 - 0x00007ff80344b000 C:\Windows\System32\WS2_32.dll
0x00007ff802270000 - 0x00007ff8022bb000 C:\Windows\SYSTEM32\POWRPROF.dll
0x00007ffff5f20000 - 0x00007ffff5f47000 C:\Windows\SYSTEM32\WINMM.dll
0x00007ffff7870000 - 0x00007ffff787a000 C:\Windows\SYSTEM32\VERSION.dll
0x00007ff802250000 - 0x00007ff802262000 C:\Windows\SYSTEM32\UMPDC.dll
0x00007ff800c80000 - 0x00007ff800c92000 C:\Windows\SYSTEM32\kernel.appcore.dll
0x00007ffffdab0000 - 0x00007ffffdaba000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\jimage.dll
0x00007fffffe00000 - 0x00007ffffffe4000 C:\Windows\SYSTEM32\DBGHELP.DLL
0x00007ffff1540000 - 0x00007ffff1574000 C:\Windows\SYSTEM32\dbgcore.DLL
0x00007ff8026d0000 - 0x00007ff802752000 C:\Windows\System32\bcryptPrimitives.dll
0x00007ffffd0c0000 - 0x00007ffffd0cf000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\instrument.dll
0x00007ffffaee0000 - 0x00007ffffaeff000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\java.dll
0x00007ff803450000 - 0x00007ff803bbd000 C:\Windows\System32\SHELL32.dll
0x00007ff8001b0000 - 0x00007ff800954000 C:\Windows\SYSTEM32\windows.storage.dll
0x00007ff8040a0000 - 0x00007ff8043f5000 C:\Windows\System32\combase.dll
0x00007ff801d70000 - 0x00007ff801d9b000 C:\Windows\SYSTEM32\Wldp.dll
0x00007ff804490000 - 0x00007ff80455d000 C:\Windows\System32\OLEAUT32.dll
0x00007ff802e20000 - 0x00007ff802ecd000 C:\Windows\System32\SHCORE.dll
0x00007ff803e50000 - 0x00007ff803ea5000 C:\Windows\System32\shlwapi.dll
0x00007ff802340000 - 0x00007ff802365000 C:\Windows\SYSTEM32\profapi.dll
0x00007ffffb2b0000 - 0x00007ffffb2c8000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\zip.dll
0x00007ffffb270000 - 0x00007ffffb280000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\net.dll
0x00007ffffcd70000 - 0x00007ffffce7a000 C:\Windows\SYSTEM32\WINHTTP.dll
0x00007ff801ad0000 - 0x00007ff801b3c000 C:\Windows\system32\mswsock.dll
0x00007ffffaf10000 - 0x00007ffffaf26000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\nio.dll
0x00007ffffb260000 - 0x00007ffffb270000 c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\verify.dll
0x00007ffffad60000 - 0x00007ffffada5000 C:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_win\org.eclipse.equinox.launcher\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.1300.v20250331-1702\eclipse_11911.dll
0x00007ff803c70000 - 0x00007ff803d9b000 C:\Windows\System32\ole32.dll
0x00007ffffe270000 - 0x00007ffffe27a000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management.dll
0x00007ffffe120000 - 0x00007ffffe12b000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\management_ext.dll
0x00007ff803190000 - 0x00007ff803198000 C:\Windows\System32\PSAPI.DLL
0x00007ff801cd0000 - 0x00007ff801ce8000 C:\Windows\SYSTEM32\CRYPTSP.dll
0x00007ff8013f0000 - 0x00007ff801428000 C:\Windows\system32\rsaenh.dll
0x00007ff802300000 - 0x00007ff80232e000 C:\Windows\SYSTEM32\USERENV.dll
0x00007ff801cc0000 - 0x00007ff801ccc000 C:\Windows\SYSTEM32\CRYPTBASE.dll
0x00007ff8017b0000 - 0x00007ff8017eb000 C:\Windows\SYSTEM32\IPHLPAPI.DLL
0x00007ff802e10000 - 0x00007ff802e18000 C:\Windows\System32\NSI.dll
0x00007ffffdb50000 - 0x00007ffffdb99000 C:\Temp\jna-74475\jna16640627127975356240.dll
0x00007ffffa5c0000 - 0x00007ffffa5d7000 C:\Windows\SYSTEM32\dhcpcsvc6.DLL
0x00007ffffa620000 - 0x00007ffffa63d000 C:\Windows\SYSTEM32\dhcpcsvc.DLL
0x00007ffffb2d0000 - 0x00007ffffb2de000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\sunmscapi.dll
0x00007ff802760000 - 0x00007ff8028bc000 C:\Windows\System32\CRYPT32.dll
0x00007ff801de0000 - 0x00007ff801e07000 C:\Windows\SYSTEM32\ncrypt.dll
0x00007ff801da0000 - 0x00007ff801ddb000 C:\Windows\SYSTEM32\NTASN1.dll
0x00007ff8017f0000 - 0x00007ff8018ba000 C:\Windows\SYSTEM32\DNSAPI.dll
0x00007ffff8980000 - 0x00007ffff898a000 C:\Windows\System32\rasadhlp.dll
0x00007ffff9100000 - 0x00007ffff9180000 C:\Windows\System32\fwpuclnt.dll
0x00007ffffb120000 - 0x00007ffffb129000 C:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\extnet.dll
dbghelp: loaded successfully - version: 4.0.5 - missing functions: none
symbol engine: initialized successfully - sym options: 0x614 - pdb path: .;c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin;C:\Windows\SYSTEM32;C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.19041.4355_none_60b8b9eb71f62e16;c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\jre\21.0.7-win32-x86_64\bin\server;C:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_win\org.eclipse.equinox.launcher\org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.1300.v20250331-1702;C:\Temp\jna-74475
VM Arguments:
jvm_args: --add-modules=ALL-SYSTEM --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/sun.nio.fs=ALL-UNNAMED -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Djava.import.generatesMetadataFilesAtProjectRoot=false -DDetectVMInstallationsJob.disabled=true -Dfile.encoding=utf8 -XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -Xlog:disable -javaagent:c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\lombok\lombok-1.18.36.jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java -Daether.dependencyCollector.impl=bf
java_command: c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\server\plugins\org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar -configuration c:\Users\KKK\AppData\Roaming\Trae\User\globalStorage\redhat.java\1.42.0\config_win -data c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java\jdt_ws --pipe=\\.\pipe\lsp-fd3e28ce223fa01906c23ab150737a64-sock
java_class_path (initial): c:\Users\KKK\.trae\extensions\redhat.java-1.42.0-win32-x64\server\plugins\org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar
Launcher Type: SUN_STANDARD
[Global flags]
uintx AdaptiveSizePolicyWeight = 90 {product} {command line}
intx CICompilerCount = 3 {product} {ergonomic}
uintx GCTimeRatio = 4 {product} {command line}
bool HeapDumpOnOutOfMemoryError = true {manageable} {command line}
ccstr HeapDumpPath = c:\Users\KKK\AppData\Roaming\Trae\User\workspaceStorage\70108cdeb054a04f894e832084557c72\redhat.java {manageable} {command line}
size_t InitialHeapSize = 104857600 {product} {command line}
size_t MaxHeapSize = 1073741824 {product} {command line}
size_t MaxNewSize = 357564416 {product} {ergonomic}
size_t MinHeapDeltaBytes = 524288 {product} {ergonomic}
size_t MinHeapSize = 104857600 {product} {command line}
size_t NewSize = 34603008 {product} {ergonomic}
uintx NonNMethodCodeHeapSize = 5832780 {pd product} {ergonomic}
uintx NonProfiledCodeHeapSize = 122912730 {pd product} {ergonomic}
size_t OldSize = 70254592 {product} {ergonomic}
uintx ProfiledCodeHeapSize = 122912730 {pd product} {ergonomic}
uintx ReservedCodeCacheSize = 251658240 {pd product} {ergonomic}
bool SegmentedCodeCache = true {product} {ergonomic}
size_t SoftMaxHeapSize = 1073741824 {manageable} {ergonomic}
bool UseCompressedOops = true {product lp64_product} {ergonomic}
bool UseLargePagesIndividualAllocation = false {pd product} {ergonomic}
bool UseParallelGC = true {product} {command line}
Logging:
Log output configuration:
#0: stdout all=off uptime,level,tags foldmultilines=false
#1: stderr all=off uptime,level,tags foldmultilines=false
Environment Variables:
JAVA_HOME=C:\Users\KKK\Desktop\java\java17
PATH=c:\Users\KKK\.trae\sdks\workspaces\70108cde\versions\node\current;c:\Users\KKK\.trae\sdks\versions\node\current;C:\Users\KKK\Desktop\PhotoTaking\PhotoTaking\PhotoTaking\bin\Debug\\adbs;C:\Users\KKK\Desktop\java\java17\bin;C:\Users\KKK\Desktop\apache-maven-3.9.9\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\Users\KKK\Desktop\java\java17\bin;C:\Users\KKK\Desktop\java\java17\jre\bin;C:\Program Files\Bandizip\;C:\Program Files\Go\bin;c:\Users\KKK\AppData\Local\Programs\cursor\resources\app\bin;C:\Program Files\dotnet\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\Users\KKK\AppData\Local\Microsoft\WindowsApps;C:\Users\KKK\AppData\Local\Programs\Windsurf\bin;C:\Users\KKK\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\KKK\AppData\Local\Programs\Trae\bin;c:\Users\KKK\AppData\Local\Programs\Trae\bin;C:\Users\KKK\go\bin;C:\Users\KKK\AppData\Local\Programs\cursor\resources\app\bin;C:\Users\KKK\.dotnet\tools;c:\Users\KKK\AppData\Local\Programs\Trae\resources\app\bin\lib
USERNAME=KKK
OS=Windows_NT
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 61 Stepping 4, GenuineIntel
TMP=C:\Temp
TEMP=C:\Temp
Periodic native trim disabled
--------------- S Y S T E M ---------------
OS:
Windows 10 , 64 bit Build 19041 (10.0.19041.5438)
OS uptime: 0 days 14:50 hours
CPU: total 4 (initial active 4) (2 cores per cpu, 2 threads per core) family 6 model 61 stepping 4 microcode 0x2d, cx8, cmov, fxsr, ht, mmx, 3dnowpref, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, lzcnt, tsc, tscinvbit, avx, avx2, aes, erms, clmul, bmi1, bmi2, adx, fma, vzeroupper, clflush, rdtscp, f16c
Processor Information for the first 4 processors :
Max Mhz: 2394, Current Mhz: 2394, Mhz Limit: 2394
Memory: 4k page, system-wide physical 8096M (1136M free)
TotalPageFile size 8096M (AvailPageFile size 5M)
current process WorkingSet (physical memory assigned to process): 216M, peak: 216M
current process commit charge ("private bytes"): 287M, peak: 287M
vm_info: OpenJDK 64-Bit Server VM (21.0.7+6-LTS) for windows-amd64 JRE (21.0.7+6-LTS), built on 2025-04-15T00:00:00Z by "admin" with MS VC++ 17.7 (VS2022)
END.

@ -1,8 +1,6 @@
package main
import (
"io/ioutil"
"github.com/sirupsen/logrus"
)
@ -10,11 +8,20 @@ var log = logrus.New()
func init() {
//TODO: next add write to file
if !debug {
log.SetOutput(ioutil.Discard)
}
// Temporarily disable log output redirection for debugging
// if !debug {
// log.SetOutput(ioutil.Discard)
// }
log.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
})
log.SetLevel(Storage.ServerLogLevel())
// Set debug level temporarily - avoid circular dependency
log.SetLevel(logrus.DebugLevel)
}
// SetLogLevel sets the log level after Storage is initialized
func SetLogLevel() {
if Storage != nil {
log.SetLevel(Storage.ServerLogLevel())
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -20,7 +20,7 @@ import (
var debug bool
var configFile string
//NewStreamCore do load config file
// NewStreamCore do load config file
func NewStreamCore() *StorageST {
flag.BoolVar(&debug, "debug", true, "set debug mode")
flag.StringVar(&configFile, "config", "config.json", "config patch (/etc/server/config.json or config.json)")
@ -46,9 +46,40 @@ func NewStreamCore() *StorageST {
os.Exit(1)
}
debug = tmp.Server.Debug
// 同步数据库启用状态
if tmp.Server.DatabaseEnabled {
tmp.Database.Enabled = true
}
// 初始化数据库连接
if tmp.Database.Enabled {
err = tmp.InitDatabase()
if err != nil {
log.WithFields(logrus.Fields{
"module": "config",
"func": "NewStreamCore",
"call": "InitDatabase",
}).Errorln(err.Error())
os.Exit(1)
}
// 从数据库加载流配置
err = tmp.LoadStreamsFromDatabase()
if err != nil {
log.WithFields(logrus.Fields{
"module": "config",
"func": "NewStreamCore",
"call": "LoadStreamsFromDatabase",
}).Errorln(err.Error())
os.Exit(1)
}
}
// 处理流配置(从配置文件或数据库)
for i, i2 := range tmp.Streams {
for i3, i4 := range i2.Channels {
channel := tmp.ChannelDefaults
var channel ChannelST
err = mergo.Merge(&channel, i4)
if err != nil {
log.WithFields(logrus.Fields{
@ -66,10 +97,11 @@ func NewStreamCore() *StorageST {
}
tmp.Streams[i] = i2
}
return &tmp
}
//ClientDelete Delete Client
// ClientDelete Delete Client
func (obj *StorageST) SaveConfig() error {
log.WithFields(logrus.Fields{
"module": "config",

@ -0,0 +1,270 @@
package main
import (
"fmt"
"time"
"github.com/sirupsen/logrus"
)
// InitDatabase 初始化数据库连接
func (obj *StorageST) InitDatabase() error {
obj.mutex.Lock()
defer obj.mutex.Unlock()
if !obj.Database.Enabled {
log.WithFields(logrus.Fields{
"module": "database",
"func": "InitDatabase",
}).Infoln("Database is disabled")
return nil
}
dbManager, err := NewDatabaseManager(obj.Database)
if err != nil {
log.WithFields(logrus.Fields{
"module": "database",
"func": "InitDatabase",
"call": "NewDatabaseManager",
}).Errorln(err.Error())
return err
}
obj.dbManager = dbManager
log.WithFields(logrus.Fields{
"module": "database",
"func": "InitDatabase",
}).Infoln("Database initialized successfully")
return nil
}
// LoadStreamsFromDatabase 从数据库加载流配置
func (obj *StorageST) LoadStreamsFromDatabase() error {
obj.mutex.Lock()
defer obj.mutex.Unlock()
if obj.dbManager == nil {
return fmt.Errorf("database manager not initialized")
}
cameras, err := obj.dbManager.GetAllCameras()
if err != nil {
log.WithFields(logrus.Fields{
"module": "database",
"func": "LoadStreamsFromDatabase",
"call": "GetAllCameras",
}).Errorln(err.Error())
return err
}
// 清空现有流配置(如果启用数据库模式)
if obj.Server.DatabaseEnabled {
obj.Streams = make(map[string]StreamST)
}
// 将摄像头转换为流配置
for _, camera := range cameras {
stream := CameraToStream(camera)
streamID := fmt.Sprintf("%d", camera.CameraID)
obj.Streams[streamID] = stream
log.WithFields(logrus.Fields{
"module": "database",
"func": "LoadStreamsFromDatabase",
"camera": camera.CameraName,
"id": camera.CameraID,
}).Infoln("Loaded camera from database")
}
log.WithFields(logrus.Fields{
"module": "database",
"func": "LoadStreamsFromDatabase",
"count": len(cameras),
}).Infoln("Loaded cameras from database")
return nil
}
// SyncStreamToDatabase 同步流配置到数据库
func (obj *StorageST) SyncStreamToDatabase(streamID string, stream StreamST) error {
if obj.dbManager == nil || !obj.Server.DatabaseEnabled {
return nil // 数据库未启用,跳过同步
}
// 检查摄像头是否已存在
existingCamera, err := obj.dbManager.GetCameraByID(streamID)
if err != nil {
return err
}
camera := StreamToCamera(streamID, stream)
if existingCamera == nil {
// 创建新摄像头
err = obj.dbManager.CreateCamera(&camera)
if err != nil {
log.WithFields(logrus.Fields{
"module": "database",
"func": "SyncStreamToDatabase",
"call": "CreateCamera",
"id": streamID,
}).Errorln(err.Error())
return err
}
log.WithFields(logrus.Fields{
"module": "database",
"func": "SyncStreamToDatabase",
"id": streamID,
"name": camera.CameraName,
}).Infoln("Created camera in database")
} else {
// 更新现有摄像头
camera.CreateTime = existingCamera.CreateTime // 保持创建时间
err = obj.dbManager.UpdateCamera(&camera)
if err != nil {
log.WithFields(logrus.Fields{
"module": "database",
"func": "SyncStreamToDatabase",
"call": "UpdateCamera",
"id": streamID,
}).Errorln(err.Error())
return err
}
log.WithFields(logrus.Fields{
"module": "database",
"func": "SyncStreamToDatabase",
"id": streamID,
"name": camera.CameraName,
}).Infoln("Updated camera in database")
}
return nil
}
// DeleteStreamFromDatabase 从数据库删除流配置
func (obj *StorageST) DeleteStreamFromDatabase(streamID string) error {
if obj.dbManager == nil || !obj.Server.DatabaseEnabled {
return nil // 数据库未启用,跳过删除
}
err := obj.dbManager.DeleteCamera(streamID)
if err != nil {
log.WithFields(logrus.Fields{
"module": "database",
"func": "DeleteStreamFromDatabase",
"call": "DeleteCamera",
"id": streamID,
}).Errorln(err.Error())
return err
}
log.WithFields(logrus.Fields{
"module": "database",
"func": "DeleteStreamFromDatabase",
"id": streamID,
}).Infoln("Deleted camera from database")
return nil
}
// RefreshStreamsFromDatabase 刷新数据库中的流配置
func (obj *StorageST) RefreshStreamsFromDatabase() error {
if obj.dbManager == nil || !obj.Server.DatabaseEnabled {
return nil
}
// 停止所有现有流
obj.StopAll()
// 等待流停止
time.Sleep(2 * time.Second)
// 重新加载流配置
err := obj.LoadStreamsFromDatabase()
if err != nil {
return err
}
// 启动所有流
obj.StreamChannelRunAll()
log.WithFields(logrus.Fields{
"module": "database",
"func": "RefreshStreamsFromDatabase",
}).Infoln("Refreshed streams from database")
return nil
}
// GetDatabaseStatus 获取数据库状态
func (obj *StorageST) GetDatabaseStatus() map[string]interface{} {
status := map[string]interface{}{
"enabled": obj.Database.Enabled,
"type": obj.Database.Type,
"host": obj.Database.Host,
"port": obj.Database.Port,
"database": obj.Database.Database,
"connected": false,
"error": nil,
}
if obj.dbManager != nil && obj.dbManager.db != nil {
if err := obj.dbManager.db.Ping(); err == nil {
status["connected"] = true
} else {
status["error"] = err.Error()
}
}
return status
}
// DatabaseGetCameras 获取所有摄像头
func (obj *StorageST) DatabaseGetCameras() ([]Camera, error) {
obj.mutex.RLock()
defer obj.mutex.RUnlock()
if obj.dbManager == nil {
return nil, fmt.Errorf("database manager not initialized")
}
return obj.dbManager.GetAllCameras()
}
// DatabaseGetCamera 根据ID获取摄像头
func (obj *StorageST) DatabaseGetCamera(id string) (*Camera, error) {
obj.mutex.RLock()
defer obj.mutex.RUnlock()
if obj.dbManager == nil {
return nil, fmt.Errorf("database manager not initialized")
}
return obj.dbManager.GetCameraByID(id)
}
// StreamExist 检查流是否存在
func (obj *StorageST) StreamExist(uuid string) bool {
obj.mutex.RLock()
defer obj.mutex.RUnlock()
_, ok := obj.Streams[uuid]
return ok
}
// CloseDatabase 关闭数据库连接
func (obj *StorageST) CloseDatabase() error {
obj.mutex.Lock()
defer obj.mutex.Unlock()
if obj.dbManager != nil {
err := obj.dbManager.Close()
obj.dbManager = nil
log.WithFields(logrus.Fields{
"module": "database",
"func": "CloseDatabase",
}).Infoln("Database connection closed")
return err
}
return nil
}

@ -42,10 +42,20 @@ func (obj *StorageST) StreamAdd(uuid string, val StreamST) error {
}
}
obj.Streams[uuid] = val
err := obj.SaveConfig()
// 同步到数据库
err := obj.SyncStreamToDatabase(uuid, val)
if err != nil {
return err
}
// 如果未启用数据库,保存到配置文件
if !obj.Server.DatabaseEnabled {
err = obj.SaveConfig()
if err != nil {
return err
}
}
return nil
}
@ -72,10 +82,20 @@ func (obj *StorageST) StreamEdit(uuid string, val StreamST) error {
}
}
obj.Streams[uuid] = val
err := obj.SaveConfig()
// 同步到数据库
err := obj.SyncStreamToDatabase(uuid, val)
if err != nil {
return err
}
// 如果未启用数据库,保存到配置文件
if !obj.Server.DatabaseEnabled {
err = obj.SaveConfig()
if err != nil {
return err
}
}
return nil
}
return ErrorStreamNotFound
@ -120,10 +140,20 @@ func (obj *StorageST) StreamDelete(uuid string) error {
}
}
delete(obj.Streams, uuid)
err := obj.SaveConfig()
// 从数据库删除
err := obj.DeleteStreamFromDatabase(uuid)
if err != nil {
return err
}
// 如果未启用数据库,保存到配置文件
if !obj.Server.DatabaseEnabled {
err = obj.SaveConfig()
if err != nil {
return err
}
}
return nil
}
return ErrorStreamNotFound

@ -12,20 +12,20 @@ import (
var Storage = NewStreamCore()
//Default stream type
// Default stream type
const (
MSE = iota
WEBRTC
RTSP
)
//Default stream status type
// Default stream status type
const (
OFFLINE = iota
ONLINE
)
//Default stream errors
// Default stream errors
var (
Success = "success"
ErrorStreamNotFound = errors.New("stream not found")
@ -43,15 +43,17 @@ var (
ErrorStreamUnauthorized = errors.New("stream request unauthorized")
)
//StorageST main storage struct
// StorageST main storage struct
type StorageST struct {
mutex sync.RWMutex
Server ServerST `json:"server" groups:"api,config"`
Streams map[string]StreamST `json:"streams,omitempty" groups:"api,config"`
ChannelDefaults ChannelST `json:"channel_defaults,omitempty" groups:"api,config"`
Database DatabaseConfig `json:"database,omitempty" groups:"api,config"`
dbManager *DatabaseManager // 数据库管理器,不序列化
}
//ServerST server storage section
// ServerST server storage section
type ServerST struct {
Debug bool `json:"debug" groups:"api,config"`
LogLevel logrus.Level `json:"log_level" groups:"api,config"`
@ -74,15 +76,16 @@ type ServerST struct {
Token Token `json:"token,omitempty" groups:"api,config"`
WebRTCPortMin uint16 `json:"webrtc_port_min" groups:"api,config"`
WebRTCPortMax uint16 `json:"webrtc_port_max" groups:"api,config"`
DatabaseEnabled bool `json:"database_enabled" groups:"api,config"`
}
//Token auth
// Token auth
type Token struct {
Enable bool `json:"enable" groups:"api,config"`
Backend string `json:"backend" groups:"api,config"`
}
//ServerST stream storage section
// ServerST stream storage section
type StreamST struct {
Name string `json:"name,omitempty" groups:"api,config"`
Channels map[string]ChannelST `json:"channels,omitempty" groups:"api,config"`
@ -107,7 +110,7 @@ type ChannelST struct {
hlsMuxer *MuxerHLS `json:"-"`
}
//ClientST client storage section
// ClientST client storage section
type ClientST struct {
mode int
signals chan int
@ -116,7 +119,7 @@ type ClientST struct {
socket net.Conn
}
//SegmentOld HLS cache section
// SegmentOld HLS cache section
type SegmentOld struct {
dur time.Duration
data []*av.Packet

Binary file not shown.

@ -0,0 +1,318 @@
/**
* 摄像头管理器 - 优先通过API获取摄像头列表
* 支持降级到流列表显示
*/
class CameraManager {
constructor() {
this.cameras = [];
this.streams = [];
this.loadingIndicator = document.getElementById('loading-indicator');
this.cameraContainer = document.getElementById('camera-list-container');
this.streamContainer = document.getElementById('stream-list-container');
this.init();
}
async init() {
try {
// 优先尝试获取摄像头列表
await this.loadCameras();
} catch (error) {
console.warn('摄像头API不可用降级到流列表:', error);
// 降级到流列表
await this.loadStreams();
}
}
async loadCameras() {
try {
const response = await fetch('/cameras');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.cameras && data.cameras.length > 0) {
this.cameras = data.cameras;
this.renderCameras();
this.updateTitle(`摄像头 (${this.cameras.length})`);
} else {
throw new Error('摄像头列表为空');
}
} catch (error) {
console.error('加载摄像头失败:', error);
throw error;
}
}
async loadStreams() {
try {
const response = await fetch('/streams');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.status === 1 && data.payload) {
this.streams = Object.entries(data.payload).map(([key, value]) => ({
id: key,
...value
}));
this.showStreamFallback();
this.updateTitle(`视频流 (${this.streams.length})`);
} else {
this.showEmptyState();
}
} catch (error) {
console.error('加载流列表失败:', error);
this.showEmptyState();
}
}
renderCameras() {
this.hideLoading();
this.cameraContainer.style.display = 'flex';
this.cameraContainer.innerHTML = '';
this.cameras.forEach(camera => {
const cameraCard = this.createCameraCard(camera);
this.cameraContainer.appendChild(cameraCard);
});
}
createCameraCard(camera) {
const col = document.createElement('div');
col.className = 'col-12 col-sm-6 col-md-3';
col.id = `camera-${camera.id || camera.camera_id}`;
const statusClass = camera.status === 'online' ? 'badge-success' : 'badge-secondary';
const statusText = camera.status === 'online' ? '在线' : '离线';
const enabledBadge = camera.enabled ?
'<span class="badge badge-success ml-1">已启用</span>' :
'<span class="badge badge-warning ml-1">已禁用</span>';
// 构建播放按钮
const playButtons = this.createPlayButtons(camera);
col.innerHTML = `
<div class="card card-outline ${camera.status === 'online' ? 'card-success' : 'card-secondary'}">
<div class="card-header">
<h3 class="card-title one-line-header">${camera.name || camera.camera_name || '未命名摄像头'}</h3>
<div class="card-tools">
<span class="badge ${statusClass}">${statusText}</span>
${enabledBadge}
</div>
</div>
<div class="card-body p-0">
<div class="camera-preview">
<img class="d-block w-100 stream-img fix-height"
src="/../static/img/noimage.svg"
alt="${camera.name || camera.camera_name}">
<div class="camera-info p-2">
<small class="text-muted">
<strong>IP:</strong> ${camera.ip}<br>
<strong>类型:</strong> ${camera.device_type || ''}<br>
${camera.unit_code ? `<strong>单位:</strong> ${camera.unit_code}<br>` : ''}
</small>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="btn-group stream">
${playButtons}
<a class="btn btn-secondary btn-flat btn-xs" href="/pages/cameras" title="管理摄像头">
<i class="fas fa-cog"></i>
</a>
${camera.enabled ?
`<a class="btn btn-info btn-flat btn-xs" href="/pages/player/all/${camera.id || camera.camera_id}/0" title="预览">
<i class="fas fa-eye"></i>
</a>` :
'<span class="btn btn-secondary btn-flat btn-xs disabled">已禁用</span>'
}
</div>
</div>
</div>
</div>
</div>
`;
return col;
}
createPlayButtons(camera) {
if (!camera.enabled || camera.status !== 'online') {
return '<span class="btn btn-secondary btn-flat btn-xs disabled">离线</span>';
}
const cameraId = camera.id || camera.camera_id;
return `
<a class="btn btn-info btn-flat btn-xs" href="/pages/player/mse/${cameraId}/0">
<i class="fas fa-play"></i> MSE
</a>
<a class="btn btn-info btn-flat btn-xs" href="/pages/player/hls/${cameraId}/0">
<i class="fas fa-play"></i> HLS
</a>
<a class="btn btn-info btn-flat btn-xs" href="/pages/player/webrtc/${cameraId}/0">
<i class="fas fa-play"></i> WebRTC
</a>
<a class="btn btn-info btn-flat btn-xs" href="/pages/player/all/${cameraId}/0">
<i class="fas fa-play"></i> ALL
</a>
`;
}
showStreamFallback() {
this.hideLoading();
this.streamContainer.style.display = 'flex';
console.info('使用流列表降级方案');
}
showEmptyState() {
this.hideLoading();
this.cameraContainer.style.display = 'flex';
this.cameraContainer.innerHTML = `
<div class="col-12">
<div class="alert alert-info text-center">
<i class="fas fa-info-circle"></i>
暂无可用的摄像头或视频流
<br><br>
<a href="/pages/cameras" class="btn btn-primary btn-sm">
<i class="fas fa-plus"></i>
</a>
<a href="/pages/stream/add" class="btn btn-success btn-sm ml-2">
<i class="fas fa-plus"></i>
</a>
</div>
</div>
`;
}
hideLoading() {
if (this.loadingIndicator) {
this.loadingIndicator.style.display = 'none';
}
}
updateTitle(title) {
const titleElement = document.querySelector('h5.mt-4.mb-2');
if (titleElement) {
titleElement.textContent = title;
}
}
// 刷新摄像头列表
async refresh() {
this.showLoading();
await this.init();
}
showLoading() {
this.cameraContainer.style.display = 'none';
this.streamContainer.style.display = 'none';
this.loadingIndicator.style.display = 'flex';
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 确保在其他脚本加载后再初始化
setTimeout(() => {
window.cameraManager = new CameraManager();
}, 100);
});
// 为Java项目集成提供的全局API
window.RTSPtoWebAPI = {
// 获取摄像头列表
async getCameras() {
const response = await fetch('/cameras');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 获取流列表
async getStreams() {
const response = await fetch('/streams');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 获取特定摄像头信息
async getCamera(cameraId) {
const response = await fetch(`/camera/${cameraId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 添加摄像头
async addCamera(cameraData) {
const response = await fetch('/camera/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(cameraData)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 更新摄像头
async updateCamera(cameraId, cameraData) {
const response = await fetch(`/camera/${cameraId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(cameraData)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 删除摄像头
async deleteCamera(cameraId) {
const response = await fetch(`/camera/${cameraId}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 刷新摄像头状态
async refreshCameras() {
const response = await fetch('/cameras/refresh', {
method: 'POST'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
},
// 获取播放URL
getPlayUrl(cameraId, channel = 0, type = 'hls') {
const baseUrl = window.location.origin;
switch (type.toLowerCase()) {
case 'hls':
return `${baseUrl}/stream/${cameraId}/channel/${channel}/hls/live/index.m3u8`;
case 'webrtc':
return `${baseUrl}/pages/player/webrtc/${cameraId}/${channel}`;
case 'mse':
return `${baseUrl}/pages/player/mse/${cameraId}/${channel}`;
default:
return `${baseUrl}/pages/player/all/${cameraId}/${channel}`;
}
}
};

@ -3,12 +3,12 @@
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">Add stream</h1>
<h1 class="m-0 text-dark">添加流</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">Add stream</li>
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active">添加流</li>
</ol>
</div>
</div>
@ -25,35 +25,35 @@
<div class="col-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Main channel<small> parameters</small></h3>
<h3 class="card-title">主通道<small> 参数</small></h3>
</div>
<div class="card-body">
<form class="stream-form main-form">
<div class="form-group">
<label for="exampleInputEmail1">Stream name</label>
<input type="text" class="form-control" name="stream-name" placeholder="Enter stream name" id="stream-name">
<small class="form-text text-muted">You can choose any name for the stream, for example "My room" or "Happy sausage"</small>
<label for="exampleInputEmail1">流名称</label>
<input type="text" class="form-control" name="stream-name" placeholder="输入流名称" id="stream-name">
<small class="form-text text-muted">您可以为流选择任何名称,例如"我的房间"或"客厅"</small>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Stream url</label>
<input type="text" name="stream-url" class="form-control" placeholder="Enter stream url">
<small class="form-text text-muted">Enter rtsp address as instructed by your camera. Look like <code>rtsp://&lt;ip&gt;:&lt;port&gt;/path </code> </small>
<label for="exampleInputPassword1">流地址</label>
<input type="text" name="stream-url" class="form-control" placeholder="输入流地址">
<small class="form-text text-muted">按照摄像头说明输入rtsp地址。格式如 <code>rtsp://&lt;ip&gt;:&lt;port&gt;/path </code> </small>
</div>
<div class="form-group">
<label for="inputStatus">Stream type</label>
<label for="inputStatus">流类型</label>
<select class="form-control custom-select" name="stream-ondemand">
<option selected disabled><small>Select One</small></option>
<option value="1">On demand only</option>
<option value="0">Persistent connection</option>
<option selected disabled><small>请选择</small></option>
<option value="1">按需连接</option>
<option value="0">持续连接</option>
</select>
<small class="form-text text-muted">On persistent connection, the server get data from the camera continuously. On demand, the server get data from the camera only when you click play button </small>
<small class="form-text text-muted">持续连接时,服务器会持续从摄像头获取数据。按需连接时,服务器只在您点击播放按钮时才从摄像头获取数据 </small>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="debug" id="debug-switch">
<label class="custom-control-label" for="debug-switch">Enable debug</label>
<label class="custom-control-label" for="debug-switch">启用调试</label>
</div>
<small class="form-text text-muted">Select this options if you want get more data about the stream </small>
<small class="form-text text-muted">如果您想获取更多关于流的数据,请选择此选项 </small>
</div>
</form>
</div>
@ -63,8 +63,8 @@
<div class="row mb-3">
<div class="col-12">
<button type="button" onclick="addChannel()" class="btn btn-secondary">Add channel</button>
<button type="button" onclick="addStreamSubmit()" class="btn btn-primary">Save stream</button>
<button type="button" onclick="addChannel()" class="btn btn-secondary">添加通道</button>
<button type="button" onclick="addStreamSubmit()" class="btn btn-primary">保存流</button>
</div>
</div>
</div>

@ -0,0 +1,548 @@
{{template "head.tmpl" .}}
<style>
.camera-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.camera-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.camera-card.online {
border-left: 4px solid #28a745;
}
.camera-card.offline {
border-left: 4px solid #dc3545;
}
.status-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
}
.status-online {
background: #d4edda;
color: #155724;
}
.status-offline {
background: #f8d7da;
color: #721c24;
}
.api-controls {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.btn-group {
margin: 5px;
}
.loading {
text-align: center;
padding: 40px;
color: #6c757d;
}
.error {
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
.success {
background: #d4edda;
color: #155724;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
}
.video-preview {
width: 100%;
height: 200px;
background: #000;
border-radius: 4px;
margin: 10px 0;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.api-response {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 15px;
margin: 10px 0;
font-family: monospace;
font-size: 12px;
max-height: 300px;
overflow-y: auto;
}
</style>
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">API演示页面</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active">API演示</li>
</ol>
</div>
</div>
</div>
</div>
<div class="content">
<div class="container-fluid">
<!-- API控制面板 -->
<div class="api-controls">
<h4><i class="fas fa-code"></i> API控制面板</h4>
<p class="text-muted">此页面演示如何通过JavaScript API与RTSPtoWeb服务交互适用于Java项目集成参考。</p>
<div class="row">
<div class="col-md-6">
<h5>摄像头管理</h5>
<div class="btn-group">
<button class="btn btn-primary" onclick="loadCamerasAPI()">获取摄像头列表</button>
<button class="btn btn-info" onclick="refreshCamerasAPI()">刷新状态</button>
<button class="btn btn-success" onclick="showAddCameraForm()">添加摄像头</button>
</div>
</div>
<div class="col-md-6">
<h5>流管理</h5>
<div class="btn-group">
<button class="btn btn-secondary" onclick="loadStreamsAPI()">获取流列表</button>
<button class="btn btn-warning" onclick="clearDisplay()">清空显示</button>
<button class="btn btn-dark" onclick="showAPIResponse()">显示API响应</button>
</div>
</div>
</div>
</div>
<!-- 状态显示 -->
<div id="status-message"></div>
<!-- 摄像头列表 -->
<div id="camera-display">
<div class="loading">
<i class="fas fa-info-circle"></i> 点击上方按钮开始API演示
</div>
</div>
<!-- API响应显示 -->
<div id="api-response-container" style="display: none;">
<h4>API响应数据</h4>
<pre id="api-response" class="api-response"></pre>
</div>
<!-- 添加摄像头表单 -->
<div class="modal fade" id="addCameraModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">添加摄像头</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<form id="addCameraForm">
<div class="form-group">
<label>摄像头名称</label>
<input type="text" class="form-control" id="cameraName" required>
</div>
<div class="form-group">
<label>IP地址</label>
<input type="text" class="form-control" id="cameraIP" required>
</div>
<div class="form-group">
<label>端口</label>
<input type="number" class="form-control" id="cameraPort" value="554">
</div>
<div class="form-group">
<label>用户名</label>
<input type="text" class="form-control" id="cameraUsername">
</div>
<div class="form-group">
<label>密码</label>
<input type="password" class="form-control" id="cameraPassword">
</div>
<div class="form-group">
<label>RTSP URL</label>
<input type="text" class="form-control" id="cameraRTSP" placeholder="rtsp://username:password@ip:port/stream">
</div>
<div class="form-group">
<label>单位代码</label>
<input type="text" class="form-control" id="cameraUnitCode">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="addCameraAPI()">添加</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// API演示脚本
let lastAPIResponse = null;
// 显示状态消息
function showStatus(message, type = 'info') {
const statusDiv = document.getElementById('status-message');
const alertClass = type === 'error' ? 'error' : (type === 'success' ? 'success' : 'alert alert-info');
statusDiv.innerHTML = `<div class="${alertClass}">${message}</div>`;
// 3秒后自动清除
setTimeout(() => {
statusDiv.innerHTML = '';
}, 3000);
}
// 加载摄像头列表
async function loadCamerasAPI() {
try {
showStatus('正在获取摄像头列表...', 'info');
const response = await fetch('/cameras');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
lastAPIResponse = data;
if (data.cameras && data.cameras.length > 0) {
displayCameras(data.cameras);
showStatus(`成功获取 ${data.cameras.length} 个摄像头`, 'success');
} else {
showEmptyState('暂无摄像头数据');
showStatus('摄像头列表为空', 'info');
}
} catch (error) {
console.error('获取摄像头失败:', error);
showStatus(`获取摄像头失败: ${error.message}`, 'error');
showEmptyState('获取摄像头失败');
}
}
// 加载流列表
async function loadStreamsAPI() {
try {
showStatus('正在获取流列表...', 'info');
const response = await fetch('/streams');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
lastAPIResponse = data;
if (data.status === 1 && data.payload) {
const streams = Object.entries(data.payload).map(([key, value]) => ({
id: key,
...value
}));
displayStreams(streams);
showStatus(`成功获取 ${streams.length} 个视频流`, 'success');
} else {
showEmptyState('暂无流数据');
showStatus('流列表为空', 'info');
}
} catch (error) {
console.error('获取流列表失败:', error);
showStatus(`获取流列表失败: ${error.message}`, 'error');
showEmptyState('获取流列表失败');
}
}
// 刷新摄像头状态
async function refreshCamerasAPI() {
try {
showStatus('正在刷新摄像头状态...', 'info');
const response = await fetch('/cameras/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
lastAPIResponse = data;
showStatus('摄像头状态刷新成功', 'success');
// 重新加载摄像头列表
setTimeout(() => {
loadCamerasAPI();
}, 1000);
} catch (error) {
console.error('刷新摄像头状态失败:', error);
showStatus(`刷新失败: ${error.message}`, 'error');
}
}
// 显示摄像头列表
function displayCameras(cameras) {
const container = document.getElementById('camera-display');
container.innerHTML = '<h4><i class="fas fa-camera"></i> 摄像头列表</h4>';
const grid = document.createElement('div');
grid.className = 'camera-grid';
cameras.forEach(camera => {
const card = createCameraCard(camera);
grid.appendChild(card);
});
container.appendChild(grid);
}
// 显示流列表
function displayStreams(streams) {
const container = document.getElementById('camera-display');
container.innerHTML = '<h4><i class="fas fa-video"></i> 视频流列表</h4>';
const grid = document.createElement('div');
grid.className = 'camera-grid';
streams.forEach(stream => {
const card = createStreamCard(stream);
grid.appendChild(card);
});
container.appendChild(grid);
}
// 创建摄像头卡片
function createCameraCard(camera) {
const card = document.createElement('div');
const statusClass = camera.status === 'online' ? 'online' : 'offline';
const statusBadgeClass = camera.status === 'online' ? 'status-online' : 'status-offline';
card.className = `camera-card ${statusClass}`;
card.innerHTML = `
<h5>${camera.name || camera.camera_name || '未命名摄像头'}</h5>
<div class="mb-2">
<span class="status-badge ${statusBadgeClass}">${camera.status === 'online' ? '在线' : '离线'}</span>
${camera.enabled ? '<span class="badge badge-success ml-1">已启用</span>' : '<span class="badge badge-warning ml-1">已禁用</span>'}
</div>
<div class="video-preview">
<i class="fas fa-video fa-2x"></i>
</div>
<p><strong>IP:</strong> ${camera.ip}</p>
<p><strong>类型:</strong> ${camera.device_type || '网络摄像头'}</p>
${camera.unit_code ? `<p><strong>单位:</strong> ${camera.unit_code}</p>` : ''}
<div class="btn-group-vertical w-100">
<button class="btn btn-sm btn-info" onclick="playCamera('${camera.id || camera.camera_id}', 'hls')">HLS播放</button>
<button class="btn btn-sm btn-success" onclick="playCamera('${camera.id || camera.camera_id}', 'webrtc')">WebRTC播放</button>
<button class="btn btn-sm btn-warning" onclick="getCameraDetails('${camera.id || camera.camera_id}')">查看详情</button>
</div>
`;
return card;
}
// 创建流卡片
function createStreamCard(stream) {
const card = document.createElement('div');
card.className = 'camera-card online';
const channelCount = Object.keys(stream.channels || {}).length;
card.innerHTML = `
<h5>${stream.name || '未命名流'}</h5>
<div class="mb-2">
<span class="status-badge status-online">活跃</span>
<span class="badge badge-info ml-1">${channelCount} 通道</span>
</div>
<div class="video-preview">
<i class="fas fa-play-circle fa-2x"></i>
</div>
<p><strong>流ID:</strong> ${stream.id}</p>
<div class="btn-group-vertical w-100">
<button class="btn btn-sm btn-info" onclick="playStream('${stream.id}', 'hls')">HLS播放</button>
<button class="btn btn-sm btn-success" onclick="playStream('${stream.id}', 'webrtc')">WebRTC播放</button>
<button class="btn btn-sm btn-primary" onclick="playStream('${stream.id}', 'all')">通用播放</button>
</div>
`;
return card;
}
// 播放摄像头
function playCamera(cameraId, type) {
const url = getPlayUrl(cameraId, 0, type);
window.open(url, '_blank');
showStatus(`正在打开 ${type.toUpperCase()} 播放器...`, 'info');
}
// 播放流
function playStream(streamId, type) {
const url = getPlayUrl(streamId, 0, type);
window.open(url, '_blank');
showStatus(`正在打开 ${type.toUpperCase()} 播放器...`, 'info');
}
// 获取播放URL
function getPlayUrl(id, channel = 0, type = 'hls') {
const baseUrl = window.location.origin;
switch (type.toLowerCase()) {
case 'hls':
return `${baseUrl}/stream/${id}/channel/${channel}/hls/live/index.m3u8`;
case 'webrtc':
return `${baseUrl}/pages/player/webrtc/${id}/${channel}`;
case 'mse':
return `${baseUrl}/pages/player/mse/${id}/${channel}`;
default:
return `${baseUrl}/pages/player/all/${id}/${channel}`;
}
}
// 获取摄像头详情
async function getCameraDetails(cameraId) {
try {
const response = await fetch(`/camera/${cameraId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const camera = await response.json();
lastAPIResponse = camera;
alert(`摄像头详情:\n名称: ${camera.name}\nIP: ${camera.ip}\n状态: ${camera.status}\n创建时间: ${camera.created_at}`);
} catch (error) {
showStatus(`获取摄像头详情失败: ${error.message}`, 'error');
}
}
// 显示添加摄像头表单
function showAddCameraForm() {
$('#addCameraModal').modal('show');
}
// 添加摄像头
async function addCameraAPI() {
try {
const formData = {
name: document.getElementById('cameraName').value,
ip: document.getElementById('cameraIP').value,
port: parseInt(document.getElementById('cameraPort').value) || 554,
username: document.getElementById('cameraUsername').value,
password: document.getElementById('cameraPassword').value,
rtsp_url: document.getElementById('cameraRTSP').value,
unit_code: document.getElementById('cameraUnitCode').value,
enabled: true
};
if (!formData.name || !formData.ip) {
showStatus('请填写摄像头名称和IP地址', 'error');
return;
}
const response = await fetch('/camera/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
lastAPIResponse = result;
$('#addCameraModal').modal('hide');
document.getElementById('addCameraForm').reset();
showStatus('摄像头添加成功', 'success');
// 重新加载摄像头列表
setTimeout(() => {
loadCamerasAPI();
}, 1000);
} catch (error) {
showStatus(`添加摄像头失败: ${error.message}`, 'error');
}
}
// 显示空状态
function showEmptyState(message) {
const container = document.getElementById('camera-display');
container.innerHTML = `
<div class="loading">
<i class="fas fa-info-circle"></i> ${message}
</div>
`;
}
// 清空显示
function clearDisplay() {
const container = document.getElementById('camera-display');
container.innerHTML = `
<div class="loading">
<i class="fas fa-info-circle"></i> 显示已清空
</div>
`;
document.getElementById('api-response-container').style.display = 'none';
showStatus('显示已清空', 'info');
}
// 显示API响应
function showAPIResponse() {
if (lastAPIResponse) {
document.getElementById('api-response').textContent = JSON.stringify(lastAPIResponse, null, 2);
document.getElementById('api-response-container').style.display = 'block';
showStatus('API响应数据已显示', 'info');
} else {
showStatus('暂无API响应数据', 'error');
}
}
// 页面加载完成后的初始化
document.addEventListener('DOMContentLoaded', function() {
showStatus('API演示页面已加载点击上方按钮开始测试', 'info');
});
</script>
{{template "foot.tmpl" .}}

@ -0,0 +1,589 @@
<!DOCTYPE html>
<html>
<head>
<title>摄像头管理 - RTSPtoWeb</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
.camera-card {
transition: transform 0.2s;
}
.camera-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.status-online {
color: #28a745;
}
.status-offline {
color: #dc3545;
}
.btn-group-actions {
gap: 5px;
}
.modal-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.form-floating {
margin-bottom: 1rem;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/">
<i class="fas fa-video"></i> RTSPtoWeb
</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/">首页</a>
<a class="nav-link active" href="/pages/cameras">摄像头管理</a>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="fas fa-camera"></i> 摄像头管理</h2>
<div>
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addCameraModal">
<i class="fas fa-plus"></i> 添加摄像头
</button>
<button class="btn btn-info" onclick="refreshCameras()">
<i class="fas fa-sync-alt"></i> 刷新
</button>
</div>
</div>
<!-- 数据库状态 -->
<div class="alert alert-info" id="dbStatus">
<i class="fas fa-database"></i> 数据库状态: <span id="dbStatusText">检查中...</span>
</div>
<!-- 摄像头列表 -->
<div class="row" id="cameraList">
<!-- 摄像头卡片将在这里动态生成 -->
</div>
</div>
<!-- 添加摄像头模态框 -->
<div class="modal fade" id="addCameraModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-plus"></i> 添加摄像头</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addCameraForm">
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="addCameraName" placeholder="摄像头名称" required>
<label for="addCameraName">摄像头名称</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="addUnitCode" placeholder="单位代码">
<label for="addUnitCode">单位代码</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="addIP" placeholder="IP地址" required>
<label for="addIP">IP地址</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="number" class="form-control" id="addPort" placeholder="端口" value="554" required>
<label for="addPort">端口</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addUsername" placeholder="用户名">
<label for="addUsername">用户名</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="password" class="form-control" id="addPassword" placeholder="密码">
<label for="addPassword">密码</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="addRTSPURL" placeholder="RTSP URL" required>
<label for="addRTSPURL">RTSP URL</label>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<select class="form-select" id="addDeviceType">
<option value="">选择设备类型</option>
<option value="网络摄像头">网络摄像头</option>
<option value="球机">球机</option>
<option value="枪机">枪机</option>
<option value="半球">半球</option>
<option value="其他">其他</option>
</select>
<label for="addDeviceType">设备类型</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addCameraProduce" placeholder="摄像头厂商">
<label for="addCameraProduce">摄像头厂商</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addNvrProduce" placeholder="NVR厂商">
<label for="addNvrProduce">NVR厂商</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addNvrPath" placeholder="NVR路径">
<label for="addNvrPath">NVR路径</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="addPlayBack" placeholder="回放地址">
<label for="addPlayBack">回放地址</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="addEnabled" checked>
<label class="form-check-label" for="addEnabled">
启用摄像头
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" onclick="addCamera()">添加</button>
</div>
</div>
</div>
</div>
<!-- 编辑摄像头模态框 -->
<div class="modal fade" id="editCameraModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-edit"></i> 编辑摄像头</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="editCameraForm">
<input type="hidden" id="editCameraId">
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="editCameraName" placeholder="摄像头名称" required>
<label for="editCameraName">摄像头名称</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="editUnitCode" placeholder="单位代码">
<label for="editUnitCode">单位代码</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="editIP" placeholder="IP地址" required>
<label for="editIP">IP地址</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="number" class="form-control" id="editPort" placeholder="端口" required>
<label for="editPort">端口</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editUsername" placeholder="用户名">
<label for="editUsername">用户名</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="password" class="form-control" id="editPassword" placeholder="密码">
<label for="editPassword">密码</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="editRTSPURL" placeholder="RTSP URL" required>
<label for="editRTSPURL">RTSP URL</label>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<select class="form-select" id="editDeviceType">
<option value="">选择设备类型</option>
<option value="网络摄像头">网络摄像头</option>
<option value="球机">球机</option>
<option value="枪机">枪机</option>
<option value="半球">半球</option>
<option value="其他">其他</option>
</select>
<label for="editDeviceType">设备类型</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editCameraProduce" placeholder="摄像头厂商">
<label for="editCameraProduce">摄像头厂商</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editNvrProduce" placeholder="NVR厂商">
<label for="editNvrProduce">NVR厂商</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editNvrPath" placeholder="NVR路径">
<label for="editNvrPath">NVR路径</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="editPlayBack" placeholder="回放地址">
<label for="editPlayBack">回放地址</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="editEnabled">
<label class="form-check-label" for="editEnabled">
启用摄像头
</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" onclick="updateCamera()">更新</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
checkDatabaseStatus();
loadCameras();
});
// 检查数据库状态
function checkDatabaseStatus() {
fetch('/database/status')
.then(response => response.json())
.then(data => {
const statusElement = document.getElementById('dbStatusText');
const alertElement = document.getElementById('dbStatus');
if (data.enabled && data.connected) {
statusElement.textContent = `已连接 (${data.type}://${data.host}:${data.port}/${data.database})`;
alertElement.className = 'alert alert-success';
} else if (data.enabled && !data.connected) {
statusElement.textContent = `连接失败: ${data.error || '未知错误'}`;
alertElement.className = 'alert alert-danger';
} else {
statusElement.textContent = '未启用';
alertElement.className = 'alert alert-warning';
}
})
.catch(error => {
console.error('检查数据库状态失败:', error);
document.getElementById('dbStatusText').textContent = '检查失败';
document.getElementById('dbStatus').className = 'alert alert-danger';
});
}
// 加载摄像头列表
function loadCameras() {
fetch('/cameras')
.then(response => response.json())
.then(data => {
if (data.cameras) {
displayCameras(data.cameras);
} else {
document.getElementById('cameraList').innerHTML =
'<div class="col-12"><div class="alert alert-info">暂无摄像头数据</div></div>';
}
})
.catch(error => {
console.error('加载摄像头失败:', error);
document.getElementById('cameraList').innerHTML =
'<div class="col-12"><div class="alert alert-danger">加载摄像头失败</div></div>';
});
}
// 显示摄像头列表
function displayCameras(cameras) {
const container = document.getElementById('cameraList');
container.innerHTML = '';
cameras.forEach(camera => {
const statusClass = camera.stream_status === 'online' ? 'status-online' : 'status-offline';
const statusIcon = camera.stream_status === 'online' ? 'fas fa-circle' : 'far fa-circle';
const enabledBadge = camera.enabled ?
'<span class="badge bg-success">已启用</span>' :
'<span class="badge bg-secondary">已禁用</span>';
const card = `
<div class="col-md-6 col-lg-4 mb-3">
<div class="card camera-card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">${camera.camera_name || camera.name || 'undefined'}</h6>
${enabledBadge}
</div>
<div class="card-body">
<p class="card-text">
<strong>IP:</strong> ${camera.ip}<br>
<strong>端口:</strong> ${camera.port || 554}<br>
<strong>用户名:</strong> ${camera.username || ''}<br>
<strong>设备类型:</strong> ${camera.device_type || '未知'}<br>
<strong>状态:</strong>
<i class="${statusIcon} ${statusClass}"></i>
<span class="${statusClass}">${camera.stream_status === 'online' ? '在线' : '离线'}</span>
</p>
<small class="text-muted">
创建时间: ${camera.create_time ? new Date(camera.create_time).toLocaleString() : '未知'}
</small>
</div>
<div class="card-footer">
<div class="d-flex btn-group-actions">
<button class="btn btn-sm btn-primary" onclick="editCamera('${camera.camera_id || camera.id}')">
<i class="fas fa-edit"></i> 编辑
</button>
<button class="btn btn-sm btn-success" onclick="previewCamera('${camera.camera_id || camera.id}')">
<i class="fas fa-play"></i> 预览
</button>
<button class="btn btn-sm btn-danger" onclick="deleteCamera('${camera.camera_id || camera.id}', '${camera.camera_name || camera.name || 'undefined'}')">
<i class="fas fa-trash"></i> 删除
</button>
</div>
</div>
</div>
</div>
`;
container.innerHTML += card;
});
}
// 添加摄像头
function addCamera() {
const cameraName = document.getElementById('addCameraName').value;
const unitCode = document.getElementById('addUnitCode').value;
const ip = document.getElementById('addIP').value;
const port = parseInt(document.getElementById('addPort').value) || 554;
const username = document.getElementById('addUsername').value;
const password = document.getElementById('addPassword').value;
const rtspUrl = document.getElementById('addRTSPURL').value;
const deviceType = document.getElementById('addDeviceType').value;
const cameraProduce = document.getElementById('addCameraProduce').value;
const nvrProduce = document.getElementById('addNvrProduce').value;
const nvrPath = document.getElementById('addNvrPath').value;
const playBack = document.getElementById('addPlayBack').value;
const enabled = document.getElementById('addEnabled').checked;
const formData = {
camera_name: cameraName,
unit_code: unitCode,
ip: ip,
port: port,
username: username,
password: password,
url: rtspUrl,
device_type: deviceType,
camera_produce: cameraProduce,
nvr_produce: nvrProduce,
nvr_path: nvrPath,
play_back: playBack,
enabled: enabled
};
fetch('/camera/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.message) {
alert('摄像头添加成功!');
bootstrap.Modal.getInstance(document.getElementById('addCameraModal')).hide();
document.getElementById('addCameraForm').reset();
loadCameras();
} else {
alert('添加失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('添加摄像头失败:', error);
alert('添加失败: ' + error.message);
});
}
// 编辑摄像头
function editCamera(cameraId) {
fetch(`/camera/${cameraId}`)
.then(response => response.json())
.then(camera => {
document.getElementById('editCameraId').value = camera.camera_id || camera.id;
document.getElementById('editCameraName').value = camera.camera_name || camera.name || '';
document.getElementById('editUnitCode').value = camera.unit_code || '';
document.getElementById('editIP').value = camera.ip || '';
document.getElementById('editPort').value = camera.port || 554;
document.getElementById('editUsername').value = camera.username || '';
document.getElementById('editPassword').value = '';
document.getElementById('editRTSPURL').value = camera.url || camera.rtsp_url || '';
document.getElementById('editDeviceType').value = camera.device_type || '';
document.getElementById('editCameraProduce').value = camera.camera_produce || '';
document.getElementById('editNvrProduce').value = camera.nvr_produce || '';
document.getElementById('editNvrPath').value = camera.nvr_path || '';
document.getElementById('editPlayBack').value = camera.play_back || '';
document.getElementById('editEnabled').checked = camera.enabled;
new bootstrap.Modal(document.getElementById('editCameraModal')).show();
})
.catch(error => {
console.error('获取摄像头信息失败:', error);
alert('获取摄像头信息失败');
});
}
// 更新摄像头
function updateCamera() {
const cameraId = document.getElementById('editCameraId').value;
const cameraName = document.getElementById('editCameraName').value;
const unitCode = document.getElementById('editUnitCode').value;
const ip = document.getElementById('editIP').value;
const port = parseInt(document.getElementById('editPort').value) || 554;
const username = document.getElementById('editUsername').value;
const password = document.getElementById('editPassword').value;
const rtspUrl = document.getElementById('editRTSPURL').value;
const deviceType = document.getElementById('editDeviceType').value;
const cameraProduce = document.getElementById('editCameraProduce').value;
const nvrProduce = document.getElementById('editNvrProduce').value;
const nvrPath = document.getElementById('editNvrPath').value;
const playBack = document.getElementById('editPlayBack').value;
const enabled = document.getElementById('editEnabled').checked;
const formData = {
camera_name: cameraName,
unit_code: unitCode,
ip: ip,
port: port,
username: username,
password: password,
url: rtspUrl,
device_type: deviceType,
camera_produce: cameraProduce,
nvr_produce: nvrProduce,
nvr_path: nvrPath,
play_back: playBack,
enabled: enabled
};
fetch(`/camera/${cameraId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.message) {
alert('摄像头更新成功!');
bootstrap.Modal.getInstance(document.getElementById('editCameraModal')).hide();
loadCameras();
} else {
alert('更新失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('更新摄像头失败:', error);
alert('更新失败: ' + error.message);
});
}
// 删除摄像头
function deleteCamera(cameraId, cameraName) {
if (confirm(`确定要删除摄像头 "${cameraName}" 吗?`)) {
fetch(`/camera/${cameraId}`, {
method: 'DELETE'
})
.then(response => response.json())
.then(data => {
if (data.message) {
alert('摄像头删除成功!');
loadCameras();
} else {
alert('删除失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('删除摄像头失败:', error);
alert('删除失败: ' + error.message);
});
}
}
// 预览摄像头
function previewCamera(cameraId) {
window.open(`/pages/player/all/${cameraId}/0`, '_blank');
}
// 刷新摄像头列表
function refreshCameras() {
fetch('/cameras/refresh', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.message) {
alert('刷新成功!');
loadCameras();
} else {
alert('刷新失败: ' + (data.error || '未知错误'));
}
})
.catch(error => {
console.error('刷新失败:', error);
alert('刷新失败: ' + error.message);
});
}
</script>
</body>
</html>

@ -3,12 +3,12 @@
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">Documentation</h1>
<h1 class="m-0 text-dark">文档</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">Documentation</li>
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active">文档</li>
</ol>
</div>
</div>
@ -20,10 +20,10 @@
<div class="row">
<div class="col-12">
<p>
<a href="https://github.com/deepch/RTSPtoWeb/tree/master/docs/api.md">API documentation</a> is available in the GitHub repository.
<a href="/docs/api.md">API 文档</a> 可在本地查看。
</p>
<p>
See the project <a href="https://github.com/deepch/RTSPtoWeb#readme">README</a> for installation and configuration instructions.
查看项目 <a href="https://github.com/deepch/RTSPtoWeb#readme">README</a> 获取安装和配置说明。
</p>
</div>
</div>

@ -3,12 +3,12 @@
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">Edit stream</h1>
<h1 class="m-0 text-dark">编辑流</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">Edit stream</li>
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active">编辑流</li>
</ol>
</div>
</div>
@ -23,42 +23,42 @@
<div class="col-md-12">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Parameters<small> main</small></h3>
<h3 class="card-title">参数<small> 主要</small></h3>
</div>
<form class="stream-form main-form">
<div class="card-body">
<div class="form-group">
<label for="exampleInputEmail1">Stream name</label>
<input type="text" class="form-control" name="stream-name" placeholder="Enter stream name" value="{{$stream.Name}}" id="stream-name">
<small class="form-text text-muted">You can choose any name for the stream, for example "My room" or "Happy sausage"</small>
<label for="exampleInputEmail1">流名称</label>
<input type="text" class="form-control" name="stream-name" placeholder="输入流名称" value="{{$stream.Name}}" id="stream-name">
<small class="form-text text-muted">您可以为流选择任何名称,例如"我的房间"或"客厅"</small>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Stream url</label>
<input type="text" name="stream-url" class="form-control" placeholder="Enter stream url" value="{{$mainChannel.URL}}">
<small class="form-text text-muted">Enter rtsp address as instructed by your camera. Look like <code>rtsp://&lt;ip&gt;:&lt;port&gt;/path </code></small>
<label for="exampleInputPassword1">流地址</label>
<input type="text" name="stream-url" class="form-control" placeholder="输入流地址" value="{{$mainChannel.URL}}">
<small class="form-text text-muted">按照摄像头说明输入rtsp地址。格式如 <code>rtsp://&lt;ip&gt;:&lt;port&gt;/path </code></small>
</div>
<div class="form-group">
<label for="inputStatus">Stream type</label>
<label for="inputStatus">流类型</label>
<select class="form-control custom-select" name="stream-ondemand">
<option selected disabled><small>Select One</small></option>
<option selected disabled><small>请选择</small></option>
<option value="1"
{{ if eq $mainChannel.OnDemand true}}
selected
{{ end }}>On demand only</option>
{{ end }}>按需连接</option>
<option value="0" {{ if eq $mainChannel.OnDemand false}}
selected
{{ end }}>Persistent connection</option>
{{ end }}>持续连接</option>
</select>
<small class="form-text text-muted">On persistent connection, the server get data from the camera continuously. On demand, the server get data from the camera only when you click play button </small>
<small class="form-text text-muted">持续连接时,服务器会持续从摄像头获取数据。按需连接时,服务器只在您点击播放按钮时才从摄像头获取数据 </small>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="debug" id="debug-switch" {{ if eq $mainChannel.Debug true}}
checked
{{ end }}>
<label class="custom-control-label" for="debug-switch">Enable debug</label>
<label class="custom-control-label" for="debug-switch">启用调试</label>
</div>
<small class="form-text text-muted">Select this options if you want get more data about the stream </small>
<small class="form-text text-muted">如果您想获取更多关于流的数据,请选择此选项 </small>
</div>
</div>
</form>
@ -70,7 +70,7 @@
<div class="col-12">
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Sub channel<small> parameters</small></h3>
<h3 class="card-title">子通道<small> 参数</small></h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" onclick="removeChannelDiv(this)"><i class="fas fa-times"></i></button>
</div>
@ -78,34 +78,34 @@
<div class="card-body">
<form class="stream-form">
<div class="form-group">
<label for="exampleInputPassword1">Substream url</label>
<input type="text" name="stream-url" class="form-control" placeholder="Enter stream url" value="{{$value.URL}}" >
<small class="form-text text-muted">Enter rtsp address as instructed by your camera. Look like <code>rtsp://&lt;ip&gt;:&lt;port&gt;/path </code> </small>
<label for="exampleInputPassword1">子流地址</label>
<input type="text" name="stream-url" class="form-control" placeholder="输入流地址" value="{{$value.URL}}" >
<small class="form-text text-muted">按照摄像头说明输入rtsp地址。格式如 <code>rtsp://&lt;ip&gt;:&lt;port&gt;/path </code> </small>
</div>
<div class="form-group">
<label for="inputStatus">Substream type</label>
<label for="inputStatus">子流类型</label>
<select class="form-control custom-select" name="stream-ondemand">
<option selected disabled><small>Select One</small></option>
<option selected disabled><small>请选择</small></option>
<option value="1"
{{ if eq $value.OnDemand true}}
selected
{{ end }}>On demand only</option>
{{ end }}>按需连接</option>
<option value="0"
{{ if eq $value.OnDemand false}}
selected
{{ end }}>Persistent connection</option>
{{ end }}>持续连接</option>
</select>
<small class="form-text text-muted">On persistent connection, the server get data from the camera continuously. On demand, the server get data from the camera only when you click play button </small>
<small class="form-text text-muted">持续连接时,服务器会持续从摄像头获取数据。按需连接时,服务器只在您点击播放按钮时才从摄像头获取数据 </small>
</div>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="debug" id="substream-debug-switch-{{$key}}" {{ if eq $value.Debug true}}
checked
{{ end }}>
<label class="custom-control-label" for="substream-debug-switch-{{$key}}">Enable debug</label>
<label class="custom-control-label" for="substream-debug-switch-{{$key}}">启用调试</label>
</div>
<small class="form-text text-muted">Select this options if you want get more data about the stream </small>
<small class="form-text text-muted">如果您想获取更多关于流的数据,请选择此选项 </small>
</div>
</form>
</div>
@ -117,8 +117,8 @@
</div>
<div class="row mb-3">
<div class="col-12">
<button type="button" onclick="addChannel()" class="btn btn-secondary">Add channel</button>
<button type="button" onclick="editStreamSubmit()" class="btn btn-primary">Save stream</button>
<button type="button" onclick="addChannel()" class="btn btn-secondary">添加通道</button>
<button type="button" onclick="editStreamSubmit()" class="btn btn-primary">保存流</button>
</div>
</div>

@ -25,12 +25,12 @@
<a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="/" class="nav-link">Home</a>
<a href="/" class="nav-link">首页</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="/pages/stream/add" role="button"><i class="fas fa-plus-square"></i> Add stream</a>
<a class="nav-link" href="/pages/stream/add" role="button"><i class="fas fa-plus-square"></i> 添加流</a>
</li>
</ul>
</nav>
@ -54,17 +54,17 @@
">
<i class="nav-icon fas fa-tachometer-alt"></i>
<p>
Dashboard
仪表板
</p>
</a>
</li>
<li class="nav-header">NAVIGATION</li>
<li class="nav-header">导航</li>
<li class="nav-item ">
<a href="/pages/stream/list" class="nav-link {{ if (eq .page "stream_list") }}
active
{{end}}">
<i class="fas fa-list-alt nav-icon"></i>
<p>Streams list</p>
<p>流列表</p>
</a>
</li>
<li class="nav-item">
@ -72,7 +72,7 @@
active
{{end}}">
<i class="fas fa-plus-square nav-icon"></i>
<p>Add stream</p>
<p>添加流</p>
</a>
</li>
@ -81,17 +81,17 @@
active
{{end}}">
<i class="fas fa-th-large nav-icon"></i>
<p>Full multiview</p>
<p>全屏多视图</p>
</a>
</li>
<li class="nav-header">MISCELLANEOUS</li>
<li class="nav-header">其他</li>
<li class="nav-item">
<a href="/pages/documentation" class="nav-link
{{ if eq .page "documentation"}}
active
{{end}}">
<i class="nav-icon fas fa-file"></i>
<p>Documentation</p>
<p>文档</p>
</a>
</li>
</ul>

@ -3,12 +3,12 @@
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">Dashboard</h1>
<h1 class="m-0 text-dark">仪表板</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">Dashboard</li>
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active">仪表板</li>
</ol>
</div>
</div>
@ -19,23 +19,46 @@
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h5 class="mt-4 mb-2">Streams ({{ len .streams}})</h5>
<div class="d-flex justify-content-between align-items-center">
<h5 class="mt-4 mb-2">视频流 ({{ len .streams}})</h5>
<div class="mt-4 mb-2">
<a href="/pages/cameras" class="btn btn-primary btn-sm">
<i class="fas fa-camera"></i> 摄像头管理
</a>
<a href="/pages/stream/add" class="btn btn-success btn-sm">
<i class="fas fa-plus"></i> 添加流
</a>
</div>
</div>
</div>
</div>
<!-- 加载状态 -->
<div class="row mt-3" id="loading-indicator">
<div class="col-12">
<div class="text-center">
<i class="fas fa-spinner fa-spin"></i> 正在加载摄像头列表...
</div>
</div>
</div>
<div class="row mt-3 ">
<!-- 摄像头列表容器 -->
<div class="row mt-3" id="camera-list-container" style="display: none;">
<!-- 动态生成的摄像头卡片将插入这里 -->
</div>
<!-- 流列表容器(降级方案) -->
<div class="row mt-3" id="stream-list-container" style="display: none;">
{{ range $key, $value := .streams }}
<div class="col-12 col-sm-6 col-md-3" id="{{ $key }}">
<div class="card card-outline card-success">
<div class="card-header">
<h3 class="card-title one-line-header">{{.Name}}</h3>
<div class="card-tools">
<span data-toggle="tooltip" title="avaliable channels" class="badge badge-success">{{len .Channels }}</span>
<span data-toggle="tooltip" title="可用通道" class="badge badge-success">{{len .Channels }}</span>
</div>
</div>
<div class="card-body p-0">
<div id="carousel_{{$key}}" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
{{ range $k, $v := .Channels }}
@ -47,21 +70,20 @@
<div class="carousel-item {{ if eq $k "0"}} active {{end}}">
<img class="d-block w-100 stream-img fix-height" channel="{{$k}}" src="/../static/img/noimage.svg">
<div class="carousel-caption d-none d-md-block">
<h5>Channel: {{$k}}</h5>
<h5>通道: {{$k}}</h5>
</div>
</div>
{{end}}
</div>
<a class="carousel-control-prev" href="#carousel_{{$key}}" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
<span class="sr-only">上一个</span>
</a>
<a class="carousel-control-next" href="#carousel_{{$key}}" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
<span class="sr-only">下一个</span>
</a>
</div>
<div class="row">
<div class="col-12">
<div class="btn-group stream">
@ -70,7 +92,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> MSE</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/mse/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/mse/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -78,7 +100,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> HLS</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/hls/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/hls/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -86,7 +108,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> WebRTC</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/webrtc/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/webrtc/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -94,7 +116,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> ALL</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/all/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/all/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -104,18 +126,12 @@
<a class="btn btn-info btn-flat btn-xs" href="/pages/player/webrtc/{{$key}}/0"><i class="fas fa-play"></i> WebRTC</a>
<a class="btn btn-info btn-flat btn-xs" href="/pages/player/all/{{$key}}/0"><i class="fas fa-play"></i> ALL</a>
{{end}}
<a class="btn btn-secondary btn-flat btn-xs" href="/pages/stream/edit/{{$key}}"><i class="fas fa-edit"></i> Edit</a>
<a class="btn btn-danger btn-flat btn-xs" onclick="deleteStream('{{ $key }}')" href="#"><i class="fas fa-times"></i> Delete</a>
<a class="btn btn-secondary btn-flat btn-xs" href="/pages/stream/edit/{{$key}}"><i class="fas fa-edit"></i> 编辑</a>
<a class="btn btn-danger btn-flat btn-xs" onclick="deleteStream('{{ $key }}')" href="#"><i class="fas fa-times"></i> 删除</a>
</div>
</div>
</div>
</div>
</div>
</div>
{{ end }}
@ -126,4 +142,7 @@
<!-- /.content -->
<!-- 引入摄像头管理器 -->
<script src="/../static/js/camera-manager.js"></script>
{{template "foot.tmpl" .}}

@ -3,12 +3,12 @@
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0 text-dark">Streams list</h1>
<h1 class="m-0 text-dark">流列表</h1>
</div>
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active">Streams list</li>
<li class="breadcrumb-item"><a href="/">首页</a></li>
<li class="breadcrumb-item active">流列表</li>
</ol>
</div>
</div>
@ -24,7 +24,7 @@
<div class="card-header">
<h3 class="card-title one-line-header">{{.Name}}</h3>
<div class="card-tools">
<span data-toggle="tooltip" title="avaliable channels" class="badge badge-success">{{len .Channels }}</span>
<span data-toggle="tooltip" title="可用通道" class="badge badge-success">{{len .Channels }}</span>
</div>
</div>
<div class="card-body p-0">
@ -40,18 +40,18 @@
<div class="carousel-item {{ if eq $k "0"}} active {{end}}">
<img class="d-block w-100 stream-img fix-height" channel="{{$k}}" src="/../static/img/noimage.svg">
<div class="carousel-caption d-none d-md-block">
<h5>Channel: {{$k}}</h5>
<h5>通道: {{$k}}</h5>
</div>
</div>
{{end}}
</div>
<a class="carousel-control-prev" href="#carousel_{{$key}}" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
<span class="sr-only">上一个</span>
</a>
<a class="carousel-control-next" href="#carousel_{{$key}}" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
<span class="sr-only">下一个</span>
</a>
</div>
@ -63,7 +63,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> MSE</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/mse/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/mse/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -71,7 +71,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> HLS</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/hls/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/hls/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -79,7 +79,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> WebRTC</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/webrtc/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/webrtc/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -87,7 +87,7 @@
<a class="btn btn-info btn-flat btn-xs" data-toggle="dropdown" href="#"><i class="fas fa-play"></i> ALL</a>
<div class="dropdown-menu">
{{ range $k, $v := .Channels }}
<a class="dropdown-item" href="/pages/player/all/{{$key}}/{{$k}}">Channel {{$k}}</a>
<a class="dropdown-item" href="/pages/player/all/{{$key}}/{{$k}}">通道 {{$k}}</a>
{{end}}
</div>
</div>
@ -100,8 +100,8 @@
<a class="btn btn-secondary btn-flat btn-xs" href="/pages/stream/edit/{{$key}}"><i class="fas fa-edit"></i> Edit</a>
<a class="btn btn-danger btn-flat btn-xs" onclick="deleteStream('{{ $key }}')" href="#"><i class="fas fa-times"></i> Delete</a>
<a class="btn btn-secondary btn-flat btn-xs" href="/pages/stream/edit/{{$key}}"><i class="fas fa-edit"></i> 编辑</a>
<a class="btn btn-danger btn-flat btn-xs" onclick="deleteStream('{{ $key }}')" href="#"><i class="fas fa-times"></i> 删除</a>
</div>
</div>
</div>

Loading…
Cancel
Save