初始化系统融合软件代码仓库
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package com.xtrh.node.controller;
|
||||
|
||||
import com.xtrh.node.service.WorkerService;
|
||||
import com.xtrh.node.config.WorkerCapabilityCondition;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 节点 HTTP 入口只负责查询与流输出,业务许可由服务层校验。
|
||||
* 播放地址包含任务号和版本,迁移时由总控向 PXHJ 回传新地址。
|
||||
* 主节点兼任执行端时在同一端口提供媒体入口,UDP 网关不承担视频任务。
|
||||
*/
|
||||
@RestController
|
||||
@Conditional(WorkerCapabilityCondition.class)
|
||||
public class WorkerController {
|
||||
private final WorkerService worker;
|
||||
|
||||
public WorkerController(WorkerService worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
// 状态快照由服务生成,控制层不读取或修改内部任务集合。
|
||||
@GetMapping("/worker/status")
|
||||
public Object status() {
|
||||
return worker.status();
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出浏览器可直接读取的 MJPEG 流,每个分段是一张完整 JPEG。
|
||||
* 先检查授权再返回成功响应,避免无效地址建立空的长连接。
|
||||
* 不缓存实时画面,防止浏览器复用结束或迁移前的内容。
|
||||
*/
|
||||
@GetMapping("/media/{taskId}/{version}/stream")
|
||||
public ResponseEntity<StreamingResponseBody> stream(
|
||||
@PathVariable String taskId, @PathVariable long version, @RequestParam String key) {
|
||||
worker.frame(taskId, version, key);
|
||||
StreamingResponseBody body = output -> {
|
||||
try {
|
||||
while (true) {
|
||||
// 每帧重新取得当前状态,准备转运行后不再引用旧任务对象。
|
||||
byte[] frame = worker.frame(taskId, version, key);
|
||||
if (frame != null) {
|
||||
String header = "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: "
|
||||
+ frame.length + "\r\n\r\n";
|
||||
output.write(header.getBytes(StandardCharsets.US_ASCII));
|
||||
output.write(frame);
|
||||
output.write("\r\n".getBytes(StandardCharsets.US_ASCII));
|
||||
output.flush();
|
||||
}
|
||||
Thread.sleep(250);
|
||||
}
|
||||
} catch (InterruptedException interrupted) {
|
||||
// 容器关闭线程时保留中断标记,流循环随之结束。
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IllegalArgumentException inactiveTask) {
|
||||
// 迁移、结束或许可到期后停止旧流,不能继续发送缓存画面。
|
||||
}
|
||||
};
|
||||
return ResponseEntity.ok().cacheControl(CacheControl.noStore())
|
||||
.header("Content-Type", "multipart/x-mixed-replace; boundary=frame").body(body);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user