Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

rtp.h File Reference

#include <asterisk/frame.h>
#include <asterisk/io.h>
#include <asterisk/sched.h>
#include <asterisk/channel.h>
#include <netinet/in.h>

Go to the source code of this file.

Data Structures

struct  ast_rtp_protocol

Defines

#define AST_RTP_DTMF   (1 << 0)
#define AST_RTP_CN   (1 << 1)
#define AST_RTP_CISCO_DTMF   (1 << 2)
#define AST_RTP_MAX   AST_RTP_CISCO_DTMF

Typedefs

typedef int(* ast_rtp_callback )(struct ast_rtp *rtp, struct ast_frame *f, void *data)

Functions

ast_rtpast_rtp_new (struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode)
void ast_rtp_set_peer (struct ast_rtp *rtp, struct sockaddr_in *them)
void ast_rtp_get_peer (struct ast_rtp *rtp, struct sockaddr_in *them)
void ast_rtp_get_us (struct ast_rtp *rtp, struct sockaddr_in *us)
void ast_rtp_destroy (struct ast_rtp *rtp)
void ast_rtp_set_callback (struct ast_rtp *rtp, ast_rtp_callback callback)
void ast_rtp_set_data (struct ast_rtp *rtp, void *data)
int ast_rtp_write (struct ast_rtp *rtp, struct ast_frame *f)
ast_frameast_rtp_read (struct ast_rtp *rtp)
ast_frameast_rtcp_read (struct ast_rtp *rtp)
int ast_rtp_fd (struct ast_rtp *rtp)
int ast_rtcp_fd (struct ast_rtp *rtp)
int ast_rtp_senddigit (struct ast_rtp *rtp, char digit)
int ast_rtp_settos (struct ast_rtp *rtp, int tos)
void ast_rtp_pt_clear (struct ast_rtp *rtp)
void ast_rtp_pt_default (struct ast_rtp *rtp)
void ast_rtp_set_m_type (struct ast_rtp *rtp, int pt)
void ast_rtp_set_rtpmap_type (struct ast_rtp *rtp, int pt, char *mimeType, char *mimeSubtype)
rtpPayloadType ast_rtp_lookup_pt (struct ast_rtp *rtp, int pt)
int ast_rtp_lookup_code (struct ast_rtp *rtp, int isAstFormat, int code)
void ast_rtp_get_current_formats (struct ast_rtp *rtp, int *astFormats, int *nonAstFormats)
char * ast_rtp_lookup_mime_subtype (int isAstFormat, int code)
void ast_rtp_setnat (struct ast_rtp *rtp, int nat)
int ast_rtp_bridge (struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
int ast_rtp_proto_register (struct ast_rtp_protocol *proto)
void ast_rtp_proto_unregister (struct ast_rtp_protocol *proto)
void ast_rtp_stop (struct ast_rtp *rtp)
void ast_rtp_init (void)
void ast_rtp_reload (void)


Define Documentation

#define AST_RTP_CISCO_DTMF   (1 << 2)
 

DTMF (Cisco Proprietary)

Definition at line 34 of file rtp.h.

Referenced by ast_rtp_read().

#define AST_RTP_CN   (1 << 1)
 

'Comfort Noise' (RFC3389)

Definition at line 32 of file rtp.h.

Referenced by ast_rtp_read().

#define AST_RTP_DTMF   (1 << 0)
 

DTMF (RFC2833)

Definition at line 30 of file rtp.h.

Referenced by ast_rtp_read().

#define AST_RTP_MAX   AST_RTP_CISCO_DTMF
 

Maximum RTP-specific code

Definition at line 36 of file rtp.h.


Typedef Documentation

typedef int(* ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data)
 

Definition at line 49 of file rtp.h.

Referenced by ast_rtp_set_callback().


Function Documentation

int ast_rtcp_fd struct ast_rtp rtp  ) 
 

Definition at line 107 of file rtp.c.

References ast_rtp::rtcp, and ast_rtcp::s.

00108 {
00109    if (rtp->rtcp)
00110       return rtp->rtcp->s;
00111    return -1;
00112 }

struct ast_frame* ast_rtcp_read struct ast_rtp rtp  ) 
 

Definition at line 321 of file rtp.c.

References AST_FRAME_NULL, ast_log(), CRASH, LOG_DEBUG, LOG_WARNING, ast_rtp::nat, option_debug, ast_rtp::rtcp, ast_rtcp::s, ast_rtp::them, and ast_rtcp::them.

00322 {
00323    static struct ast_frame null_frame = { AST_FRAME_NULL, };
00324    int len;
00325    int hdrlen = 8;
00326    int res;
00327    struct sockaddr_in sin;
00328    unsigned int rtcpdata[1024];
00329    
00330    if (!rtp->rtcp)
00331       return &null_frame;
00332 
00333    len = sizeof(sin);
00334    
00335    res = recvfrom(rtp->rtcp->s, rtcpdata, sizeof(rtcpdata),
00336                0, (struct sockaddr *)&sin, &len);
00337    
00338    if (res < 0) {
00339       ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno));
00340       if (errno == EBADF)
00341          CRASH;
00342       return &null_frame;
00343    }
00344 
00345    if (res < hdrlen) {
00346       ast_log(LOG_WARNING, "RTP Read too short\n");
00347       return &null_frame;
00348    }
00349 
00350    if (rtp->nat) {
00351       /* Send to whoever sent to us */
00352       if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
00353           (rtp->rtcp->them.sin_port != sin.sin_port)) {
00354          memcpy(&rtp->them, &sin, sizeof(rtp->them));
00355          ast_log(LOG_DEBUG, "RTP NAT: Using address %s:%d\n", inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
00356       }
00357    }
00358    if (option_debug)
00359       ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res);
00360    return &null_frame;
00361 }

