Hướng dẫn sử dụng thiết bị camera cho iOS

Loại thiết bị Camera được triển khai bằng 2 đặc điểm: PushAvStreamTransportTrait, xử lý việc truyền luồng âm thanh và video bằng các giao thức dựa trên thao tác đẩy và WebRtcLiveViewTrait, cung cấp khả năng kiểm soát luồng phát trực tiếp và tính năng phản hồi bằng giọng nói.

Luôn kiểm tra xem thiết bị có hỗ trợ thuộc tính và lệnh hay không trước khi sử dụng bất kỳ tính năng nào hoặc cố gắng cập nhật thuộc tính. Hãy xem bài viết Điều khiển thiết bị trêniOS để biết thêm thông tin.

Loại thiết bị Home API Đặc điểm Ứng dụng mẫu Swift Trường hợp sử dụng

Máy ảnh

GoogleCameraDeviceType

home.matter.6006.types.0158

Thiết bị chụp ảnh tĩnh hoặc quay video. Camera có thể có tính năng phát trực tiếp, nói chuyện hai chiều hoặc phát hiện sự kiện.

Đặc điểm bắt buộc
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Camera

Bắt đầu phát trực tiếp

Để bắt đầu một sự kiện phát trực tiếp, hãy gửi chuỗi Giao thức mô tả phiên (SDP) đến phương thức startLiveView(offerSdp:) của đặc điểm WebRtcLiveViewTrait. Phương thức này sẽ trả về 3 giá trị:

  • SDP cho phiên.
  • Thời lượng phiên tính bằng giây.
  • Mã phiên, có thể dùng để kéo dài hoặc kết thúc phiên.
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
  }
}

Kéo dài thời gian phát trực tiếp

Sự kiện phát trực tiếp có thời lượng đặt sẵn và sẽ hết hạn sau thời lượng đó. Để kéo dài thời lượng của một luồng đang hoạt động, hãy đưa ra yêu cầu gia hạn bằng phương thức 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
  }
}

Bắt đầu và dừng TalkBack

Để bắt đầu TalkBack, hãy gọi phương thức startTalkback(mediaSessionId:optionalArgsProvider:) của đặc điểm WebRtcLiveViewTrait. Để dừng, hãy dùng biểu tượng 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)")
  }
}

Bật và tắt tính năng ghi âm

Để bật tính năng ghi hình của camera, hãy truyền TransportStatusEnum.Active đến phương thức setTransportStatus(transportStatus:optionalArgsProvider:) của đặc điểm PushAvStreamTransportTrait. Để tắt khả năng ghi, hãy truyền TransportStatusEnum.Inactive. Trong ví dụ sau, chúng ta sẽ gói các lệnh gọi này trong một lệnh gọi duy nhất sử dụng Boolean để bật/tắt khả năng ghi:

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

Việc bật hoặc tắt khả năng ghi hình của camera cũng giống như việc bật hoặc tắt video của camera. Khi video của camera đang bật, camera sẽ ghi hình (cho mục đích ghi lại các sự kiện và đoạn trích liên quan).

Khi tính năng ghi hình bị tắt (video camera ở trạng thái tắt):

  • Camera vẫn có thể hiển thị là đang trực tuyến theo connectivityState của loại thiết bị.
  • Bạn không thể truy cập vào phiên phát trực tiếp và camera cũng không phát hiện thấy sự kiện nào trên đám mây.

Kiểm tra xem bạn có bật tính năng ghi hình hay không

Để xác định xem camera có được bật tính năng ghi hình hay không, hãy kiểm tra xem có kết nối nào đang hoạt động hay không. Ví dụ sau đây xác định 2 hàm để thực hiện việc này:

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
}

Cài đặt âm thanh

Bạn có thể kiểm soát nhiều chế độ cài đặt âm thanh của camera thông qua Home API.

Bật hoặc tắt micrô

Để bật hoặc tắt micrô của thiết bị, hãy cập nhật thuộc tính microphoneMuted của đặc điểm CameraAvStreamManagementTrait bằng hàm setMicrophoneMuted tích hợp:

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

Bật hoặc tắt tính năng ghi âm

Để bật hoặc tắt tính năng ghi âm cho thiết bị, hãy cập nhật thuộc tính recordingMicrophoneMuted của đặc điểm CameraAvStreamManagementTrait bằng cách sử dụng hàm setRecordingMicrophoneMuted tích hợp:

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

Điều chỉnh âm lượng loa

Để điều chỉnh âm lượng loa cho thiết bị, hãy cập nhật thuộc tính speakerVolumeLevel của đặc điểm CameraAvStreamManagementTrait bằng hàm setSpeakerVolumeLevel tích hợp:

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

Chế độ cài đặt khác

Bạn có thể điều khiển nhiều chế độ cài đặt khác của camera thông qua Home API.

Thay đổi hướng của hình ảnh

Bạn có thể xoay hướng của hình ảnh (video) do camera ghi lại. Bạn chỉ có thể xoay video 180 độ.

