root/src/ietf/sdp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gf_sdp_fmtp_new
  2. gf_sdp_fmtp_del
  3. SDP_GetFMTPForPayload
  4. SDP_ParseAttribute
  5. gf_sdp_media_del
  6. gf_sdp_conn_new
  7. gf_sdp_conn_del
  8. gf_sdp_media_new
  9. gf_sdp_info_new
  10. gf_sdp_info_reset
  11. gf_sdp_info_del
  12. SDP_IsDynamicPayload
  13. SDP_MakeSeconds
  14. gf_sdp_info_parse
  15. SDP_CheckConnection
  16. gf_sdp_info_check
  17. gf_sdp_info_write

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / IETF RTP/RTSP/SDP sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC 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 Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */



#include <gpac/ietf.h>

#ifndef GPAC_DISABLE_STREAMING

#include <gpac/token.h>


#define SDP_WRITE_STEPALLOC             2048


GF_EXPORT
GF_SDP_FMTP *gf_sdp_fmtp_new()
{
        GF_SDP_FMTP *tmp = (GF_SDP_FMTP*)gf_malloc(sizeof(GF_SDP_FMTP));
        tmp->PayloadType = 0;
        tmp->Attributes = gf_list_new();
        return tmp;
}

GF_EXPORT
void gf_sdp_fmtp_del(GF_SDP_FMTP *fmtp)
{
        GF_X_Attribute *att;
        if (!fmtp) return;
        while (gf_list_count(fmtp->Attributes)) {
                att = (GF_X_Attribute*)gf_list_get(fmtp->Attributes, 0);
                gf_list_rem(fmtp->Attributes, 0);
                if (att->Name) gf_free(att->Name);
                if (att->Value) gf_free(att->Value);
                gf_free(att);
        }
        gf_list_del(fmtp->Attributes);
        gf_free(fmtp);
}

GF_SDP_FMTP *SDP_GetFMTPForPayload(GF_SDPMedia *media, u32 PayloadType)
{
        GF_SDP_FMTP *tmp;
        u32 i;
        if (!media) return NULL;
        i=0;
        while ((tmp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &i))) {
                if (tmp->PayloadType == PayloadType) return tmp;
        }
        return NULL;
}

void SDP_ParseAttribute(GF_SDPInfo *sdp, char *buffer, GF_SDPMedia *media)
{
        s32 pos;
        u32 PayT;
        char comp[3000];
        GF_RTPMap *map;
        GF_SDP_FMTP *fmtp;
        GF_X_Attribute *att;

        pos = gf_token_get(buffer, 0, " :\t\r\n", comp, 3000);

        if (!strcmp(comp, "cat")) {
                if (media) return;
                /*pos = */gf_token_get(buffer, pos, ":\t\r\n", comp, 3000);
                sdp->a_cat = gf_strdup(comp);
                return;
        }
        if (!strcmp(comp, "keywds")) {
                if (media) return;
                /*pos = */gf_token_get(buffer, pos, ":\t\r\n", comp, 3000);
                sdp->a_keywds = gf_strdup(comp);
                return;
        }
        if (!strcmp(comp, "tool")) {
                if (media) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                sdp->a_tool = gf_strdup(comp);
                return;
        }

        if (!strcmp(comp, "ptime")) {
                if (!media) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                media->PacketTime = atoi(comp);
                return;
        }
        if (!strcmp(comp, "recvonly")) {
                if (!media) {
                        sdp->a_SendReceive = 1;
                } else {
                        media->SendReceive = 1;
                }
                return;
        }
        if (!strcmp(comp, "sendonly")) {
                if (!media) {
                        sdp->a_SendReceive = 2;
                } else {
                        media->SendReceive = 2;
                }
                return;
        }
        if (!strcmp(comp, "sendrecv")) {
                if (!media) {
                        sdp->a_SendReceive = 3;
                } else {
                        media->SendReceive = 3;
                }
                return;
        }
        if (!strcmp(comp, "orient")) {
                if (!media || media->Type) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                media->orientation = gf_strdup(comp);
                return;
        }
        if (!strcmp(comp, "type")) {
                if (media) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                sdp->a_type = gf_strdup(comp);
                return;
        }
        if (!strcmp(comp, "charset")) {
                if (media) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                sdp->a_charset = gf_strdup(comp);
                return;
        }
        if (!strcmp(comp, "sdplang")) {
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                if (media) {
                        media->sdplang = gf_strdup(comp);
                } else {
                        sdp->a_sdplang = gf_strdup(comp);
                }
                return;
        }
        if (!strcmp(comp, "lang")) {
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                if (media) {
                        media->lang = gf_strdup(comp);
                } else {
                        sdp->a_lang = gf_strdup(comp);
                }
                return;
        }
        if (!strcmp(comp, "framerate")) {
                //only for video
                if (!media || (media->Type != 1)) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                media->FrameRate = atof(comp);
                return;
        }
        if (!strcmp(comp, "quality")) {
                if (!media) return;
                /*pos = */gf_token_get(buffer, pos, ":\r\n", comp, 3000);
                media->Quality = atoi(comp);
                return;
        }
        if (!strcmp(comp, "rtpmap")) {
                if (!media) return;
                map = (GF_RTPMap*)gf_malloc(sizeof(GF_RTPMap));
                pos = gf_token_get(buffer, pos, ": \r\n", comp, 3000);
                map->PayloadType = atoi(comp);
                pos = gf_token_get(buffer, pos, " /\r\n", comp, 3000);
                map->payload_name = gf_strdup(comp);
                pos = gf_token_get(buffer, pos, " /\r\n", comp, 3000);
                map->ClockRate = atoi(comp);
                pos = gf_token_get(buffer, pos, " /\r\n", comp, 3000);
                map->AudioChannels = (pos > 0) ? atoi(comp) : 0;
                gf_list_add(media->RTPMaps, map);
                return;
        }
        //FMTP
        if (!strcmp(comp, "fmtp")) {
                if (!media) return;
                pos = gf_token_get(buffer, pos, ": \r\n", comp, 3000);
                PayT = atoi(comp);
                fmtp = SDP_GetFMTPForPayload(media, PayT);
                if (!fmtp) {
                        fmtp = gf_sdp_fmtp_new();
                        fmtp->PayloadType = PayT;
                        gf_list_add(media->FMTP, fmtp);
                }
                while (1) {
                        pos = gf_token_get(buffer, pos, "; =\r\n", comp, 3000);
                        if (pos <= 0) break;
                        att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
                        att->Name = gf_strdup(comp);
                        att->Value = NULL;
                        pos ++;
                        pos = gf_token_get(buffer, pos, ";\r\n", comp, 3000);
                        if (pos > 0) att->Value = gf_strdup(comp);
                        gf_list_add(fmtp->Attributes, att);
                }
                return;
        }
        //the rest cannot be discarded that way as it may be application-specific
        //so keep it.
        //a= <attribute> || <attribute>:<value>
        //we add <attribute> <value> in case ...
        pos = gf_token_get(buffer, 0, " :\r\n", comp, 3000);
        att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
        att->Name = gf_strdup(comp);
        att->Value = NULL;
        pos += 1;
        if (buffer[pos] == ' ') pos += 1;
        pos = gf_token_get(buffer, pos, "\r\n", comp, 3000);
        if (pos > 0) att->Value = gf_strdup(comp);

        if (media) {
                gf_list_add(media->Attributes, att);
        } else {
                gf_list_add(sdp->Attributes, att);
        }
}



