mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 01:30:38 +00:00
GESTimeline: Use GstDiscoverer for incomplete filesources
If a GESTimelineFileSource is added to a layer and: * It doesn't have specified supported formats * OR it doesn't have a specified maximum duration * OR it doesn't have a specifed duration Then we asynchronously send it to the GstDiscoverer. If this happens, the state change of the timeline from READY to PAUSED will happen asynchronously and be completed when everything has been properly discovered. Part 2 of GstDiscoverer integration
This commit is contained in:
parent
eb4b02f3ed
commit
1bb45a61a6
2 changed files with 184 additions and 5 deletions
|
@ -60,9 +60,19 @@ enum
|
||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static GstBinClass *parent_class;
|
||||||
|
|
||||||
static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
|
static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
gint custom_find_track (TrackPrivate * priv, GESTrack * track);
|
gint custom_find_track (TrackPrivate * priv, GESTrack * track);
|
||||||
|
static GstStateChangeReturn
|
||||||
|
ges_timeline_change_state (GstElement * element, GstStateChange transition);
|
||||||
|
static void
|
||||||
|
discoverer_ready_cb (GstDiscoverer * discoverer, GESTimeline * timeline);
|
||||||
|
static void
|
||||||
|
discoverer_discovered_cb (GstDiscoverer * discoverer,
|
||||||
|
GstDiscovererInformation * info, GError * err, GESTimeline * timeline);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ges_timeline_get_property (GObject * object, guint property_id,
|
ges_timeline_get_property (GObject * object, guint property_id,
|
||||||
GValue * value, GParamSpec * pspec)
|
GValue * value, GParamSpec * pspec)
|
||||||
|
@ -86,6 +96,13 @@ ges_timeline_set_property (GObject * object, guint property_id,
|
||||||
static void
|
static void
|
||||||
ges_timeline_dispose (GObject * object)
|
ges_timeline_dispose (GObject * object)
|
||||||
{
|
{
|
||||||
|
GESTimeline *timeline = GES_TIMELINE (object);
|
||||||
|
|
||||||
|
if (timeline->discoverer) {
|
||||||
|
gst_discoverer_stop (timeline->discoverer);
|
||||||
|
g_object_unref (timeline->discoverer);
|
||||||
|
timeline->discoverer = NULL;
|
||||||
|
}
|
||||||
G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
|
G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,13 +116,17 @@ static void
|
||||||
ges_timeline_class_init (GESTimelineClass * klass)
|
ges_timeline_class_init (GESTimelineClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
|
parent_class = g_type_class_peek_parent (klass);
|
||||||
|
|
||||||
|
element_class->change_state = ges_timeline_change_state;
|
||||||
|
|
||||||
object_class->get_property = ges_timeline_get_property;
|
object_class->get_property = ges_timeline_get_property;
|
||||||
object_class->set_property = ges_timeline_set_property;
|
object_class->set_property = ges_timeline_set_property;
|
||||||
object_class->dispose = ges_timeline_dispose;
|
object_class->dispose = ges_timeline_dispose;
|
||||||
object_class->finalize = ges_timeline_finalize;
|
object_class->finalize = ges_timeline_finalize;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GESTimeline::track-added
|
* GESTimeline::track-added
|
||||||
* @timeline: the #GESTimeline
|
* @timeline: the #GESTimeline
|
||||||
|
@ -161,6 +182,14 @@ ges_timeline_init (GESTimeline * self)
|
||||||
{
|
{
|
||||||
self->layers = NULL;
|
self->layers = NULL;
|
||||||
self->tracks = NULL;
|
self->tracks = NULL;
|
||||||
|
|
||||||
|
/* New discoverer with a 15s timeout */
|
||||||
|
self->discoverer = gst_discoverer_new (15 * GST_SECOND);
|
||||||
|
g_signal_connect (self->discoverer, "ready", G_CALLBACK (discoverer_ready_cb),
|
||||||
|
self);
|
||||||
|
g_signal_connect (self->discoverer, "discovered",
|
||||||
|
G_CALLBACK (discoverer_discovered_cb), self);
|
||||||
|
gst_discoverer_start (self->discoverer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,13 +245,10 @@ ges_timeline_save (GESTimeline * timeline, gchar * uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object)
|
||||||
GESTimeline * timeline)
|
|
||||||
{
|
{
|
||||||
GList *tmp;
|
GList *tmp;
|
||||||
|
|
||||||
GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
|
|
||||||
|
|
||||||
for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
|
for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
|
||||||
TrackPrivate *priv = (TrackPrivate *) tmp->data;
|
TrackPrivate *priv = (TrackPrivate *) tmp->data;
|
||||||
GESTrack *track = priv->track;
|
GESTrack *track = priv->track;
|
||||||
|
@ -239,6 +265,151 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
||||||
GST_LOG ("Got new TrackObject %p, adding it to track", trobj);
|
GST_LOG ("Got new TrackObject %p, adding it to track", trobj);
|
||||||
ges_track_add_object (track, trobj);
|
ges_track_add_object (track, trobj);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_async_start (GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GstMessage *message;
|
||||||
|
|
||||||
|
timeline->async_pending = TRUE;
|
||||||
|
|
||||||
|
message = gst_message_new_async_start (GST_OBJECT_CAST (timeline), FALSE);
|
||||||
|
parent_class->handle_message (GST_BIN_CAST (timeline), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_async_done (GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GstMessage *message;
|
||||||
|
|
||||||
|
if (timeline->async_pending) {
|
||||||
|
GST_DEBUG_OBJECT (timeline, "Emitting async-done");
|
||||||
|
message = gst_message_new_async_done (GST_OBJECT_CAST (timeline));
|
||||||
|
parent_class->handle_message (GST_BIN_CAST (timeline), message);
|
||||||
|
|
||||||
|
timeline->async_pending = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
discoverer_ready_cb (GstDiscoverer * discoverer, GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
do_async_done (timeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
discoverer_discovered_cb (GstDiscoverer * discoverer,
|
||||||
|
GstDiscovererInformation * info, GError * err, GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GList *tmp;
|
||||||
|
gboolean found = FALSE;
|
||||||
|
GESTimelineFileSource *tfs = NULL;
|
||||||
|
|
||||||
|
GST_DEBUG ("Discovered uri %s", info->uri);
|
||||||
|
|
||||||
|
/* Find corresponding TimelineFileSource in the sources */
|
||||||
|
for (tmp = timeline->pendingobjects; tmp; tmp = tmp->next) {
|
||||||
|
tfs = (GESTimelineFileSource *) tmp->data;
|
||||||
|
|
||||||
|
if (!g_strcmp0 (tfs->uri, info->uri)) {
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
/* Remove object from list */
|
||||||
|
timeline->pendingobjects =
|
||||||
|
g_list_delete_link (timeline->pendingobjects, tmp);
|
||||||
|
|
||||||
|
/* FIXME : Handle errors in discovery */
|
||||||
|
|
||||||
|
/* Update timelinefilesource properties based on info */
|
||||||
|
for (tmp = info->stream_list; tmp; tmp = tmp->next) {
|
||||||
|
GstStreamInformation *sinf = (GstStreamInformation *) tmp->data;
|
||||||
|
|
||||||
|
if (sinf->streamtype == GST_STREAM_AUDIO)
|
||||||
|
tfs->supportedformats |= GES_TRACK_TYPE_AUDIO;
|
||||||
|
else if ((sinf->streamtype == GST_STREAM_VIDEO) ||
|
||||||
|
(sinf->streamtype == GST_STREAM_IMAGE))
|
||||||
|
tfs->supportedformats |= GES_TRACK_TYPE_VIDEO;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set (tfs, "max-duration", info->duration, NULL);
|
||||||
|
|
||||||
|
/* Continue the processing on tfs */
|
||||||
|
add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
ges_timeline_change_state (GstElement * element, GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
|
GESTimeline *timeline = GES_TIMELINE (element);
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
|
if (timeline->pendingobjects) {
|
||||||
|
do_async_start (timeline);
|
||||||
|
ret = GST_STATE_CHANGE_ASYNC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
GstStateChangeReturn bret;
|
||||||
|
|
||||||
|
bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
|
||||||
|
do_async_done (timeline);
|
||||||
|
ret = bret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
do_async_done (timeline);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
|
||||||
|
GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GList *tmp;
|
||||||
|
|
||||||
|
GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
|
||||||
|
|
||||||
|
if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
|
||||||
|
GESTimelineFileSource *tfs = GES_TIMELINE_FILE_SOURCE (object);
|
||||||
|
|
||||||
|
/* Send the filesource to the discoverer if:
|
||||||
|
* * it doesn't have specified supported formats
|
||||||
|
* * OR it doesn't have a specified max-duration
|
||||||
|
* * OR it doesn't have a valid duration */
|
||||||
|
|
||||||
|
if (tfs->supportedformats == GES_TRACK_TYPE_UNKNOWN ||
|
||||||
|
tfs->maxduration == GST_CLOCK_TIME_NONE || object->duration == 0) {
|
||||||
|
GST_LOG ("Incomplete TimelineFileSource, discovering it");
|
||||||
|
timeline->pendingobjects =
|
||||||
|
g_list_append (timeline->pendingobjects, object);
|
||||||
|
gst_discoverer_append_uri (timeline->discoverer,
|
||||||
|
GES_TIMELINE_FILE_SOURCE (object)->uri);
|
||||||
|
} else
|
||||||
|
add_object_to_tracks (timeline, object);
|
||||||
|
} else {
|
||||||
|
add_object_to_tracks (timeline, object);
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG ("done");
|
GST_DEBUG ("done");
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/discoverer/gstdiscoverer.h>
|
||||||
#include <ges/ges-types.h>
|
#include <ges/ges-types.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
@ -54,6 +55,13 @@ struct _GESTimeline {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
GList *layers; /* A list of GESTimelineLayer sorted by priority */
|
GList *layers; /* A list of GESTimelineLayer sorted by priority */
|
||||||
GList *tracks; /* A list of private track data */
|
GList *tracks; /* A list of private track data */
|
||||||
|
|
||||||
|
/* discoverer used for virgin sources */
|
||||||
|
GstDiscoverer *discoverer;
|
||||||
|
/* Objects that are being discovered FIXME : LOCK ! */
|
||||||
|
GList *pendingobjects;
|
||||||
|
/* Whether we are changing state asynchronously or not */
|
||||||
|
gboolean async_pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GESTimelineClass {
|
struct _GESTimelineClass {
|
||||||
|
|
Loading…
Reference in a new issue