view stream/freesdp/parser.c @ 34544:36ef1a75aa48

Some hacks to allow stream_ffmpeg to compile against newer FFmpeg. Keep the old code so that it is still possible to compile against older FFmpeg without using internal API.
author reimar
date Sat, 28 Jan 2012 13:47:01 +0000
parents 08a90b0e44e1
children
line wrap: on
line source

/*
  This file is part of FreeSDP
  Copyright (C) 2001,2002,2003 Federico Montesino Pouzols <fedemp@altern.org>

  FreeSDP 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 of the License, or
  (at your option) any later version.

  This program 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 General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Benjamin Zores, (C) 2006
    added support in parser for the a=control: lines.
    added support in parser for the a=range: lines.
*/

/**
 * @file
 *
 * @short Parsing module implementation.
 *
 * This file implements the parsing routine <code>fsdp_parse</code>
 * and the <code>fsdp_get_xxxx</code> routines that allow to get the
 * session properties from a session description object build through
 * the application of <code>fsdp_parse</code> to a textual SDP session
 * description.
 **/

#include "parserpriv.h"

/**
 * \brief find the start of the next line
 * \param c pointer to current position in string
 * \return pointer to start of next line or NULL if illegal (i.e.
 *         a '\r' is not followed by a '\n'
 */
static const char *next_line(const char *c) {
  c += strcspn(c, "\n\r");
  if (*c == 0) return c;
  if (*c == '\r') c++;
  if (*c == '\n')
    return c + 1;
  return NULL;
}

/**
 * Moves the <code>c<code> pointer up to the beginning of the next
 * line.
 *
 * @param c char pointer to pointer
 * @retval FSDPE_ILLEGAL_CHARACTER, when an illegal '\r' character
 * (not followed by a '\n') is found, returns
 */
#define NEXT_LINE(c) do { if (!(c = next_line(c))) return FSDPE_ILLEGAL_CHARACTER; } while (0);