#define SDPM_DESTROY(p) if (media->p) gf_free(media->p)
GF_EXPORT
void gf_sdp_media_del(GF_SDPMedia *media)
{
        GF_SDPBandwidth *bw;
        GF_RTPMap *map;
        GF_SDPConnection *conn;
        GF_SDP_FMTP *fmtp;
        GF_X_Attribute *att;
        if (!media) return;

        while (gf_list_count(media->FMTP)) {
                fmtp = (GF_SDP_FMTP*)gf_list_get(media->FMTP, 0);
                gf_list_rem(media->FMTP, 0);
                gf_sdp_fmtp_del(fmtp);
        }
        gf_list_del(media->FMTP);

        while (gf_list_count(media->Attributes)) {
                att = (GF_X_Attribute*)gf_list_get(media->Attributes, 0);
                gf_list_rem(media->Attributes, 0);
                if (att->Name) gf_free(att->Name);
                if (att->Value) gf_free(att->Value);
                gf_free(att);
        }
        gf_list_del(media->Attributes);

        while (gf_list_count(media->RTPMaps)) {
                map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0);
                gf_free(map->payload_name);
                gf_free(map);
                gf_list_rem(media->RTPMaps, 0);
        }
        gf_list_del(media->RTPMaps);

        while (gf_list_count(media->Connections)) {
                conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0);
                gf_list_rem(media->Connections, 0);
                gf_sdp_conn_del(conn);
        }
        gf_list_del(media->Connections);

        while (gf_list_count(media->Bandwidths)) {
                bw = (GF_SDPBandwidth*)gf_list_get(media->Bandwidths, 0);
                gf_list_rem(media->Bandwidths, 0);
                if (bw->name) gf_free(bw->name);
                gf_free(bw);
        }
        gf_list_del(media->Bandwidths);

        SDPM_DESTROY(orientation);
        SDPM_DESTROY(sdplang);
        SDPM_DESTROY(lang);
        SDPM_DESTROY(Profile);
        SDPM_DESTROY(fmt_list);
        SDPM_DESTROY(k_method);
        SDPM_DESTROY(k_key);
        gf_free(media);
}


GF_EXPORT
GF_SDPConnection *gf_sdp_conn_new()
{
        GF_SDPConnection *conn;
        GF_SAFEALLOC(conn, GF_SDPConnection);
        if (!conn) return NULL;
        conn->TTL = -1;
        return conn;
}

GF_EXPORT
void gf_sdp_conn_del(GF_SDPConnection *conn)
{
        if (conn->add_type) gf_free(conn->add_type);
        if (conn->host) gf_free(conn->host);
        if (conn->net_type) gf_free(conn->net_type);
        gf_free(conn);
}

GF_EXPORT
GF_SDPMedia *gf_sdp_media_new()
{
        GF_SDPMedia *tmp;
        GF_SAFEALLOC(tmp, GF_SDPMedia);
        if (!tmp) return NULL;
        tmp->FMTP = gf_list_new();
        tmp->RTPMaps = gf_list_new();
        tmp->Attributes = gf_list_new();
        tmp->Connections = gf_list_new();
        tmp->Bandwidths = gf_list_new();
        tmp->Quality = -1;
        return tmp;
}

