Esempio n. 1
0
def encode(*args, to_base64=False):
    """Encode the given byte args in TLV format.

    :param args: Even-number, variable length positional arguments repeating a tag
        followed by a value.
    :type args: ``bytes``

    :param toBase64: Whether to encode the resuting TLV byte sequence to a base64 str.
    :type toBase64: ``bool``

    :return: The args in TLV format
    :rtype: ``bytes`` if ``toBase64`` is False and ``str`` otherwise.
    """
    if len(args) % 2 != 0:
        raise ValueError("Even number of args expected (%d given)" % len(args))

    pieces = []
    for x in range(0, len(args), 2):
        tag = args[x]
        data = args[x + 1]
        total_length = len(data)
        if len(data) <= 255:
            encoded = tag + struct.pack("B", total_length) + data
        else:
            encoded = b""
            for y in range(0, total_length // 255):
                encoded = encoded + tag + b"\xFF" + data[y * 255 : (y + 1) * 255]
            remaining = total_length % 255
            encoded = encoded + tag + struct.pack("B", remaining) + data[-remaining:]

        pieces.append(encoded)

    result = b"".join(pieces)

    return util.to_base64_str(result) if to_base64 else result
Esempio n. 2
0
    def set_endpoints(self, value):
        """Configure streaming endpoints.

        Called when iOS sets the SetupEndpoints ``Characteristic``. The endpoint
        information for the camera should be set as the current value of SetupEndpoints.

        :param value: The base64-encoded stream session details in TLV format.
        :param value: ``str``
        """
        objs = tlv.decode(value, from_base64=True)
        session_id = UUID(bytes=objs[SETUP_TYPES['SESSION_ID']])

        # Extract address info
        address_tlv = objs[SETUP_TYPES['ADDRESS']]
        address_info_objs = tlv.decode(address_tlv)
        is_ipv6 = struct.unpack(
            '?', address_info_objs[SETUP_ADDR_INFO['ADDRESS_VER']])[0]
        address = address_info_objs[SETUP_ADDR_INFO['ADDRESS']].decode('utf8')
        target_video_port = struct.unpack(
            '<H', address_info_objs[SETUP_ADDR_INFO['VIDEO_RTP_PORT']])[0]
        target_audio_port = struct.unpack(
            '<H', address_info_objs[SETUP_ADDR_INFO['AUDIO_RTP_PORT']])[0]

        # Video SRTP Params
        video_srtp_tlv = objs[SETUP_TYPES['VIDEO_SRTP_PARAM']]
        video_info_objs = tlv.decode(video_srtp_tlv)
        video_crypto_suite = video_info_objs[SETUP_SRTP_PARAM['CRYPTO']][0]
        video_master_key = video_info_objs[SETUP_SRTP_PARAM['MASTER_KEY']]
        video_master_salt = video_info_objs[SETUP_SRTP_PARAM['MASTER_SALT']]

        # Audio SRTP Params
        audio_srtp_tlv = objs[SETUP_TYPES['AUDIO_SRTP_PARAM']]
        audio_info_objs = tlv.decode(audio_srtp_tlv)
        audio_crypto_suite = audio_info_objs[SETUP_SRTP_PARAM['CRYPTO']][0]
        audio_master_key = audio_info_objs[SETUP_SRTP_PARAM['MASTER_KEY']]
        audio_master_salt = audio_info_objs[SETUP_SRTP_PARAM['MASTER_SALT']]

        logger.debug(
            'Received endpoint configuration:'
            '\nsession_id: %s\naddress: %s\nis_ipv6: %s'
            '\ntarget_video_port: %s\ntarget_audio_port: %s'
            '\nvideo_crypto_suite: %s\nvideo_srtp: %s'
            '\naudio_crypto_suite: %s\naudio_srtp: %s', session_id, address,
            is_ipv6, target_video_port, target_audio_port, video_crypto_suite,
            to_base64_str(video_master_key + video_master_salt),
            audio_crypto_suite,
            to_base64_str(audio_master_key + audio_master_salt))

        # Configure the SetupEndpoints response

        if self.has_srtp:
            video_srtp_tlv = tlv.encode(
                SETUP_SRTP_PARAM['CRYPTO'],
                SRTP_CRYPTO_SUITES['AES_CM_128_HMAC_SHA1_80'],
                SETUP_SRTP_PARAM['MASTER_KEY'], video_master_key,
                SETUP_SRTP_PARAM['MASTER_SALT'], video_master_salt)

            audio_srtp_tlv = tlv.encode(
                SETUP_SRTP_PARAM['CRYPTO'],
                SRTP_CRYPTO_SUITES['AES_CM_128_HMAC_SHA1_80'],
                SETUP_SRTP_PARAM['MASTER_KEY'], audio_master_key,
                SETUP_SRTP_PARAM['MASTER_SALT'], audio_master_salt)
        else:
            video_srtp_tlv = NO_SRTP
            audio_srtp_tlv = NO_SRTP

        video_ssrc = int.from_bytes(os.urandom(3), byteorder="big")
        audio_ssrc = int.from_bytes(os.urandom(3), byteorder="big")

        res_address_tlv = tlv.encode(SETUP_ADDR_INFO['ADDRESS_VER'],
                                     self.stream_address_isv6,
                                     SETUP_ADDR_INFO['ADDRESS'],
                                     self.stream_address.encode('utf-8'),
                                     SETUP_ADDR_INFO['VIDEO_RTP_PORT'],
                                     struct.pack('<H', target_video_port),
                                     SETUP_ADDR_INFO['AUDIO_RTP_PORT'],
                                     struct.pack('<H', target_audio_port))

        response_tlv = tlv.encode(SETUP_TYPES['SESSION_ID'],
                                  session_id.bytes,
                                  SETUP_TYPES['STATUS'],
                                  SETUP_STATUS['SUCCESS'],
                                  SETUP_TYPES['ADDRESS'],
                                  res_address_tlv,
                                  SETUP_TYPES['VIDEO_SRTP_PARAM'],
                                  video_srtp_tlv,
                                  SETUP_TYPES['AUDIO_SRTP_PARAM'],
                                  audio_srtp_tlv,
                                  SETUP_TYPES['VIDEO_SSRC'],
                                  struct.pack('<I', video_ssrc),
                                  SETUP_TYPES['AUDIO_SSRC'],
                                  struct.pack('<I', audio_ssrc),
                                  to_base64=True)

        self.sessions[session_id] = {
            'id': session_id,
            'address': address,
            'v_port': target_video_port,
            'v_srtp_key': to_base64_str(video_master_key + video_master_salt),
            'v_ssrc': video_ssrc,
            'a_port': target_audio_port,
            'a_srtp_key': to_base64_str(audio_master_key + audio_master_salt),
            'a_ssrc': audio_ssrc
        }

        self.get_service('CameraRTPStreamManagement')\
            .get_characteristic('SetupEndpoints')\
            .set_value(response_tlv)
Esempio n. 3
0
    def get_supported_audio_stream_config(audio_params):
        """Return a tlv representation of the supported audio stream configuration.

        iOS supports only AACELD and OPUS

        Expected audio parameters:
        - codecs
        - comfort_noise

        :param audio_params: Supported audio configurations
        :type audio_params: dict
        """
        has_supported_codec = False
        configs = b''
        for codec_param in audio_params['codecs']:
            param_type = codec_param['type']
            if param_type == 'OPUS':
                has_supported_codec = True
                codec = AUDIO_CODEC_TYPES['OPUS']
                bitrate = AUDIO_CODEC_PARAM_BIT_RATE_TYPES['VARIABLE']
            elif param_type == 'AAC-eld':
                has_supported_codec = True
                codec = AUDIO_CODEC_TYPES['AACELD']
                bitrate = AUDIO_CODEC_PARAM_BIT_RATE_TYPES['VARIABLE']
            else:
                logger.warning('Unsupported codec %s', param_type)
                continue

            param_samplerate = codec_param['samplerate']
            if param_samplerate == 8:
                samplerate = AUDIO_CODEC_PARAM_SAMPLE_RATE_TYPES['KHZ_8']
            elif param_samplerate == 16:
                samplerate = AUDIO_CODEC_PARAM_SAMPLE_RATE_TYPES['KHZ_16']
            elif param_samplerate == 24:
                samplerate = AUDIO_CODEC_PARAM_SAMPLE_RATE_TYPES['KHZ_24']
            else:
                logger.warning('Unsupported sample rate %s', param_samplerate)
                continue

            param_tlv = tlv.encode(AUDIO_CODEC_PARAM_TYPES['CHANNEL'], b'\x01',
                                   AUDIO_CODEC_PARAM_TYPES['BIT_RATE'],
                                   bitrate,
                                   AUDIO_CODEC_PARAM_TYPES['SAMPLE_RATE'],
                                   samplerate)
            config_tlv = tlv.encode(AUDIO_TYPES['CODEC'], codec,
                                    AUDIO_TYPES['CODEC_PARAM'], param_tlv)
            configs += tlv.encode(SUPPORTED_AUDIO_CODECS_TAG, config_tlv)

        if not has_supported_codec:
            logger.warning(
                'Client does not support any audio codec that iOS supports.')

            codec = AUDIO_CODEC_TYPES['OPUS']
            bitrate = AUDIO_CODEC_PARAM_BIT_RATE_TYPES['VARIABLE']
            samplerate = AUDIO_CODEC_PARAM_SAMPLE_RATE_TYPES['KHZ_24']

            param_tlv = tlv.encode(AUDIO_CODEC_PARAM_TYPES['CHANNEL'], b'\x01',
                                   AUDIO_CODEC_PARAM_TYPES['BIT_RATE'],
                                   bitrate,
                                   AUDIO_CODEC_PARAM_TYPES['SAMPLE_RATE'],
                                   samplerate)

            config_tlv = tlv.encode(AUDIO_TYPES['CODEC'], codec,
                                    AUDIO_TYPES['CODEC_PARAM'], param_tlv)

            configs = tlv.encode(SUPPORTED_AUDIO_CODECS_TAG, config_tlv)

        comfort_noise = byte_bool(audio_params.get('comfort_noise', False))
        audio_config = to_base64_str(
            configs + tlv.encode(SUPPORTED_COMFORT_NOISE_TAG, comfort_noise))
        return audio_config