⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtpsession.c

📁 VLC Player Source Code
💻 C
字号:
/** * @file session.c * @brief RTP session handling *//***************************************************************************** * Copyright © 2008 Rémi Denis-Courmont * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2.0 * of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA ****************************************************************************/#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <stdlib.h>#include <assert.h>#include <errno.h>#include <vlc/vlc.h>#include <vlc_demux.h>#include "rtp.h"typedef struct rtp_source_t rtp_source_t;/** State for a RTP session: */struct rtp_session_t{    rtp_source_t **srcv;    unsigned       srcc;    uint8_t        ptc;    rtp_pt_t      *ptv;};static rtp_source_t *rtp_source_create (demux_t *, const rtp_session_t *, uint32_t, uint16_t);static voidrtp_source_destroy (demux_t *, const rtp_session_t *, rtp_source_t *);static void rtp_decode (demux_t *, const rtp_session_t *, rtp_source_t *);/** * Creates a new RTP session. */rtp_session_t *rtp_session_create (demux_t *demux){    rtp_session_t *session = malloc (sizeof (*session));    if (session == NULL)        return NULL;    session->srcv = NULL;    session->srcc = 0;    session->ptc = 0;    session->ptv = NULL;    (void)demux;    return session;}/** * Destroys an RTP session. */void rtp_session_destroy (demux_t *demux, rtp_session_t *session){    for (unsigned i = 0; i < session->srcc; i++)        rtp_source_destroy (demux, session, session->srcv[i]);    free (session->srcv);    free (session->ptv);    free (session);    (void)demux;}static void *no_init (demux_t *demux){    (void)demux;    return NULL;}static void no_destroy (demux_t *demux, void *opaque){    (void)demux; (void)opaque;}static void no_decode (demux_t *demux, void *opaque, block_t *block){    (void)demux; (void)opaque;    block_Release (block);}/** * Adds a payload type to an RTP session. */int rtp_add_type (demux_t *demux, rtp_session_t *ses, const rtp_pt_t *pt){    if (ses->srcc > 0)    {        msg_Err (demux, "cannot change RTP payload formats during session");        return EINVAL;    }    rtp_pt_t *ppt = realloc (ses->ptv, (ses->ptc + 1) * sizeof (rtp_pt_t));    if (ppt == NULL)        return ENOMEM;    ses->ptv = ppt;    ppt += ses->ptc++;    ppt->init = pt->init ? pt->init : no_init;    ppt->destroy = pt->destroy ? pt->destroy : no_destroy;    ppt->decode = pt->decode ? pt->decode : no_decode;    ppt->frequency = pt->frequency;    ppt->number = pt->number;    msg_Dbg (demux, "added payload type %"PRIu8" (f = %"PRIu32" Hz)",             ppt->number, ppt->frequency);    assert (ppt->frequency > 0); /* SIGFPE! */    (void)demux;    return 0;}/** State for an RTP source */struct rtp_source_t{    mtime_t  expiry;  /* inactivation date */    uint32_t ssrc;    uint16_t bad_seq; /* tentatively next expected sequence for resync */    uint16_t max_seq; /* next expected sequence */    uint16_t last_seq; /* sequence of the last dequeued packet */    block_t *blocks; /* re-ordered blocks queue */    void    *opaque[0]; /* Per-source private payload data */};/** * Initializes a new RTP source within an RTP session. */static rtp_source_t *rtp_source_create (demux_t *demux, const rtp_session_t *session,                   uint32_t ssrc, uint16_t init_seq){    rtp_source_t *source;    source = malloc (sizeof (*source) + (sizeof (void *) * session->ptc));    if (source == NULL)        return NULL;    source->ssrc = ssrc;    source->max_seq = source->bad_seq = init_seq;    source->last_seq = init_seq - 1;    source->blocks = NULL;    /* Initializes all payload */    for (unsigned i = 0; i < session->ptc; i++)        source->opaque[i] = session->ptv[i].init (demux);    msg_Dbg (demux, "added RTP source (%08x)", ssrc);    return source;}/** * Destroys an RTP source and its associated streams. */static voidrtp_source_destroy (demux_t *demux, const rtp_session_t *session,                    rtp_source_t *source){    msg_Dbg (demux, "removing RTP source (%08x)", source->ssrc);    for (unsigned i = 0; i < session->ptc; i++)        session->ptv[i].destroy (demux, source->opaque[i]);    block_ChainRelease (source->blocks);    free (source);}static inline uint16_t rtp_seq (const block_t *block){    assert (block->i_buffer >= 4);    return GetWBE (block->p_buffer + 2);}/** * Receives an RTP packet and queues it. * @param demux VLC demux object * @param session RTP session receiving the packet * @param block RTP packet including the RTP header */voidrtp_receive (demux_t *demux, rtp_session_t *session, block_t *block){    demux_sys_t *p_sys = demux->p_sys;    /* RTP header sanity checks (see RFC 3550) */    if (block->i_buffer < 12)        goto drop;    if ((block->p_buffer[0] >> 6 ) != 2) /* RTP version number */        goto drop;    /* Remove padding if present */    if (block->p_buffer[0] & 0x20)    {        uint8_t padding = block->p_buffer[block->i_buffer - 1];        if ((padding == 0) || (block->i_buffer < (12u + padding)))            goto drop; /* illegal value */        block->i_buffer -= padding;    }    mtime_t        now = mdate ();    rtp_source_t  *src  = NULL;    const uint16_t seq  = GetWBE (block->p_buffer + 2);    const uint32_t ssrc = GetDWBE (block->p_buffer + 8);    /* In most case, we know this source already */    for (unsigned i = 0, max = session->srcc; i < max; i++)    {        rtp_source_t *tmp = session->srcv[i];        if (tmp->ssrc == ssrc)        {            src = tmp;            break;        }        /* RTP source garbage collection */        if (tmp->expiry < now)        {            rtp_source_destroy (demux, session, tmp);            if (--session->srcc > 0)                session->srcv[i] = session->srcv[session->srcc - 1];        }    }    if (src == NULL)    {        /* New source */        if (session->srcc >= p_sys->max_src)        {            msg_Warn (demux, "too many RTP sessions");            goto drop;        }        rtp_source_t **tab;        tab = realloc (session->srcv, (session->srcc + 1) * sizeof (*tab));        if (tab == NULL)            goto drop;        session->srcv = tab;        src = rtp_source_create (demux, session, ssrc, seq);        if (src == NULL)            goto drop;        tab[session->srcc++] = src;    }    /* Be optimistic for the first packet. Certain codec, such as Vorbis     * do not like loosing the first packet(s), so we cannot just wait     * for proper sequence synchronization. And we don't want to assume that     * the sender starts at seq=0 either. */    if (src->blocks == NULL)        src->max_seq = seq - p_sys->max_dropout;    /* Check sequence number */    /* NOTE: the sequence number is per-source,     * but is independent from the payload type. */    uint16_t delta_seq = seq - (src->max_seq + 1);    if ((delta_seq < 0x8000) ? (delta_seq > p_sys->max_dropout)                             : ((65535 - delta_seq) > p_sys->max_misorder))    {        msg_Dbg (demux, "sequence discontinuity (got: %u, expected: %u)",                 seq, (src->max_seq + 1) & 0xffff);        if (seq == ((src->bad_seq + 1) & 0xffff))        {            src->max_seq = src->bad_seq = seq;            msg_Warn (demux, "sequence resynchronized");            block_ChainRelease (src->blocks);            src->blocks = NULL;        }        else        {            src->bad_seq = seq;            goto drop;        }    }    else    if (delta_seq < 0x8000)        src->max_seq = seq;    /* Queues the block in sequence order,     * hence there is a single queue for all payload types. */    block_t **pp = &src->blocks;    for (block_t *prev = *pp; prev != NULL; prev = *pp)    {        int16_t delta_seq = seq - rtp_seq (prev);        if (delta_seq < 0)            break;        if (delta_seq == 0)            goto drop; /* duplicate */        pp = &prev->p_next;    }    block->p_next = *pp;    *pp = block;    rtp_decode (demux, session, src);    return;drop:    block_Release (block);}static voidrtp_decode (demux_t *demux, const rtp_session_t *session, rtp_source_t *src){    block_t *block = src->blocks;    /* Buffer underflow? */    if (!block || !block->p_next || !block->p_next->p_next)        return;    /* TODO: use time rather than packet counts for buffer measurement */    src->blocks = block->p_next;    block->p_next = NULL;    /* Discontinuity detection */    if (((src->last_seq + 1) & 0xffff) != rtp_seq (block))        block->i_flags |= BLOCK_FLAG_DISCONTINUITY;    src->last_seq = rtp_seq (block);    /* Match the payload type */    const rtp_pt_t *pt = NULL;    void *pt_data = NULL;    const uint8_t   ptype = block->p_buffer[1] & 0x7F;    for (unsigned i = 0; i < session->ptc; i++)    {        if (session->ptv[i].number == ptype)        {            pt = &session->ptv[i];            pt_data = src->opaque[i];            break;        }    }    if (pt == NULL)    {        msg_Dbg (demux, "ignoring unknown payload (%"PRIu8")", ptype);        goto drop;    }    /* Computes the PTS from the RTP timestamp and payload RTP frequency.     * DTS is unknown. Also, while the clock frequency depends on the payload     * format, a single source MUST only use payloads of a chosen frequency.     * Otherwise it would be impossible to compute consistent timestamps. */    /* FIXME: handle timestamp wrap properly */    /* TODO: sync multiple sources sanely... */    const uint32_t timestamp = GetDWBE (block->p_buffer + 4);    block->i_pts = UINT64_C(1) * CLOCK_FREQ * timestamp / pt->frequency;    /* CSRC count */    size_t skip = 12u + (block->p_buffer[0] & 0x0F) * 4;    /* Extension header (ignored for now) */    if (block->p_buffer[0] & 0x10)    {        skip += 4;        if (block->i_buffer < skip)            goto drop;        skip += 4 * GetWBE (block->p_buffer + skip - 2);    }    if (block->i_buffer < skip)        goto drop;    block->p_buffer += skip;    block->i_buffer -= skip;    pt->decode (demux, pt_data, block);    return;drop:    block_Release (block);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -