/* 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 inline void dec_outstanding (GstBufferPool * pool) { if (g_atomic_int_dec_and_test (&pool->priv->outstanding)) { /* all buffers are returned to the pool, see if we need to free them */ 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)) do_stop (pool); GST_BUFFER_POOL_UNLOCK (pool); } } }
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 inline void dec_outstanding (GstBufferPool * pool) { if (g_atomic_int_dec_and_test (&pool->priv->outstanding)) { /* all buffers are returned to the pool, see if we need to free them */ if (GST_BUFFER_POOL_IS_FLUSHING (pool)) { /* take the lock so that set_active is not run concurrently */ GST_BUFFER_POOL_LOCK (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); } } }
static GstFlowReturn gst_imx_v4l2_buffer_pool_acquire_buffer(GstBufferPool *bpool, GstBuffer **buffer, G_GNUC_UNUSED GstBufferPoolAcquireParams *params) { GstImxV4l2BufferPool *pool = GST_IMX_V4L2_BUFFER_POOL(bpool); struct v4l2_buffer vbuffer; GstBuffer *buf; GstImxV4l2Meta *meta; if (GST_BUFFER_POOL_IS_FLUSHING(bpool)) return GST_FLOW_FLUSHING; memset(&vbuffer, 0, sizeof(vbuffer)); vbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vbuffer.memory = V4L2_MEMORY_MMAP; if (ioctl(GST_IMX_FD_OBJECT_GET_FD(pool->fd_obj_v4l), VIDIOC_DQBUF, &vbuffer) < 0) { GST_ERROR_OBJECT(pool, "VIDIOC_DQBUF failed: %s", g_strerror(errno)); return GST_FLOW_ERROR; } buf = pool->buffers[vbuffer.index]; GST_DEBUG_OBJECT(pool, "dqbuf %u %p", vbuffer.index, (gpointer)buf); pool->buffers[vbuffer.index] = NULL; g_assert(buf); meta = GST_IMX_V4L2_META_GET(buf); g_assert(meta); gst_buffer_remove_all_memory(buf); gst_buffer_append_memory(buf, gst_memory_new_wrapped(0, meta->mem, meta->vbuffer.length, 0, vbuffer.bytesused, NULL, NULL)); GST_BUFFER_TIMESTAMP(buf) = GST_TIMEVAL_TO_TIME(vbuffer.timestamp); *buffer = buf; return GST_FLOW_OK; }
/* 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); } }
static GstFlowReturn gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) { GstFlowReturn ret; GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "acquire"); if (GST_BUFFER_POOL_IS_FLUSHING (bpool)) goto flushing; switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: /* capture, This function should return a buffer with new captured data */ switch (obj->mode) { case GST_V4L2_IO_RW: /* take empty buffer from the pool */ ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, buffer, params); break; case GST_V4L2_IO_MMAP: /* just dequeue a buffer, we basically use the queue of v4l2 as the * storage for our buffers. This function does poll first so we can * interrupt it fine. */ ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer); if (G_UNLIKELY (ret != GST_FLOW_OK)) goto done; /* start copying buffers when we are running low on buffers */ if (pool->num_queued < pool->copy_threshold) { GstBuffer *copy; /* copy the memory */ copy = gst_buffer_copy (*buffer); GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buffer, copy); /* and requeue so that we can continue capturing */ ret = gst_v4l2_buffer_pool_qbuf (pool, *buffer); *buffer = copy; } break; case GST_V4L2_IO_USERPTR: default: g_assert_not_reached (); break; } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: /* playback, This function should return an empty buffer */ switch (obj->mode) { case GST_V4L2_IO_RW: /* get an empty buffer */ ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, buffer, params); break; case GST_V4L2_IO_MMAP: /* get a free unqueued buffer */ ret = GST_BUFFER_POOL_CLASS (parent_class)->acquire_buffer (bpool, buffer, params); break; case GST_V4L2_IO_USERPTR: default: g_assert_not_reached (); break; } break; default: g_assert_not_reached (); break; } done: return ret; /* ERRORS */ flushing: { GST_DEBUG_OBJECT (pool, "We are flushing"); return GST_FLOW_FLUSHING; } }
/** * gst_v4l2_buffer_pool_process: * @bpool: a #GstBufferPool * @buf: a #GstBuffer, maybe be replaced * * Process @buf in @bpool. For capture devices, this functions fills @buf with * data from the device. For output devices, this functions send the contents of * @buf to the device for playback. * * Returns: %GST_FLOW_OK on success. */ GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) { GstFlowReturn ret = GST_FLOW_OK; GstBufferPool *bpool = GST_BUFFER_POOL_CAST (pool); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "process buffer %p", buf); g_return_val_if_fail (gst_buffer_pool_is_active (bpool), GST_FLOW_ERROR); if (GST_BUFFER_POOL_IS_FLUSHING (pool)) return GST_FLOW_FLUSHING; switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: /* capture */ switch (obj->mode) { case GST_V4L2_IO_RW: /* capture into the buffer */ ret = gst_v4l2_do_read (pool, *buf); break; case GST_V4L2_IO_MMAP: case GST_V4L2_IO_DMABUF: { GstBuffer *tmp; if ((*buf)->pool == bpool) { if (gst_buffer_get_size (*buf) == 0) goto eos; /* start copying buffers when we are running low on buffers */ if (g_atomic_int_get (&pool->num_queued) < pool->copy_threshold) { GstBuffer *copy; if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { if (gst_buffer_pool_acquire_buffer (bpool, ©, NULL) == GST_FLOW_OK) { gst_v4l2_buffer_pool_release_buffer (bpool, copy); goto done; } } /* copy the buffer */ copy = gst_buffer_copy_region (*buf, GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, 0, -1); GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buf, copy); /* and requeue so that we can continue capturing */ gst_buffer_unref (*buf); *buf = copy; } /* nothing, data was inside the buffer when we did _acquire() */ goto done; } /* buffer not from our pool, grab a frame and copy it into the target */ if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK) goto done; /* An empty buffer on capture indicates the end of stream */ if (gst_buffer_get_size (tmp) == 0) { gst_v4l2_buffer_pool_release_buffer (bpool, tmp); goto eos; } ret = gst_v4l2_buffer_pool_copy_buffer (pool, *buf, tmp); /* an queue the buffer again after the copy */ gst_v4l2_buffer_pool_release_buffer (bpool, tmp); if (ret != GST_FLOW_OK) goto copy_failed; break; } case GST_V4L2_IO_USERPTR: { struct UserPtrData *data; /* Replace our buffer with downstream allocated buffer */ data = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); gst_buffer_replace (buf, data->buffer); _unmap_userptr_frame (data); break; } case GST_V4L2_IO_DMABUF_IMPORT: { GstBuffer *tmp; /* Replace our buffer with downstream allocated buffer */ tmp = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); gst_buffer_replace (buf, tmp); gst_buffer_unref (tmp); break; } default: g_assert_not_reached (); break; } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: /* playback */ switch (obj->mode) { case GST_V4L2_IO_RW: /* FIXME, do write() */ GST_WARNING_OBJECT (pool, "implement write()"); break; case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF_IMPORT: case GST_V4L2_IO_DMABUF: case GST_V4L2_IO_MMAP: { GstBuffer *to_queue = NULL; GstV4l2MemoryGroup *group; gint index; if ((*buf)->pool != bpool) goto copying; if (!gst_v4l2_is_buffer_valid (*buf, &group)) goto copying; index = group->buffer.index; GST_LOG_OBJECT (pool, "processing buffer %i from our pool", index); index = group->buffer.index; if (pool->buffers[index] != NULL) { GST_LOG_OBJECT (pool, "buffer %i already queued, copying", index); goto copying; } /* we can queue directly */ to_queue = gst_buffer_ref (*buf); copying: if (to_queue == NULL) { GstBufferPoolAcquireParams params = { 0 }; GST_LOG_OBJECT (pool, "alloc buffer from our pool"); /* this can return EOS if all buffers are outstanding which would * be strange because we would expect the upstream element to have * allocated them and returned to us.. */ params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT; ret = gst_buffer_pool_acquire_buffer (bpool, &to_queue, ¶ms); if (ret != GST_FLOW_OK) goto acquire_failed; ret = gst_v4l2_buffer_pool_prepare_buffer (pool, to_queue, *buf); if (ret != GST_FLOW_OK) { gst_buffer_unref (to_queue); goto prepare_failed; } } if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK) goto queue_failed; /* if we are not streaming yet (this is the first buffer, start * streaming now */ if (!gst_v4l2_buffer_pool_streamon (pool)) { /* don't check return value because qbuf would have failed */ gst_v4l2_is_buffer_valid (to_queue, &group); /* qbuf has taken the ref of the to_queue buffer but we are no in * streaming state, so the flush logic won't be performed. * To avoid leaks, flush the allocator and restore the queued * buffer as non-queued */ gst_v4l2_allocator_flush (pool->vallocator); pool->buffers[group->buffer.index] = NULL; gst_mini_object_set_qdata (GST_MINI_OBJECT (to_queue), GST_V4L2_IMPORT_QUARK, NULL, NULL); gst_buffer_unref (to_queue); g_atomic_int_add (&pool->num_queued, -1); goto start_failed; } if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) { GstBuffer *out; /* all buffers are queued, try to dequeue one and release it back * into the pool so that _acquire can get to it again. */ ret = gst_v4l2_buffer_pool_dqbuf (pool, &out); if (ret == GST_FLOW_OK) /* release the rendered buffer back into the pool. This wakes up any * thread waiting for a buffer in _acquire(). */ gst_buffer_unref (out); } break; } default: g_assert_not_reached (); break; } break; default: g_assert_not_reached (); break; } done: return ret; /* ERRORS */ copy_failed: { GST_ERROR_OBJECT (pool, "failed to copy buffer"); return ret; } eos: { GST_DEBUG_OBJECT (pool, "end of stream reached"); return GST_FLOW_EOS; } acquire_failed: { if (ret == GST_FLOW_FLUSHING) GST_DEBUG_OBJECT (pool, "flushing"); else GST_WARNING_OBJECT (pool, "failed to acquire a buffer: %s", gst_flow_get_name (ret)); return ret; } prepare_failed: { GST_ERROR_OBJECT (pool, "failed to prepare data"); return ret; } queue_failed: { GST_ERROR_OBJECT (pool, "failed to queue buffer"); return ret; } start_failed: { GST_ERROR_OBJECT (pool, "failed to start streaming"); return GST_FLOW_ERROR; } }
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; } }