Interface CameraRecordingDelegate

A CameraRecordingDelegate is responsible for handling recordings of a HomeKit Secure Video camera.

It is responsible for maintaining the prebuffer (see CameraRecordingOptions.prebufferLength, once recording was activated (see updateRecordingActive).

Before recording is considered enabled two things must happen:

A typical recording event scenario happens as follows:

  • The camera is in idle mode, maintaining the prebuffer (the duration of the prebuffer depends on the selected CameraRecordingConfiguration).
  • A recording event is triggered (e.g. motion or doorbell button press) and the camera signals it through the respective characteristics (e.g. Characteristic.MotionDetected or Characteristic.ProgrammableSwitchEvent). Further, the camera saves the content of the prebuffer and starts recording the video. The camera should continue to store the recording until it runs out of space. In any case the camera should preserve recordings which are nearest to the triggered event. A stored recording might be completely deleted if a stream request wasn't initiated for eight seconds.
  • A HomeKit Controller will open a new recording session to download the next recording. This results in a call to handleRecordingStreamRequest.
  • Once the recording event is finished the camera will reset the state accordingly (e.g. in the Service.MotionSensor or Service.Doorbell service). It will continue to send the remaining fragments of the currently ongoing recording stream request.
  • The camera will either reach the end of the recording (and signal this via RecordingPacket.isLast. Also see acknowledgeStream) or it will continue to stream til the HomeKit Controller closes the stream closeRecordingStream with reason HDSProtocolSpecificErrorReason.NORMAL.
  • The camera goes back into idle mode.
interface CameraRecordingDelegate {
    acknowledgeStream?(streamId: number): void;
    closeRecordingStream(streamId: number, reason: undefined | HDSProtocolSpecificErrorReason): void;
    handleRecordingStreamRequest(streamId: number): AsyncGenerator<RecordingPacket, any, any>;
    updateRecordingActive(active: boolean): void;
    updateRecordingConfiguration(configuration: undefined | CameraRecordingConfiguration): void;
}

Methods

  • This method is called to notify the delegate that a recording stream started via handleRecordingStreamRequest was closed.

    The method is also called if an ongoing recording stream is closed gracefully (using HDSProtocolSpecificErrorReason.NORMAL). In either case, the delegate should stop supplying further fragments to the recording stream. The AsyncGenerator function must return without yielding any further RecordingPackets. HAP-NodeJS won't send out any fragments from this point onwards.

    Parameters

    • streamId: number

      The streamId for which the close event was sent.

    • reason: undefined | HDSProtocolSpecificErrorReason

      The reason with which the stream was closed. NOTE: This method is also called in case of a closed connection. This is encoded by setting the reason to undefined.

    Returns void

  • This method is called to stream the next recording event. It is guaranteed that there is only ever one ongoing recording stream request at a time.

    When this method is called return the currently ongoing (or next in case of a potentially queued) recording via a AsyncGenerator. Every yield of the generator represents a complete recording packet. The first packet MUST always be the PacketDataType.MEDIA_INITIALIZATION packet. Any following packet will transport the actual mp4 fragments in PacketDataType.MEDIA_FRAGMENT packets, starting with the content of the prebuffer. Every PacketDataType.MEDIA_FRAGMENT starts with a key frame and must not be longer than the specified duration set via the CameraRecordingConfiguration.mediaContainerConfiguration.fragmentLength selected by the HomeKit Controller in updateRecordingConfiguration.

    NOTE: You MUST respect the value of Characteristic.RecordingAudioActive characteristic of the Service.CameraOperatingMode service. When the characteristic is set to false you MUST NOT include audio in the mp4 fragments. You can access the characteristic via the CameraController.recordingManagement.operatingModeService property.

    You might throw an error in this method if encountering a non-recoverable state. You may throw a HDSProtocolError to manually define the HDSProtocolSpecificErrorReason for the DATA_SEND CLOSE event.

    There are three ways an ongoing recording stream can be closed:

    • Closed by the Accessory: There are no further fragments to transmit. The delegate MUST signal this by setting RecordingPacket.isLast to true. Once the HomeKit Controller receives this last fragment it will call acknowledgeStream to notify the accessory about the successful transmission.
    • Closed by the HomeKit Controller (expectedly): After the event trigger has been reset, the accessory continues to stream fragments. At some point the HomeKit Controller will decide to shut down the stream by calling closeRecordingStream with a reason of HDSProtocolSpecificErrorReason.NORMAL.
    • Closed by the HomeKit Controller (unexpectedly): A HomeKit Controller might at any point decide to close a recording stream if it encounters erroneous state. This is signaled by a call to closeRecordingStream with the respective reason.

    Once a close of stream is signaled, the AsyncGenerator function must return gracefully.

    For more information about AsyncGenerators you might have a look at:

    NOTE: HAP-NodeJS guarantees that this method is only called with a valid selected CameraRecordingConfiguration.

    NOTE: Don't rely on the streamId for unique identification. Two DataStreamConnections might share the same identifier space.

    Parameters

    • streamId: number

      The streamId of the currently ongoing stream.

    Returns AsyncGenerator<RecordingPacket, any, any>

  • A call to this method notifies the CameraRecordingDelegate about a change to the CameraRecordingManagement.Active characteristic. This characteristic controls if the camera should react to recording events.

    If recording is disabled the camera can stop maintaining its prebuffer. If recording is enabled the camera should start recording into its prebuffer.

    A CameraRecordingDelegate should assume active to be false on startup. HAP-NodeJS will persist the state of the Active characteristic across reboots and will call updateRecordingActive accordingly on startup, if recording was previously enabled.

    NOTE: HAP-NodeJS cannot guarantee that a CameraRecordingConfiguration is present when recording is activated (e.g. the selected configuration might be erased due to changes in the supplied CameraRecordingOptions, but the camera is still active; or we can't otherwise influence the order which a HomeKit Controller might call those characteristics). However, HAP-NodeJS guarantees that if there is a valid CameraRecordingConfiguration, updateRecordingConfiguration is called before updateRecordingActive (when enabling) to avoid any unnecessary and potentially expensive reconfigurations.

    Parameters

    • active: boolean

      Specifies if recording is active or not.

    Returns void

  • A call to this method signals that the selected (by the HomeKit Controller) recording configuration of the camera has changed.

    On startup the delegate should assume configuration = undefined. HAP-NodeJS will persist the state of both across reboots and will call updateRecordingConfiguration on startup if there is a selected configuration present.

    NOTE: An update to the recording configuration might happen while there is still a running recording stream. The camera MUST continue to use the previous configuration for the currently running stream and only apply the updated configuration to the next stream.

    Parameters

    • configuration: undefined | CameraRecordingConfiguration

      The CameraRecordingConfiguration. Reconfigure your recording pipeline accordingly. The parameter might be undefined when the selected configuration became invalid. This typically ony happens e.g. due to a factory reset (when all pairings are removed). Disable the recording pipeline in such a case even if recording is still enabled for the camera.

    Returns void