static void
really_remove_tee (GstPad *pad, gboolean blocked, RBGstPipelineOp *op)
{
	GstElement *bin;
	GstElement *parent;

	rb_debug ("really removing tee %p", op->element);

	_rb_player_gst_tee_emit_tee_pre_remove (RB_PLAYER_GST_TEE (op->player), op->element);

	/* find bin, remove everything */
	bin = GST_ELEMENT_PARENT (op->element);
	g_object_ref (bin);

	parent = GST_ELEMENT_PARENT (bin);
	gst_bin_remove (GST_BIN (parent), bin);

	gst_element_set_state (bin, GST_STATE_NULL);
	gst_bin_remove (GST_BIN (bin), op->element);
	g_object_unref (bin);

	/* if we're supposed to be playing, unblock the sink */
	if (blocked) {
		rb_debug ("unblocking pad after removing tee");
		gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback)pipeline_op_done, NULL);
	}

	free_pipeline_op (op);
}
Beispiel #2
0
bool MediaNode::disconnectNode(QObject *obj)
{
    MediaNode *sink = qobject_cast<MediaNode*>(obj);
    if (root()) {
        // Disconnecting elements while playing or paused seems to cause
        // potential deadlock. Hence we force the pipeline into ready state
        // before any nodes are disconnected.
        gst_element_set_state(root()->pipeline(), GST_STATE_READY);    

        Q_ASSERT(sink->root()); //sink has to have a root since it is onnected

        if (sink->description() & (AudioSink)) {
            GstPad *sinkPad = gst_element_get_pad(sink->audioElement(), "sink");
            // Release requested src pad from tee
            GstPad *requestedPad = gst_pad_get_peer(sinkPad);
            if (requestedPad) {
                gst_element_release_request_pad(m_audioTee, requestedPad);
                gst_object_unref(requestedPad);
            }
            if (GST_ELEMENT_PARENT(sink->audioElement()))
                gst_bin_remove(GST_BIN(root()->audioGraph()), sink->audioElement());
            gst_object_unref(sinkPad);
        }

        if (sink->description() & (VideoSink)) {
            GstPad *sinkPad = gst_element_get_pad(sink->videoElement(), "sink");
            // Release requested src pad from tee
            GstPad *requestedPad = gst_pad_get_peer(sinkPad);
            if (requestedPad) {
                gst_element_release_request_pad(m_videoTee, requestedPad);
                gst_object_unref(requestedPad);
            }
            if (GST_ELEMENT_PARENT(sink->videoElement()))
                gst_bin_remove(GST_BIN(root()->videoGraph()), sink->videoElement());
            gst_object_unref(sinkPad);
        }

        sink->breakGraph();
        sink->setRoot(0);
    }

    m_videoSinkList.removeAll(obj);
    m_audioSinkList.removeAll(obj);

    if (sink->m_description & AudioSink) {
        // Remove sink from graph
        MediaNodeEvent event(MediaNodeEvent::AudioSinkRemoved, sink);
        mediaNodeEvent(&event);
        return true;
    }

    if ((m_description & VideoSource) && (sink->m_description & VideoSink)) {
        // Remove sink from graph
        MediaNodeEvent event(MediaNodeEvent::VideoSinkRemoved, sink);
        mediaNodeEvent(&event);
        return true;
    }

    return false;
}
Beispiel #3
0
GstElement* PlaybackPipeline::pipeline()
{
    if (!m_webKitMediaSrc || !GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())))
        return nullptr;

    return GST_ELEMENT_PARENT(GST_ELEMENT_PARENT(GST_ELEMENT(m_webKitMediaSrc.get())));
}
Beispiel #4
0
static inline GstElementStats *
get_element_stats (GstStatsTracer * self, GstElement * element)
{
  GstElementStats *stats;
  gboolean is_new = FALSE;

  if (!element) {
    no_elem_stats.index = G_MAXUINT;
    return &no_elem_stats;
  }

  G_LOCK (_elem_stats);
  if (!(stats = g_object_get_qdata ((GObject *) element, data_quark))) {
    stats = create_element_stats (self, element);
    is_new = TRUE;
  }
  G_UNLOCK (_elem_stats);
  if (G_UNLIKELY (stats->parent_ix == G_MAXUINT)) {
    GstElement *parent = GST_ELEMENT_PARENT (element);
    if (parent) {
      GstElementStats *parent_stats = get_element_stats (self, parent);
      stats->parent_ix = parent_stats->index;
    }
  }
  if (G_UNLIKELY (is_new)) {
    log_new_element_stats (stats, element, GST_CLOCK_TIME_NONE);
  }
  return stats;
}
Beispiel #5
0
static gboolean
gst_bus_cb(GstBus *bus, GstMessage *msg, BusCbCtx *ctx)
{
	if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
		gst_structure_has_name(msg->structure, "level")) {

		GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
		gchar *name = gst_element_get_name(src);

		if (!strcmp(name, "level")) {
			gdouble percent;
			gdouble threshold;
			GstElement *valve;

			percent = gst_msg_db_to_percent(msg, "rms");
			gtk_progress_bar_set_fraction(ctx->level, percent * 5);

			percent = gst_msg_db_to_percent(msg, "decay");
			threshold = gtk_range_get_value(ctx->threshold) / 100.0;
			valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve");
			g_object_set(valve, "drop", (percent < threshold), NULL);
			g_object_set(ctx->level,
					"text", (percent < threshold) ? _("DROP") : " ", NULL);
		}

		g_free(name);
	}

	return TRUE;
}
Beispiel #6
0
bool MediaNode::linkMediaNodeList(QList<QObject *> &list, GstElement *bin, GstElement *tee, GstElement *fakesink, GstElement *src)
{
    if (!GST_ELEMENT_PARENT(tee)) {
        gst_bin_add(GST_BIN(bin), tee);
        if (!gst_element_link_pads(src, "src", tee, "sink"))
            return false;
        gst_element_set_state(tee, GST_STATE(bin));
    }
    if (list.isEmpty()) {
        //connect node to a fake sink to avoid clogging the pipeline
        if (!connectToFakeSink(tee, fakesink, bin))
            return false;
    } else {
        // Remove fake sink if previously connected
        if (!releaseFakeSinkIfConnected(tee, fakesink, bin))
            return false;

        for (int i = 0 ; i < list.size() ; ++i) {
            QObject *sink = list[i];
            if (MediaNode *output = qobject_cast<MediaNode*>(sink)) {
                if (!addOutput(output, tee))
                    return false;
            }
        }
    }
    return true;
}
void
gst_trace_element_discoved_entry_init_set_element (GstTraceElementDiscoveredEntry *entry,  GstElement *element)
{
  entry->element_id = element;
  g_strlcpy (entry->element_name, LGI_ELEMENT_NAME (element), GST_ELEMENT_NAME_LENGTH_MAX);
  g_strlcpy (entry->element_type_name, LGI_OBJECT_TYPE_NAME (element), GST_ELEMENT_TYPE_NAME_LENGTH_MAX);
  entry->parent_element_id = element ? GST_ELEMENT_PARENT (element) : NULL;
}
static void
really_add_tee (GstPad *pad, gboolean blocked, RBGstPipelineOp *op)
{
	GstElement *queue;
	GstElement *audioconvert;
	GstElement *bin;
	GstElement *parent_bin;
	GstPad *sinkpad;
	GstPad *ghostpad;

	rb_debug ("really adding tee %p", op->element);

	/* set up containing bin */
	bin = gst_bin_new (NULL);
	queue = gst_element_factory_make ("queue", NULL);
	audioconvert = gst_element_factory_make ("audioconvert", NULL);

	/* The bin contains elements that change state asynchronously
	 * and not as part of a state change in the entire pipeline.
	 */
	g_object_set (bin, "async-handling", TRUE, NULL);

	g_object_set (queue, "max-size-buffers", 3, NULL);

	gst_bin_add_many (GST_BIN (bin), queue, audioconvert, op->element, NULL);
	gst_element_link_many (queue, audioconvert, op->element, NULL);

	/* add ghost pad */
	sinkpad = gst_element_get_static_pad (queue, "sink");
	ghostpad = gst_ghost_pad_new ("sink", sinkpad);
	gst_element_add_pad (bin, ghostpad);
	gst_object_unref (sinkpad);

	/* add it into the pipeline */
	parent_bin = GST_ELEMENT_PARENT (op->fixture);
	gst_bin_add (GST_BIN (parent_bin), bin);
	gst_element_link (op->fixture, bin);

	/* if we're supposed to be playing, unblock the sink */
	if (blocked) {
		rb_debug ("unblocking pad after adding tee");

		gst_element_set_state (parent_bin, GST_STATE_PLAYING);
		gst_object_ref (ghostpad);
		gst_pad_set_blocked_async (pad,
					   FALSE,
					   (GstPadBlockCallback)pipeline_op_done,
					   ghostpad);
	} else {
		gst_element_set_state (bin, GST_STATE_PAUSED);
		gst_object_ref (ghostpad);
		pipeline_op_done (NULL, FALSE, ghostpad);
	}

	_rb_player_gst_tee_emit_tee_inserted (RB_PLAYER_GST_TEE (op->player), op->element);

	free_pipeline_op (op);
}
Beispiel #9
0
// Used to seal up unconnected source nodes by connecting unconnected src pads to fake sinks
bool MediaNode::releaseFakeSinkIfConnected(GstElement *tee, GstElement *fakesink, GstElement *bin)
{
    if (GST_ELEMENT_PARENT(fakesink) == GST_ELEMENT(bin)) {
        GstPad *sinkPad = gst_element_get_pad(fakesink, "sink");

        // Release requested src pad from tee
        GstPad *requestedPad = gst_pad_get_peer(sinkPad);
        if (requestedPad) {
            gst_element_release_request_pad(tee, requestedPad);
            gst_object_unref(requestedPad);
        }
        gst_object_unref(sinkPad);

        gst_element_set_state(fakesink, GST_STATE_NULL);
        gst_bin_remove(GST_BIN(bin), fakesink);
        Q_ASSERT(!GST_ELEMENT_PARENT(fakesink));
    }
    return true;
}
Beispiel #10
0
static void
request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
{
	GstElement *parent = GST_ELEMENT_PARENT(pad);
	GstIterator *iter;
	GstPad *remaining_pad;
	GstIteratorResult result;

	gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad);
	iter = gst_element_iterate_src_pads(parent);

	result = gst_iterator_next(iter, (gpointer)&remaining_pad);

	if (result == GST_ITERATOR_DONE) {
		gst_element_set_locked_state(parent, TRUE);
		gst_element_set_state(parent, GST_STATE_NULL);
		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent);
	} else if (result == GST_ITERATOR_OK) {
		gst_object_unref(remaining_pad);
	}

	gst_iterator_free(iter);
}
Beispiel #11
0
bool MediaNode::unlink()
{
    Q_ASSERT(root());
    if (description() & AudioSource) {
        if (GST_ELEMENT_PARENT(m_audioTee) == GST_ELEMENT(root()->audioGraph())) {
           gst_element_set_state(m_audioTee, GST_STATE_NULL);    
           gst_bin_remove(GST_BIN(root()->audioGraph()), m_audioTee);
       }
        for (int i=0; i<m_audioSinkList.size(); ++i) {
            QObject *audioSink = m_audioSinkList[i];
            if (MediaNode *output = qobject_cast<MediaNode*>(audioSink)) {
                GstElement *element = output->audioElement();
                if (GST_ELEMENT_PARENT(element) == GST_ELEMENT(root()->audioGraph())) {
                    gst_element_set_state(element, GST_STATE_NULL);    
                    gst_bin_remove(GST_BIN(root()->audioGraph()), element);
                }
            }
        }
    } else if (description() & VideoSource) {
        if (GST_ELEMENT_PARENT(m_videoTee) == GST_ELEMENT(root()->videoGraph())) {
           gst_element_set_state(m_videoTee, GST_STATE_NULL);    
           gst_bin_remove(GST_BIN(root()->videoGraph()), m_videoTee);
        }
        for (int i=0; i <m_videoSinkList.size(); ++i) {
            QObject *videoSink = m_videoSinkList[i];
            if (MediaNode *vw = qobject_cast<MediaNode*>(videoSink)) {
                GstElement *element = vw->videoElement();
                if (GST_ELEMENT_PARENT(element) == GST_ELEMENT(root()->videoGraph())) {
                    gst_element_set_state(element, GST_STATE_NULL);    
                    gst_bin_remove(GST_BIN(root()->videoGraph()), element);
                }
            }
        }
    }
    return true;
}
Beispiel #12
0
void GstUtils::sync_state_with_parent(GstElement *element) {
  if (!GST_IS_ELEMENT(element)) {
    g_debug("GstUtils::sync_state_with_parent, arg is not an element");
    return;
  }

  GstElement *parent = GST_ELEMENT(GST_ELEMENT_PARENT(element));
  if (GST_IS_ELEMENT(parent)) {
    if (GST_STATE(parent) != GST_STATE_TARGET(parent))
      gst_element_set_state(element, GST_STATE_TARGET(parent));
    else
      gst_element_sync_state_with_parent(element);
  }
  else
    g_warning
        ("GstUtils::sync_state_with_parent, cannot sync an orphan element");
}
Beispiel #13
0
static void
window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow)
{
	GstElement *sink;

	if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
			!gst_structure_has_name(msg->structure,
			"prepare-xwindow-id"))
		return;

	sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
	while (sink != ow->sink) {
		if (sink == NULL)
			return;
		sink = GST_ELEMENT_PARENT(sink);
	}

	g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_FUNC
			| G_SIGNAL_MATCH_DATA, 0, 0, NULL,
			window_id_cb, ow);

	gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(
			GST_MESSAGE_SRC(msg)), ow->window_id);
}
Beispiel #14
0
gboolean
purple_media_manager_remove_output_window(PurpleMediaManager *manager,
		gulong output_window_id)
{
#ifdef USE_VV
	PurpleMediaOutputWindow *output_window = NULL;
	GList *iter;

	g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);

	iter = manager->priv->output_windows;
	for (; iter; iter = g_list_next(iter)) {
		PurpleMediaOutputWindow *ow = iter->data;
		if (ow->id == output_window_id) {
			manager->priv->output_windows = g_list_delete_link(
					manager->priv->output_windows, iter);
			output_window = ow;
			break;
		}
	}

	if (output_window == NULL)
		return FALSE;

	if (output_window->sink != NULL) {
		GstPad *pad = gst_element_get_static_pad(
				output_window->sink, "sink");
		GstPad *peer = gst_pad_get_peer(pad);
		GstElement *colorspace = GST_ELEMENT_PARENT(peer), *queue;
		gst_object_unref(pad);
		gst_object_unref(peer);
		pad = gst_element_get_static_pad(colorspace, "sink");
		peer = gst_pad_get_peer(pad);
		queue = GST_ELEMENT_PARENT(peer);
		gst_object_unref(pad);
		gst_object_unref(peer);
		pad = gst_element_get_static_pad(queue, "sink");
		peer = gst_pad_get_peer(pad);
		gst_object_unref(pad);
		if (peer != NULL)
			gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer);
		gst_element_set_locked_state(queue, TRUE);
		gst_element_set_state(queue, GST_STATE_NULL);
		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue);
		gst_element_set_locked_state(colorspace, TRUE);
		gst_element_set_state(colorspace, GST_STATE_NULL);
		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(colorspace)), colorspace);
		gst_element_set_locked_state(output_window->sink, TRUE);
		gst_element_set_state(output_window->sink, GST_STATE_NULL);
		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)),
				output_window->sink);
	}

	g_free(output_window->session_id);
	g_free(output_window->participant);
	g_free(output_window);

	return TRUE;
