camera-streaming

द्वारा facebook

स्ट्रीम, वीडियो फ्रेम, फोटो कैप्चर, रिज़ॉल्यूशन/फ्रेम दर कॉन्फ़िगरेशन

npx skills add https://github.com/facebook/meta-wearables-dat-ios --skill camera-streaming

Camera Streaming (iOS)

Guide for implementing camera streaming and photo capture with the DAT SDK.

For PCM audio delivered with this stream, use the audio-streaming skill.

Key concepts

  • DeviceSession: The connection to the glasses. Create and start it first; it can stay connected with no camera attached.
  • Camera: The capability you attach to a started DeviceSession with addCamera(config:). It owns the Stream.
  • Stream: camera.stream — start/stop streaming, capture photos, observe frames, state, and errors.
  • VideoFrame: Individual video frames. Use .makeUIImage() to render, or .sampleBuffer when you need the raw CMSampleBuffer (for example to write compressed frames to a file).
  • StreamConfiguration: Configure video codec, resolution, and frame rate.
  • PhotoData: Still image captured from glasses (.data plus .format).

Treat "start a session" and "start the preview" as two separate user-visible steps, as the CameraAccess sample does. That keeps the session reusable when the user stops and restarts the preview.

Creating a DeviceSession

import MWDATCamera
import MWDATCore

let wearables = Wearables.shared
let deviceSelector = AutoDeviceSelector(wearables: wearables)
// Or for a specific device: SpecificDeviceSelector(device: deviceId)
let deviceSession = try wearables.createSession(deviceSelector: deviceSelector)
try deviceSession.start()

// Wait for the device session to reach the started state
for await state in deviceSession.stateStream() {
    if state == .started { break }
}

Adding a Camera

Once the DeviceSession is started, add the Camera capability and stream through camera.stream.

Check camera permission first. checkPermissionStatus(.camera) is a silent query, while requestPermission(.camera) switches to the Meta AI app. CameraAccess only calls requestPermission after the user confirms the app switch, so the redirect is never a surprise:

if try await wearables.checkPermissionStatus(.camera) != .granted {
    // Confirm with the user, then:
    guard try await wearables.requestPermission(.camera) == .granted else { return }
}

Then attach the camera:

let config = StreamConfiguration(
    videoCodec: .raw,
    resolution: .medium,  // 504x896
    frameRate: 24
)

guard let camera = try deviceSession.addCamera(config: config) else {
    // DeviceSession must be in the started state before adding a camera
    return
}
let stream = camera.stream

Resolution options

ResolutionSize
.high720 x 1280
.medium504 x 896
.low360 x 640

Frame rate options

frameRate is a UInt. Valid values: 2, 7, 15, 24, 30 FPS.

Lower resolution and frame rate yield higher visual quality due to less Bluetooth compression.

Codec options

VideoCodec has two cases:

  • .raw — decoded frames. Render them directly with frame.makeUIImage().
  • .hvc1 — compressed HEVC frames. Use this when you want to write frames to a file in passthrough mode; you must decode frame.sampleBuffer yourself for on-screen preview, as CameraAccess does.

StreamConfiguration() with no arguments defaults to .raw, .medium, and 30 FPS.

Observing stream state

StreamState transitions: stopping → stopped → waitingForDevice → starting → streaming → paused

let stateToken = stream.statePublisher.listen { state in
    Task { @MainActor in
        switch state {
        case .streaming:
            // Stream is active, frames are flowing
        case .waitingForDevice:
            // Waiting for glasses to connect
        case .stopped:
            // Stream ended — release resources
        case .paused:
            // Temporarily suspended — keep connection, wait
        default:
            break
        }
    }
}

Receiving video frames

let frameToken = stream.videoFramePublisher.listen { frame in
    guard let image = frame.makeUIImage() else { return }
    Task { @MainActor in
        self.previewImage = image
    }
}

Observing stream errors

stream.errorPublisher emits typed StreamError values such as .permissionDenied, .deviceNotConnected, .hingesClosed, .thermalHot, .batteryLow, and .photoCaptureFailed. Surface error.localizedDescription rather than maintaining your own error-message map.

let errorToken = stream.errorPublisher.listen { error in
    Task { @MainActor in
        self.errorMessage = error.localizedDescription
    }
}

