Saltar al contenido principal
Esta página fue traducida automáticamente. Si encuentra errores o tiene sugerencias, contáctenos.

Descripción general

Las cámaras Rhombus exponen varias formas de acceder al contenido de video a través de la API pública:
  • Transmisión en vivo para monitoreo en tiempo real, incrustada como un iframe alojado por Rhombus
  • Manifiestos HLS (.m3u8) para reproducción directa en tu propio reproductor
  • Imágenes en miniatura para paneles y vistas previas rápidas
  • Capturas de fotogramas en cualquier marca de tiempo histórica
Para descargar grabaciones como archivos MP4, consulta Acceso a video grabado.

Obtención de miniaturas de cámara

Todas las cámaras Rhombus suben automáticamente miniaturas. Son ideales para paneles, interfaces de monitoreo y verificaciones rápidas de estado.

Endpoint de miniaturas

Recupera las miniaturas de la cámara mediante una solicitud GET a:
https://media.rhombussystems.com/media/{cameraUuid}/{mediaRegion}/snapshot.jpeg
Todos los endpoints de media.rhombussystems.com usan la misma autenticación que la API principal — incluye los encabezados x-auth-scheme y x-auth-apikey en cada solicitud.
Para obtener el cameraUuid y el mediaRegion de una cámara, llama a POST /api/camera/getMinimalCameraStateList. La respuesta contiene un arreglo cameraStates; cada entrada tiene los campos uuid y mediaRegion.

Implementación básica

cURL
# 1. Lista las cámaras para obtener UUIDs y regiones de medios
curl -X POST \
  "https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{}'
cURL
# 2. Obtén la miniatura usando el UUID y la región de medios de una cámara
curl -X GET \
  "https://media.rhombussystems.com/media/{CAMERA_UUID}/{MEDIA_REGION}/snapshot.jpeg" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  --output thumbnail.jpg

Obtener fotogramas en momentos específicos

Para obtener una imagen JPEG en un momento histórico específico, llama a POST /api/video/getExactFrameUri. La respuesta contiene un frameUri — haz una solicitud GET a ese URI con los mismos encabezados de autenticación para descargar el fotograma.
async function getFrameAtTime(cameraUuid, timestampMs, apiKey) {
  // Get a signed frame URI
  const frameUriResponse = await fetch('https://api2.rhombussystems.com/api/video/getExactFrameUri', {
    method: 'POST',
    headers: {
      'x-auth-scheme': 'api-token',
      'x-auth-apikey': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      cameraUuid: cameraUuid,
      timestampMs: timestampMs // epoch milliseconds
    })
  });

  const { frameUri } = await frameUriResponse.json();

  // Fetch the actual JPEG
  const frameResponse = await fetch(frameUri, {
    headers: {
      'x-auth-scheme': 'api-token',
      'x-auth-apikey': apiKey
    }
  });

  return frameResponse.blob();
}
La solicitud también acepta los campos opcionales downscaleFactor, jpgQuality y permyriadCropX/permyriadCropY/permyriadCropWidth/permyriadCropHeight (cada uno en milésimas de las dimensiones de la imagen, donde 10000 = 100%).

Transmisión de video en vivo

Rhombus ofrece dos formas de reproducir video en vivo en tu aplicación:
  1. Incrustar el reproductor Rhombus como iframe — la opción más sencilla; los analíticos y eventos de la línea de tiempo se renderizan automáticamente.
  2. Reproducir el manifiesto HLS directamente — usa tu propio reproductor (hls.js, Safari nativo, etc.) para tener control total de la UI.
Ambas opciones provienen de la misma llamada a la API: POST /api/camera/createSharedLiveVideoStream. La respuesta contiene ambas URLs:
CampoDescripción
sharedLiveVideoStreamUrlURL del reproductor alojado por Rhombus — incrústala en un iframe
sharedLiveM3U8StreamUrlManifiesto HLS (.m3u8) — reprodúcelo directamente con cualquier reproductor HLS
sharedLiveVideoStreamUuidUUID del recurso compartido creado, útil para revocarlo más tarde

Incrustar transmisiones compartidas como iframes (Recomendado)

Crea una transmisión compartida y luego coloca la URL devuelta dentro de un iframe. El reproductor del iframe maneja la autenticación a través de la propia URL, por lo que no se necesita una API key en el navegador.
async function createSharedStream(cameraUuid, apiKey) {
  const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
    method: 'POST',
    headers: {
      'x-auth-scheme': 'api-token',
      'x-auth-apikey': apiKey,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ cameraUuid })
  });

  const data = await response.json();
  return data.sharedLiveVideoStreamUrl;
}
Incrusta la URL devuelta:
<iframe
  allow="fullscreen"
  frameborder="0"
  height="100%"
  width="100%"
  src="SHARED_STREAM_URL_GOES_HERE">