int ast_rtp_bridge struct ast_channel c0,
struct ast_channel c1,
int  flags,
struct ast_frame **  fo,
struct ast_channel **  rc
 

Definition at line 1157 of file rtp.c.

References ast_autoservice_start(), ast_autoservice_stop(), AST_BRIDGE_DTMF_CHANNEL_0, AST_BRIDGE_DTMF_CHANNEL_1, ast_check_hangup(), AST_FRAME_DTMF, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frfree(), ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_read(), ast_rtp_get_peer(), ast_waitfor_n(), ast_write(), ast_frame::frametype, inaddrcmp(), ast_channel::lock, LOG_DEBUG, LOG_WARNING, ast_channel::name, ast_channel_pvt::pvt, and ast_channel::pvt.

01158 {
01159    struct ast_frame *f;
01160    struct ast_channel *who, *cs[3];
01161    struct ast_rtp *p0, *p1;
01162    struct ast_rtp *vp0, *vp1;
01163    struct ast_rtp_protocol *pr0, *pr1;
01164    struct sockaddr_in ac0, ac1;
01165    struct sockaddr_in vac0, vac1;
01166    struct sockaddr_in t0, t1;
01167    struct sockaddr_in vt0, vt1;
01168    
01169    void *pvt0, *pvt1;
01170    int to;
01171    memset(&vt0, 0, sizeof(vt0));
01172    memset(&vt1, 0, sizeof(vt1));
01173    memset(&vac0, 0, sizeof(vac0));
01174    memset(&vac1, 0, sizeof(vac1));
01175 
01176    /* XXX Wait a half a second for things to settle up 
01177          this really should be fixed XXX */
01178    ast_autoservice_start(c0);
01179    ast_autoservice_start(c1);
01180    usleep(500000);
01181    ast_autoservice_stop(c0);
01182    ast_autoservice_stop(c1);
01183 
01184    /* if need DTMF, cant native bridge */
01185    if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
01186       return -2;
01187    ast_mutex_lock(&c0->lock);
01188    ast_mutex_lock(&c1->lock);
01189    pr0 = get_proto(c0);
01190    pr1 = get_proto(c1);
01191    if (!pr0) {
01192       ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
01193       ast_mutex_unlock(&c0->lock);
01194       ast_mutex_unlock(&c1->lock);
01195       return -1;
01196    }
01197    if (!pr1) {
01198       ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
01199       ast_mutex_unlock(&c0->lock);
01200       ast_mutex_unlock(&c1->lock);
01201       return -1;
01202    }
01203    pvt0 = c0->pvt->pvt;
01204    pvt1 = c1->pvt->pvt;
01205    p0 = pr0->get_rtp_info(c0);
01206    if (pr0->get_vrtp_info)
01207       vp0 = pr0->get_vrtp_info(c0);
01208    else
01209       vp0 = NULL;
01210    p1 = pr1->get_rtp_info(c1);
01211    if (pr1->get_vrtp_info)
01212       vp1 = pr1->get_vrtp_info(c1);
01213    else
01214       vp1 = NULL;
01215    if (!p0 || !p1) {
01216       /* Somebody doesn't want to play... */
01217       ast_mutex_unlock(&c0->lock);
01218       ast_mutex_unlock(&c1->lock);
01219       return -2;
01220    }
01221    if (pr0->get_codec && pr1->get_codec) {
01222       int codec0,codec1;
01223       codec0 = pr0->get_codec(c0);
01224       codec1 = pr1->get_codec(c1);
01225       /* Hey, we can't do reinvite if both parties speak diffrent codecs */
01226       if (!(codec0 & codec1)) {
01227          ast_log(LOG_WARNING, "codec0 = %d is not codec1 = %d, cannot native bridge.\n",codec0,codec1);
01228          ast_mutex_unlock(&c0->lock);
01229          ast_mutex_unlock(&c1->lock);
01230          return -2;
01231       }
01232    }
01233    if (pr0->set_rtp_peer(c0, p1, vp1)) 
01234       ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
01235    else {
01236       /* Store RTP peer */
01237       ast_rtp_get_peer(p1, &ac1);
01238       if (vp1)
01239          ast_rtp_get_peer(p1, &vac1);
01240    }
01241    if (pr1->set_rtp_peer(c1, p0, vp0))
01242       ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
01243    else {
01244       /* Store RTP peer */
01245       ast_rtp_get_peer(p0, &ac0);
01246       if (vp0)
01247          ast_rtp_get_peer(p0, &vac0);
01248    }
01249    ast_mutex_unlock(&c0->lock);
01250    ast_mutex_unlock(&c1->lock);
01251    cs[0] = c0;
01252    cs[1] = c1;
01253    cs[2] = NULL;
01254    for (;;) {
01255       if ((c0->pvt->pvt != pvt0)  ||
01256          (c1->pvt->pvt != pvt1) ||
01257          (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
01258             ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
01259             if (c0->pvt->pvt == pvt0) {
01260                if (pr0->set_rtp_peer(c0, NULL, NULL)) 
01261                   ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
01262             }
01263             if (c1->pvt->pvt == pvt1) {
01264                if (pr1->set_rtp_peer(c1, NULL, NULL)) 
01265                   ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
01266             }
01267             /* Tell it to try again later */
01268             return -3;
01269       }
01270       to = -1;
01271       ast_rtp_get_peer(p1, &t1);
01272       ast_rtp_get_peer(p0, &t0);
01273       if (vp1)
01274          ast_rtp_get_peer(vp1, &vt1);
01275       if (vp0)
01276          ast_rtp_get_peer(vp0, &vt0);
01277       if (inaddrcmp(&t1, &ac1) || (vp1 && inaddrcmp(&vt1, &vac1))) {
01278          ast_log(LOG_DEBUG, "Oooh, '%s' changed end address\n", c1->name);
01279          if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL)) 
01280             ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name);
01281          memcpy(&ac1, &t1, sizeof(ac1));
01282          memcpy(&vac1, &vt1, sizeof(vac1));
01283       }
01284       if (inaddrcmp(&t0, &ac0) || (vp0 && inaddrcmp(&vt0, &vac0))) {
01285          ast_log(LOG_DEBUG, "Oooh, '%s' changed end address\n", c0->name);
01286          if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL))
01287             ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name);
01288          memcpy(&ac0, &t0, sizeof(ac0));
01289          memcpy(&vac0, &vt0, sizeof(vac0));
01290       }
01291       who = ast_waitfor_n(cs, 2, &to);
01292       if (!who) {
01293          ast_log(LOG_DEBUG, "Ooh, empty read...\n");
01294          /* check for hagnup / whentohangup */
01295          if (ast_check_hangup(c0) || ast_check_hangup(c1))
01296             break;
01297          continue;
01298       }
01299       f = ast_read(who);
01300       if (!f || ((f->frametype == AST_FRAME_DTMF) &&
01301                (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || 
01302                 ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
01303          *fo = f;
01304          *rc = who;
01305          ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup");
01306          if ((c0->pvt->pvt == pvt0) && (!c0->_softhangup)) {
01307             if (pr0->set_rtp_peer(c0, NULL, NULL)) 
01308                ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
01309          }
01310          if ((c1->pvt->pvt == pvt1) && (!c1->_softhangup)) {
01311             if (pr1->set_rtp_peer(c1, NULL, NULL)) 
01312                ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
01313          }
01314          /* That's all we needed */
01315          return 0;
01316       } else {
01317          if ((f->frametype == AST_FRAME_DTMF) || 
01318             (f->frametype == AST_FRAME_VOICE) || 
01319             (f->frametype == AST_FRAME_VIDEO)) {
01320             /* Forward voice or DTMF frames if they happen upon us */
01321             if (who == c0) {
01322                ast_write(c1, f);
01323             } else if (who == c1) {
01324                ast_write(c0, f);
01325             }
01326          }
01327          ast_frfree(f);
01328       }
01329       /* Swap priority not that it's a big deal at this point */
01330       cs[2] = cs[0];
01331       cs[0] = cs[1];
01332       cs[1] = cs[2];
01333       
01334    }
01335    return -1;
01336 }

