mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 04:00:37 +00:00
1e27ea63af
The `gst_v4l2_buffer_pool_dqbuf` function contains this ominous comment: /* get our GstBuffer with that index from the pool, if the buffer was * outstanding we have a serious problem. */ outbuf = pool->buffers[group->buffer.index]; Unfortunately it is common for buffers in _output_ buffer pools to be both queued and outstanding at the same time. This can happen if the upstream element keeps a reference to the buffer, or in an encoder element itself when it keeps a reference to the input buffer for each frame. Since the current code doesn't handle this case properly we can end up with crashes in other elements such as: (gst-launch-1.0:32559): CRITICAL **: 17:33:35.740: gst_video_frame_map_id: assertion 'GST_IS_BUFFER (buffer)' failed and: (gst-launch-1.0:231): GStreamer-CRITICAL **: 00:16:20.882: write map requested on non-writable buffer Both these crashes are caused by a race condition related to releasing the same buffer twice from two different threads. If a buffer is queued and outstanding this situation is possible: **Thread 1** - Calls `gst_buffer_unref` decrementing the reference count to zero. - The core GstBufferPool object marks the buffer non-outstanding. - Calls the V4L2 release buffer function. - If the buffer is _not_ queued: - Release it back to the free pool (containing non-queued buffers). **Thread 2** - Dequeues the queued output buffer. - Marks the buffer as not queued. - If the buffer is _not_ outstanding: - Calls the V4L2 release buffer function. - Release it back to the free pool (containing non-queued buffers). If both of these threads run at exactly the same time there is a small window where the buffer is marked both not outstanding and not queued but before it has been released. In this case the buffer will be freed twice causing the above crashes. Unfortunately the variable recording whether a buffer is outstanding is part of the core `GstBuffer` object and is managed by `GstBufferPool` so it's not as straightforward as adding a mutex. Instead we can fix this by additionally recording the buffer state in `GstV4l2BufferPool`, and handle "internal" and "external" buffer release separately so we can detect when a buffer becomes not outstanding. In the new solution: - The "external" buffer pool release and the "dqbuf" functions atomically update the buffer state and determine if a buffer is still queued or outstanding. - Subsequent code and a new `gst_v4l2_buffer_pool_complete_release_buffer` function can proceed to release (or not) a buffer knowing that it's not racing with another thread. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1010>
129 lines
4.7 KiB
C
129 lines
4.7 KiB
C
/* GStreamer
|
|
*
|
|
* Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
|
* 2006 Edgard Lima <edgard.lima@gmail.com>
|
|
* 2009 Texas Instruments, Inc - http://www.ti.com/
|
|
*
|
|
* gstv4l2bufferpool.h V4L2 buffer pool class
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __GST_V4L2_BUFFER_POOL_H__
|
|
#define __GST_V4L2_BUFFER_POOL_H__
|
|
|
|
#include <gst/gst.h>
|
|
|
|
typedef struct _GstV4l2BufferPool GstV4l2BufferPool;
|
|
typedef struct _GstV4l2BufferPoolClass GstV4l2BufferPoolClass;
|
|
typedef struct _GstV4l2Meta GstV4l2Meta;
|
|
|
|
#include "gstv4l2object.h"
|
|
#include "gstv4l2allocator.h"
|
|
|
|
G_BEGIN_DECLS
|
|
|
|
#define GST_TYPE_V4L2_BUFFER_POOL (gst_v4l2_buffer_pool_get_type())
|
|
#define GST_IS_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_BUFFER_POOL))
|
|
#define GST_V4L2_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_BUFFER_POOL, GstV4l2BufferPool))
|
|
#define GST_V4L2_BUFFER_POOL_CAST(obj) ((GstV4l2BufferPool*)(obj))
|
|
|
|
/* Returns true if the pool is streaming. Must be called with stream lock
|
|
* held. */
|
|
#define GST_V4L2_BUFFER_POOL_IS_STREAMING(obj) (GST_V4L2_BUFFER_POOL (obj)->streaming)
|
|
|
|
/* This flow return is used to indicate that the last buffer has been dequeued
|
|
* during draining. This should normally only occur for mem-2-mem devices. */
|
|
#define GST_V4L2_FLOW_LAST_BUFFER GST_FLOW_CUSTOM_SUCCESS
|
|
|
|
/* This flow return is used to indicated that the returned buffer was marked
|
|
* with the error flag and had no payload. This error should be recovered by
|
|
* simply waiting for next buffer. */
|
|
#define GST_V4L2_FLOW_CORRUPTED_BUFFER GST_FLOW_CUSTOM_SUCCESS_1
|
|
|
|
/* This flow return is used to indicate that a SOURCE_CHANGE event with the
|
|
* resolution change flag set was received. */
|
|
#define GST_V4L2_FLOW_RESOLUTION_CHANGE GST_FLOW_CUSTOM_SUCCESS_2
|
|
|
|
struct _GstV4l2BufferPool
|
|
{
|
|
GstBufferPool parent;
|
|
|
|
GstV4l2Object *obj; /* the v4l2 object */
|
|
gint video_fd; /* a dup(2) of the v4l2object's video_fd */
|
|
GstPoll *poll; /* a poll for video_fd */
|
|
GstPollFD pollfd;
|
|
gboolean can_poll_device;
|
|
|
|
gboolean empty;
|
|
GCond empty_cond;
|
|
|
|
gboolean orphaned;
|
|
|
|
GstV4l2Allocator *vallocator;
|
|
GstAllocator *allocator;
|
|
GstAllocationParams params;
|
|
GstBufferPool *other_pool;
|
|
guint size;
|
|
GstVideoInfo caps_info; /* Default video information */
|
|
|
|
gboolean add_videometa; /* set if video meta should be added */
|
|
gboolean enable_copy_threshold; /* If copy_threshold should be set */
|
|
|
|
guint min_latency; /* number of buffers we will hold */
|
|
guint max_latency; /* number of buffers we can hold */
|
|
guint num_queued; /* number of buffers queued in the driver */
|
|
guint num_allocated; /* number of buffers allocated */
|
|
guint copy_threshold; /* when our pool runs lower, start handing out copies */
|
|
|
|
gboolean streaming;
|
|
gboolean flushing;
|
|
|
|
GstBuffer *buffers[VIDEO_MAX_FRAME];
|
|
volatile gint buffer_state[VIDEO_MAX_FRAME];
|
|
|
|
/* signal handlers */
|
|
gulong group_released_handler;
|
|
|
|
/* Control to warn only once on buggy feild driver bug */
|
|
gboolean has_warned_on_buggy_field;
|
|
};
|
|
|
|
struct _GstV4l2BufferPoolClass
|
|
{
|
|
GstBufferPoolClass parent_class;
|
|
};
|
|
|
|
GType gst_v4l2_buffer_pool_get_type (void);
|
|
|
|
GstBufferPool * gst_v4l2_buffer_pool_new (GstV4l2Object *obj, GstCaps *caps);
|
|
|
|
GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * bpool, GstBuffer ** buf, guint32 * frame_number);
|
|
|
|
void gst_v4l2_buffer_pool_set_other_pool (GstV4l2BufferPool * pool,
|
|
GstBufferPool * other_pool);
|
|
void gst_v4l2_buffer_pool_copy_at_threshold (GstV4l2BufferPool * pool,
|
|
gboolean copy);
|
|
|
|
gboolean gst_v4l2_buffer_pool_flush (GstBufferPool *pool);
|
|
|
|
gboolean gst_v4l2_buffer_pool_orphan (GstBufferPool ** pool);
|
|
|
|
void gst_v4l2_buffer_pool_enable_resolution_change (GstV4l2BufferPool *self);
|
|
|
|
G_END_DECLS
|
|
|
|
#endif /*__GST_V4L2_BUFFER_POOL_H__ */
|