Przewodnik po urządzeniach z kamerą na iOS

Typ urządzenia Camera jest implementowany za pomocą 2 cech:PushAvStreamTransportTrait, która obsługuje transport strumieni audio i wideo za pomocą protokołów opartych na wysyłaniu, oraz WebRtcLiveViewTrait, która umożliwia sterowanie transmisjami na żywo i funkcją talkback.

Przed użyciem jakichkolwiek funkcji lub próbą zaktualizowania atrybutów zawsze sprawdzaj, czy urządzenie obsługuje atrybuty i polecenia. Więcej informacji znajdziesz w artykule Sterowanie urządzeniami na iOS.

Typ urządzenia interfejsów Home API Cechy Przykładowa aplikacja w Swift Przypadek użycia

Aparat

GoogleCameraDeviceType

home.matter.6006.types.0158

Urządzenie, które robi zdjęcia lub nagrywa filmy. Kamery mogą oferować dostępne transmisje na żywo, rozmowę dwukierunkową lub wykrywanie zdarzeń.

Wymagane cechy
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Aparat

Rozpoczynanie transmisji na żywo

Aby rozpocząć transmisję na żywo, wyślij ciąg protokołu SDP (Session Description Protocol) do metody startLiveView(offerSdp:) cechy WebRtcLiveViewTrait, która zwraca 3 wartości:

  • SDP sesji.
  • Czas trwania sesji w sekundach.
  • Identyfikator sesji, który może służyć do przedłużenia lub zakończenia sesji.
public func sendOffer(offerSdp: String) async throws
-> (answerSdp: String, mediaSessionId: String, liveViewDuration: TimeInterval)
{
  do {
    // Sending StartLiveView command
    let response = try await liveViewTrait.startLiveView(
      offerSdp: offerSdp
    )
    // Received StartLiveView response
    return (
      answerSdp: response.answerSdp,
      mediaSessionId: response.mediaSessionId,
      liveViewDuration: TimeInterval(response.liveSessionDurationSeconds)
    )
  } catch {
    // Failed to send StartLiveView command
    throw error
  }
}

Przedłużanie transmisji na żywo

Transmisje na żywo mają z góry określony czas trwania, po którym wygasają. Aby wydłużyć czas trwania aktywnego strumienia, wyślij prośbę o przedłużenie za pomocą metody extendLiveView(mediaSessionId:optionalArgsProvider:):

public func extendLiveView(mediaSessionId: String) async throws {
  do {
    // Extending live view
    let extendedDuration = try await liveViewTrait.extendLiveView(mediaSessionId: mediaSessionId)
  } catch {
    // Failed to extend live view
    throw error
  }
}

Włączanie i wyłączanie TalkBack

Aby uruchomić TalkBack, wywołaj metodę startTalkback(mediaSessionId:optionalArgsProvider:) cechy WebRtcLiveViewTrait. Aby zatrzymać, kliknij stopTalkback(mediaSessionId:).

public func toggleTwoWayTalk(isOn: Bool, mediaSessionId: String) async throws {
  do {
    if isOn {
      try await liveViewTrait.startTalkback(mediaSessionId: mediaSessionId)
    } else {
      try await liveViewTrait.stopTalkback(mediaSessionId: mediaSessionId)
    }
  } catch {
    throw HomeError.commandFailed("Failed to toggle twoWayTalk: \(error)")
  }
}

Włączanie i wyłączanie możliwości nagrywania

Aby włączyć funkcję nagrywania kamery, przekaż wartość TransportStatusEnum.Active do metody PushAvStreamTransportTrait cechy setTransportStatus(transportStatus:optionalArgsProvider:). Aby wyłączyć możliwość nagrywania, przekaż wartość TransportStatusEnum.Inactive. W poniższym przykładzie umieszczamy te wywołania w jednym wywołaniu, które używa Boolean, aby włączać i wyłączać możliwość nagrywania:

