You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RTSPtoWeb/JAVA_INTEGRATION.md

512 lines
16 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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