#else
	return FALSE;
#endif
}
Beispiel #15
0
gboolean
purple_media_manager_create_output_window(PurpleMediaManager *manager,
		PurpleMedia *media, const gchar *session_id,
		const gchar *participant)
{
#ifdef USE_VV
	GList *iter;

	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);

	iter = manager->priv->output_windows;
	for(; iter; iter = g_list_next(iter)) {
		PurpleMediaOutputWindow *ow = iter->data;

		if (ow->sink == NULL && ow->media == media &&
				((participant != NULL &&
				ow->participant != NULL &&
				!strcmp(participant, ow->participant)) ||
				(participant == ow->participant)) &&
				!strcmp(session_id, ow->session_id)) {
			GstBus *bus;
			GstElement *queue, *colorspace;
			GstElement *tee = purple_media_get_tee(media,
					session_id, participant);

			if (tee == NULL)
				continue;

			queue = gst_element_factory_make(
					"queue", NULL);
			colorspace = gst_element_factory_make(
					"ffmpegcolorspace", NULL);
			ow->sink = purple_media_manager_get_element(
					manager, PURPLE_MEDIA_RECV_VIDEO,
					ow->media, ow->session_id,
					ow->participant);

			if (participant == NULL) {
				/* aka this is a preview sink */
				GObjectClass *klass =
						G_OBJECT_GET_CLASS(ow->sink);
				if (g_object_class_find_property(klass,
						"sync"))
					g_object_set(G_OBJECT(ow->sink),
							"sync", "FALSE", NULL);
				if (g_object_class_find_property(klass,
						"async"))
					g_object_set(G_OBJECT(ow->sink),
							"async", FALSE, NULL);
			}

			gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
					queue, colorspace, ow->sink, NULL);

			bus = gst_pipeline_get_bus(GST_PIPELINE(
					manager->priv->pipeline));
			g_signal_connect(bus, "sync-message::element",
					G_CALLBACK(window_id_cb), ow);
			gst_object_unref(bus);

			gst_element_set_state(ow->sink, GST_STATE_PLAYING);
			gst_element_set_state(colorspace, GST_STATE_PLAYING);
			gst_element_set_state(queue, GST_STATE_PLAYING);
			gst_element_link(colorspace, ow->sink);
			gst_element_link(queue, colorspace);
			gst_element_link(tee, queue);
		}
	}
	return TRUE;