fsdp_error_t
fsdp_parse (const char *text_description, fsdp_description_t * dsc)
{
  fsdp_error_t result;
  const char *p = text_description, *p2;
  unsigned int j;
  /* temps for sscanf */
  const unsigned int TEMPCHARS = 6;
  char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN];
  char longfsdp_buf[MAXLONGFIELDLEN];
  const unsigned int TEMPINTS = 2;
  unsigned long int wuint[TEMPINTS];

  if ((NULL == text_description) || (NULL == dsc))
    return FSDPE_INVALID_PARAMETER;

  /***************************************************************************/
  /* A) parse session-level description                                      */
  /***************************************************************************/

  /* `v=' line (protocol version) */
  /* according to the RFC, only `v=0' is valid */
  if (sscanf (p, "v=%1lu", &wuint[0]))
  {
    if (wuint[0] != 0)
      return FSDPE_INVALID_VERSION;
  }
  else
  {
    return FSDPE_MISSING_VERSION;
  }
  NEXT_LINE (p);

  /* `o=' line (owner/creator and session identifier) */
  /* o=<username> <session id> <version> <network type> <address type>
     <address> */
  if (!strncmp (p, "o=", 2))
  {
    p += 2;
    /* note that the following max lengths may vary in the future and
       are quite arbitary */
    if (sscanf
        (p,
         "%" MSFLENS "[\x21-\xFF] %" MSFLENS "[0-9] %" MSFLENS
         "[0-9] %2s %3s %" MSFLENS "s", fsdp_buf[0], fsdp_buf[1],
         fsdp_buf[2], fsdp_buf[3], fsdp_buf[4], fsdp_buf[5]) != 6)
      return FSDPE_INVALID_OWNER;
    dsc->o_username = strdup (fsdp_buf[0]);
    dsc->o_session_id = strdup (fsdp_buf[1]);
    dsc->o_announcement_version = strdup (fsdp_buf[2]);
    if (!strncmp (fsdp_buf[3], "IN", 2))
    {
      dsc->o_network_type = FSDP_NETWORK_TYPE_INET;
      if (!strncmp (fsdp_buf[4], "IP4", 3))
        dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV4;
      else if (!strncmp (fsdp_buf[4], "IP6", 3))
        dsc->o_address_type = FSDP_ADDRESS_TYPE_IPV6;
      else
        return FSDPE_INVALID_OWNER;
    }
    else
    {
      return FSDPE_INVALID_OWNER;
    }
    /* TODO? check valid unicast address/FQDN */
    dsc->o_address = strdup (fsdp_buf[5]);
  }
  else
  {
    return FSDPE_MISSING_OWNER;
  }
  NEXT_LINE (p);

  /* `s=' line (session name) -note that the name string cannot be empty */
  /* s=<session name> */
  if (!strncmp (p, "s=", 2))
  {
    if (sscanf (p, "s=%" MLFLENS "[^\r\n]", longfsdp_buf) < 1)
      return FSDPE_EMPTY_NAME;
    dsc->s_name = strdup (longfsdp_buf);
  }
  else
  {
    return FSDPE_MISSING_NAME;
  }
  NEXT_LINE (p);

  /* `i=' line (session information) [optional] */
  /* i=<session description> */
  if (!strncmp (p, "i=", 2)
      && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf))
  {
    dsc->i_information = strdup (longfsdp_buf);
    NEXT_LINE (p);
  }
  else
  {
    /* (optional) information absent */
  }

  /* `u=' line (URI of description)  [optional] */
  /* u=<URI> */
  if (!strncmp (p, "u=", 2)
      && sscanf (p, "u=%" MLFLENS "[^\r\n]", longfsdp_buf))
  {
    /* TODO? check valid uri */
    dsc->u_uri = strdup (longfsdp_buf);
    NEXT_LINE (p);
  }
  else
  {
    /* (optional) uri absent */
  }

  /* `e=' lines (email address) [zero or more] */
  /* e=<email address> */
  p2 = p;
  j = 0;
  while (!strncmp (p2, "e=", 2))
  {
    /* First, count how many emails are there */
    j++;
    NEXT_LINE (p2);
  }
  dsc->emails_count = j;
  if (dsc->emails_count > 0)
  {
    /* Then, build the array of emails */
    dsc->emails = calloc (j, sizeof (const char *));
    for (j = 0; j < dsc->emails_count; j++)
    {
      sscanf (p, "e=%" MLFLENS "[^\r\n]", longfsdp_buf);
      /* TODO? check valid email-address. */
      dsc->emails[j] = strdup (longfsdp_buf);
      NEXT_LINE (p);
    }
  }

  /* `p=' lines (phone number) [zero or more] */
  /*  p=<phone number> */
  j = 0;
  /* assert ( p2 == p ); */
  while (!strncmp (p2, "p=", 2))
  {
    j++;
    NEXT_LINE (p2);
  }
  dsc->phones_count = j;
  if (dsc->phones_count > 0)
  {
    dsc->phones = calloc (j, sizeof (const char *));
    for (j = 0; j < dsc->phones_count; j++)
    {
      sscanf (p, "p=%" MLFLENS "[^\r\n]", longfsdp_buf);
      /* TODO? check valid phone-number. */
      dsc->phones[j] = strdup (longfsdp_buf);
      NEXT_LINE (p);
    }
  }

  /* `c=' line (connection information - not required if included in all media) [optional] */
  /* c=<network type> <address type> <connection address> */
  result = fsdp_parse_c (&p, &(dsc->c_network_type), &(dsc->c_address_type),
			 &(dsc->c_address));
  if (FSDPE_OK != result)
    return result;

  /* `b=' lines (bandwidth information) [optional] */
  /* b=<modifier>:<bandwidth-value> */
  result =
    fsdp_parse_b (&p, &(dsc->bw_modifiers), &(dsc->bw_modifiers_count));
  if (FSDPE_OK != result)
    return result;

  /* A.1) Time descriptions: */

  /* `t=' lines (time the session is active) [1 or more] */
  /* t=<start time>  <stop time> */
  j = 0;
  p2 = p;
  while (!strncmp (p2, "t=", 2))
  {
    j++;
    NEXT_LINE (p2);
    while (!strncmp (p2, "r=", 2))
      NEXT_LINE (p2);
  }
  dsc->time_periods_count = j;
  if (dsc->time_periods_count == 0)
    return FSDPE_MISSING_TIME;
  dsc->time_periods = calloc (dsc->time_periods_count,
			      sizeof (fsdp_time_period_t *));
  for (j = 0; j < dsc->time_periods_count; j++)
  {
    unsigned int h = 0;
    if (sscanf (p, "t=%10lu %10lu", &wuint[0], &wuint[1]) != 2)
    {
      /* not all periods have been successfully parsed */
      dsc->time_periods_count = j;
      return FSDPE_INVALID_TIME;
    }
    dsc->time_periods[j] = calloc (1, sizeof (fsdp_time_period_t));

    /* convert from NTP to time_t time */
    if (wuint[0] != 0)
      wuint[0] -= NTP_EPOCH_OFFSET;
    if (wuint[1] != 0)
      wuint[1] -= NTP_EPOCH_OFFSET;
    dsc->time_periods[j]->start = wuint[0];
    dsc->time_periods[j]->stop = wuint[1];
    NEXT_LINE (p);

    /* `r' lines [zero or more repeat times for each t=] */
    /*r=<repeat interval> <active duration> <list of offsets from
      start-time> */
    p2 = p;
    while (!strncmp (p2, "r=", 2))
    {
      h++;
      NEXT_LINE (p2);
    }
    dsc->time_periods[j]->repeats_count = h;
    if (h > 0)
    {
      unsigned int index2 = 0;
      dsc->time_periods[j]->repeats =
        calloc (h, sizeof (fsdp_repeat_t *));
      for (h = 0; h < dsc->time_periods[j]->repeats_count; h++)
      {
        /*
          get_repeat_values(p,&(dsc->time_periods[index].repeats[index2]));
          fsdp_error_t get_repeat_values (const char *r, fsdp_repeat_t
          *repeat);
          */
        if (sscanf (p, "r=%10s %10s %" MLFLENS "[^\r\n]",
                    fsdp_buf[0], fsdp_buf[1], longfsdp_buf) == 3)
        {
          fsdp_repeat_t *repeat;
          dsc->time_periods[j]->repeats[h] =
            calloc (1, sizeof (fsdp_repeat_t));
          repeat = dsc->time_periods[j]->repeats[h];
          /* get interval, duration and list of offsets */
          result =
            fsdp_repeat_time_to_uint (fsdp_buf[0],
                                      &(repeat->interval));
          if (result == FSDPE_OK)
          {
            result =
              fsdp_repeat_time_to_uint (fsdp_buf[1],
                                        &(repeat->duration));
            if (result == FSDPE_OK)
            {
              unsigned int k = 1;
              const char *i = longfsdp_buf;
              while (NULL != (i = strchr (i, ' ')))
              {
                k++;
                if (NULL != i)
                  i++;
              }
              repeat->offsets_count = k;
              repeat->offsets = calloc (k, sizeof (time_t));
              i = longfsdp_buf;
              for (k = 0;
                   (k < repeat->offsets_count)
                     && (result == FSDPE_OK); k++)
              {
                result =
                  fsdp_repeat_time_to_uint (i,
                                            &(repeat->
                                              offsets[k]));
                i = strchr (i, ' ');
                if (NULL != i)
                  i++;
              }
              if (k < repeat->offsets_count)
              {
                /* there where invalid repeat offsets */
                dsc->time_periods[j]->repeats_count = k;
                return FSDPE_INVALID_REPEAT;
              }
            }
          }
          if (result != FSDPE_OK)
          {
            /* not all repeats have been succesfully parsed */
            dsc->time_periods[j]->repeats_count = h;
            return FSDPE_INVALID_REPEAT;
          }
          NEXT_LINE (p);
        }
        else
        {
          /* not all repeats have been succesfully parsed */
          dsc->time_periods[j]->repeats_count = h;
          return FSDPE_INVALID_REPEAT;
        }
        index2++;
      }
    }
  }

  /* `z=' line (time zone adjustments) [zero or more] */
  /* z=<adjustment time> <offset> <adjustment time> <offset> .... */
  if (!strncmp (p, "z=", 2))
  {
    if (sscanf (p, "z=%" MLFLENS "[^\r\n]", longfsdp_buf))
    {
      /* TODO: guess how many pairs are there and process them */
      dsc->timezone_adj = strdup (longfsdp_buf);
      NEXT_LINE (p);
    }
    else
    {
      return FSDPE_INVALID_TIMEZONE;
    }
  }

  /* `k=' line (encryption key) [optional] */
  /* k=<method>
     k=<method>:<encryption key> */
  result = fsdp_parse_k (&p, &(dsc->k_encryption_method),
			 &(dsc->k_encryption_content));
  if (result != FSDPE_OK)
    return result;

  /* A.2) Attributes */
  /* `a=' lines (session attribute) [0 or more] */
  /* a=<attribute>
     a=<attribute>:<value> */
  while (!strncmp (p, "a=", 2))
  {
    /* The "9" length specifier of the first string is subject to
       changes */
    if (sscanf
        (p, "a=%9[^:\r\n]:%" MSFLENS "[^\r\n]", fsdp_buf[0],
         fsdp_buf[1]) == 2)
    {
      /* session-level value attributes */
      if (!strncmp (fsdp_buf[0], "cat", 3))
        dsc->a_category = strdup (fsdp_buf[1]);
      else if (!strncmp (fsdp_buf[0], "keywds", 6))
        dsc->a_keywords = strdup (fsdp_buf[1]);
      else if (!strncmp (fsdp_buf[0], "tool", 4))
        dsc->a_keywords = strdup (fsdp_buf[1]);
      else if (!strncmp (fsdp_buf[0], "rtpmap", 6))
        fsdp_parse_rtpmap (&(dsc->a_rtpmaps),
                           &(dsc->a_rtpmaps_count), fsdp_buf[1]);
      else if (!strncmp (fsdp_buf[0], "type", 4))
      {
        if (!strncmp (fsdp_buf[1], "broadcast", 9))
          dsc->a_type = FSDP_SESSION_TYPE_BROADCAST;
        else if (!strncmp (fsdp_buf[1], "meeting", 7))
          dsc->a_type = FSDP_SESSION_TYPE_MEETING;
        else if (!strncmp (fsdp_buf[1], "moderated", 9))
          dsc->a_type = FSDP_SESSION_TYPE_MODERATED;
        else if (!strncmp (fsdp_buf[1], "test", 4))
          dsc->a_type = FSDP_SESSION_TYPE_TEST;
        else if (!strncmp (fsdp_buf[1], "H332", 4))
          dsc->a_type = FSDP_SESSION_TYPE_H332;
        else
          return FSDPE_INVALID_SESSION_TYPE;
      }
      else if (!strncmp (fsdp_buf[0], "charset", 7))
        dsc->a_charset = strdup (fsdp_buf[1]);
      else if (!strncmp (fsdp_buf[0], "sdplang", 7))
      {
        if (NULL == dsc->a_sdplangs)
        {
          dsc->a_sdplangs_count = 0;
          dsc->a_sdplangs =
            calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
        }
        if (dsc->a_sdplangs_count < SDPLANGS_MAX_COUNT)
        {
          dsc->a_sdplangs[dsc->a_sdplangs_count] =
            strdup (fsdp_buf[1]);
          dsc->a_sdplangs_count++;
        }
      }
      else if (!strncmp (fsdp_buf[0], "lang", 4))
      {
        if (NULL == dsc->a_langs)
        {
          dsc->a_langs_count = 0;
          dsc->a_langs = calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
        }
        if (dsc->a_langs_count < SDPLANGS_MAX_COUNT)
        {
          dsc->a_langs[dsc->a_langs_count] = strdup (fsdp_buf[1]);
          dsc->a_langs_count++;
        }
      }
      else if (!strncmp (fsdp_buf[0], "control", 7))
      {
        if (NULL == dsc->a_controls)
        {
          dsc->a_controls_count = 0;
          dsc->a_controls =
            calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *));
        }
        if (dsc->a_controls_count < SDPCONTROLS_MAX_COUNT)
        {
          dsc->a_controls[dsc->a_controls_count] =
            strdup (fsdp_buf[1]);
          dsc->a_controls_count++;
        }
      }
      else if (!strncmp (fsdp_buf[0], "range", 5))
      {
        free (dsc->a_range);
        dsc->a_range = strdup (fsdp_buf[1]);
      }
      else
      {
        /* ignore unknown attributes, but provide access to them */
        *longfsdp_buf = '\0';
        strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN-1);
        strncat (longfsdp_buf, ":", MAXLONGFIELDLEN-strlen(longfsdp_buf)-1);
        strncat (longfsdp_buf, fsdp_buf[1], MAXLONGFIELDLEN-strlen(longfsdp_buf)-1);
        if (NULL == dsc->unidentified_attributes)
        {
          dsc->unidentified_attributes_count = 0;
          dsc->unidentified_attributes =
            calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
                    sizeof (char *));
        }
        if (dsc->unidentified_attributes_count <
            UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
        {
          dsc->unidentified_attributes
            [dsc->unidentified_attributes_count] =
            strdup (longfsdp_buf);
          dsc->unidentified_attributes_count++;
        }
      }
      NEXT_LINE (p);
    }
    else if (sscanf (p, "a=%20s", fsdp_buf[0]) == 1)
    {
      /* session-level property attributes */
      if (!strncmp (fsdp_buf[0], "recvonly", 8))
        dsc->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY;
      else if (!strncmp (fsdp_buf[0], "sendonly", 8))
        dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY;
      else if (!strncmp (fsdp_buf[0], "inactive", 8))
        dsc->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE;
      else if (!strncmp (fsdp_buf[0], "sendrecv", 8))
        dsc->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV;
      else
      {
        /* ignore unknown attributes, but provide access to them */
        *longfsdp_buf = '\0';
        strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN-1);
        if (NULL == dsc->unidentified_attributes)
        {
          dsc->unidentified_attributes_count = 0;
          dsc->unidentified_attributes =
            calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
                    sizeof (char *));
        }
        if (dsc->unidentified_attributes_count <
            UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
        {
          dsc->unidentified_attributes
            [dsc->unidentified_attributes_count] =
            strdup (longfsdp_buf);
          dsc->unidentified_attributes_count++;
        }
      }
      NEXT_LINE (p);
    }
    else
      return FSDPE_INVALID_ATTRIBUTE;
  }

  /***************************************************************************/
  /* B) parse media-level descriptions                                       */
  /***************************************************************************/
  p2 = p;
  j = 0;
  while ((*p2 != '\0') && !strncmp (p2, "m=", 2))
  {
    char c;
    j++;
    NEXT_LINE (p2);
    while (sscanf (p2, "%c=", &c) == 1)
    {
      if (c == 'i' || c == 'c' || c == 'b' || c == 'k' || c == 'a')
      {
        NEXT_LINE (p2);
      }
      else if (c == 'm')
      {
        break;
      }
      else
      {
        return FSDPE_INVALID_LINE;
      }
    }
  }
  dsc->media_announcements_count = j;
  if (dsc->media_announcements_count == 0)
  {
    ;
    /*return FSDPE_MISSING_MEDIA; */
  }
  else
  {				/* dsc->media_announcements_count > 0 */
    dsc->media_announcements =
      calloc (j, sizeof (fsdp_media_announcement_t *));
    for (j = 0; j < dsc->media_announcements_count; j++)
    {
      fsdp_media_announcement_t *media = NULL;
      /* `m=' line (media name, transport address and format list) */
      /* m=<media>  <port>  <transport> <fmt list> */
      /* The max. string lengths are subject to change */
      if (sscanf (p, "m=%11s %8s %7s %" MLFLENS "[^\r\n]",
                  fsdp_buf[0], fsdp_buf[1], fsdp_buf[2],
                  longfsdp_buf) != 4)
      {
        return FSDPE_INVALID_MEDIA;
      }
      else
      {
        dsc->media_announcements[j] =
          calloc (1, sizeof (fsdp_media_announcement_t));
        media = dsc->media_announcements[j];
        if (!strncmp (fsdp_buf[0], "audio", 5))
          media->media_type = FSDP_MEDIA_AUDIO;
        else if (!strncmp (fsdp_buf[0], "video", 5))
          media->media_type = FSDP_MEDIA_VIDEO;
        else if (!strncmp (fsdp_buf[0], "application", 11))
          media->media_type = FSDP_MEDIA_APPLICATION;
        else if (!strncmp (fsdp_buf[0], "data", 4))
          media->media_type = FSDP_MEDIA_DATA;
        else if (!strncmp (fsdp_buf[0], "control", 7))
          media->media_type = FSDP_MEDIA_CONTROL;
        else
          return FSDPE_UNKNOWN_MEDIA_TYPE;
        {			/* try to get port specification as port/number */
          char *slash;
          if ((slash = strchr (fsdp_buf[1], '/')))
          {
            *slash = '\0';
            slash++;
            media->port = strtol (fsdp_buf[1], NULL, 10);
            media->port_count = strtol (slash, NULL, 10);
          }
          else
          {
            media->port = strtol (fsdp_buf[1], NULL, 10);
            media->port_count = 0;
          }
        }
        if (!strncmp (fsdp_buf[2], "RTP/AVP", 7))
          media->transport = FSDP_TP_RTP_AVP;
        else if (!strncmp (fsdp_buf[2], "udp", 3))
          media->transport = FSDP_TP_UDP;
        else if (!strncmp (fsdp_buf[2], "TCP", 3))
          media->transport = FSDP_TP_TCP;
        else if (!strncmp (fsdp_buf[2], "UDPTL", 5))
          media->transport = FSDP_TP_UDPTL;
        else if (!strncmp (fsdp_buf[2], "vat", 3))
          media->transport = FSDP_TP_VAT;
        else if (!strncmp (fsdp_buf[2], "rtp", 3))
          media->transport = FSDP_TP_OLD_RTP;
        else
          return FSDPE_UNKNOWN_MEDIA_TRANSPORT;
        {
          unsigned int k = 0;
          char *s = longfsdp_buf;
          while (NULL != (s = strchr (s, ' ')))
          {
            k++;
            if (NULL != s)
              s++;
          }
          k++;		/* when there is no space left, count the last format */
          media->formats_count = k;
          media->formats = calloc (k, sizeof (char *));
          s = longfsdp_buf;
          for (k = 0; k < media->formats_count; k++)
          {
            char *space = strchr (s, ' ');
            if (NULL != space)
              *space = '\0';
            media->formats[k] = strdup (s);
            s = space + 1;
          }
        }
        NEXT_LINE (p);
      }

      /* `i=' line (media title) [optional] */
      /* i=<media title> */
      if (!strncmp (p, "i=", 2)
          && sscanf (p, "i=%" MLFLENS "[^\r\n]", longfsdp_buf))
      {
        media->i_title = strdup (longfsdp_buf);
        NEXT_LINE (p);
      }
      else
      {
        /* (optional) information absent */
      }

      /* `c=' line (connection information - overrides session-level
         line) [optional if provided at session-level] */
      /* c=<network type> <address type> <connection address> */
      result = fsdp_parse_c (&p, &(media->c_network_type),
                             &(media->c_address_type),
                             &(media->c_address));
      if (result != FSDPE_OK)
        return result;

      /* `b=' lines (bandwidth information) [optional] */
      /* b=<modifier>:<bandwidth-value> */
      result = fsdp_parse_b (&p, &(media->bw_modifiers),
                             &(media->bw_modifiers_count));
      if (FSDPE_OK != result)
        return result;

      /* `k=' line (encryption key) [optional] */
      /* k=<method>
         k=<method>:<encryption key> */
      result = fsdp_parse_k (&p, &(media->k_encryption_method),
                             &(media->k_encryption_content));
      if (result != FSDPE_OK)
        return result;

      /* B.1) Attributes */

      /* `a=' lines (zero or more media attribute lines) [optional] */
      /* a=<attribute>
         a=<attribute>:<value> */
      while (!strncmp (p, "a=", 2))
      {
        if (sscanf
            (p, "a=%9[^:\r\n]:%" MLFLENS "[^\r\n]", fsdp_buf[0],
             longfsdp_buf) == 2)
        {
          /* media-level value attributes */
          if (!strncmp (fsdp_buf[0], "ptime", 5))
            media->a_ptime = strtoul (longfsdp_buf, NULL, 10);
          else if (!strncmp (fsdp_buf[0], "maxptime", 8))
            media->a_maxptime = strtoul (longfsdp_buf, NULL, 10);
          else if (!strncmp (fsdp_buf[0], "rtpmap", 6))
            fsdp_parse_rtpmap (&(media->a_rtpmaps),
                               &(media->a_rtpmaps_count),
                               longfsdp_buf);
          else if (!strncmp (fsdp_buf[0], "orient", 6))
          {
            if (!strncmp (longfsdp_buf, "portrait", 8))
              media->a_orient = FSDP_ORIENT_PORTRAIT;
            else if (!strncmp (longfsdp_buf, "landscape", 9))
              media->a_orient = FSDP_ORIENT_LANDSCAPE;
            else if (!strncmp (longfsdp_buf, "seascape", 9))
              media->a_orient = FSDP_ORIENT_SEASCAPE;
          }
          else if (!strncmp (fsdp_buf[0], "sdplang", 7))
          {
            if (NULL == dsc->a_sdplangs)
            {
              media->a_sdplangs_count = 0;
              media->a_sdplangs =
                calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
            }
            if (media->a_sdplangs_count < SDPLANGS_MAX_COUNT)
            {
              media->a_sdplangs[dsc->a_sdplangs_count] =
                strdup (longfsdp_buf);
              media->a_sdplangs_count++;
            }
          }
          else if (!strncmp (fsdp_buf[0], "lang", 4))
          {
            if (NULL == dsc->a_langs)
            {
              media->a_langs_count = 0;
              media->a_langs =
                calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
            }
            if (media->a_langs_count < SDPLANGS_MAX_COUNT)
            {
              media->a_langs[dsc->a_langs_count] =
                strdup (longfsdp_buf);
              media->a_langs_count++;
            }
          }
          else if (!strncmp (fsdp_buf[0], "control", 7))
          {
            if (NULL == media->a_controls)
            {
              media->a_controls_count = 0;
              media->a_controls =
                calloc (SDPCONTROLS_MAX_COUNT, sizeof (char *));
            }
            if (media->a_controls_count < SDPCONTROLS_MAX_COUNT)
            {
              media->a_controls[media->a_controls_count] =
                strdup (longfsdp_buf);
              media->a_controls_count++;
            }
          }
          else if (!strncmp (fsdp_buf[0], "range", 5))
          {
            free (media->a_range);
            media->a_range = strdup (fsdp_buf[1]);
          }
          else if (!strncmp (fsdp_buf[0], "framerate", 9))
            media->a_framerate = strtod (longfsdp_buf, NULL);
          else if (!strncmp (fsdp_buf[0], "fmtp", 4))
          {
            if (NULL == media->a_fmtps)
            {
              media->a_fmtps_count = 0;
              media->a_fmtps =
                calloc (SDPLANGS_MAX_COUNT, sizeof (char *));
            }
            if (media->a_fmtps_count < SDPLANGS_MAX_COUNT)
            {
              media->a_fmtps[media->a_fmtps_count] =
                strdup (longfsdp_buf);
              media->a_fmtps_count++;
            }
          }
          else if (!strncmp (fsdp_buf[0], "rtcp", 4))
          {
            int opts = 0;
            /* rtcp attribute: a=rtcp:<port> <nettype> <addrtype> <address> */
            opts =
              sscanf (longfsdp_buf, "%lu %2s %3s %" MSFLENS "s",
                      &wuint[0], fsdp_buf[0], fsdp_buf[1],
                      fsdp_buf[2]);
            if (opts >= 1)
            {
              media->a_rtcp_port = wuint[0];
              if (opts >= 2)
              {
                if (!strncmp (fsdp_buf[0], "IN", 2))
                {
                  media->a_rtcp_network_type =
                    FSDP_NETWORK_TYPE_INET;
                }	/* else
                           ; TODO: define error code? */
                if (opts >= 3)
                {
                  if (!strncmp (fsdp_buf[1], "IP4", 3))
                    media->a_rtcp_address_type =
                      FSDP_ADDRESS_TYPE_IPV4;
                  else if (!strncmp (fsdp_buf[1], "IP6", 3))
                    media->a_rtcp_address_type =
                      FSDP_ADDRESS_TYPE_IPV6;
                  else
                    return FSDPE_INVALID_CONNECTION_NETTYPE;
                  /*add specific code? */
                  if (opts >= 4)
                    media->a_rtcp_address =
                      strdup (fsdp_buf[2]);
                }
              }
            }
          }
          else
          {
            /* ignore unknown attributes, but provide access to them */
            *fsdp_buf[1] = '\0';
            strncat (fsdp_buf[1], fsdp_buf[0], MAXSHORTFIELDLEN-1);
            strncat (fsdp_buf[1], ":", MAXSHORTFIELDLEN-strlen(fsdp_buf[1])-1);
            strncat (fsdp_buf[1], longfsdp_buf, MAXSHORTFIELDLEN-strlen(fsdp_buf[1])-1);
            if (NULL == media->unidentified_attributes)
            {
              media->unidentified_attributes_count = 0;
              media->unidentified_attributes =
                calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
                        sizeof (char *));
            }
            if (media->unidentified_attributes_count <
                UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
            {
              media->unidentified_attributes
                [media->unidentified_attributes_count] =
                strdup (fsdp_buf[1]);
              media->unidentified_attributes_count++;
            }
          }
          NEXT_LINE (p);
        }
        else if (sscanf (p, "a=%8s", fsdp_buf[0]) == 1)
        {
          /* media-level property attributes */
          if (!strncmp (fsdp_buf[0], "recvonly", 8))
            media->a_sendrecv_mode = FSDP_SENDRECV_RECVONLY;
          else if (!strncmp (fsdp_buf[0], "sendonly", 8))
            media->a_sendrecv_mode = FSDP_SENDRECV_SENDONLY;
          else if (!strncmp (fsdp_buf[0], "inactive", 8))
            media->a_sendrecv_mode = FSDP_SENDRECV_INACTIVE;
          else if (!strncmp (fsdp_buf[0], "sendrecv", 8))
            media->a_sendrecv_mode = FSDP_SENDRECV_SENDRECV;
          else
          {
            /* ignore unknown attributes, but provide access to them */
            *longfsdp_buf = '\0';
            strncat (longfsdp_buf, fsdp_buf[0], MAXLONGFIELDLEN-1);
            if (NULL == media->unidentified_attributes)
            {
              media->unidentified_attributes_count = 0;
              media->unidentified_attributes =
                calloc (UNIDENTIFIED_ATTRIBUTES_MAX_COUNT,
                        sizeof (char *));
            }
            if (media->unidentified_attributes_count <
                UNIDENTIFIED_ATTRIBUTES_MAX_COUNT)
            {
              media->unidentified_attributes
                [media->unidentified_attributes_count] =
                strdup (longfsdp_buf);
              media->unidentified_attributes_count++;
            }
          }
          NEXT_LINE (p);
        }
        else
          return FSDPE_INVALID_ATTRIBUTE;
      }
    }			/* end of for */
  }

  /* Check c= has been given at session level or at media level for
     all media */
  if (NULL == dsc->c_address.address)
  {
    unsigned int c;
    for (c = 0; c < dsc->media_announcements_count; c++)
      if (NULL == dsc->media_announcements[c]->c_address.address)
        return FSDPE_MISSING_CONNECTION_INFO;
  }

  /* finish */
  if (*p == '\0')
    return FSDPE_OK;
  else
    return FSDPE_OVERFILLED;
}

