机械臂摄像头
This commit is contained in:
+8
-18
@@ -1130,26 +1130,16 @@ def api_camera_capture():
|
||||
|
||||
@app.route("/api/camera/arm_refresh")
|
||||
def api_arm_camera_refresh():
|
||||
"""从机械臂拉一张 JPEG(流式读第一个完整帧)"""
|
||||
"""从机械臂拉一张 JPEG(请求 snapshot 端点,简单 HTTP GET)"""
|
||||
import requests
|
||||
try:
|
||||
r = requests.get(ARM_CAMERA_CONFIG["url"], stream=True, timeout=8)
|
||||
if r.status_code != 200:
|
||||
return "", 404
|
||||
data = b""
|
||||
for chunk in r.iter_content(chunk_size=8192):
|
||||
data += chunk
|
||||
# 在累积数据中找 JPEG 完整帧
|
||||
s = data.find(b"\xff\xd8")
|
||||
e = data.find(b"\xff\xd9", s + 2) if s >= 0 else -1
|
||||
if s >= 0 and e > s:
|
||||
r.close()
|
||||
return Response(data[s:e+2], mimetype="image/jpeg")
|
||||
# 累积超过 5MB 还没找到完整帧,说明流异常,放弃
|
||||
if len(data) > 5 * 1024 * 1024:
|
||||
r.close()
|
||||
return "", 404
|
||||
r.close()
|
||||
r = requests.get(ARM_CAMERA_CONFIG.get("snapshot_url", ARM_CAMERA_CONFIG["url"]), timeout=8)
|
||||
if r.status_code == 200 and r.content:
|
||||
resp = Response(r.content, mimetype="image/jpeg")
|
||||
resp.headers["Cache-Control"] = "no-cache, no-store, must-revalidate, max-age=0"
|
||||
resp.headers["Pragma"] = "no-cache"
|
||||
resp.headers["Expires"] = "0"
|
||||
return resp
|
||||
return "", 404
|
||||
except Exception as ex:
|
||||
logger.error(f"arm_refresh 失败: {ex}")
|
||||
|
||||
@@ -40,6 +40,7 @@ CAMERA_CONFIG = {
|
||||
# ========== 机械臂摄像头流 ==========
|
||||
ARM_CAMERA_CONFIG = {
|
||||
"url": "http://192.168.110.164:5003/api/camera/preview",
|
||||
"snapshot_url": "http://192.168.110.164:5003/api/camera/snapshot",
|
||||
}
|
||||
|
||||
# ========== HTTP 上传 ==========
|
||||
|
||||
@@ -45,6 +45,7 @@ def _ensure_ffmpeg():
|
||||
"ffmpeg",
|
||||
"-f", "v4l2",
|
||||
"-input_format", "mjpeg",
|
||||
"-re",
|
||||
"-i", f"/dev/video{ARM_CAMERA_INDEX}",
|
||||
"-vf", "rotate=PI",
|
||||
"-q:v", "8",
|
||||
@@ -103,6 +104,40 @@ def arm_camera_restart():
|
||||
_ensure_ffmpeg()
|
||||
return jsonify({"ok": True})
|
||||
|
||||
@arm_video_app.route("/api/camera/snapshot")
|
||||
def arm_camera_snapshot():
|
||||
"""机械臂摄像头单帧 JPEG — pkill -9 强杀旧 ffmpeg,再临时抓一帧"""
|
||||
import subprocess
|
||||
global _ffmpeg_proc
|
||||
# 用 pkill -9 强杀所有 ffmpeg 进程,释放 /dev/video0
|
||||
subprocess.run(["pkill", "-9", "-f", "ffmpeg"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=3)
|
||||
time.sleep(0.3)
|
||||
_ffmpeg_proc = None
|
||||
proc = subprocess.run(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-f", "v4l2",
|
||||
"-input_format", "mjpeg",
|
||||
"-i", f"/dev/video{ARM_CAMERA_INDEX}",
|
||||
"-vf", "rotate=PI",
|
||||
"-vframes", "1",
|
||||
"-q:v", "8",
|
||||
"-f", "mjpeg",
|
||||
"pipe:1"
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
timeout=5,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
if proc.returncode == 0 and proc.stdout:
|
||||
r = Response(proc.stdout, mimetype="image/jpeg")
|
||||
r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate, max-age=0"
|
||||
r.headers["Pragma"] = "no-cache"
|
||||
r.headers["Expires"] = "0"
|
||||
return r
|
||||
logger.warning(f"ffmpeg snapshot failed: rc={proc.returncode}")
|
||||
return "", 500
|
||||
|
||||
|
||||
# ========== RoboFlow 630 Socket API 客户端 ==========
|
||||
class RoboFlowClient:
|
||||
|
||||
Reference in New Issue
Block a user