IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Series ESP-WebRTC: Tích Hợp Amazon KVS — Stream Video ESP32 Lên AWS Cloud

Các bài trước dùng signaling server tự deploy. Amazon Kinesis Video Streams (KVS) là giải pháp managed — AWS xử lý toàn bộ signaling, STUN/TURN, lưu trữ video và scaling. Phù hợp khi cần production-grade camera system.

KVS Là Gì?

Amazon Kinesis Video Streams là dịch vụ video streaming managed của AWS với hai mode:

ModeMô TảDùng Khi
KVS Producer SDKIngest video → lưu S3, phân tích AICamera giám sát, phân tích hành vi
KVS WebRTCP2P real-time qua AWS signalingVideo call, live monitoring

Series này dùng KVS WebRTC — không lưu trữ, chỉ live stream với latency thấp.

Chi Phí

KVS WebRTC tính tiền:

  • $0.05 per signaling channel per month
  • $0.05 per 1000 TURN minutes

Ước tính: camera stream 8 giờ/ngày, 30 ngày = ~14.000 TURN minutes/tháng = $0.70/tháng. Rất rẻ cho production.

Thiết Lập AWS

Tạo KVS Signaling Channel

# Cài AWS CLI
pip install awscli
aws configure  # Nhập Access Key, Secret Key, Region (ap-southeast-1 cho VN)

# Tạo signaling channel
aws kinesisvideo create-signaling-channel \
    --channel-name "esp32-camera" \
    --channel-type SINGLE_MASTER \
    --region ap-southeast-1

Kết quả trả về ChannelARN — lưu lại để dùng trong config ESP32.

Tạo IAM User Cho ESP32

ESP32 cần credentials để kết nối KVS. Tạo IAM user với quyền tối thiểu:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "kinesisvideo:ConnectAsMaster",
        "kinesisvideo:GetSignalingChannelEndpoint",
        "kinesisvideo:GetIceServerConfig"
      ],
      "Resource": "arn:aws:kinesisvideo:ap-southeast-1:*:channel/esp32-camera/*"
    }
  ]
}

Tạo Access Key cho IAM user này — dùng trong ESP32.

Cấu Hình ESP32

#include "esp_webrtc.h"
#include "esp_webrtc_kvs.h"

// AWS credentials — lưu trong NVS hoặc menuconfig, KHÔNG hardcode
#define AWS_REGION          "ap-southeast-1"
#define AWS_ACCESS_KEY_ID   CONFIG_AWS_ACCESS_KEY_ID
#define AWS_SECRET_KEY      CONFIG_AWS_SECRET_KEY
#define KVS_CHANNEL_NAME    "esp32-camera"

void app_main(void) {
    wifi_init_sta(CONFIG_WIFI_SSID, CONFIG_WIFI_PASS);
    wifi_wait_connected();

    // Camera + capture pipeline
    esp_capture_handle_t cap;
    init_capture(&cap);

    // WebRTC với KVS signaling
    esp_webrtc_cfg_t cfg = {
        .signaling = {
            .type                = ESP_WEBRTC_SIGNALING_KVS,
            .kvs_channel_name    = KVS_CHANNEL_NAME,
            .kvs_region          = AWS_REGION,
            .kvs_access_key      = AWS_ACCESS_KEY_ID,
            .kvs_secret_key      = AWS_SECRET_KEY,
            .kvs_role            = ESP_WEBRTC_KVS_ROLE_MASTER,  // ESP32 là MASTER
        },
        .video_codec = ESP_WEBRTC_VIDEO_CODEC_H264,
        .audio_codec = ESP_WEBRTC_AUDIO_CODEC_OPUS,
    };

    esp_webrtc_handle_t webrtc;
    esp_webrtc_init(&cfg, &webrtc);
    esp_webrtc_set_av_source(webrtc, cap);
    esp_webrtc_start(webrtc);

    ESP_LOGI("kvs", "KVS WebRTC Master started, channel: %s", KVS_CHANNEL_NAME);
}

📷 [Hình minh hoạ: Architecture diagram: ESP32 → KVS WebRTC → AWS → Browser Viewer]

Lưu Trữ Credentials An Toàn

Không hard-code AWS credentials trong firmware. Dùng ESP-IDF NVS (Non-Volatile Storage):

