static void gst_buffer_pool_init (GstBufferPool * pool) { GstBufferPoolPrivate *priv; priv = pool->priv = GST_BUFFER_POOL_GET_PRIVATE (pool); g_rec_mutex_init (&priv->rec_lock); priv->poll = gst_poll_new_timer (); priv->queue = gst_atomic_queue_new (16); pool->flushing = 1; priv->active = FALSE; priv->configured = FALSE; priv->started = FALSE; priv->config = gst_structure_new_id_empty (GST_QUARK (BUFFER_POOL_CONFIG)); gst_buffer_pool_config_set_params (priv->config, NULL, 0, 0, 0); priv->allocator = NULL; gst_allocation_params_init (&priv->params); gst_buffer_pool_config_set_allocator (priv->config, priv->allocator, &priv->params); /* 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 - the wait token */ gst_poll_write_control (priv->poll); GST_DEBUG_OBJECT (pool, "created"); }
/* 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); gst_poll_write_control (priv->poll); if (pclass->flush_start) pclass->flush_start (pool); } else { if (pclass->flush_stop) pclass->flush_stop (pool); gst_poll_read_control (priv->poll); g_atomic_int_set (&pool->flushing, 0); } }
static void default_release_buffer (GstBufferPool * pool, GstBuffer * buffer) { GST_LOG_OBJECT (pool, "released buffer %p %d", buffer, GST_MINI_OBJECT_FLAGS (buffer)); /* memory should be untouched */ if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY)) goto discard; /* 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; /* all memory should be exclusive to this buffer (and thus be writable) */ if (!gst_buffer_is_all_memory_writable (buffer)) goto discard; /* keep it around in our queue */ gst_atomic_queue_push (pool->priv->queue, buffer); gst_poll_write_control (pool->priv->poll); return; discard: { do_free_buffer (pool, buffer); return; } }
static void default_release_buffer (GstBufferPool * pool, GstBuffer * buffer) { /* keep it around in our queue */ GST_LOG_OBJECT (pool, "released buffer %p", buffer); gst_atomic_queue_push (pool->priv->queue, buffer); gst_poll_write_control (pool->priv->poll); }
static GstFlowReturn default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) { GstFlowReturn result; GstBufferPoolPrivate *priv = pool->priv; while (TRUE) { if (G_UNLIKELY (GST_BUFFER_POOL_IS_FLUSHING (pool))) goto flushing; /* 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); result = GST_FLOW_OK; GST_LOG_OBJECT (pool, "acquired buffer %p", *buffer); break; } /* no buffer, try to allocate some more */ GST_LOG_OBJECT (pool, "no buffer, trying to allocate"); result = do_alloc_buffer (pool, buffer, NULL); if (G_LIKELY (result == GST_FLOW_OK)) /* we have a buffer, return it */ break; if (G_UNLIKELY (result != GST_FLOW_EOS)) /* something went wrong, return error */ break; /* check if we need to wait */ if (params && (params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT)) { GST_LOG_OBJECT (pool, "no more buffers"); break; } /* 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); } return result; /* ERRORS */ flushing: { GST_DEBUG_OBJECT (pool, "we are flushing"); return GST_FLOW_FLUSHING; } }
static void default_release_buffer (GstBufferPool * pool, GstBuffer * buffer) { GST_LOG_OBJECT (pool, "released buffer %p %d", buffer, GST_MINI_OBJECT_FLAGS (buffer)); /* memory should be untouched */ 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 (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 (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); gst_poll_write_control (pool->priv->poll); 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); return; } }
/* 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 * @active: the new active state * * Control the active state of @pool. When the pool is active, new calls to * gst_buffer_pool_acquire_buffer() will return with GST_FLOW_FLUSHING. * * Activating the bufferpool will preallocate all resources in the pool based on * the configuration of the pool. * * Deactivating will free the resources again when there are no outstanding * buffers. When there are outstanding buffers, they will be freed as soon as * they are all returned to the pool. * * Returns: %FALSE when the pool was not configured or when preallocation of the * buffers failed. */ gboolean gst_buffer_pool_set_active (GstBufferPool * pool, gboolean active) { gboolean res = TRUE; GstBufferPoolPrivate *priv; g_return_val_if_fail (GST_IS_BUFFER_POOL (pool), FALSE); GST_LOG_OBJECT (pool, "active %d", active); priv = pool->priv; GST_BUFFER_POOL_LOCK (pool); /* just return if we are already in the right state */ if (priv->active == active) goto was_ok; /* we need to be configured */ if (!priv->configured) goto not_configured; if (active) { if (!do_start (pool)) goto start_failed; /* unset the flushing state now */ gst_poll_read_control (priv->poll); g_atomic_int_set (&pool->flushing, 0); } else { gint outstanding; /* set to flushing first */ g_atomic_int_set (&pool->flushing, 1); gst_poll_write_control (priv->poll); /* when all buffers are in the pool, free them. Else they will be * freed when they are released */ outstanding = g_atomic_int_get (&priv->outstanding); GST_LOG_OBJECT (pool, "outstanding buffers %d", outstanding); if (outstanding == 0) { if (!do_stop (pool)) goto stop_failed; } } priv->active = active; GST_BUFFER_POOL_UNLOCK (pool); return res; was_ok: { GST_DEBUG_OBJECT (pool, "pool was in the right state"); GST_BUFFER_POOL_UNLOCK (pool); return TRUE; } not_configured: { GST_ERROR_OBJECT (pool, "pool was not configured"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } start_failed: { GST_ERROR_OBJECT (pool, "start failed"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } stop_failed: { GST_WARNING_OBJECT (pool, "stop failed"); GST_BUFFER_POOL_UNLOCK (pool); return FALSE; } }
/** * gst_bus_post: * @bus: a #GstBus to post on * @message: (transfer full): the #GstMessage to post * * Post a message on the given bus. Ownership of the message * is taken by the bus. * * Returns: %TRUE if the message could be posted, %FALSE if the bus is flushing. * * MT safe. */ gboolean gst_bus_post (GstBus * bus, GstMessage * message) { GstBusSyncReply reply = GST_BUS_PASS; GstBusSyncHandler handler; gboolean emit_sync_message; gpointer handler_data; g_return_val_if_fail (GST_IS_BUS (bus), FALSE); g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE); GST_DEBUG_OBJECT (bus, "[msg %p] posting on bus %" GST_PTR_FORMAT, message, message); /* check we didn't accidentally add a public flag that maps to same value */ g_assert (!GST_MINI_OBJECT_FLAG_IS_SET (message, GST_MESSAGE_FLAG_ASYNC_DELIVERY)); GST_OBJECT_LOCK (bus); /* check if the bus is flushing */ if (GST_OBJECT_FLAG_IS_SET (bus, GST_BUS_FLUSHING)) goto is_flushing; handler = bus->priv->sync_handler; handler_data = bus->priv->sync_handler_data; emit_sync_message = bus->priv->num_sync_message_emitters > 0; GST_OBJECT_UNLOCK (bus); /* first call the sync handler if it is installed */ if (handler) reply = handler (bus, message, handler_data); /* emit sync-message if requested to do so via gst_bus_enable_sync_message_emission. terrible but effective */ if (emit_sync_message && reply != GST_BUS_DROP && handler != gst_bus_sync_signal_handler) gst_bus_sync_signal_handler (bus, message, NULL); /* If this is a bus without async message delivery * always drop the message */ if (!bus->priv->poll) reply = GST_BUS_DROP; /* now see what we should do with the message */ switch (reply) { case GST_BUS_DROP: /* drop the message */ GST_DEBUG_OBJECT (bus, "[msg %p] dropped", message); break; case GST_BUS_PASS: /* pass the message to the async queue, refcount passed in the queue */ GST_DEBUG_OBJECT (bus, "[msg %p] pushing on async queue", message); gst_atomic_queue_push (bus->priv->queue, message); gst_poll_write_control (bus->priv->poll); GST_DEBUG_OBJECT (bus, "[msg %p] pushed on async queue", message); break; case GST_BUS_ASYNC: { /* async delivery, we need a mutex and a cond to block * on */ GCond *cond = GST_MESSAGE_GET_COND (message); GMutex *lock = GST_MESSAGE_GET_LOCK (message); g_cond_init (cond); g_mutex_init (lock); GST_MINI_OBJECT_FLAG_SET (message, GST_MESSAGE_FLAG_ASYNC_DELIVERY); GST_DEBUG_OBJECT (bus, "[msg %p] waiting for async delivery", message); /* now we lock the message mutex, send the message to the async * queue. When the message is handled by the app and destroyed, * the cond will be signalled and we can continue */ g_mutex_lock (lock); gst_atomic_queue_push (bus->priv->queue, message); gst_poll_write_control (bus->priv->poll); /* now block till the message is freed */ g_cond_wait (cond, lock); /* we acquired a new ref from gst_message_dispose() so we can clean up */ g_mutex_unlock (lock); GST_DEBUG_OBJECT (bus, "[msg %p] delivered asynchronously", message); GST_MINI_OBJECT_FLAG_UNSET (message, GST_MESSAGE_FLAG_ASYNC_DELIVERY); g_mutex_clear (lock); g_cond_clear (cond); gst_message_unref (message); break; } default: g_warning ("invalid return from bus sync handler"); break; } return TRUE; /* ERRORS */ is_flushing: { GST_DEBUG_OBJECT (bus, "bus is flushing"); GST_OBJECT_UNLOCK (bus); gst_message_unref (message); return FALSE; } }
static GstFlowReturn default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) { GstFlowReturn result; GstBufferPoolPrivate *priv = pool->priv; while (TRUE) { if (G_UNLIKELY (GST_BUFFER_POOL_IS_FLUSHING (pool))) goto flushing; /* try to get a buffer from the queue */ *buffer = gst_atomic_queue_pop (priv->queue); if (G_LIKELY (*buffer)) { 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; } /* no buffer, try to allocate some more */ GST_LOG_OBJECT (pool, "no buffer, trying to allocate"); result = do_alloc_buffer (pool, buffer, params); if (G_LIKELY (result == GST_FLOW_OK)) /* we have a buffer, return it */ break; if (G_UNLIKELY (result != GST_FLOW_EOS)) /* something went wrong, return error */ break; /* check if we need to wait */ if (params && (params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT)) { GST_LOG_OBJECT (pool, "no more buffers"); break; } /* now we release the control socket, we wait for a buffer release or * flushing */ 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; /* ERRORS */ flushing: { GST_DEBUG_OBJECT (pool, "we are flushing"); return GST_FLOW_FLUSHING; } }