void ast_rtp_destroy struct ast_rtp rtp  ) 
 

Definition at line 820 of file rtp.c.

References ast_io_remove(), ast_smoother_free(), free, ast_rtp::io, ast_rtp::ioid, ast_rtp::rtcp, ast_rtcp::s, ast_rtp::s, and ast_rtp::smoother.

00821 {
00822    if (rtp->smoother)
00823       ast_smoother_free(rtp->smoother);
00824    if (rtp->ioid)
00825       ast_io_remove(rtp->io, rtp->ioid);
00826    if (rtp->s > -1)
00827       close(rtp->s);
00828    if (rtp->rtcp) {
00829       close(rtp->rtcp->s);
00830       free(rtp->rtcp);
00831    }
00832    free(rtp);
00833 }

int ast_rtp_fd struct ast_rtp rtp  ) 
 

Definition at line 102 of file rtp.c.

References ast_rtp::s.

00103 {
00104    return rtp->s;
00105 }

void ast_rtp_get_current_formats struct ast_rtp rtp,
int *  astFormats,
int *  nonAstFormats
 

Definition at line 629 of file rtp.c.

References rtpPayloadType::code, ast_rtp::current_RTP_PT, rtpPayloadType::isAstFormat, and MAX_RTP_PT.

00630                                                    {
00631   int pt;
00632 
00633   *astFormats = *nonAstFormats = 0;
00634   for (pt = 0; pt < MAX_RTP_PT; ++pt) {
00635     if (rtp->current_RTP_PT[pt].isAstFormat) {
00636       *astFormats |= rtp->current_RTP_PT[pt].code;
00637     } else {
00638       *nonAstFormats |= rtp->current_RTP_PT[pt].code;
00639     }
00640   }
00641 }

void ast_rtp_get_peer struct ast_rtp rtp,
struct sockaddr_in *  them
 

Definition at line 798 of file rtp.c.

References ast_rtp::them.

Referenced by ast_rtp_bridge().

00799 {
00800    them->sin_family = AF_INET;
00801    them->sin_port = rtp->them.sin_port;
00802    them->sin_addr = rtp->them.sin_addr;
00803 }

void ast_rtp_get_us struct ast_rtp rtp,
struct sockaddr_in *  us
 

Definition at line 805 of file rtp.c.

References ast_rtp::us.

00806 {
00807    memcpy(us, &rtp->us, sizeof(rtp->us));
00808 }

void ast_rtp_init void   ) 
 

Definition at line 1371 of file rtp.c.

References ast_rtp_reload().

Referenced by main().

01372 {
01373    ast_rtp_reload();
01374 }

int ast_rtp_lookup_code struct ast_rtp rtp,
int  isAstFormat,
int  code
 

Definition at line 653 of file rtp.c.