#include "nvs_flash.h"
#include "nvs.h"

void store_credentials(const char *access_key, const char *secret_key) {
    nvs_handle_t handle;
    nvs_open("aws_creds", NVS_READWRITE, &handle);
    nvs_set_str(handle, "access_key", access_key);
    nvs_set_str(handle, "secret_key", secret_key);
    nvs_commit(handle);
    nvs_close(handle);
}

void load_credentials(char *access_key, size_t ak_len,
                      char *secret_key, size_t sk_len) {
    nvs_handle_t handle;
    nvs_open("aws_creds", NVS_READONLY, &handle);
    nvs_get_str(handle, "access_key", access_key, &ak_len);
    nvs_get_str(handle, "secret_key", secret_key, &sk_len);
    nvs_close(handle);
}

Provision credentials một lần qua BLE hoặc trang web setup — không qua firmware flash.

Viewer: Xem Stream Từ Browser

KVS WebRTC cần viewer kết nối từ phía browser. AWS cung cấp JavaScript SDK:

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/amazon-kinesis-video-streams-webrtc/dist/kvs-webrtc.min.js"></script>
</head>
<body>
<video id="remoteVideo" autoplay playsinline style="width:100%"></video>

<script>
const AWS_REGION = 'ap-southeast-1';
const CHANNEL_NAME = 'esp32-camera';
// Credentials cho viewer (dùng cognito hoặc temporary creds)
const ACCESS_KEY = 'AKIA...';
const SECRET_KEY = '...';

async function startViewer() {
  const kinesisVideoClient = new AWS.KinesisVideo({
    region: AWS_REGION,
    credentials: { accessKeyId: ACCESS_KEY, secretAccessKey: SECRET_KEY }
  });

  // Lấy endpoint
  const { ResourceEndpointList } = await kinesisVideoClient.getSignalingChannelEndpoint({
    ChannelARN: CHANNEL_ARN,
    SingleMasterChannelEndpointConfiguration: {
      Protocols: ['WSS', 'HTTPS'],
      Role: 'VIEWER'
    }
  }).promise();

  // Khởi tạo SignalingClient
  const signalingClient = new KVSWebRTC.SignalingClient({
    role: KVSWebRTC.Role.VIEWER,
    // ... (endpoint từ trên)
  });

  const pc = new RTCPeerConnection({ iceServers: [] });
  pc.ontrack = e => document.getElementById('remoteVideo').srcObject = e.streams[0];

  signalingClient.on('sdpAnswer', async answer => {
    await pc.setRemoteDescription(answer);
  });
  signalingClient.on('iceCandidate', candidate => {
    pc.addIceCandidate(candidate);
  });

  signalingClient.open();
  const offer = await pc.createOffer({ offerToReceiveVideo: true, offerToReceiveAudio: true });
  await pc.setLocalDescription(offer);
  signalingClient.sendSdpOffer(offer);
}

startViewer();
</script>
</body>
</html>

KVS vs MediaMTX: Khi Nào Dùng Cái Nào?

Tiêu ChíKVS (AWS)MediaMTX (Self-hosted)
Chi phí~$1-5/thángChi phí VPS (~$5/tháng)
SetupAWS console + IAMDocker 1 lệnh
ScalingTự động (AWS)Tự quản lý
Reliability99.9% SLATùy bạn
Latency~300-500ms~100-200ms
Lưu trữ videoCó (thêm Producer SDK)Không
PrivacyVideo qua AWSTự control

Kết luận: MediaMTX cho prototype và dự án cá nhân. KVS cho sản phẩm cần reliability cao, nhiều camera, hoặc cần lưu trữ video.

Lưu Ý Security

  1. Viewer credentials: Không dùng AWS root credentials cho viewer. Tạo Cognito Identity Pool để issue temporary credentials — người dùng không thấy AWS keys.
  1. Channel access control: KVS Signaling Channel mặc định private — chỉ IAM user/role được phép mới kết nối được.
  1. Rotation credentials: Đặt rotation policy cho IAM access key — 90 ngày đổi một lần.

Series ESP-WebRTC đến đây kết thúc 13 bài kỹ thuật. Xem Tổng Quan Series ESP-WebRTC để xem toàn bộ lộ trình và liên kết tất cả bài.