home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / general / procssng / ccs / ccs-11tl.lha / lbl / lib / rtp_subr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-09  |  14.1 KB  |  510 lines

  1. /*    RTP_SUBR . C
  2. #
  3. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  4.  
  5. This software is copyright (C) by the Lawrence Berkeley Laboratory.
  6. Permission is granted to reproduce this software for non-commercial
  7. purposes provided that this notice is left intact.
  8.  
  9. It is acknowledged that the U.S. Government has rights to this software
  10. under Contract DE-AC03-765F00098 between the U.S.  Department of Energy
  11. and the University of California.
  12.  
  13. This software is provided as a professional and academic contribution
  14. for joint exchange. Thus, it is experimental, and is provided ``as is'',
  15. with no warranties of any kind whatsoever, no support, no promise of
  16. updates, or printed documentation. By using this software, you
  17. acknowledge that the Lawrence Berkeley Laboratory and Regents of the
  18. University of California shall have no liability with respect to the
  19. infringement of other copyrights by any part of this software.
  20.  
  21. For further information about this notice, contact William Johnston,
  22. Bld. 50B, Rm. 2239, Lawrence Berkeley Laboratory, Berkeley, CA, 94720.
  23. (wejohnston@lbl.gov)
  24.  
  25. For further information about this software, contact:
  26.     Jin Guojun
  27.     Bld. 50B, Rm. 2275, Lawrence Berkeley Laboratory, Berkeley, CA, 94720.
  28.     g_jin@lbl.gov
  29.  
  30. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  31. %
  32. % Author:    Jin Guojun - LBL    3/1/93
  33. */
  34.  
  35. #define    KERNEL
  36. #include "net_need.h"
  37.  
  38. #ifndef    RTP_MARGIN
  39. #define    RTP_MARGIN    16
  40. #endif
  41.  
  42. #define    RTP_OPT_LEN    ((sizeof(rtcpsdeschdr_t) + sizeof(rtcp_movie_t)) >> 2)
  43. #define    RTCP_MOVIEHDR_LEN    (sizeof(rtphdr_t) >> 2)
  44. #define    TOTAL_HEADER_BYTES    TOTAL_RTP_MOVIE_HEADER_BYTES
  45.  
  46. #ifndef    Max_FRAGMENTS
  47. #define    Max_FRAGMENTS    144
  48. #endif
  49.  
  50. #ifndef    MaxRTPchannel
  51. #define    MaxRTPchannel    64
  52. #endif
  53.  
  54. #ifdef    RTP_READ_ACCURACY
  55. #define    DEFAULT_TOLERANCE    85
  56. #else
  57. #define    DEFAULT_TOLERANCE    106
  58. #endif
  59.  
  60.  
  61. static    rtp_flow_ctrl    *rtp_resource[MaxRTPchannel][2];
  62. static    int    mislist[256];    /* shared resource, so get it right way    */
  63. extern    struct sockaddr_in *get_soaddr();
  64.  
  65.  
  66. /* called at open RTP channel    */
  67. rtp_request_port(int flow_id, int port, int mode, int flags)
  68. {
  69. register int    i=flow_id;
  70.     if (i < 0 || i >= MaxRTPchannel)    return    -1;
  71.     {
  72.     register rtp_flow_ctrl    **rrp = rtp_resource[i];
  73.     int    use_ip = flags & RTP_OVERIP ? IP_HDR_MINLEN : 0;
  74.     int    n=sizeof(n), packet_size;
  75.     rtphdr_t    *rtphdrp;
  76.     rtcp_movie_t    *rtcp_mhp;
  77.     rtcpsdeschdr_t    *rtcp_sdp;
  78.     if (mode < 0 || mode)    {    /* transmittor    */
  79.     rrp[1] = (rtp_flow_ctrl*) ZALLOC(sizeof(rtp_flow_ctrl), 1, "flow_ctrl");
  80.     rrp[1]->flag = flags;
  81.     rrp[1]->len = 0;
  82. /*    rrp[1]->port = port;    */
  83.  
  84.     if ((i=getsockopt(i, SOL_SOCKET, SO_SNDBUF, &packet_size, &n)) < 0)
  85.         return    i;
  86.     if (!(rrp[1]->buf=NZALLOC(packet_size, 1, "rtp")))    return    -1;
  87.  
  88.     rrp[1]->aux = packet_size - use_ip - TOTAL_HEADER_BYTES - RTP_MARGIN;
  89.     get_soaddr(1, &rrp[1]->soaddr);
  90.     if (use_ip)    init_ip_header(rrp[1]->buf, flow_id, 5);
  91.     rtphdrp = rrp[1]->rtphdrp= (rtphdr_t *) (rrp[1]->buf + use_ip);
  92.     rtcp_sdp = (rtcpsdeschdr_t*)(rtphdrp + 1);
  93.     rtcp_mhp = (rtcp_movie_t *)(rtcp_sdp + 1);
  94.  
  95.     rtphdrp->rh_vers = RTP_VERSION;
  96.     rtphdrp->rh_flow = flow_id;
  97.     rtphdrp->rh_opts = True;
  98.     rtphdrp->rh_content = RTPCONT_MOVIE;
  99.     rtcp_sdp->rtsh_fin = 1;
  100.     rtcp_sdp->rtsh_type = RTPOPT_SDES;
  101.     rtcp_sdp->rtsh_optlen = RTP_OPT_LEN;
  102.     rtcp_sdp->rtsh_class = 0;
  103.     rtcp_sdp->rtsh_msglen = RTCP_MOVIEHDR_LEN;
  104.     rtcp_mhp->group_id = kludgeGID;
  105.     rtcp_mhp->ttl = 33;    /* 33 ms    */
  106.     rtcp_mhp->linewidth = 0;
  107.     rtcp_mhp->size = packet_size;
  108.     }
  109.     if (mode <= 0)    {    /* reciever    */
  110.     rrp[0] = (rtp_flow_ctrl*) ZALLOC(1, sizeof(rtp_flow_ctrl), "flow_ctrl");
  111.     rrp[0]->flag = flags;
  112.     rrp[0]->len = 0;
  113.     rrp[0]->list = mislist;
  114.     rrp[0]->port = port;
  115.     rrp[0]->group_id = kludgeGID;
  116.  
  117.     if ((i=getsockopt(i, SOL_SOCKET, SO_RCVBUF, &packet_size, &n)) < 0)
  118.         return    i;
  119.     if (!(rrp[0]->buf=NZALLOC(packet_size,
  120.             flags&RTP_LOCALBUF ? Max_FRAGMENTS : 1, "rtp")))
  121.         return    -1;
  122.     get_soaddr(0, &rrp[0]->soaddr);
  123.     rrp[0]->aux = packet_size;
  124.     rrp[0]->rtphdrp = (rtphdr_t *) (rrp[0]->buf + use_ip);
  125.     }
  126.  
  127.     i = flow_id;
  128. #ifdef    AUTO_CONNECT
  129.     if (flags & RTP_OVERTCP)    {
  130. #ifdef    LWP
  131.     lwpcreate
  132. #else
  133.     switch (fork())    {
  134.     case    -1:
  135.     case    0:    /* parent    */
  136.     default:    /* child    */
  137.         exit    (0);
  138.     }
  139. #endif
  140.     }
  141. #endif
  142.     return    i;    /* should be port # ?    */
  143.     }
  144. }
  145.  
  146. void    /*    used for close RTP descriptot    */
  147. rtp_release_port(register int    fd)
  148. {
  149. register rtp_flow_ctrl    **rrp = rtp_resource[fd];
  150. if (fd < 0 || fd >= MaxRTPchannel)
  151.     prgmerr(0, "RTP port %d is out of range", fd);
  152. else    {
  153.     if (rrp[0])
  154.     free(rrp[0]->buf),    free(rrp[0]);
  155.     if (rrp[1])
  156.     free(rrp[1]->buf),    free(rrp[1]);
  157. }
  158. }
  159.  
  160.  
  161. rtp_write(int s, VType* data, int datalen)
  162. {
  163. if (s < 0 || s >= MaxRTPchannel)    return    -1;
  164. {
  165. rtp_flow_ctrl*    rrp = rtp_resource[s][1];
  166. char    *vp=rrp->buf;
  167. rtphdr_t    *rtphdrp=rrp->rtphdrp;
  168. rtcpsdeschdr_t    *rtcp_sdp=(rtcpsdeschdr_t *)(rtphdrp+1);
  169. rtcp_movie_t    *rtcp_mhp=(rtcp_movie_t *)(rtcp_sdp+1);
  170. int    hdrlen = (char*)(rtcp_mhp + 1) - vp,
  171.     i=sizeof(i), n, linewidth=rtcp_mhp->linewidth, packet_size=rrp->aux;
  172. bool    use_ip = rrp->flag & RTP_OVERIP;
  173.  
  174.     rtcp_mhp->total_len = datalen;
  175.     rtcp_mhp->fragment = datalen / packet_size;
  176. #ifndef    OLD_RTP
  177.     rrp->rtpmsg.msg_name = (caddr_t)&rrp->soaddr;
  178.     rrp->rtpmsg.msg_namelen = sizeof(rrp->soaddr);
  179.     rrp->rtpmsg.msg_iov = rrp->rtpiov;
  180.     rrp->rtpmsg.msg_iovlen = 2;    /* rtphdr & data    */
  181.     rrp->rtpiov[0].iov_base = (caddr_t)vp;
  182.     rrp->rtpiov[0].iov_len = hdrlen;
  183. #endif
  184.     time((time_t*)&rtphdrp->rh_ts);
  185.  
  186.     if (rrp->flag & RTP_OVERTCP)    {
  187.         if (write(s, vp, hdrlen) != hdrlen)    return    -1;
  188.         return    write(s, data, datalen);
  189.     }
  190.     if (use_ip)    set_ip_dsize(vp, hdrlen + packet_size, 0);
  191.  
  192.     for (rtcp_mhp->fin_p=i=0; datalen > 0; i++)    {
  193.         rtcp_mhp->pkt_id = i;
  194.         n = packet_size * i;
  195.         if (linewidth)    {
  196.         register int    y = n / linewidth;
  197.             rtcp_mhp->offset.cord.y = y;
  198.             rtcp_mhp->offset.cord.x = n - y * linewidth;
  199.         } else    rtcp_mhp->offset.pos = n;
  200. #ifdef    _DEBUG_
  201.     message("GID %d : offset.pos = %d\n",
  202.         rtcp_mhp->group_id, rtcp_mhp->offset.pos);
  203. #endif
  204.         if (datalen < packet_size)    {
  205.             packet_size = datalen;
  206.             rtphdrp->rh_sync = rtcp_mhp->fin_p = 1;
  207.             if (use_ip)
  208.                 set_ip_dsize(vp, hdrlen + packet_size, 0);
  209.         }
  210.         datalen -= packet_size;
  211. #ifdef    OLD_RTP
  212.         memcpy(vp + hdrlen, (byte*)data + n, packet_size);
  213. /*
  214.         if (write(s, vp, packet_size) < 0)
  215. */
  216. rtp_retry:    if (sendto(s, vp, packet_size, 0, &rrp->soaddr, sizeof(rrp->soaddr)) < 0)
  217. #else
  218.         rrp->rtpiov[1].iov_base = (caddr_t)data + n;
  219.         rrp->rtpiov[1].iov_len = packet_size;
  220. rtp_retry:    if (sendmsg(s, &rrp->rtpmsg, 0) < 0)
  221. #endif
  222.             if (errno == ENOBUFS)    {
  223.             udelay(0, -1);    goto    rtp_retry;
  224.             } else    return    prgmerr(0, "rtp gwrite");
  225.         if (n=rrp->udelay)    {
  226.             n >>= 4;    /* call cost factor    */
  227.             for (n += !n; n--;)
  228.             time((time_t*)&rtphdrp->rh_ts);
  229.         }
  230.     }
  231.     datalen = rtcp_mhp->total_len;
  232.     rtcp_mhp->total_len = 0;
  233.     if (use_ip)    set_ip_dsize(vp, hdrlen, 0);
  234.     for (n=hdrlen, i=2; i--;)
  235.         sendto(s, vp, n, 0, &rrp->soaddr, sizeof(rrp->soaddr));
  236.     rtcp_mhp->group_id++;
  237. return    datalen;
  238. }
  239. }
  240.  
  241. rtp_read(int    s, char    *data, int tolerance)
  242. {
  243. static char*    lastbuf;
  244. rtp_flow_ctrl*    rrp = rtp_resource[s][0];
  245. rtphdr_t    *rtphdrp = rrp->rtphdrp;
  246. bool    video = lastbuf==data && rrp->flag & RTP_SLOWVIDEO;
  247. int    l, packet_size = rrp->aux, nrcv=rrp->miss=0, slen=sizeof(rrp->soaddr),
  248.     gid, total_recv=0,
  249.     use_ip=(char*)rtphdrp - rrp->buf, hdrlen=TOTAL_HEADER_BYTES + use_ip;
  250. register int    n = rrp->flag & RTP_LOCALBUF;
  251. char    *buf,    /* working area    and data pointer */
  252.     *dp = buf = n | video ? rrp->buf + hdrlen : data,
  253.     *localbuf = n ? dp : 0;    /* data assembling area    */
  254.  
  255. #ifdef    RTP_CAN_USETCP
  256.     if (rrp->flag & RTP_OVERTCP)    {
  257.         if (read(s=rrp->tcp, rrp->buf, hdrlen) < hdrlen)
  258.             return    -1;
  259.         rtcp_sdp = 0;
  260.         rtcp_getoptions(rtphdrp, 1, RTPOPT_SDES, &rtcp_sdp);
  261.         if (!rtcp_sdp)    return    -1;
  262.         rmopt = (rtcp_movie_t *)(rtcp_sdp + 1);
  263.         return    read(s, data, rmopt->total_len);
  264.     }
  265. #endif
  266.     /* require at least one buffer */
  267.     if (!((int)localbuf | (int)data)) {
  268.         errno = ENOBUFS;    return    -1;
  269.     }
  270.  
  271. #ifndef    OLD_RTP
  272.     rrp->rtpmsg.msg_name = (caddr_t)&rrp->soaddr;
  273.     rrp->rtpmsg.msg_namelen = sizeof(rrp->soaddr);
  274.     rrp->rtpmsg.msg_iov = rrp->rtpiov;
  275.     rrp->rtpmsg.msg_iovlen = 2;    /* rtphdr & data    */
  276.     rrp->rtpiov[0].iov_base = (caddr_t)rtphdrp;
  277.     rrp->rtpiov[0].iov_len = hdrlen;
  278. #endif
  279.     if (tolerance < 25 || tolerance > 100)    /* ? rrp->ignore_err */
  280.         tolerance = DEFAULT_TOLERANCE;
  281. #ifndef    RTP_READ_ACCURACY
  282.     else    tolerance += tolerance >> 2;    /* 1.25 / 1.28    */
  283. #endif
  284.     if (rrp->len)    {
  285.         if (!video && !localbuf) /* move out small saving area */
  286.         /*    message("move out %d from %x to %x\n",
  287.                 rrp->len, rrp->buf + hdrlen, dp),    */
  288.             memcpy(dp, rrp->buf + hdrlen, rrp->len);
  289.         goto    addata;
  290.     }
  291.  
  292.     Loop    {
  293. rtcpsdeschdr_t    *rtcp_sdp;
  294. rtcp_movie_t    *rmopt;
  295. #define    dsp    rrp->header_len
  296. /*
  297.     l = read(s, rtphdrp, packet_size);
  298. */
  299. #if defined OLD_RTP | defined MAYBE_FAST
  300.     l = recvfrom(s, rtphdrp, packet_size, 0, &rrp->soaddr, &slen);
  301. #else
  302.     rrp->rtpiov[1].iov_base = (caddr_t)dp;
  303.     rrp->rtpiov[1].iov_len = packet_size;
  304.     l = recvmsg(s, &rrp->rtpmsg, 0);
  305. #endif
  306.     if (l < 1)    return    l;
  307.  
  308. addata:    rtcp_sdp = 0;
  309.     dsp = rtcp_getoptions(rtphdrp, 1, RTPOPT_SDES, &rtcp_sdp);
  310.     if (!rtcp_sdp)    {
  311.         message("strange RTP movie packet");
  312.         continue;
  313.     }
  314.     rmopt = (rtcp_movie_t *)(rtcp_sdp + 1);
  315.     gid = n = rmopt->group_id;
  316. #ifdef    _DEBUG_
  317.     message("GID %d : rev = %d, dsp = %d\n", n, l, dsp);
  318. #endif
  319.     if (n && n < rrp->group_id)    /* n=0 is next gid loop    */
  320.         continue;
  321.     if (!rmopt->total_len)    goto    rend;
  322.  
  323.     l -= dsp;    /* data length    */
  324.     if (n > rrp->group_id)    {    /* new group coming in    */
  325.         rrp->group_id = n;    /* == rmopt->group_id;    */
  326.         rrp->len = l;
  327.         if (!video && localbuf && dp != buf)    /* not buffered    */
  328.         /* save first packet in small buffer for next frame */
  329.         /* message("save first packet %d from %x to %x\n",
  330.             l, dp, rrp->buf + hdrlen),    */
  331.             memcpy(rrp->buf + hdrlen, dp, l);
  332. exam:        n = total_recv -
  333. #ifdef    RTP_READ_ACCURACY
  334.                 rmopt->total_len * tolerance / 100;
  335. #else
  336.                 (rmopt->total_len * tolerance >> 7);
  337. #endif
  338.         if (n >= 0 || data == lastbuf) {
  339.             lastbuf = data;
  340.             if (localbuf && data)    /* require extra copy    */{
  341.             /*    message("extra copy    ");    */
  342.                 memcpy(data, localbuf, rmopt->total_len);
  343.             }
  344.             return    total_recv;
  345.         }
  346. #ifdef    NO_ERROR_RERURN
  347.         nrcv = 0;    /* discard old group    */
  348. #else
  349.         return    n;
  350. #endif
  351.     }
  352.     if (n=rmopt->linewidth)
  353.         n = rmopt->offset.cord.y * n + rmopt->offset.cord.x;
  354.     else    n = rmopt->offset.pos;
  355.     total_recv += l;
  356.  
  357.     if (!video)    {
  358.         if (n != dp - buf && tolerance < DEFAULT_TOLERANCE)
  359.     /*    message("move lost packet %d from %x to %x+%d\n",
  360.             l, dp, buf, n),    */
  361.         memcpy(buf + n, dp, l);    /* lossing packet, so    */
  362.     /* we are in the loosing area, and to be moved to the right place */
  363.         /* else    discard frame anyway    */
  364.  
  365.         if (n+l > rmopt->total_len && !localbuf)
  366.         dp = buf;    /* let last packet go to small local area */
  367.         else if (n >= dp - buf)
  368.         dp = buf + n + l;    /* point to next block    */
  369.         /* otherwise, don't move the data pointer    */
  370.     } else    memcpy(data + n, dp, l);
  371.  
  372. #ifdef    _DEBUG_
  373.     message("receive %d : total %d (%d) (pos %d)\n",
  374.         l, total_recv, rmopt->fin_p, n);
  375. #endif
  376.  
  377.     {    register int    w = rmopt->pkt_id - nrcv;
  378.     /* ignore case k < 0 which indicates retransmission    */
  379.     if (w > 0) {    /* missing packet, and packet arrives in order    */
  380. #ifdef    RTP_INTERPOLATION
  381.         if (rrp->flag & RTP_NOINPOLA)
  382.         while (w--)    mislist[rrp->miss++] = nrcv++;
  383.         else if (localbuf)    {    /* not true anymore    */
  384.         while (w--)    nrcv++,
  385.             memcpy(data + n - w*l, localbuf + n - w*l, l);
  386.         } else if (data != lastbuf)    {
  387.         while (w--)    nrcv++,
  388.             memcpy(data + n - w*l, lastbuf + n - w*l, l);
  389.         }
  390.         /* otherwise, treat it as video which uses same buf    */
  391. #else
  392.     while (w--)    mislist[rrp->miss++] = nrcv++;
  393. #endif
  394.     }    else    nrcv++;
  395.     }
  396.  
  397.     if (n=rmopt->size > packet_size) {    /* should be in connection */
  398.             /* and should not happen at last packet !!!    */
  399.         packet_size = rrp->aux = n;
  400.         if (!localbuf)    {
  401.             verify_buffer_size(&rrp->buf, n, 1, No);
  402.             if (!rrp->buf)
  403.                 return    -1;
  404.             rtphdrp = (rtphdr_t*)(rrp->buf + use_ip);
  405.             if (video)    buf = rrp->buf + hdrlen;
  406.         }
  407.         if ((rrp=rtp_resource[s][1]) && n < rrp->aux)
  408.             rrp->aux = n;
  409.         rrp = rtp_resource[s][0];
  410.     }    /* end initializing buffer agreement    */
  411.  
  412.     n = rmopt->total_len + hdrlen;
  413.     if (localbuf && n > pointer_buffer_size(rrp->buf))    {
  414.         if (!(rrp->buf = realloc(rrp->buf, n)))
  415.             return    -1;
  416.         buf = localbuf = rrp->buf + hdrlen;
  417.     }    /* end resizing local buffer    */
  418.  
  419. rend:    if (rmopt->fin_p)    {
  420.         rrp->group_id = ++gid;
  421.         rrp->len = 0;
  422.         goto    exam;
  423.     }
  424.     }
  425. }
  426.  
  427.  
  428. #ifndef    MIN_RCVBUF
  429. #define    MIN_RCVBUF    1024
  430. #endif
  431.  
  432. rtp_open(char *host, char *port, int carrier, int mode, int buf_size)
  433. {
  434. int    s, rev = !(mode && ~mode), flag = RTP_LOCALBUF;
  435.  
  436. if (!host && mode)
  437.     return    prgmerr(0, "no host name for transmission");
  438.  
  439. if (carrier != SOCK_RAW && carrier != SOCK_STREAM)
  440.     carrier = SOCK_DGRAM;    /* deafult carrieer    */
  441. if (!port)    port = "1920";
  442.  
  443. if (buf_size < MIN_RCVBUF && rev)
  444.     buf_size = Max_WINDOW_SIZE, flag = No;
  445. else if (buf_size < 512 + TOTAL_HEADER_BYTES ||
  446.     buf_size > UDP_BUF_LIMIT && carrier != SOCK_STREAM)
  447.     buf_size = FRAGMENT_SIZE << rev;
  448.  
  449. s = build_socket(host, port, carrier, mode, &buf_size, No, NULL);
  450.  
  451. if (carrier == SOCK_STREAM)    flag = RTP_OVERTCP;
  452. else if (carrier == SOCK_RAW)    flag |= RTP_OVERIP;
  453. return    rtp_request_port(s, get_soaddr(1, NULL)->sin_port, mode, flag);
  454. }
  455.  
  456. void
  457. rtp_close(int    fd)
  458. {
  459. rtp_release_port(fd);
  460. close(fd);
  461. }
  462.  
  463. rtp_setoption(int s, int option, int op1, int op2)
  464. {
  465. if (s < 0 || s >= MaxRTPchannel)    return    -1;
  466. {
  467. rtp_flow_ctrl*    rrp = rtp_resource[s][1];    /* transmittor    */
  468.  
  469. switch (option)    {
  470. case RTP_OPT_UDELAY:
  471.     rrp->udelay = op1;    break;
  472. case RTP_OPT_HDRLEN: /* user data header need to be repeated in each packet */
  473.     rrp->header_len = op1;    break;
  474. case RTP_OPT_GROUPID:
  475.     rrp->group_id = op1;    break;
  476. case RTP_OPT_FLAGS:
  477.     rrp->flag = op1;    break;
  478. case RTP_OPT_ADDFLAG:
  479.     rrp->flag |= op1;    break;
  480. case RTP_OPT_QOS:    {
  481. rtcp_movie_t*    opthdt = (rtcp_movie_t*)(rrp->buf + sizeof(rtphdr_t) +
  482.             sizeof(rtcpsdeschdr_t));
  483.     opthdt->fin_p = False;    break;    /* not right now !!!    */
  484. }
  485. }
  486.  
  487. }
  488. return    0;
  489. }
  490.  
  491. rtp_flow_ctrl    *
  492. get_rtp_control(int fd, int rw /* 0=r */)
  493. {
  494.     if (fd < 0 || fd >= MaxRTPchannel)    return    NULL;
  495.     return    rtp_resource[fd][rw];
  496. }
  497.  
  498. rtp_receive_cmd(int s, rtcp_movie_t *rmp)
  499. {
  500. if (s < 0 || s >= MaxRTPchannel)    return  0;
  501. {
  502. register rtcp_movie_t*    romp = (rtcp_movie_t*)( (char*)
  503.         (rtp_resource[s][0]->rtphdrp + 1) + sizeof(rtcpsdeschdr_t) );
  504.     if (rmp)    *rmp = *romp;
  505.     else    rmp = romp;
  506. return    rmp->command;
  507. }
  508. }
  509.  
  510.