References rtpPayloadType::code, rtpPayloadType::isAstFormat, MAX_RTP_PT, ast_rtp::rtp_lookup_code_cache_code, ast_rtp::rtp_lookup_code_cache_isAstFormat, and ast_rtp::rtp_lookup_code_cache_result.

Referenced by ast_rtp_write().

00653                                                                         {
00654   int pt;
00655 
00656   /* Looks up an RTP code out of our *static* outbound list */
00657 
00658   if (isAstFormat == rtp->rtp_lookup_code_cache_isAstFormat &&
00659       code == rtp->rtp_lookup_code_cache_code) {
00660     // Use our cached mapping, to avoid the overhead of the loop below
00661     return rtp->rtp_lookup_code_cache_result;
00662   }
00663 
00664   for (pt = 0; pt < MAX_RTP_PT; ++pt) {
00665     if (static_RTP_PT[pt].code == code &&
00666       static_RTP_PT[pt].isAstFormat == isAstFormat) {
00667       rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat;
00668       rtp->rtp_lookup_code_cache_code = code;
00669       rtp->rtp_lookup_code_cache_result = pt;
00670       return pt;
00671     }
00672   }
00673   return -1;
00674 }

char* ast_rtp_lookup_mime_subtype int  isAstFormat,
int  code
 

Definition at line 676 of file rtp.c.

00676                                                              {
00677   int i;
00678 
00679   for (i = 0; i < sizeof mimeTypes/sizeof mimeTypes[0]; ++i) {
00680     if (mimeTypes[i].payloadType.code == code &&
00681    mimeTypes[i].payloadType.isAstFormat == isAstFormat) {
00682       return mimeTypes[i].subtype;
00683     }
00684   }
00685   return "";
00686 }

struct rtpPayloadType ast_rtp_lookup_pt struct ast_rtp rtp,
int  pt
 

Definition at line 643 of file rtp.c.

References ast_rtp_lookup_pt(), rtpPayloadType::code, rtpPayloadType::isAstFormat, and MAX_RTP_PT.

Referenced by ast_rtp_lookup_pt(), and ast_rtp_read().

00643                                                                      {
00644   if (pt < 0 || pt > MAX_RTP_PT) {
00645     struct rtpPayloadType result;
00646     result.isAstFormat = result.code = 0;
00647     return result; // bogus payload type
00648   }
00649   /* Gotta use our static one, since that's what we sent against */
00650   return static_RTP_PT[pt];
00651 }

struct ast_rtp* ast_rtp_new struct sched_context sched,
struct io_context io,
int  rtcpenable,
int  callbackmode
 

Definition at line 708 of file rtp.c.

References ast_io_add(), AST_IO_IN, ast_log(), ast_rtp_pt_default(), free, LOG_WARNING, and malloc.

00709 {
00710    struct ast_rtp *rtp;
00711    int x;
00712    int flags;
00713    int startplace;
00714    rtp = malloc(sizeof(struct ast_rtp));
00715    if (!rtp)
00716       return NULL;
00717    memset(rtp, 0, sizeof(struct ast_rtp));
00718    rtp->them.sin_family = AF_INET;
00719    rtp->us.sin_family = AF_INET;
00720    rtp->s = socket(AF_INET, SOCK_DGRAM, 0);
00721    rtp->ssrc = rand();
00722    rtp->seqno = rand() & 0xffff;
00723    if (rtp->s < 0) {
00724       free(rtp);
00725       ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
00726       return NULL;
00727    }
00728    if (sched && rtcpenable) {
00729       rtp->sched = sched;
00730       rtp->rtcp = ast_rtcp_new();
00731    }
00732    flags = fcntl(rtp->s, F_GETFL);
00733    fcntl(rtp->s, F_SETFL, flags | O_NONBLOCK);
00734    /* Find us a place */
00735    x = (rand() % (rtpend-rtpstart)) + rtpstart;
00736    x = x & ~1;
00737    startplace = x;
00738    for (;;) {
00739       /* Must be an even port number by RTP spec */
00740       rtp->us.sin_port = htons(x);
00741       if (rtp->rtcp)
00742          rtp->rtcp->us.sin_port = htons(x + 1);
00743       if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us)) &&
00744          (!rtp->rtcp || !bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))))
00745          break;
00746       if (errno != EADDRINUSE) {
00747          ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno));
00748          close(rtp->s);
00749          if (rtp->rtcp) {
00750             close(rtp->rtcp->s);
00751             free(rtp->rtcp);
00752          }
00753          free(rtp);
00754          return NULL;
00755       }
00756       x += 2;
00757       if (x > rtpend)
00758          x = (rtpstart + 1) & ~1;
00759       if (x == startplace) {
00760          ast_log(LOG_WARNING, "No RTP ports remaining\n");
00761          close(rtp->s);
00762          if (rtp->rtcp) {
00763             close(rtp->rtcp->s);
00764             free(rtp->rtcp);
00765          }
00766          free(rtp);
00767          return NULL;
00768       }
00769    }
00770    if (io && sched && callbackmode) {
00771       /* Operate this one in a callback mode */
00772       rtp->sched = sched;
00773       rtp->io = io;
00774       rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
00775    }
00776    ast_rtp_pt_default(rtp);
00777    return rtp;
00778 }

int ast_rtp_proto_register struct ast_rtp_protocol proto  ) 
 

Definition at line 1128 of file rtp.c.

References ast_log(), LOG_WARNING, ast_rtp_protocol::next, and ast_rtp_protocol::type.

01129 {
01130    struct ast_rtp_protocol *cur;
01131    cur = protos;
01132    while(cur) {
01133       if (cur->type == proto->type) {
01134          ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
01135          return -1;
01136       }
01137       cur = cur->next;
01138    }
01139    proto->next = protos;
01140    protos = proto;
01141    return 0;
01142 }