Keep listener tokens alive for as long as you need callbacks. MWDATCore provides ListenerTokenBag and AnyListenerToken.store(in:) for that:

let streamTokens = ListenerTokenBag()
stream.statePublisher.listen { _ in }.store(in: streamTokens)
stream.videoFramePublisher.listen { _ in }.store(in: streamTokens)
// On teardown:
streamTokens.clear()

Starting and stopping

// Start the stream capability
stream.start()

// Stop the camera — this cascades to its stream child and detaches the camera
// from the session, so a later addCamera() can register a new one.
camera.stop()

// Stop the parent device session when you're done with all capabilities.
// Teardown cascades parent -> child, not child -> parent.
deviceSession.stop()

In-stream photo capture

This is the lightweight capture path used while video is streaming. For standalone high-quality capture with resolution, quality, transfer progress, and its own lifecycle, use the camera-capture skill.

Capture a still photo while streaming. capturePhoto(format:) returns Bool: false means the capture was not started (for example another capture is already in flight), so no photoDataPublisher event will arrive.

// Listen for photo data
let photoToken = stream.photoDataPublisher.listen { photoData in
    let imageData = photoData.data  // photoData.format is .jpeg or .heic
    // Convert to UIImage or save
}

// Trigger capture
let started = stream.capturePhoto(format: .jpeg)
if !started {
    // Re-enable the shutter button and tell the user to try again
}

Bandwidth and quality

Resolution and frame rate are constrained by Bluetooth Classic bandwidth. The SDK automatically reduces quality when bandwidth is limited:

  1. First lowers resolution (e.g., High → Medium)
  2. Then reduces frame rate (e.g., 30 → 24), never below 15 FPS

Request lower settings for higher visual quality per frame.

Links

facebook की और Skills

gc-safe-coding
facebook
पूर्ण स्पष्टीकरण और तर्क के लिए, doc/GCSafeCoding.md देखें।
app-review-prep
facebook
Meta ऐप को App Review के लिए तैयार करें — वर्तमान स्थिति, लंबित आवश्यकताएँ, प्रदत्त विशेषाधिकार, और सबमिशन इतिहास जाँचें। ऐप सबमिट करने से पहले उपयोग करें…
api-health
facebook
मेटा ऐप के लिए API स्वास्थ्य की निगरानी करें — दर सीमाएँ, कॉल वॉल्यूम और API डिप्रिकेशन जाँचें। थ्रॉटलिंग का निदान करने, क्षमता की योजना बनाने या API संस्करण… के लिए तैयारी करने हेतु उपयोग करें।
debug-webhooks
facebook
मेटा ऐप के लिए वेबहुक समस्याओं का निवारण करें — सक्रिय सदस्यताओं की जाँच करें, गलत कॉन्फ़िगरेशन की पहचान करें, और डिलीवरी सत्यापित करने के लिए परीक्षण पेलोड भेजें। तब उपयोग करें जब…
api-integration
facebook
किसी डेवलपर को शुरू से Meta API इंटीग्रेशन सेट करने में मार्गदर्शन करें — सही APIs खोजें, सेटअप गाइड प्राप्त करें, प्रमाणीकरण आवश्यकताएँ,…
webhook-setup
facebook
Meta ऐप के लिए webhooks को end-to-end सेट करें — उपलब्ध topics खोजें, fields को subscribe करें, और test payload से verify करें। इसका उपयोग webhooks कॉन्फ़िगर करते समय करें…
test-ui
facebook
iwsdk CLI का उपयोग करके पोक उदाहरण के विरुद्ध Test UI सिस्टम (PanelUI, ScreenSpace) का परीक्षण करें।
flags
facebook
React रिलीज़ चैनलों में फीचर फ्लैग स्थितियों का निरीक्षण और तुलना करें। सभी चैनलों (www, www-modern, canary, next, experimental, rn वेरिएंट) में सभी फ्लैग देखें या --diff के साथ विशिष्ट चैनलों की तुलना करें। आउटपुट प्रारूपों में डिफ़ॉल्ट तालिका दृश्य, CSV निर्यात और क्लीनअप स्थिति समूहीकरण शामिल है। फ्लैग स्थितियाँ प्रतीकों द्वारा इंगित की जाती हैं: सक्षम (✅), अक्षम (❌), वेरिएंट परीक्षण (🧪), केवल प्रोफाइलिंग