示例#1
0
/*
v=0
o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
[email protected] (Mark Handley)
c=IN IP4 224.2.17.12/127
t=2873397496 2873404696
a=recvonly
m=audio 3456 RTP/AVP 0
m=video 2232 RTP/AVP 31
m=whiteboard 32416 UDP WB
a=orient:portrait
*/
int rtsp_client_sdp(struct rtsp_client_t* rtsp, const char* content)
{
	int i, j, n, count;
	int formats[N_MEDIA_FORMAT];
	struct rtsp_media_t* media;
	void* sdp;

	sdp = sdp_parse(content);
	if (!sdp)
		return -1;

	count = sdp_media_count(sdp);
	if(count > N_MEDIA)
	{
		rtsp->media_ptr = (struct rtsp_media_t*)malloc(sizeof(struct rtsp_media_t)*(count-N_MEDIA));
		if(!rtsp->media_ptr)
		{
			sdp_destroy(sdp);
			return ENOMEM;
		}
		memset(rtsp->media_ptr, 0, sizeof(struct rtsp_media_t)*(count-N_MEDIA));
	}

	rtsp->media_count = count;

	// rfc 2326 C.1.1 Control URL (p80)
	// If found at the session level, the attribute indicates the URL for aggregate control
	rtsp->aggregate = rtsp_media_aggregate_control_enable(sdp);
	rtsp_get_session_uri(sdp, rtsp->aggregate_uri, sizeof(rtsp->aggregate_uri), rtsp->uri, rtsp->baseuri, rtsp->location);

	for(i = 0; i < count; i++)
	{
		media = rtsp_get_media(rtsp, i);
		//media->cseq = rand();

		// RTSP2326 C.1.1 Control URL
		rtsp_get_media_uri(sdp, i, media->uri, sizeof(media->uri), rtsp->aggregate_uri);

		n = sdp_media_formats(sdp, i, formats, N_MEDIA_FORMAT);
		media->avformat_count = n > N_MEDIA_FORMAT ? N_MEDIA_FORMAT : n;
		for(j = 0; j < media->avformat_count; j++)
		{
			media->avformats[j].fmt = formats[j];
		}

		// update media encoding
		sdp_media_attribute_list(sdp, i, NULL, rtsp_media_onattr, media);		
	}

	sdp_destroy(sdp);
	return 0;
}
示例#2
0
/*
v=0
o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps
[email protected] (Mark Handley)
c=IN IP4 224.2.17.12/127
t=2873397496 2873404696
a=recvonly
m=audio 3456 RTP/AVP 0
m=video 2232 RTP/AVP 31
m=whiteboard 32416 UDP WB
a=orient:portrait
*/
int rtsp_client_sdp(struct rtsp_client_context_t* ctx, void* sdp)
{
	int i, count;
	int formats[N_MEDIA_FORMAT];
	struct rtsp_media_t* media;

	assert(sdp);
	count = sdp_media_count(sdp);
	if(count > N_MEDIA)
	{
		ctx->media_ptr = (struct rtsp_media_t*)malloc(sizeof(struct rtsp_media_t)*(count-N_MEDIA));
		if(!ctx->media_ptr)
			return -1;
		memset(ctx->media_ptr, 0, sizeof(struct rtsp_media_t)*(count-N_MEDIA));
	}

	ctx->media_count = count;

	// rfc 2326 C.1.1 Control URL (p80)
	// If found at the session level, the attribute indicates the URL for aggregate control
	ctx->aggregate = rtsp_media_aggregate_control_enable(sdp);
	rtsp_get_session_uri(sdp, ctx->aggregate_uri, sizeof(ctx->aggregate_uri), ctx->uri, ctx->baseuri, ctx->location);

	for(i = 0; i < count; i++)
	{
		int j, n;
		media = rtsp_get_media(ctx, i);
		media->cseq = rand();

		// RTSP2326 C.1.1 Control URL
		rtsp_get_media_uri(sdp, i, media->uri, sizeof(media->uri), ctx->aggregate_uri);

		n = sdp_media_formats(sdp, i, formats, N_MEDIA_FORMAT);
		media->avformat_count = n > N_MEDIA_FORMAT ? N_MEDIA_FORMAT : n;
		for(j = 0; j < media->avformat_count; j++)
		{
			media->avformats[j].pt = formats[j];
		}

		// update media encoding
		sdp_media_attribute_list(sdp, i, NULL, rtsp_media_onattr, media);		
	}

	return 0;
}
示例#3
0
/** Upgrade m= lines within session */
static
int soa_sdp_upgrade(soa_session_t *ss,
		    su_home_t *home,
		    sdp_session_t *session,
		    sdp_session_t const *user,
		    sdp_session_t const *remote,
		    int **return_u2s,
		    int **return_s2u)
{
  soa_static_session_t *sss = (soa_static_session_t *)ss;

  int Ns, Nu, Nr, Nmax, n, i, j;
  sdp_media_t *m, **mm, *um;
  sdp_media_t **s_media, **o_media, **u_media;
  sdp_media_t const *rm, **r_media;
  int *u2s = NULL, *s2u = NULL;

  if (session == NULL || user == NULL)
    return (errno = EFAULT), -1;

  Ns = sdp_media_count(session, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0);
  Nu = sdp_media_count(user, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0);
  Nr = sdp_media_count(remote, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0);

  if (remote == NULL)
    Nmax = Ns + Nu;
  else if (Ns < Nr)
    Nmax = Nr;
  else
    Nmax = Ns;

  s_media = su_zalloc(home, (Nmax + 1) * (sizeof *s_media));
  o_media = su_zalloc(home, (Ns + 1) * (sizeof *o_media));
  u_media = su_zalloc(home, (Nu + 1) * (sizeof *u_media));
  r_media = su_zalloc(home, (Nr + 1) * (sizeof *r_media));
  if (!s_media || !o_media || !u_media || !r_media)
    return -1;

  um = sdp_media_dup_all(home, user->sdp_media, session);
  if (!um && user->sdp_media)
    return -1;

  u2s = su_alloc(home, (Nu + 1) * sizeof(*u2s));
  s2u = su_alloc(home, (Nmax + 1) * sizeof(*s2u));
  if (!u2s || !s2u)
    return -1;

  for (i = 0; i < Nu; i++)
    u2s[i] = U2S_NOT_USED;
  u2s[Nu] = U2S_SENTINEL;

  for (i = 0; i < Nmax; i++)
    s2u[i] = U2S_NOT_USED;
  s2u[Nmax] = U2S_SENTINEL;

  for (i = 0, m = session->sdp_media; m && i < Ns; m = m->m_next)
    o_media[i++] = m;
  assert(i == Ns);
  for (i = 0, m = um; m && i < Nu; m = m->m_next)
    u_media[i++] = m;
  assert(i == Nu);
  m = remote ? remote->sdp_media : NULL;
  for (i = 0; m && i < Nr; m = m->m_next)
      r_media[i++] = m;
  assert(i == Nr);

  if (sss->sss_ordered_user && sss->sss_u2s) {     /* User SDP is ordered */
    for (j = 0; sss->sss_u2s[j] != U2S_SENTINEL; j++) {
      i = sss->sss_u2s[j];
      if (i == U2S_NOT_USED)
	continue;
      if (j >= Nu) /* lines removed from user SDP */
	continue;
	  if (i >= Ns) /* I should never be called but somehow i and Ns are 0 here sometimes */
    continue;
      s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
      u2s[j] = i, s2u[i] = j;
    }
  }

  if (remote) {
    /* Update session according to remote */
    for (i = 0; i < Nr; i++) {
      rm = r_media[i];
      m = s_media[i];

      if (!m) {
	int codec_mismatch = 0;

	if (!rm->m_rejected)
	  j = soa_sdp_matching_mindex(ss, u_media, rm, &codec_mismatch);
	else
	  j = -1;

	if (j == -1) {
	  s_media[i] = soa_sdp_make_rejected_media(home, rm, session, 0);
	  continue;
	}
	else if (codec_mismatch && !ss->ss_rtp_mismatch) {
	  m = soa_sdp_make_rejected_media(home, u_media[j], session, 1);
	  soa_sdp_set_rtpmap_pt(s_media[i] = m, rm);
	  continue;
	}

	s_media[i] = m = u_media[j]; u_media[j] = SDP_MEDIA_NONE;
	u2s[j] = i, s2u[i] = j;
      }

      if (sdp_media_uses_rtp(rm))
	soa_sdp_media_upgrade_rtpmaps(ss, m, rm);
    }
  }
  else {

    if (sss->sss_ordered_user) {
      /* Update session with unused media in u_media */

      if (!sss->sss_reuse_rejected) {
	/* Mark previously used slots */
	for (i = 0; i < Ns; i++) {
	  if (s_media[i])
	    continue;
	  s_media[i] =
	    soa_sdp_make_rejected_media(home, o_media[i], session, 0);
	}
      }

      for (j = 0; j < Nu; j++) {
	if (u_media[j] == SDP_MEDIA_NONE)
	  continue;

	for (i = 0; i < Nmax; i++) {
	  if (s_media[i] == NULL) {
	    s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
	    u2s[j] = i, s2u[i] = j;
	    break;
	  }
	}

	assert(i != Nmax);
      }
    }

    /* Match unused user media by media types with the existing session */
    for (i = 0; i < Ns; i++) {
      if (s_media[i])
	continue;

      j = soa_sdp_matching_mindex(ss, u_media, o_media[i], NULL);
      if (j == -1) {
	s_media[i] = soa_sdp_make_rejected_media(home, o_media[i], session, 0);
	continue;
      }

      s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
      u2s[j] = i, s2u[i] = j;
    }

    /* Here we just append new media at the end */
    for (j = 0; j < Nu; j++) {
      if (u_media[j] != SDP_MEDIA_NONE) {
	s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
	u2s[j] = i, s2u[i] = j;
	i++;
      }
    }
    assert(i <= Nmax);
  }

  mm = &session->sdp_media;
  for (i = 0; s_media[i]; i++) {
    m = s_media[i]; *mm = m; mm = &m->m_next;
  }
  *mm = NULL;

  s2u[n = i] = U2S_SENTINEL;
  *return_u2s = u2s;
  *return_s2u = s2u;

#ifndef NDEBUG			/* X check */
  for (j = 0; j < Nu; j++) {
    i = u2s[j];
    assert(i == U2S_NOT_USED || s2u[i] == j);
  }
  for (i = 0; i < n; i++) {
    j = s2u[i];
    assert(j == U2S_NOT_USED || u2s[j] == i);
  }
#endif

  return 0;
}
示例#4
0
/**
 * Updates the modified copy of local SDP based
 * on application provided local SDP and remote SDP.
 */