void ast_rtp_proto_unregister struct ast_rtp_protocol proto  ) 
 

Definition at line 1110 of file rtp.c.

References ast_rtp_protocol::next.

01111 {
01112    struct ast_rtp_protocol *cur, *prev;
01113    cur = protos;
01114    prev = NULL;
01115    while(cur) {
01116       if (cur == proto) {
01117          if (prev)
01118             prev->next = proto->next;
01119          else
01120             protos = proto->next;
01121          return;
01122       }
01123       prev = cur;
01124       cur = cur->next;
01125    }
01126 }

void ast_rtp_pt_clear struct ast_rtp rtp  ) 
 

Definition at line 571 of file rtp.c.

References rtpPayloadType::code, ast_rtp::current_RTP_PT, rtpPayloadType::isAstFormat, MAX_RTP_PT, ast_rtp::rtp_lookup_code_cache_code, ast_rtp::rtp_lookup_code_cache_isAstFormat, and ast_rtp::rtp_lookup_code_cache_result.

00572 {
00573   int i;
00574 
00575   for (i = 0; i < MAX_RTP_PT; ++i) {
00576     rtp->current_RTP_PT[i].isAstFormat = 0;
00577     rtp->current_RTP_PT[i].code = 0;
00578   }
00579 
00580   rtp->rtp_lookup_code_cache_isAstFormat = 0;
00581   rtp->rtp_lookup_code_cache_code = 0;
00582   rtp->rtp_lookup_code_cache_result = 0;
00583 }

void ast_rtp_pt_default struct ast_rtp rtp  ) 
 

Definition at line 585 of file rtp.c.

References rtpPayloadType::code, ast_rtp::current_RTP_PT, rtpPayloadType::isAstFormat, MAX_RTP_PT, ast_rtp::rtp_lookup_code_cache_code, ast_rtp::rtp_lookup_code_cache_isAstFormat, and ast_rtp::rtp_lookup_code_cache_result.

Referenced by ast_rtp_new().

00586 {
00587   int i;
00588   /* Initialize to default payload types */
00589   for (i = 0; i < MAX_RTP_PT; ++i) {
00590     rtp->current_RTP_PT[i].isAstFormat = static_RTP_PT[i].isAstFormat;
00591     rtp->current_RTP_PT[i].code = static_RTP_PT[i].code;
00592   }
00593 
00594   rtp->rtp_lookup_code_cache_isAstFormat = 0;
00595   rtp->rtp_lookup_code_cache_code = 0;
00596   rtp->rtp_lookup_code_cache_result = 0;
00597 }

struct ast_frame* ast_rtp_read struct ast_rtp rtp  ) 
 

Definition at line 363 of file rtp.c.

References AST_FORMAT_ADPCM, AST_FORMAT_ALAW, AST_FORMAT_G723_1, AST_FORMAT_G729A, AST_FORMAT_GSM, AST_FORMAT_ILBC, AST_FORMAT_MAX_AUDIO, AST_FORMAT_SLINEAR, AST_FORMAT_SPEEX, AST_FORMAT_ULAW, AST_FRAME_NULL, AST_FRAME_VIDEO, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_getformatname(), ast_log(), AST_RTP_CISCO_DTMF, AST_RTP_CN, AST_RTP_DTMF, ast_rtp_lookup_pt(), rtpPayloadType::code, CRASH, ast_frame::data, ast_frame::datalen, ast_rtp::dtmfcount, ast_rtp::f, ast_frame::frametype, rtpPayloadType::isAstFormat, ast_rtp::lastividtimestamp, ast_rtp::lastrxformat, ast_rtp::lastrxts, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, ast_frame::mallocd, ast_rtp::nat, ast_frame::offset, ast_rtp::rawdata, ast_rtp::resp, ast_rtp::s, ast_frame::samples, ast_frame::src, ast_frame::subclass, and ast_rtp::them.

