# Captura de foto desde cámara HiLook (NVR + RTSP + FFmpeg)

Guía completa para configurar y diagnosticar la captura de imágenes desde el NVR **HiLook NVR-108H-D/8P** con cámara **IPC-D140H** en el sistema *entradas_volteos*.

---

## Resumen

| Componente | Rol |
|------------|-----|
| **NVR HiLook NVR-108H-D/8P** | Recibe la cámara por red y expone RTSP en el puerto 554 |
| **Cámara IPC-D140H** | Envía video al NVR (no se conecta directo a Laravel) |
| **FFmpeg** | En el PC/servidor PHP, extrae 1 frame JPEG del stream RTSP |
| **Laravel** | Ejecuta FFmpeg vía `FfmpegHelper` y guarda la foto en `storage/app/public/volteos/` |

**Importante:** el HiLook NVR **no tiene snapshot HTTP/ISAPI** como las cámaras Hikvision enterprise. La captura en esta app usa **RTSP + FFmpeg**.

---

## Hardware de referencia (instalación actual)

| Equipo | Modelo | IP ejemplo |
|--------|--------|------------|
| NVR | HiLook **NVR-108H-D/8P** | `192.168.1.68` |
| Cámara | HiLook **IPC-D140H** | Conectada al NVR (canal D1) |
| PC con Laravel | Windows | Misma red `192.168.1.x` |

Los modelos **son compatibles** con RTSP. Si falla la foto, casi siempre es **configuración de codec (H.265)**, **red/firewall** o **sesiones RTSP saturadas**, no incompatibilidad de modelo.

---

## Arquitectura del flujo

```
IPC-D140H  ──►  NVR (192.168.1.68:554)  ──RTSP──►  PC (php + ffmpeg)  ──►  JPEG en storage
```

URLs RTSP estándar HiLook/Hikvision vía NVR:

| Stream | Canal | URL |
|--------|-------|-----|
| Principal | 1 | `rtsp://usuario:clave@192.168.1.68:554/Streaming/Channels/101` |
| Secundario (substream) | 1 | `rtsp://usuario:clave@192.168.1.68:554/Streaming/Channels/102` |

Regla del número de canal: **canal N → principal `N×100+1`, substream `N×100+2`**.  
Canal 1 → `101` y `102`.

**Recomendación para captura de fotos:** usar **substream (`102`)** en **H.264**.

---

## Parte 1 — Instalar FFmpeg en Windows

### Opción A: winget (recomendada)

```powershell
winget install Gyan.FFmpeg
```

Tras instalar, **cierre y vuelva a abrir** la terminal (o reinicie Cursor) para que el PATH se actualice.

Verificar:

```powershell
ffmpeg -version
```

### Opción B: ruta explícita en `.env`

Si PHP no encuentra `ffmpeg` en PATH, use la ruta completa (ejemplo WinGet):

```env
FFMPEG_PATH=C:\Users\TU_USUARIO\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe\ffmpeg-8.1.1-full_build\bin\ffmpeg.exe
```

---

## Parte 2 — Variables en `.env`

Añadir o revisar en la raíz del proyecto:

```env
# Ruta a ffmpeg.exe (obligatorio si no está en PATH de PHP)
FFMPEG_PATH=C:\Users\...\ffmpeg.exe

# RTSP: en Windows usar solo UDP reduce error -10106
FFMPEG_RTSP_TRANSPORTS=udp

# Si falla 102, probar también 101 (separado por coma en código; aquí solo fallback numérico)
FFMPEG_RTSP_FALLBACK_CHANNELS=101

# Timeout opcional (segundos)
# FFMPEG_RTSP_TIMEOUT=35
```

Limpiar caché de configuración:

```powershell
cd c:\proyecto_personal\entradas_volteos
php artisan config:clear
php artisan serve
```

---

## Parte 3 — Configuración del NVR (web)

Acceso: `http://192.168.1.68` (usuario/contraseña del NVR, ej. `admin`).

### 3.1 Activar RTSP

**Configuración → Red → Ajustes avanzados → Servicio de red**

- Marcar **Activar RTSP**
- Guardar

### 3.2 Video: usar H.264 en substream (crítico)

**Configuración → Video y audio → Video**

1. **Cámara:** seleccionar la cámara (ej. `[D1] patio atras`).
2. **Tipo de flujo:** cambiar a **Flujo secundario (Normal)** — no dejar solo el principal.
3. Configurar:

