/* GStreamer * Copyright (C) 2020 Collabora Ltd. * Author: Nicolas Dufresne * * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "gstv4l2codecpool.h" #include struct _GstV4l2CodecPool { GstBufferPool parent; GstAtomicQueue *queue; GstV4l2CodecAllocator *allocator; /* Used to set GstVideoMeta */ GstVideoInfo *vinfo; }; G_DEFINE_TYPE (GstV4l2CodecPool, gst_v4l2_codec_pool, GST_TYPE_BUFFER_POOL); static GstBuffer * gst_v4l2_codec_pool_create_empty_buffer (void) { GstVideoMeta *vmeta; GstBuffer *buffer = gst_buffer_new (); vmeta = gst_buffer_add_video_meta (buffer, 0, GST_VIDEO_FORMAT_NV12, 1, 1); GST_META_FLAG_SET (vmeta, GST_META_FLAG_POOLED); return buffer; } static GstFlowReturn gst_v4l2_codec_pool_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) { GstV4l2CodecPool *self = GST_V4L2_CODEC_POOL (pool); GstBuffer *buf; GstVideoMeta *vmeta; /* A GstVideoInfo must be set before buffer can be acquired */ g_return_val_if_fail (self->vinfo, GST_FLOW_ERROR); buf = gst_atomic_queue_pop (self->queue); if (!buf) buf = gst_v4l2_codec_pool_create_empty_buffer (); /* First, just try and obtain a buffer. */ if (!gst_v4l2_codec_allocator_prepare_buffer (self->allocator, buf)) { GstFlowReturn flow_ret = GST_FLOW_OK; /* If none were available, try and allocate one. */ if (!gst_v4l2_codec_allocator_create_buffer (self->allocator)) { /* Otherwise, wait if this is allowed. */ if (params && params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT) { flow_ret = GST_FLOW_EOS; } else { if (!gst_v4l2_codec_allocator_wait_for_buffer (self->allocator)) flow_ret = GST_FLOW_FLUSHING; } } if (flow_ret != GST_FLOW_OK) { gst_atomic_queue_push (self->queue, buf); return flow_ret; } /* Finally, pop the buffer we created or waited for. */ gst_v4l2_codec_allocator_prepare_buffer (self->allocator, buf); } vmeta = gst_buffer_get_video_meta (buf); vmeta->format = GST_VIDEO_INFO_FORMAT (self->vinfo); vmeta->width = GST_VIDEO_INFO_WIDTH (self->vinfo); vmeta->height = GST_VIDEO_INFO_HEIGHT (self->vinfo); vmeta->n_planes = GST_VIDEO_INFO_N_PLANES (self->vinfo); memcpy (vmeta->offset, self->vinfo->offset, sizeof (vmeta->offset)); memcpy (vmeta->stride, self->vinfo->stride, sizeof (vmeta->stride)); *buffer = buf; return GST_FLOW_OK; } static void gst_v4l2_codec_pool_reset_buffer (GstBufferPool * pool, GstBuffer * buffer) { GstBufferPoolClass *klass = GST_BUFFER_POOL_CLASS (gst_v4l2_codec_pool_parent_class); /* Clears all the memories and only pool the GstBuffer objects */ gst_buffer_remove_all_memory (buffer); klass->reset_buffer (pool, buffer); GST_BUFFER_FLAGS (buffer) = 0; } static void gst_v4l2_codec_pool_release_buffer (GstBufferPool * pool, GstBuffer * buffer) { GstV4l2CodecPool *self = GST_V4L2_CODEC_POOL (pool); gst_atomic_queue_push (self->queue, buffer); } static void gst_v4l2_codec_pool_init (GstV4l2CodecPool * self) { self->queue = gst_atomic_queue_new (4); } static void gst_v4l2_codec_pool_finalize (GObject * object) { GstV4l2CodecPool *self = GST_V4L2_CODEC_POOL (object); GstBuffer *buf; while ((buf = gst_atomic_queue_pop (self->queue))) gst_buffer_unref (buf); gst_atomic_queue_unref (self->queue); g_object_unref (self->allocator); if (self->vinfo) gst_video_info_free (self->vinfo); G_OBJECT_CLASS (gst_v4l2_codec_pool_parent_class)->finalize (object); } static void gst_v4l2_codec_pool_class_init (GstV4l2CodecPoolClass * klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GstBufferPoolClass *pool_class = GST_BUFFER_POOL_CLASS (klass); object_class->finalize = gst_v4l2_codec_pool_finalize; pool_class->start = NULL; pool_class->acquire_buffer = gst_v4l2_codec_pool_acquire_buffer; pool_class->reset_buffer = gst_v4l2_codec_pool_reset_buffer; pool_class->release_buffer = gst_v4l2_codec_pool_release_buffer; } GstV4l2CodecPool * gst_v4l2_codec_pool_new (GstV4l2CodecAllocator * allocator, const GstVideoInfo * vinfo) { GstV4l2CodecPool *pool = g_object_new (GST_TYPE_V4L2_CODEC_POOL, NULL); gsize pool_size; pool->allocator = g_object_ref (allocator); pool->vinfo = gst_video_info_copy (vinfo); pool_size = gst_v4l2_codec_allocator_get_pool_size (allocator); for (gsize i = 0; i < pool_size; i++) { GstBuffer *buffer = gst_v4l2_codec_pool_create_empty_buffer (); gst_atomic_queue_push (pool->queue, buffer); } return pool; } guint32 gst_v4l2_codec_buffer_get_index (GstBuffer * buffer) { GstMemory *mem = gst_buffer_peek_memory (buffer, 0); return gst_v4l2_codec_memory_get_index (mem); }