00364 {
00365    int res;
00366    struct sockaddr_in sin;
00367    int len;
00368    unsigned int seqno;
00369    int payloadtype;
00370    int hdrlen = 12;
00371    int mark;
00372    unsigned int timestamp;
00373    unsigned int *rtpheader;
00374    static struct ast_frame *f, null_frame = { AST_FRAME_NULL, };
00375    struct rtpPayloadType rtpPT;
00376    
00377    len = sizeof(sin);
00378    
00379    /* Cache where the header will go */
00380    res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET,
00381                0, (struct sockaddr *)&sin, &len);
00382 
00383 
00384    rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
00385    if (res < 0) {
00386       ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno));
00387       if (errno == EBADF)
00388          CRASH;
00389       return &null_frame;
00390    }
00391    if (res < hdrlen) {
00392       ast_log(LOG_WARNING, "RTP Read too short\n");
00393       return &null_frame;
00394    }
00395    if (rtp->nat) {
00396       /* Send to whoever sent to us */
00397       if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
00398           (rtp->them.sin_port != sin.sin_port)) {
00399          memcpy(&rtp->them, &sin, sizeof(rtp->them));
00400          ast_log(LOG_DEBUG, "RTP NAT: Using address %s:%d\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
00401       }
00402    }
00403    /* Ignore if the other side hasn't been given an address
00404       yet.  */
00405    if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
00406       return &null_frame;
00407 
00408    /* Get fields */
00409    seqno = ntohl(rtpheader[0]);
00410    payloadtype = (seqno & 0x7f0000) >> 16;
00411    mark = seqno & (1 << 23);
00412    seqno &= 0xffff;
00413    timestamp = ntohl(rtpheader[1]);
00414 
00415 #if 0
00416    printf("Got RTP packet from %s:%d (type %d, seq %d, ts %d, len = %d)\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen);
00417 #endif   
00418    rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
00419    if (!rtpPT.isAstFormat) {
00420      // This is special in-band data that's not one of our codecs
00421      if (rtpPT.code == AST_RTP_DTMF) {
00422        /* It's special -- rfc2833 process it */
00423        f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
00424        if (f) return f; else return &null_frame;
00425      } else if (rtpPT.code == AST_RTP_CISCO_DTMF) {
00426        /* It's really special -- process it the Cisco way */
00427        f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
00428        if (f) return f; else return &null_frame;
00429      } else if (rtpPT.code == AST_RTP_CN) {
00430        /* Comfort Noise */
00431        f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
00432        if (f) return f; else return &null_frame;
00433      } else {
00434        ast_log(LOG_NOTICE, "Unknown RTP codec %d received\n", payloadtype);
00435        return &null_frame;
00436      }
00437    }
00438    rtp->f.subclass = rtpPT.code;
00439    if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO)
00440       rtp->f.frametype = AST_FRAME_VOICE;
00441    else
00442       rtp->f.frametype = AST_FRAME_VIDEO;
00443    rtp->lastrxformat = rtp->f.subclass;
00444 
00445    if (!rtp->lastrxts)
00446       rtp->lastrxts = timestamp;
00447 
00448    if (rtp->dtmfcount) {
00449 #if 0
00450       printf("dtmfcount was %d\n", rtp->dtmfcount);
00451 #endif      
00452       rtp->dtmfcount -= (timestamp - rtp->lastrxts);
00453       if (rtp->dtmfcount < 0)
00454          rtp->dtmfcount = 0;
00455 #if 0
00456       if (dtmftimeout != rtp->dtmfcount)
00457          printf("dtmfcount is %d\n", rtp->dtmfcount);
00458 #endif
00459    }
00460    rtp->lastrxts = timestamp;
00461 
00462    /* Send any pending DTMF */
00463    if (rtp->resp && !rtp->dtmfcount) {
00464       ast_log(LOG_DEBUG, "Sending pending DTMF\n");
00465       return send_dtmf(rtp);
00466    }
00467    rtp->f.mallocd = 0;
00468    rtp->f.datalen = res - hdrlen;
00469    rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
00470    rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
00471    if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) {
00472       switch(rtp->f.subclass) {
00473       case AST_FORMAT_ULAW:
00474       case AST_FORMAT_ALAW:
00475          rtp->f.samples = rtp->f.datalen;
00476          break;
00477       case AST_FORMAT_SLINEAR:
00478          rtp->f.samples = rtp->f.datalen / 2;
00479          break;
00480       case AST_FORMAT_GSM:
00481          rtp->f.samples = 160 * (rtp->f.datalen / 33);
00482          break;
00483       case AST_FORMAT_ILBC:
00484          rtp->f.samples = 240 * (rtp->f.datalen / 50);
00485          break;
00486       case AST_FORMAT_ADPCM:
00487          rtp->f.samples = rtp->f.datalen * 2;
00488          break;
00489       case AST_FORMAT_G729A:
00490          rtp->f.samples = rtp->f.datalen * 8;
00491          break;
00492       case AST_FORMAT_G723_1:
00493          rtp->f.samples = g723_samples(rtp->f.data, rtp->f.datalen);
00494          break;
00495       case AST_FORMAT_SPEEX:
00496               rtp->f.samples = 160;
00497          // assumes that the RTP packet contained one Speex frame
00498          break;
00499       default:
00500          ast_log(LOG_NOTICE, "Unable to calculate samples for format %s\n", ast_getformatname(rtp->f.subclass));
00501          break;
00502       }
00503    } else {
00504       /* Video -- samples is # of samples vs. 90000 */
00505       if (!rtp->lastividtimestamp)
00506          rtp->lastividtimestamp = timestamp;
00507       rtp->f.samples = timestamp - rtp->lastividtimestamp;
00508       rtp->lastividtimestamp = timestamp;
00509       if (mark)
00510          rtp->f.subclass |= 0x1;
00511       
00512    }
00513    rtp->f.src = "RTP";
00514    return &rtp->f;
00515 }

void ast_rtp_reload void   ) 
 

Definition at line 1338 of file rtp.c.

References ast_destroy(), ast_load(), ast_log(), ast_variable_retrieve(), ast_verbose(), LOG_WARNING, option_verbose, s, and VERBOSE_PREFIX_2.

Referenced by ast_module_reload(), and ast_rtp_init().

01339 {
01340    struct ast_config *cfg;
01341    char *s;
01342    rtpstart = 5000;
01343    rtpend = 31000;
01344    cfg = ast_load("rtp.conf");
01345    if (cfg) {
01346       if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
01347          rtpstart = atoi(s);
01348          if (rtpstart < 1024)
01349             rtpstart = 1024;
01350          if (rtpstart > 65535)
01351             rtpstart = 65535;
01352       }
01353       if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
01354          rtpend = atoi(s);
01355          if (rtpend < 1024)
01356             rtpend = 1024;
01357          if (rtpend > 65535)
01358             rtpend = 65535;
01359       }
01360       ast_destroy(cfg);
01361    }
01362    if (rtpstart >= rtpend) {
01363       ast_log(LOG_WARNING, "Unreasonable values for RTP start/end\n");
01364       rtpstart = 5000;
01365       rtpend = 31000;
01366    }
01367    if (option_verbose > 1)
01368       ast_verbose(VERBOSE_PREFIX_2 "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
01369 }

