static gboolean match_send_codec_no_pt (CodecAssociation *old_ca, gpointer user_data) { FsCodec *old_codec; FsCodec *tmpcodec = NULL; CodecAssociation *new_ca = user_data; gboolean ret; if (old_ca->disable || old_ca->reserved) return FALSE; if (new_ca->send_codec->id == old_ca->send_codec->id) { old_codec = old_ca->send_codec; } else { tmpcodec = old_codec = fs_codec_copy (old_ca->send_codec); old_codec->id = new_ca->codec->id; } ret = fs_codec_are_equal (old_codec, new_ca->codec); fs_codec_destroy (tmpcodec); return ret; }
GList * finish_codec_negotiation ( GList *old_codec_associations, GList *new_codec_associations) { int i; GList *item; /* Now, lets fill all of the PTs that were previously used in the session * even if they are not currently used, so they can't be re-used */ for (i=0; i < 128; i++) { CodecAssociation *local_ca = NULL; /* We can skip ids where something already exists */ if (lookup_codec_association_by_pt_list (new_codec_associations, i, TRUE)) continue; /* We check if our local table (our offer) and if we offered * something, we add it. Some broken implementation (like Tandberg's) * send packets on PTs that they did not put in their response */ local_ca = lookup_codec_association_by_pt_list (old_codec_associations, i, FALSE); if (local_ca) { CodecAssociation *new_ca = codec_association_copy (local_ca); new_ca->recv_only = TRUE; new_codec_associations = g_list_append (new_codec_associations, new_ca); } } for (item = new_codec_associations; item; item = g_list_next (item)) { CodecAssociation *new_ca = item->data; CodecAssociation *old_ca = NULL; if (new_ca->disable || new_ca->reserved || new_ca->recv_only) { new_ca->need_config = FALSE; continue; } old_ca = lookup_codec_association_custom_internal (old_codec_associations, TRUE, match_send_codec_no_pt, new_ca); if (old_ca && old_ca->send_codec && fs_codec_are_equal (new_ca->send_codec, old_ca->send_codec)) keep_config_from_old_codec (new_ca->codec, old_ca->codec); new_ca->need_config = codec_needs_config (new_ca->codec); } return new_codec_associations; }
static gboolean _codec_list_has_codec (GList *list, FsCodec *codec) { for (; list; list = g_list_next (list)) { FsCodec *listcodec = list->data; if (fs_codec_are_equal (codec, listcodec)) return TRUE; } return FALSE; }
/** * fs_rtp_special_sources_remove: * @extra_sources: A pointer to the #GList returned by previous calls to this * function * @negotiated_codec_associations: A pointer to the #GList of current negotiated * #CodecAssociation * @mutex: the mutex protecting the last two things * @send_codec: A pointer to the currently selected send codec * * This function removes any special source that are not compatible with the * currently selected send codec. * * Returns: %TRUE if a source was removed */ gboolean fs_rtp_special_sources_remove ( GList **extra_sources, GList **negotiated_codec_associations, GMutex *mutex, FsCodec *send_codec) { GList *klass_item = NULL; gboolean changed = FALSE; fs_rtp_special_sources_init (); for (klass_item = g_list_first (classes); klass_item; klass_item = g_list_next (klass_item)) { FsRtpSpecialSourceClass *klass = klass_item->data; GList *obj_item; FsRtpSpecialSource *obj = NULL; restart: g_mutex_lock (mutex); /* Check if we already have an object for this type */ for (obj_item = g_list_first (*extra_sources); obj_item; obj_item = g_list_next (obj_item)) { obj = obj_item->data; if (G_OBJECT_TYPE(obj) == G_OBJECT_CLASS_TYPE(klass)) break; } if (obj_item) { FsCodec *telephony_codec = fs_rtp_special_source_class_get_codec (klass, *negotiated_codec_associations, send_codec); if (!telephony_codec || !fs_codec_are_equal (telephony_codec, obj->codec)) { *extra_sources = g_list_remove (*extra_sources, obj); changed = TRUE; g_mutex_unlock (mutex); g_object_unref (obj); goto restart; } } g_mutex_unlock (mutex); } return changed; }
static gboolean _compare_codec_lists (GList *list1, GList *list2) { for (; list1 && list2; list1 = g_list_next (list1), list2 = g_list_next (list2)) { if (!fs_codec_are_equal (list1->data, list2->data)) return FALSE; } if (list1 == NULL && list2 == NULL) return TRUE; else return FALSE; }
CodecAssociation * lookup_codec_association_by_codec (GList *codec_associations, FsCodec *codec) { while (codec_associations) { if (codec_associations->data) { CodecAssociation *ca = codec_associations->data; if (fs_codec_are_equal (ca->codec, codec)) return ca; } codec_associations = g_list_next (codec_associations); } return NULL; }
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; }
gboolean codec_associations_list_are_equal (GList *list1, GList *list2) { for (;list1 && list2; list1 = g_list_next (list1), list2 = g_list_next (list2)) { CodecAssociation *ca1 = NULL; CodecAssociation *ca2 = NULL; /* Skip disabled codecs */ while (list1) { ca1 = list1->data; if (!ca1->disable || !ca1->reserved) break; list1 = g_list_next (list1); } while (list2) { ca2 = list2->data; if (!ca2->disable || !ca2->reserved) break; list2 = g_list_next (list2); } if (list1 == NULL || list2 == NULL) break; /* We must emit the notification if the recv-only status * of a codec has changed */ if (ca1->recv_only != ca2->recv_only) return FALSE; if (!fs_codec_are_equal (ca1->codec, ca2->codec)) return FALSE; } if (list1 == NULL && list2 == NULL) return TRUE; else return FALSE; }
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); } } }
static void _substream_codec_changed (FsRtpSubStream *substream, FsRtpStream *stream) { GList *substream_item = NULL; GList *codeclist = NULL; FsRtpSession *session = fs_rtp_stream_get_session (stream, NULL); if (!session) return; FS_RTP_SESSION_LOCK (session); if (!substream->codec) { FS_RTP_SESSION_UNLOCK (session); g_object_unref (session); return; } codeclist = g_list_prepend (NULL, fs_codec_copy (substream->codec)); for (substream_item = stream->substreams; substream_item; substream_item = g_list_next (substream_item)) { FsRtpSubStream *othersubstream = substream_item->data; if (othersubstream != substream) { if (othersubstream->codec) { if (fs_codec_are_equal (substream->codec, othersubstream->codec)) break; if (!_codec_list_has_codec (codeclist, othersubstream->codec)) codeclist = g_list_append (codeclist, fs_codec_copy (othersubstream->codec)); } } } FS_RTP_SESSION_UNLOCK (session); if (substream_item == NULL) { GstElement *conf = NULL; g_object_notify (G_OBJECT (stream), "current-recv-codecs"); g_object_get (session, "conference", &conf, NULL); gst_element_post_message (conf, gst_message_new_element (GST_OBJECT (conf), gst_structure_new ("farstream-recv-codecs-changed", "stream", FS_TYPE_STREAM, stream, "codecs", FS_TYPE_CODEC_LIST, codeclist, NULL))); gst_object_unref (conf); } fs_codec_list_destroy (codeclist); g_object_unref (session); }
gboolean fs_rtp_sub_stream_set_codecbin_unlock (FsRtpSubStream *substream, FsCodec *codec, GstElement *codecbin, GError **error) { GstCaps *caps = NULL; gchar *tmp; gboolean ret = FALSE; GstPad *pad; gboolean codec_changed = TRUE; FS_RTP_SUB_STREAM_LOCK (substream); if (substream->priv->stopped) { FS_RTP_SUB_STREAM_UNLOCK (substream); FS_RTP_SESSION_UNLOCK (substream->priv->session); gst_object_unref (codecbin); fs_codec_destroy (codec); fs_rtp_sub_stream_try_stop (substream); return TRUE; } substream->priv->modifying = TRUE; FS_RTP_SUB_STREAM_UNLOCK (substream); if (substream->codec) { if (!fs_codec_are_equal (codec, substream->codec)) codec_changed = FALSE; } if (substream->priv->codecbin) { FsCodec *saved_codec = substream->codec; GstElement *old_codecbin; gst_element_set_locked_state (substream->priv->codecbin, TRUE); if (gst_element_set_state (substream->priv->codecbin, GST_STATE_NULL) != GST_STATE_CHANGE_SUCCESS) { gst_element_set_locked_state (substream->priv->codecbin, FALSE); g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, "Could not set the codec bin for ssrc %u" " and payload type %d to the state NULL", substream->ssrc, substream->pt); FS_RTP_SUB_STREAM_LOCK (substream); substream->priv->modifying = FALSE; FS_RTP_SUB_STREAM_UNLOCK (substream); FS_RTP_SESSION_UNLOCK (substream->priv->session); gst_object_unref (codecbin); fs_codec_destroy (codec); fs_rtp_sub_stream_try_stop (substream); return FALSE; } old_codecbin = substream->priv->codecbin; substream->priv->codecbin = NULL; FS_RTP_SESSION_UNLOCK (substream->priv->session); gst_bin_remove (GST_BIN (substream->priv->conference), old_codecbin); FS_RTP_SESSION_LOCK (substream->priv->session); if (substream->codec == saved_codec) { fs_codec_destroy (substream->codec); substream->codec = NULL; } if (substream->priv->caps) gst_caps_unref (substream->priv->caps); substream->priv->caps = NULL; } FS_RTP_SESSION_UNLOCK (substream->priv->session); gst_object_ref (codecbin); if (!gst_bin_add (GST_BIN (substream->priv->conference), codecbin)) { gst_object_unref (codecbin); g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not add the codec bin to the conference"); FS_RTP_SUB_STREAM_LOCK (substream); substream->priv->modifying = FALSE; FS_RTP_SUB_STREAM_UNLOCK (substream); fs_rtp_sub_stream_try_stop (substream); return FALSE; } if (gst_element_set_state (codecbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not set the codec bin to the playing state"); goto error; } if (!gst_element_link_pads (codecbin, "src", substream->priv->output_valve, "sink")) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link the codec bin to the output_valve"); goto error; } if (!gst_element_link_pads (substream->priv->capsfilter, "src", codecbin, "sink")) { g_set_error (error, FS_ERROR, FS_ERROR_CONSTRUCTION, "Could not link the receive capsfilter and the codecbin for pt %d", substream->pt); goto error; } caps = fs_codec_to_gst_caps (codec); tmp = gst_caps_to_string (caps); GST_DEBUG ("Setting caps %s on recv substream", tmp); g_free (tmp); g_object_set (substream->priv->capsfilter, "caps", caps, NULL); pad = gst_element_get_static_pad (codecbin, "sink"); if (!pad) { g_set_error (error, FS_ERROR, FS_ERROR_INTERNAL, "Could not get sink pad" " from codecbin"); goto error; } /* This is a non-error error * Some codecs require config data to start.. so we should just ignore them */ if (!gst_pad_set_caps (pad, caps)) { ret = TRUE; gst_object_unref (pad); gst_caps_unref (caps); GST_DEBUG ("Could not set the caps on the codecbin, waiting on config-data" " for SSRC:%x pt:%d", substream->ssrc, substream->pt); /* We call this to drop all buffers until something comes up */ fs_rtp_sub_stream_add_probe_locked (substream); goto error; } gst_object_unref (pad); FS_RTP_SESSION_LOCK (substream->priv->session); substream->priv->caps = caps; substream->priv->codecbin = codecbin; substream->codec = codec; FS_RTP_SUB_STREAM_LOCK (substream); substream->priv->modifying = FALSE; FS_RTP_SUB_STREAM_UNLOCK (substream); if (substream->priv->stream && !substream->priv->output_ghostpad) { if (!fs_rtp_sub_stream_add_output_ghostpad_unlock (substream, error)) goto error; } else { FS_RTP_SESSION_UNLOCK (substream->priv->session); if (codec_changed) g_signal_emit (substream, signals[CODEC_CHANGED], 0); } gst_object_unref (codecbin); fs_rtp_sub_stream_try_stop (substream); return TRUE; error: FS_RTP_SUB_STREAM_LOCK (substream); substream->priv->modifying = FALSE; FS_RTP_SUB_STREAM_UNLOCK (substream); gst_element_set_locked_state (codecbin, TRUE); gst_element_set_state (codecbin, GST_STATE_NULL); gst_object_ref (codecbin); gst_bin_remove (GST_BIN (substream->priv->conference), codecbin); gst_object_unref (codecbin); fs_codec_destroy (codec); fs_rtp_sub_stream_try_stop (substream); return ret; }