static GList * list_insert_local_ca (GList *list, CodecAssociation *ca) { if (codec_association_is_valid_for_sending (ca, TRUE)) { GList *item; for (item = list; item; item = item->next) if (!codec_association_is_valid_for_sending (item->data, TRUE)) break; if (item) return g_list_insert_before (list, item, ca); } return g_list_append (list, ca); }
static gboolean _is_law_codec (CodecAssociation *ca, gpointer user_data) { if (codec_association_is_valid_for_sending (ca, FALSE) && (ca->codec->id == 0 || ca->codec->id == 8)) return TRUE; else return FALSE; }
static gboolean _is_telephony_codec (CodecAssociation *ca, gpointer user_data) { guint clock_rate = GPOINTER_TO_UINT (user_data); if (codec_association_is_valid_for_sending (ca, FALSE) && ca->codec->media_type == FS_MEDIA_TYPE_AUDIO && !g_ascii_strcasecmp (ca->codec->encoding_name, "telephone-event") && ca->codec->clock_rate == clock_rate) return TRUE; else return FALSE; }
CodecAssociation * lookup_codec_association_by_codec_for_sending (GList *codec_associations, FsCodec *codec) { GList *item; for (item = codec_associations; item; item = g_list_next (item)) { CodecAssociation *ca = item->data; if (codec_association_is_valid_for_sending (ca, FALSE) && fs_codec_are_equal (ca->codec, codec)) return ca; } return NULL; }
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; }