diff --git a/gst/hls/Makefile.am b/gst/hls/Makefile.am index fec8eeb278..7a8297545f 100644 --- a/gst/hls/Makefile.am +++ b/gst/hls/Makefile.am @@ -4,6 +4,7 @@ plugin_LTLIBRARIES = libgstfragmented.la libgstfragmented_la_SOURCES = \ m3u8.c \ gsthlsdemux.c \ + gstfragment.c \ gstfragmentedplugin.c libgstfragmented_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(SOUP_CFLAGS) @@ -14,6 +15,7 @@ libgstfragmented_la_LIBTOOLFLAGS = --tag=disable-static # headers we need but don't want installed noinst_HEADERS = \ gstfragmented.h \ + gstfragment.h \ gsthlsdemux.h \ m3u8.h diff --git a/gst/hls/gstfragment.c b/gst/hls/gstfragment.c new file mode 100644 index 0000000000..210a930268 --- /dev/null +++ b/gst/hls/gstfragment.c @@ -0,0 +1,226 @@ +/* GStreamer + * Copyright (C) 2011 Andoni Morales Alastruey + * + * gstfragment.c: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gstfragmented.h" +#include "gstfragment.h" + +#define GST_CAT_DEFAULT fragmented_debug + +#define GST_FRAGMENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_FRAGMENT, GstFragmentPrivate)) + +enum +{ + PROP_0, + PROP_INDEX, + PROP_NAME, + PROP_DURATION, + PROP_DISCONTINOUS, + PROP_BUFFER_LIST, + PROP_LAST +}; + +struct _GstFragmentPrivate +{ + GstBufferList *buffer_list; + GstBufferListIterator *buffer_iterator; + gboolean can_set_headers; + gboolean has_headers; +}; + +G_DEFINE_TYPE (GstFragment, gst_fragment, G_TYPE_OBJECT); + +static void gst_fragment_dispose (GObject * object); +static void gst_fragment_finalize (GObject * object); + +static void +gst_fragment_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + GstFragment *fragment = GST_FRAGMENT (object); + + switch (property_id) { + case PROP_INDEX: + g_value_set_uint (value, fragment->index); + break; + + case PROP_NAME: + g_value_set_string (value, fragment->name); + break; + + case PROP_DURATION: + g_value_set_uint64 (value, fragment->stop_time - fragment->start_time); + break; + + case PROP_DISCONTINOUS: + g_value_set_boolean (value, fragment->discontinuous); + break; + + case PROP_BUFFER_LIST: + g_value_set_object (value, gst_fragment_get_buffer_list (fragment)); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gst_fragment_class_init (GstFragmentClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GstFragmentPrivate)); + + gobject_class->get_property = gst_fragment_get_property; + gobject_class->dispose = gst_fragment_dispose; + gobject_class->finalize = gst_fragment_finalize; + + g_object_class_install_property (gobject_class, PROP_INDEX, + g_param_spec_uint ("index", "Index", "Index of the fragment", 0, + G_MAXUINT, 0, G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, PROP_NAME, + g_param_spec_string ("name", "Name", + "Name of the fragment (eg:fragment-12.ts)", NULL, G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, PROP_DISCONTINOUS, + g_param_spec_boolean ("discontinuous", "Discontinous", + "Whether this fragment has a discontinuity or not", + FALSE, G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, PROP_DURATION, + g_param_spec_uint64 ("duration", "Fragment duration", + "Duration of the fragment", 0, G_MAXUINT64, 0, G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, PROP_BUFFER_LIST, + g_param_spec_object ("buffer-list", "Buffer List", + "A list with the fragment's buffers", GST_TYPE_FRAGMENT, + G_PARAM_READABLE)); +} + +static void +gst_fragment_init (GstFragment * fragment) +{ + GstFragmentPrivate *priv; + + fragment->priv = priv = GST_FRAGMENT_GET_PRIVATE (fragment); + fragment->priv->buffer_list = gst_buffer_list_new (); + fragment->priv->buffer_iterator = + gst_buffer_list_iterate (fragment->priv->buffer_list); + gst_buffer_list_iterator_add_group (fragment->priv->buffer_iterator); + fragment->priv->can_set_headers = TRUE; + fragment->priv->has_headers = FALSE; + fragment->download_start_time = g_get_real_time (); + fragment->start_time = 0; + fragment->stop_time = 0; + fragment->index = 0; + fragment->name = g_strdup (""); + fragment->completed = FALSE; + fragment->discontinuous = FALSE; +} + +GstFragment * +gst_fragment_new (void) +{ + return GST_FRAGMENT (g_object_new (GST_TYPE_FRAGMENT, NULL)); +} + +static void +gst_fragment_finalize (GObject * gobject) +{ + GstFragment *fragment = GST_FRAGMENT (gobject); + + g_free (fragment->name); + + G_OBJECT_CLASS (gst_fragment_parent_class)->finalize (gobject); +} + +void +gst_fragment_dispose (GObject * object) +{ + GstFragment *fragment = GST_FRAGMENT (object); + + if (fragment->priv->buffer_list != NULL) { + gst_buffer_list_iterator_free (fragment->priv->buffer_iterator); + gst_buffer_list_unref (fragment->priv->buffer_list); + fragment->priv->buffer_list = NULL; + } + + G_OBJECT_CLASS (gst_fragment_parent_class)->dispose (object); +} + +GstBufferList * +gst_fragment_get_buffer_list (GstFragment * fragment) +{ + g_return_val_if_fail (fragment != NULL, NULL); + + if (!fragment->completed) + return NULL; + + gst_buffer_list_ref (fragment->priv->buffer_list); + return fragment->priv->buffer_list; +} + +gboolean +gst_fragment_set_headers (GstFragment * fragment, GstBuffer ** buffer, + guint count) +{ + guint i; + + g_return_val_if_fail (fragment != NULL, FALSE); + g_return_val_if_fail (buffer != NULL, FALSE); + + if (!fragment->priv->can_set_headers) + return FALSE; + + for (i = 0; i < count; i++) { + gst_buffer_ref (buffer[i]); + gst_buffer_list_iterator_add (fragment->priv->buffer_iterator, buffer[i]); + gst_buffer_list_iterator_add_group (fragment->priv->buffer_iterator); + } + fragment->priv->has_headers = TRUE; + return TRUE; +} + +gboolean +gst_fragment_add_buffer (GstFragment * fragment, GstBuffer * buffer) +{ + g_return_val_if_fail (fragment != NULL, FALSE); + g_return_val_if_fail (buffer != NULL, FALSE); + + if (fragment->completed) { + GST_WARNING ("Fragment is completed, could not add more buffers"); + return FALSE; + } + + /* if this is the first buffer forbid setting the headers anymore */ + if (G_UNLIKELY (fragment->priv->can_set_headers)) { + fragment->priv->can_set_headers = FALSE; + } + + GST_DEBUG ("Adding new buffer to the fragment"); + gst_buffer_ref (buffer); + gst_buffer_list_iterator_add (fragment->priv->buffer_iterator, buffer); + return TRUE; +} diff --git a/gst/hls/gstfragment.h b/gst/hls/gstfragment.h new file mode 100644 index 0000000000..9ea018606e --- /dev/null +++ b/gst/hls/gstfragment.h @@ -0,0 +1,69 @@ +/* GStreamer + * Copyright (C) 2011 Andoni Morales Alastruey + * + * gstfragment.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GSTFRAGMENT_H__ +#define __GSTFRAGMENT_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_FRAGMENT (gst_fragment_get_type()) +#define GST_FRAGMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FRAGMENT,GstFragment)) +#define GST_FRAGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FRAGMENT,GstFragmentClass)) +#define GST_IS_FRAGMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FRAGMENT)) +#define GST_IS_FRAGMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FRAGMENT)) + +typedef struct _GstFragment GstFragment; +typedef struct _GstFragmentPrivate GstFragmentPrivate; +typedef struct _GstFragmentClass GstFragmentClass; + +struct _GstFragment +{ + GObject parent; + + gchar * name; /* Name of the fragment */ + gboolean completed; /* Whether the fragment is complete or not */ + guint64 download_start_time; /* Epoch time when the download started */ + guint64 download_stop_time; /* Epoch time when the download finished */ + guint64 start_time; /* Start time of the fragment */ + guint64 stop_time; /* Stop time of the fragment */ + gboolean index; /* Index of the fragment */ + gboolean discontinuous; /* Whether this fragment is discontinuous or not */ + + GstFragmentPrivate *priv; +}; + +struct _GstFragmentClass +{ + GObjectClass parent_class; +}; + +GType gst_fragment_get_type (void); + +GstBufferList * gst_fragment_get_buffer_list (GstFragment *fragment); +gboolean gst_fragment_set_headers (GstFragment *fragment, GstBuffer **buffer, guint count); +gboolean gst_fragment_add_buffer (GstFragment *fragment, GstBuffer *buffer); +GstFragment * gst_fragment_new (void); + +G_END_DECLS +#endif /* __GSTFRAGMENT_H__ */