public func toggleIsRecording(isOn: Bool) {
  self.uiState = .loading

  guard let pushAvStreamTransportTrait else {
    // PushAvStreamTransportTrait not found.
    return
  }
  Task {
    do {
      try await pushAvStreamTransportTrait.setTransportStatus(
        transportStatus: isOn ? .active : .inactive)
      if isOn {
        do {
          self.player = try self.createWebRtcPlayer()
        } catch {
          // Failed to initialize WebRtcPlayer
          self.uiState = .disconnected
          return
        }
        await self.player?.initialize()
        self.uiState = .live
      } else {
        self.player = nil
        self.uiState = .off
      }
    } catch {
      // Failed to toggle onOff
    }
  }
}

Włączenie lub wyłączenie nagrywania przez kamerę jest równoznaczne z włączeniem lub wyłączeniem wideo z kamery. Gdy wideo z kamery jest włączone, kamera nagrywa (na potrzeby zdarzeń i powiązanych klipów).

Gdy funkcja nagrywania jest wyłączona (wideo z kamery jest wyłączone):

  • Kamera może nadal być widoczna jako online zgodnie z connectivityStatetypu urządzenia.
  • Nie można uzyskać dostępu do transmisji na żywo, a kamera nie wykrywa żadnych zdarzeń w chmurze.

Sprawdzanie, czy funkcja nagrywania jest włączona

Aby sprawdzić, czy funkcja nagrywania kamery jest włączona, sprawdź, czy są aktywne jakiekolwiek połączenia. Poniższy przykład definiuje 2 funkcje, które to umożliwiają:

public func isDeviceRecording() -> Bool {
  guard let pushAvStreamTransportTrait else {
    // PushAvStreamTransportTrait not found.
    return false
  }
  guard
    let hasActiveConnection =
      pushAvStreamTransportTrait
      .attributes
      .currentConnections?
      .contains(where: { $0.transportStatus == .active })
  else {
    return false
  }
  return hasActiveConnection
}

Ustawienia dźwięku

Różnymi ustawieniami dźwięku kamery można sterować za pomocą interfejsów API Home.

Włączanie i wyłączanie mikrofonu

Aby włączyć lub wyłączyć mikrofon urządzenia, zaktualizuj atrybut microphoneMuted cechy CameraAvStreamManagementTrait za pomocą wbudowanej funkcji setMicrophoneMuted:

// Turn the device's microphone on or off
func setMicrophone(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

Włączanie i wyłączanie nagrywania dźwięku

Aby włączyć lub wyłączyć nagrywanie dźwięku na urządzeniu, zaktualizuj atrybut recordingMicrophoneMuted cechy CameraAvStreamManagementTrait za pomocą wbudowanej funkcji setRecordingMicrophoneMuted:

// Turn audio recording on or off for the device
func setAudioRecording(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setRecordingMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

Dostosowywanie głośności głośnika

Aby dostosować głośność głośnika urządzenia, zaktualizuj atrybutspeakerVolumeLevel cechy CameraAvStreamManagementTrait za pomocą wbudowanej funkcjisetSpeakerVolumeLevel:

// Adjust the camera speaker volume
func setSpeakerVolume(to value: UInt8) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setSpeakerVolumeLevel(value)
    }
  } catch {
    // Error
  }
}

Inne ustawienia

Za pomocą interfejsów API Home można sterować różnymi innymi ustawieniami kamery.

Zmienianie orientacji obrazu

Orientację obrazu z kamery (filmu) można obracać. Film można obrócić tylko o 180 stopni.

Aby zmienić orientację obrazu z kamery, zaktualizuj atrybut imageRotation cechy CameraAvStreamManagementTrait za pomocą wbudowanej funkcji setImageRotation:

// Change the camera's image orientation
// Value must be 0 or 180
func setImageRotation(to value: UInt16) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setImageRotation(value)
    }
  } catch {
    // Error
  }
}

Włączanie i wyłączanie widzenia w nocy

Aby włączyć lub wyłączyć widzenie w nocy w przypadku kamery, użyj TriStateAutoEnum, aby zaktualizować atrybut nightVision cechy CameraAvStreamManagementTrait za pomocą wbudowanej funkcji setNightVision:

// Turn night vision on or off
func setNightVision(
  to value: Google.CameraAvStreamManagementTrait.TriStateAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setNightVision(value)
    }
  } catch {
    // Error
  }
}

Zmiana jasności diody LED stanu