static fsdp_error_t
fsdp_parse_c (const char **p, fsdp_network_type_t * ntype,
	      fsdp_address_type_t * atype,
	      fsdp_connection_address_t * address)
{
  const unsigned int TEMPCHARS = 3;
  char fsdp_buf[TEMPCHARS][MAXSHORTFIELDLEN];

  if (!strncmp (*p, "c=", 2))
  {
    if (sscanf (*p, "c=%2s %3s %" MSFLENS "s",
                fsdp_buf[0], fsdp_buf[1], fsdp_buf[2]))
    {
      if (!strncmp (fsdp_buf[0], "IN", 2))
      {
        *ntype = FSDP_NETWORK_TYPE_INET;
        if (!strncmp (fsdp_buf[1], "IP4", 3))
          *atype = FSDP_ADDRESS_TYPE_IPV4;
        else if (!strncmp (fsdp_buf[1], "IP6", 3))
          *atype = FSDP_ADDRESS_TYPE_IPV6;
        else
          return FSDPE_INVALID_CONNECTION_NETTYPE;
      }
      else
      {
        return FSDPE_INVALID_CONNECTION_ADDRTYPE;
      }
      {
        char *slash = strchr (fsdp_buf[2], '/');
        if (NULL == slash)
        {
          address->address = strdup (fsdp_buf[2]);
          address->address_ttl = 0;
          address->address_count = 0;
        }
        else
        {
          /* address is IP4 multicast */
          char *slash2;
          *slash = '\0';
          slash++;
          address->address = strdup (fsdp_buf[2]);
          slash2 = strchr (slash + 1, '/');
          if (NULL == slash2)
          {
            address->address_ttl = strtol (slash, NULL, 10);
            address->address_count = 0;
          }
          else
          {
            *slash2 = '\0';
            slash2++;
            address->address_ttl = strtol (slash, NULL, 10);
            address->address_count = strtol (slash2, NULL, 10);
          }
        }
      }
      NEXT_LINE (*p);
    }
    else
    {
      return FSDPE_INVALID_CONNECTION;
    }
  }
  return FSDPE_OK;
}

