diff --git a/INSTALL.md b/INSTALL.md index 50cfc63..b4ce048 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,21 +1,73 @@ -# IPCam Orange Pi Dashboard — INSTALL +# IPCam Orange Pi Dashboard - INSTALL -Tài liệu này hướng dẫn triển khai trên Orange Pi (Linux). Mục tiêu: MediaMTX chạy như media server, FastAPI chạy như dashboard backend, React build ra static. +Tài liệu này hướng dẫn triển khai toàn bộ hệ thống trên duy nhất một thiết bị `Orange Pi 5`. -## 0) Mô hình triển khai +Mục tiêu: -- MediaMTX + Dashboard backend + Dashboard frontend chạy cùng máy. +- `MediaMTX` chạy trên chính Orange Pi 5 +- `FastAPI backend` chạy trên chính Orange Pi 5 +- `Frontend React` build static và serve ngay trên chính Orange Pi 5 +- File recordings lưu trên ổ cứng gắn trực tiếp vào Orange Pi 5 -## 1) Yêu cầu +## 0) Cấu hình phần cứng khuyến nghị -- Orange Pi chạy Debian/Ubuntu -- MediaMTX đã cài và chạy được -- Python 3.9+ (khuyến nghị 3.10+) -- Node.js 20+ (để build frontend) +Bắt buộc theo kịch bản này: -## 2) MediaMTX cấu hình tối thiểu +- Thiết bị: `Orange Pi 5` +- RAM: `8GB` hoặc `16GB` +- Ổ lưu trữ: `>= 256GB` -Trong file `mediamtx.yml`: +Khuyến nghị mạnh: + +- Dùng `NVMe SSD` thay vì thẻ nhớ +- Nếu ghi nhiều camera hoặc lưu lâu ngày, nên dùng `512GB` hoặc `1TB` +- Không nên để recordings trên thẻ microSD + +## 1) Mô hình triển khai + +Tất cả dịch vụ chạy trên cùng một máy Orange Pi 5: + +```text +Orange Pi 5 +|- MediaMTX +|- FastAPI backend +|- Frontend React static +`- Thu muc recordings tren SSD/NVMe +``` + +Không cần thêm server riêng cho MediaMTX hay dashboard. + +## 2) Yêu cầu hệ điều hành và phần mềm + +- Orange Pi 5 chạy Debian/Ubuntu +- Python `3.9+` khuyến nghị `3.10+` +- Node.js `20+` +- Docker + Docker Compose plugin +- Nginx hoặc Apache nếu muốn serve frontend static production + +## 3) Chuẩn bị ổ lưu trữ + +Ví dụ SSD được mount tại `/mnt/ssd`. + +Tạo thư mục dự án và thư mục recordings: + +```bash +sudo mkdir -p /mnt/ssd/IPCam_OrangePi_Dashboard +sudo mkdir -p /mnt/ssd/IPCam_OrangePi_Dashboard/mediamtx/recordings +sudo chown -R $USER:$USER /mnt/ssd/IPCam_OrangePi_Dashboard +``` + +Clone source: + +```bash +cd /mnt/ssd +git clone IPCam_OrangePi_Dashboard +cd /mnt/ssd/IPCam_OrangePi_Dashboard +``` + +## 4) MediaMTX cấu hình tối thiểu + +Trong `mediamtx/mediamtx.yml` cần có tối thiểu: ```yaml api: yes @@ -27,116 +79,147 @@ webrtcAllowOrigin: '*' record: yes recordFormat: fmp4 -recordPath: /recordings/%path/%Y-%m-%d_%H-%M-%S-%f +recordPath: /mnt/ssd/IPCam_OrangePi_Dashboard/mediamtx/recordings/%path/%Y-%m-%d_%H-%M-%S-%f recordPartDuration: 5m ``` -### 2.1) Nếu MediaMTX API bị 401 Unauthorized +Ghi chú: -401 nghĩa là request thiếu thông tin xác thực hợp lệ. Khi bật auth trong MediaMTX, bạn cần tạo user có quyền `api` và cấu hình backend dashboard gửi Basic Auth. +- `recordPath` nên trỏ thẳng vào SSD/NVMe +- Dashboard sẽ tự đồng bộ camera vào `mediamtx.yml` +- Dashboard cần truy cập được MediaMTX API ở `127.0.0.1:9997` -Ví dụ trong `mediamtx.yml`: +### 4.1) Nếu MediaMTX API dùng auth + +Nếu MediaMTX bật auth và dashboard bị `401 Unauthorized`, thêm user có quyền `api`: ```yaml authMethod: internal authInternalUsers: - user: dashboard pass: dashboard_password - ips: ['127.0.0.1', '::1', '192.168.88.0/24'] + ips: ['127.0.0.1', '::1', '192.168.1.0/24'] permissions: - action: api ``` -Sau đó set trực tiếp trong `api/data/config.json` của dashboard backend: +Sau đó khai báo trong `api/data/config.json`: -``` -"mediamtx_api_user": "dashboard", -"mediamtx_api_pass": "dashboard_password" +```json +{ + "mediamtx_api_user": "dashboard", + "mediamtx_api_pass": "dashboard_password" +} ``` -Trong `paths`, bạn có thể để dashboard tự thêm path bằng Settings (nhập RTSP URL). Backend sẽ cập nhật trực tiếp file `mediamtx.yml`. +## 5) Chạy MediaMTX trên Orange Pi 5 -Tạo thư mục recordings: +Nếu project đã có `mediamtx/docker-compose.yml`, chạy: ```bash -sudo mkdir -p /recordings -sudo chown -R $USER:$USER /recordings +cd /mnt/ssd/IPCam_OrangePi_Dashboard +docker compose -f mediamtx/docker-compose.yml up -d ``` -Mở firewall/cổng (tuỳ hệ thống): - -- `8554/tcp` RTSP -- `8889/tcp` WebRTC HTTP -- `9997/tcp` MediaMTX Control API -- `8189/udp` ICE - -## 3) Backend FastAPI - -### 3.1 Cài dependencies +Kiểm tra: ```bash -cd IPCam_OrangePi_Dashboard +docker compose -f mediamtx/docker-compose.yml ps +curl http://127.0.0.1:9997/v3/paths/list +``` + +## 6) Cài backend FastAPI + +### 6.1) Cài dependencies + +```bash +cd /mnt/ssd/IPCam_OrangePi_Dashboard python3 -m venv api/.venv source api/.venv/bin/activate pip install -r api/requirements.txt ``` -### 3.2 Cấu hình +### 6.2) Cấu hình backend -Backend chỉ đọc cấu hình từ `api/data/config.json` (không đọc `.env`). -Chạy lần đầu sẽ tự tạo file này (lưu camera + schedule + các tham số backend). -Danh sách camera trong `config.json` sẽ được backend đồng bộ từ `mediamtx.yml`. -Các thông số kết nối MediaMTX (`mediamtx_api_url`, `mediamtx_webrtc_url`, credentials, `recordings_dir`) do user điền trong dashboard Settings hoặc chỉnh trực tiếp `config.json`. +Backend chỉ đọc cấu hình từ `api/data/config.json`. -Bạn có thể chỉnh: +File này chứa: -- `mediamtx_api_url` (mặc định `http://127.0.0.1:9997`) -- `mediamtx_webrtc_url` (mặc định `http://127.0.0.1:8889`) -- `mediamtx_api_user` / `mediamtx_api_pass` (nếu bật auth API của MediaMTX) -- `recordings_dir` (mặc định `./mediamtx/recordings` trong project) -- `api_port` (mặc định `8008`) +- thông số MediaMTX +- thư mục recordings +- danh sách camera +- lịch ghi hình +- cổng backend -Các thao tác trong Settings: +Chạy lần đầu, backend có thể tự tạo file nếu chưa có. -- Add Camera: chỉ nhập RTSP URL, backend tự tạo tên `camN` trong `mediamtx.yml` -- Delete Camera: xóa path tương ứng trong `mediamtx.yml` -- MediaMTX record: bật/tắt `pathDefaults.record` trong `mediamtx.yml` -- Restart MediaMTX Docker: gọi `docker compose -f mediamtx/docker-compose.yml restart mediamtx` +Các giá trị nên dùng trên Orange Pi 5: -### 3.3 Chạy backend +- `mediamtx_api_url`: `http://127.0.0.1:9997` +- `mediamtx_webrtc_url`: `http://127.0.0.1:8889` +- `recordings_dir`: `/mnt/ssd/IPCam_OrangePi_Dashboard/mediamtx/recordings` +- `api_port`: `8008` + +Ví dụ `api/data/config.json`: + +```json +{ + "mediamtx_api_url": "http://127.0.0.1:9997", + "mediamtx_webrtc_url": "http://127.0.0.1:8889", + "mediamtx_api_user": null, + "mediamtx_api_pass": null, + "recordings_dir": "/mnt/ssd/IPCam_OrangePi_Dashboard/mediamtx/recordings", + "api_port": 8008, + "cameras": [], + "schedule": { + "enabled": true, + "weekdays_from": "18:00", + "weekdays_to": "08:00", + "weekend_all_day": true + } +} +``` + +### 6.3) Chạy backend ```bash +cd /mnt/ssd/IPCam_OrangePi_Dashboard source api/.venv/bin/activate python3 -m api.run ``` -## 4) Frontend (build static) - -### 4.1 Build +Kiểm tra: ```bash -cd IPCam_OrangePi_Dashboard +curl http://127.0.0.1:8008/api/health +``` + +## 7) Build frontend + +```bash +cd /mnt/ssd/IPCam_OrangePi_Dashboard npm install npm run build ``` Output nằm ở `dist/`. -### 4.2 Serve frontend +## 8) Serve frontend production -Có 2 cách phổ biến: +Có thể dùng `Nginx` hoặc `Apache`. Mục tiêu là: -1) Nginx serve `dist/` và reverse proxy `/api` + `/videos` về backend `:8008` -2) Dùng Caddy tương tự +- serve thư mục `dist/` +- reverse proxy `/api` về backend `127.0.0.1:8008` +- reverse proxy `/videos` về backend `127.0.0.1:8008` -Ví dụ Nginx server block tối thiểu: +### 8.1) Ví dụ Nginx ```nginx server { listen 80; server_name _; - root /opt/ipcam-dashboard/dist; + root /mnt/ssd/IPCam_OrangePi_Dashboard/dist; index index.html; location / { @@ -153,14 +236,14 @@ server { } ``` -Ví dụ Apache VirtualHost tối thiểu: +### 8.2) Ví dụ Apache ```apache ServerName _ - DocumentRoot /opt/ipcam-dashboard/dist + DocumentRoot /mnt/ssd/IPCam_OrangePi_Dashboard/dist - + Options Indexes FollowSymLinks AllowOverride All Require all granted @@ -180,16 +263,14 @@ Ví dụ Apache VirtualHost tối thiểu: ``` -Apache cần bật module trước khi reload: +Nếu dùng Apache: ```bash sudo a2enmod rewrite proxy proxy_http sudo systemctl reload apache2 ``` -## 5) Systemd service (khuyến nghị) - -### 5.1 Backend service +## 9) Systemd service cho backend Tạo file `/etc/systemd/system/ipcam-dashboard.service`: @@ -199,8 +280,8 @@ Description=IPCam Dashboard Backend After=network.target [Service] -WorkingDirectory=/opt/ipcam-dashboard -ExecStart=/opt/ipcam-dashboard/api/.venv/bin/python /opt/ipcam-dashboard/api/run.py +WorkingDirectory=/mnt/ssd/IPCam_OrangePi_Dashboard +ExecStart=/mnt/ssd/IPCam_OrangePi_Dashboard/api/.venv/bin/python /mnt/ssd/IPCam_OrangePi_Dashboard/api/run.py Restart=always RestartSec=2 @@ -216,9 +297,31 @@ sudo systemctl enable --now ipcam-dashboard sudo systemctl status ipcam-dashboard ``` -## 6) Kiểm tra nhanh +## 10) Firewall/cổng cần mở -- Backend: `curl http://localhost:8008/api/health` -- MediaMTX API: `curl http://localhost:9997/v3/paths/list` -- Frontend: mở `http:///` +Tùy hệ thống mạng, cần cho phép: + +- `8554/tcp` cho RTSP +- `8889/tcp` cho WebRTC HTTP +- `9997/tcp` cho MediaMTX API +- `8189/udp` cho ICE/WebRTC +- `80/tcp` hoặc `443/tcp` nếu serve dashboard qua web server + +## 11) Kiểm tra nhanh sau triển khai + +- Backend: `curl http://127.0.0.1:8008/api/health` +- MediaMTX API: `curl http://127.0.0.1:9997/v3/paths/list` +- Frontend: mở `http:///` +- Trong Settings: + - thêm camera bằng RTSP URL + - lưu cấu hình + - kiểm tra camera xuất hiện trong Live View + - kiểm tra file recordings được tạo trên SSD/NVMe + +## 12) Ghi chú vận hành + +- Nên để toàn bộ project và recordings trên cùng SSD/NVMe +- Nếu ghi nhiều camera liên tục, cần theo dõi dung lượng trống định kỳ +- Orange Pi 5 RAM `16GB` phù hợp hơn khi số camera lớn hơn hoặc cần playback đồng thời +- Khi cần ổn định cao, nên dùng nguồn tốt và tản nhiệt phù hợp cho Orange Pi 5 diff --git a/README.md b/README.md index c91308d..c9116b6 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,83 @@ # IPCam Orange Pi Dashboard -Dashboard giám sát camera IP gọn nhẹ chạy trên Orange Pi, dựa trên MediaMTX: +Dashboard giám sát camera IP chạy gọn trên duy nhất một thiết bị `Orange Pi 5`, dùng `MediaMTX` làm media server, `FastAPI` làm backend và `React` làm frontend. -- Live View (WebRTC WHEP) dạng grid, auto reconnect, lazy load -- Playback fMP4 theo camera + ngày (file list + HTML5 video) -- Settings: quản lý camera trực tiếp trong `mediamtx.yml` + lịch ghi hình +## Mô hình triển khai khuyến nghị + +Toàn bộ hệ thống chạy trên cùng một máy: + +- Thiết bị: `Orange Pi 5` +- RAM: `8GB` hoặc `16GB` +- Lưu trữ: `SSD/NVMe >= 256GB` +- Hệ điều hành: Debian/Ubuntu cho Orange Pi +- Thành phần chạy cùng máy: + - `MediaMTX` + - `FastAPI backend` + - `Frontend React build static` + - Thư mục recordings + +Mô hình này phù hợp khi cần một hộp camera nội bộ gọn nhẹ, dễ bảo trì và không phụ thuộc thêm server khác. + +## Tính năng chính + +- Live View qua `WebRTC/WHEP`, dạng grid, tự reconnect, lazy load +- Playback file ghi hình `fMP4` theo camera và ngày +- Settings để: + - thêm/xóa camera trực tiếp vào `mediamtx.yml` + - bật/tắt ghi hình + - chỉnh lịch ghi hình + - cấu hình API/WebRTC URL của MediaMTX + - restart container MediaMTX ## Cấu trúc thư mục - `src/`: frontend React - `api/`: backend FastAPI -- `.trae/documents/`: tài liệu yêu cầu/kiến trúc +- `mediamtx/`: Dockerfile, `docker-compose.yml`, `mediamtx.yml` +- `.trae/documents/`: tài liệu yêu cầu và kiến trúc + +## Luồng hệ thống + +```text +IP Camera -> MediaMTX -> FastAPI Backend -> Frontend Dashboard + | + -> recordings tren SSD/NVMe +``` + +## Yêu cầu phần cứng + +- `Orange Pi 5` RAM `8GB` hoặc `16GB` +- Ổ lưu trữ `>= 256GB` +- Khuyến nghị dùng `NVMe SSD` thay vì thẻ nhớ để ghi hình ổn định hơn +- Nếu ghi nhiều camera hoặc giữ file lâu ngày, nên dùng `512GB` hoặc `1TB` + +## Yêu cầu phần mềm + +- Python `3.9+` (khuyến nghị `3.10+`) +- Node.js `20+` +- Docker và Docker Compose plugin cho MediaMTX +- Nginx hoặc Apache nếu muốn serve frontend static qua web server ## Backend API - `GET /api/health` - `GET /api/config` -- `POST /api/config/basic` (cập nhật thông số `config.json`) -- `GET /api/paths` (proxy trạng thái từ MediaMTX) -- `POST /api/recording` (bật/tắt ghi hình ngay) -- `POST /api/scheduler/enabled` / `POST /api/scheduler/schedule` +- `POST /api/config/basic` +- `GET /api/paths` +- `POST /api/recording` +- `POST /api/scheduler/enabled` +- `POST /api/scheduler/schedule` - `GET /api/mediamtx/config` -- `POST /api/mediamtx/cameras` (body: `{ "rtsp_url": "..." }`) +- `POST /api/mediamtx/cameras` - `DELETE /api/mediamtx/cameras/{name}` -- `POST /api/mediamtx/recording` (ghi `pathDefaults.record` vào `mediamtx.yml`) -- `POST /api/mediamtx/restart` (restart container `mediamtx`) +- `POST /api/mediamtx/recording` +- `POST /api/mediamtx/restart` - `GET /api/recordings?camera=cam1&date=YYYY-MM-DD` - `GET /videos//.fmp4` -## Chạy dev (máy dev) +## Chạy development -### 1) Chạy backend +### Backend ```bash python3 -m venv api/.venv @@ -39,30 +86,32 @@ pip install -r api/requirements.txt python3 -m api.run ``` -### 2) Chạy frontend +### Frontend ```bash npm install npm run dev ``` -Frontend dev server đã được cấu hình proxy `/api` và `/videos` sang `http://localhost:8008`. +Frontend dev server đã proxy sẵn `/api` và `/videos` sang `http://localhost:8008`. ## Cấu hình -Backend chỉ dùng file `api/data/config.json` (không đọc `.env`). -Frontend dùng `.env` chỉ với `VITE_DEV_BACKEND_URL` và `VITE_API_BASE_URL`. +Backend chỉ đọc `api/data/config.json`. +Frontend chỉ dùng `.env` cho môi trường dev với `VITE_DEV_BACKEND_URL` và `VITE_API_BASE_URL`. + +Các key quan trọng trong `config.json`: - `mediamtx_api_url`: ví dụ `http://127.0.0.1:9997` - `mediamtx_webrtc_url`: ví dụ `http://127.0.0.1:8889` -- `mediamtx_api_user`: username API (nếu bật auth trong MediaMTX) -- `mediamtx_api_pass`: password API (nếu bật auth trong MediaMTX) -- `recordings_dir`: ví dụ `./mediamtx/recordings` (cùng máy) hoặc đường dẫn mount NFS/SMB -- `api_port`: cổng chạy backend (mặc định `8008`) -- `cameras`: danh sách camera được đồng bộ từ `mediamtx.yml` (`name` + `rtsp_url`) +- `mediamtx_api_user`: username API nếu bật auth +- `mediamtx_api_pass`: password API nếu bật auth +- `recordings_dir`: đường dẫn recordings trên SSD/NVMe +- `api_port`: cổng backend, mặc định `8008` +- `cameras`: danh sách camera đồng bộ từ `mediamtx.yml` - `schedule`: lịch ghi hình -Ví dụ `config.json`: +Ví dụ: ```json { @@ -82,6 +131,14 @@ Ví dụ `config.json`: } ``` -## Triển khai +## Triển khai production -Xem hướng dẫn chi tiết trong `INSTALL.md`. +Kịch bản production chuẩn cho project này: + +- `Orange Pi 5` là máy duy nhất chạy cả dashboard và MediaMTX +- MediaMTX chạy local trên chính Orange Pi +- Backend FastAPI chạy local trên chính Orange Pi +- Frontend build ra `dist/` và được serve local +- Recordings lưu vào SSD/NVMe gắn trực tiếp với Orange Pi + +Hướng dẫn cài đặt chi tiết xem trong `INSTALL.md`.