int ast_rtp_senddigit struct ast_rtp rtp,
char  digit
 

Definition at line 851 of file rtp.c.

References ast_log(), ast_rtp::dtmfmute, ast_rtp::lastts, LOG_NOTICE, LOG_WARNING, ast_rtp::s, ast_rtp::seqno, ast_rtp::ssrc, and ast_rtp::them.

00852 {
00853    unsigned int *rtpheader;
00854    int hdrlen = 12;
00855    int res;
00856    int ms;
00857    int pred;
00858    int x;
00859    char data[256];
00860 
00861    if ((digit <= '9') && (digit >= '0'))
00862       digit -= '0';
00863    else if (digit == '*')
00864       digit = 10;
00865    else if (digit == '#')
00866       digit = 11;
00867    else if ((digit >= 'A') && (digit <= 'D')) 
00868       digit = digit - 'A' + 12;
00869    else if ((digit >= 'a') && (digit <= 'd')) 
00870       digit = digit - 'a' + 12;
00871    else {
00872       ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
00873       return -1;
00874    }
00875    
00876 
00877    /* If we have no peer, return immediately */ 
00878    if (!rtp->them.sin_addr.s_addr)
00879       return 0;
00880 
00881    gettimeofday(&rtp->dtmfmute, NULL);
00882    rtp->dtmfmute.tv_usec += (500 * 1000);
00883    if (rtp->dtmfmute.tv_usec > 1000000) {
00884       rtp->dtmfmute.tv_usec -= 1000000;
00885       rtp->dtmfmute.tv_sec += 1;
00886    }
00887 
00888    ms = calc_txstamp(rtp);
00889    /* Default prediction */
00890    pred = rtp->lastts + ms * 8;
00891    
00892    /* Get a pointer to the header */
00893    rtpheader = (unsigned int *)data;
00894    rtpheader[0] = htonl((2 << 30) | (1 << 23) | (101 << 16) | (rtp->seqno++));
00895    rtpheader[1] = htonl(rtp->lastts);
00896    rtpheader[2] = htonl(rtp->ssrc); 
00897    rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0));
00898    for (x=0;x<4;x++) {
00899       if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
00900          res = sendto(rtp->s, (void *)rtpheader, hdrlen + 4, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
00901          if (res <0) 
00902             ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
00903    #if 0
00904       printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
00905    #endif      
00906       }
00907       if (x ==0) {
00908          /* Clear marker bit and increment seqno */
00909          rtpheader[0] = htonl((2 << 30)  | (101 << 16) | (rtp->seqno++));
00910          /* Make duration 800 (100ms) */
00911          rtpheader[3] |= htonl((800));
00912          /* Set the End bit for the last 3 */
00913          rtpheader[3] |= htonl((1 << 23));
00914       }
00915    }
00916    return 0;
00917 }

void ast_rtp_set_callback struct ast_rtp rtp,
ast_rtp_callback  callback
 

Definition at line 155 of file rtp.c.

References ast_rtp_callback, and ast_rtp::callback.

00156 {
00157    rtp->callback = callback;
00158 }

void ast_rtp_set_data struct ast_rtp rtp,
void *  data
 

Definition at line 150 of file rtp.c.

References ast_rtp::data.

00151 {
00152    rtp->data = data;
00153 }

void ast_rtp_set_m_type struct ast_rtp rtp,
int  pt
 

Definition at line 602 of file rtp.c.

References rtpPayloadType::code, ast_rtp::current_RTP_PT, and MAX_RTP_PT.

00602                                                      {
00603   if (pt < 0 || pt > MAX_RTP_PT) return; // bogus payload type
00604 
00605   if (static_RTP_PT[pt].code != 0) {
00606     rtp->current_RTP_PT[pt] = static_RTP_PT[pt];
00607   }
00608 } 

void ast_rtp_set_peer struct ast_rtp rtp,
struct sockaddr_in *  them
 

Definition at line 788 of file rtp.c.

References ast_rtp::rtcp, ast_rtcp::them, and ast_rtp::them.

00789 {
00790    rtp->them.sin_port = them->sin_port;
00791    rtp->them.sin_addr = them->sin_addr;
00792    if (rtp->rtcp) {
00793       rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1);
00794       rtp->rtcp->them.sin_addr = them->sin_addr;
00795    }
00796 }

void ast_rtp_set_rtpmap_type struct ast_rtp rtp,
int  pt,
char *  mimeType,
char *  mimeSubtype
 

Definition at line 612 of file rtp.c.

References ast_rtp::current_RTP_PT, MAX_RTP_PT, and subtype.

00613                                              {
00614   int i;
00615 
00616   if (pt < 0 || pt > MAX_RTP_PT) return; // bogus payload type
00617 
00618   for (i = 0; i < sizeof mimeTypes/sizeof mimeTypes[0]; ++i) {
00619     if (strcasecmp(mimeSubtype, mimeTypes[i].subtype) == 0 &&
00620    strcasecmp(mimeType, mimeTypes[i].type) == 0) {
00621       rtp->current_RTP_PT[pt] = mimeTypes[i].payloadType;
00622       return;
00623     }
00624   }
00625 } 

void ast_rtp_setnat struct ast_rtp rtp,
int  nat
 

Definition at line 160 of file rtp.c.

References ast_rtp::nat.

00161 {
00162    rtp->nat = nat;
00163 }

int ast_rtp_settos struct ast_rtp rtp,
int  tos
 

Definition at line 780 of file rtp.c.

References ast_log(), LOG_WARNING, and ast_rtp::s.

