X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstbufferpool.c;h=e5c7a5872ab7d0c81a7772b1c95d306714f792c0;hb=a143d9cb0caee85bbe0c5e8b86e9f05918153c9d;hp=fdc63882cc8d5046147a806f9dcd4a0a88b71e59;hpb=ec69ad4e8adddc83d4cdc8c5608099d74f7397d3;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstbufferpool.c b/gst/gstbufferpool.c index fdc6388..e5c7a58 100644 --- a/gst/gstbufferpool.c +++ b/gst/gstbufferpool.c @@ -21,6 +21,7 @@ /** * SECTION:gstbufferpool + * @title: GstBufferPool * @short_description: Pool for buffers * @see_also: #GstBuffer * @@ -61,8 +62,6 @@ * * Use gst_object_unref() to release the reference to a bufferpool. If the * refcount of the pool reaches 0, the pool will be freed. - * - * Last reviewed on 2014-01-30 (1.3.0) */ #include "gst_private.h" @@ -82,12 +81,15 @@ #include "gstbufferpool.h" +#ifdef G_OS_WIN32 +# ifndef EWOULDBLOCK +# define EWOULDBLOCK EAGAIN /* This is just to placate gcc */ +# endif +#endif /* G_OS_WIN32 */ + GST_DEBUG_CATEGORY_STATIC (gst_buffer_pool_debug); #define GST_CAT_DEFAULT gst_buffer_pool_debug -#define GST_BUFFER_POOL_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_BUFFER_POOL, GstBufferPoolPrivate)) - #define GST_BUFFER_POOL_LOCK(pool) (g_rec_mutex_lock(&pool->priv->rec_lock)) #define GST_BUFFER_POOL_UNLOCK(pool) (g_rec_mutex_unlock(&pool->priv->rec_lock)) @@ -115,7 +117,7 @@ struct _GstBufferPoolPrivate static void gst_buffer_pool_finalize (GObject * object); -G_DEFINE_TYPE (GstBufferPool, gst_buffer_pool, GST_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_PRIVATE (GstBufferPool, gst_buffer_pool, GST_TYPE_OBJECT); static gboolean default_start (GstBufferPool * pool); static gboolean default_stop (GstBufferPool * pool); @@ -134,8 +136,6 @@ gst_buffer_pool_class_init (GstBufferPoolClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; - g_type_class_add_private (klass, sizeof (GstBufferPoolPrivate)); - gobject_class->finalize = gst_buffer_pool_finalize; klass->start = default_start; @@ -156,7 +156,7 @@ gst_buffer_pool_init (GstBufferPool * pool) { GstBufferPoolPrivate *priv; - priv = pool->priv = GST_BUFFER_POOL_GET_PRIVATE (pool); + priv = pool->priv = gst_buffer_pool_get_instance_private (pool); g_rec_mutex_init (&priv->rec_lock); @@ -172,9 +172,9 @@ gst_buffer_pool_init (GstBufferPool * pool) gst_allocation_params_init (&priv->params); gst_buffer_pool_config_set_allocator (priv->config, priv->allocator, &priv->params); - /* 1 control write for flushing */ + /* 1 control write for flushing - the flush token */ gst_poll_write_control (priv->poll); - /* 1 control write for marking that we are not waiting for poll */ + /* 1 control write for marking that we are not waiting for poll - the wait token */ gst_poll_write_control (priv->poll); GST_DEBUG_OBJECT (pool, "created"); @@ -189,7 +189,7 @@ gst_buffer_pool_finalize (GObject * object) pool = GST_BUFFER_POOL_CAST (object); priv = pool->priv; - GST_DEBUG_OBJECT (pool, "finalize"); + GST_DEBUG_OBJECT (pool, "%p finalize", pool); gst_buffer_pool_set_active (pool, FALSE); gst_atomic_queue_unref (priv->queue); @@ -207,16 +207,19 @@ gst_buffer_pool_finalize (GObject * object) * * Creates a new #GstBufferPool instance. * - * Returns: (transfer floating): a new #GstBufferPool instance + * Returns: (transfer full): a new #GstBufferPool instance */ GstBufferPool * gst_buffer_pool_new (void) { GstBufferPool *result; - result = g_object_newv (GST_TYPE_BUFFER_POOL, 0, NULL); + result = g_object_new (GST_TYPE_BUFFER_POOL, NULL); GST_DEBUG_OBJECT (result, "created new buffer pool"); + /* Clear floating flag */ + gst_object_ref_sink (result); + return result; } @@ -229,16 +232,17 @@ default_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer, *buffer = gst_buffer_new_allocate (priv->allocator, priv->size, &priv->params); + if (!*buffer) + return GST_FLOW_ERROR; + return GST_FLOW_OK; } static gboolean mark_meta_pooled (GstBuffer * buffer, GstMeta ** meta, gpointer user_data) { - GstBufferPool *pool = user_data; - - GST_DEBUG_OBJECT (pool, "marking meta %p as POOLED in buffer %p", *meta, - buffer); + GST_DEBUG_OBJECT (GST_BUFFER_POOL (user_data), + "marking meta %p as POOLED in buffer %p", *meta, buffer); GST_META_FLAG_SET (*meta, GST_META_FLAG_POOLED); GST_META_FLAG_SET (*meta, GST_META_FLAG_LOCKED); @@ -280,7 +284,7 @@ do_alloc_buffer (GstBufferPool * pool, GstBuffer ** buffer, GST_BUFFER_FLAG_UNSET (*buffer, GST_BUFFER_FLAG_TAG_MEMORY); GST_LOG_OBJECT (pool, "allocated buffer %d/%d, %p", cur_buffers, - max_buffers, buffer); + max_buffers, *buffer); return result; @@ -359,7 +363,6 @@ do_start (GstBufferPool * pool) return TRUE; } - static void default_free_buffer (GstBufferPool * pool, GstBuffer * buffer) { @@ -392,7 +395,17 @@ default_stop (GstBufferPool * pool) /* clear the pool */ while ((buffer = gst_atomic_queue_pop (priv->queue))) { - gst_poll_read_control (priv->poll); + while (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* We put the buffer into the queue but did not finish writing control + * yet, let's wait a bit and retry */ + g_thread_yield (); + continue; + } else { + /* Critical error but GstPoll already complained */ + break; + } + } do_free_buffer (pool, buffer); } return priv->cur_buffers == 0; @@ -419,6 +432,46 @@ do_stop (GstBufferPool * pool) return TRUE; } +/* must be called with the lock */ +static void +do_set_flushing (GstBufferPool * pool, gboolean flushing) +{ + GstBufferPoolPrivate *priv = pool->priv; + GstBufferPoolClass *pclass; + + pclass = GST_BUFFER_POOL_GET_CLASS (pool); + + if (GST_BUFFER_POOL_IS_FLUSHING (pool) == flushing) + return; + + if (flushing) { + g_atomic_int_set (&pool->flushing, 1); + /* Write the flush token to wake up any waiters */ + gst_poll_write_control (priv->poll); + + if (pclass->flush_start) + pclass->flush_start (pool); + } else { + if (pclass->flush_stop) + pclass->flush_stop (pool); + + while (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* This should not really happen unless flushing and unflushing + * happens on different threads. Let's wait a bit to get back flush + * token from the thread that was setting it to flushing */ + g_thread_yield (); + continue; + } else { + /* Critical error but GstPoll already complained */ + break; + } + } + + g_atomic_int_set (&pool->flushing, 0); + } +} + /** * gst_buffer_pool_set_active: * @pool: a #GstBufferPool @@ -462,15 +515,17 @@ gst_buffer_pool_set_active (GstBufferPool * pool, gboolean active) if (!do_start (pool)) goto start_failed; + /* flush_stop my release buffers, setting to active to avoid running + * do_stop while activating the pool */ + priv->active = TRUE; + /* unset the flushing state now */ - gst_poll_read_control (priv->poll); - g_atomic_int_set (&pool->flushing, 0); + do_set_flushing (pool, FALSE); } else { gint outstanding; /* set to flushing first */ - g_atomic_int_set (&pool->flushing, 1); - gst_poll_write_control (priv->poll); + do_set_flushing (pool, TRUE); /* when all buffers are in the pool, free them. Else they will be * freed when they are released */ @@ -480,8 +535,9 @@ gst_buffer_pool_set_active (GstBufferPool * pool, gboolean active) if (!do_stop (pool)) goto stop_failed; } + + priv->active = FALSE; } - priv->active = active; GST_BUFFER_POOL_UNLOCK (pool); return res; @@ -577,9 +633,11 @@ wrong_config: * @pool: a #GstBufferPool * @config: (transfer full): a #GstStructure * - * Set the configuration of the pool. The pool must be inactive and all buffers - * allocated form this pool must be returned or else this function will do - * nothing and return FALSE. + * Set the configuration of the pool. If the pool is already configured, and + * the configuration haven't change, this function will return %TRUE. If the + * pool is active, this method will return %FALSE and active configuration + * will remain. Buffers allocated form this pool must be returned or else this + * function will do nothing and return %FALSE. * * @config is a #GstStructure that contains the configuration parameters for * the pool. A default and mandatory set of parameters can be configured with @@ -607,6 +665,11 @@ gst_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) priv = pool->priv; GST_BUFFER_POOL_LOCK (pool); + + /* nothing to do if config is unchanged */ + if (priv->configured && gst_structure_is_equal (config, priv->config)) + goto config_unchanged; + /* can't change the settings when active */ if (priv->active) goto was_active; @@ -623,25 +686,31 @@ gst_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config) else result = FALSE; - if (result) { - if (priv->config) - gst_structure_free (priv->config); - priv->config = config; + /* save the config regardless of the result so user can read back the + * modified config and evaluate if the changes are acceptable */ + if (priv->config) + gst_structure_free (priv->config); + priv->config = config; + if (result) { /* now we are configured */ priv->configured = TRUE; - } else { - gst_structure_free (config); } GST_BUFFER_POOL_UNLOCK (pool); return result; +config_unchanged: + { + gst_structure_free (config); + GST_BUFFER_POOL_UNLOCK (pool); + return TRUE; + } /* ERRORS */ was_active: { gst_structure_free (config); - GST_WARNING_OBJECT (pool, "can't change config, we are active"); + GST_INFO_OBJECT (pool, "can't change config, we are active"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } @@ -725,7 +794,7 @@ invalid_result: * * Check if the bufferpool supports @option. * - * Returns: a %NULL terminated array of strings. + * Returns: %TRUE if the buffer pool contains @option. */ gboolean gst_buffer_pool_has_option (GstBufferPool * pool, const gchar * option) @@ -773,8 +842,8 @@ gst_buffer_pool_config_set_params (GstStructure * config, GstCaps * caps, /** * gst_buffer_pool_config_set_allocator: * @config: a #GstBufferPool configuration - * @allocator: a #GstAllocator - * @params: #GstAllocationParams + * @allocator: (allow-none): a #GstAllocator + * @params: (allow-none): #GstAllocationParams * * Set the @allocator and @params on @config. * @@ -955,12 +1024,12 @@ gst_buffer_pool_config_get_params (GstStructure * config, GstCaps ** caps, /** * gst_buffer_pool_config_get_allocator: * @config: (transfer none): a #GstBufferPool configuration - * @allocator: (transfer none): a #GstAllocator - * @params: #GstAllocationParams + * @allocator: (out) (allow-none) (transfer none): a #GstAllocator, or %NULL + * @params: (out) (allow-none): #GstAllocationParams, or %NULL * * Get the @allocator and @params from @config. * - * Returns: %TRUE, if the values are set. + * Returns: %TRUE, if the values are set. */ gboolean gst_buffer_pool_config_get_allocator (GstStructure * config, @@ -985,6 +1054,46 @@ gst_buffer_pool_config_get_allocator (GstStructure * config, return TRUE; } +/** + * gst_buffer_pool_config_validate_params: + * @config: (transfer none): a #GstBufferPool configuration + * @caps: (transfer none): the excepted caps of buffers + * @size: the expected size of each buffer, not including prefix and padding + * @min_buffers: the expected minimum amount of buffers to allocate. + * @max_buffers: the expect maximum amount of buffers to allocate or 0 for unlimited. + * + * Validate that changes made to @config are still valid in the context of the + * expected parameters. This function is a helper that can be used to validate + * changes made by a pool to a config when gst_buffer_pool_set_config() + * returns %FALSE. This expects that @caps haven't changed and that + * @min_buffers aren't lower then what we initially expected. + * This does not check if options or allocator parameters are still valid, + * won't check if size have changed, since changing the size is valid to adapt + * padding. + * + * Since: 1.4 + * + * Returns: %TRUE, if the parameters are valid in this context. + */ +gboolean +gst_buffer_pool_config_validate_params (GstStructure * config, GstCaps * caps, + guint size, guint min_buffers, G_GNUC_UNUSED guint max_buffers) +{ + GstCaps *newcaps; + guint newsize, newmin; + gboolean ret = FALSE; + + g_return_val_if_fail (config != NULL, FALSE); + + gst_buffer_pool_config_get_params (config, &newcaps, &newsize, &newmin, NULL); + + if (gst_caps_is_equal (caps, newcaps) && (newsize >= size) + && (newmin >= min_buffers)) + ret = TRUE; + + return ret; +} + static GstFlowReturn default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) @@ -999,7 +1108,17 @@ default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, /* try to get a buffer from the queue */ *buffer = gst_atomic_queue_pop (priv->queue); if (G_LIKELY (*buffer)) { - gst_poll_read_control (priv->poll); + while (!gst_poll_read_control (priv->poll)) { + if (errno == EWOULDBLOCK) { + /* We put the buffer into the queue but did not finish writing control + * yet, let's wait a bit and retry */ + g_thread_yield (); + continue; + } else { + /* Critical error but GstPoll already complained */ + break; + } + } result = GST_FLOW_OK; GST_LOG_OBJECT (pool, "acquired buffer %p", *buffer); break; @@ -1007,7 +1126,7 @@ default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, /* no buffer, try to allocate some more */ GST_LOG_OBJECT (pool, "no buffer, trying to allocate"); - result = do_alloc_buffer (pool, buffer, NULL); + result = do_alloc_buffer (pool, buffer, params); if (G_LIKELY (result == GST_FLOW_OK)) /* we have a buffer, return it */ break; @@ -1024,10 +1143,33 @@ default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, /* now we release the control socket, we wait for a buffer release or * flushing */ - gst_poll_read_control (pool->priv->poll); - GST_LOG_OBJECT (pool, "waiting for free buffers or flushing"); - gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); - gst_poll_write_control (pool->priv->poll); + if (!gst_poll_read_control (pool->priv->poll)) { + if (errno == EWOULDBLOCK) { + /* This means that we have two threads trying to allocate buffers + * already, and the other one already got the wait token. This + * means that we only have to wait for the poll now and not write the + * token afterwards: we will be woken up once the other thread is + * woken up and that one will write the wait token it removed */ + GST_LOG_OBJECT (pool, "waiting for free buffers or flushing"); + gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); + } else { + /* This is a critical error, GstPoll already gave a warning */ + result = GST_FLOW_ERROR; + break; + } + } else { + /* We're the first thread waiting, we got the wait token and have to + * write it again later + * OR + * We're a second thread and just consumed the flush token and block all + * other threads, in which case we must not wait and give it back + * immediately */ + if (!GST_BUFFER_POOL_IS_FLUSHING (pool)) { + GST_LOG_OBJECT (pool, "waiting for free buffers or flushing"); + gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); + } + gst_poll_write_control (pool->priv->poll); + } } return result; @@ -1048,9 +1190,9 @@ dec_outstanding (GstBufferPool * pool) if (GST_BUFFER_POOL_IS_FLUSHING (pool)) { /* take the lock so that set_active is not run concurrently */ GST_BUFFER_POOL_LOCK (pool); - /* recheck the flushing state in the lock, the pool could have been - * set to active again */ - if (GST_BUFFER_POOL_IS_FLUSHING (pool)) + /* now that we have the lock, check if we have been de-activated with + * outstanding buffers */ + if (!pool->priv->active) do_stop (pool); GST_BUFFER_POOL_UNLOCK (pool); @@ -1079,6 +1221,10 @@ default_reset_buffer (GstBufferPool * pool, GstBuffer * buffer) GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE; + /* if the memory is intact reset the size to the full size */ + if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY)) + gst_buffer_resize (buffer, 0, pool->priv->size); + /* remove all metadata without the POOLED flag */ gst_buffer_foreach_meta (buffer, remove_meta_unpooled, pool); } @@ -1137,17 +1283,17 @@ default_release_buffer (GstBufferPool * pool, GstBuffer * buffer) GST_MINI_OBJECT_FLAGS (buffer)); /* memory should be untouched */ - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY)) - goto discard; + if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY))) + goto memory_tagged; /* size should have been reset. This is not a catch all, pool with * size requirement per memory should do their own check. */ - if (gst_buffer_get_size (buffer) != pool->priv->size) - goto discard; + if (G_UNLIKELY (gst_buffer_get_size (buffer) != pool->priv->size)) + goto size_changed; /* all memory should be exclusive to this buffer (and thus be writable) */ - if (!gst_buffer_is_all_memory_writable (buffer)) - goto discard; + if (G_UNLIKELY (!gst_buffer_is_all_memory_writable (buffer))) + goto not_writable; /* keep it around in our queue */ gst_atomic_queue_push (pool->priv->queue, buffer); @@ -1155,6 +1301,25 @@ default_release_buffer (GstBufferPool * pool, GstBuffer * buffer) return; +memory_tagged: + { + GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pool, + "discarding buffer %p: memory tag set", buffer); + goto discard; + } +size_changed: + { + GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pool, + "discarding buffer %p: size %" G_GSIZE_FORMAT " != %u", + buffer, gst_buffer_get_size (buffer), pool->priv->size); + goto discard; + } +not_writable: + { + GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, pool, + "discarding buffer %p: memory not writable", buffer); + goto discard; + } discard: { do_free_buffer (pool, buffer); @@ -1200,3 +1365,37 @@ gst_buffer_pool_release_buffer (GstBufferPool * pool, GstBuffer * buffer) /* decrease the refcount that the buffer had to us */ gst_object_unref (pool); } + +/** + * gst_buffer_pool_set_flushing: + * @pool: a #GstBufferPool + * @flushing: whether to start or stop flushing + * + * Enable or disable the flushing state of a @pool without freeing or + * allocating buffers. + * + * Since: 1.4 + */ +void +gst_buffer_pool_set_flushing (GstBufferPool * pool, gboolean flushing) +{ + GstBufferPoolPrivate *priv; + + g_return_if_fail (GST_IS_BUFFER_POOL (pool)); + + GST_LOG_OBJECT (pool, "flushing %d", flushing); + + priv = pool->priv; + + GST_BUFFER_POOL_LOCK (pool); + + if (!priv->active) { + GST_WARNING_OBJECT (pool, "can't change flushing state of inactive pool"); + goto done; + } + + do_set_flushing (pool, flushing); + +done: + GST_BUFFER_POOL_UNLOCK (pool); +}