diff --git a/ChangeLog b/ChangeLog index 2fcfd2c41f..3f79e9ecd2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2005-05-05 Wim Taymans + + * gst/base/Makefile.am: + * gst/base/gstbasesink.h: + * gst/base/gstbasesrc.c: (gst_basesrc_init), + (gst_basesrc_set_dataflow_funcs), (gst_basesrc_query): + * gst/base/gstcollectpads.c: (gst_collectpads_get_type), + (gst_collectpads_class_init), (gst_collectpads_init), + (gst_collectpads_finalize), (gst_collectpads_new), + (gst_collectpads_set_function), (gst_collectpads_add_pad), + (find_pad), (gst_collectpads_remove_pad), + (gst_collectpads_is_active), (gst_collectpads_collect), + (gst_collectpads_collect_range), (gst_collectpads_start), + (gst_collectpads_stop), (gst_collectpads_peek), + (gst_collectpads_pop), (gst_collectpads_available), + (gst_collectpads_read), (gst_collectpads_flush), + (gst_collectpads_chain): + * gst/base/gstcollectpads.h: + * gst/elements/Makefile.am: + * gst/elements/gstelements.c: + * gst/elements/gstfakesink.c: (gst_fakesink_class_init), + (gst_fakesink_get_times), (gst_fakesink_event), + (gst_fakesink_preroll), (gst_fakesink_render): + * gst/elements/gstfilesink.c: (gst_filesink_class_init), + (gst_filesink_init), (gst_filesink_set_location), + (gst_filesink_open_file), (gst_filesink_close_file), + (gst_filesink_pad_query), (gst_filesink_event), + (gst_filesink_render), (gst_filesink_change_state): + * gst/elements/gstfilesink.h: + Added object to help in making collect pad based elements. + Ported filesink. + Make event function in sink baseclass return gboolean. + 2005-05-05 Wim Taymans * gst/gstbin.c: (gst_bin_send_event), (compare_name), diff --git a/gst/base/Makefile.am b/gst/base/Makefile.am index c1c12ffac5..e1083536a6 100644 --- a/gst/base/Makefile.am +++ b/gst/base/Makefile.am @@ -6,6 +6,7 @@ libgstbase_@GST_MAJORMINOR@_la_SOURCES = \ gstbasesink.c \ gstbasesrc.c \ gstbasetransform.c \ + gstcollectpads.c \ gsttypefindhelper.c libgstbase_@GST_MAJORMINOR@_la_CFLAGS = $(GST_OBJ_CFLAGS) @@ -20,5 +21,6 @@ libgstbase_@GST_MAJORMINOR@include_HEADERS = \ gstbasesink.h \ gstbasesrc.h \ gstbasetransform.h \ + gstcollectpads.h \ gsttypefindhelper.h diff --git a/gst/base/gstbasesink.h b/gst/base/gstbasesink.h index b3eafade44..12c67c5a11 100644 --- a/gst/base/gstbasesink.h +++ b/gst/base/gstbasesink.h @@ -75,7 +75,7 @@ struct _GstBaseSinkClass { void (*get_times) (GstBaseSink *sink, GstBuffer *buffer, GstClockTime *start, GstClockTime *end); - void (*event) (GstBaseSink *sink, GstEvent *event); + gboolean (*event) (GstBaseSink *sink, GstEvent *event); GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer); GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer); }; diff --git a/gst/base/gstbasesrc.c b/gst/base/gstbasesrc.c index f4908ae5d7..9d83d6dad0 100644 --- a/gst/base/gstbasesrc.c +++ b/gst/base/gstbasesrc.c @@ -169,6 +169,7 @@ gst_basesrc_init (GstBaseSrc * basesrc, gpointer g_class) basesrc->srcpad = pad; gst_element_add_pad (GST_ELEMENT (basesrc), pad); + basesrc->random_access = TRUE; basesrc->segment_start = -1; basesrc->segment_end = -1; basesrc->blocksize = DEFAULT_BLOCKSIZE; @@ -179,6 +180,8 @@ gst_basesrc_init (GstBaseSrc * basesrc, gpointer g_class) static void gst_basesrc_set_dataflow_funcs (GstBaseSrc * this) { + GST_DEBUG ("updating dataflow functions"); + if (this->has_loop) gst_pad_set_loop_function (this->srcpad, gst_basesrc_loop); else @@ -232,7 +235,6 @@ gst_basesrc_query (GstPad * pad, GstQueryType type, { gboolean ret; - /* FIXME-wim: is this cast right? */ ret = gst_basesrc_get_size (src, (guint64 *) value); GST_DEBUG ("getting length %d %lld", ret, *value); return ret; diff --git a/gst/base/gstcollectpads.c b/gst/base/gstcollectpads.c new file mode 100644 index 0000000000..71965b8b25 --- /dev/null +++ b/gst/base/gstcollectpads.c @@ -0,0 +1,538 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstcollectpads.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 "gstcollectpads.h" + +static GstFlowReturn gst_collectpads_chain (GstPad * pad, GstBuffer * buffer); + +static void gst_collectpads_class_init (GstCollectPadsClass * klass); +static void gst_collectpads_init (GstCollectPads * pads); +static void gst_collectpads_finalize (GObject * object); + +static GstObjectClass *parent_class = NULL; + +GType +gst_collectpads_get_type (void) +{ + static GType collect_type = 0; + + if (!collect_type) { + static const GTypeInfo collect_info = { + sizeof (GstCollectPadsClass), + NULL, + NULL, + (GClassInitFunc) gst_collectpads_class_init, + NULL, + NULL, + sizeof (GstCollectPads), + 0, + (GInstanceInitFunc) gst_collectpads_init, + NULL + }; + + collect_type = g_type_register_static (GST_TYPE_OBJECT, "GstCollectPads", + &collect_info, 0); + } + return collect_type; +} + +static void +gst_collectpads_class_init (GstCollectPadsClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_collectpads_finalize); + + parent_class = g_type_class_ref (GST_TYPE_OBJECT); +} + +static void +gst_collectpads_init (GstCollectPads * pads) +{ + pads->cond = g_cond_new (); + pads->data = NULL; + pads->cookie = 0; + pads->numpads = 0; + pads->queuedpads = 0; + pads->started = FALSE; +} + +static void +gst_collectpads_finalize (GObject * object) +{ + GstCollectPads *pads = GST_COLLECTPADS (object); + + g_cond_free (pads->cond); + /* FIXME, free data */ + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/** + * gst_collectpads_new: + * + * Create a new instance of #GstCollectsPads. + * + * Returns: a new #GstCollectPads, or NULL in case of an error. + * + * MT safe. + */ +GstCollectPads * +gst_collectpads_new (void) +{ + GstCollectPads *newcoll; + + newcoll = g_object_new (GST_TYPE_COLLECTPADS, NULL); + + return newcoll; +} + +/** + * gst_collectpads_set_function: + * @pads: the collectspads to use + * @func: the function to set + * @user_data: user data passed to the function + * + * Set the callback function and user data that will be called when + * all the pads added to the collection have buffers queued. + * + * MT safe. + */ +void +gst_collectpads_set_function (GstCollectPads * pads, + GstCollectPadsFunction func, gpointer user_data) +{ + g_return_if_fail (pads != NULL); + + GST_LOCK (pads); + pads->func = func; + pads->user_data = user_data; + GST_UNLOCK (pads); +} + +/** + * gst_collectpads_add_pad: + * @pads: the collectspads to use + * @pad: the pad to add + * @size: the size of the returned GstCollectData structure + * + * Add a pad to the collection of collect pads. The pad has to be + * a sinkpad. + * + * You specify a size for the returned #GstCollectData structure + * so that you can use it to store additional information. + * + * Returns: a new #GstCollectData to identify the new pad. Or NULL + * if wrong parameters are supplied. + * + * MT safe. + */ +GstCollectData * +gst_collectpads_add_pad (GstCollectPads * pads, GstPad * pad, guint size) +{ + GstCollectData *data; + + g_return_val_if_fail (pads != NULL, NULL); + g_return_val_if_fail (pad != NULL, NULL); + g_return_val_if_fail (GST_PAD_IS_SINK (pad), NULL); + g_return_val_if_fail (size >= sizeof (GstCollectData), NULL); + + data = g_malloc0 (size); + data->collect = pads; + data->pad = pad; + data->buffer = NULL; + + GST_LOCK (pads); + pads->data = g_slist_append (pads->data, data); + gst_pad_set_chain_function (pad, gst_collectpads_chain); + gst_pad_set_element_private (pad, data); + pads->numpads++; + pads->cookie++; + GST_UNLOCK (pads); + + return data; +} + +static gint +find_pad (GstCollectData * data, GstPad * pad) +{ + if (data->pad == pad) + return 0; + return 1; +} + +/** + * gst_collectpads_remove_pad: + * @pads: the collectspads to use + * @pad: the pad to remove + * + * Remove a pad from the collection of collect pads. + * + * Returns: TRUE if the pad could be removed. + * + * MT safe. + */ +gboolean +gst_collectpads_remove_pad (GstCollectPads * pads, GstPad * pad) +{ + GSList *list; + + g_return_val_if_fail (pads != NULL, FALSE); + g_return_val_if_fail (pad != NULL, FALSE); + + GST_LOCK (pads); + list = g_slist_find_custom (pads->data, pad, (GCompareFunc) find_pad); + if (list) { + g_free (list->data); + pads->data = g_slist_delete_link (pads->data, list); + } + pads->numpads--; + pads->cookie++; + GST_UNLOCK (pads); + + return list != NULL; +} + +/** + * gst_collectpads_is_active: + * @pads: the collectspads to use + * @pad: the pad to check + * + * Check if a pad is active. + * + * Returns: TRUE if the pad is active. + * + * MT safe. + */ +gboolean +gst_collectpads_is_active (GstCollectPads * pads, GstPad * pad) +{ + g_return_val_if_fail (pads != NULL, FALSE); + g_return_val_if_fail (pad != NULL, FALSE); + + return FALSE; +} + +/** + * gst_collectpads_collect: + * @pads: the collectspads to use + * + * Collect data on all pads. This function is usually called + * from a GstTask function in an element. + * + * Returns: GstFlowReturn of the operation. + * + * MT safe. + */ +GstFlowReturn +gst_collectpads_collect (GstCollectPads * pads) +{ + g_return_val_if_fail (pads != NULL, GST_FLOW_ERROR); + + return GST_FLOW_ERROR; +} + +/** + * gst_collectpads_collect_range: + * @pads: the collectspads to use + * @offset: the offset to collect + * @length: the length to collect + * + * Collect data with @offset and @length on all pads. This function + * is typically called in the getrange function of an element. + * + * Returns: GstFlowReturn of the operation. + * + * MT safe. + */ +GstFlowReturn +gst_collectpads_collect_range (GstCollectPads * pads, guint64 offset, + guint length) +{ + g_return_val_if_fail (pads != NULL, GST_FLOW_ERROR); + + return GST_FLOW_ERROR; +} + +/** + * gst_collectpads_start: + * @pads: the collectspads to use + * + * Starts the processing of data in the collectpads. + * + * MT safe. + */ +void +gst_collectpads_start (GstCollectPads * pads) +{ + g_return_if_fail (pads != NULL); + + GST_LOCK (pads); + pads->started = TRUE; + GST_UNLOCK (pads); +} + +/** + * gst_collectpads_stop: + * @pads: the collectspads to use + * + * Stops the processing of data in the collectpads. this function + * will also unblock any blocking operations. + * + * MT safe. + */ +void +gst_collectpads_stop (GstCollectPads * pads) +{ + g_return_if_fail (pads != NULL); + + GST_LOCK (pads); + pads->started = FALSE; + GST_COLLECTPADS_SIGNAL (pads); + GST_UNLOCK (pads); +} + +/** + * gst_collectpads_peek: + * @pads: the collectspads to peek + * @data: the data to use + * + * Peek at the buffer currently queued in @data. This function + * should be called with the @pads LOCK held, such as in the callback + * handler. + * + * Returns: The buffer in @data or NULL if no buffer is queued. You + * should unref the buffer after usage. + * + * MT safe. + */ +GstBuffer * +gst_collectpads_peek (GstCollectPads * pads, GstCollectData * data) +{ + GstBuffer *result; + + result = data->buffer; + gst_buffer_ref (result); + + return result; +} + +/** + * gst_collectpads_pop: + * @pads: the collectspads to pop + * @data: the data to use + * + * Pop the buffer currently queued in @data. This function + * should be called with the @pads LOCK held, such as in the callback + * handler. + * + * Returns: The buffer in @data or NULL if no buffer was queued. The + * You should unref the buffer after usage. + * + * MT safe. + */ +GstBuffer * +gst_collectpads_pop (GstCollectPads * pads, GstCollectData * data) +{ + GstBuffer *result; + + result = data->buffer; + gst_buffer_replace (&data->buffer, NULL); + data->pos = 0; + pads->queuedpads--; + + GST_COLLECTPADS_SIGNAL (pads); + + return result; +} + +/** + * gst_collectpads_available: + * @pads: the collectspads to query + * + * Query how much bytes can be read from each queued buffer. This means + * that the result of this call is the maximum number of bytes that can + * be read from each of the pads. + * + * This function should be called with @pads LOCK held, such as + * in the callback. + * + * Returns: The maximum number of bytes queued on all pad. This function + * returns 0 if a pad has no queued buffer. + * + * MT safe. + */ +guint +gst_collectpads_available (GstCollectPads * pads) +{ + GSList *collected; + guint result = G_MAXUINT; + + for (collected = pads->data; collected; collected = g_slist_next (collected)) { + GstCollectData *pdata; + gint size; + + pdata = (GstCollectData *) collected->data; + + if (pdata->buffer == NULL) + goto not_filled; + + size = GST_BUFFER_SIZE (pdata->buffer) - pdata->pos; + + if (size < result) + result = size; + } + return result; + +not_filled: + { + return 0; + } +} + +/** + * gst_collectpads_read: + * @pads: the collectspads to query + * @data: the data to use + * @bytes: a pointer to a byte array + * @size: the number of bytes to read + * + * Get a pointer in @bytes where @size bytes can be read from the + * given pad data. + * + * This function should be called with @pads LOCK held, such as + * in the callback. + * + * Returns: The number of bytes available for consumption in the + * memory pointed to by @bytes. This can be less than @size and + * is 0 if the pad is end-of-stream. + * + * MT safe. + */ +guint +gst_collectpads_read (GstCollectPads * pads, GstCollectData * data, + guint8 ** bytes, guint size) +{ + guint readsize; + + readsize = MIN (size, GST_BUFFER_SIZE (data->buffer) - data->pos); + + *bytes = GST_BUFFER_DATA (data->buffer) + data->pos; + + return readsize; +} + +/** + * gst_collectpads_flush: + * @pads: the collectspads to query + * @data: the data to use + * @size: the number of bytes to flush + * + * Flush @size bytes from the pad @data. + * + * This function should be called with @pads LOCK held, such as + * in the callback. + * + * Returns: The number of bytes flushed This can be less than @size and + * is 0 if the pad was end-of-stream. + * + * MT safe. + */ +guint +gst_collectpads_flush (GstCollectPads * pads, GstCollectData * data, guint size) +{ + guint flushsize; + + flushsize = MIN (size, GST_BUFFER_SIZE (data->buffer) - data->pos); + + data->pos += size; + + if (data->pos >= GST_BUFFER_SIZE (data->buffer)) { + GstBuffer *buf; + + buf = gst_collectpads_pop (pads, data); + gst_buffer_unref (buf); + } + + return flushsize; +} + +static GstFlowReturn +gst_collectpads_chain (GstPad * pad, GstBuffer * buffer) +{ + GstCollectData *data; + GstCollectPads *pads; + guint64 size; + GstFlowReturn ret; + + GST_DEBUG ("chain"); + + /* some magic to get the managing collectpads */ + data = (GstCollectData *) gst_pad_get_element_private (pad); + if (data == NULL) + goto not_ours; + + pads = data->collect; + size = GST_BUFFER_SIZE (buffer); + + GST_LOCK (pads); + + /* if not started, bail out */ + if (!pads->started) + goto not_started; + + /* queue buffer on this pad, block if filled */ + while (data->buffer != NULL) { + GST_COLLECTPADS_WAIT (pads); + /* after a signal, we could be stopped */ + if (!pads->started) + goto not_started; + } + pads->queuedpads++; + gst_buffer_replace (&data->buffer, buffer); + + /* if all pads have data and we have a function, call it */ + if ((pads->queuedpads == pads->numpads) && pads->func) { + ret = pads->func (pads, pads->user_data); + } else { + ret = GST_FLOW_OK; + } + GST_UNLOCK (pads); + + return ret; + + /* ERRORS */ +not_ours: + { + GST_DEBUG ("collectpads not ours"); + return GST_FLOW_ERROR; + } +not_started: + { + GST_UNLOCK (pads); + GST_DEBUG ("collectpads not started"); + return GST_FLOW_WRONG_STATE; + } +} diff --git a/gst/base/gstcollectpads.h b/gst/base/gstcollectpads.h new file mode 100644 index 0000000000..3ae8c66b61 --- /dev/null +++ b/gst/base/gstcollectpads.h @@ -0,0 +1,132 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstcollectpads.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 __GST_COLLECTPADS_H__ +#define __GST_COLLECTPADS_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_COLLECTPADS (gst_collectpads_get_type()) +#define GST_COLLECTPADS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_COLLECTPADS,GstCollectPads)) +#define GST_COLLECTPADS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_COLLECTPADS,GstCollectPadsClass)) +#define GST_COLLECTPADS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_COLLECTPADS, GstCollectPadsClass)) +#define GST_IS_COLLECTPADS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_COLLECTPADS)) +#define GST_IS_COLLECTPADS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_COLLECTPADS)) + +/* manages a set of pads that operate in collect mode. This means + * that control is given to the manager of this object when all + * pads have data. + * + * - pads are added to the collection with add/remove_pad. The pad + * has to be a sinkpad. The chain function of the pad is + * overridden. The element_private of the pad is used to store + * private information. + * - For each pad, data is queued in the chain function or by + * performing a pull_range. + * - When data is queued on all pads, a callback function is + * called. + * - Data can be dequeued from the pad with the _pop() method. + * One can _peek() at the data with the peek function. + * - Data can also be dequeued with the available/read/flush + * calls. + */ + +typedef struct _GstCollectPads GstCollectPads; +typedef struct _GstCollectPadsClass GstCollectPadsClass; + +typedef struct _GstCollectData +{ + GstCollectPads *collect; + GstPad *pad; + GstBuffer *buffer; + guint pos; + gboolean eos; +} GstCollectData; + +/* function will be called when all pads have data */ +typedef GstFlowReturn (*GstCollectPadsFunction) (GstCollectPads *pads, gpointer user_data); + +#define GST_COLLECTPADS_GET_COND(pads) (((GstCollectPads *)pads)->cond) +#define GST_COLLECTPADS_WAIT(pads) (g_cond_wait (GST_COLLECTPADS_GET_COND (pads), GST_GET_LOCK (pads))) +#define GST_COLLECTPADS_SIGNAL(pads) (g_cond_signal (GST_COLLECTPADS_GET_COND (pads))) +#define GST_COLLECTPADS_BROADCAST(pads)(g_cond_broadcast (GST_COLLECTPADS_GET_COND (pads))) + +struct _GstCollectPads { + GstObject object; + + /*< public >*/ /* with LOCK */ + GSList *data; /* GstCollectData in this collection */ + guint32 cookie; + + GCond *cond; /* to signal removal of data */ + + /*< private >*/ + GstCollectPadsFunction func; /* function and user_data for callback */ + gpointer user_data; + + guint numpads; /* number of pads */ + guint queuedpads; /* number of pads with a buffer */ + + gboolean started; +}; + +struct _GstCollectPadsClass { + GstObjectClass parent_class; + +}; + +GType gst_collectpads_get_type(void); + +/* creating the object */ +GstCollectPads* gst_collectpads_new (void); + +/* set the callback */ +void gst_collectpads_set_function (GstCollectPads *pads, GstCollectPadsFunction func, + gpointer user_data); + +/* pad management */ +GstCollectData* gst_collectpads_add_pad (GstCollectPads *pads, GstPad *pad, guint size); +gboolean gst_collectpads_remove_pad (GstCollectPads *pads, GstPad *pad); +gboolean gst_collectpads_is_active (GstCollectPads *pads, GstPad *pad); + +/* start/stop collection */ +GstFlowReturn gst_collectpads_collect (GstCollectPads *pads); +GstFlowReturn gst_collectpads_collect_range (GstCollectPads *pads, guint64 offset, guint length); + +void gst_collectpads_start (GstCollectPads *pads); +void gst_collectpads_stop (GstCollectPads *pads); + +/* get collected buffers */ +GstBuffer* gst_collectpads_peek (GstCollectPads *pads, GstCollectData *data); +GstBuffer* gst_collectpads_pop (GstCollectPads *pads, GstCollectData *data); + +/* get collected bytes */ +guint gst_collectpads_available (GstCollectPads *pads); +guint gst_collectpads_read (GstCollectPads *pads, GstCollectData *data, + guint8 **bytes, guint size); +guint gst_collectpads_flush (GstCollectPads *pads, GstCollectData *data, + guint size); + +G_END_DECLS + +#endif /* __GST_COLLECTPADS_H__ */ diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index 2d631c75ab..561aed437c 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -15,6 +15,7 @@ libgstelements_la_SOURCES = \ gstcapsfilter.c \ gstfakesrc.c \ gstfakesink.c \ + gstfilesink.c \ gstfilesrc.c \ gstidentity.c \ gstelements.c \ @@ -27,7 +28,6 @@ libgstelements_la_SOURCES = \ EXTRA_DIST = \ gstaggregator.c \ gstcapsfilter.c \ - gstfilesink.c \ gstfdsink.c \ gstfdsrc.c \ gstmd5sink.c \ diff --git a/gst/elements/gstelements.c b/gst/elements/gstelements.c index d5b228b93f..7427256c78 100644 --- a/gst/elements/gstelements.c +++ b/gst/elements/gstelements.c @@ -64,7 +64,7 @@ static struct _elements_entry _elements[] = { {"identity", GST_RANK_NONE, gst_identity_get_type}, // {"fdsink", GST_RANK_NONE, gst_fdsink_get_type}, // {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, -// {"filesink", GST_RANK_NONE, gst_filesink_get_type}, + {"filesink", GST_RANK_NONE, gst_filesink_get_type}, // {"md5sink", GST_RANK_NONE, gst_md5sink_get_type}, #ifndef HAVE_WIN32 // {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type}, diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index 30a8fdb2b5..cbb7dfc338 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -116,7 +116,7 @@ static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink, GstBuffer * buffer); static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buffer); -static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event); +static gboolean gst_fakesink_event (GstBaseSink * bsink, GstEvent * event); static void gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end); @@ -269,7 +269,7 @@ gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, } } -static void +static gboolean gst_fakesink_event (GstBaseSink * bsink, GstEvent * event) { GstFakeSink *sink = GST_FAKESINK (bsink); @@ -283,6 +283,8 @@ gst_fakesink_event (GstBaseSink * bsink, GstEvent * event) g_object_notify (G_OBJECT (sink), "last_message"); } + + return TRUE; } static GstFlowReturn diff --git a/gst/elements/gstfilesink.c b/gst/elements/gstfilesink.c index 1386758a3c..162f649dda 100644 --- a/gst/elements/gstfilesink.c +++ b/gst/elements/gstfilesink.c @@ -99,17 +99,19 @@ static void gst_filesink_get_property (GObject * object, guint prop_id, static gboolean gst_filesink_open_file (GstFileSink * sink); static void gst_filesink_close_file (GstFileSink * sink); -static gboolean gst_filesink_handle_event (GstPad * pad, GstEvent * event); +static gboolean gst_filesink_event (GstBaseSink * sink, GstEvent * event); +static GstFlowReturn gst_filesink_render (GstBaseSink * sink, + GstBuffer * buffer); + static gboolean gst_filesink_pad_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value); -static void gst_filesink_chain (GstPad * pad, GstData * _data); static void gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data); static GstElementStateReturn gst_filesink_change_state (GstElement * element); -static guint gst_filesink_signals[LAST_SIGNAL] = { 0 }; +//static guint gst_filesink_signals[LAST_SIGNAL] = { 0 }; static void _do_init (GType filesink_type) @@ -126,10 +128,9 @@ _do_init (GType filesink_type) "filesink element"); } -GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstElement, GST_TYPE_ELEMENT, +GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstBaseSink, GST_TYPE_BASESINK, _do_init); - static void gst_filesink_base_init (gpointer g_class) { @@ -144,6 +145,7 @@ static void gst_filesink_class_init (GstFileSinkClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSinkClass *gstbasesink_class = GST_BASESINK_CLASS (klass); gobject_class->set_property = gst_filesink_set_property; gobject_class->get_property = gst_filesink_get_property; @@ -152,25 +154,17 @@ gst_filesink_class_init (GstFileSinkClass * klass) g_param_spec_string ("location", "File Location", "Location of the file to write", NULL, G_PARAM_READWRITE)); - gst_filesink_signals[SIGNAL_HANDOFF] = - g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - gobject_class->dispose = gst_filesink_dispose; + + gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_filesink_render); + gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_filesink_event); } static void gst_filesink_init (GstFileSink * filesink) { GstPad *pad; - pad = - gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), - "sink"); - gst_element_add_pad (GST_ELEMENT (filesink), pad); - gst_pad_set_chain_function (pad, gst_filesink_chain); - - GST_FLAG_SET (GST_ELEMENT (filesink), GST_ELEMENT_EVENT_AWARE); + pad = GST_BASESINK_PAD (filesink); gst_pad_set_query_function (pad, gst_filesink_pad_query); gst_pad_set_query_type_function (pad, gst_filesink_get_query_types); @@ -196,10 +190,7 @@ static gboolean gst_filesink_set_location (GstFileSink * sink, const gchar * location) { /* the element must be stopped or paused in order to do this */ - if (GST_STATE (sink) > GST_STATE_PAUSED) - return FALSE; - if (GST_STATE (sink) == GST_STATE_PAUSED && - GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)) + if (GST_STATE (sink) >= GST_STATE_PAUSED) return FALSE; g_free (sink->filename); @@ -212,9 +203,6 @@ gst_filesink_set_location (GstFileSink * sink, const gchar * location) sink->uri = NULL; } - if (GST_STATE (sink) == GST_STATE_PAUSED) - gst_filesink_open_file (sink); - return TRUE; } static void @@ -260,8 +248,6 @@ gst_filesink_get_property (GObject * object, guint prop_id, GValue * value, static gboolean gst_filesink_open_file (GstFileSink * sink) { - g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE); - /* open the file */ if (sink->filename == NULL || sink->filename[0] == '\0') { GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, @@ -277,8 +263,6 @@ gst_filesink_open_file (GstFileSink * sink) return FALSE; } - GST_FLAG_SET (sink, GST_FILESINK_OPEN); - sink->data_written = 0; return TRUE; @@ -287,13 +271,9 @@ gst_filesink_open_file (GstFileSink * sink) static void gst_filesink_close_file (GstFileSink * sink) { - g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)); - if (fclose (sink->file) != 0) { GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM); - } else { - GST_FLAG_UNSET (sink, GST_FILESINK_OPEN); } } @@ -307,11 +287,9 @@ gst_filesink_pad_query (GstPad * pad, GstQueryType type, case GST_QUERY_TOTAL: switch (*format) { case GST_FORMAT_BYTES: - if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) { - *value = sink->data_written; /* FIXME - doesn't the kernel provide - such a function? */ - break; - } + *value = sink->data_written; /* FIXME - doesn't the kernel provide + such a function? */ + break; default: return FALSE; } @@ -319,10 +297,8 @@ gst_filesink_pad_query (GstPad * pad, GstQueryType type, case GST_QUERY_POSITION: switch (*format) { case GST_FORMAT_BYTES: - if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) { - *value = ftell (sink->file); - break; - } + *value = ftell (sink->file); + break; default: return FALSE; } @@ -336,30 +312,23 @@ gst_filesink_pad_query (GstPad * pad, GstQueryType type, /* handle events (search) */ static gboolean -gst_filesink_handle_event (GstPad * pad, GstEvent * event) +gst_filesink_event (GstBaseSink * sink, GstEvent * event) { GstEventType type; GstFileSink *filesink; - filesink = GST_FILESINK (gst_pad_get_parent (pad)); - - if (!(GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN))) { - gst_event_unref (event); - return FALSE; - } + filesink = GST_FILESINK (sink); type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; switch (type) { case GST_EVENT_SEEK: if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) { - gst_event_unref (event); return FALSE; } if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { if (fflush (filesink->file)) { - gst_event_unref (event); GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, (_("Error while writing to file \"%s\"."), filesink->filename), GST_ERROR_SYSTEM); @@ -381,33 +350,24 @@ gst_filesink_handle_event (GstPad * pad, GstEvent * event) g_warning ("unknown seek method!"); break; } - gst_event_unref (event); break; case GST_EVENT_DISCONTINUOUS: { - gint64 offset; + gint64 soffset, eoffset; - if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset)) - fseek (filesink->file, offset, SEEK_SET); - - gst_event_unref (event); + if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &soffset, + &eoffset)) + fseek (filesink->file, soffset, SEEK_SET); break; } case GST_EVENT_FLUSH: if (fflush (filesink->file)) { - gst_event_unref (event); GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, (_("Error while writing to file \"%s\"."), filesink->filename), GST_ERROR_SYSTEM); } break; - case GST_EVENT_EOS: - gst_event_unref (event); - gst_filesink_close_file (filesink); - gst_element_set_eos (GST_ELEMENT (filesink)); - break; default: - gst_pad_event_default (pad, event); break; } @@ -421,75 +381,80 @@ gst_filesink_handle_event (GstPad * pad, GstEvent * event) * * take the buffer from the pad and write to file if it's open */ -static void -gst_filesink_chain (GstPad * pad, GstData * _data) +static GstFlowReturn +gst_filesink_render (GstBaseSink * sink, GstBuffer * buffer) { - GstBuffer *buf = GST_BUFFER (_data); GstFileSink *filesink; + guint bytes_written = 0, back_pending = 0; + guint size; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); + size = GST_BUFFER_SIZE (buffer); - filesink = GST_FILESINK (gst_pad_get_parent (pad)); + filesink = GST_FILESINK (sink); - if (GST_IS_EVENT (buf)) { - gst_filesink_handle_event (pad, GST_EVENT (buf)); - return; - } + if (ftell (filesink->file) < filesink->data_written) + back_pending = filesink->data_written - ftell (filesink->file); - if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN)) { - guint bytes_written = 0, back_pending = 0; + while (bytes_written < size) { + size_t wrote = fwrite (GST_BUFFER_DATA (buffer) + bytes_written, 1, + size - bytes_written, filesink->file); - if (ftell (filesink->file) < filesink->data_written) - back_pending = filesink->data_written - ftell (filesink->file); - while (bytes_written < GST_BUFFER_SIZE (buf)) { - size_t wrote = fwrite (GST_BUFFER_DATA (buf) + bytes_written, 1, - GST_BUFFER_SIZE (buf) - bytes_written, - filesink->file); - - if (wrote <= 0) { - GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, - (_("Error while writing to file \"%s\"."), filesink->filename), - ("Only %d of %d bytes written: %s", - bytes_written, GST_BUFFER_SIZE (buf), strerror (errno))); - break; - } - bytes_written += wrote; + if (wrote <= 0) { + GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, + (_("Error while writing to file \"%s\"."), filesink->filename), + ("Only %d of %d bytes written: %s", + bytes_written, size, strerror (errno))); + break; } - - filesink->data_written += bytes_written - back_pending; + bytes_written += wrote; } - gst_buffer_unref (buf); + filesink->data_written += bytes_written - back_pending; - g_signal_emit (G_OBJECT (filesink), - gst_filesink_signals[SIGNAL_HANDOFF], 0, filesink); + return GST_FLOW_OK; } static GstElementStateReturn gst_filesink_change_state (GstElement * element) { - g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE); + GstElementStateReturn ret; + gint transition; - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_PAUSED_TO_READY: - if (GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) - gst_filesink_close_file (GST_FILESINK (element)); + transition = GST_STATE_TRANSITION (element); + + switch (transition) { + case GST_STATE_NULL_TO_READY: break; - case GST_STATE_READY_TO_PAUSED: - if (!GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) { - if (!gst_filesink_open_file (GST_FILESINK (element))) - return GST_STATE_FAILURE; - } + if (!gst_filesink_open_file (GST_FILESINK (element))) + goto open_error; + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + default: break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - return GST_STATE_SUCCESS; + switch (transition) { + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_filesink_close_file (GST_FILESINK (element)); + break; + case GST_STATE_READY_TO_NULL: + break; + default: + break; + } + + return ret; + +open_error: + { + return GST_STATE_FAILURE; + } } /*** GSTURIHANDLER INTERFACE *************************************************/ diff --git a/gst/elements/gstfilesink.h b/gst/elements/gstfilesink.h index bd85a1d012..54090efea6 100644 --- a/gst/elements/gstfilesink.h +++ b/gst/elements/gstfilesink.h @@ -25,10 +25,10 @@ #define __GST_FILESINK_H__ #include +#include G_BEGIN_DECLS - #define GST_TYPE_FILESINK \ (gst_filesink_get_type()) #define GST_FILESINK(obj) \ @@ -43,14 +43,8 @@ G_BEGIN_DECLS typedef struct _GstFileSink GstFileSink; typedef struct _GstFileSinkClass GstFileSinkClass; -typedef enum { - GST_FILESINK_OPEN = GST_ELEMENT_FLAG_LAST, - - GST_FILESINK_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2 -} GstFileSinkFlags; - struct _GstFileSink { - GstElement element; + GstBaseSink parent; gchar *filename; gchar *uri; @@ -60,10 +54,7 @@ struct _GstFileSink { }; struct _GstFileSinkClass { - GstElementClass parent_class; - - /* signals */ - void (*handoff) (GstElement *element, GstPad *pad); + GstBaseSinkClass parent_class; }; GType gst_filesink_get_type(void); diff --git a/libs/gst/base/Makefile.am b/libs/gst/base/Makefile.am index c1c12ffac5..e1083536a6 100644 --- a/libs/gst/base/Makefile.am +++ b/libs/gst/base/Makefile.am @@ -6,6 +6,7 @@ libgstbase_@GST_MAJORMINOR@_la_SOURCES = \ gstbasesink.c \ gstbasesrc.c \ gstbasetransform.c \ + gstcollectpads.c \ gsttypefindhelper.c libgstbase_@GST_MAJORMINOR@_la_CFLAGS = $(GST_OBJ_CFLAGS) @@ -20,5 +21,6 @@ libgstbase_@GST_MAJORMINOR@include_HEADERS = \ gstbasesink.h \ gstbasesrc.h \ gstbasetransform.h \ + gstcollectpads.h \ gsttypefindhelper.h diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h index b3eafade44..12c67c5a11 100644 --- a/libs/gst/base/gstbasesink.h +++ b/libs/gst/base/gstbasesink.h @@ -75,7 +75,7 @@ struct _GstBaseSinkClass { void (*get_times) (GstBaseSink *sink, GstBuffer *buffer, GstClockTime *start, GstClockTime *end); - void (*event) (GstBaseSink *sink, GstEvent *event); + gboolean (*event) (GstBaseSink *sink, GstEvent *event); GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer); GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer); }; diff --git a/libs/gst/base/gstbasesrc.c b/libs/gst/base/gstbasesrc.c index f4908ae5d7..9d83d6dad0 100644 --- a/libs/gst/base/gstbasesrc.c +++ b/libs/gst/base/gstbasesrc.c @@ -169,6 +169,7 @@ gst_basesrc_init (GstBaseSrc * basesrc, gpointer g_class) basesrc->srcpad = pad; gst_element_add_pad (GST_ELEMENT (basesrc), pad); + basesrc->random_access = TRUE; basesrc->segment_start = -1; basesrc->segment_end = -1; basesrc->blocksize = DEFAULT_BLOCKSIZE; @@ -179,6 +180,8 @@ gst_basesrc_init (GstBaseSrc * basesrc, gpointer g_class) static void gst_basesrc_set_dataflow_funcs (GstBaseSrc * this) { + GST_DEBUG ("updating dataflow functions"); + if (this->has_loop) gst_pad_set_loop_function (this->srcpad, gst_basesrc_loop); else @@ -232,7 +235,6 @@ gst_basesrc_query (GstPad * pad, GstQueryType type, { gboolean ret; - /* FIXME-wim: is this cast right? */ ret = gst_basesrc_get_size (src, (guint64 *) value); GST_DEBUG ("getting length %d %lld", ret, *value); return ret; diff --git a/libs/gst/base/gstcollectpads.c b/libs/gst/base/gstcollectpads.c new file mode 100644 index 0000000000..71965b8b25 --- /dev/null +++ b/libs/gst/base/gstcollectpads.c @@ -0,0 +1,538 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstcollectpads.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 "gstcollectpads.h" + +static GstFlowReturn gst_collectpads_chain (GstPad * pad, GstBuffer * buffer); + +static void gst_collectpads_class_init (GstCollectPadsClass * klass); +static void gst_collectpads_init (GstCollectPads * pads); +static void gst_collectpads_finalize (GObject * object); + +static GstObjectClass *parent_class = NULL; + +GType +gst_collectpads_get_type (void) +{ + static GType collect_type = 0; + + if (!collect_type) { + static const GTypeInfo collect_info = { + sizeof (GstCollectPadsClass), + NULL, + NULL, + (GClassInitFunc) gst_collectpads_class_init, + NULL, + NULL, + sizeof (GstCollectPads), + 0, + (GInstanceInitFunc) gst_collectpads_init, + NULL + }; + + collect_type = g_type_register_static (GST_TYPE_OBJECT, "GstCollectPads", + &collect_info, 0); + } + return collect_type; +} + +static void +gst_collectpads_class_init (GstCollectPadsClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_collectpads_finalize); + + parent_class = g_type_class_ref (GST_TYPE_OBJECT); +} + +static void +gst_collectpads_init (GstCollectPads * pads) +{ + pads->cond = g_cond_new (); + pads->data = NULL; + pads->cookie = 0; + pads->numpads = 0; + pads->queuedpads = 0; + pads->started = FALSE; +} + +static void +gst_collectpads_finalize (GObject * object) +{ + GstCollectPads *pads = GST_COLLECTPADS (object); + + g_cond_free (pads->cond); + /* FIXME, free data */ + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/** + * gst_collectpads_new: + * + * Create a new instance of #GstCollectsPads. + * + * Returns: a new #GstCollectPads, or NULL in case of an error. + * + * MT safe. + */ +GstCollectPads * +gst_collectpads_new (void) +{ + GstCollectPads *newcoll; + + newcoll = g_object_new (GST_TYPE_COLLECTPADS, NULL); + + return newcoll; +} + +/** + * gst_collectpads_set_function: + * @pads: the collectspads to use + * @func: the function to set + * @user_data: user data passed to the function + * + * Set the callback function and user data that will be called when + * all the pads added to the collection have buffers queued. + * + * MT safe. + */ +void +gst_collectpads_set_function (GstCollectPads * pads, + GstCollectPadsFunction func, gpointer user_data) +{ + g_return_if_fail (pads != NULL); + + GST_LOCK (pads); + pads->func = func; + pads->user_data = user_data; + GST_UNLOCK (pads); +} + +/** + * gst_collectpads_add_pad: + * @pads: the collectspads to use + * @pad: the pad to add + * @size: the size of the returned GstCollectData structure + * + * Add a pad to the collection of collect pads. The pad has to be + * a sinkpad. + * + * You specify a size for the returned #GstCollectData structure + * so that you can use it to store additional information. + * + * Returns: a new #GstCollectData to identify the new pad. Or NULL + * if wrong parameters are supplied. + * + * MT safe. + */ +GstCollectData * +gst_collectpads_add_pad (GstCollectPads * pads, GstPad * pad, guint size) +{ + GstCollectData *data; + + g_return_val_if_fail (pads != NULL, NULL); + g_return_val_if_fail (pad != NULL, NULL); + g_return_val_if_fail (GST_PAD_IS_SINK (pad), NULL); + g_return_val_if_fail (size >= sizeof (GstCollectData), NULL); + + data = g_malloc0 (size); + data->collect = pads; + data->pad = pad; + data->buffer = NULL; + + GST_LOCK (pads); + pads->data = g_slist_append (pads->data, data); + gst_pad_set_chain_function (pad, gst_collectpads_chain); + gst_pad_set_element_private (pad, data); + pads->numpads++; + pads->cookie++; + GST_UNLOCK (pads); + + return data; +} + +static gint +find_pad (GstCollectData * data, GstPad * pad) +{ + if (data->pad == pad) + return 0; + return 1; +} + +/** + * gst_collectpads_remove_pad: + * @pads: the collectspads to use + * @pad: the pad to remove + * + * Remove a pad from the collection of collect pads. + * + * Returns: TRUE if the pad could be removed. + * + * MT safe. + */ +gboolean +gst_collectpads_remove_pad (GstCollectPads * pads, GstPad * pad) +{ + GSList *list; + + g_return_val_if_fail (pads != NULL, FALSE); + g_return_val_if_fail (pad != NULL, FALSE); + + GST_LOCK (pads); + list = g_slist_find_custom (pads->data, pad, (GCompareFunc) find_pad); + if (list) { + g_free (list->data); + pads->data = g_slist_delete_link (pads->data, list); + } + pads->numpads--; + pads->cookie++; + GST_UNLOCK (pads); + + return list != NULL; +} + +/** + * gst_collectpads_is_active: + * @pads: the collectspads to use + * @pad: the pad to check + * + * Check if a pad is active. + * + * Returns: TRUE if the pad is active. + * + * MT safe. + */ +gboolean +gst_collectpads_is_active (GstCollectPads * pads, GstPad * pad) +{ + g_return_val_if_fail (pads != NULL, FALSE); + g_return_val_if_fail (pad != NULL, FALSE); + + return FALSE; +} + +/** + * gst_collectpads_collect: + * @pads: the collectspads to use + * + * Collect data on all pads. This function is usually called + * from a GstTask function in an element. + * + * Returns: GstFlowReturn of the operation. + * + * MT safe. + */ +GstFlowReturn +gst_collectpads_collect (GstCollectPads * pads) +{ + g_return_val_if_fail (pads != NULL, GST_FLOW_ERROR); + + return GST_FLOW_ERROR; +} + +/** + * gst_collectpads_collect_range: + * @pads: the collectspads to use + * @offset: the offset to collect + * @length: the length to collect + * + * Collect data with @offset and @length on all pads. This function + * is typically called in the getrange function of an element. + * + * Returns: GstFlowReturn of the operation. + * + * MT safe. + */ +GstFlowReturn +gst_collectpads_collect_range (GstCollectPads * pads, guint64 offset, + guint length) +{ + g_return_val_if_fail (pads != NULL, GST_FLOW_ERROR); + + return GST_FLOW_ERROR; +} + +/** + * gst_collectpads_start: + * @pads: the collectspads to use + * + * Starts the processing of data in the collectpads. + * + * MT safe. + */ +void +gst_collectpads_start (GstCollectPads * pads) +{ + g_return_if_fail (pads != NULL); + + GST_LOCK (pads); + pads->started = TRUE; + GST_UNLOCK (pads); +} + +/** + * gst_collectpads_stop: + * @pads: the collectspads to use + * + * Stops the processing of data in the collectpads. this function + * will also unblock any blocking operations. + * + * MT safe. + */ +void +gst_collectpads_stop (GstCollectPads * pads) +{ + g_return_if_fail (pads != NULL); + + GST_LOCK (pads); + pads->started = FALSE; + GST_COLLECTPADS_SIGNAL (pads); + GST_UNLOCK (pads); +} + +/** + * gst_collectpads_peek: + * @pads: the collectspads to peek + * @data: the data to use + * + * Peek at the buffer currently queued in @data. This function + * should be called with the @pads LOCK held, such as in the callback + * handler. + * + * Returns: The buffer in @data or NULL if no buffer is queued. You + * should unref the buffer after usage. + * + * MT safe. + */ +GstBuffer * +gst_collectpads_peek (GstCollectPads * pads, GstCollectData * data) +{ + GstBuffer *result; + + result = data->buffer; + gst_buffer_ref (result); + + return result; +} + +/** + * gst_collectpads_pop: + * @pads: the collectspads to pop + * @data: the data to use + * + * Pop the buffer currently queued in @data. This function + * should be called with the @pads LOCK held, such as in the callback + * handler. + * + * Returns: The buffer in @data or NULL if no buffer was queued. The + * You should unref the buffer after usage. + * + * MT safe. + */ +GstBuffer * +gst_collectpads_pop (GstCollectPads * pads, GstCollectData * data) +{ + GstBuffer *result; + + result = data->buffer; + gst_buffer_replace (&data->buffer, NULL); + data->pos = 0; + pads->queuedpads--; + + GST_COLLECTPADS_SIGNAL (pads); + + return result; +} + +/** + * gst_collectpads_available: + * @pads: the collectspads to query + * + * Query how much bytes can be read from each queued buffer. This means + * that the result of this call is the maximum number of bytes that can + * be read from each of the pads. + * + * This function should be called with @pads LOCK held, such as + * in the callback. + * + * Returns: The maximum number of bytes queued on all pad. This function + * returns 0 if a pad has no queued buffer. + * + * MT safe. + */ +guint +gst_collectpads_available (GstCollectPads * pads) +{ + GSList *collected; + guint result = G_MAXUINT; + + for (collected = pads->data; collected; collected = g_slist_next (collected)) { + GstCollectData *pdata; + gint size; + + pdata = (GstCollectData *) collected->data; + + if (pdata->buffer == NULL) + goto not_filled; + + size = GST_BUFFER_SIZE (pdata->buffer) - pdata->pos; + + if (size < result) + result = size; + } + return result; + +not_filled: + { + return 0; + } +} + +/** + * gst_collectpads_read: + * @pads: the collectspads to query + * @data: the data to use + * @bytes: a pointer to a byte array + * @size: the number of bytes to read + * + * Get a pointer in @bytes where @size bytes can be read from the + * given pad data. + * + * This function should be called with @pads LOCK held, such as + * in the callback. + * + * Returns: The number of bytes available for consumption in the + * memory pointed to by @bytes. This can be less than @size and + * is 0 if the pad is end-of-stream. + * + * MT safe. + */ +guint +gst_collectpads_read (GstCollectPads * pads, GstCollectData * data, + guint8 ** bytes, guint size) +{ + guint readsize; + + readsize = MIN (size, GST_BUFFER_SIZE (data->buffer) - data->pos); + + *bytes = GST_BUFFER_DATA (data->buffer) + data->pos; + + return readsize; +} + +/** + * gst_collectpads_flush: + * @pads: the collectspads to query + * @data: the data to use + * @size: the number of bytes to flush + * + * Flush @size bytes from the pad @data. + * + * This function should be called with @pads LOCK held, such as + * in the callback. + * + * Returns: The number of bytes flushed This can be less than @size and + * is 0 if the pad was end-of-stream. + * + * MT safe. + */ +guint +gst_collectpads_flush (GstCollectPads * pads, GstCollectData * data, guint size) +{ + guint flushsize; + + flushsize = MIN (size, GST_BUFFER_SIZE (data->buffer) - data->pos); + + data->pos += size; + + if (data->pos >= GST_BUFFER_SIZE (data->buffer)) { + GstBuffer *buf; + + buf = gst_collectpads_pop (pads, data); + gst_buffer_unref (buf); + } + + return flushsize; +} + +static GstFlowReturn +gst_collectpads_chain (GstPad * pad, GstBuffer * buffer) +{ + GstCollectData *data; + GstCollectPads *pads; + guint64 size; + GstFlowReturn ret; + + GST_DEBUG ("chain"); + + /* some magic to get the managing collectpads */ + data = (GstCollectData *) gst_pad_get_element_private (pad); + if (data == NULL) + goto not_ours; + + pads = data->collect; + size = GST_BUFFER_SIZE (buffer); + + GST_LOCK (pads); + + /* if not started, bail out */ + if (!pads->started) + goto not_started; + + /* queue buffer on this pad, block if filled */ + while (data->buffer != NULL) { + GST_COLLECTPADS_WAIT (pads); + /* after a signal, we could be stopped */ + if (!pads->started) + goto not_started; + } + pads->queuedpads++; + gst_buffer_replace (&data->buffer, buffer); + + /* if all pads have data and we have a function, call it */ + if ((pads->queuedpads == pads->numpads) && pads->func) { + ret = pads->func (pads, pads->user_data); + } else { + ret = GST_FLOW_OK; + } + GST_UNLOCK (pads); + + return ret; + + /* ERRORS */ +not_ours: + { + GST_DEBUG ("collectpads not ours"); + return GST_FLOW_ERROR; + } +not_started: + { + GST_UNLOCK (pads); + GST_DEBUG ("collectpads not started"); + return GST_FLOW_WRONG_STATE; + } +} diff --git a/libs/gst/base/gstcollectpads.h b/libs/gst/base/gstcollectpads.h new file mode 100644 index 0000000000..3ae8c66b61 --- /dev/null +++ b/libs/gst/base/gstcollectpads.h @@ -0,0 +1,132 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstcollectpads.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 __GST_COLLECTPADS_H__ +#define __GST_COLLECTPADS_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_COLLECTPADS (gst_collectpads_get_type()) +#define GST_COLLECTPADS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_COLLECTPADS,GstCollectPads)) +#define GST_COLLECTPADS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_COLLECTPADS,GstCollectPadsClass)) +#define GST_COLLECTPADS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_COLLECTPADS, GstCollectPadsClass)) +#define GST_IS_COLLECTPADS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_COLLECTPADS)) +#define GST_IS_COLLECTPADS_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_COLLECTPADS)) + +/* manages a set of pads that operate in collect mode. This means + * that control is given to the manager of this object when all + * pads have data. + * + * - pads are added to the collection with add/remove_pad. The pad + * has to be a sinkpad. The chain function of the pad is + * overridden. The element_private of the pad is used to store + * private information. + * - For each pad, data is queued in the chain function or by + * performing a pull_range. + * - When data is queued on all pads, a callback function is + * called. + * - Data can be dequeued from the pad with the _pop() method. + * One can _peek() at the data with the peek function. + * - Data can also be dequeued with the available/read/flush + * calls. + */ + +typedef struct _GstCollectPads GstCollectPads; +typedef struct _GstCollectPadsClass GstCollectPadsClass; + +typedef struct _GstCollectData +{ + GstCollectPads *collect; + GstPad *pad; + GstBuffer *buffer; + guint pos; + gboolean eos; +} GstCollectData; + +/* function will be called when all pads have data */ +typedef GstFlowReturn (*GstCollectPadsFunction) (GstCollectPads *pads, gpointer user_data); + +#define GST_COLLECTPADS_GET_COND(pads) (((GstCollectPads *)pads)->cond) +#define GST_COLLECTPADS_WAIT(pads) (g_cond_wait (GST_COLLECTPADS_GET_COND (pads), GST_GET_LOCK (pads))) +#define GST_COLLECTPADS_SIGNAL(pads) (g_cond_signal (GST_COLLECTPADS_GET_COND (pads))) +#define GST_COLLECTPADS_BROADCAST(pads)(g_cond_broadcast (GST_COLLECTPADS_GET_COND (pads))) + +struct _GstCollectPads { + GstObject object; + + /*< public >*/ /* with LOCK */ + GSList *data; /* GstCollectData in this collection */ + guint32 cookie; + + GCond *cond; /* to signal removal of data */ + + /*< private >*/ + GstCollectPadsFunction func; /* function and user_data for callback */ + gpointer user_data; + + guint numpads; /* number of pads */ + guint queuedpads; /* number of pads with a buffer */ + + gboolean started; +}; + +struct _GstCollectPadsClass { + GstObjectClass parent_class; + +}; + +GType gst_collectpads_get_type(void); + +/* creating the object */ +GstCollectPads* gst_collectpads_new (void); + +/* set the callback */ +void gst_collectpads_set_function (GstCollectPads *pads, GstCollectPadsFunction func, + gpointer user_data); + +/* pad management */ +GstCollectData* gst_collectpads_add_pad (GstCollectPads *pads, GstPad *pad, guint size); +gboolean gst_collectpads_remove_pad (GstCollectPads *pads, GstPad *pad); +gboolean gst_collectpads_is_active (GstCollectPads *pads, GstPad *pad); + +/* start/stop collection */ +GstFlowReturn gst_collectpads_collect (GstCollectPads *pads); +GstFlowReturn gst_collectpads_collect_range (GstCollectPads *pads, guint64 offset, guint length); + +void gst_collectpads_start (GstCollectPads *pads); +void gst_collectpads_stop (GstCollectPads *pads); + +/* get collected buffers */ +GstBuffer* gst_collectpads_peek (GstCollectPads *pads, GstCollectData *data); +GstBuffer* gst_collectpads_pop (GstCollectPads *pads, GstCollectData *data); + +/* get collected bytes */ +guint gst_collectpads_available (GstCollectPads *pads); +guint gst_collectpads_read (GstCollectPads *pads, GstCollectData *data, + guint8 **bytes, guint size); +guint gst_collectpads_flush (GstCollectPads *pads, GstCollectData *data, + guint size); + +G_END_DECLS + +#endif /* __GST_COLLECTPADS_H__ */ diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index 2d631c75ab..561aed437c 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -15,6 +15,7 @@ libgstelements_la_SOURCES = \ gstcapsfilter.c \ gstfakesrc.c \ gstfakesink.c \ + gstfilesink.c \ gstfilesrc.c \ gstidentity.c \ gstelements.c \ @@ -27,7 +28,6 @@ libgstelements_la_SOURCES = \ EXTRA_DIST = \ gstaggregator.c \ gstcapsfilter.c \ - gstfilesink.c \ gstfdsink.c \ gstfdsrc.c \ gstmd5sink.c \ diff --git a/plugins/elements/gstelements.c b/plugins/elements/gstelements.c index d5b228b93f..7427256c78 100644 --- a/plugins/elements/gstelements.c +++ b/plugins/elements/gstelements.c @@ -64,7 +64,7 @@ static struct _elements_entry _elements[] = { {"identity", GST_RANK_NONE, gst_identity_get_type}, // {"fdsink", GST_RANK_NONE, gst_fdsink_get_type}, // {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, -// {"filesink", GST_RANK_NONE, gst_filesink_get_type}, + {"filesink", GST_RANK_NONE, gst_filesink_get_type}, // {"md5sink", GST_RANK_NONE, gst_md5sink_get_type}, #ifndef HAVE_WIN32 // {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type}, diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index 30a8fdb2b5..cbb7dfc338 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -116,7 +116,7 @@ static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink, GstBuffer * buffer); static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buffer); -static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event); +static gboolean gst_fakesink_event (GstBaseSink * bsink, GstEvent * event); static void gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, GstClockTime * start, GstClockTime * end); @@ -269,7 +269,7 @@ gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer, } } -static void +static gboolean gst_fakesink_event (GstBaseSink * bsink, GstEvent * event) { GstFakeSink *sink = GST_FAKESINK (bsink); @@ -283,6 +283,8 @@ gst_fakesink_event (GstBaseSink * bsink, GstEvent * event) g_object_notify (G_OBJECT (sink), "last_message"); } + + return TRUE; } static GstFlowReturn diff --git a/plugins/elements/gstfilesink.c b/plugins/elements/gstfilesink.c index 1386758a3c..162f649dda 100644 --- a/plugins/elements/gstfilesink.c +++ b/plugins/elements/gstfilesink.c @@ -99,17 +99,19 @@ static void gst_filesink_get_property (GObject * object, guint prop_id, static gboolean gst_filesink_open_file (GstFileSink * sink); static void gst_filesink_close_file (GstFileSink * sink); -static gboolean gst_filesink_handle_event (GstPad * pad, GstEvent * event); +static gboolean gst_filesink_event (GstBaseSink * sink, GstEvent * event); +static GstFlowReturn gst_filesink_render (GstBaseSink * sink, + GstBuffer * buffer); + static gboolean gst_filesink_pad_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value); -static void gst_filesink_chain (GstPad * pad, GstData * _data); static void gst_filesink_uri_handler_init (gpointer g_iface, gpointer iface_data); static GstElementStateReturn gst_filesink_change_state (GstElement * element); -static guint gst_filesink_signals[LAST_SIGNAL] = { 0 }; +//static guint gst_filesink_signals[LAST_SIGNAL] = { 0 }; static void _do_init (GType filesink_type) @@ -126,10 +128,9 @@ _do_init (GType filesink_type) "filesink element"); } -GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstElement, GST_TYPE_ELEMENT, +GST_BOILERPLATE_FULL (GstFileSink, gst_filesink, GstBaseSink, GST_TYPE_BASESINK, _do_init); - static void gst_filesink_base_init (gpointer g_class) { @@ -144,6 +145,7 @@ static void gst_filesink_class_init (GstFileSinkClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSinkClass *gstbasesink_class = GST_BASESINK_CLASS (klass); gobject_class->set_property = gst_filesink_set_property; gobject_class->get_property = gst_filesink_get_property; @@ -152,25 +154,17 @@ gst_filesink_class_init (GstFileSinkClass * klass) g_param_spec_string ("location", "File Location", "Location of the file to write", NULL, G_PARAM_READWRITE)); - gst_filesink_signals[SIGNAL_HANDOFF] = - g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstFileSinkClass, handoff), NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - gobject_class->dispose = gst_filesink_dispose; + + gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_filesink_render); + gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_filesink_event); } static void gst_filesink_init (GstFileSink * filesink) { GstPad *pad; - pad = - gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), - "sink"); - gst_element_add_pad (GST_ELEMENT (filesink), pad); - gst_pad_set_chain_function (pad, gst_filesink_chain); - - GST_FLAG_SET (GST_ELEMENT (filesink), GST_ELEMENT_EVENT_AWARE); + pad = GST_BASESINK_PAD (filesink); gst_pad_set_query_function (pad, gst_filesink_pad_query); gst_pad_set_query_type_function (pad, gst_filesink_get_query_types); @@ -196,10 +190,7 @@ static gboolean gst_filesink_set_location (GstFileSink * sink, const gchar * location) { /* the element must be stopped or paused in order to do this */ - if (GST_STATE (sink) > GST_STATE_PAUSED) - return FALSE; - if (GST_STATE (sink) == GST_STATE_PAUSED && - GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)) + if (GST_STATE (sink) >= GST_STATE_PAUSED) return FALSE; g_free (sink->filename); @@ -212,9 +203,6 @@ gst_filesink_set_location (GstFileSink * sink, const gchar * location) sink->uri = NULL; } - if (GST_STATE (sink) == GST_STATE_PAUSED) - gst_filesink_open_file (sink); - return TRUE; } static void @@ -260,8 +248,6 @@ gst_filesink_get_property (GObject * object, guint prop_id, GValue * value, static gboolean gst_filesink_open_file (GstFileSink * sink) { - g_return_val_if_fail (!GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN), FALSE); - /* open the file */ if (sink->filename == NULL || sink->filename[0] == '\0') { GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, @@ -277,8 +263,6 @@ gst_filesink_open_file (GstFileSink * sink) return FALSE; } - GST_FLAG_SET (sink, GST_FILESINK_OPEN); - sink->data_written = 0; return TRUE; @@ -287,13 +271,9 @@ gst_filesink_open_file (GstFileSink * sink) static void gst_filesink_close_file (GstFileSink * sink) { - g_return_if_fail (GST_FLAG_IS_SET (sink, GST_FILESINK_OPEN)); - if (fclose (sink->file) != 0) { GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE, (_("Error closing file \"%s\"."), sink->filename), GST_ERROR_SYSTEM); - } else { - GST_FLAG_UNSET (sink, GST_FILESINK_OPEN); } } @@ -307,11 +287,9 @@ gst_filesink_pad_query (GstPad * pad, GstQueryType type, case GST_QUERY_TOTAL: switch (*format) { case GST_FORMAT_BYTES: - if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) { - *value = sink->data_written; /* FIXME - doesn't the kernel provide - such a function? */ - break; - } + *value = sink->data_written; /* FIXME - doesn't the kernel provide + such a function? */ + break; default: return FALSE; } @@ -319,10 +297,8 @@ gst_filesink_pad_query (GstPad * pad, GstQueryType type, case GST_QUERY_POSITION: switch (*format) { case GST_FORMAT_BYTES: - if (GST_FLAG_IS_SET (GST_ELEMENT (sink), GST_FILESINK_OPEN)) { - *value = ftell (sink->file); - break; - } + *value = ftell (sink->file); + break; default: return FALSE; } @@ -336,30 +312,23 @@ gst_filesink_pad_query (GstPad * pad, GstQueryType type, /* handle events (search) */ static gboolean -gst_filesink_handle_event (GstPad * pad, GstEvent * event) +gst_filesink_event (GstBaseSink * sink, GstEvent * event) { GstEventType type; GstFileSink *filesink; - filesink = GST_FILESINK (gst_pad_get_parent (pad)); - - if (!(GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN))) { - gst_event_unref (event); - return FALSE; - } + filesink = GST_FILESINK (sink); type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN; switch (type) { case GST_EVENT_SEEK: if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_BYTES) { - gst_event_unref (event); return FALSE; } if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { if (fflush (filesink->file)) { - gst_event_unref (event); GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, (_("Error while writing to file \"%s\"."), filesink->filename), GST_ERROR_SYSTEM); @@ -381,33 +350,24 @@ gst_filesink_handle_event (GstPad * pad, GstEvent * event) g_warning ("unknown seek method!"); break; } - gst_event_unref (event); break; case GST_EVENT_DISCONTINUOUS: { - gint64 offset; + gint64 soffset, eoffset; - if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &offset)) - fseek (filesink->file, offset, SEEK_SET); - - gst_event_unref (event); + if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &soffset, + &eoffset)) + fseek (filesink->file, soffset, SEEK_SET); break; } case GST_EVENT_FLUSH: if (fflush (filesink->file)) { - gst_event_unref (event); GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, (_("Error while writing to file \"%s\"."), filesink->filename), GST_ERROR_SYSTEM); } break; - case GST_EVENT_EOS: - gst_event_unref (event); - gst_filesink_close_file (filesink); - gst_element_set_eos (GST_ELEMENT (filesink)); - break; default: - gst_pad_event_default (pad, event); break; } @@ -421,75 +381,80 @@ gst_filesink_handle_event (GstPad * pad, GstEvent * event) * * take the buffer from the pad and write to file if it's open */ -static void -gst_filesink_chain (GstPad * pad, GstData * _data) +static GstFlowReturn +gst_filesink_render (GstBaseSink * sink, GstBuffer * buffer) { - GstBuffer *buf = GST_BUFFER (_data); GstFileSink *filesink; + guint bytes_written = 0, back_pending = 0; + guint size; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); + size = GST_BUFFER_SIZE (buffer); - filesink = GST_FILESINK (gst_pad_get_parent (pad)); + filesink = GST_FILESINK (sink); - if (GST_IS_EVENT (buf)) { - gst_filesink_handle_event (pad, GST_EVENT (buf)); - return; - } + if (ftell (filesink->file) < filesink->data_written) + back_pending = filesink->data_written - ftell (filesink->file); - if (GST_FLAG_IS_SET (filesink, GST_FILESINK_OPEN)) { - guint bytes_written = 0, back_pending = 0; + while (bytes_written < size) { + size_t wrote = fwrite (GST_BUFFER_DATA (buffer) + bytes_written, 1, + size - bytes_written, filesink->file); - if (ftell (filesink->file) < filesink->data_written) - back_pending = filesink->data_written - ftell (filesink->file); - while (bytes_written < GST_BUFFER_SIZE (buf)) { - size_t wrote = fwrite (GST_BUFFER_DATA (buf) + bytes_written, 1, - GST_BUFFER_SIZE (buf) - bytes_written, - filesink->file); - - if (wrote <= 0) { - GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, - (_("Error while writing to file \"%s\"."), filesink->filename), - ("Only %d of %d bytes written: %s", - bytes_written, GST_BUFFER_SIZE (buf), strerror (errno))); - break; - } - bytes_written += wrote; + if (wrote <= 0) { + GST_ELEMENT_ERROR (filesink, RESOURCE, WRITE, + (_("Error while writing to file \"%s\"."), filesink->filename), + ("Only %d of %d bytes written: %s", + bytes_written, size, strerror (errno))); + break; } - - filesink->data_written += bytes_written - back_pending; + bytes_written += wrote; } - gst_buffer_unref (buf); + filesink->data_written += bytes_written - back_pending; - g_signal_emit (G_OBJECT (filesink), - gst_filesink_signals[SIGNAL_HANDOFF], 0, filesink); + return GST_FLOW_OK; } static GstElementStateReturn gst_filesink_change_state (GstElement * element) { - g_return_val_if_fail (GST_IS_FILESINK (element), GST_STATE_FAILURE); + GstElementStateReturn ret; + gint transition; - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_PAUSED_TO_READY: - if (GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) - gst_filesink_close_file (GST_FILESINK (element)); + transition = GST_STATE_TRANSITION (element); + + switch (transition) { + case GST_STATE_NULL_TO_READY: break; - case GST_STATE_READY_TO_PAUSED: - if (!GST_FLAG_IS_SET (element, GST_FILESINK_OPEN)) { - if (!gst_filesink_open_file (GST_FILESINK (element))) - return GST_STATE_FAILURE; - } + if (!gst_filesink_open_file (GST_FILESINK (element))) + goto open_error; + break; + case GST_STATE_PAUSED_TO_PLAYING: + break; + default: break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - return GST_STATE_SUCCESS; + switch (transition) { + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_filesink_close_file (GST_FILESINK (element)); + break; + case GST_STATE_READY_TO_NULL: + break; + default: + break; + } + + return ret; + +open_error: + { + return GST_STATE_FAILURE; + } } /*** GSTURIHANDLER INTERFACE *************************************************/ diff --git a/plugins/elements/gstfilesink.h b/plugins/elements/gstfilesink.h index bd85a1d012..54090efea6 100644 --- a/plugins/elements/gstfilesink.h +++ b/plugins/elements/gstfilesink.h @@ -25,10 +25,10 @@ #define __GST_FILESINK_H__ #include +#include G_BEGIN_DECLS - #define GST_TYPE_FILESINK \ (gst_filesink_get_type()) #define GST_FILESINK(obj) \ @@ -43,14 +43,8 @@ G_BEGIN_DECLS typedef struct _GstFileSink GstFileSink; typedef struct _GstFileSinkClass GstFileSinkClass; -typedef enum { - GST_FILESINK_OPEN = GST_ELEMENT_FLAG_LAST, - - GST_FILESINK_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2 -} GstFileSinkFlags; - struct _GstFileSink { - GstElement element; + GstBaseSink parent; gchar *filename; gchar *uri; @@ -60,10 +54,7 @@ struct _GstFileSink { }; struct _GstFileSinkClass { - GstElementClass parent_class; - - /* signals */ - void (*handoff) (GstElement *element, GstPad *pad); + GstBaseSinkClass parent_class; }; GType gst_filesink_get_type(void);