static fsdp_error_t
fsdp_parse_b (const char **p, fsdp_bw_modifier_t ** bw_modifiers,
	      unsigned int *bw_modifiers_count)
{
  char fsdp_buf[MAXSHORTFIELDLEN];
  unsigned long int wuint;
  unsigned int i = 0;
  const char *lp = *p;

  /* count b= lines */
  while (!strncmp (lp, "b=", 2))
  {
    NEXT_LINE (lp);
    i++;
  }
  *bw_modifiers = calloc (i, sizeof (fsdp_bw_modifier_t));
  *bw_modifiers_count = i;

  while (i > 0)
  {
    unsigned int index = *bw_modifiers_count - i;
    if (2 == sscanf (*p, "b=%20[^:\r\n]:%lu", fsdp_buf, &wuint))
    {
      if (!strncmp (fsdp_buf, "CT", 2))
        (*bw_modifiers)[index].b_mod_type =
          FSDP_BW_MOD_TYPE_CONFERENCE_TOTAL;
      else if (!strncmp (fsdp_buf, "AS", 2))
        (*bw_modifiers)[index].b_mod_type =
          FSDP_BW_MOD_TYPE_APPLICATION_SPECIFIC;
      else if (!strncmp (fsdp_buf, "RS", 2))
        (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_RTCP_SENDERS;
      else if (!strncmp (fsdp_buf, "RR", 2))
        (*bw_modifiers)[index].b_mod_type =
          FSDP_BW_MOD_TYPE_RTCP_RECEIVERS;
      else
      {
        (*bw_modifiers)[index].b_mod_type = FSDP_BW_MOD_TYPE_UNKNOWN;
        (*bw_modifiers)[index].b_unknown_bw_modt =
          (char *) strdup (fsdp_buf);
      }
      (*bw_modifiers)[index].b_value = wuint;
      NEXT_LINE (*p);
    }
    else
    {
      *bw_modifiers_count -= i;
      return FSDPE_INVALID_BANDWIDTH;
    }
    i--;
  }
  return FSDPE_OK;
}

static fsdp_error_t
fsdp_parse_k (const char **p, fsdp_encryption_method_t * method,
	      char **content)
{
  char fsdp_buf[MAXSHORTFIELDLEN];
  char longfsdp_buf[MAXLONGFIELDLEN];

  if (!strncmp (*p, "k=", 2))
  {
    if (sscanf (*p, "k=prompt"))
    {
      *method = FSDP_ENCRYPTION_METHOD_PROMPT;
      *content = NULL;
      NEXT_LINE (*p);
    }
    else
    {
      if (sscanf
          (*p, "k=%6[^:\r\n]:%" MLFLENS "s", fsdp_buf, longfsdp_buf))
      {
        if (!strncmp (fsdp_buf, "clear", 5))
          *method = FSDP_ENCRYPTION_METHOD_CLEAR;
        else if (!strncmp (fsdp_buf, "base64", 6))
          *method = FSDP_ENCRYPTION_METHOD_BASE64;
        else if (!strncmp (fsdp_buf, "uri", 3))
          *method = FSDP_ENCRYPTION_METHOD_URI;
        else
          return FSDPE_INVALID_ENCRYPTION_METHOD;
        *content = strdup (longfsdp_buf);
        NEXT_LINE (*p);
      }
    }
  }
  return FSDPE_OK;
}

static fsdp_error_t
fsdp_parse_rtpmap (fsdp_rtpmap_t *** rtpmap, unsigned int *counter,
		   const char *value)
{
  fsdp_error_t result = FSDPE_OK;

  if (0 == *counter)
  {
    *counter = 0;
    *rtpmap = calloc (MEDIA_RTPMAPS_MAX_COUNT, sizeof (fsdp_rtpmap_t *));
  }
  if (*counter < MEDIA_RTPMAPS_MAX_COUNT)
  {
    unsigned int c = *counter;
    fsdp_rtpmap_t **map = *rtpmap;
    char fsdp_buf[MAXSHORTFIELDLEN];
    char longfsdp_buf[MAXLONGFIELDLEN];
    map[c] = calloc (1, sizeof (fsdp_rtpmap_t));

    /* a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding
       parameters]> */
    if (2 == sscanf (value, "%s %s", fsdp_buf, longfsdp_buf))
    {
      char *slash1;
      map[c]->pt = strdup (fsdp_buf);
      /* parse <encoding name>/<clock rate>[/<encoding parameters>] */
      slash1 = strchr (longfsdp_buf, '/');
      if (NULL == slash1)
      {
        result = FSDPE_INVALID_ATTRIBUTE_RTPMAP;
      }
      else
      {
        char *slash2;
        *slash1 = '\0';
        slash1++;
        map[c]->encoding_name = strdup (longfsdp_buf);
        slash2 = strchr (slash1, '/');
        if (NULL != slash2)
        {
          *slash2 = '\0';
          slash2++;
          map[c]->parameters = strdup (slash2);
        }
        map[c]->clock_rate = strtol (slash1, NULL, 10);
      }
      (*counter)++;
    }
  }
  return result;
}

static fsdp_error_t
fsdp_repeat_time_to_uint (const char *time, unsigned long int *seconds)
{
  const unsigned long SECONDS_PER_DAY = 86400;
  const unsigned long SECONDS_PER_HOUR = 3600;
  const unsigned long SECONDS_PER_MINUTE = 60;
  char c;
  unsigned long int wuint;

  if (sscanf (time, "%lu%c", &wuint, &c) == 2)
  {
    /* time with unit specification character */
    switch (c)
    {
    case 'd':
      *seconds = wuint * SECONDS_PER_DAY;
      break;
    case 'h':
      *seconds = wuint * SECONDS_PER_HOUR;
      break;
    case 'm':
      *seconds = wuint * SECONDS_PER_MINUTE;
      break;
    case 's':
      *seconds = wuint;
      break;
    default:
      return FSDPE_INVALID_REPEAT;
      break;
    }
  }
  else if (sscanf (time, "%lu", &wuint) == 1)
  {
    /* time without unit specification character */
    *seconds = wuint;
  }
  else
  {
    return FSDPE_INVALID_REPEAT;
  }
  return FSDPE_OK;
}

unsigned int
fsdp_get_version (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->version;
}

const char *
fsdp_get_owner_username (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->o_username;
}

const char *
fsdp_get_session_id (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->o_session_id;
}

const char *
fsdp_get_announcement_version (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->o_announcement_version;
}

fsdp_network_type_t
fsdp_get_owner_network_type (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_NETWORK_TYPE_UNDEFINED;
  return dsc->o_network_type;
}

fsdp_address_type_t
fsdp_get_owner_address_type (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_ADDRESS_TYPE_UNDEFINED;
  return dsc->o_address_type;
}

const char *
fsdp_get_owner_address (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->o_address;
}

const char *
fsdp_get_name (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->s_name;
}

const char *
fsdp_get_information (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->i_information;
}

const char *
fsdp_get_uri (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->u_uri;
}

unsigned int
fsdp_get_emails_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->emails_count;
}

