mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-12 10:25:33 +00:00
ges: pipeline: Implement context sharing logic
That is the logic from `playbin2` Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/4005 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7883>
This commit is contained in:
parent
0cb829c589
commit
bb6bf7d23b
1 changed files with 220 additions and 6 deletions
|
@ -75,6 +75,8 @@ struct _GESPipelinePrivate
|
||||||
{
|
{
|
||||||
GESTimeline *timeline;
|
GESTimeline *timeline;
|
||||||
GstElement *playsink;
|
GstElement *playsink;
|
||||||
|
GstElement *video_sink;
|
||||||
|
GstElement *audio_sink;
|
||||||
GstElement *encodebin;
|
GstElement *encodebin;
|
||||||
/* Note : urisink is only created when a URI has been provided */
|
/* Note : urisink is only created when a URI has been provided */
|
||||||
GstElement *urisink;
|
GstElement *urisink;
|
||||||
|
@ -88,6 +90,9 @@ struct _GESPipelinePrivate
|
||||||
GstEncodingProfile *profile;
|
GstEncodingProfile *profile;
|
||||||
|
|
||||||
GThread *valid_thread;
|
GThread *valid_thread;
|
||||||
|
|
||||||
|
GList *contexts;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -210,12 +215,10 @@ ges_pipeline_set_property (GObject * object, guint property_id,
|
||||||
|
|
||||||
switch (property_id) {
|
switch (property_id) {
|
||||||
case PROP_AUDIO_SINK:
|
case PROP_AUDIO_SINK:
|
||||||
g_object_set_property (G_OBJECT (self->priv->playsink), "audio-sink",
|
ges_pipeline_preview_set_audio_sink (self, g_value_get_object (value));
|
||||||
value);
|
|
||||||
break;
|
break;
|
||||||
case PROP_VIDEO_SINK:
|
case PROP_VIDEO_SINK:
|
||||||
g_object_set_property (G_OBJECT (self->priv->playsink), "video-sink",
|
ges_pipeline_preview_set_video_sink (self, g_value_get_object (value));
|
||||||
value);
|
|
||||||
break;
|
break;
|
||||||
case PROP_TIMELINE:
|
case PROP_TIMELINE:
|
||||||
ges_pipeline_set_timeline (GES_PIPELINE (object),
|
ges_pipeline_set_timeline (GES_PIPELINE (object),
|
||||||
|
@ -265,6 +268,88 @@ ges_pipeline_constructed (GObject * object)
|
||||||
((GObjectClass *) ges_pipeline_parent_class)->constructed (object);
|
((GObjectClass *) ges_pipeline_parent_class)->constructed (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ges_pipeline_update_context (GESPipeline * self, GstContext * context)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
const gchar *context_type;
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (self);
|
||||||
|
context_type = gst_context_get_context_type (context);
|
||||||
|
for (l = self->priv->contexts; l; l = l->next) {
|
||||||
|
GstContext *tmp = l->data;
|
||||||
|
const gchar *tmp_type = gst_context_get_context_type (tmp);
|
||||||
|
|
||||||
|
/* Always store newest context but never replace
|
||||||
|
* a persistent one by a non-persistent one */
|
||||||
|
if (strcmp (context_type, tmp_type) == 0 &&
|
||||||
|
(gst_context_is_persistent (context) ||
|
||||||
|
!gst_context_is_persistent (tmp))) {
|
||||||
|
gst_context_replace ((GstContext **) & l->data, context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Not found? Add */
|
||||||
|
if (l == NULL) {
|
||||||
|
self->priv->contexts =
|
||||||
|
g_list_prepend (self->priv->contexts, gst_context_ref (context));
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ges_pipeline_handle_message (GstBin * bin, GstMessage * msg)
|
||||||
|
{
|
||||||
|
GESPipeline *self = GES_PIPELINE (bin);
|
||||||
|
|
||||||
|
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
|
||||||
|
const gchar *context_type;
|
||||||
|
GList *iter;
|
||||||
|
|
||||||
|
gst_message_parse_context_type (msg, &context_type);
|
||||||
|
GST_OBJECT_LOCK (self);
|
||||||
|
for (iter = self->priv->contexts; iter; iter = g_list_next (iter)) {
|
||||||
|
GstContext *tmp = iter->data;
|
||||||
|
const gchar *tmp_type = gst_context_get_context_type (tmp);
|
||||||
|
|
||||||
|
if (strcmp (context_type, tmp_type) == 0) {
|
||||||
|
GST_DEBUG_OBJECT (msg->src, "Setting context %s %" GST_PTR_FORMAT,
|
||||||
|
tmp_type, tmp);
|
||||||
|
gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
|
||||||
|
/* don't need to post upward this if it's answered by us */
|
||||||
|
if (iter) {
|
||||||
|
gst_message_unref (msg);
|
||||||
|
msg = NULL;
|
||||||
|
}
|
||||||
|
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
|
||||||
|
GstContext *context;
|
||||||
|
|
||||||
|
gst_message_parse_have_context (msg, &context);
|
||||||
|
ges_pipeline_update_context (self, context);
|
||||||
|
gst_context_unref (context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg) {
|
||||||
|
GST_BIN_CLASS (ges_pipeline_parent_class)->handle_message (bin, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ges_pipeline_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GESPipeline *self = GES_PIPELINE (object);
|
||||||
|
|
||||||
|
g_list_free_full (self->priv->contexts, (GDestroyNotify) gst_context_unref);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (ges_pipeline_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ges_pipeline_dispose (GObject * object)
|
ges_pipeline_dispose (GObject * object)
|
||||||
{
|
{
|
||||||
|
@ -277,6 +362,8 @@ ges_pipeline_dispose (GObject * object)
|
||||||
gst_object_unref (self->priv->playsink);
|
gst_object_unref (self->priv->playsink);
|
||||||
self->priv->playsink = NULL;
|
self->priv->playsink = NULL;
|
||||||
}
|
}
|
||||||
|
gst_clear_object ((GstObject **) & self->priv->video_sink);
|
||||||
|
gst_clear_object ((GstObject **) & self->priv->audio_sink);
|
||||||
|
|
||||||
if (self->priv->encodebin) {
|
if (self->priv->encodebin) {
|
||||||
if (self->priv->mode & (GES_PIPELINE_MODE_RENDER |
|
if (self->priv->mode & (GES_PIPELINE_MODE_RENDER |
|
||||||
|
@ -308,6 +395,7 @@ ges_pipeline_class_init (GESPipelineClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
GstBinClass *bin_klass = (GstBinClass *) klass;
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (ges_pipeline_debug, "gespipeline",
|
GST_DEBUG_CATEGORY_INIT (ges_pipeline_debug, "gespipeline",
|
||||||
GST_DEBUG_FG_YELLOW, "ges pipeline");
|
GST_DEBUG_FG_YELLOW, "ges pipeline");
|
||||||
|
@ -315,6 +403,7 @@ ges_pipeline_class_init (GESPipelineClass * klass)
|
||||||
|
|
||||||
object_class->constructed = ges_pipeline_constructed;
|
object_class->constructed = ges_pipeline_constructed;
|
||||||
object_class->dispose = ges_pipeline_dispose;
|
object_class->dispose = ges_pipeline_dispose;
|
||||||
|
object_class->finalize = ges_pipeline_finalize;
|
||||||
object_class->get_property = ges_pipeline_get_property;
|
object_class->get_property = ges_pipeline_get_property;
|
||||||
object_class->set_property = ges_pipeline_set_property;
|
object_class->set_property = ges_pipeline_set_property;
|
||||||
|
|
||||||
|
@ -395,6 +484,7 @@ ges_pipeline_class_init (GESPipelineClass * klass)
|
||||||
g_object_class_install_properties (object_class, PROP_LAST, properties);
|
g_object_class_install_properties (object_class, PROP_LAST, properties);
|
||||||
|
|
||||||
element_class->change_state = GST_DEBUG_FUNCPTR (ges_pipeline_change_state);
|
element_class->change_state = GST_DEBUG_FUNCPTR (ges_pipeline_change_state);
|
||||||
|
bin_klass->handle_message = GST_DEBUG_FUNCPTR (ges_pipeline_handle_message);
|
||||||
|
|
||||||
/* TODO : Add state_change handlers
|
/* TODO : Add state_change handlers
|
||||||
* Don't change state if we don't have a timeline */
|
* Don't change state if we don't have a timeline */
|
||||||
|
@ -605,6 +695,22 @@ ges_pipeline_change_state (GstElement * element, GstStateChange transition)
|
||||||
self = GES_PIPELINE (element);
|
self = GES_PIPELINE (element);
|
||||||
|
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||||
|
{
|
||||||
|
if (self->priv->video_sink == NULL) {
|
||||||
|
GstElement *tmpsink =
|
||||||
|
gst_element_factory_make ("autovideosink", "internal-sink");
|
||||||
|
ges_pipeline_preview_set_video_sink (self, g_object_ref_sink (tmpsink));
|
||||||
|
gst_object_unref (tmpsink);
|
||||||
|
}
|
||||||
|
if (self->priv->audio_sink == NULL) {
|
||||||
|
GstElement *tmpsink =
|
||||||
|
gst_element_factory_make ("autoaudiosink", "internal-sink");
|
||||||
|
ges_pipeline_preview_set_audio_sink (self, g_object_ref_sink (tmpsink));
|
||||||
|
gst_object_unref (tmpsink);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
if (G_UNLIKELY (self->priv->timeline == NULL)) {
|
if (G_UNLIKELY (self->priv->timeline == NULL)) {
|
||||||
GST_ERROR_OBJECT (element,
|
GST_ERROR_OBJECT (element,
|
||||||
|
@ -1474,6 +1580,57 @@ ges_pipeline_preview_get_video_sink (GESPipeline * self)
|
||||||
return sink;
|
return sink;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static GstBusSyncReply
|
||||||
|
activate_sink_bus_handler (GstBus * bus, GstMessage * msg, GESPipeline * self)
|
||||||
|
{
|
||||||
|
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
|
||||||
|
/* Only proxy errors from a fixed sink. If that fails we can just error out
|
||||||
|
* early as stuff will fail later anyway */
|
||||||
|
if (self->priv->video_sink
|
||||||
|
&& gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
|
||||||
|
GST_OBJECT_CAST (self->priv->video_sink)))
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (self), msg);
|
||||||
|
else
|
||||||
|
gst_message_unref (msg);
|
||||||
|
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
|
||||||
|
const gchar *context_type;
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
gst_message_parse_context_type (msg, &context_type);
|
||||||
|
GST_OBJECT_LOCK (self);
|
||||||
|
for (l = self->priv->contexts; l; l = l->next) {
|
||||||
|
GstContext *tmp = l->data;
|
||||||
|
const gchar *tmp_type = gst_context_get_context_type (tmp);
|
||||||
|
|
||||||
|
if (strcmp (context_type, tmp_type) == 0) {
|
||||||
|
gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
|
||||||
|
/* Forward if we couldn't answer the message */
|
||||||
|
if (l == NULL) {
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (self), msg);
|
||||||
|
} else {
|
||||||
|
gst_message_unref (msg);
|
||||||
|
}
|
||||||
|
} else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
|
||||||
|
GstContext *context;
|
||||||
|
|
||||||
|
gst_message_parse_have_context (msg, &context);
|
||||||
|
ges_pipeline_update_context (self, context);
|
||||||
|
gst_context_unref (context);
|
||||||
|
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (self), msg);
|
||||||
|
} else {
|
||||||
|
gst_element_post_message (GST_ELEMENT_CAST (self), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Doesn't really matter, nothing is using this bus */
|
||||||
|
return GST_BUS_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ges_pipeline_preview_set_video_sink:
|
* ges_pipeline_preview_set_video_sink:
|
||||||
* @self: A #GESPipeline in #GST_STATE_NULL
|
* @self: A #GESPipeline in #GST_STATE_NULL
|
||||||
|
@ -1484,10 +1641,39 @@ ges_pipeline_preview_get_video_sink (GESPipeline * self)
|
||||||
void
|
void
|
||||||
ges_pipeline_preview_set_video_sink (GESPipeline * self, GstElement * sink)
|
ges_pipeline_preview_set_video_sink (GESPipeline * self, GstElement * sink)
|
||||||
{
|
{
|
||||||
|
GstStateChangeReturn sret;
|
||||||
|
|
||||||
g_return_if_fail (GES_IS_PIPELINE (self));
|
g_return_if_fail (GES_IS_PIPELINE (self));
|
||||||
CHECK_THREAD (self);
|
CHECK_THREAD (self);
|
||||||
|
|
||||||
g_object_set (self->priv->playsink, "video-sink", sink, NULL);
|
gst_object_replace ((GstObject **) & self->priv->video_sink,
|
||||||
|
(GstObject *) sink);
|
||||||
|
|
||||||
|
g_object_set (self->priv->playsink, "video-sink", self->priv->video_sink,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!sink)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GstBus *bus = gst_bus_new ();
|
||||||
|
if (!GST_OBJECT_PARENT (sink)) {
|
||||||
|
gst_bus_set_sync_handler (bus,
|
||||||
|
(GstBusSyncHandler) activate_sink_bus_handler, self, NULL);
|
||||||
|
gst_element_set_bus (sink, bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
sret = gst_element_set_state (sink, GST_STATE_READY);
|
||||||
|
if (sret == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not activate sink");
|
||||||
|
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (bus) {
|
||||||
|
gst_element_set_bus (sink, NULL);
|
||||||
|
gst_object_unref (bus);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1521,8 +1707,36 @@ ges_pipeline_preview_get_audio_sink (GESPipeline * self)
|
||||||
void
|
void
|
||||||
ges_pipeline_preview_set_audio_sink (GESPipeline * self, GstElement * sink)
|
ges_pipeline_preview_set_audio_sink (GESPipeline * self, GstElement * sink)
|
||||||
{
|
{
|
||||||
|
GstStateChangeReturn sret;
|
||||||
|
|
||||||
g_return_if_fail (GES_IS_PIPELINE (self));
|
g_return_if_fail (GES_IS_PIPELINE (self));
|
||||||
CHECK_THREAD (self);
|
CHECK_THREAD (self);
|
||||||
|
|
||||||
g_object_set (self->priv->playsink, "audio-sink", sink, NULL);
|
gst_object_replace ((GstObject **) & self->priv->audio_sink,
|
||||||
|
(GstObject *) sink);
|
||||||
|
g_object_set (self->priv->playsink, "audio-sink", self->priv->audio_sink,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!sink)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GstBus *bus = gst_bus_new ();
|
||||||
|
if (!GST_OBJECT_PARENT (sink)) {
|
||||||
|
gst_bus_set_sync_handler (bus,
|
||||||
|
(GstBusSyncHandler) activate_sink_bus_handler, self, NULL);
|
||||||
|
gst_element_set_bus (sink, bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
sret = gst_element_set_state (sink, GST_STATE_READY);
|
||||||
|
if (sret == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
GST_ERROR_OBJECT (self, "Could not activate sink");
|
||||||
|
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (bus) {
|
||||||
|
gst_element_set_bus (sink, NULL);
|
||||||
|
gst_object_unref (bus);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue