ortp事件处理源码分析

1 篇文章 0 订阅

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属于某一个rtpsessionq中存储的是需要处理的事件,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数据包,如果找到了则继续刚才的过程,否则事件处理结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值