Aby zmienić jasność diody LED stanu, użyj ThreeLevelAutoEnum do zaktualizowania atrybutu statusLightBrightness cechy CameraAvStreamManagementTrait za pomocą wbudowanej funkcji setStatusLightBrightness:

// Set the LED brightness
func setStatusLightBrightness(
  to value: Google.CameraAvStreamManagementTrait.ThreeLevelAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setStatusLightBrightness(value)
    }
  } catch {
    // Error
  }
}

Zmiana widocznego obszaru kamery

Widok z kamery jest taki sam jak w przypadku funkcji powiększania i kadrowania opisanej w artykule pomocy Powiększanie i ulepszanie obrazu z kamery Nest.

Obszar wyświetlania jest zdefiniowany w ViewportStruct, który zawiera 4 wartości używane jako współrzędne obszaru wyświetlania. Współrzędne są zdefiniowane w ten sposób:

(x1,y1) -- (x2,y1)
   |          |
(x1,y2) -- (x2,y2)

Określanie wartości parametru ViewportStruct zależy od interfejsu aplikacji i implementacji kamery. Aby ustawić pole widzenia kamery, zaktualizuj atrybut viewport cechy CameraAvStreamManagementTrait za pomocą funkcji ViewportStruct, używając wbudowanej funkcji setViewport.

func setCrop(x1: UInt16, y1: UInt16, x2: UInt16, y2: UInt16) {

  let viewport = Google.CameraAvStreamManagementTrait.ViewportStruct(
    x1: x1,
    y1: y1,
    x2: x2,
    y2: y2
  )

  Task {
    do {
      try await cameraAvStreamManagementTrait.update {
        $0.setViewport(viewport)
      }
    } catch {
      // Error
    }
  }

}

Wygeneruj TransportOptionsStruct

Niektóre ustawienia wymagają modyfikacji właściwości w TransportOptionsStruct, które są następnie przekazywane do opcji transportu połączenia strumieniowego. W przypadku języka Swift tę strukturę należy wygenerować przed zaktualizowaniem jakichkolwiek właściwości.

Użyj tej funkcji pomocniczej, aby wygenerować strukturę do wykorzystania przy wprowadzaniu tych zmian ustawień:

func getTransportOptions(
  transportOptions: Google.PushAvStreamTransportTrait.TransportOptionsStruct,
  wakeUpSensitivity: UInt8?,
  maxEventLength: UInt32?
) async throws
  -> Google.PushAvStreamTransportTrait.TransportOptionsStruct
{

  var newMotionTimeControl:
    Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct? = nil
  if let maxEventLength {
    guard let motionTimeControl = transportOptions.triggerOptions.motionTimeControl else {
      throw HomeError.failedPrecondition(
        // Error - cannot update max event length without motion time control
    }
    newMotionTimeControl =
      Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct(
        initialDuration: motionTimeControl.initialDuration,
        augmentationDuration: motionTimeControl.augmentationDuration,
        maxDuration: maxEventLength,
        blindDuration: motionTimeControl.blindDuration
      )
  }

  return Google.PushAvStreamTransportTrait.TransportOptionsStruct(
    streamUsage: .recording,
    videoStreamID: nil,
    audioStreamID: nil,
    tlsEndpointID: transportOptions.tlsEndpointID,
    url: transportOptions.url,
    triggerOptions: Google.PushAvStreamTransportTrait.TransportTriggerOptionsStruct(
      triggerType: .motion,
      motionZones: nil,
      motionSensitivity: wakeUpSensitivity,
      motionTimeControl: newMotionTimeControl,
      maxPreRollLen: nil
    ),
    ingestMethod: .cmafIngest,
    containerOptions: Google.PushAvStreamTransportTrait.ContainerOptionsStruct(
      containerType: .cmaf,
      cmafContainerOptions: nil
    ),
    expiryTime: nil
  )
}

private func getRecordingConnection() async throws
  -> Google.PushAvStreamTransportTrait.TransportConfigurationStruct?
{
  guard let pushAvStreamTransportTrait else {
    // Error - PushAvStreamTransport trait not available
    return nil
  }

  let connections = try await pushAvStreamTransportTrait.findTransport().transportConfigurations

  for connection in connections {
    guard let transportOptions = connection.transportOptions,
      transportOptions.streamUsage == .recording
    else {
      continue
    }

    return connection
  }

  return nil
}

Dostosowywanie czułości wybudzania urządzenia

Czułość wybudzania urządzenia służy do oszczędzania baterii poprzez zmniejszenie zakresu, w którym urządzenie może wykrywać aktywność, i wydłużenie czasu wybudzania po wykryciu tej aktywności.

W interfejsach Home API można to ustawić za pomocą właściwości motionSensitivity elementu triggerOptionstransportOptions urządzenia. Te opcje są zdefiniowane w PushAvStreamTransportTrait dla każdego urządzenia.

Czułość wybudzania można ustawić tylko na te wartości:

  • 1 = Niski
  • 5 = Średni
  • 10 = Wysoki

Proces aktualizacji polega na znalezieniu konfiguracji transportu dla aktywnych strumieni nagrywania za pomocą polecenia findTransport, a następnie zmodyfikowaniu konfiguracji za pomocą nowej wartości czułości za pomocą polecenia modifyPushTransport.

Polecenie modifyPushTransport wymaga przekazania pełnego TransportOptionsStruct, więc najpierw musisz skopiować istniejące wartości z bieżącej konfiguracji. Aby to zrobić, zapoznaj się z sekcją Generowanie TransportOptionsStruct dla funkcji pomocniczej.

func setWakeUpSensitivity(to value: UInt8) async {
  do {
    let connection = try await getRecordingConnection()
    guard let connection,
      let transportOptions = connection.transportOptions
    else {
      // Error - Transport options not available
      return
    }

    guard transportOptions.triggerOptions.motionSensitivity != nil else {
      // Error - Motion sensitivity not available to be updated for this device
      return
    }

    try await pushAvStreamTransportTrait.modifyPushTransport(
      connectionID: connection.connectionID,
      transportOptions: self.getTransportOptions(
        transportOptions: transportOptions,
        wakeUpSensitivity: value,
        maxEventLength: nil
      )
    )

  } catch {
    // Error
  }
}

Dostosowywanie maksymalnego czasu trwania zdarzenia

Maksymalna długość zdarzenia to czas, przez jaki kamera będzie nagrywać klip w przypadku zdarzenia. Za pomocą interfejsów Home API można skonfigurować dla każdego urządzenia te same długości, co w przypadku Google Home app (GHA), w odstępach sekund:

  • 10 sekund
  • 15 sekund
  • 30 sekund
  • 60 sekund (1 minuta)
  • 120 sekund (2 minuty)
  • 180 sekund (3 minuty)

W interfejsach Home API można to ustawić za pomocą właściwości motionTimeControl elementu triggerOptionstransportOptions urządzenia. Te opcje są zdefiniowane w PushAvStreamTransportTrait dla każdego urządzenia.

Aby zaktualizować konfigurację, znajdź konfigurację transportu dla aktywnych strumieni nagrywania za pomocą polecenia findTransport, a następnie zmodyfikuj konfigurację, podając nową wartość długości zdarzenia, za pomocą polecenia modifyPushTransport.

Polecenie modifyPushTransport wymaga przekazania pełnego TransportOptionsStruct, więc najpierw musisz skopiować istniejące wartości z bieżącej konfiguracji. Aby to zrobić, zapoznaj się z sekcją Generowanie TransportOptionsStruct dla funkcji pomocniczej.

func setMaxEventLength(to value: UInt32) async {
  do {
    let connection = try await getRecordingConnection()
    guard let connection,
      let transportOptions = connection.transportOptions
    else {
      // Error - Transport options not available
      return
    }

    guard transportOptions.triggerOptions.motionTimeControl != nil else {
      // Error - Motion time control not available to be updated for this device
      return
    }

    try await pushAvStreamTransportTrait.modifyPushTransport(
      connectionID: connection.connectionID,
      transportOptions: self.getTransportOptions(
        transportOptions: transportOptions,
        wakeUpSensitivity: nil,
        maxEventLength: value
      )
    )

  } catch {
    // Error
  }
}