const char *
fsdp_get_email (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->emails_count))
    return NULL;
  return dsc->emails[index];
}

unsigned int
fsdp_get_phones_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->phones_count;
}

const char *
fsdp_get_phone (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->phones_count))
    return NULL;
  return dsc->phones[index];
}

fsdp_network_type_t
fsdp_get_global_conn_network_type (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_NETWORK_TYPE_UNDEFINED;
  return dsc->c_network_type;
}

fsdp_address_type_t
fsdp_get_global_conn_address_type (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_ADDRESS_TYPE_UNDEFINED;
  return dsc->c_address_type;
}

const char *
fsdp_get_global_conn_address (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->c_address.address;
}

unsigned int
fsdp_get_global_conn_address_ttl (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->c_address.address_ttl;
}

unsigned int
fsdp_get_global_conn_address_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->c_address.address_count;
}

unsigned int
fsdp_get_bw_modifier_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->bw_modifiers_count;
}

fsdp_bw_modifier_type_t
fsdp_get_bw_modifier_type (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->bw_modifiers_count))
    return FSDP_BW_MOD_TYPE_UNDEFINED;
  return dsc->bw_modifiers[index].b_mod_type;
}

const char *
fsdp_get_bw_modifier_type_unknown (const fsdp_description_t * dsc,
				   unsigned int index)
{
  if ((!dsc) || (index >= dsc->bw_modifiers_count) ||
      (dsc->bw_modifiers[index].b_mod_type != FSDP_BW_MOD_TYPE_UNKNOWN))
    return NULL;
  return dsc->bw_modifiers[index].b_unknown_bw_modt;
}