</iframe>

Ejemplo de componente React

import React, { useEffect, useState } from 'react';

const RhombusSharedStream = ({
  cameraUuid,
  apiKey,
  width = "100%",
  height = "400px",
  options = {}
}) => {
  const [streamUrl, setStreamUrl] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;

    (async () => {
      try {
        setLoading(true);
        const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
          method: 'POST',
          headers: {
            'x-auth-scheme': 'api-token',
            'x-auth-apikey': apiKey,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ cameraUuid })
        });

        if (!response.ok) throw new Error('Failed to create shared stream');
        const data = await response.json();

        const url = new URL(data.sharedLiveVideoStreamUrl);
        Object.entries(options).forEach(([key, value]) => {
          url.searchParams.set(key, value);
        });

        if (!cancelled) {
          setStreamUrl(url.toString());
          setError(null);
        }
      } catch (err) {
        if (!cancelled) setError(err.message);
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();

    return () => { cancelled = true; };
  }, [cameraUuid, apiKey, options]);

  if (loading) return <div>Loading stream...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <iframe
      src={streamUrl}
      width={width}
      height={height}
      frameBorder="0"
      allow="fullscreen"
      title={`Camera ${cameraUuid} Stream`}
    />
  );
};

export default RhombusSharedStream;

Parámetros de URL para control mejorado

Personaliza el reproductor incrustado mediante parámetros de URL. Todos los parámetros pueden establecerse en true o false:
ParámetroDescripción
disableautoplayActiva o desactiva la reproducción automática al cargar
hideeventsActiva o desactiva la línea de tiempo y los eventos relacionados
realtimeActiva o desactiva la transmisión en tiempo real por defecto
showheaderActiva o desactiva los botones de zoom y transmisión arriba
showfooterActiva o desactiva el nombre de la cámara y la marca de tiempo abajo
lowResFuerza baja resolución en la transmisión de video
performanceModeFuerza el modo de rendimiento permitiendo el uso de GPU
tMarca de tiempo (milisegundos epoch) donde debe iniciar el video

Uso de parámetros

Anexa los parámetros a la URL de la transmisión compartida: https://{{url}}/?{{variable}}=true&{{variable}}=false Ejemplo:
https://console.rhombussystems.com/share/live/ehrF58ABSj2VT8QzXh7GVA?disableautoplay=true&hideevents=true&realtime=true

Uso en React con parámetros

<RhombusSharedStream
  cameraUuid="your-camera-uuid"
  apiKey="your-api-key"
  options={{
    disableautoplay: 'true',
    hideevents: 'true',
    realtime: 'true',
    showheader: 'false',
    lowRes: 'false'
  }}
/>

Incrustar transmisiones HLS directamente

Si quieres renderizar video en vivo en tu propio reproductor en lugar del iframe alojado, usa sharedLiveM3U8StreamUrl de la misma respuesta de createSharedLiveVideoStream. El manifiesto es un .m3u8 HLS estándar y está firmado con un token en la URL — no se necesita el encabezado x-auth-apikey para obtenerlo.
La URL HLS está limitada a una sola organización y tiene un tiempo de expiración. Trátala como una credencial y no la expongas en páginas públicas sin una política de expiración.
Safari reproduce HLS de forma nativa. Para otros navegadores, usa hls.js:
<video id="rhombus-live" controls autoplay muted style="width: 100%;"></video>

<script type="module">
  import Hls from 'https://cdn.jsdelivr.net/npm/hls.js@latest/dist/hls.min.js';

  async function startLive(cameraUuid, apiKey) {
    const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
      method: 'POST',
      headers: {
        'x-auth-scheme': 'api-token',
        'x-auth-apikey': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ cameraUuid })
    });

    const { sharedLiveM3U8StreamUrl } = await response.json();
    const video = document.getElementById('rhombus-live');

    if (Hls.isSupported()) {
      const hls = new Hls();
      hls.loadSource(sharedLiveM3U8StreamUrl);
      hls.attachMedia(video);
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = sharedLiveM3U8StreamUrl;
    }
  }

  startLive('YOUR_CAMERA_UUID', 'YOUR_API_KEY');
</script>
Como la creación de una transmisión compartida requiere una API key, ejecuta la llamada a createSharedLiveVideoStream desde un servidor que controles y pasa al navegador únicamente la sharedLiveM3U8StreamUrl (o sharedLiveVideoStreamUrl) resultante. Mantén la API key fuera del código del cliente.

