static const gchar * kms_base_rtp_session_process_remote_ssrc (KmsBaseRtpSession * self, GstSDPMedia * remote_media, SdpMediaConfig * neg_mconf) { const gchar *media_str = gst_sdp_media_get_media (remote_media); guint ssrc; ssrc = sdp_utils_media_get_fid_ssrc (remote_media, 0); if (ssrc == 0) { ssrc = sdp_utils_media_get_ssrc (remote_media); } if (g_strcmp0 (AUDIO_STREAM_NAME, media_str) == 0) { GST_DEBUG_OBJECT (self, "Add remote audio ssrc: %u", ssrc); self->remote_audio_ssrc = ssrc; self->audio_neg_mconf = neg_mconf; return AUDIO_RTP_SESSION_STR; } else if (g_strcmp0 (VIDEO_STREAM_NAME, media_str) == 0) { GST_DEBUG_OBJECT (self, "Add remote video ssrc: %u", ssrc); self->remote_video_ssrc = ssrc; self->video_neg_mconf = neg_mconf; return VIDEO_RTP_SESSION_STR; } GST_WARNING_OBJECT (self, "Media '%s' not supported", media_str); return NULL; }
static SdpHandler * kms_sdp_agent_get_handler_for_media (KmsSdpAgent * agent, const GstSDPMedia * media) { GSList *l; for (l = agent->priv->handlers; l != NULL; l = l->next) { SdpHandler *sdp_handler; sdp_handler = l->data; if (g_strcmp0 (sdp_handler->media, gst_sdp_media_get_media (media)) != 0) { /* This handler can not manage this media */ continue; } if (!kms_sdp_media_handler_manage_protocol (sdp_handler->handler, gst_sdp_media_get_proto (media))) { continue; } return sdp_handler; } return NULL; }
GstSDPMedia * kms_sdp_reject_media_handler_create_answer (KmsSdpMediaHandler * handler, const GstSDPMessage * msg, const GstSDPMedia * offer, GError ** error) { GstSDPMedia *m = NULL; if (gst_sdp_media_new (&m) != GST_SDP_OK) { g_set_error (error, KMS_SDP_AGENT_ERROR, SDP_AGENT_UNEXPECTED_ERROR, "Can not create '%s' media answer", gst_sdp_media_get_media (offer)); goto error; } /* Create m-line */ if (!KMS_SDP_MEDIA_HANDLER_GET_CLASS (handler)->init_answer (handler, offer, m, error)) { goto error; } /* Add attributes to m-line */ if (!KMS_SDP_MEDIA_HANDLER_GET_CLASS (handler)->add_answer_attributes (handler, offer, m, error)) { goto error; } return m; error: if (m != NULL) { gst_sdp_media_free (m); } return NULL; }
static GstSDPMedia * reject_media_answer (const GstSDPMedia * offered) { GstSDPMedia *media; const gchar *mid; guint i, len; gst_sdp_media_new (&media); /* [rfc3264] To reject an offered stream, the port number in the */ /* corresponding stream in the answer MUST be set to zero. Any */ /* media formats listed are ignored. */ gst_sdp_media_set_media (media, gst_sdp_media_get_media (offered)); gst_sdp_media_set_port_info (media, 0, 1); gst_sdp_media_set_proto (media, gst_sdp_media_get_proto (offered)); len = gst_sdp_media_formats_len (offered); for (i = 0; i < len; i++) { const gchar *format; format = gst_sdp_media_get_format (offered, i); gst_sdp_media_insert_format (media, i, format); } /* [rfc5888] mid attribute must be present in answer as well */ mid = gst_sdp_media_get_attribute_val (offered, "mid"); if (mid == NULL) { return media; } gst_sdp_media_add_attribute (media, "mid", mid); return media; }
static gboolean kms_base_rtp_session_configure_connection (KmsBaseRtpSession * self, SdpMediaConfig * neg_mconf, SdpMediaConfig * remote_mconf, gboolean offerer) { GstSDPMedia *neg_media = kms_sdp_media_config_get_sdp_media (neg_mconf); const gchar *neg_proto_str = gst_sdp_media_get_proto (neg_media); const gchar *neg_media_str = gst_sdp_media_get_media (neg_media); GstSDPMedia *remote_media = kms_sdp_media_config_get_sdp_media (remote_mconf); const gchar *remote_proto_str = gst_sdp_media_get_proto (remote_media); const gchar *remote_media_str = gst_sdp_media_get_media (remote_media); gboolean active; if (g_strcmp0 (neg_proto_str, remote_proto_str) != 0) { GST_WARNING_OBJECT (self, "Negotiated proto ('%s') not matching with remote proto ('%s')", neg_proto_str, remote_proto_str); return FALSE; } if (!kms_utils_contains_proto (neg_proto_str, "RTP")) { GST_DEBUG_OBJECT (self, "'%s' protocol does not need RTP connection", neg_proto_str); /* It cannot be managed here but could be managed by the child class */ return FALSE; } if (g_strcmp0 (neg_media_str, remote_media_str) != 0) { GST_WARNING_OBJECT (self, "Negotiated media ('%s') not matching with remote media ('%s')", neg_media_str, remote_media_str); return FALSE; } if (kms_base_rtp_session_process_remote_ssrc (self, remote_media, neg_mconf) == NULL) { return TRUE; /* It cannot be managed here but could be managed by the child class */ } active = sdp_utils_media_is_active (neg_media, offerer); return kms_base_rtp_session_add_connection_for_session (self, neg_mconf, active); }
static void kms_rtp_endpoint_start_transport_send (KmsBaseSdpEndpoint * base_sdp_endpoint, gboolean offerer) { KmsRtpEndpoint *self = KMS_RTP_ENDPOINT (base_sdp_endpoint); SdpMessageContext *remote_ctx = kms_base_sdp_endpoint_get_remote_sdp_ctx (base_sdp_endpoint); const GstSDPMessage *sdp = kms_sdp_message_context_get_sdp_message (remote_ctx); const GSList *item = kms_sdp_message_context_get_medias (remote_ctx); const GstSDPConnection *msg_conn = gst_sdp_message_get_connection (sdp); /* Chain up */ KMS_BASE_SDP_ENDPOINT_CLASS (parent_class)->start_transport_send (base_sdp_endpoint, offerer); for (; item != NULL; item = g_slist_next (item)) { SdpMediaConfig *mconf = item->data; GstSDPMedia *media = kms_sdp_media_config_get_sdp_media (mconf); const GstSDPConnection *media_con; KmsRtpBaseConnection *conn; guint port; if (media->port == 0) { continue; } if (gst_sdp_media_connections_len (media) != 0) { media_con = gst_sdp_media_get_connection (media, 0); } else { media_con = msg_conn; } if (media_con == NULL || media_con->address == NULL || media_con->address[0] == '\0') { const gchar *media_str = gst_sdp_media_get_media (media); GST_WARNING_OBJECT (self, "Missing connection information for '%s'", media_str); continue; } conn = kms_rtp_endpoint_media_get_connection (self, mconf); if (conn == NULL) { continue; } port = gst_sdp_media_get_port (media); kms_rtp_base_connection_set_remote_info (conn, media_con->address, port, port + 1); /* TODO: get rtcp port from attr if it exists */ } }
static gboolean test_response_sdp (GstRTSPClient * client, GstRTSPMessage * response, gboolean close, gpointer user_data) { guint8 *data; guint size; GstSDPMessage *sdp_msg; const GstSDPMedia *sdp_media; const GstSDPBandwidth *bw; gint bandwidth_val = GPOINTER_TO_INT (user_data); fail_unless (gst_rtsp_message_get_body (response, &data, &size) == GST_RTSP_OK); gst_sdp_message_new (&sdp_msg); fail_unless (gst_sdp_message_parse_buffer (data, size, sdp_msg) == GST_SDP_OK); /* session description */ /* v= */ fail_unless (gst_sdp_message_get_version (sdp_msg) != NULL); /* o= */ fail_unless (gst_sdp_message_get_origin (sdp_msg) != NULL); /* s= */ fail_unless (gst_sdp_message_get_session_name (sdp_msg) != NULL); /* t=0 0 */ fail_unless (gst_sdp_message_times_len (sdp_msg) == 0); /* verify number of medias */ fail_unless (gst_sdp_message_medias_len (sdp_msg) == 1); /* media description */ sdp_media = gst_sdp_message_get_media (sdp_msg, 0); fail_unless (sdp_media != NULL); /* m= */ fail_unless (gst_sdp_media_get_media (sdp_media) != NULL); /* media bandwidth */ if (bandwidth_val) { fail_unless (gst_sdp_media_bandwidths_len (sdp_media) == 1); bw = gst_sdp_media_get_bandwidth (sdp_media, 0); fail_unless (bw != NULL); fail_unless (g_strcmp0 (bw->bwtype, "AS") == 0); fail_unless (bw->bandwidth == bandwidth_val); } else { fail_unless (gst_sdp_media_bandwidths_len (sdp_media) == 0); } gst_sdp_message_free (sdp_msg); return TRUE; }
static gboolean create_media_answer (const GstSDPMedia * media, struct SdpAnswerData *data) { KmsSdpAgent *agent = data->agent; GstSDPMedia *answer_media = NULL; SdpHandler *sdp_handler; GError **err = data->err; SDP_AGENT_LOCK (agent); sdp_handler = kms_sdp_agent_get_handler_for_media (agent, media); SDP_AGENT_UNLOCK (agent); if (sdp_handler == NULL) { GST_WARNING_OBJECT (agent, "No handler for '%s' media proto '%s' found", gst_sdp_media_get_media (media), gst_sdp_media_get_proto (media)); goto answer; } answer_media = kms_sdp_media_handler_create_answer (sdp_handler->handler, data->ctx, media, err); if (answer_media == NULL) { return FALSE; } answer: { SdpMediaConfig *mconf; gboolean do_call = TRUE; if (answer_media == NULL) { answer_media = reject_media_answer (media); do_call = FALSE; } mconf = kms_sdp_message_context_add_media (data->ctx, answer_media, err); if (mconf == NULL) { return FALSE; } if (do_call && data->agent->priv->configure_media_callback_data != NULL) { data->agent->priv->configure_media_callback_data->callback (data->agent, mconf, data->agent->priv->configure_media_callback_data->user_data); } return TRUE; } }
static gboolean kms_sdp_reject_media_handler_init_answer (KmsSdpMediaHandler * handler, const GstSDPMedia * offer, GstSDPMedia * answer, GError ** error) { guint i, len; if (gst_sdp_media_set_media (answer, gst_sdp_media_get_media (offer)) != GST_SDP_OK) { g_set_error (error, KMS_SDP_AGENT_ERROR, SDP_AGENT_INVALID_PARAMETER, "Can not set '%s' media ttribute", gst_sdp_media_get_media (offer)); return FALSE; } if (gst_sdp_media_set_proto (answer, gst_sdp_media_get_proto (offer)) != GST_SDP_OK) { g_set_error (error, KMS_SDP_AGENT_ERROR, SDP_AGENT_INVALID_PARAMETER, "Can not set proto '%s' attribute", gst_sdp_media_get_proto (offer)); return FALSE; } if (gst_sdp_media_set_port_info (answer, 0, 1) != GST_SDP_OK) { g_set_error_literal (error, KMS_SDP_AGENT_ERROR, SDP_AGENT_INVALID_PARAMETER, "Can not set port info attribute"); return FALSE; } len = gst_sdp_media_formats_len (offer); for (i = 0; i < len; i++) { const gchar *format; format = gst_sdp_media_get_format (offer, i); gst_sdp_media_insert_format (answer, i, format); } return TRUE; }
static gboolean kms_sdp_rtp_avpf_media_handler_add_rtcp_fb_attrs (KmsSdpMediaHandler * handler, GstSDPMedia * media, GError ** error) { const gchar *media_str; guint i; media_str = gst_sdp_media_get_media (media); if (g_strcmp0 (media_str, "video") != 0) { /* Only nack video rtcp_fb attributes are supported */ /* [rfc4585] 4.2 */ return TRUE; } for (i = 0;; i++) { const gchar *val; gchar **codec; val = gst_sdp_media_get_attribute_val_n (media, "rtpmap", i); if (val == NULL) { break; } codec = g_strsplit (val, " ", 0); if (!is_supported_encoder (codec[1] /* encoder */ )) { g_strfreev (codec); continue; } if (!kms_sdp_rtp_avpf_media_handler_rtcp_fb_attrs (handler, media, codec[0] /* format */ , codec[1] /* encoder */ , error)) { g_strfreev (codec); return FALSE; } g_strfreev (codec); } return TRUE; }
static __inline__ void rtp_session_attach_loop(RTP_Session *rtp_s) { const gchar *media; gint w = LOOP_WEIGHT_AUDIO; if (rtp_s->interleaved) return; media = gst_sdp_media_get_media(rtp_s->m); if (media && strstr(media, "video")) /* fix me */ { w = LOOP_WEIGHT_VEDIO; } g_scheduler_add((GEvent*)rtp_s->rtp_src, w); #ifdef CONFIG_RTCP_SUPPORT g_scheduler_add((GEvent*)rtp_s->rtcp_src, LOOP_WEIGHT_RTCP); #endif }
SdpMediaConfig * kms_sdp_message_context_add_media (SdpMessageContext * ctx, GstSDPMedia * media, GError ** error) { SdpMediaConfig *mconf; const gchar *media_type; gchar *mid; guint *counter; if (ctx->type == KMS_SDP_ANSWER && g_slist_length (ctx->groups) > 0 && gst_sdp_media_get_attribute_val (media, "mid") == NULL) { g_set_error_literal (error, KMS_SDP_AGENT_ERROR, SDP_AGENT_UNEXPECTED_ERROR, "Missing mid attribute for media"); return NULL; } if (configure_pending_mediaconfig (ctx, media, &mconf)) { return mconf; } media_type = gst_sdp_media_get_media (media); counter = g_hash_table_lookup (ctx->mids, media_type); if (counter == NULL) { /* No stored medias of this type yet */ counter = g_slice_new0 (guint); g_hash_table_insert (ctx->mids, g_strdup (media_type), counter); } mid = g_strdup_printf ("%s%u", media_type, (*counter)++); mconf = kms_sdp_context_new_media_config (g_slist_length (ctx->medias), mid, media); ctx->medias = g_slist_append (ctx->medias, mconf); return mconf; }
static __inline__ RTP_Session * rtp_session_create(RTSP_media *medium, const GstSDPMedia *m) { RTSP_client_session *rtsp_s; gint err; RTP_Session *rtp_s; gchar *media; rtsp_s = (RTSP_client_session*)medium->rtsp_s; rtp_s = rtp_session_new(rtsp_s); if (!rtp_s) { return NULL; } rtp_s->m = (GstSDPMedia*)m; rtp_s->rtsp_s = rtsp_client_session_ref(rtsp_s); #if 0 const gchar *pt_value; pt_value = gst_sdp_media_get_attribute_val(m, "rtpmap"); if (strstr(pt_value, "H264")) { rtp_s->pt = 96; rtp_s->parser = rtp_parser_create((gchar*)"H264"); if (!rtp_s->parser) { fnc_log(FNC_LOG_ERR, "[FC] Init RTP parser failed, mime:'%s'", "H264"); goto no_rtp_parser; } } else if (strstr(pt_value, "JPF-GENERIC")) { rtp_s->pt = 99; rtp_s->parser = rtp_parser_create((gchar*)"JPF-GENERIC"); if (!rtp_s->parser) { fnc_log(FNC_LOG_ERR, "[FC] Init RTP parser failed, mime:'%s'", "JPF-GENERIC"); goto no_rtp_parser; } } else { goto no_rtp_parser; } #else rtp_s->pt = 0; rtp_s->parser = rtp_parser_create((gchar*)"RTP-FAKE"); if (!rtp_s->parser) { fnc_log(FNC_LOG_ERR, "[FC] Init RTP parser failed, mime:'%s'", "RTP"); goto no_rtp_parser; } #endif media = gst_sdp_media_get_media(m); if (!strcmp(media, "video")) rtp_s->stm_type = STM_VIDEO; else if (!strcmp(media, "audio")) rtp_s->stm_type = STM_AUDIO; else rtp_s->stm_type = STM_OTHER; err = rtp_s->parser->initialize(rtp_s, rtp_s->parser, rtp_s->pt); if (err) { rtp_parser_release(rtp_s->parser); goto no_rtp_parser; } rtp_s->ssrc = &rtp_s->rtp_ssrc_init; if (!rtp_s->interleaved) { rtp_s->rtp_src = rtp_session_create_event(rtp_s, R_EV_RTP, rtp_s->rtp_sock); #ifdef CONFIG_RTCP_SUPPORT rtp_s->rtcp_src = rtp_session_create_event(rtp_s, R_EV_RTCP, rtp_s->rtcp_sock); #endif } return rtp_s; no_rtp_parser: rtp_session_free(rtp_s); return NULL; }