as

Settings
Sign out
Notifications
Alexa
Amazon Appstore
Ring
AWS
Documentation
Support
Contact Us
My Cases
Ring

Live Video Streaming

Ring provides live video streaming through WebRTC connectivity using the WHEP (WebRTC HTTP Egress Protocol) standard. This enables low-latency video streaming from Ring devices to partner applications.

Overview

  • Video only: Live streams deliver video content only — audio is not available
  • WebRTC-based: Industry standard for real-time communication
  • WHEP protocol: Simplified WebRTC session establishment via HTTP
  • Receive only: Partners receive video from the device (recvonly) — no bidirectional media
  • Session duration limits: Battery-powered devices up to 30 seconds; line-powered up to 60 seconds

Starting a Live Video Session

Session Initiation

Create an SDP offer using WebRTC and send it to the WHEP endpoint:

POST https://api.amazonvision.com/v1/devices/{device_id}/media/streaming/whep/sessions
Authorization: Bearer <access_token>
Content-Type: application/sdp

[SDP Protocol Offer — generated by WebRTC peer connection]

Successful Response

HTTP/1.1 201 Created
Content-Type: application/sdp
Location: https://api.amazonvision.com/v1/devices/{device_id}/media/streaming/whep/sessions/{session_id}

[SDP Protocol Answer — contains WebRTC connection details]

Key response elements:

  • HTTP 201: Successful session creation
  • Location header: Session management URL for closing the stream
  • SDP Protocol Answer: Connection details for establishing the WebRTC session

Closing a Session

Properly close streaming sessions to optimize battery life:

DELETE https://api.amazonvision.com/v1/devices/{device_id}/media/streaming/whep/sessions/{session_id}
Authorization: Bearer <access_token>

Session Duration Limits

Device Power Type Maximum Stream Duration
Battery-powered 30 seconds
Line-powered 60 seconds

After the maximum duration, the session is automatically terminated. Partners should monitor duration and implement reconnection logic if extended viewing is needed.

Complete WebRTC Implementation

async function startLiveVideo(deviceId, accessToken, videoElement) {
  const whepEndpoint =
    `https://api.amazonvision.com/v1/devices/${deviceId}/media/streaming/whep/sessions`;

  const peerConnection = new RTCPeerConnection({
    iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
  });

  // Set up event handlers BEFORE createOffer
  peerConnection.ontrack = (event) => {
    if (event.streams && event.streams[0]) {
      videoElement.srcObject = event.streams[0];
    } else {
      videoElement.srcObject = new MediaStream([event.track]);
    }
    videoElement.play().catch((err) =>
      console.warn('Autoplay blocked:', err)
    );
  };

  peerConnection.onconnectionstatechange = () => {
    if (peerConnection.connectionState === 'failed') {
      console.error('WebRTC connection failed');
    }
  };

  // Video-only receive transceiver (no audio)
  peerConnection.addTransceiver('video', { direction: 'recvonly' });

  // Create and send SDP offer
  const offer = await peerConnection.createOffer();
  await peerConnection.setLocalDescription(offer);

  // Wait for ICE gathering to complete
  await new Promise((resolve) => {
    if (peerConnection.iceGatheringState === 'complete') {
      resolve();
    } else {
      peerConnection.addEventListener('icegatheringstatechange', () => {
        if (peerConnection.iceGatheringState === 'complete') resolve();
      });
    }
  });

  // Send to Ring WHEP endpoint
  const response = await fetch(whepEndpoint, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/sdp'
    },
    body: peerConnection.localDescription.sdp
  });

  if (response.status !== 201) {
    peerConnection.close();
    throw new Error(`WHEP session failed (${response.status})`);
  }

  // Set remote SDP answer
  await peerConnection.setRemoteDescription({
    type: 'answer',
    sdp: await response.text()
  });

  const sessionUrl = response.headers.get('Location');
  return { sessionUrl, peerConnection };
}

async function closeLiveVideo(sessionUrl, peerConnection, accessToken, videoElement) {
  if (videoElement) videoElement.srcObject = null;
  if (peerConnection) peerConnection.close();
  if (sessionUrl) {
    await fetch(sessionUrl, {
      method: 'DELETE',
      headers: { 'Authorization': `Bearer ${accessToken}` }
    });
  }
}

Error Handling

Common Error Responses

Device Offline (503)

{
  "errors": [{ "status": "503", "title": "Device Unavailable", "detail": "Device is currently offline" }]
}

Invalid Authentication (401)

{
  "errors": [{ "status": "401", "title": "Invalid Client" }]
}

Device Not Found (404)

{
  "errors": [{ "status": "404", "title": "Not Found" }]
}

Common Mistakes

"Incompatible send direction" SDP error

Cause: An audio transceiver was added. Live streams are video only.

Fix: Only add a video transceiver:

// CORRECT:
peerConnection.addTransceiver('video', { direction: 'recvonly' });

// WRONG — do NOT add audio:
// peerConnection.addTransceiver('audio', { direction: 'recvonly' });

No video appearing despite "connected" state

Cause: ontrack handler was attached after setRemoteDescription().

Fix: Always set up ontrack before calling createOffer() or setRemoteDescription().

Texture size [0x0] errors when processing video frames

Cause: Frame processing started before the video element had valid dimensions.

Fix:

function waitForVideoReady(videoElement) {
  return new Promise((resolve) => {
    const check = () => {
      if (videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
        resolve();
      } else {
        requestAnimationFrame(check);
      }
    };
    check();
  });
}

Best Practices

  1. Always close sessions: Use DELETE endpoint when streaming ends
  2. Set up event handlers first: Attach handlers before createOffer()
  3. Wait for ICE gathering: Only send SDP offer when iceGatheringState === 'complete'
  4. Minimize session duration: Close streams when not actively viewing
  5. Monitor connection state: Watch connectionState to detect failures
  6. Handle reconnection: Implement reconnection logic for extended viewing

Next: Media Clips →