Để thay đổi hướng hình ảnh của camera, hãy cập nhật thuộc tính imageRotation của đặc điểm CameraAvStreamManagementTrait bằng hàm setImageRotation tích hợp sẵn:

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

Bật hoặc tắt chế độ nhìn ban đêm

Để bật hoặc tắt chế độ nhìn ban đêm cho camera, hãy dùng TriStateAutoEnum để cập nhật thuộc tính nightVision của đặc điểm CameraAvStreamManagementTrait bằng hàm setNightVision tích hợp:

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

Thay đổi độ sáng của đèn LED trạng thái

Để thay đổi độ sáng của đèn LED trạng thái, hãy dùng ThreeLevelAutoEnum để cập nhật thuộc tính statusLightBrightness của đặc điểm CameraAvStreamManagementTrait bằng hàm setStatusLightBrightness tích hợp:

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

Thay đổi khung nhìn camera

Khung hiển thị camera giống với tính năng Thu phóng và cắt mà bạn có thể xem trong bài viết hỗ trợ Thu phóng và cải thiện video của camera Nest.

Khung hiển thị được xác định trong một ViewportStruct chứa 4 giá trị, được dùng làm toạ độ của khung hiển thị. Toạ độ được xác định như sau:

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

Việc xác định các giá trị cho ViewportStruct phụ thuộc vào giao diện người dùng và cách triển khai camera của ứng dụng. Ở cấp độ rất cơ bản, để đặt khung hiển thị của video từ camera, hãy cập nhật thuộc tính viewport của đặc điểm CameraAvStreamManagementTrait bằng ViewportStruct, sử dụng hàm setViewport tích hợp.

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

}

Tạo TransportOptionsStruct

Một số chế độ cài đặt yêu cầu sửa đổi các thuộc tính trong TransportOptionsStruct, sau đó được truyền vào các lựa chọn truyền tải của một kết nối truyền phát trực tiếp. Đối với Swift, bạn cần tạo cấu trúc này trước khi cập nhật bất kỳ thuộc tính nào.

Sử dụng hàm trợ giúp này để tạo cấu trúc nhằm sử dụng với các thay đổi về chế độ cài đặt sau:

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
}

Điều chỉnh độ nhạy đánh thức thiết bị

Độ nhạy đánh thức của thiết bị được dùng để tiết kiệm pin bằng cách giảm phạm vi mà thiết bị có thể cảm nhận được hoạt động và tăng thời gian đánh thức sau khi phát hiện thấy hoạt động đó.

Trong Home API, bạn có thể thiết lập chế độ này bằng cách dùng thuộc tính motionSensitivity của triggerOptions trong transportOptions của thiết bị. Các lựa chọn này được xác định trong đặc điểm PushAvStreamTransportTrait cho từng thiết bị.

Bạn chỉ có thể đặt độ nhạy đánh thức thành các giá trị sau:

  • 1 = Thấp
  • 5 = Trung bình
  • 10 = Cao

Quy trình cập nhật là tìm cấu hình truyền tải cho các luồng ghi đang hoạt động bằng lệnh findTransport, sau đó sửa đổi cấu hình bằng giá trị độ nhạy mới bằng lệnh modifyPushTransport.

Lệnh modifyPushTransport yêu cầu phải truyền toàn bộ TransportOptionsStruct, vì vậy, trước tiên, bạn phải sao chép các giá trị hiện có từ cấu hình hiện tại. Hãy xem phần Tạo TransportOptionsStruct cho một hàm trợ giúp để thực hiện việc này.

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

Điều chỉnh thời lượng tối đa của sự kiện

Thời lượng tối đa của sự kiện là khoảng thời gian camera sẽ ghi lại một đoạn video cho một sự kiện. Thông qua Home API, bạn có thể định cấu hình giá trị này cho từng thiết bị với cùng độ dài như thông qua Google Home app (GHA), theo khoảng thời gian tính bằng giây:

  • 10 giây
  • 15 giây
  • 30 giây
  • 60 giây (1 phút)
  • 120 giây (2 phút)
  • 180 giây (3 phút)

Trong Home API, bạn có thể thiết lập chế độ này bằng cách dùng thuộc tính motionTimeControl của triggerOptions trong transportOptions của thiết bị. Các lựa chọn này được xác định trong đặc điểm PushAvStreamTransportTrait cho từng thiết bị.

Quy trình cập nhật là tìm cấu hình truyền tải cho các luồng ghi đang hoạt động bằng lệnh findTransport, sau đó sửa đổi cấu hình bằng giá trị độ dài sự kiện mới bằng lệnh modifyPushTransport.

Lệnh modifyPushTransport yêu cầu phải truyền toàn bộ TransportOptionsStruct, vì vậy, trước tiên, bạn phải sao chép các giá trị hiện có từ cấu hình hiện tại. Hãy xem phần Tạo TransportOptionsStruct cho một hàm trợ giúp để thực hiện việc này.

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