Guia de dispositivos de câmera para iOS

O tipo de dispositivo de câmera é implementado usando duas características: PushAvStreamTransportTrait, que processa o transporte de stream de áudio e vídeo usando protocolos baseados em push, e WebRtcLiveViewTrait, que oferece a capacidade de controlar transmissões ao vivo e talkback.

Sempre verifique se um dispositivo é compatível com atributos e comandos antes de usar qualquer recurso ou tentar atualizar atributos. Consulte Controlar dispositivos no iOS para mais informações.

Tipo de dispositivo das APIs do Google Home Características App de exemplo do Swift Caso de uso

Câmera

GoogleCameraDeviceType

home.matter.6006.types.0158

Um dispositivo que captura imagens estáticas ou vídeo. As câmeras podem ter transmissões ao vivo acessíveis, comunicação bidirecional ou eventos de detecção.

Traços obrigatórios
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Câmera

Iniciar uma transmissão ao vivo

Para iniciar uma transmissão ao vivo, envie a string do Protocolo de Descrição de Sessão (SDP) para o método WebRtcLiveViewTrait da característica startLiveView(offerSdp:), que retorna três valores:

  • O SDP da sessão.
  • A duração da sessão em segundos.
  • O ID da sessão, que pode ser usado para estender ou encerrar a sessão.
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
  }
}

Estender uma transmissão ao vivo

As transmissões ao vivo têm uma duração predefinida após a qual expiram. Para aumentar a duração de um stream ativo, envie uma solicitação de extensão usando o método 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
  }
}

Iniciar e interromper o TalkBack

Para iniciar o talkback, chame o método startTalkback(mediaSessionId:optionalArgsProvider:) da característica WebRtcLiveViewTrait. Para parar, use 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)")
  }
}

Ativar e desativar a capacidade de gravação

Para ativar a capacidade de gravação da câmera, transmita TransportStatusEnum.Active ao método PushAvStreamTransportTrait da característica setTransportStatus(transportStatus:optionalArgsProvider:). Para desativar a capacidade de gravação, transmita TransportStatusEnum.Inactive. No exemplo a seguir, agrupamos essas chamadas em uma única chamada que usa um Boolean para ativar ou desativar a capacidade de gravação:

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
    }
  }
}

Ativar ou desativar a capacidade de gravação da câmera é o mesmo que ligar ou desligar o vídeo dela. Quando o vídeo de uma câmera está ativado, ela está gravando (para fins de eventos e clipes relacionados).

Quando a capacidade de gravação está desativada (o vídeo da câmera está desligado):

Verificar se a capacidade de gravação está ativada

Para determinar se a capacidade de gravação de uma câmera está ativada, verifique se há conexões ativas. O exemplo a seguir define duas funções para fazer isso:

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
}

Configurações de áudio

Várias configurações de áudio da câmera podem ser controladas pelas APIs Home.

Ativar ou desativar o microfone

Para ativar ou desativar o microfone do dispositivo, atualize o atributo microphoneMuted da característica CameraAvStreamManagementTrait usando a função setMicrophoneMuted integrada:

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

Ativar ou desativar a gravação de áudio

Para ativar ou desativar a gravação de áudio no dispositivo, atualize o atributo recordingMicrophoneMuted da característica CameraAvStreamManagementTrait usando a função setRecordingMicrophoneMuted integrada:

// 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
  }
}

Ajustar o volume do alto-falante

Para ajustar o volume do alto-falante do dispositivo, atualize o atributo speakerVolumeLevel da característica CameraAvStreamManagementTrait usando a função setSpeakerVolumeLevel integrada:

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

Outras configurações

Várias outras configurações de câmera podem ser controladas pelas APIs Home.

Mudar a orientação da imagem

A orientação da imagem da câmera (vídeo) pode ser girada. O vídeo só pode ser girado em 180 graus.

Para mudar a orientação da imagem da câmera, atualize o atributo imageRotation da característica CameraAvStreamManagementTrait usando a função setImageRotation integrada:

// 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
  }
}

Ativar ou desativar a visão noturna

Para ativar ou desativar a visão noturna da câmera, use TriStateAutoEnum para atualizar o atributo nightVision da característica CameraAvStreamManagementTrait usando a função setNightVision integrada:

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

Mudar o brilho do LED de status

Para mudar o brilho do LED de status, use ThreeLevelAutoEnum para atualizar o atributo statusLightBrightness da característica CameraAvStreamManagementTrait usando a função setStatusLightBrightness integrada:

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

Mudar a janela de visualização da câmera

A janela de visualização da câmera é a mesma do recurso de zoom e corte descrito no artigo de suporte Zoom e modo Enhance no vídeo da câmera Nest.

A janela de visualização é definida em um ViewportStruct que contém quatro valores, usados como coordenadas da janela. As coordenadas são definidas como:

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

A determinação dos valores para ViewportStruct depende da interface do app e da implementação da câmera. Em um nível muito básico, para definir a janela de visualização do vídeo da câmera, atualize o atributo viewport da característica CameraAvStreamManagementTrait com um ViewportStruct, usando a função setViewport integrada.

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
    }
  }

}

Gerar um TransportOptionsStruct

Algumas configurações exigem modificações nas propriedades de um TransportOptionsStruct, que é transmitido para as opções de transporte de uma conexão de streaming. Em Swift, essa struct precisa ser gerada antes de atualizar qualquer propriedade.

Use essa função auxiliar para gerar a struct a ser usada com as seguintes mudanças de configuração:

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
}

Ajustar a sensibilidade de ativação do dispositivo

A sensibilidade de ativação do dispositivo é usada para economizar bateria, diminuindo o alcance em que o dispositivo pode detectar atividade e aumentando o tempo para ativar após detectar essa atividade.

Nas APIs Home, isso pode ser definido usando a propriedade motionSensitivity do triggerOptions no transportOptions do dispositivo. Essas opções são definidas no traço PushAvStreamTransportTrait de cada dispositivo.

A sensibilidade de ativação só pode ser definida com os seguintes valores:

  • 1 = Baixa
  • 5 = Médio
  • 10 = Alto

Para atualizar, encontre a configuração de transporte para fluxos de gravação ativos usando o comando findTransport e modifique a configuração com o novo valor de sensibilidade usando o comando modifyPushTransport.

O comando modifyPushTransport exige que o TransportOptionsStruct completo seja transmitido. Portanto, primeiro copie os valores da configuração atual. Consulte Gerar um TransportOptionsStruct para uma função auxiliar que faça isso.

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
  }
}

Ajustar a duração máxima de eventos

A duração máxima do evento é o tempo que a câmera vai gravar um clipe para um evento. Com as APIs Home, isso pode ser configurado por dispositivo com os mesmos comprimentos que com o Google Home app (GHA), em intervalos de segundos:

  • 10 segundos
  • 15 segundos
  • 30 segundos
  • 60 segundos (1 minuto)
  • 120 segundos (2 minutos)
  • 180 segundos (3 minutos)

Nas APIs Home, isso pode ser definido usando a propriedade motionTimeControl do triggerOptions no transportOptions do dispositivo. Essas opções são definidas no traço PushAvStreamTransportTrait de cada dispositivo.

Para atualizar, encontre a configuração de transporte para fluxos de gravação ativos usando o comando findTransport e modifique a configuração com o novo valor de duração do evento usando o comando modifyPushTransport.

O comando modifyPushTransport exige que o TransportOptionsStruct completo seja transmitido. Portanto, primeiro copie os valores da configuração atual. Consulte Gerar um TransportOptionsStruct para uma função auxiliar que faça isso.

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
  }
}