static int offer_answer_step(soa_session_t *ss,
			     enum offer_answer_action action,
			     char const *by)
{
  soa_static_session_t *sss = (soa_static_session_t *)ss;

  sdp_session_t *local = ss->ss_local->ssd_sdp;
  sdp_session_t local0[1];

  sdp_session_t *user = ss->ss_user->ssd_sdp;
  unsigned user_version = ss->ss_user_version;

  sdp_session_t *remote = ss->ss_remote->ssd_sdp;
  unsigned remote_version = ss->ss_remote_version;

  int fresh = 0;

  sdp_origin_t o[1] = {{ sizeof(o) }};
  sdp_connection_t *c, c0[1] = {{ sizeof(c0) }};
  char c0_buffer[64];

  sdp_time_t t[1] = {{ sizeof(t) }};

  int *u2s = NULL, *s2u = NULL, *tbf;

  sdp_session_t *latest = NULL, *previous = NULL;

  char const *phrase = "Internal Media Error";

  su_home_t tmphome[SU_HOME_AUTO_SIZE(8192)];

  su_home_auto(tmphome, sizeof tmphome);

  SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n",
	      (void *)ss, by));

  if (user == NULL)
    return soa_set_status(ss, 500, "No session set by user");

  if (action == generate_offer)
    remote = NULL;
  else if (remote == NULL)
    return soa_set_status(ss, 500, "No remote SDP");

  /* Pre-negotiation Step: Expand truncated remote SDP */
  if (local && remote) switch (action) {
  case generate_answer:
  case process_answer:
    if (sdp_media_count(remote, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0) <
	sdp_media_count(local, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0)) {
      SU_DEBUG_5(("%s: remote %s is truncated: expanding\n",
		  by, action == generate_answer ? "offer" : "answer"));
      remote = soa_sdp_expand_media(tmphome, remote, local);
      if (remote == NULL)
	return soa_set_status(ss, 500, "Cannot expand remote session");
    }
  default:
    break;
  }

  /* Step A: Create local SDP session (based on user-supplied SDP) */
  if (local == NULL) switch (action) {
  case generate_offer:
  case generate_answer:
    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		"generating local description"));

    fresh = 1;
    local = local0;
    *local = *user, local->sdp_media = NULL;

    o->o_username = "******";
    o->o_address = c0;
    c0->c_address = c0_buffer;

    if (!local->sdp_origin)
      local->sdp_origin = o;
    break;

  case process_answer:
  default:
    goto internal_error;
  }

  /* Step B: upgrade local SDP (add m= lines to it)  */
  switch (action) {
  case generate_offer:
    /* Upgrade local SDP based on user SDP */
    if (local != local0 && ss->ss_local_user_version == user_version)
      break;
    if (local != local0)
      *local0 = *local, local = local0;
    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		"upgrade with local description"));
    if (soa_sdp_upgrade(ss, tmphome, local, user, NULL, &u2s, &s2u) < 0)
      goto internal_error;
    break;
  case generate_answer:
    /* Upgrade local SDP based on remote SDP */
    if (ss->ss_local_user_version == user_version &&
	ss->ss_local_remote_version == remote_version)
      break;
    if (1) {
      if (local != local0)
	*local0 = *local, local = local0;
      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		  "upgrade with remote description"));
      if (soa_sdp_upgrade(ss, tmphome, local, user, remote, &u2s, &s2u) < 0)
	goto internal_error;
    }
    break;
  case process_answer:
  default:
    break;
  }


  /* Step C: reject media */
  switch (action) {
  case generate_offer:
    /* Local media is marked as rejected already in upgrade phase */
    break;
  case generate_answer:
  case process_answer:
    if (ss->ss_local_remote_version == remote_version)
      break;
    if (soa_sdp_reject_is_needed(local, remote)) {
      if (local != local0) {
	*local0 = *local, local = local0;
#define DUP_LOCAL(local)					 \
	do {							 \
	  if (!local->sdp_media) break;				 \
	  local->sdp_media =					 \
	    sdp_media_dup_all(tmphome, local->sdp_media, local); \
	  if (!local->sdp_media)				 \
	    goto internal_error;				 \
	} while (0)
	DUP_LOCAL(local);
      }
      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		  "marking rejected media"));
      soa_sdp_reject(tmphome, local, remote);
    }
    break;
  default:
    break;
  }

  /* Step D: Set media mode bits */
  switch (action) {
    int const *s2u_;

  case generate_offer:
  case generate_answer:
  case process_answer:
    s2u_ = s2u;

    if (!s2u_) s2u_ = sss->sss_s2u;

    if (soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 1)) {
      if (local != local0) {
	*local0 = *local, local = local0;
	DUP_LOCAL(local);
      }

      soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 0);
    }
    break;
  default:
    break;
  }

  /* Step E: Upgrade codecs by answer. */
  switch (action) {
  case process_answer:
    /* Upgrade local SDP based on remote SDP */
    if (ss->ss_local_remote_version == remote_version)
      break;
    if (1 /* We don't have good test for codecs */) {
      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		  "upgrade codecs with remote description"));
      if (local != local0) {
	*local0 = *local, local = local0;
	DUP_LOCAL(local);
      }
      soa_sdp_session_upgrade_rtpmaps(ss, local, remote);
    }
    break;
  case generate_offer:
  case generate_answer:
  default:
    break;
  }

  /* Step F0: Initialize o= line */
  if (fresh) {
	if (user->sdp_origin) {
      o->o_username = user->sdp_origin->o_username;

	  if (user->sdp_origin->o_address)
	    o->o_address = user->sdp_origin->o_address;

      if (user->sdp_origin->o_id)
		  o->o_id = user->sdp_origin->o_id;

	  if (user->sdp_origin->o_version && user->sdp_origin->o_version != o->o_version) {
		  o->o_version = user->sdp_origin->o_version;
		  o->o_version--;
	  }
	}

    if (soa_init_sdp_origin_with_session(ss, o, c0_buffer, local) < 0) {
      phrase = "Cannot Get IP Address for Session Description";
      goto internal_error;
    }

    local->sdp_origin = o;
  }

  /* Step F: Update c= line(s) */
  switch (action) {
    sdp_connection_t *user_c, *local_c;

  case generate_offer:
  case generate_answer:
    user_c = user->sdp_connection;
    if (!soa_check_sdp_connection(user_c))
      user_c = NULL;

    local_c = local->sdp_connection;
    if (!soa_check_sdp_connection(local_c))
      local_c = NULL;

    if (ss->ss_local_user_version != user_version ||
	local_c == NULL ||
	(user_c != NULL && sdp_connection_cmp(local_c, user_c))) {
      sdp_media_t *m;

      if (user_c)
	c = user_c;
      else
	c = local->sdp_origin->o_address;

      /* Every m= line (even rejected one) must have a c= line
       * or there must be a c= line at session level
       */
      for (m = local->sdp_media; m; m = m->m_next)
	if (m->m_connections == NULL)
	  break;

      if (m) {
	if (local != local0) {
	  *local0 = *local, local = local0;
	  DUP_LOCAL(local);
	}
	local->sdp_connection = c;
      }
    }
    break;

  default:
    break;
  }

  soa_description_free(ss, ss->ss_previous);
  su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL;
  su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL;

  if (u2s) {
    u2s = u2s_alloc(ss->ss_home, u2s);
    s2u = u2s_alloc(ss->ss_home, s2u);
    if (!u2s || !s2u)
      goto internal_error;
  }

  if (ss->ss_local->ssd_sdp != local &&
      sdp_session_cmp(ss->ss_local->ssd_sdp, local)) {
    int bump;

    switch (action) {
    case generate_offer:
      bump = sdp_session_cmp(local, sss->sss_latest);
      break;
    case generate_answer:
      bump = 1;
      break;
    case process_answer:
    default:
      bump = 0;
      break;
    }

    if (bump) {
      /* Upgrade the version number */
      if (local->sdp_origin != o)
	*o = *local->sdp_origin, local->sdp_origin = o;
      o->o_version++;
    }

    /* Do sanity checks for the created SDP */
    if (!local->sdp_subject)	/* s= is mandatory */
      local->sdp_subject = "-";
    if (!local->sdp_time)	/* t= is mandatory */
      local->sdp_time = t;

    if (action == generate_offer) {
      /* Keep a copy of previous session state */
      int *previous_u2s = u2s_alloc(ss->ss_home, sss->sss_u2s);
      int *previous_s2u = u2s_alloc(ss->ss_home, sss->sss_s2u);

      if ((sss->sss_u2s && !previous_u2s) || (sss->sss_s2u && !previous_s2u))
	goto internal_error;

      *ss->ss_previous = *ss->ss_local;
      memset(ss->ss_local, 0, (sizeof *ss->ss_local));
      ss->ss_previous_user_version = ss->ss_local_user_version;
      ss->ss_previous_remote_version = ss->ss_local_remote_version;
      sss->sss_previous.u2s = previous_u2s;
      sss->sss_previous.s2u = previous_s2u;
    }

    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		"storing local description"));

    /* Update the unparsed and pretty-printed descriptions  */
    if (soa_description_set(ss, ss->ss_local, local, NULL, 0) < 0) {
      if (action == generate_offer) {
	/* Remove 2nd reference to local session state */
	memset(ss->ss_previous, 0, (sizeof *ss->ss_previous));
	ss->ss_previous_user_version = 0;
	ss->ss_previous_remote_version = 0;
	su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL;
	su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL;
      }

      su_free(ss->ss_home, u2s), su_free(ss->ss_home, s2u);

      goto internal_error;
    }

    if (bump) {
      latest = sdp_session_dup(ss->ss_home, ss->ss_local->ssd_sdp);
      previous = sss->sss_latest;
    }
  }

  if (u2s) {
    tbf = sss->sss_u2s, sss->sss_u2s = u2s, su_free(ss->ss_home, tbf);
    tbf = sss->sss_s2u, sss->sss_s2u = s2u, su_free(ss->ss_home, tbf);
  }

  /* Update version numbers */
  switch (action) {
  case generate_offer:
    ss->ss_local_user_version = user_version;
    sss->sss_latest = latest;
    break;
  case generate_answer:
    ss->ss_local_user_version = user_version;
    ss->ss_local_remote_version = remote_version;
    sss->sss_latest = latest;
    break;
  case process_answer:
    ss->ss_local_remote_version = remote_version;
  default:
    break;
  }

  if (previous)
    su_free(ss->ss_home, previous);

  su_home_deinit(tmphome);
  return 0;

 internal_error:
  su_home_deinit(tmphome);
  return soa_set_status(ss, 500, phrase);
}