00781 {
00782    int res;
00783    if ((res = setsockopt(rtp->s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))) 
00784       ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
00785    return res;
00786 }

void ast_rtp_stop struct ast_rtp rtp  ) 
 

Definition at line 810 of file rtp.c.

References ast_rtp::rtcp, ast_rtcp::them, and ast_rtp::them.

00811 {
00812    memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
00813    memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
00814    if (rtp->rtcp) {
00815       memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
00816       memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port));
00817    }
00818 }

int ast_rtp_write struct ast_rtp rtp,
struct ast_frame f
 

Definition at line 997 of file rtp.c.

References AST_FORMAT_ALAW, AST_FORMAT_G723_1, AST_FORMAT_G729A, AST_FORMAT_GSM, AST_FORMAT_H261, AST_FORMAT_H263, AST_FORMAT_ILBC, AST_FORMAT_SPEEX, AST_FORMAT_ULAW, AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_frdup(), ast_getformatname(), ast_log(), ast_rtp_lookup_code(), ast_smoother_feed(), ast_smoother_free(), ast_smoother_new(), ast_smoother_read(), ast_frame::datalen, ast_frame::frametype, ast_rtp::lasttxformat, LOG_DEBUG, LOG_WARNING, ast_frame::offset, ast_rtp::smoother, ast_frame::subclass, and ast_rtp::them.

00998 {
00999    struct ast_frame *f;
01000    int codec;
01001    int hdrlen = 12;
01002    int subclass;
01003    
01004 
01005    /* If we have no peer, return immediately */ 
01006    if (!rtp->them.sin_addr.s_addr)
01007       return 0;
01008 
01009    /* If there is no data length, return immediately */
01010    if (!_f->datalen) 
01011       return 0;
01012    
01013    /* Make sure we have enough space for RTP header */
01014    if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO)) {
01015       ast_log(LOG_WARNING, "RTP can only send voice\n");
01016       return -1;
01017    }
01018 
01019    subclass = _f->subclass;
01020    if (_f->frametype == AST_FRAME_VIDEO)
01021       subclass &= ~0x1;
01022 
01023    codec = ast_rtp_lookup_code(rtp, 1, subclass);
01024    if (codec < 0) {
01025       ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass));
01026       return -1;
01027    }
01028 
01029    if (rtp->lasttxformat != subclass) {
01030       /* New format, reset the smoother */
01031       ast_log(LOG_DEBUG, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass));
01032       rtp->lasttxformat = subclass;
01033       if (rtp->smoother)
01034          ast_smoother_free(rtp->smoother);
01035       rtp->smoother = NULL;
01036    }
01037 
01038 
01039    switch(subclass) {
01040    case AST_FORMAT_ULAW:
01041    case AST_FORMAT_ALAW:
01042       if (!rtp->smoother) {
01043          rtp->smoother = ast_smoother_new(160);
01044       }
01045       if (!rtp->smoother) {
01046          ast_log(LOG_WARNING, "Unable to create smoother :(\n");
01047          return -1;
01048       }
01049       ast_smoother_feed(rtp->smoother, _f);
01050       
01051       while((f = ast_smoother_read(rtp->smoother)))
01052          ast_rtp_raw_write(rtp, f, codec);
01053       break;
01054    case AST_FORMAT_G729A:
01055       if (!rtp->smoother) {
01056          rtp->smoother = ast_smoother_new(20);
01057       }
01058       if (!rtp->smoother) {
01059          ast_log(LOG_WARNING, "Unable to create g729 smoother :(\n");
01060          return -1;
01061       }
01062       ast_smoother_feed(rtp->smoother, _f);
01063       
01064       while((f = ast_smoother_read(rtp->smoother)))
01065          ast_rtp_raw_write(rtp, f, codec);
01066       break;
01067    case AST_FORMAT_GSM:
01068       if (!rtp->smoother) {
01069          rtp->smoother = ast_smoother_new(33);
01070       }
01071       if (!rtp->smoother) {
01072          ast_log(LOG_WARNING, "Unable to create GSM smoother :(\n");
01073          return -1;
01074       }
01075       ast_smoother_feed(rtp->smoother, _f);
01076       while((f = ast_smoother_read(rtp->smoother)))
01077          ast_rtp_raw_write(rtp, f, codec);
01078       break;
01079    case AST_FORMAT_ILBC:
01080       if (!rtp->smoother) {
01081          rtp->smoother = ast_smoother_new(50);
01082       }
01083       if (!rtp->smoother) {
01084          ast_log(LOG_WARNING, "Unable to create ILBC smoother :(\n");
01085          return -1;
01086       }
01087       ast_smoother_feed(rtp->smoother, _f);
01088       while((f = ast_smoother_read(rtp->smoother)))
01089          ast_rtp_raw_write(rtp, f, codec);
01090       break;
01091    default: 
01092       ast_log(LOG_WARNING, "Not sure about sending format %s packets\n", ast_getformatname(subclass));
01093       // fall through to...
01094    case AST_FORMAT_H261:
01095    case AST_FORMAT_H263:
01096    case AST_FORMAT_G723_1:
01097    case AST_FORMAT_SPEEX:
01098            // Don't buffer outgoing frames; send them one-per-packet:
01099       if (_f->offset < hdrlen) {
01100          f = ast_frdup(_f);
01101       } else {
01102          f = _f;
01103       }
01104       ast_rtp_raw_write(rtp, f, codec);
01105    }
01106       
01107    return 0;
01108 }


Generated on Sun Apr 18 23:34:17 2004 for Asterisk by doxygen 1.3.6-20040222