GstCaps * fs_codec_to_gst_caps (const FsCodec *codec) { GstCaps *caps; GstStructure *structure; GList *item; if (codec == NULL) return NULL; structure = gst_structure_new ("application/x-rtp", NULL); if (codec->encoding_name) { gchar *encoding_name = g_ascii_strup (codec->encoding_name, -1); if (!g_ascii_strcasecmp (encoding_name, "H263-N800")) { g_free (encoding_name); encoding_name = g_strdup ("H263-1998"); } gst_structure_set (structure, "encoding-name", G_TYPE_STRING, encoding_name, NULL); g_free (encoding_name); } if (codec->clock_rate) gst_structure_set (structure, "clock-rate", G_TYPE_INT, codec->clock_rate, NULL); if (fs_media_type_to_string (codec->media_type)) gst_structure_set (structure, "media", G_TYPE_STRING, fs_media_type_to_string (codec->media_type), NULL); if (codec->id >= 0 && codec->id < 128) gst_structure_set (structure, "payload", G_TYPE_INT, codec->id, NULL); if (codec->channels) gst_structure_set (structure, "channels", G_TYPE_INT, codec->channels, NULL); for (item = codec->optional_params; item; item = g_list_next (item)) { FsCodecParameter *param = item->data; gchar *lower_name = g_ascii_strdown (param->name, -1); gst_structure_set (structure, lower_name, G_TYPE_STRING, param->value, NULL); g_free (lower_name); } caps = gst_caps_new_full (structure, NULL); return caps; }
/** * fs_codec_to_string * @codec: A farsight codec * * Returns a newly-allocated string representing the codec * * Return value: the newly-allocated string */ gchar * fs_codec_to_string (const FsCodec *codec) { GString *string = NULL; GList *item; gchar *charstring; if (codec == NULL) return g_strdup ("(NULL)"); string = g_string_new (""); g_string_printf (string, "%d: %s %s clock:%d channels:%d", codec->id, fs_media_type_to_string (codec->media_type), codec->encoding_name, codec->clock_rate, codec->channels); for (item = codec->optional_params; item; item = g_list_next (item)) { FsCodecParameter *param = item->data; g_string_append_printf (string, " %s=%s", param->name, param->value); } charstring = string->str; g_string_free (string, FALSE); return charstring; }
/** * fs_codec_to_string * @codec: A farstream codec * * Returns a newly-allocated string representing the codec * * Return value: the newly-allocated string */ gchar * fs_codec_to_string (const FsCodec *codec) { GString *string = NULL; GList *item; gchar *charstring; if (codec == NULL) return g_strdup ("(NULL)"); string = g_string_new (""); g_string_printf (string, "%d: %s %s clock:%d channels:%d", codec->id, fs_media_type_to_string (codec->media_type), codec->encoding_name, codec->clock_rate, codec->channels); if (codec->minimum_reporting_interval != G_MAXUINT) g_string_append_printf (string, " trr-int=%u", codec->minimum_reporting_interval); for (item = codec->optional_params; item; item = g_list_next (item)) { FsCodecParameter *param = item->data; g_string_append_printf (string, " %s=%s", param->name, param->value); } for (item = codec->feedback_params; item; item = g_list_next (item)) { FsFeedbackParameter *param = item->data; g_string_append_printf (string, " %s/%s=%s", param->type, param->subtype, param->extra_params); } charstring = string->str; g_string_free (string, FALSE); return charstring; }
/** * fs_rtp_stream_set_remote_codecs: * @stream: an #FsStream * @remote_codecs: a #GList of #FsCodec representing the remote codecs * @error: location of a #GError, or NULL if no error occured * * This function will set the list of remote codecs for this stream. If * the given remote codecs couldn't be negotiated with the list of local * codecs or already negotiated codecs for the corresponding #FsSession, @error * will be set and %FALSE will be returned. The @remote_codecs list will be * copied so it must be free'd using fs_codec_list_destroy() when done. * * Returns: %FALSE if the remote codecs couldn't be set. */ static gboolean fs_rtp_stream_set_remote_codecs (FsStream *stream, GList *remote_codecs, GError **error) { FsRtpStream *self = FS_RTP_STREAM (stream); GList *item = NULL; FsMediaType media_type; FsRtpSession *session = fs_rtp_stream_get_session (self, error); if (!session) return FALSE; if (remote_codecs == NULL) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "You can not set NULL remote codecs"); goto error; } g_object_get (session, "media-type", &media_type, NULL); for (item = g_list_first (remote_codecs); item; item = g_list_next (item)) { FsCodec *codec = item->data; if (!codec->encoding_name) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The codec must have an encoding name"); goto error; } if (codec->id < 0 || codec->id > 128) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The codec id must be between 0 ans 128 for %s", codec->encoding_name); goto error; } if (codec->media_type != media_type) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "The media type for codec %s is not %s", codec->encoding_name, fs_media_type_to_string (media_type)); goto error; } } if (self->priv->new_remote_codecs_cb (self, remote_codecs, error, self->priv->user_data_for_cb)) { gboolean is_new = TRUE; FS_RTP_SESSION_LOCK (session); if (self->remote_codecs) { is_new = !fs_codec_list_are_equal (self->remote_codecs, remote_codecs); fs_codec_list_destroy (self->remote_codecs); } self->remote_codecs = fs_codec_list_copy (remote_codecs); FS_RTP_SESSION_UNLOCK (session); if (is_new) g_object_notify (G_OBJECT (stream), "remote-codecs"); } else { goto error; } g_object_unref (session); return TRUE; error: g_object_unref (session); return FALSE; }
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; }
GstCaps * fs_codec_to_gst_caps (const FsCodec *codec) { GstCaps *caps; GstStructure *structure; GList *item; if (codec == NULL) return NULL; caps = gst_caps_new_empty_simple ("application/x-rtp"); structure = gst_caps_get_structure (caps, 0); if (codec->encoding_name) { gchar *encoding_name = g_ascii_strup (codec->encoding_name, -1); gst_structure_set (structure, "encoding-name", G_TYPE_STRING, encoding_name, NULL); g_free (encoding_name); } if (codec->clock_rate) gst_structure_set (structure, "clock-rate", G_TYPE_INT, codec->clock_rate, NULL); if (fs_media_type_to_string (codec->media_type)) gst_structure_set (structure, "media", G_TYPE_STRING, fs_media_type_to_string (codec->media_type), NULL); if (codec->id >= 0 && codec->id < 128) gst_structure_set (structure, "payload", G_TYPE_INT, codec->id, NULL); if (codec->channels) gst_structure_set (structure, "channels", G_TYPE_INT, codec->channels, NULL); for (item = codec->optional_params; item; item = g_list_next (item)) { FsCodecParameter *param = item->data; gchar *lower_name = g_ascii_strdown (param->name, -1); if (!strcmp (lower_name, "ptime") || !strcmp (lower_name, "maxptime")) gst_structure_set (structure, lower_name, G_TYPE_UINT, atoi (param->value), NULL); else gst_structure_set (structure, lower_name, G_TYPE_STRING, param->value, NULL); g_free (lower_name); } for (item = codec->feedback_params; item; item = g_list_next (item)) { FsFeedbackParameter *param = item->data; gchar *lower_type = g_ascii_strdown (param->type, -1); gchar *rtcpfb_name; if (param->subtype[0]) { gchar *lower_subt = g_ascii_strdown (param->subtype, -1); rtcpfb_name = g_strdup_printf ("rtcp-fb-%s-%s", lower_type, lower_subt); g_free (lower_subt); } else { rtcpfb_name = g_strdup_printf ("rtcp-fb-%s", lower_type); } gst_structure_set (structure, rtcpfb_name, G_TYPE_STRING, param->extra_params, NULL); g_free (lower_type); g_free (rtcpfb_name); } return caps; }
/** * fs_rtp_blueprints_get * @media_type: a #FsMediaType * * find all plugins that follow the pattern: * input (microphone) -> N* -> rtp payloader -> network * network -> rtp depayloader -> N* -> output (soundcard) * media_type defines if we want audio or video codecs * * Returns : a #GList of #CodecBlueprint or NULL on error */ GList * fs_rtp_blueprints_get (FsMediaType media_type, GError **error) { GstCaps *caps; GList *recv_list = NULL; GList *send_list = NULL; gboolean ret; if (media_type > FS_MEDIA_TYPE_LAST) { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "Invalid media type given"); return NULL; } codecs_lists_ref[media_type]++; /* if already computed just return list */ if (codecs_lists_ref[media_type] > 1) return list_codec_blueprints[media_type]; list_codec_blueprints[media_type] = load_codecs_cache (media_type, NULL); if (list_codec_blueprints[media_type]) { GST_DEBUG ("Loaded codec blueprints from cache file"); return list_codec_blueprints[media_type]; } /* caps used to find the payloaders and depayloaders based on media type */ if (media_type == FS_MEDIA_TYPE_AUDIO) { caps = gst_caps_new_simple ("application/x-rtp", "media", G_TYPE_STRING, "audio", NULL); } else if (media_type == FS_MEDIA_TYPE_VIDEO) { caps = gst_caps_new_simple ("application/x-rtp", "media", G_TYPE_STRING, "video", NULL); } else { g_set_error (error, FS_ERROR, FS_ERROR_INVALID_ARGUMENTS, "Invalid media type given to load_codecs"); codecs_lists_ref[media_type]--; return NULL; } recv_list = detect_recv_codecs (caps); send_list = detect_send_codecs (caps); gst_caps_unref (caps); /* if we can't send or recv let's just stop here */ if (!recv_list && !send_list) { codecs_lists_ref[media_type]--; g_set_error (error, FS_ERROR, FS_ERROR_NO_CODECS, "No codecs for media type %s detected", fs_media_type_to_string (media_type)); list_codec_blueprints[media_type] = NULL; goto out; } ret = create_codec_lists (media_type, recv_list, send_list); /* Save the codecs blueprint cache */ save_codecs_cache (media_type, list_codec_blueprints[media_type]); out: if (recv_list) codec_cap_list_free (recv_list); if (send_list) codec_cap_list_free (send_list); return list_codec_blueprints[media_type];; }
GstElement * create_codec_bin_from_blueprint (const FsCodec *codec, CodecBlueprint *blueprint, const gchar *name, gboolean is_send, GError **error) { GstElement *codec_bin = NULL; gchar *direction_str = (is_send == TRUE) ? "send" : "receive"; GList *walk = NULL; GstElement *current_element = NULL; GstElement *previous_element = NULL; GList *pipeline_factory = NULL; if (is_send) pipeline_factory = blueprint->send_pipeline_factory; else pipeline_factory = blueprint->receive_pipeline_factory; if (!pipeline_factory) { g_set_error (error, FS_ERROR, FS_ERROR_UNKNOWN_CODEC, "The %s codec %s does not have a pipeline," " its probably a special codec", fs_media_type_to_string (codec->media_type), codec->encoding_name); return NULL; } GST_DEBUG ("creating %s codec bin for id %d, pipeline_factory %p", direction_str, codec->id, pipeline_factory); if (is_send) codec_bin = gst_bin_new (name); else codec_bin = fs_rtp_bin_error_downgrade_new (name); for (walk = g_list_first (pipeline_factory); walk; walk = g_list_next (walk)) { if (g_list_next (g_list_first (walk->data))) { /* We have to check some kind of configuration to see if we have a favorite */ current_element = gst_element_factory_make ("autoconvert", NULL); if (!current_element) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not create autoconvert element"); goto error; } g_object_set (current_element, "factories", walk->data, NULL); } else { current_element = gst_element_factory_create ( GST_ELEMENT_FACTORY (g_list_first (walk->data)->data), NULL); if (!current_element) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not create element for pt %d", codec->id); goto error; } } if (!gst_bin_add (GST_BIN (codec_bin), current_element)) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not add new element to %s codec_bin for pt %d", direction_str, codec->id); goto error; } if (_g_object_has_property (G_OBJECT (current_element), "pt")) g_object_set (current_element, "pt", codec->id, NULL); /* Lets create the ghost pads on the codec bin */ if (g_list_previous (walk) == NULL) /* if its the first element of the codec bin */ if (!_create_ghost_pad (current_element, is_send ? "src" : "sink", codec_bin, error)) goto error; if (g_list_next (walk) == NULL) /* if its the last element of the codec bin */ if (!_create_ghost_pad (current_element, is_send ? "sink" : "src" , codec_bin, error)) goto error; /* let's link them together using the specified media_caps if any * this will ensure that multi-codec encoders/decoders will select the * appropriate codec based on caps negotiation */ if (previous_element) { GstPad *sinkpad; GstPad *srcpad; GstPadLinkReturn ret; if (is_send) sinkpad = gst_element_get_static_pad (previous_element, "sink"); else sinkpad = gst_element_get_static_pad (current_element, "sink"); if (!sinkpad) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not get the sink pad one of the elements in the %s codec bin" " for pt %d", direction_str, codec->id); goto error; } if (is_send) srcpad = gst_element_get_static_pad (current_element, "src"); else srcpad = gst_element_get_static_pad (previous_element, "src"); if (!srcpad) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not get the src pad one of the elements in the %s codec bin" " for pt %d", direction_str, codec->id); gst_object_unref (sinkpad); goto error; } ret = gst_pad_link (srcpad, sinkpad); gst_object_unref (srcpad); gst_object_unref (sinkpad); if (GST_PAD_LINK_FAILED (ret)) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link element inside the %s codec bin for pt %d", direction_str, codec->id); goto error; } } previous_element = current_element; } return codec_bin; error: gst_object_unref (codec_bin); return NULL; }