| Parámetro | Valor recomendado |
|-----------|-------------------|
| Codificación de video | **H.264** (no H.265) |
| Resolución | 640×480 o 1280×720 |
| Velocidad de frames | 10–15 fps |
| Veloc. máx. bits | 512–1024 Kbps |

4. **Guardar**.

Si deja **H.265** en el flujo principal (como en la captura inicial del proyecto), FFmpeg puede **conectar** pero **no generar JPEG** (errores `hevc`, `PPS id out of range`).

### 3.3 Comprobar cámara Online

**Vista en vivo** → el canal debe aparecer **Online**, no “Sin señal” / Offline.

### 3.4 Opcional

- **Red → Protocolo de integración:** activar ONVIF si está disponible.
- **Sistema → Seguridad:** desbloquear usuario `admin` si hubo bloqueo por intentos fallidos.

---

## Parte 4 — Configuración en la aplicación

### 4.1 Hardware (cámara IP)

Menú: **Administración → Configuración de hardware**  
Ruta front: `/configuracion-hardware/lista`

Crear o editar dispositivo tipo **Cámara IP**:

| Campo | Valor ejemplo |
|-------|----------------|
| Nombre | Cámara caseta / patio |
| IP / URL | `192.168.1.68` |
| Puerto | `554` |
| Usuario / contraseña | Credenciales del NVR |
| **URL Snapshot / RTSP** | `rtsp://admin:CLAVE@192.168.1.68:554/Streaming/Channels/102` |
| Activo | Sí |

### 4.2 Módulo de prueba (temporal)

Ruta front: `/prueba-camara`  
Menú: **Administración → Prueba cámara**

Endpoints API:

| Método | Ruta | Descripción |
|--------|------|-------------|
| GET | `/api/camara-prueba/estado` | FFmpeg, cámara activa, diagnóstico de red/RTSP |
| POST | `/api/camara-prueba/capturar-foto` | Captura real vía RTSP |
| POST | `/api/camara-prueba/capturar-foto-test` | Placeholder sin cámara |

**Buenas prácticas al probar:**

- No pulse **Verificar** y **Tomar foto** seguidos (el NVR acepta pocas sesiones RTSP).
- Espere 10–15 s entre pruebas.
- Cierre VLC y procesos `ffmpeg.exe` colgados antes de probar.

### 4.3 Uso en producción (Vigilante)

La captura desde NVR se reutiliza en `BoletoController::capturarFoto()` cuando `url_snapshot` empieza con `rtsp://`.  
Véase también: `EJECUCION_VIGILANTE_CAMARA_VIAJES_TICKET.md` en la raíz del repo.

---

## Parte 5 — Firewall de Windows (script)

El puerto 554 puede responder en un test TCP, pero **ffmpeg.exe** o **php.exe** pueden estar bloqueados para salir a la red privada → error **-10106**.

### Ejecutar script (PowerShell como administrador)

1. Inicio → **Windows PowerShell** → clic derecho → **Ejecutar como administrador**.
2. Ejecutar **un comando por línea**:

```powershell
cd c:\proyecto_personal\entradas_volteos
```

```powershell
Set-ExecutionPolicy -Scope Process Bypass -Force
```

```powershell
.\scripts\permitir-ffmpeg-firewall.ps1
```

O en una sola línea:

```powershell
cd c:\proyecto_personal\entradas_volteos; Set-ExecutionPolicy -Scope Process Bypass -Force; .\scripts\permitir-ffmpeg-firewall.ps1
```

Salida esperada:

```
Regla creada: PHP Laravel RTSP
Regla creada: FFmpeg RTSP outbound
Regla creada: FFprobe RTSP outbound
Listo. Reinicie php artisan serve y pruebe de nuevo en Prueba camara.
```

Si dice `Regla existente`, las reglas ya estaban creadas.

**Nota:** el script está escrito para **Windows PowerShell 5.1** (no usar operador `?.` de PowerShell 7).

---

## Parte 6 — Diagnóstico de errores

### Tabla rápida

| Síntoma | Causa probable | Acción |
|---------|----------------|--------|
| `FFmpeg no disponible` | No instalado o `FFMPEG_PATH` incorrecto | Instalar FFmpeg, configurar `.env`, `config:clear` |
| `-10106` / `Connection to tcp://...554 failed` | Firewall, muchas sesiones RTSP, NVR saturado | Script firewall, cerrar VLC/ffmpeg, reiniciar NVR, no spamear “Verificar” |
| Puerto 554 abierto pero UDP/TCP falla | Igual que arriba + usuario bloqueado | Firewall, reinicio NVR, revisar Seguridad del NVR |
| `hevc`, `PPS id out of range`, timeout sin JPG | Stream en **H.265** o cámara Offline | Substream **H.264**, canal Online, URL `/Channels/102` |
| VLC no abre la URL | Red, NVR apagado, credenciales, cámara Offline | Arreglar en NVR/red antes de la app |
| VLC sí abre, la app no | Firewall PHP/FFmpeg | Ejecutar script firewall como admin |