#else
	return FALSE;
#endif
}
Beispiel #16
0
gboolean
shmdata_base_reader_start (shmdata_base_reader_t * reader, const char *socketPath)
{
    if (NULL == reader)
        return FALSE;
    g_mutex_lock (&reader->mutex_);
    destroy_polling_g_source (reader);
    if (reader->install_sync_handler_)
    {
        g_debug ("installing a sync handler");
        //looking for the bus, searching the top level
        GstElement *pipe = reader->bin_;

        while (pipe != NULL && !GST_IS_PIPELINE (pipe))
            pipe = GST_ELEMENT_PARENT (pipe);

        if( GST_IS_PIPELINE (pipe))
        {
            GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipe));

            gst_bus_set_sync_handler (bus, shmdata_base_reader_message_handler, NULL);
            gst_object_unref (bus);
        }
        else
        {
            g_warning ("no top level pipeline found when starting, cannot install sync_handler");
            g_mutex_unlock (&reader->mutex_);
            return FALSE;
        }
    }
    reader->socket_name_ = g_strdup (socketPath);
    //monitoring the shared memory file
    reader->shmfile_ = g_file_new_for_commandline_arg (reader->socket_name_);
    if (g_file_query_exists (reader->shmfile_, NULL))
    {
        g_debug ("existing shmdata, attaching (%s)",
                 reader->socket_name_);
        reader->initialized_ = TRUE;
        g_mutex_unlock (&reader->mutex_);//give hand to user
        reader->on_first_data_ (reader, reader->on_first_data_userData_);
        shmdata_base_reader_attach (reader); //attach is acquiring mutex
        g_mutex_lock (&reader->mutex_);//get hand back
    }
    else
        g_debug ("monitoring %s",
                 reader->socket_name_);
    //#ifdef HAVE_OSX