Acceso a video grabado

La descarga de grabaciones como un archivo MP4 único no está expuesta como una operación de la API pública. La capa de almacenamiento de Rhombus sirve el video grabado como una secuencia de segmentos de medios en lugar de un MP4 pre-renderizado, por lo que producir un clip descargable requiere obtener y concatenar los segmentos del lado del cliente. La forma admitida de hacerlo es la Rhombus CLI:
# Descarga la última hora de grabaciones de una cámara como MP4
rhombus footage --camera CAMERA_UUID --duration 1h --output footage.mp4

# Descarga el clip asociado a una alerta específica
rhombus alert download --alert ALERT_UUID --output alert.mp4
Para flujos basados en alertas, configura un webhook para entregar eventos de alerta a tu servicio y luego invoca la CLI (o replica su lógica de obtención de segmentos) para recuperar el clip correspondiente. Para listar alertas recientes de forma programática, usa POST /api/event/getPolicyAlerts — el endpoint anterior getPolicyAlertsV2 está obsoleto.

Ejemplo de implementación completa

Un componente React que admite tanto vista de miniatura como transmisión en vivo:
import React, { useEffect, useRef, useState, useCallback } from 'react';

const RhombusCameraViewer = ({
  cameraUuid,
  apiKey,
  viewType = 'thumbnail', // 'thumbnail' | 'stream'
  autoRefresh = true,
  refreshInterval = 5000,
  streamOptions = {},
  width = "100%",
  height = "400px"
}) => {
  const [thumbnailUrl, setThumbnailUrl] = useState(null);
  const [streamUrl, setStreamUrl] = useState(null);
  const [cameraDetails, setCameraDetails] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const lastObjectUrl = useRef(null);

  const getCameraDetails = useCallback(async () => {
    const response = await fetch('https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList', {
      method: 'POST',
      headers: {
        'x-auth-scheme': 'api-token',
        'x-auth-apikey': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({})
    });

    if (!response.ok) throw new Error('Failed to list cameras');
    const data = await response.json();
    const camera = (data.cameraStates || []).find(c => c.uuid === cameraUuid);
    if (!camera) throw new Error('Camera not found');
    return camera;
  }, [cameraUuid, apiKey]);

  const loadThumbnail = useCallback(async (camera) => {
    const response = await fetch(
      `https://media.rhombussystems.com/media/${camera.uuid}/${camera.mediaRegion}/snapshot.jpeg`,
      { headers: { 'x-auth-scheme': 'api-token', 'x-auth-apikey': apiKey } }
    );
    if (!response.ok) throw new Error('Failed to load thumbnail');

    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    if (lastObjectUrl.current) URL.revokeObjectURL(lastObjectUrl.current);
    lastObjectUrl.current = url;
    setThumbnailUrl(url);
  }, [apiKey]);

  const createSharedStream = useCallback(async () => {
    const response = await fetch('https://api2.rhombussystems.com/api/camera/createSharedLiveVideoStream', {
      method: 'POST',
      headers: {
        'x-auth-scheme': 'api-token',
        'x-auth-apikey': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ cameraUuid })
    });
    if (!response.ok) throw new Error('Failed to create shared stream');

    const data = await response.json();
    const url = new URL(data.sharedLiveVideoStreamUrl);
    Object.entries(streamOptions).forEach(([key, value]) => {
      url.searchParams.set(key, value);
    });
    setStreamUrl(url.toString());
  }, [cameraUuid, apiKey, streamOptions]);

  useEffect(() => {
    let cancelled = false;
    let interval = null;

    (async () => {
      try {
        setIsLoading(true);
        const camera = await getCameraDetails();
        if (cancelled) return;
        setCameraDetails(camera);

        if (viewType === 'thumbnail') {
          await loadThumbnail(camera);
          if (autoRefresh) {
            interval = setInterval(() => {
              loadThumbnail(camera).catch(err => setError(err.message));
            }, refreshInterval);
          }
        } else if (viewType === 'stream') {
          await createSharedStream();
        }

        setError(null);
      } catch (err) {
        if (!cancelled) setError(err.message);
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    })();

    return () => {
      cancelled = true;
      if (interval) clearInterval(interval);
      if (lastObjectUrl.current) {
        URL.revokeObjectURL(lastObjectUrl.current);
        lastObjectUrl.current = null;
      }
    };
  }, [viewType, autoRefresh, refreshInterval, getCameraDetails, loadThumbnail, createSharedStream]);

  if (error) {
    return (
      <div style={{ padding: '20px', border: '1px solid #ff6b6b', borderRadius: '4px' }}>
        <p>Error: {error}</p>
      </div>
    );
  }

  if (isLoading) {
    return <div style={{ padding: '20px', textAlign: 'center' }}>Loading camera...</div>;
  }

  return (
    <div style={{ width, height }}>
      {viewType === 'thumbnail' && thumbnailUrl && (
        <img
          src={thumbnailUrl}
          alt={cameraDetails?.name || `Camera ${cameraUuid}`}
          style={{ width: '100%', height: '100%', objectFit: 'cover' }}
        />
      )}
      {viewType === 'stream' && streamUrl && (
        <iframe
          src={streamUrl}
          width="100%"
          height="100%"
          frameBorder="0"
          allow="fullscreen"
          title={`Camera ${cameraUuid} Stream`}
        />
      )}
    </div>
  );
};

export default RhombusCameraViewer;

Ejemplos de uso

// Vista de miniatura con auto-refresco
<RhombusCameraViewer
  cameraUuid="your-camera-uuid"
  apiKey="your-api-key"
  viewType="thumbnail"
  autoRefresh={true}
  refreshInterval={3000}
  width="640px"
  height="480px"
/>

// Transmisión en vivo con opciones personalizadas
<RhombusCameraViewer
  cameraUuid="your-camera-uuid"
  apiKey="your-api-key"
  viewType="stream"
  streamOptions={{
    disableautoplay: 'false',
    hideevents: 'false',
    realtime: 'true',
    showheader: 'true',
    showfooter: 'true'
  }}
  width="100%"
  height="600px"
/>

Mejores prácticas

Optimización del rendimiento

  • Caché de miniaturas: Almacena las miniaturas en caché del lado del cliente y refréscalas en un intervalo razonable en lugar de hacer polling continuo.
  • Carga diferida: Carga el contenido de video solo cuando sea necesario o cuando esté en el viewport.
  • Selección de calidad: Usa lowRes=true en el reproductor del iframe para cuadrículas y paneles.

Consideraciones de seguridad

  • Mantén las API keys del lado del servidor: Nunca expongas x-auth-apikey en código del navegador. Llama a createSharedLiveVideoStream desde tu backend y pasa al cliente solo las URLs resultantes.
  • Solo HTTPS: Usa siempre HTTPS para las llamadas a la API y para las URLs de transmisiones compartidas.
  • Limita el alcance de las claves: Restringe cada API key a los permisos mínimos necesarios.
  • Establece expiraciones para las transmisiones compartidas: Pasa expirationTimeSecs al crear transmisiones compartidas para que las URLs no sobrevivan al caso de uso.

Manejo de errores

class RhombusVideoError extends Error {
  constructor(message, code, cameraUuid) {
    super(message);
    this.name = 'RhombusVideoError';
    this.code = code;
    this.cameraUuid = cameraUuid;
  }
}

async function fetchWithRetry(url, init, retries = 3) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 10000);
    try {
      const response = await fetch(url, { ...init, signal: controller.signal });
      clearTimeout(timer);
      if (!response.ok) {
        throw new RhombusVideoError(`HTTP ${response.status}`, response.status, init?.cameraUuid);
      }
      return response;
    } catch (error) {
      clearTimeout(timer);
      if (attempt === retries) throw error;
      await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
    }
  }
}