unsigned long int
fsdp_get_bw_value (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->bw_modifiers_count))
    return 0;
  return dsc->bw_modifiers[index].b_value;
}

time_t
fsdp_get_period_start (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->time_periods_count))
    return 0;
  return dsc->time_periods[index]->start;
}

time_t
fsdp_get_period_stop (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->time_periods_count))
    return 0;
  return dsc->time_periods[index]->stop;
}

unsigned int
fsdp_get_period_repeats_count (const fsdp_description_t * dsc,
			       unsigned int index)
{
  if ((!dsc) || (index >= dsc->time_periods_count))
    return 0;
  return dsc->time_periods[index]->repeats_count;
}

unsigned long int
fsdp_get_period_repeat_interval (const fsdp_description_t * dsc,
				 unsigned int index, unsigned int rindex)
{
  if ((!dsc) || (index >= dsc->time_periods_count))
    return 0;
  return dsc->time_periods[index]->repeats[rindex]->interval;
}

unsigned long int
fsdp_get_period_repeat_duration (const fsdp_description_t * dsc,
				 unsigned int index, unsigned int rindex)
{
  if ((!dsc) || (index >= dsc->time_periods_count))
    return 0;
  return dsc->time_periods[index]->repeats[rindex]->duration;
}

const unsigned long int *
fsdp_get_period_repeat_offsets (const fsdp_description_t * dsc,
				unsigned int index, unsigned int rindex)
{
  if ((!dsc) || (index >= dsc->time_periods_count))
    return NULL;
  return dsc->time_periods[index]->repeats[rindex]->offsets;
}