#if 1
    //directory monitoring is not working on osx, use polling :(
    /* g_timeout_add (500, */
    /*                shmdata_base_reader_poll_shmdata_path, */
    /* 		 reader); */
    //GSource *source;
    reader->polling_g_source_ = g_timeout_source_new (500);
    g_source_set_callback (reader->polling_g_source_,
                           shmdata_base_reader_poll_shmdata_path,
                           reader,
                           NULL);
    g_source_attach (reader->polling_g_source_,
                     reader->g_main_context_);
    g_source_unref (reader->polling_g_source_);
#else
    //FIXME fix monitoring with custom gmaincontext... find how to use "g_main_context_push_thread_default"...
    if (reader->g_main_context_ != NULL)
    {
        g_debug ("shmdata_base_reader_start: set a custom maincontext");
        g_main_context_push_thread_default (reader->g_main_context_);
    }
    GFile *dir = g_file_get_parent (reader->shmfile_);
    if (!g_file_supports_thread_contexts(dir))
        g_debug ("does not support thread_context");
    GError *error = NULL;
    reader->dirMonitor_ = g_file_monitor_directory (dir,
                          G_FILE_MONITOR_NONE,
                          NULL, &error);
    g_object_unref (dir);
    if (reader->dirMonitor_ == NULL)
    {
        g_warning ("monitor directory failled: %s",
                   error->message);
        g_error_free (error);
        g_mutex_unlock (&reader->mutex_);
        return FALSE;
    }
    g_signal_connect (reader->dirMonitor_,
                      "changed",
                      G_CALLBACK (shmdata_base_reader_file_system_monitor_change),
                      reader);
    if (reader->g_main_context_ != NULL)
        g_main_context_pop_thread_default (reader->g_main_context_);
