|
|
# 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的摄像头管理和视频流功能。 |