GF_EXPORT
GF_SDPInfo *gf_sdp_info_new()
{
        GF_SDPInfo *sdp;
        GF_SAFEALLOC(sdp, GF_SDPInfo);
        if (!sdp) return NULL;
        sdp->b_bandwidth = gf_list_new();
        sdp->media_desc = gf_list_new();
        sdp->Attributes = gf_list_new();
        sdp->Timing = gf_list_new();
        return sdp;
}

#define SDP_DESTROY(p) if (sdp->p)      \
                                        gf_free(sdp->p);        \
                                        sdp->p = NULL;


GF_EXPORT
void gf_sdp_info_reset(GF_SDPInfo *sdp)
{
        GF_SDPBandwidth *bw;
        GF_SDPMedia *media;
        GF_SDPTiming *timing;
        GF_X_Attribute *att;

        if (!sdp) return;

        while (gf_list_count(sdp->media_desc)) {
                media = (GF_SDPMedia*)gf_list_get(sdp->media_desc, 0);
                gf_list_rem(sdp->media_desc, 0);
                gf_sdp_media_del(media);
        }
        while (gf_list_count(sdp->Attributes)) {
                att = (GF_X_Attribute*)gf_list_get(sdp->Attributes, 0);
                gf_list_rem(sdp->Attributes, 0);
                if (att->Name) gf_free(att->Name);
                if (att->Value) gf_free(att->Value);
                gf_free(att);
        }
        while (gf_list_count(sdp->b_bandwidth)) {
                bw = (GF_SDPBandwidth*)gf_list_get(sdp->b_bandwidth, 0);
                gf_list_rem(sdp->b_bandwidth, 0);
                if (bw->name) gf_free(bw->name);
                gf_free(bw);
        }
        while (gf_list_count(sdp->Timing)) {
                timing = (GF_SDPTiming*)gf_list_get(sdp->Timing, 0);
                gf_list_rem(sdp->Timing, 0);
                gf_free(timing);
        }

        //then delete all info ...
        SDP_DESTROY(o_username);
        SDP_DESTROY(o_session_id);
        SDP_DESTROY(o_version);
        SDP_DESTROY(o_address);
        SDP_DESTROY(o_net_type);
        SDP_DESTROY(o_add_type);
        SDP_DESTROY(s_session_name);
        SDP_DESTROY(i_description);
        SDP_DESTROY(u_uri);
        SDP_DESTROY(e_email);
        SDP_DESTROY(p_phone);
        SDP_DESTROY(k_method);
        SDP_DESTROY(k_key);
        SDP_DESTROY(a_cat);
        SDP_DESTROY(a_keywds);
        SDP_DESTROY(a_tool);
        SDP_DESTROY(a_type);
        SDP_DESTROY(a_charset);
        SDP_DESTROY(a_sdplang);
        SDP_DESTROY(a_lang);

        if (sdp->c_connection) {
                gf_sdp_conn_del(sdp->c_connection);
                sdp->c_connection = NULL;
        }
        sdp->a_SendReceive = 0;
}

GF_EXPORT
void gf_sdp_info_del(GF_SDPInfo *sdp)
{
        if (!sdp) return;
        gf_sdp_info_reset(sdp);
        gf_list_del(sdp->media_desc);
        gf_list_del(sdp->Attributes);
        gf_list_del(sdp->b_bandwidth);
        gf_list_del(sdp->Timing);
        gf_free(sdp);
}


Bool SDP_IsDynamicPayload(GF_SDPMedia *media, char *payt)
{
        u32 i;
        GF_RTPMap *map;
        char buf[10];
        i=0;
        while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &i))) {
                sprintf(buf, "%d", map->PayloadType);
                if (!strcmp(payt, buf)) return GF_TRUE;
        }
        return GF_FALSE;
}

//translate h || m || d in sec. Fractions are not allowed with this writing
s32 SDP_MakeSeconds(char *buf)
{
        s32 sign;
        char num[30], *test;
        sign = 1;
        if (buf[0] == '-') {
                sign = -1;
                buf += 1;
        }
        memset(num, 0, 30);
        test = strstr(buf, "d");
        if (test) {
                assert(strlen(buf)-strlen(test) < sizeof(num));
                strncpy(num, buf, strlen(buf)-strlen(test));
                return (atoi(num)*sign*86400);
        }
        test = strstr(buf, "h");
        if (test) {
                assert(strlen(buf)-strlen(test) < sizeof(num));
                strncpy(num, buf, strlen(buf)-strlen(test));
                return (atoi(num)*sign*3600);
        }
        test = strstr(buf, "m");
        if (test) {
                assert(strlen(buf)-strlen(test) < sizeof(num));
                strncpy(num, buf, strlen(buf)-strlen(test));
                return (atoi(num)*sign*60);
        }
        return (atoi(buf) * sign);
}