#endif
    g_debug ("shmdata reader started (%s)", reader->socket_name_);
    g_mutex_unlock (&reader->mutex_);
    return TRUE;
}
Beispiel #17
0
static void
gst_tcp_mix_src_request_link_pad (GstTCPMixSrc * src, GstTCPMixSrcPad * pad)
{
  GstTCPMixSrcPad *p;
  GstPad *pp;
  GList *item;
  gboolean linked = FALSE;
  GstBin *parent;
  GstElement *target;
  GstPadLinkReturn linkRet;

  if (gst_pad_is_linked (GST_PAD (pad))) {
#if 1
    pp = GST_PAD_PEER (pad);
    GST_WARNING_OBJECT (src, "Pad %s.%s already linked to %s.%s",
        GST_ELEMENT_NAME (src), GST_PAD_NAME (pad),
        GST_ELEMENT_NAME (GST_PAD_PARENT (pp)), GST_PAD_NAME (pp));
#endif
    return;
  }

  GST_LOG_OBJECT (src, "Linking pad '%s.%s'",
      GST_ELEMENT_NAME (src), GST_PAD_NAME (pad));

  INFO ("link");

  /**
   *  Don't do GST_OBJECT_LOCK() here, it causes DEADLOCK.
   */
  /* GST_OBJECT_LOCK (src); */

  if (!src->autosink)
    goto find_sink;

  parent = GST_BIN (GST_ELEMENT_PARENT (src));
  target = gst_bin_get_by_name (parent, src->autosink);

  INFO ("link");

  if (!target)
    goto find_sink;

  pp = gst_element_get_request_pad (target, "sink_%u");

  GST_DEBUG_OBJECT (src, "Link %s.%s-%s.%s",
      GST_ELEMENT_NAME (src), GST_PAD_NAME (pad),
      GST_ELEMENT_NAME (GST_PAD_PARENT (pp)), GST_PAD_NAME (pp));

  INFO ("link");

  linkRet = gst_pad_link (GST_PAD (pad), GST_PAD (pp));
  if (GST_PAD_LINK_FAILED (linkRet)) {
    GST_ERROR_OBJECT (src, "can't link");
  }

  return;

find_sink:
#if 1
  for (item = GST_ELEMENT_PADS (src); item; item = g_list_next (item)) {
    p = GST_TCP_MIX_SRC_PAD (item->data);
    if (GST_PAD_IS_SRC (p)) {
      GST_OBJECT_LOCK (p);
      if ((pp = GST_PAD_PEER (p))) {
        GstElement *ele = GST_ELEMENT (GST_PAD_PARENT (pp));

        // FIXME: pad name calculation
        pp = gst_element_get_request_pad (ele, "sink_%u");

        GST_DEBUG_OBJECT (src, "Link %s.%s-%s.%s",
            GST_ELEMENT_NAME (src), GST_PAD_NAME (pad),
            GST_ELEMENT_NAME (GST_PAD_PARENT (pp)), GST_PAD_NAME (pp));

        linkRet = gst_pad_link (GST_PAD (pad), GST_PAD (pp));
        if (GST_PAD_LINK_FAILED (linkRet)) {
          GST_ERROR_OBJECT (src, "can't link");
        } else {
          linked = TRUE;
        }
      }
      GST_OBJECT_UNLOCK (p);

      if (linked)
        break;
    }
  }
#endif

  /* GST_OBJECT_UNLOCK (src); */
  return;
}
static GstStateChangeReturn
gst_camerabin_image_change_state (GstElement * element,
    GstStateChange transition)
{
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
  GstCameraBinImage *img = GST_CAMERABIN_IMAGE (element);

  GST_DEBUG_OBJECT (element, "changing state: %s -> %s",
      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));

  switch (transition) {
    case GST_STATE_CHANGE_NULL_TO_READY:
      if (!gst_camerabin_image_create_elements (img)) {
        return GST_STATE_CHANGE_FAILURE;
      }
      /* Allow setting filename when image bin in READY state */
      gst_element_set_locked_state (img->sink, TRUE);
      GST_INFO_OBJECT (img, "locking imagebin->sink state to %s",
          gst_element_state_get_name (GST_STATE (img->sink)));
      break;
    case GST_STATE_CHANGE_READY_TO_PAUSED:
      if (!g_str_equal (img->filename->str, "")) {
        GST_INFO_OBJECT (img, "preparing image with filename: %s",
            img->filename->str);
        gst_element_set_locked_state (img->sink, FALSE);
      } else {
        GST_INFO_OBJECT (img, "keep sink locked, we have no filename yet");
      }
      break;
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      /* Set sink to NULL in order to write the file _now_ */
      GST_INFO_OBJECT (img, "write image with filename: %s",
          img->filename->str);
      gst_element_set_locked_state (img->sink, TRUE);
      gst_element_set_state (img->sink, GST_STATE_NULL);
      g_string_assign (img->filename, "");
      break;
    default:
      break;
  }

  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      /* Write debug graph to file */
      GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (GST_ELEMENT_PARENT (img)),
          GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
          GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, "imagebin.playing");
      break;
    case GST_STATE_CHANGE_READY_TO_NULL:
      gst_camerabin_image_destroy_elements (img);
      break;
    default:
      break;
  }

  GST_DEBUG_OBJECT (element, "changed state: %s -> %s = %s",
      gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
      gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)),
      gst_element_state_change_return_get_name (ret));

  return ret;
}