For right now, I'm gathering my notes on how the sipcc library works. Until my understanding improves significantly, this will be incomplete and probably contain numerous inaccuracies. Caveat implementor. --Adam Roach <email@example.com>
For information on the changes necessary to add a new message to SIPCC, see Media/WebRTC/SIPCCMessaging.
At the heart of the SIPCC Core functioning is a state machine that dispatches events to the correct functions.
Typical Message from PeerConnection to GSM
This is a bit of a mouse maze, as getting from the application down to where the actual FSM is invoked passes through two inter-thread queues by way of at least 14 different functions (once you get through the "softphone" C++ wrapper), many of which are very small passthroughs to the next function. The following sections are meant to serve as fenceposts to aid in figuring out where you'll typically emerge when you call into one of these functions.
The dispatcher for the SIPCC FSM is sm_process_event() in core/gsm/sm.c, which is in turn invoked from fim_process_event() in core/gsm/fim.c. This, in turn, is called most importantly (but not exclusively) by gsm_process_msg() in core/gsm/gsm.c. The whole task is called in a loop by GSMTask() in core/gsm/gsm.c, which draws the relevant events off a message queue (gsm_msg_queue from core/gsm/h/gsm.h).
Messages are queued onto this gsm_msg_queue queue via gsm_send_msg(), in core/gsm/gsm.c, called from cc_send_msg() in core/gsm/ccapi.c. This function is, in turn, called from the various API methods:
The most relevant path to get to these functions, from a WebRTC perspective, appears to be through processSessionEvent(), in core/ccapp/ccprovider.c. This is called via ccp_handler() from core/ccapp/ccprovider.c, when the second parameter is set to CCAPP_INVOKE_FEATURE. This is all driven by a generic "listener-per-event-type" loop in CCApp_task(), from core/ccapp/ccapp_task.c. It draws messages from the queue ccapp_msgq. (The ccp_handler() function is set up in that listener table by CCAppInit() while the thread is still being setup.)
The events in that queue are of type "cc_msgs_t", defined in core/includes/ccapi.h.
Using Thread (DOM Thread?)
Messages are, in turn, queued on ccapp_msgq by ccappTaskSendMsg() in core/ccapp/ccapp_task.c, which is called (exclusively?) by ccappTaskPostMsg() in the same file. This is called from a rather large handful of functions in the various files under core/ccapp. In the WebRTC implementation, these functions are invoked by the various classes in ../softphonewrapper (among other locations)
States, Events, and Transitions
Quite a bit here TBD
Presently, the handling of the key WebRTC messages appears to proceed independent of the FSM state. These messages are:
The CC_MSG_ONHOOK message does appear to come through from time to time, and its behavior does vary by state -- so the state isn't completely irrelevant to call handling. This event will occasionally arrive in IDLE state, which seems a bit surprising; it's not clear that this is intended to happen. The invocation of onhook happens when the PeerConnection is closed, by way of CSF::CC_SIPCCCall::endCall, CCAPI_Call_endCall, and CC_CallFeature_terminateCall. It may happen via a different callchain as well, but I haven't found any others yet.
Here is a high-level sketch of what appear to be the main structures around offer/answer handling. The fields shown are a subset of what each structure actually contains, selected to be illustrative of the kind of information present in a struct.
State machine state is stored in a structure of type "fsm_fcb_t." It contains the current state, the previous state, an enumeration indicating what kind of FSM is being represented (I believe this will always be FSM_TYPE_DEF for WebRTC), and (distinct, non-unioned) pointers to data specific to that kind of FSM. For FSM_TYPE_DEF, this is a pointer to a DCB.
The implementation of the FCB is the various functions in core/gsm/fsmdef.c. For WebRTC, this appears to deal exclusively with SDP negotiation. The configuration of the SIPCC library includes a boolean attribute, CFGID_SDPMODE; it must be set to make use of the WebRTC methods.
The DCB (type fsmdef_dcb_t is the big kahuna. It is the anchor for everything having to do with an ongoing session. Notable members include:
- media_list (type sll_lite_list_t) -- linked list of fsmdef_media_t, corresponding to the media streams associated with the session. This is tightly bound to SDP m= lines.
- media_cap_tbl (type cc_media_cap_table_t *) -- Not quite sure yet. Table of size 4 supposedly representing the "media capabilities" of the call.
- sdp (type cc_sdp_t) -- Keeps track of local and remote SDP (see following section).
- ice_ufrag, ice_pwd, ice_default_candidate_addr -- strings representing ICE parameters. (This seems like the wrong level to represent this information)
Inside the DCB, there is an "sdp" field. It points to a structure of type cc_sdp_t (from ccapi.h), which contains nothing but two void (!) pointers (src_sdp and dest_sdp). These pointers point to structure of type sdp_t (from sdp.h), which contains all the information about the SDP itself. (NB: many of these fields use a fixed size of 256 bytes. This may benefit from dynamic string allocation.).
The sdp_t structure contains a linked list of sdp_mca ("media, connection, and attributes") structures, which has fields for all the m= line related information, plus a linked list of sdp_attr structures. Observation: due to the fixed field size, above, each and every attribute takes is going to take on the order of 273 bytes. Since WebRTC might get pretty chatty with attributes, this can quickly pile up to take several dozen kb per session just to store attributes -- although this might just be noise in the grand scheme of things.