ortp事件处理源码分析
ortp是使用c语言编写的一个库,里面的数据结构都是以c语言的形式写成的,都是有struct作为存储结构,一些专用的操作函数作为方法。这篇文章专注于其中的事件处理代码。
结构
mblk_t
typedef struct msgb
{
struct msgb *b_prev;
struct msgb *b_next;
struct msgb *b_cont;
struct datab *b_datap;
unsigned char *b_rptr;
unsigned char *b_wptr;
uint32_t reserved1;
uint32_t reserved2;
#if defined(ORTP_TIMESTAMP)
struct timeval timestamp;
#endif
ortp_recv_addr_t recv_addr; /*contains the destination address of incoming packets, used for ICE processing*/
struct sockaddr_storage net_addr; /*source address of incoming packet, or dest address of outgoing packet, used only by simulator and modifiers*/
socklen_t net_addrlen; /*source (dest) address of incoming (outgoing) packet length used by simulator and modifiers*/
uint8_t ttl_or_hl;
} mblk_t;
这个结构体是消息块,大量使用于ortp库中用来存储的数据块,因为到处都在用,所以结构体中有一些冗余的部分。其内部的结构中主要有一个前向指针b_prev
和一个后向指针b_next
,可以用来当链表,还有一个数据指针b_datap
,存着真正的数据,接着一个读指针b_rptr
和一个写指针b_wptr
。还有一些其他结构。发送接受rtp、rtcp数据的时候都是用这个数据结构,因为在事件处理中只用到了一小部分,所以不做详细介绍。
queue_t
typedef struct _queue
{
mblk_t _q_stopper;
int q_mcount; /*number of packet in the q */
} queue_t;
一个线程不安全的环形队列,使用头插法,结构记录了队列中的元素数量。直接使用了mblk_t
作为前后指针。是在ortp库中大量使用的数据结构。
OList
struct _OList {
struct _OList *next;
struct _OList *prev;
void *data;
};
typedef struct _OList OList;
如代码,是一个标准的双向链表。
OrtpEvQueue
typedef struct OrtpEvQueue{
queue_t q;
ortp_mutex_t mutex;
} OrtpEvQueue;
一个简单的同步队列,用于存储ortp的事件,可以看到有一个互斥锁,用来多线程同步。
OrtpEvDispatcherCb
typedef void (*OrtpEvDispatcherCb)(const OrtpEventData *evd, void *user_data);
事件处理回调函数的签名
OrtpEvDispatcherData
typedef struct OrtpEvDispatcherData{
OrtpEventType type;
rtcp_type_t subtype;
OrtpEvDispatcherCb on_found;
void* user_data;
} OrtpEvDispatcherData;
事件处理器结构,存储了要处理的事件类型和处理函数和数据。
OrtpEventData
struct _OrtpEventData{
mblk_t *packet; /* most events are associated to a received packet */
struct sockaddr_storage source_addr;
socklen_t source_addrlen;
ortpTimeSpec ts;
union {
int telephone_event;
int payload_type;
bool_t dtls_stream_encrypted;
bool_t zrtp_stream_encrypted;
struct _ZrtpSas{
char sas[32]; // up to 31 + null characters
bool_t verified;
} zrtp_sas;
OrtpSocketType socket_type;
bool_t ice_processing_successful;
uint64_t tmmbr_mxtbr;
uint32_t received_rtt_character;
} info;
};
typedef struct _OrtpEventData OrtpEventData;
事件数据结构,为了处理多种事件有一些冗余,主要内容是packet
成员。存储了数据的主要内容。
OrtpEvDispatcher
typedef struct OrtpEvDispatcher{
OrtpEvQueue *q;
struct _RtpSession* session;
OList *cbs;
} OrtpEvDispatcher;
ortp的事件调度器结构,可以从代码中看出来,每个Dispatcher
属于某一个rtpsession
。q
中存储的是需要处理的事件,cbs
中存储了所有的事件处理函数,cbs
存储的类型其实是OrtpEvDispatcherData
类型的元素。
函数
ortp_ev_dispatcher_new
OrtpEvDispatcher * ortp_ev_dispatcher_new(RtpSession* session) {
OrtpEvDispatcher *d=ortp_new(OrtpEvDispatcher,1);
d->session = session;
d->q = ortp_ev_queue_new();
rtp_session_register_event_queue(session, d->q);
d->cbs = NULL;
return d;
}
创建事件调度器,首先分配OrtpEvDispatcher
结构的内存空间,然后设置rtpsession
,然后构建待处理事件队列,rtp_session_register_event_queue
的作用是将刚创建的待处理事件队列追加到rtpsession
的事件队列链表中。
ortp_ev_dispatcher_connect
void ortp_ev_dispatcher_connect(OrtpEvDispatcher *d
, OrtpEventType type
, rtcp_type_t subtype
, OrtpEvDispatcherCb cb
, void *user_data) {
OrtpEvDispatcherData *data=ortp_new(OrtpEvDispatcherData,1);
data->type = type;
data->subtype = subtype;
data->on_found = cb;
data->user_data = user_data;
d->cbs = o_list_append(d->cbs, data);
}
这个函数就是在调度器中注册一个事件处理器。首先分配内存,然后填入要相应的事件类型和回调函数以及数据。最后将这个结构追加到事件处理器链表的最后。
ortp_ev_dispatcher_iterate
void ortp_ev_dispatcher_iterate(OrtpEvDispatcher *d) {
OrtpEvent *ev = NULL;
while ((ev = ortp_ev_queue_get(d->q)) != NULL) {
iterate_cbs(d, ev);
ortp_event_destroy(ev);
}
}
这个函数就是处理调度器中的事件,使用ortp_ev_queue_get
函数遍历取出待处理事件队列中的事件,然后使用iterate_cbs
函数查找合适的事件处理器进行处理。处理完成后用ortp_event_destroy
删除事件。一直循环直到待处理事件队列为空。
这个函数需要库的使用者主动调用,ORTP
库内部是不会主动调用这个函数的。这点一定要注意。如果和这个调度器关联的session
上产生了事件,但是不主动调用此函数的话,那么待处理事件就会堆积在待处理队列中,造成内存泄漏的效果,这点一定要注意。
iterate_cbs
static void iterate_cbs(OrtpEvDispatcher *disp, OrtpEvent *ev) {
OrtpEventData *d = ortp_event_get_data(ev);
do {
/*for each packet part, if ANY iterate through the whole callback list to see if
anyone is interested in it*/
OrtpEventData *d = ortp_event_get_data(ev);
OList* it;
OrtpEventType evt = ortp_event_get_type(ev);
for (it = disp->cbs; it != NULL; it = it->next){
OrtpEvDispatcherData *data = (OrtpEvDispatcherData *)it->data;
/*
const rtcp_common_header_t *ch = rtcp_get_common_header(d->packet);
rtcp_type_t packet_type = 0;
if (ch != NULL) {
packet_type = rtcp_common_header_get_packet_type(ch);
}
*/
if (evt == data->type) {
if (!is_rtcp_event(data->type) || rtcp_is_type(d->packet, data->subtype)) {
data->on_found(d, data->user_data);
}
}
}
} while (d->packet!=NULL && rtcp_next_packet(d->packet));
}
这个函数的作用是寻找事件对应的处理器,并调用事件处理函数。这个函数有两重循环,第一层循环是遍历事件的数据包,第二层循环是针对每个事件的数据包循环处理器。
首先从事件中取出事件的类型和数据部分。然后遍历调度器中已经注册事件处理器,获取每个事件处理器数据的类型和事件类型对比,如果一致说明初步找到了要处理的事件对应的处理器。然后进一步判断是否是不是rtcp事件,如果不是rtcp事件则说明找到了对应的处理器。如果是rtcp事件,则要进一步判断事件的数据包中的rtcp包类型和事件处理器数据的子类型是否一致,如果一致则说明找到了对应的处理器。找到了处理器后调用处理器数据的存储的on_found
回调函数。因为有可能对同一类型注册多个处理器,所以要继续遍历剩余的处理器。
因为rtcp协议中允许一次性发送多个数据包,所以要尝试寻找事件数据中的下一个rtcp数据包,如果找到了则继续刚才的过程,否则事件处理结束。