GF_EXPORT
GF_Err gf_sdp_info_parse(GF_SDPInfo *sdp, char *sdp_text, u32 text_size)
{
        GF_SDPBandwidth *bw;
        GF_SDPConnection *conn;
        GF_SDPMedia *media;
        GF_SDPTiming *timing;
        u32 i;
        s32 pos, LinePos;
        char LineBuf[3000], comp[3000];

        media = NULL;
        timing = NULL;

        if (!sdp) return GF_BAD_PARAM;

        //Clean SDP info
        gf_sdp_info_reset(sdp);

        LinePos = 0;
        while (1) {
                LinePos = gf_token_get_line(sdp_text, LinePos, text_size, LineBuf, 3000);
                if (LinePos <= 0) break;
                if (!strcmp(LineBuf, "\r\n") || !strcmp(LineBuf, "\n") || !strcmp(LineBuf, "\r")) continue;


                switch (LineBuf[0]) {
                case 'v':
                        /*pos = */gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
                        sdp->Version = atoi(comp);
                        break;
                case 'o':
                        pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
                        sdp->o_username = gf_strdup(comp);
                        pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        sdp->o_session_id = gf_strdup(comp);
                        pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        sdp->o_version = gf_strdup(comp);

                        pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        sdp->o_net_type = gf_strdup(comp);

                        pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        sdp->o_add_type = gf_strdup(comp);

                        /*pos = */gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        sdp->o_address = gf_strdup(comp);
                        break;
                case 's':
                        /*pos = */gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
                        sdp->s_session_name = gf_strdup(comp);
                        break;
                case 'i':
                        /*pos = */gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
                        sdp->i_description = gf_strdup(comp);
                        break;
                case 'u':
                        /*pos = */gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
                        sdp->u_uri = gf_strdup(comp);
                        break;
                case 'e':
                        /*pos = */gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
                        sdp->e_email = gf_strdup(comp);
                        break;
                case 'p':
                        /*pos = */gf_token_get(LineBuf, 2, "\t\r\n", comp, 3000);
                        sdp->p_phone = gf_strdup(comp);
                        break;
                case 'c':
                        //if at session level, only 1 is allowed for all SDP
                        if (sdp->c_connection) break;

                        conn = gf_sdp_conn_new();

                        pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
                        conn->net_type = gf_strdup(comp);

                        pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        conn->add_type = gf_strdup(comp);

                        pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000);
                        conn->host = gf_strdup(comp);
                        if (gf_sk_is_multicast_address(conn->host)) {
                                //a valid SDP will have TTL if address is multicast
                                pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000);
                                if (pos > 0) {
                                        conn->TTL = atoi(comp);
                                        //multiple address indication is only valid for media
                                        pos = gf_token_get(LineBuf, pos, "/\r\n", comp, 3000);
                                }
                                if (pos > 0) {
                                        if (!media) {
                                                gf_sdp_conn_del(conn);
                                                break;
                                        }
                                        conn->add_count = atoi(comp);
                                }
                        }
                        if (!media)
                                sdp->c_connection = conn;
                        else
                                gf_list_add(media->Connections, conn);

                        break;
                case 'b':
                        pos = gf_token_get(LineBuf, 2, ":\r\n", comp, 3000);
                        if (strcmp(comp, "CT") && strcmp(comp, "AS") && (comp[0] != 'X')) break;

                        GF_SAFEALLOC(bw, GF_SDPBandwidth);
                        if (!bw) return GF_OUT_OF_MEM;
                        bw->name = gf_strdup(comp);
                        /*pos = */gf_token_get(LineBuf, pos, ":\r\n", comp, 3000);
                        bw->value = atoi(comp);
                        if (media) {
                                gf_list_add(media->Bandwidths, bw);
                        } else {
                                gf_list_add(sdp->b_bandwidth, bw);
                        }
                        break;

                case 't':
                        if (media) break;
                        //create a new time structure for each entry
                        GF_SAFEALLOC(timing, GF_SDPTiming);
                        if (!timing) return GF_OUT_OF_MEM;
                        pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
                        timing->StartTime = atoi(comp);
                        /*pos = */gf_token_get(LineBuf, pos, "\r\n", comp, 3000);
                        timing->StopTime = atoi(comp);
                        gf_list_add(sdp->Timing, timing);
                        break;
                case 'r':
                        if (media) break;
                        pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
                        if (!timing) return GF_NON_COMPLIANT_BITSTREAM;
                        timing->RepeatInterval = SDP_MakeSeconds(comp);
                        pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                        timing->ActiveDuration = SDP_MakeSeconds(comp);
                        while (1) {
                                pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                                if (pos <= 0) break;
                                timing->OffsetFromStart[timing->NbRepeatOffsets] = SDP_MakeSeconds(comp);
                                timing->NbRepeatOffsets += 1;
                        }
                        break;
                case 'z':
                        if (media) break;
                        pos = 2;
                        if (!timing) return GF_NON_COMPLIANT_BITSTREAM;
                        while (1) {
                                pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                                if (pos <= 0) break;
                                timing->AdjustmentTime[timing->NbZoneOffsets] = atoi(comp);
                                pos = gf_token_get(LineBuf, pos, " \t\r\n", comp, 3000);
                                timing->AdjustmentOffset[timing->NbZoneOffsets] = SDP_MakeSeconds(comp);
                                timing->NbZoneOffsets += 1;
                        }
                        break;
                case 'k':
                        pos = gf_token_get(LineBuf, 2, ":\t\r\n", comp, 3000);
                        if (media) {
                                media->k_method = gf_strdup(comp);
                        } else {
                                sdp->k_method = gf_strdup(comp);
                        }
                        pos = gf_token_get(LineBuf, pos, ":\r\n", comp, 3000);
                        if (pos > 0) {
                                if (media) {
                                        media->k_key = gf_strdup(comp);
                                } else {
                                        sdp->k_key = gf_strdup(comp);
                                }
                        }
                        break;
                case 'a':
                        SDP_ParseAttribute(sdp, LineBuf+2, media);
                        break;
                case 'm':
                        pos = gf_token_get(LineBuf, 2, " \t\r\n", comp, 3000);
                        if (strcmp(comp, "audio")
                                && strcmp(comp, "data")
                                && strcmp(comp, "control")
                                && strcmp(comp, "video")
                                && strcmp(comp, "text")
                                && strcmp(comp, "application")) {
                                return GF_SERVICE_ERROR;
                        }
                        media = gf_sdp_media_new();
                        //media type
                        if (!strcmp(comp, "video")) media->Type = 1;
                        else if (!strcmp(comp, "audio")) media->Type = 2;
                        else if (!strcmp(comp, "text")) media->Type = 3;
                        else if (!strcmp(comp, "data")) media->Type = 4;
                        else if (!strcmp(comp, "control")) media->Type = 5;
                        else media->Type = 0;
                        //port numbers
                        gf_token_get(LineBuf, pos, " ", comp, 3000);
                        if (!strstr(comp, "/")) {
                                pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
                                media->PortNumber = atoi(comp);
                                media->NumPorts = 0;
                        } else {
                                pos = gf_token_get(LineBuf, pos, " /\r\n", comp, 3000);
                                media->PortNumber = atoi(comp);
                                pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
                                media->NumPorts = atoi(comp);
                        }
                        //transport Profile
                        pos = gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
                        media->Profile = gf_strdup(comp);
                        /*pos = */gf_token_get(LineBuf, pos, " \r\n", comp, 3000);
                        media->fmt_list = gf_strdup(comp);

                        gf_list_add(sdp->media_desc, media);
                        break;
                }
        }
        //finally rewrite the fmt_list for all media, and remove dynamic payloads
        //from the list
        i=0;
        while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) {
                pos = 0;
                LinePos = 1;
                strcpy(LineBuf, "");
                while (1) {
                        if (!media->fmt_list) break;
                        pos = gf_token_get(media->fmt_list, pos, " ", comp, 3000);
                        if (pos <= 0) break;
                        if (!SDP_IsDynamicPayload(media, comp)) {
                                if (!LinePos) {
                                        strcat(LineBuf, " ");
                                } else {
                                        LinePos = 0;
                                }
                                strcat(LineBuf, comp);
                        }
                        gf_free(media->fmt_list);
                        media->fmt_list = NULL;
                        if (strlen(LineBuf)) {
                                media->fmt_list = gf_strdup(LineBuf);
                        }
                }
        }
        return GF_OK;
}