const char *
fsdp_get_timezone_adj (const fsdp_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->timezone_adj;
}

unsigned int
fsdp_get_unidentified_attribute_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->unidentified_attributes_count;
}

const char *
fsdp_get_unidentified_attribute (const fsdp_description_t * dsc,
				 unsigned int index)
{
  if (!dsc || (index < dsc->unidentified_attributes_count))
    return NULL;
  return dsc->unidentified_attributes[index];
}

fsdp_encryption_method_t
fsdp_get_encryption_method (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_ENCRYPTION_METHOD_UNDEFINED;
  return dsc->k_encryption_method;
}

const char *
fsdp_get_encryption_content (const fsdp_description_t * dsc)
{
  if (!dsc || (dsc->k_encryption_method == FSDP_ENCRYPTION_METHOD_UNDEFINED))
    return NULL;
  return dsc->k_encryption_content;
}

const char *
fsdp_get_str_att (const fsdp_description_t * dsc, fsdp_session_str_att_t att)
{
  /*TODO: change these individual attributes with a table, thus
    avoiding this slow switch */
  char *result;

  if (!dsc)
    return NULL;

  switch (att)
  {
  case FSDP_SESSION_STR_ATT_CATEGORY:
    result = dsc->a_category;
    break;
  case FSDP_SESSION_STR_ATT_KEYWORDS:
    result = dsc->a_keywords;
    break;
  case FSDP_SESSION_STR_ATT_TOOL:
    result = dsc->a_tool;
    break;
  case FSDP_SESSION_STR_ATT_CHARSET:
    result = dsc->a_charset;
    break;
  default:
    result = NULL;
  }
  return result;
}

unsigned int
fsdp_get_sdplang_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_sdplangs_count;
}

const char *
fsdp_get_sdplang (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->a_sdplangs_count))
    return NULL;
  return dsc->a_sdplangs[index];
}

unsigned int
fsdp_get_control_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_controls_count;
}

const char *
fsdp_get_control (const fsdp_description_t * dsc, unsigned int index)
{
  if ((!dsc) || (index >= dsc->a_controls_count))
    return NULL;
  return dsc->a_controls[index];
}

const char *
fsdp_get_range (const fsdp_description_t * dsc)
{
  return dsc->a_range;
}

fsdp_sendrecv_mode_t
fsdp_get_sendrecv_mode (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_SENDRECV_UNDEFINED;
  return dsc->a_sendrecv_mode;
}

fsdp_session_type_t
fsdp_get_session_type (const fsdp_description_t * dsc)
{
  if (!dsc)
    return FSDP_SESSION_TYPE_UNDEFINED;
  return dsc->a_type;
}

unsigned int
fsdp_get_media_count (const fsdp_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->media_announcements_count;
}