### Sobre el mensaje «tcp» con `FFMPEG_RTSP_TRANSPORTS=udp`

RTSP **siempre negocia la sesión por TCP en el puerto 554**.  
`udp` indica que el **video** va por UDP; el log puede seguir mostrando `tcp://192.168.1.68:554`.

### Prueba definitiva con VLC

Medios → Abrir URL de red:

```
rtsp://admin:CLAVE@192.168.1.68:554/Streaming/Channels/102
```

- **VLC OK** → problema en PC (firewall, sesiones, Laravel).
- **VLC falla** → problema NVR/red/cámara.

### Diagnóstico desde consola (opcional)

```powershell
cd c:\proyecto_personal\entradas_volteos
php artisan config:clear
php -r "require 'vendor/autoload.php'; $a=require 'bootstrap/app.php'; $a->make('Illuminate\Contracts\Console\Kernel')->bootstrap(); echo json_encode(App\Support\FfmpegHelper::diagnoseRtsp('rtsp://admin:CLAVE@192.168.1.68:554/Streaming/Channels/102'), JSON_PRETTY_PRINT);"
```

Interpretación:

- `puerto_abierto: true` + `udp.conecta: true` + detalle con `hevc` → **cambiar a H.264** en el NVR.
- `puerto_abierto: true` + `udp.conecta: false` → firewall o sesiones RTSP.
- `puerto_abierto: false` → NVR apagado, IP incorrecta o PC en otra red.

---

## Parte 7 — Archivos relevantes en el código

| Archivo | Función |
|---------|---------|
| `app/Support/FfmpegHelper.php` | Ruta FFmpeg, captura RTSP, diagnóstico, mensajes de error |
| `app/Http/Controllers/BoletoController.php` | `capturarFoto()` / `capturarFotoDesdeRtsp()` |
| `app/Http/Controllers/CamaraPruebaController.php` | Módulo `/prueba-camara` |
| `app/Http/Controllers/ConfiguracionHardwareController.php` | CRUD hardware, probar video |
| `config/services.php` | Claves `ffmpeg.*` |
| `scripts/permitir-ffmpeg-firewall.ps1` | Reglas firewall outbound para php/ffmpeg |
| `resources/js/src/views/prueba-camara/index.vue` | UI de prueba |

---

## Parte 8 — Checklist de puesta en marcha

- [ ] NVR encendido en `192.168.1.68`, PC en `192.168.1.x`
- [ ] RTSP activado en el NVR
- [ ] Cámara **Online** en vista en vivo
- [ ] Substream en **H.264** (no H.265)
- [ ] URL en Hardware: `.../Streaming/Channels/102`
- [ ] FFmpeg instalado; `FFMPEG_PATH` en `.env` si hace falta
- [ ] `FFMPEG_RTSP_TRANSPORTS=udp` en `.env`
- [ ] `php artisan config:clear` y reinicio de `php artisan serve`
- [ ] Script firewall ejecutado **como administrador**
- [ ] Sin VLC ni `ffmpeg.exe` colgados
- [ ] Prueba en `/prueba-camara` → **Tomar foto** (solo una vez, tras esperar)

---

## Parte 9 — Orden recomendado si sigue fallando

1. Cambiar substream a **H.264** y guardar en el NVR.
2. Reiniciar NVR (30 s apagado).
3. Probar URL **102** en VLC.
4. Ejecutar script firewall como admin.
5. `php artisan config:clear` + reiniciar servidor Laravel.
6. Una sola prueba **Tomar foto** en la app.
7. Si persiste, revisar `storage/logs/laravel.log` y el panel **Red (desde el servidor)** en Prueba cámara.

---

## Referencias

- Manual HiLook NVR-108H-D/8P: RTSP vía `/Streaming/Channels/101` y `102`
- Especificaciones NVR: H.264/H.265; la app prefiere **H.264** para captura JPEG con FFmpeg
- Documento relacionado en repo: `EJECUCION_VIGILANTE_CAMARA_VIAJES_TICKET.md`

---

*Última actualización: mayo 2026 — entradas_volteos / Macuspana caseta*