GF_Err SDP_CheckConnection(GF_SDPConnection *conn)
{
        if (!conn) return GF_BAD_PARAM;
        if (!conn->host || !conn->add_type || !conn->net_type) return GF_REMOTE_SERVICE_ERROR;
        if (gf_sk_is_multicast_address(conn->host)) {
                if (conn->TTL < 0 || conn->TTL > 255) return GF_REMOTE_SERVICE_ERROR;
        } else {
                conn->TTL = -1;
                conn->add_count = 0;
        }
        return GF_OK;
}

//return GF_BAD_PARAM if invalid structure, GF_REMOTE_SERVICE_ERROR if bad formatting
//or GF_OK
GF_EXPORT
GF_Err gf_sdp_info_check(GF_SDPInfo *sdp)
{
        GF_Err e;
        u32 i, j, count;
        GF_SDPMedia *media;
        GF_SDPConnection *conn;
        GF_RTPMap *map;
        Bool HasGlobalConnection, HasSeveralPorts;

        if (!sdp || !sdp->media_desc || !sdp->Attributes) return GF_BAD_PARAM;
        //we force at least one media per SDP
        if (!gf_list_count(sdp->media_desc)) return GF_REMOTE_SERVICE_ERROR;

        //normative fields
        //o=
        if (!sdp->o_add_type || !sdp->o_address || !sdp->o_username || !sdp->o_session_id || !sdp->o_version)
                return GF_REMOTE_SERVICE_ERROR;
        //s=
        //commented for intermedia demos
//      if (!sdp->s_session_name) return GF_REMOTE_SERVICE_ERROR;
        //t=
//      if () return GF_REMOTE_SERVICE_ERROR;
        //c=
        if (sdp->c_connection) {
                e = SDP_CheckConnection(sdp->c_connection);
                if (e) return e;
                //multiple addresses are only for media desc
                if (sdp->c_connection->add_count >= 2) return GF_REMOTE_SERVICE_ERROR;
                HasGlobalConnection = GF_TRUE;
        } else {
                HasGlobalConnection = GF_FALSE;
        }

        //then check all media
        i=0;
        while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) {
                HasSeveralPorts = GF_FALSE;

                //m= : force non-null port, profile and fmt_list
                if (/*!media->PortNumber || */ !media->Profile) return GF_REMOTE_SERVICE_ERROR;
                if (media->NumPorts) HasSeveralPorts = GF_TRUE;

                //no connections specified - THIS IS AN ERROR IN SDP BUT NOT IN ALL RTSP SESSIONS...
//              if (!HasGlobalConnection && !gf_list_count(media->Connections)) return GF_REMOTE_SERVICE_ERROR;
                //too many connections specified
                if (HasGlobalConnection && gf_list_count(media->Connections)) return GF_REMOTE_SERVICE_ERROR;

                //check all connections, and make sure we don't have multiple addresses
                //and multiple ports at the same time
                count = gf_list_count(media->Connections);
                if (count>1 && HasSeveralPorts) return GF_REMOTE_SERVICE_ERROR;

                for (j=0; j<count; j++) {
                        conn = (GF_SDPConnection*)gf_list_get(media->Connections, j);
                        e = SDP_CheckConnection(conn);
                        if (e) return e;
                        if ((conn->add_count >= 2) && HasSeveralPorts) return GF_REMOTE_SERVICE_ERROR;
                }
                //RTPMaps. 0 is tolerated, but if some are specified check them
                j=0;
                while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &j))) {
                        //RFC2327 is not clear here, but we assume the PayloadType should be a DYN one
                        //however this depends on the profile (RTP/AVP or others) so don't check it
                        //ClockRate SHALL NOT be NULL
                        if (!map->payload_name || !map->ClockRate) return GF_REMOTE_SERVICE_ERROR;
                }
        }
        //Encryption: nothing tells wether the scope of the global key is eclusive or not.
        //we accept a global key + keys per media entry, assuming that the media key primes
        //on the global key

        return GF_OK;
}