Accesibilidad

<video
  controls
  aria-label={`Live feed from camera ${cameraName}`}
  poster={thumbnailUrl}
>
  <track kind="captions" src="captions.vtt" srcLang="en" label="English" />
  Your browser does not support the video tag.
</video>

Solución de problemas

La miniatura no carga
  • Verifica que cameraUuid y mediaRegion provengan de una respuesta reciente de getMinimalCameraStateListmediaRegion puede cambiar.
  • Comprueba que la API key tenga acceso a la ubicación de la cámara.
  • Confirma que la cámara esté en línea (campo connectionStatus en la respuesta de estado de la cámara).
Fallos de conexión de transmisión
  • Verifica la conectividad de red hacia *.rhombussystems.com.
  • Para reproducción HLS, confirma que el navegador soporte HLS de forma nativa o que hls.js esté cargado.
  • Revisa las reglas de firewall/proxy — los proxies corporativos suelen bloquear las solicitudes de segmentos de medios.
Calidad de video deficiente
  • Pasa lowRes=true para clientes con ancho de banda limitado.
  • Verifica el ancho de banda de subida en el sitio de la cámara.

Próximos pasos

Reproductor de video

Crea un reproductor de video personalizado con reproducción HLS y controles de línea de tiempo.

Respaldo de grabaciones

Descarga y archiva grabaciones de cámara en almacenamiento local.
Última modificación el 7 de mayo de 2026