const fsdp_media_description_t *
fsdp_get_media (const fsdp_description_t * dsc, unsigned int index)
{
  if ((index >= dsc->media_announcements_count))
    return NULL;
  return dsc->media_announcements[index];
}

fsdp_media_t
fsdp_get_media_type (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_MEDIA_UNDEFINED;
  return dsc->media_type;
}

unsigned int
fsdp_get_media_port (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->port;
}

unsigned int
fsdp_get_media_port_count (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->port_count;
}

fsdp_transport_protocol_t
fsdp_get_media_transport_protocol (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_TP_UNDEFINED;
  return dsc->transport;
}

unsigned int
fsdp_get_media_formats_count (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->formats_count;
}

const char *
fsdp_get_media_format (const fsdp_media_description_t * dsc,
		       unsigned int index)
{
  if (!dsc || (index < dsc->formats_count - 1))
    return NULL;
  return dsc->formats[index];
}

const char *
fsdp_get_media_title (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->i_title;
}

fsdp_network_type_t
fsdp_get_media_network_type (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_NETWORK_TYPE_UNDEFINED;
  return dsc->c_network_type;
}

fsdp_address_type_t
fsdp_get_media_address_type (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_ADDRESS_TYPE_UNDEFINED;
  return dsc->c_address_type;
}

const char *
fsdp_get_media_address (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->c_address.address;
}

unsigned int
fsdp_get_media_address_ttl (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->c_address.address_ttl;
}

unsigned int
fsdp_get_media_address_count (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->c_address.address_count;
}

fsdp_bw_modifier_type_t
fsdp_get_media_bw_modifier_type (const fsdp_media_description_t * dsc,
				 unsigned int index)
{
  if (!dsc || (index >= dsc->bw_modifiers_count))
    return FSDP_BW_MOD_TYPE_UNDEFINED;
  return dsc->bw_modifiers[index].b_mod_type;
}

const char *
fsdp_get_media_bw_modifier_type_unknown (const fsdp_media_description_t * dsc,
					 unsigned int index)
{
  if (!dsc || (index >= dsc->bw_modifiers_count) ||
      (FSDP_BW_MOD_TYPE_UNKNOWN != dsc->bw_modifiers[index].b_mod_type))
    return NULL;
  return dsc->bw_modifiers[index].b_unknown_bw_modt;
}

unsigned long int
fsdp_get_media_bw_value (const fsdp_media_description_t * dsc,
			 unsigned int index)
{
  if (!dsc || (index >= dsc->bw_modifiers_count))
    return 0;
  return dsc->bw_modifiers[index].b_value;
}

fsdp_encryption_method_t
fsdp_get_media_encryption_method (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_ENCRYPTION_METHOD_UNDEFINED;
  return dsc->k_encryption_method;
}

const char *
fsdp_get_media_encryption_content (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->k_encryption_content;
}

unsigned int
fsdp_get_media_ptime (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_ptime;
}

unsigned int
fsdp_get_media_maxptime (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_maxptime;
}

unsigned int
fsdp_get_media_rtpmap_count (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->a_rtpmaps_count;
}

const char *
fsdp_get_media_rtpmap_payload_type (const fsdp_media_description_t * mdsc,
				    unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_rtpmaps_count))
    return NULL;
  return mdsc->a_rtpmaps[index]->pt;
}

const char *
fsdp_get_media_rtpmap_encoding_name (const fsdp_media_description_t * mdsc,
				     unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_rtpmaps_count))
    return NULL;
  return mdsc->a_rtpmaps[index]->encoding_name;
}

unsigned int
fsdp_get_media_rtpmap_clock_rate (const fsdp_media_description_t * mdsc,
				  unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_rtpmaps_count))
    return 0;
  return mdsc->a_rtpmaps[index]->clock_rate;
}

const char *
fsdp_get_media_rtpmap_encoding_parameters (const fsdp_description_t * mdsc,
					   unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_rtpmaps_count))
    return NULL;
  return mdsc->a_rtpmaps[index]->parameters;
}

unsigned int
fsdp_get_media_sdplang_count (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->a_sdplangs_count;
}

const char *
fsdp_get_media_sdplang (const fsdp_media_description_t * mdsc,
			unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_sdplangs_count))
    return NULL;
  return mdsc->a_sdplangs[index];
}

unsigned int
fsdp_get_media_lang_count (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->a_langs_count;
}

const char *
fsdp_get_media_lang (const fsdp_media_description_t * mdsc,
		     unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_langs_count))
    return NULL;
  return mdsc->a_langs[index];
}

unsigned int
fsdp_get_media_control_count (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->a_controls_count;
}

char *
fsdp_get_media_control (const fsdp_media_description_t * mdsc,
			unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_controls_count))
    return NULL;
  return mdsc->a_controls[index];
}

char *
fsdp_get_media_range (const fsdp_media_description_t * mdsc)
{
  return mdsc->a_range;
}

unsigned int
fsdp_get_media_fmtp_count (const fsdp_media_description_t * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->a_fmtps_count;
}

const char *
fsdp_get_media_fmtp (const fsdp_media_description_t * mdsc,
		     unsigned int index)
{
  if (!mdsc || (index >= mdsc->a_fmtps_count))
    return NULL;
  return mdsc->a_fmtps[index];
}

fsdp_orient_t
fsdp_get_media_orient (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_ORIENT_UNDEFINED;
  return dsc->a_orient;
}

fsdp_sendrecv_mode_t
fsdp_get_media_sendrecv (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_SENDRECV_UNDEFINED;
  return dsc->a_sendrecv_mode;
}

float
fsdp_get_media_framerate (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_framerate;
}

unsigned int
fsdp_get_media_quality (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_quality;
}

unsigned int
fsdp_get_media_rtcp_port (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return 0;
  return dsc->a_rtcp_port;
}

fsdp_network_type_t
fsdp_get_media_rtcp_network_type (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_NETWORK_TYPE_UNDEFINED;
  return dsc->a_rtcp_network_type;
}

fsdp_address_type_t
fsdp_get_media_rtcp_address_type (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return FSDP_ADDRESS_TYPE_UNDEFINED;
  return dsc->a_rtcp_address_type;
}

const char *
fsdp_get_media_rtcp_address (const fsdp_media_description_t * dsc)
{
  if (!dsc)
    return NULL;
  return dsc->a_rtcp_address;
}

unsigned int
fsdp_get_media_unidentified_attribute_count (const fsdp_media_description_t
					     * mdsc)
{
  if (!mdsc)
    return 0;
  return mdsc->unidentified_attributes_count;
}

const char *
fsdp_get_media_unidentified_attribute (const fsdp_media_description_t * mdsc,
				       unsigned int index)
{
  if (!mdsc || (index < mdsc->unidentified_attributes_count))
    return NULL;
  return mdsc->unidentified_attributes[index];
}