static CodecBlueprint * _find_matching_blueprint (FsCodec *codec, GList *blueprints) { GList *item = NULL; GstCaps *caps = NULL; caps = fs_codec_to_gst_caps (codec); if (!caps) { gchar *tmp = fs_codec_to_string (codec); GST_WARNING ("Could not transform codec into caps: %s", tmp); g_free (tmp); return NULL; } for (item = g_list_first (blueprints); item; item = g_list_next (item)) { CodecBlueprint *bp = item->data; if (gst_caps_can_intersect (caps, bp->rtp_caps)) break; } gst_caps_unref (caps); if (item) return item->data; else return NULL; }
static void _src_pad_added (FsStream *stream, GstPad *pad, FsCodec *codec, gpointer user_data) { struct SimpleTestStream *st = user_data; GstElement *fakesink = gst_element_factory_make ("fakesink", NULL); GstPad *fakesink_pad = NULL; GstPadLinkReturn ret; FsCodec *codeccopy = fs_codec_copy (codec); gchar *str = NULL; g_assert (fakesink); g_object_set (fakesink, "signal-handoffs", TRUE, "sync", TRUE, "async", TRUE, NULL); ts_fail_if (codec->encoding_name == NULL, "Got invalid codec without an encoding_name with id %u" " and clock_rate %u", codec->id, codec->clock_rate); g_object_set_data (G_OBJECT (fakesink), "codec", codeccopy); g_object_weak_ref (G_OBJECT (fakesink), (GWeakNotify) fs_codec_destroy, codeccopy); g_signal_connect (fakesink, "handoff", st->handoff_handler, st); gst_bin_add (GST_BIN (st->dat->pipeline), fakesink); fakesink_pad = gst_element_get_static_pad (fakesink, "sink"); ret = gst_pad_link (pad, fakesink_pad); gst_object_unref (fakesink_pad); ts_fail_if (GST_PAD_LINK_FAILED(ret), "Could not link fakesink"); ts_fail_if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE, "Could not set the fakesink to playing"); str = fs_codec_to_string (codec); GST_DEBUG ("%d:%d: Added Fakesink for codec %s", st->dat->id, st->target->id, str); g_free (str); if (max_src_pads > 1) ts_fail_unless (count_stream_pads (stream) <= max_src_pads); else ts_fail_unless (count_stream_pads (stream) == 1); }
static void _current_send_codec_changed (FsSession *session, FsCodec *codec) { struct SimpleTestConference *dat = NULL; FsConference *conf = NULL; gchar *str = NULL; g_object_get (session, "conference", &conf, NULL); dat = g_object_get_data (G_OBJECT (conf), "dat"); gst_object_unref (conf); str = fs_codec_to_string (codec); g_debug ("%d: New send codec: %s", dat->id, str); g_free (str); }
static gboolean async_bus_cb (GstBus *bus, GstMessage *message, gpointer user_data) { switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: { GError *error = NULL; gchar *debug_str = NULL; gst_message_parse_error (message, &error, &debug_str); g_print ("Got gst message: %s %s", error->message, debug_str); } break; case GST_MESSAGE_WARNING: { GError *error = NULL; gchar *debug_str = NULL; gst_message_parse_warning (message, &error, &debug_str); g_warning ("Got gst message: %s %s", error->message, debug_str); } break; case GST_MESSAGE_ELEMENT: { const GstStructure *s = gst_message_get_structure (message); if (gst_structure_has_name (s, "farstream-error")) { gint error; const gchar *error_msg = gst_structure_get_string (s, "error-msg"); g_assert (gst_structure_get_enum (s, "error-no", FS_TYPE_ERROR, &error)); if (FS_ERROR_IS_FATAL (error)) g_error ("Farstream fatal error: %d %s", error, error_msg); else g_warning ("Farstream non-fatal error: %d %s", error, error_msg); } else if (gst_structure_has_name (s, "farstream-new-local-candidate")) { const GValue *val = gst_structure_get_value (s, "candidate"); FsCandidate *cand = NULL; g_assert (val); cand = g_value_get_boxed (val); g_print ("New candidate: socket %s\n", cand->ip); g_print ("You can press ENTER on the other side\n"); } else if (gst_structure_has_name (s, "farstream-local-candidates-prepared")) { g_print ("Local candidates prepared\n"); } else if (gst_structure_has_name (s, "farstream-recv-codecs-changed")) { const GValue *val = gst_structure_get_value (s, "codecs"); GList *codecs = NULL; g_assert (val); codecs = g_value_get_boxed (val); g_print ("Recv codecs changed:\n"); for (; codecs; codecs = g_list_next (codecs)) { FsCodec *codec = codecs->data; gchar *tmp = fs_codec_to_string (codec); g_print ("%s\n", tmp); g_free (tmp); } } else if (gst_structure_has_name (s, "farstream-send-codec-changed")) { const GValue *val = gst_structure_get_value (s, "codec"); FsCodec *codec = NULL; gchar *tmp; g_assert (val); codec = g_value_get_boxed (val); tmp = fs_codec_to_string (codec); g_print ("Send codec changed: %s\n", tmp); g_free (tmp); } } break; default: break; } return TRUE; }
static void mex_telepathy_channel_on_src_pad_added (TfContent *content, TpHandle handle, FsStream *stream, GstPad *pad, FsCodec *codec, gpointer user_data) { MexTelepathyChannel *self = MEX_TELEPATHY_CHANNEL (user_data); MexTelepathyChannelPrivate *priv = self->priv; gchar *cstr = fs_codec_to_string (codec); FsMediaType mtype; GstPad *sinkpad; GstElement *element; GstStateChangeReturn ret; /* Upon pad added, clear the "in progress" box+padding */ clutter_actor_hide (CLUTTER_ACTOR (priv->busy_box)); clutter_actor_show (CLUTTER_ACTOR (priv->full_frame) ); MEX_DEBUG ("New src pad: %s", cstr); g_object_get (content, "media-type", &mtype, NULL); switch (mtype) { case FS_MEDIA_TYPE_AUDIO: element = gst_parse_bin_from_description ( "audioconvert ! audioresample ! audioconvert ! autoaudiosink", TRUE, NULL); break; case FS_MEDIA_TYPE_VIDEO: element = priv->incoming_sink; break; default: MEX_WARNING ("Unknown media type"); return; } if (!gst_bin_add (GST_BIN (priv->pipeline), element)) { MEX_WARNING ("Failed to add sink element to pipeline"); } sinkpad = gst_element_get_pad (element, "sink"); ret = gst_element_set_state (element, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { tp_channel_close_async (TP_CHANNEL (priv->channel), NULL, NULL); MEX_WARNING ("Failed to start tee sink pipeline !?"); return; } if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sinkpad))) { tp_channel_close_async (TP_CHANNEL (priv->channel), NULL, NULL); MEX_WARNING ("Couldn't link sink pipeline !?"); return; } g_object_unref (sinkpad); /* Start in FULL mode */ mex_telepathy_channel_set_tool_mode (self, TOOL_MODE_FULL, 100); }
static void _handoff_handler (GstElement *element, GstBuffer *buffer, GstPad *pad, gpointer user_data) { struct SimpleTestStream *st = user_data; int i; gboolean stop = TRUE; GList *codecs = NULL; g_object_get (st->dat->session, "codecs", &codecs, NULL); ts_fail_if (codecs == NULL, "Could not get codecs"); if (st->flags & WAITING_ON_LAST_CODEC) { if (fs_codec_are_equal ( g_list_last (codecs)->data, g_object_get_data (G_OBJECT (element), "codec"))) { st->flags &= ~WAITING_ON_LAST_CODEC; st->flags |= SHOULD_BE_LAST_CODEC; max_buffer_count += st->buffer_count; g_debug ("We HAVE last codec"); } else { gchar *str = fs_codec_to_string ( g_object_get_data (G_OBJECT (element), "codec")); gchar *str2 = fs_codec_to_string (g_list_last (codecs)->data); g_debug ("not yet the last codec, skipping (we have %s, we want %s)", str, str2); g_free (str); g_free (str2); fs_codec_list_destroy (codecs); return; } } if (select_last_codec || st->flags & SHOULD_BE_LAST_CODEC) ts_fail_unless ( fs_codec_are_equal ( g_list_last (codecs)->data, g_object_get_data (G_OBJECT (element), "codec")), "The handoff handler got a buffer from the wrong codec (last)"); else ts_fail_unless ( fs_codec_are_equal ( g_list_first (codecs)->data, g_object_get_data (G_OBJECT (element), "codec")), "The handoff handler got a buffer from the wrong codec"); fs_codec_list_destroy (codecs); st->buffer_count++; if (st->buffer_count % 10 == 0) g_debug ("%d:%d: Buffer %d", st->dat->id, st->target->id, st->buffer_count); /* ts_fail_if (dat->buffer_count > max_buffer_count, "Too many buffers %d > max_buffer_count", dat->buffer_count); */ for (i = 0; i < count && !stop ; i++) { GList *item; for (item = g_list_first (dats[i]->streams); item; item = g_list_next (item)) { struct SimpleTestStream *st2 = item->data; if (st2->buffer_count < max_buffer_count) { stop = FALSE; break; } } } if (stop) { if (reset_to_last_codec && !(st->flags & HAS_BEEN_RESET)) { GError *error = NULL; GList *nego_codecs = NULL; gchar *str = NULL; g_object_get (st->target->session, "codecs", &nego_codecs, NULL); ts_fail_if (nego_codecs == NULL, "No codecs"); ts_fail_if (g_list_length (nego_codecs) < 2, "Only one negotiated codec"); str = fs_codec_to_string (g_list_last (nego_codecs)->data); g_debug ("Setting codec to: %s", str); g_free (str); ts_fail_unless (fs_session_set_send_codec (st->target->session, g_list_last (nego_codecs)->data, &error), "Could not set the send codec: %s", error ? error->message : "NO GError!!!"); g_clear_error (&error); fs_codec_list_destroy (nego_codecs); st->flags |= HAS_BEEN_RESET | WAITING_ON_LAST_CODEC; g_debug ("RESET TO LAST CODEC"); } else { g_main_loop_quit (loop); } } }
GList * negotiate_stream_codecs ( const GList *remote_codecs, GList *current_codec_associations, gboolean multi_stream) { GList *new_codec_associations = NULL; const GList *rcodec_e = NULL; GList *item = NULL; GST_DEBUG ("Negotiating stream codecs (for %s)", multi_stream ? "a single stream" : "multiple streams"); for (rcodec_e = remote_codecs; rcodec_e; rcodec_e = g_list_next (rcodec_e)) { FsCodec *remote_codec = rcodec_e->data; FsCodec *nego_codec = NULL; FsCodec *nego_send_codec = NULL; CodecAssociation *old_ca = NULL; gchar *tmp = fs_codec_to_string (remote_codec); GST_DEBUG ("Remote codec %s", tmp); g_free (tmp); /* First lets try the codec that is in the same PT */ old_ca = lookup_codec_association_by_pt_list (current_codec_associations, remote_codec->id, FALSE); if (old_ca) { GST_DEBUG ("Have local codec in the same PT, lets try it first"); negotiate_stream_codec (old_ca, remote_codec, multi_stream, &nego_codec, &nego_send_codec); } if (!nego_codec) { for (item = current_codec_associations; item; item = g_list_next (item)) { old_ca = item->data; if (old_ca->disable || old_ca->reserved) continue; negotiate_stream_codec (old_ca, remote_codec, multi_stream, &nego_codec, &nego_send_codec); if (nego_codec) { /* If we have multiple streams with codecs, * then priorize the local IDs */ if (multi_stream) nego_send_codec->id = nego_codec->id = old_ca->codec->id; break; } } } if (nego_codec) { CodecAssociation *new_ca = g_slice_new0 (CodecAssociation); gchar *tmp; new_ca->need_config = old_ca->need_config; new_ca->codec = nego_codec; new_ca->send_codec = nego_send_codec; new_ca->blueprint = old_ca->blueprint; new_ca->send_profile = g_strdup (old_ca->send_profile); new_ca->recv_profile = g_strdup (old_ca->recv_profile); tmp = fs_codec_to_string (nego_codec); GST_DEBUG ("Negotiated codec %s", tmp); g_free (tmp); new_codec_associations = g_list_append (new_codec_associations, new_ca); } else { gchar *tmp = fs_codec_to_string (remote_codec); CodecAssociation *new_ca = g_slice_new0 (CodecAssociation); GST_DEBUG ("Could not find a valid intersection... for codec %s", tmp); g_free (tmp); new_ca->codec = fs_codec_copy (remote_codec); new_ca->disable = TRUE; new_codec_associations = g_list_append (new_codec_associations, new_ca); } } /* * Check if there is a non-disabled codec left that we can use * for sending */ for (item = new_codec_associations; item; item = g_list_next (item)) { CodecAssociation *ca = item->data; if (codec_association_is_valid_for_sending (ca, TRUE)) return new_codec_associations; } /* Else we destroy when and return NULL.. ie .. an error */ codec_association_list_destroy (new_codec_associations); return NULL; }
GList * create_local_codec_associations ( GList *blueprints, GList *codec_prefs, GList *current_codec_associations) { GList *codec_associations = NULL; GList *bp_e = NULL; GList *codec_pref_e = NULL; GList *lca_e = NULL; gboolean has_valid_codec = FALSE; CodecAssociation *oldca = NULL; if (blueprints == NULL) return NULL; GST_DEBUG ("Creating local codec associations"); /* First, lets create the original table by looking at our preferred codecs */ for (codec_pref_e = codec_prefs; codec_pref_e; codec_pref_e = g_list_next (codec_pref_e)) { FsCodec *codec_pref = codec_pref_e->data; CodecBlueprint *bp = _find_matching_blueprint (codec_pref, blueprints); CodecAssociation *ca = NULL; GList *bp_param_e = NULL; /* If its a negative pref, ignore it in this stage */ if (codec_pref->id == FS_CODEC_ID_DISABLE) continue; /* If we want to disable a codec ID, we just insert a reserved codec assoc * in the list */ if (codec_pref->id >= 0 && codec_pref->id < 128 && codec_pref->encoding_name && !g_ascii_strcasecmp (codec_pref->encoding_name, "reserve-pt")) { CodecAssociation *ca = g_slice_new0 (CodecAssociation); ca->codec = fs_codec_copy (codec_pref); ca->reserved = TRUE; codec_associations = g_list_append (codec_associations, ca); continue; } /* No matching blueprint, can't use this codec */ if (!bp && !fs_codec_get_optional_parameter (codec_pref, RECV_PROFILE_ARG, NULL)) { GST_LOG ("Could not find matching blueprint for preferred codec %s/%s", fs_media_type_to_string (codec_pref->media_type), codec_pref->encoding_name); continue; } /* Now lets see if there is an existing codec that matches this preference */ if (codec_pref->id == FS_CODEC_ID_ANY) { oldca = lookup_codec_association_custom_internal ( current_codec_associations, TRUE, match_original_codec_and_codec_pref, codec_pref); } else { oldca = lookup_codec_association_by_pt_list (current_codec_associations, codec_pref->id, FALSE); if (oldca && oldca->reserved) oldca = NULL; } /* In this case, we have a matching codec association, lets keep the * payload type from it */ if (oldca) { FsCodec *codec = sdp_negotiate_codec ( oldca->codec, FS_PARAM_TYPE_BOTH | FS_PARAM_TYPE_CONFIG, codec_pref, FS_PARAM_TYPE_ALL); FsCodec *send_codec; if (codec) { fs_codec_destroy (codec); send_codec = sdp_negotiate_codec ( oldca->send_codec, FS_PARAM_TYPE_SEND, codec_pref, FS_PARAM_TYPE_SEND | FS_PARAM_TYPE_SEND_AVOID_NEGO); if (send_codec) fs_codec_destroy (send_codec); else oldca = NULL; } else { oldca = NULL; } } ca = g_slice_new0 (CodecAssociation); ca->blueprint = bp; ca->codec = fs_codec_copy (codec_pref); codec_remove_parameter (ca->codec, SEND_PROFILE_ARG); codec_remove_parameter (ca->codec, RECV_PROFILE_ARG); ca->send_codec = codec_copy_filtered (codec_pref, FS_PARAM_TYPE_CONFIG); codec_remove_parameter (ca->send_codec, SEND_PROFILE_ARG); codec_remove_parameter (ca->send_codec, RECV_PROFILE_ARG); if (oldca) ca->send_codec->id = ca->codec->id = oldca->codec->id; ca->send_profile = dup_param_value (codec_pref, SEND_PROFILE_ARG); ca->recv_profile = dup_param_value (codec_pref, RECV_PROFILE_ARG); if (bp) { /* Codec pref does not come with a number, but * The blueprint has its own id, lets use it */ if (ca->codec->id == FS_CODEC_ID_ANY && (bp->codec->id >= 0 || bp->codec->id < 128)) { ca->send_codec->id = ca->codec->id = bp->codec->id; } if (ca->codec->clock_rate == 0) ca->codec->clock_rate = bp->codec->clock_rate; if (ca->codec->channels == 0) ca->codec->channels = bp->codec->channels; for (bp_param_e = bp->codec->optional_params; bp_param_e; bp_param_e = g_list_next (bp_param_e)) { FsCodecParameter *bp_param = bp_param_e->data; if (fs_codec_get_optional_parameter (ca->codec, bp_param->name, NULL)) fs_codec_add_optional_parameter (ca->codec, bp_param->name, bp_param->value); } } { gchar *tmp = fs_codec_to_string (ca->codec); GST_LOG ("Added preferred codec %s", tmp); g_free (tmp); } codec_associations = list_insert_local_ca (codec_associations, ca); } /* Now, only codecs with specified ids are here, * the rest are dynamic * Lets attribute them here */ for (lca_e = codec_associations; lca_e; lca_e = g_list_next (lca_e)) { CodecAssociation *lca = lca_e->data; if (lca->reserved) continue; if (lca->codec->id < 0) { lca->send_codec->id = lca->codec->id = _find_first_empty_dynamic_entry ( current_codec_associations, codec_associations); if (lca->codec->id < 0) { GST_ERROR ("We've run out of dynamic payload types"); goto error; } } } /* Now, lets add all other codecs from the blueprints */ for (bp_e = g_list_first (blueprints); bp_e; bp_e = g_list_next (bp_e)) { CodecBlueprint *bp = bp_e->data; CodecAssociation *ca = NULL; GList *tmpca_e = NULL; gboolean next = FALSE; FsCodec *codec; /* Lets skip codecs that dont have all of the required informations */ if (bp->codec->clock_rate == 0) continue; /* Check if its already used */ for (tmpca_e = codec_associations; tmpca_e; tmpca_e = g_list_next (tmpca_e)) { CodecAssociation *tmpca = tmpca_e->data; if (tmpca->blueprint == bp) break; } if (tmpca_e) continue; /* Check if it is disabled in the list of preferred codecs */ if (_is_disabled (codec_prefs, bp)) { gchar *tmp = fs_codec_to_string (bp->codec); GST_DEBUG ("Codec %s disabled by config", tmp); g_free (tmp); continue; } /* Re-use already existing codec associations with this blueprint * if any, we only keep the PT from the old assoc * (the rest will be regenerated by the renegotiation) */ for (tmpca_e = current_codec_associations; tmpca_e; tmpca_e = g_list_next (tmpca_e)) { CodecAssociation *tmpca = tmpca_e->data; if (tmpca->blueprint == bp) { /* Ignore reserved (we've just regenerated them )*/ if (tmpca->reserved) continue; /* Ignore it if there is already something for this PT */ if (lookup_codec_association_by_pt_list (codec_associations, tmpca->codec->id, TRUE)) continue; /* Can't keep this codec, for some reason its wrong */ codec = sdp_negotiate_codec (tmpca->codec, FS_PARAM_TYPE_CONFIG, bp->codec, FS_PARAM_TYPE_ALL); if (!codec) continue; fs_codec_destroy (codec); ca = g_slice_new0 (CodecAssociation); ca->blueprint = bp; ca->codec = fs_codec_copy (bp->codec); ca->send_codec = codec_copy_filtered (bp->codec, FS_PARAM_TYPE_CONFIG); ca->codec->id = ca->send_codec->id = tmpca->codec->id; codec_associations = list_insert_local_ca (codec_associations, ca); next = TRUE; } } if (next) continue; codec = sdp_negotiate_codec (bp->codec, FS_PARAM_TYPE_ALL, bp->codec, FS_PARAM_TYPE_ALL); /* If it does not negotiate against itself, there must be something wrong */ if (!codec) continue; fs_codec_destroy (codec); ca = g_slice_new0 (CodecAssociation); ca->blueprint = bp; ca->codec = fs_codec_copy (bp->codec); if (ca->codec->id < 0) { ca->codec->id = _find_first_empty_dynamic_entry ( current_codec_associations, codec_associations); if (ca->codec->id < 0) { GST_WARNING ("We've run out of dynamic payload types"); goto error; } } ca->send_codec = codec_copy_filtered (ca->codec, FS_PARAM_TYPE_CONFIG); codec_associations = list_insert_local_ca (codec_associations, ca); } for (lca_e = codec_associations; lca_e; lca_e = g_list_next (lca_e)) { CodecAssociation *ca = lca_e->data; if (codec_association_is_valid_for_sending (ca, TRUE)) has_valid_codec = TRUE; } if (!has_valid_codec) { GST_WARNING ("All codecs disabled by preferences"); goto error; } return codec_associations; error: codec_association_list_destroy (codec_associations); return NULL; }
/** * validate_codecs_configuration: * @media_type: The #FsMediaType these codecs should be for * @blueprints: A #GList of #CodecBlueprints to validate the codecs agsint * @codecs: a #GList of #FsCodec that represent the preferences * * This function validates a GList of passed FarsightCodec structures * against the valid discovered payloaders * It removes all "invalid" codecs from the list, it modifies the list * passed in as an argument. * * Returns: the #GList of #FsCodec minus the invalid ones */ GList * validate_codecs_configuration (FsMediaType media_type, GList *blueprints, GList *codecs) { GList *codec_e = codecs; while (codec_e) { FsCodec *codec = codec_e->data; GList *blueprint_e = NULL; FsCodecParameter *param; /* Check if codec is for the wrong media_type.. this would be wrong */ if (media_type != codec->media_type) goto remove_this_codec; if (codec->id >= 0 && codec->id < 128 && codec->encoding_name && !g_ascii_strcasecmp (codec->encoding_name, "reserve-pt")) goto accept_codec; for (blueprint_e = g_list_first (blueprints); blueprint_e; blueprint_e = g_list_next (blueprint_e)) { CodecBlueprint *blueprint = blueprint_e->data; /* First, lets check the encoding name */ if (g_ascii_strcasecmp (blueprint->codec->encoding_name, codec->encoding_name)) continue; /* If both have a clock_rate, it must be the same */ if (blueprint->codec->clock_rate && codec->clock_rate && blueprint->codec->clock_rate != codec->clock_rate) continue; /* At least one needs to have a clockrate */ else if (!blueprint->codec->clock_rate && !codec->clock_rate) continue; if (codec_sdp_compare (blueprint->codec, codec)) break; } /* If there are send and/or recv profiles, lets test them */ param = fs_codec_get_optional_parameter (codec, RECV_PROFILE_ARG, NULL); if (param && !validate_codec_profile (codec, param->value, FALSE)) goto remove_this_codec; param = fs_codec_get_optional_parameter (codec, SEND_PROFILE_ARG, NULL); if (param && !validate_codec_profile (codec, param->value, TRUE)) goto remove_this_codec; /* If no blueprint was found */ if (blueprint_e == NULL) { /* Accept codecs with no blueprints if they have a valid profile */ if (fs_codec_get_optional_parameter (codec, RECV_PROFILE_ARG, NULL) && codec->encoding_name && codec->clock_rate) goto accept_codec; goto remove_this_codec; } accept_codec: codec_e = g_list_next (codec_e); continue; remove_this_codec: { GList *nextcodec_e = g_list_next (codec_e); gchar *tmp = fs_codec_to_string (codec); GST_DEBUG ("Preferred codec %s could not be matched with a blueprint", tmp); g_free (tmp); fs_codec_destroy (codec); codecs = g_list_delete_link (codecs, codec_e); codec_e = nextcodec_e; } } return codecs; }