/* mod_skinny.h should be loaded first */
#include "mod_skinny.h"
+/*****************************************************************************/
+/* SKINNY TYPES */
+/*****************************************************************************/
+typedef enum {
+ SKINNY_CODEC_NONE = 0,
+ SKINNY_CODEC_NONSTANDARD = 1,
+ SKINNY_CODEC_ALAW_64K = 2,
+ SKINNY_CODEC_ALAW_56K = 3,
+ SKINNY_CODEC_ULAW_64K = 4,
+ SKINNY_CODEC_ULAW_56K = 5,
+ SKINNY_CODEC_G722_64K = 6,
+ SKINNY_CODEC_G722_56K = 7,
+ SKINNY_CODEC_G722_48K = 8,
+ SKINNY_CODEC_G723_1 = 9,
+ SKINNY_CODEC_G728 = 10,
+ SKINNY_CODEC_G729 = 11,
+ SKINNY_CODEC_G729A = 12,
+ SKINNY_CODEC_IS11172 = 13,
+ SKINNY_CODEC_IS13818 = 14,
+ SKINNY_CODEC_G729B = 15,
+ SKINNY_CODEC_G729AB = 16,
+ SKINNY_CODEC_GSM_FULL = 18,
+ SKINNY_CODEC_GSM_HALF = 19,
+ SKINNY_CODEC_GSM_EFULL = 20,
+ SKINNY_CODEC_WIDEBAND_256K = 25,
+ SKINNY_CODEC_DATA_64K = 32,
+ SKINNY_CODEC_DATA_56K = 33,
+ SKINNY_CODEC_G722_1_32K = 40,
+ SKINNY_CODEC_G722_1_24K = 41,
+ SKINNY_CODEC_GSM = 80,
+ SKINNY_CODEC_ACTIVEVOICE = 81,
+ SKINNY_CODEC_G726_32K = 82,
+ SKINNY_CODEC_G726_24K = 83,
+ SKINNY_CODEC_G726_16K = 84,
+ SKINNY_CODEC_G729B_BIS = 85,
+ SKINNY_CODEC_G729B_LOW = 86,
+ SKINNY_CODEC_H261 = 100,
+ SKINNY_CODEC_H263 = 101,
+ SKINNY_CODEC_VIDEO = 102,
+ SKINNY_CODEC_H264 = 103,
+ SKINNY_CODEC_T120 = 105,
+ SKINNY_CODEC_H224 = 106,
+ SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257
+} skinny_codecs;
+
+char* skinny_codec2string(skinny_codecs skinnycodec);
+
/*****************************************************************************/
/* SKINNY MESSAGE DATA */
/*****************************************************************************/
#define DEVICE_UPDATECAPABILITIES 0x0030
+#define MAX_CUSTOM_PICTURES 6
+#define MAX_LAYOUT_WITH_SAME_SERVICE 5
+#define MAX_SERVICE_TYPE 4
+#define SKINNY_MAX_CAPABILITIES 18 /*!< max capabilities allowed in Cap response message */
+#define SKINNY_MAX_VIDEO_CAPABILITIES 10
+#define SKINNY_MAX_DATA_CAPABILITIES 5
+#define MAX_LEVEL_PREFERENCE 4
+
+/*!
+ * \brief Picture Format Structure
+ */
+typedef struct {
+ uint32_t customPictureFormatWidth; /*!< Picture Width */
+ uint32_t customPictureFormatHeight; /*!< Picture Height */
+ uint32_t customPictureFormatpixelAspectRatio; /*!< Picture Pixel Aspect Ratio */
+ uint32_t customPictureFormatpixelclockConversionCode; /*!< Picture Pixel Conversion Code */
+ uint32_t customPictureFormatpixelclockDivisor; /*!< Picture Pixel Divisor */
+} customPictureFormat_t;
+
+
+/*!
+ * \brief Video Level Preference Structure
+ */
+typedef struct {
+ uint32_t transmitPreference; /*!< Transmit Preference */
+ uint32_t format; /*!< Format / Codec */
+ uint32_t maxBitRate; /*!< Maximum BitRate */
+ uint32_t minBitRate; /*!< Minimum BitRate */
+ uint32_t MPI; /*!< */
+ uint32_t serviceNumber; /*!< Service Number */
+} levelPreference_t; /*!< Level Preference Structure */
+
+/*!
+ * \brief Layout Config Structure (Update Capabilities Message Struct)
+ * \since 20080111
+ */
+typedef struct {
+ uint32_t layout; /*!< Layout \todo what is layout? */
+} layoutConfig_t; /*!< Layout Config Structure */
+
+
+/*!
+ * \brief Service Resource Structure
+ */
+typedef struct {
+ uint32_t layoutCount; /*!< Layout Count */
+ layoutConfig_t layout[MAX_LAYOUT_WITH_SAME_SERVICE]; /*!< Layout */
+ uint32_t serviceNum; /*!< Service Number */
+ uint32_t maxStreams; /*!< Maximum number of Streams */
+ uint32_t maxConferences; /*!< Maximum number of Conferences */
+ uint32_t activeConferenceOnRegistration; /*!< Active Conference On Registration */
+} serviceResource_t;
+
+
+
+/*!
+ * \brief Audio Capabilities Structure
+ */
+typedef struct {
+ skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */
+ uint32_t lel_maxFramesPerPacket; /*!< Maximum Number of Frames per IP Packet */
+ uint32_t lel_unknown[2]; /*!< this are related to G.723 */
+} audioCap_t;
+
+/*!
+ * \brief Video Capabilities Structure
+ */
+typedef struct {
+ skinny_codecs lel_payloadCapability; /*!< PayLoad Capability */
+ uint32_t lel_transmitOreceive; /*!< Transmit of Receive */
+ uint32_t lel_levelPreferenceCount; /*!< Level of Preference Count */
+
+ levelPreference_t levelPreference[MAX_LEVEL_PREFERENCE]; /*!< Level Preference */
+
+// uint32_t lel_codec_options[2]; /*!< Codec Options */
+
+ union {
+ struct {
+ uint32_t unknown1;
+ uint32_t unknown2;
+ } h263;
+ struct {
+ uint32_t profile; /*!< H264 profile */
+ uint32_t level; /*!< H264 level */
+ } h264;
+ } codec_options;
+
+ /**
+ * Codec options contains data specific for every codec
+ *
+ * Here is a list of known parameters per codec
+ // H.261
+ uint32_t lel_temporalSpatialTradeOffCapability;
+ uint32_t lel_stillImageTransmission;
+
+ // H.263
+ uint32_t lel_h263_capability_bitfield;
+ uint32_t lel_annexNandWFutureUse;
+
+ // Video
+ uint32_t lel_modelNumber;
+ uint32_t lel_bandwidth;
+ */
+} videoCap_t; /*!< Video Capabilities Structure */
+
+/*!
+ * \brief Data Capabilities Structure
+ */
+typedef struct {
+ uint32_t payloadCapability; /*!< Payload Capability */
+ uint32_t transmitOrReceive; /*!< Transmit or Receive */
+ uint32_t protocolDependentData; /*!< Protocol Dependent Data */
+ uint32_t maxBitRate; /*!< Maximum BitRate */
+} dataCap_t; /*!< Data Capabilities Structure */
+
+
+struct PACKED update_capabilities_message {
+ uint32_t lel_audioCapCount; /*!< Audio Capability Count */
+ uint32_t lel_videoCapCount; /*!< Video Capability Count */
+ uint32_t lel_dataCapCount; /*!< Data Capability Count */
+ uint32_t RTPPayloadFormat; /*!< RTP Payload Format */
+ uint32_t customPictureFormatCount; /*!< Custom Picture Format Count */
+
+ customPictureFormat_t customPictureFormat[MAX_CUSTOM_PICTURES]; /*!< Custom Picture Format */
+
+ uint32_t activeStreamsOnRegistration; /*!< Active Streams on Registration */
+ uint32_t maxBW; /*!< Max BW ?? */
+
+ uint32_t serviceResourceCount; /*!< Service Resource Count */
+ serviceResource_t serviceResource[MAX_SERVICE_TYPE]; /*!< Service Resource */
+
+ audioCap_t audioCaps[SKINNY_MAX_CAPABILITIES]; /*!< Audio Capabilities */
+ videoCap_t videoCaps[SKINNY_MAX_VIDEO_CAPABILITIES]; /*!< Video Capabilities */
+ dataCap_t dataCaps[SKINNY_MAX_DATA_CAPABILITIES]; /*!< Data Capabilities */
+
+ uint32_t unknown; /*!< Unknown */
+};
+
+
/* ServiceUrlStatReqMessage */
#define SERVICE_URL_STAT_REQ_MESSAGE 0x0033
struct PACKED service_url_stat_req_message {
};
#define SERVER_RESPONSE_MESSAGE 0x009E
+#define ServerMaxNameSize 48
+#define StationMaxServers 5
+/*!
+ * \brief Station Identifier Structure
+ */
+typedef struct {
+ char serverName[ServerMaxNameSize]; /*!< Server Name */
+} ServerIdentifier;
+
+struct PACKED server_response_message {
+ ServerIdentifier server[StationMaxServers]; /*!< Server Identifier */
+ uint32_t serverListenPort[StationMaxServers]; /*!< Server is Listening on Port */
+ uint32_t serverIpAddr[StationMaxServers]; /*!< Server IP Port */
+}; /*!< Server Result Message Structure */
+
/* ResetMessage */
#define RESET_MESSAGE 0x009F
/* no data for CAPABILITIES_REQ_MESSAGE */
struct register_reject_message reg_rej;
struct reset_message reset;
+ struct server_response_message serv_res_mess;
/* no data for KEEP_ALIVE_ACK_MESSAGE */
struct open_receive_channel_message open_receive_channel;
struct close_receive_channel_message close_receive_channel;
/* see field "extended_data" for USER_TO_DEVICE_DATA_VERSION1_MESSAGE */
struct dialed_phone_book_ack_message dialed_phone_book_ack;
+ struct update_capabilities_message upd_cap;
struct data_message data;
struct extended_data_message extended_data;
typedef struct skinny_message skinny_message_t;
-/*****************************************************************************/
-/* SKINNY TYPES */
-/*****************************************************************************/
-typedef enum {
- SKINNY_CODEC_NONE = 0,
- SKINNY_CODEC_NONSTANDARD = 1,
- SKINNY_CODEC_ALAW_64K = 2,
- SKINNY_CODEC_ALAW_56K = 3,
- SKINNY_CODEC_ULAW_64K = 4,
- SKINNY_CODEC_ULAW_56K = 5,
- SKINNY_CODEC_G722_64K = 6,
- SKINNY_CODEC_G722_56K = 7,
- SKINNY_CODEC_G722_48K = 8,
- SKINNY_CODEC_G723_1 = 9,
- SKINNY_CODEC_G728 = 10,
- SKINNY_CODEC_G729 = 11,
- SKINNY_CODEC_G729A = 12,
- SKINNY_CODEC_IS11172 = 13,
- SKINNY_CODEC_IS13818 = 14,
- SKINNY_CODEC_G729B = 15,
- SKINNY_CODEC_G729AB = 16,
- SKINNY_CODEC_GSM_FULL = 18,
- SKINNY_CODEC_GSM_HALF = 19,
- SKINNY_CODEC_GSM_EFULL = 20,
- SKINNY_CODEC_WIDEBAND_256K = 25,
- SKINNY_CODEC_DATA_64K = 32,
- SKINNY_CODEC_DATA_56K = 33,
- SKINNY_CODEC_G722_1_32K = 40,
- SKINNY_CODEC_G722_1_24K = 41,
- SKINNY_CODEC_GSM = 80,
- SKINNY_CODEC_ACTIVEVOICE = 81,
- SKINNY_CODEC_G726_32K = 82,
- SKINNY_CODEC_G726_24K = 83,
- SKINNY_CODEC_G726_16K = 84,
- SKINNY_CODEC_G729B_BIS = 85,
- SKINNY_CODEC_G729B_LOW = 86,
- SKINNY_CODEC_H261 = 100,
- SKINNY_CODEC_H263 = 101,
- SKINNY_CODEC_VIDEO = 102,
- SKINNY_CODEC_H264 = 103,
- SKINNY_CODEC_T120 = 105,
- SKINNY_CODEC_H224 = 106,
- SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257
-} skinny_codecs;
-char* skinny_codec2string(skinny_codecs skinnycodec);
/*****************************************************************************/
/* SKINNY FUNCTIONS */
uint32_t mode);
#define send_set_speaker_mode(listener, ...) perform_send_set_speaker_mode(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__)
+switch_status_t perform_send_srvreq_response(listener_t *listener,
+ const char *file, const char *func, int line,
+ char *ip, uint32_t port);
+#define send_srvreq_response(listener, ...) perform_send_srvreq_response(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__)
+
switch_status_t perform_send_start_media_transmission(listener_t *listener,
const char *file, const char *func, int line,
uint32_t conference_id,
return SWITCH_STATUS_SUCCESS;
}
+switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_message_t *request)
+{
+ char *sql;
+ skinny_profile_t *profile;
+
+ uint32_t i = 0;
+ uint32_t n = 0;
+ char *codec_order[SWITCH_MAX_CODECS];
+ char *codec_string;
+
+ size_t string_len, string_pos, pos;
+
+ switch_assert(listener->profile);
+ switch_assert(listener->device_name);
+
+ profile = listener->profile;
+
+ skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount));
+
+ n = request->data.upd_cap.lel_audioCapCount;
+ if (n > SWITCH_MAX_CODECS) {
+ n = SWITCH_MAX_CODECS;
+ }
+ string_len = -1;
+
+ skinny_check_data_length(request, sizeof(request->data.upd_cap.lel_audioCapCount) + n * sizeof(request->data.upd_cap.audioCaps[0]));
+
+ for (i = 0; i < n; i++) {
+ char *codec = skinny_codec2string(request->data.upd_cap.audioCaps[i].lel_payloadCapability);
+ codec_order[i] = codec;
+ string_len += strlen(codec)+1;
+ }
+ i = 0;
+ pos = 0;
+ codec_string = switch_core_alloc(listener->pool, string_len+1);
+ for (string_pos = 0; string_pos < string_len; string_pos++) {
+ char *codec = codec_order[i];
+ switch_assert(i < n);
+ if(pos == strlen(codec)) {
+ codec_string[string_pos] = ',';
+ i++;
+ pos = 0;
+ } else {
+ codec_string[string_pos] = codec[pos++];
+ }
+ }
+ codec_string[string_len] = '\0';
+ if ((sql = switch_mprintf(
+ "UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'",
+ codec_string,
+ listener->device_name
+ ))) {
+ skinny_execute_sql(profile, sql, profile->sql_mutex);
+ switch_safe_free(sql);
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
+ "Codecs %s supported.\n", codec_string);
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+switch_status_t skinny_handle_server_req_message(listener_t *listener, skinny_message_t *request)
+{
+ skinny_profile_t *profile;
+
+ profile = listener->profile;
+
+ skinny_log_l(listener, SWITCH_LOG_INFO, "Received Server Request Message (length=%d).\n", request->length);
+
+ send_srvreq_response(listener, profile->ip, profile->port);
+ return SWITCH_STATUS_SUCCESS;
+}
+
switch_status_t skinny_handle_xml_alarm(listener_t *listener, skinny_message_t *request)
{
switch_event_t *event = NULL;
skinny_log_l(listener, SWITCH_LOG_DEBUG, "Received %s (type=%x,length=%d).\n",
skinny_message_type2str(request->type), request->type, request->length);
}
- if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE) {
+ if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE && request->type != KEEP_ALIVE_MESSAGE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Device should send a register message first. Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length);
return SWITCH_STATUS_FALSE;
return skinny_handle_accessory_status_message(listener, request);
case XML_ALARM_MESSAGE:
return skinny_handle_xml_alarm(listener, request);
+ case DEVICE_UPDATECAPABILITIES:
+ return skinny_handle_updatecapabilities(listener, request);
+ case SERVER_REQ_MESSAGE:
+ return skinny_handle_server_req_message(listener, request);
default:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Unhandled %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length);