#define SDP_WRITE_ALLOC_STR_WITHOUT_CHECK(str, space)           \
        if (strlen(str)+pos + (space ? 1 : 0) >= buf_size) {    \
                buf_size += SDP_WRITE_STEPALLOC;        \
                buf = (char*)gf_realloc(buf, sizeof(char)*buf_size);            \
        }       \
        strcpy(buf+pos, str);           \
        pos += (u32) strlen(str);               \
        if (space) {                    \
                strcat(buf+pos, " ");   \
                pos += 1;               \
        }

#define SDP_WRITE_ALLOC_STR(str, space)         \
        if (str) { \
                SDP_WRITE_ALLOC_STR_WITHOUT_CHECK(str, space); \
        }               \
 
#define SDP_WRITE_ALLOC_INT(d, spa, sig)                \
        if (sig < 0) { \
                sprintf(temp, "%d", d);         \
        } else { \
                sprintf(temp, "%u", d);         \
        }       \
        SDP_WRITE_ALLOC_STR_WITHOUT_CHECK(temp, spa);

#define SDP_WRITE_ALLOC_FLOAT(d, spa)           \
        sprintf(temp, "%.2f", d);               \
        SDP_WRITE_ALLOC_STR_WITHOUT_CHECK(temp, spa);

#define TEST_SDP_WRITE_SINGLE(type, str, sep)           \
        if (str) {              \
                SDP_WRITE_ALLOC_STR(type, 0);           \
                if (sep) SDP_WRITE_ALLOC_STR(":", 0);           \
                SDP_WRITE_ALLOC_STR(str, 0);            \
                SDP_WRITE_ALLOC_STR("\r\n", 0);         \
        }


#define SDP_WRITE_CONN(conn)            \
        if (conn) {                     \
                SDP_WRITE_ALLOC_STR("c=", 0);   \
                SDP_WRITE_ALLOC_STR(conn->net_type, 1);         \
                SDP_WRITE_ALLOC_STR(conn->add_type, 1);         \
                SDP_WRITE_ALLOC_STR(conn->host, 0);                     \
                if (gf_sk_is_multicast_address(conn->host)) {   \
                        SDP_WRITE_ALLOC_STR("/", 0);                    \
                        SDP_WRITE_ALLOC_INT(conn->TTL, 0, 0);           \
                        if (conn->add_count >= 2) {             \
                                SDP_WRITE_ALLOC_STR("/", 0);            \
                                SDP_WRITE_ALLOC_INT(conn->add_count, 0, 0);     \
                        }               \
                }       \
                SDP_WRITE_ALLOC_STR("\r\n", 0);         \
        }

