机械臂摄像头
This commit is contained in:
+8
-18
@@ -1130,26 +1130,16 @@ def api_camera_capture():
|
|||||||
|
|
||||||
@app.route("/api/camera/arm_refresh")
|
@app.route("/api/camera/arm_refresh")
|
||||||
def api_arm_camera_refresh():
|
def api_arm_camera_refresh():
|
||||||
"""从机械臂拉一张 JPEG(流式读第一个完整帧)"""
|
"""从机械臂拉一张 JPEG(请求 snapshot 端点,简单 HTTP GET)"""
|
||||||
import requests
|
import requests
|
||||||
try:
|
try:
|
||||||
r = requests.get(ARM_CAMERA_CONFIG["url"], stream=True, timeout=8)
|
r = requests.get(ARM_CAMERA_CONFIG.get("snapshot_url", ARM_CAMERA_CONFIG["url"]), timeout=8)
|
||||||
if r.status_code != 200:
|
if r.status_code == 200 and r.content:
|
||||||
return "", 404
|
resp = Response(r.content, mimetype="image/jpeg")
|
||||||
data = b""
|
resp.headers["Cache-Control"] = "no-cache, no-store, must-revalidate, max-age=0"
|
||||||
for chunk in r.iter_content(chunk_size=8192):
|
resp.headers["Pragma"] = "no-cache"
|
||||||
data += chunk
|
resp.headers["Expires"] = "0"
|
||||||
# 在累积数据中找 JPEG 完整帧
|
return resp
|
||||||
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()
|
|
||||||
return "", 404
|
return "", 404
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(f"arm_refresh 失败: {ex}")
|
logger.error(f"arm_refresh 失败: {ex}")
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ CAMERA_CONFIG = {
|
|||||||
# ========== 机械臂摄像头流 ==========
|
# ========== 机械臂摄像头流 ==========
|
||||||
ARM_CAMERA_CONFIG = {
|
ARM_CAMERA_CONFIG = {
|
||||||
"url": "http://192.168.110.164:5003/api/camera/preview",
|
"url": "http://192.168.110.164:5003/api/camera/preview",
|
||||||
|
"snapshot_url": "http://192.168.110.164:5003/api/camera/snapshot",
|
||||||
}
|
}
|
||||||
|
|
||||||
# ========== HTTP 上传 ==========
|
# ========== HTTP 上传 ==========
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ def _ensure_ffmpeg():
|
|||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
"-f", "v4l2",
|
"-f", "v4l2",
|
||||||
"-input_format", "mjpeg",
|
"-input_format", "mjpeg",
|
||||||
|
"-re",
|
||||||
"-i", f"/dev/video{ARM_CAMERA_INDEX}",
|
"-i", f"/dev/video{ARM_CAMERA_INDEX}",
|
||||||
"-vf", "rotate=PI",
|
"-vf", "rotate=PI",
|
||||||
"-q:v", "8",
|
"-q:v", "8",
|
||||||
@@ -103,6 +104,40 @@ def arm_camera_restart():
|
|||||||
_ensure_ffmpeg()
|
_ensure_ffmpeg()
|
||||||
return jsonify({"ok": True})
|
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 客户端 ==========
|
# ========== RoboFlow 630 Socket API 客户端 ==========
|
||||||
class RoboFlowClient:
|
class RoboFlowClient:
|
||||||
|
|||||||
Reference in New Issue
Block a user