修复问题
This commit is contained in:
@@ -31,6 +31,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.bytedeco.opencv.global.opencv_core.CV_8UC3;
|
||||
|
||||
@@ -344,7 +345,7 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable
|
||||
if (enableDetection) initDetectors();
|
||||
} catch (Exception e) {
|
||||
log.error("初始化检测模型失败:{}", e.getMessage(), e);
|
||||
enableDetection = false; // 模型失败不影响推流
|
||||
// 模型失败不影响推流,但不禁用检测
|
||||
}
|
||||
|
||||
if (!createGrabber()) return;
|
||||
@@ -362,68 +363,18 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable
|
||||
|
||||
long startTime = 0;
|
||||
long videoTS;
|
||||
|
||||
// === 解耦线程:解码 / 推理 ===
|
||||
Thread tDecode = null, tInfer = null;
|
||||
if (enableDetection) {
|
||||
tDecode = new Thread(() -> {
|
||||
while (running && grabberStatus) {
|
||||
try {
|
||||
Frame f = grabber.grabImage();
|
||||
if (f == null) continue;
|
||||
Mat m = toMat.convert(f);
|
||||
if (m == null || m.empty()) continue;
|
||||
Mat copy = new Mat(m.rows(), m.cols(), CV_8UC3);
|
||||
m.copyTo(copy);
|
||||
Mat old = latestFrame.getAndSet(copy);
|
||||
if (old != null) old.release();
|
||||
} catch (Exception e) {
|
||||
log.debug("decode err: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}, "det-decode");
|
||||
|
||||
int inferFps = 15;
|
||||
long period = 1_000_000_000L / inferFps;
|
||||
tInfer = new Thread(() -> {
|
||||
long next = System.nanoTime();
|
||||
while (running && grabberStatus) {
|
||||
long now = System.nanoTime();
|
||||
if (now < next) { LockSupport.parkNanos(next - now); continue; }
|
||||
next += period;
|
||||
|
||||
Mat src = latestFrame.get();
|
||||
if (src == null || src.empty()) continue;
|
||||
|
||||
Mat snap = new Mat();
|
||||
src.copyTo(snap);
|
||||
try {
|
||||
List<Detection> dets = detector.detect(snap);
|
||||
latestDetections.set(dets);
|
||||
|
||||
// 窗口巡检期间:回调 onDetections
|
||||
if (windowMode && detectionListener != null && currentJobId != null && currentDeviceId != null) {
|
||||
long ts = System.currentTimeMillis();
|
||||
try { detectionListener.onDetections(currentJobId, currentDeviceId, dets, ts); }
|
||||
catch (Exception ignore) {}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.debug("infer err: {}", e.getMessage());
|
||||
} finally {
|
||||
snap.release();
|
||||
}
|
||||
}
|
||||
}, "det-infer");
|
||||
|
||||
tDecode.start();
|
||||
tInfer.start();
|
||||
}
|
||||
|
||||
// === 主发送循环 ===
|
||||
|
||||
// 检测频率控制变量
|
||||
final long DETECTION_INTERVAL_MS = 3000; // 每3秒检测一次
|
||||
long lastDetectionTime = 0;
|
||||
List<Detection> currentDetections = Collections.emptyList(); // 当前显示的检测结果
|
||||
|
||||
// 避免递归和栈溢出的关键:不使用递归调用,使用循环
|
||||
// 避免过深的方法调用链
|
||||
for (; running && grabberStatus && recorderStatus; ) {
|
||||
try {
|
||||
if (transferFlag) {
|
||||
// 仅转复用(未叠框)
|
||||
// 仅转复用(未叠框)- 这部分代码保持不变
|
||||
long startGrab = System.currentTimeMillis();
|
||||
AVPacket pkt = grabber.grabPacket();
|
||||
if ((System.currentTimeMillis() - startGrab) > 5000) {
|
||||
@@ -438,85 +389,176 @@ public class MediaTransferFlvByJavacv extends MediaTransfer implements Runnable
|
||||
recorder.recordPacket(pkt);
|
||||
}
|
||||
} else {
|
||||
// 转码(可叠框)
|
||||
// 转码(可叠框)- 优化这部分以避免栈溢出
|
||||
long startGrab = System.currentTimeMillis();
|
||||
Frame frame;
|
||||
|
||||
if (enableDetection) {
|
||||
Mat src = latestFrame.get();
|
||||
if (src == null || src.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 叠加最近一次检测结果
|
||||
List<Detection> dets = latestDetections.get();
|
||||
if (!CollectionUtils.isEmpty(dets)) {
|
||||
Overlay.draw(dets, src);
|
||||
}
|
||||
// 更新"最近叠好框的帧"用于存证
|
||||
updateLatestAnnotated(src);
|
||||
|
||||
// 统计(仅窗口巡检时)
|
||||
if (windowMode) updateStats(dets);
|
||||
|
||||
// 窗口结束判定
|
||||
if (windowMode && System.currentTimeMillis() >= windowEndMs) {
|
||||
finishWindow();
|
||||
}
|
||||
|
||||
// 安全处理:跳过检测模式,直接使用grabber.grab()
|
||||
frame = grabber.grab();
|
||||
|
||||
// 如果需要,可以在这里添加日志
|
||||
log.debug("检测模式下使用直接抓取帧");
|
||||
} else {
|
||||
Frame frame = null;
|
||||
|
||||
try {
|
||||
// 直接获取帧,不进行复杂处理
|
||||
frame = grabber.grab();
|
||||
} catch (Exception e) {
|
||||
log.error("获取帧异常: {}", e.getMessage());
|
||||
continue; // 跳过这一帧
|
||||
}
|
||||
|
||||
|
||||
if ((System.currentTimeMillis() - startGrab) > 5000) {
|
||||
log.info("{} 网络异常(转码)", cameraDto.getUrl());
|
||||
closeMedia();
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果启用了检测且获取到了有效帧
|
||||
if (frame != null && enableDetection) {
|
||||
// 控制检测频率
|
||||
long currentTime = System.currentTimeMillis();
|
||||
boolean shouldDetect = (currentTime - lastDetectionTime >= DETECTION_INTERVAL_MS);
|
||||
|
||||
if (shouldDetect) {
|
||||
// 每隔一段时间进行一次检测
|
||||
try {
|
||||
// 安全转换为Mat
|
||||
Mat detectionMat = null;
|
||||
try {
|
||||
detectionMat = toMat.convert(frame);
|
||||
} catch (Exception e) {
|
||||
log.debug("帧转换异常: {}", e.getMessage());
|
||||
continue; // 跳过这一帧
|
||||
}
|
||||
|
||||
if (detectionMat != null && !detectionMat.empty()) {
|
||||
// 执行检测
|
||||
try {
|
||||
currentDetections = detector.detect(detectionMat);
|
||||
lastDetectionTime = currentTime;
|
||||
latestDetections.set(currentDetections);
|
||||
|
||||
// 窗口巡检回调
|
||||
if (windowMode && detectionListener != null &&
|
||||
currentJobId != null && currentDeviceId != null) {
|
||||
try {
|
||||
detectionListener.onDetections(currentJobId,
|
||||
currentDeviceId,
|
||||
currentDetections,
|
||||
currentTime);
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("检测异常: {}", e.getMessage());
|
||||
} finally {
|
||||
// 确保释放Mat
|
||||
try {
|
||||
if (detectionMat != null && !detectionMat.isNull())
|
||||
detectionMat.release();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("检测流程异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 不再尝试将检测结果叠加到当前帧上
|
||||
// 而是在UI层面处理叠加,这样可以避免复杂的帧处理和转换
|
||||
// 或者使用更简单的方式叠加
|
||||
|
||||
// 统计(仅窗口巡检时)
|
||||
if (windowMode) {
|
||||
try {
|
||||
updateStats(currentDetections);
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
// 窗口结束判定
|
||||
if (windowMode && System.currentTimeMillis() >= windowEndMs) {
|
||||
try {
|
||||
finishWindow();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
// 直接使用原始帧,避免复杂的转换
|
||||
if (frame != null) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (startTime == 0) startTime = now;
|
||||
videoTS = 1000 * (now - startTime);
|
||||
if (videoTS > recorder.getTimestamp()) recorder.setTimestamp(videoTS);
|
||||
recorder.record(frame);
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
if (startTime == 0) startTime = now;
|
||||
videoTS = 1000 * (now - startTime);
|
||||
if (videoTS > recorder.getTimestamp()) recorder.setTimestamp(videoTS);
|
||||
recorder.record(frame);
|
||||
} catch (Exception e) {
|
||||
log.error("记录帧异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (FrameGrabber.Exception e) {
|
||||
log.error("拉流异常: {}", e.getMessage());
|
||||
grabberStatus = false;
|
||||
MediaService.cameras.remove(cameraDto.getMediaKey());
|
||||
} catch (FrameRecorder.Exception e) {
|
||||
log.error("推流异常: {}", e.getMessage());
|
||||
recorderStatus = false;
|
||||
MediaService.cameras.remove(cameraDto.getMediaKey());
|
||||
} catch (Exception e) {
|
||||
log.error("其他异常: {}", e.getMessage());
|
||||
// 不要立即退出,尝试继续处理
|
||||
try {
|
||||
Thread.sleep(100); // 短暂暂停
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
// 输出缓存到客户端
|
||||
if (bos.size() > 0) {
|
||||
byte[] b = bos.toByteArray();
|
||||
bos.reset();
|
||||
sendFrameData(b);
|
||||
try {
|
||||
if (bos.size() > 0) {
|
||||
byte[] b = bos.toByteArray();
|
||||
bos.reset();
|
||||
sendFrameData(b);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送数据异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// === 收尾 ===
|
||||
// 安全地关闭资源
|
||||
safeCloseResources();
|
||||
}
|
||||
|
||||
// 将资源关闭逻辑提取到单独的方法,避免嵌套太深
|
||||
private void safeCloseResources() {
|
||||
try {
|
||||
if (detector != null) try { detector.close(); } catch (Exception ignored) {}
|
||||
if (modelManager != null) try { modelManager.close(); } catch (Exception ignored) {}
|
||||
if (recorder != null) recorder.close();
|
||||
if (grabber != null) grabber.close();
|
||||
bos.close();
|
||||
} catch (Exception ignored) {
|
||||
} finally {
|
||||
if (detector != null) {
|
||||
try { detector.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (modelManager != null) {
|
||||
try { modelManager.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (recorder != null) {
|
||||
try { recorder.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (grabber != null) {
|
||||
try { grabber.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
try { bos.close(); } catch (Exception ignored) {}
|
||||
|
||||
Mat m = latestFrame.getAndSet(null);
|
||||
if (m != null) m.release();
|
||||
if (m != null) {
|
||||
try { m.release(); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
Mat a = latestAnnotatedFrame.getAndSet(null);
|
||||
if (a != null) a.release();
|
||||
if (a != null) {
|
||||
try { a.release(); } catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
closeMedia();
|
||||
} catch (Exception e) {
|
||||
log.error("关闭资源异常: {}", e.getMessage());
|
||||
}
|
||||
|
||||
log.info("关闭媒体流-javacv: {}", cameraDto.getUrl());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user