GF_EXPORT
GF_Err gf_sdp_info_write(GF_SDPInfo *sdp, char **out_str_buf)
{
        char *buf;
        GF_SDP_FMTP *fmtp;
        char temp[50];
        GF_SDPMedia *media;
        GF_SDPBandwidth *bw;
        u32 buf_size, pos, i, j, k;
        GF_RTPMap *map;
        GF_SDPConnection *conn;
        GF_Err e;
        GF_SDPTiming *timing;
        GF_X_Attribute *att;

        e = gf_sdp_info_check(sdp);
        if (e) return e;

        buf = (char *)gf_malloc(SDP_WRITE_STEPALLOC);
        buf_size = SDP_WRITE_STEPALLOC;
        pos = 0;

        //v
        SDP_WRITE_ALLOC_STR("v=", 0);
        SDP_WRITE_ALLOC_INT(sdp->Version, 0, 0);
        SDP_WRITE_ALLOC_STR("\r\n", 0);
        //o
        SDP_WRITE_ALLOC_STR("o=", 0);
        SDP_WRITE_ALLOC_STR(sdp->o_username, 1);
        SDP_WRITE_ALLOC_STR(sdp->o_session_id, 1);
        SDP_WRITE_ALLOC_STR(sdp->o_version, 1);
        SDP_WRITE_ALLOC_STR(sdp->o_net_type, 1);
        SDP_WRITE_ALLOC_STR(sdp->o_add_type, 1);
        SDP_WRITE_ALLOC_STR(sdp->o_address, 0);
        SDP_WRITE_ALLOC_STR("\r\n", 0);
        //s
        TEST_SDP_WRITE_SINGLE("s=", sdp->s_session_name, 0);
        //i
        TEST_SDP_WRITE_SINGLE("i=", sdp->i_description, 0);
        //u
        TEST_SDP_WRITE_SINGLE("u=", sdp->u_uri, 0);
        //e
        TEST_SDP_WRITE_SINGLE("e=", sdp->e_email, 0);
        //p
        TEST_SDP_WRITE_SINGLE("p=", sdp->p_phone, 0);
        //c
        SDP_WRITE_CONN(sdp->c_connection);
        //b
        i=0;
        while ((bw = (GF_SDPBandwidth*)gf_list_enum(sdp->b_bandwidth, &i))) {
                SDP_WRITE_ALLOC_STR("b=", 0);
                SDP_WRITE_ALLOC_STR(bw->name, 0);
                SDP_WRITE_ALLOC_STR(":", 0);
                SDP_WRITE_ALLOC_INT(bw->value, 0, 0);
                SDP_WRITE_ALLOC_STR("\r\n", 0);
        }
        //t+r+z
        i=0;
        while ((timing = (GF_SDPTiming*)gf_list_enum(sdp->Timing, &i))) {
                if (timing->NbRepeatOffsets > GF_SDP_MAX_TIMEOFFSET) timing->NbRepeatOffsets = GF_SDP_MAX_TIMEOFFSET;
                if (timing->NbZoneOffsets > GF_SDP_MAX_TIMEOFFSET) timing->NbZoneOffsets = GF_SDP_MAX_TIMEOFFSET;
                //t
                SDP_WRITE_ALLOC_STR("t=", 0);
                SDP_WRITE_ALLOC_INT(timing->StartTime, 1, 0);
                SDP_WRITE_ALLOC_INT(timing->StopTime, 0, 0);
                SDP_WRITE_ALLOC_STR("\r\n", 0);
                if (timing->NbRepeatOffsets) {
                        SDP_WRITE_ALLOC_STR("r=", 0);
                        SDP_WRITE_ALLOC_INT(timing->RepeatInterval, 1, 0);
                        SDP_WRITE_ALLOC_INT(timing->ActiveDuration, 0, 0);
                        for (j=0; j<timing->NbRepeatOffsets; j++) {
                                SDP_WRITE_ALLOC_STR(" ", 0);
                                SDP_WRITE_ALLOC_INT(timing->OffsetFromStart[j], 0, 0);
                        }
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                if (timing->NbZoneOffsets) {
                        SDP_WRITE_ALLOC_STR("z=", 0);
                        for (j=0; j<timing->NbZoneOffsets; j++) {
                                SDP_WRITE_ALLOC_INT(timing->AdjustmentTime[j], 1, 0);
                                if (j+1 == timing->NbRepeatOffsets) {
                                        SDP_WRITE_ALLOC_INT(timing->AdjustmentOffset[j], 0, 1);
                                } else {
                                        SDP_WRITE_ALLOC_INT(timing->AdjustmentOffset[j], 1, 1);
                                }
                        }
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
        }
        //k
        if (sdp->k_method) {
                SDP_WRITE_ALLOC_STR("k=", 0);
                SDP_WRITE_ALLOC_STR(sdp->k_method, 0);
                if (sdp->k_key) {
                        SDP_WRITE_ALLOC_STR(":", 0);
                        SDP_WRITE_ALLOC_STR(sdp->k_key, 0);
                }
                SDP_WRITE_ALLOC_STR("\r\n", 0);
        }
        //a=cat
        TEST_SDP_WRITE_SINGLE("a=cat", sdp->a_cat, 1);
        //a=keywds
        TEST_SDP_WRITE_SINGLE("a=keywds", sdp->a_keywds, 1);
        //a=tool
        TEST_SDP_WRITE_SINGLE("a=tool", sdp->a_tool, 1);
        //a=SendRecv
        switch (sdp->a_SendReceive) {
        case 1:
                TEST_SDP_WRITE_SINGLE("a=", "recvonly", 0);
                break;
        case 2:
                TEST_SDP_WRITE_SINGLE("a=", "sendonly", 0);
                break;
        case 3:
                TEST_SDP_WRITE_SINGLE("a=", "sendrecv", 0);
                break;
        default:
                break;
        }
        //a=type
        TEST_SDP_WRITE_SINGLE("a=type", sdp->a_type, 1);
        //a=charset
        TEST_SDP_WRITE_SINGLE("a=charset", sdp->a_charset, 1);
        //a=sdplang
        TEST_SDP_WRITE_SINGLE("a=sdplang", sdp->a_sdplang, 1);
        //a=lang
        TEST_SDP_WRITE_SINGLE("a=lang", sdp->a_lang, 1);

        //the rest
        i=0;
        while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) {
                SDP_WRITE_ALLOC_STR("a=", 0);
                SDP_WRITE_ALLOC_STR(att->Name, 0);
                if (att->Value) {
                        SDP_WRITE_ALLOC_STR(":", 0);
                        SDP_WRITE_ALLOC_STR(att->Value, 0);
                }
                SDP_WRITE_ALLOC_STR("\r\n", 0);
        }

        //now write media specific
        i=0;
        while ((media = (GF_SDPMedia*)gf_list_enum(sdp->media_desc, &i))) {
                //m=
                SDP_WRITE_ALLOC_STR("m=", 0);
                switch (media->Type) {
                case 1:
                        SDP_WRITE_ALLOC_STR("video", 1);
                        break;
                case 2:
                        SDP_WRITE_ALLOC_STR("audio", 1);
                        break;
                case 3:
                        SDP_WRITE_ALLOC_STR("data", 1);
                        break;
                case 4:
                        SDP_WRITE_ALLOC_STR("control", 1);
                        break;
                default:
                        SDP_WRITE_ALLOC_STR("application", 1);
                        break;
                }
                SDP_WRITE_ALLOC_INT(media->PortNumber, 0, 0);
                if (media->NumPorts >= 2) {
                        SDP_WRITE_ALLOC_STR("/", 0);
                        SDP_WRITE_ALLOC_INT(media->NumPorts, 1, 0);
                } else {
                        SDP_WRITE_ALLOC_STR(" ", 0);
                }
                SDP_WRITE_ALLOC_STR(media->Profile, 1);
                SDP_WRITE_ALLOC_STR(media->fmt_list, 0);

                j=0;
                while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &j))) {
                        SDP_WRITE_ALLOC_STR(" ", 0);
                        SDP_WRITE_ALLOC_INT(map->PayloadType, 0, 0);
                }
                SDP_WRITE_ALLOC_STR("\r\n", 0);

                //c=
                j=0;
                while ((conn = (GF_SDPConnection*)gf_list_enum(media->Connections, &j))) {
                        SDP_WRITE_CONN(conn);
                }

                //k=
                if (media->k_method) {
                        SDP_WRITE_ALLOC_STR("k=", 0);
                        SDP_WRITE_ALLOC_STR(media->k_method, 0);
                        if (media->k_key) {
                                SDP_WRITE_ALLOC_STR(":", 0);
                                SDP_WRITE_ALLOC_STR(media->k_key, 0);
                        }
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                //b
                j=0;
                while ((bw = (GF_SDPBandwidth*)gf_list_enum(media->Bandwidths, &j))) {
                        SDP_WRITE_ALLOC_STR("b=", 0);
                        SDP_WRITE_ALLOC_STR(bw->name, 0);
                        SDP_WRITE_ALLOC_STR(":", 0);
                        SDP_WRITE_ALLOC_INT(bw->value, 0, 0);
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }

                //a=rtpmap
                j=0;
                while ((map = (GF_RTPMap*)gf_list_enum(media->RTPMaps, &j))) {

                        SDP_WRITE_ALLOC_STR("a=rtpmap", 0);
                        SDP_WRITE_ALLOC_STR(":", 0);
                        SDP_WRITE_ALLOC_INT(map->PayloadType, 1, 0);
                        SDP_WRITE_ALLOC_STR(map->payload_name, 0);
                        SDP_WRITE_ALLOC_STR("/", 0);
                        SDP_WRITE_ALLOC_INT(map->ClockRate, 0, 0);
                        if (map->AudioChannels > 1) {
                                SDP_WRITE_ALLOC_STR("/", 0);
                                SDP_WRITE_ALLOC_INT(map->AudioChannels, 0, 0);
                        }
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                //a=fmtp
                j=0;
                while ((fmtp = (GF_SDP_FMTP*)gf_list_enum(media->FMTP, &j))) {
                        SDP_WRITE_ALLOC_STR("a=fmtp:", 0);
                        SDP_WRITE_ALLOC_INT(fmtp->PayloadType, 1 , 0);
                        k=0;
                        while ((att = (GF_X_Attribute*)gf_list_enum(fmtp->Attributes, &k)) ) {
                                if (k>1) SDP_WRITE_ALLOC_STR(";", 0);
                                SDP_WRITE_ALLOC_STR(att->Name, 0);
                                if (att->Value) {
                                        SDP_WRITE_ALLOC_STR("=", 0);
                                        SDP_WRITE_ALLOC_STR(att->Value, 0);
                                }
                        }
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                //a=ptime
                if (media->PacketTime) {
                        SDP_WRITE_ALLOC_STR("a=ptime:", 0);
                        SDP_WRITE_ALLOC_INT(media->PacketTime, 0, 0);
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                //a=FrameRate
                if (media->Type == 1 && media->FrameRate) {
                        SDP_WRITE_ALLOC_STR("a=framerate:", 0);
                        SDP_WRITE_ALLOC_FLOAT(media->FrameRate, 0);
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                //a=SendRecv
                switch (media->SendReceive) {
                case 1:
                        TEST_SDP_WRITE_SINGLE("a=", "recvonly", 0);
                        break;
                case 2:
                        TEST_SDP_WRITE_SINGLE("a=", "sendonly", 0);
                        break;
                case 3:
                        TEST_SDP_WRITE_SINGLE("a=", "sendrecv", 0);
                        break;
                default:
                        break;
                }
                //a=orient
                TEST_SDP_WRITE_SINGLE("a=orient", media->orientation, 1);
                //a=sdplang
                TEST_SDP_WRITE_SINGLE("a=sdplang", media->sdplang, 1);
                //a=lang
                TEST_SDP_WRITE_SINGLE("a=lang", media->lang, 1);
                //a=quality
                if (media->Quality >= 0) {
                        SDP_WRITE_ALLOC_STR("a=quality:", 0);
                        SDP_WRITE_ALLOC_INT(media->Quality, 0, 0);
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
                //the rest
                j=0;
                while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &j))) {
                        SDP_WRITE_ALLOC_STR("a=", 0);
                        SDP_WRITE_ALLOC_STR(att->Name, 0);
                        if (att->Value) {
                                SDP_WRITE_ALLOC_STR(":", 0);
                                SDP_WRITE_ALLOC_STR(att->Value, 0);
                        }
                        SDP_WRITE_ALLOC_STR("\r\n", 0);
                }
        }

        //finally gf_realloc
        //finall NULL char
        pos += 1;
        buf = (char *)gf_realloc(buf, pos);
        *out_str_buf = buf;
        return GF_OK;
}

#endif /*GPAC_DISABLE_STREAMING*/

/* [<][>][^][v][top][bottom][index][help] */