void gst_v4l2_allocator_flush (GstV4l2Allocator * allocator) { gint i; GST_OBJECT_LOCK (allocator); if (!g_atomic_int_get (&allocator->active)) goto done; for (i = 0; i < allocator->count; i++) { GstV4l2MemoryGroup *group = allocator->groups[i]; gint n; if (IS_QUEUED (group->buffer)) { UNSET_QUEUED (group->buffer); gst_v4l2_allocator_reset_group (allocator, group); for (n = 0; n < group->n_mem; n++) gst_memory_unref (group->mem[n]); } } done: GST_OBJECT_UNLOCK (allocator); }
gboolean gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { GstV4l2Object *obj = allocator->obj; gboolean ret = TRUE; gint i; g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE); /* update sizes */ if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { for (i = 0; i < group->n_mem; i++) group->planes[i].bytesused = gst_memory_get_sizes (group->mem[i], NULL, NULL); } else { group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL); } /* Ensure the memory will stay around and is RO */ for (i = 0; i < group->n_mem; i++) gst_memory_ref (group->mem[i]); if (obj->ioctl (obj->video_fd, VIDIOC_QBUF, &group->buffer) < 0) { GST_ERROR_OBJECT (allocator, "failed queueing buffer %i: %s", group->buffer.index, g_strerror (errno)); /* Release the memory, possibly making it RW again */ for (i = 0; i < group->n_mem; i++) gst_memory_unref (group->mem[i]); ret = FALSE; if (IS_QUEUED (group->buffer)) { GST_DEBUG_OBJECT (allocator, "driver pretends buffer is queued even if queue failed"); UNSET_QUEUED (group->buffer); } goto done; } GST_LOG_OBJECT (allocator, "queued buffer %i (flags 0x%X)", group->buffer.index, group->buffer.flags); if (!IS_QUEUED (group->buffer)) { GST_DEBUG_OBJECT (allocator, "driver pretends buffer is not queued even if queue succeeded"); SET_QUEUED (group->buffer); } done: return ret; }
GstV4l2MemoryGroup * gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator) { struct v4l2_buffer buffer = { 0 }; struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} }; gint i; GstV4l2MemoryGroup *group = NULL; g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE); buffer.type = allocator->type; buffer.memory = allocator->memory; if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { buffer.length = allocator->format.fmt.pix_mp.num_planes; buffer.m.planes = planes; } if (v4l2_ioctl (allocator->video_fd, VIDIOC_DQBUF, &buffer) < 0) goto error; group = allocator->groups[buffer.index]; if (!IS_QUEUED (group->buffer)) { GST_ERROR_OBJECT (allocator, "buffer %i was not queued, this indicate a driver bug.", buffer.index); return NULL; } group->buffer = buffer; GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index, buffer.flags); if (IS_QUEUED (group->buffer)) { GST_DEBUG_OBJECT (allocator, "driver pretends buffer is queued even if dequeue succeeded"); UNSET_QUEUED (group->buffer); } if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { group->buffer.m.planes = group->planes; memcpy (group->planes, buffer.m.planes, sizeof (planes)); } else { group->planes[0].bytesused = group->buffer.bytesused; group->planes[0].length = group->buffer.length; g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m)); memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m)); } /* And update memory size */ if (V4L2_TYPE_IS_OUTPUT (allocator->type)) { gst_v4l2_allocator_reset_size (allocator, group); } else { /* for capture, simply read the size */ for (i = 0; i < group->n_mem; i++) { gst_memory_resize (group->mem[i], 0, group->planes[i].bytesused); } } /* Release the memory, possibly making it RW again */ for (i = 0; i < group->n_mem; i++) gst_memory_unref (group->mem[i]); return group; error: GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s", memory_type_to_str (allocator->memory), g_strerror (errno)); switch (errno) { case EAGAIN: GST_WARNING_OBJECT (allocator, "Non-blocking I/O has been selected using O_NONBLOCK and" " no buffer was in the outgoing queue."); break; case EINVAL: GST_ERROR_OBJECT (allocator, "The buffer type is not supported, or the index is out of bounds, " "or no buffers have been allocated yet, or the userptr " "or length are invalid."); break; case ENOMEM: GST_ERROR_OBJECT (allocator, "insufficient memory to enqueue a user pointer buffer"); break; case EIO: GST_INFO_OBJECT (allocator, "VIDIOC_DQBUF failed due to an internal error." " Can also indicate temporary problems like signal loss." " Note the driver might dequeue an (empty) buffer despite" " returning an error, or even stop capturing."); /* have we de-queued a buffer ? */ if (!IS_QUEUED (buffer)) { GST_DEBUG_OBJECT (allocator, "reenqueing buffer"); /* FIXME ... should we do something here? */ } break; case EINTR: GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device"); break; default: GST_WARNING_OBJECT (allocator, "Grabbing frame got interrupted unexpectedly. %d: %s.", errno, g_strerror (errno)); break; } return NULL; }
GstFlowReturn gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator, GstV4l2MemoryGroup ** group_out) { GstV4l2Object *obj = allocator->obj; struct v4l2_buffer buffer = { 0 }; struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} }; gint i; GstV4l2MemoryGroup *group = NULL; g_return_val_if_fail (g_atomic_int_get (&allocator->active), GST_FLOW_ERROR); buffer.type = obj->type; buffer.memory = allocator->memory; if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { buffer.length = obj->format.fmt.pix_mp.num_planes; buffer.m.planes = planes; } if (obj->ioctl (obj->video_fd, VIDIOC_DQBUF, &buffer) < 0) goto error; group = allocator->groups[buffer.index]; if (!IS_QUEUED (group->buffer)) { GST_ERROR_OBJECT (allocator, "buffer %i was not queued, this indicate a driver bug.", buffer.index); return GST_FLOW_ERROR; } group->buffer = buffer; GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index, buffer.flags); if (IS_QUEUED (group->buffer)) { GST_DEBUG_OBJECT (allocator, "driver pretends buffer is queued even if dequeue succeeded"); UNSET_QUEUED (group->buffer); } if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.m.planes = group->planes; memcpy (group->planes, buffer.m.planes, sizeof (planes)); } else { group->planes[0].bytesused = group->buffer.bytesused; group->planes[0].length = group->buffer.length; g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m)); memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m)); } /* And update memory size */ if (V4L2_TYPE_IS_OUTPUT (obj->type)) { gst_v4l2_allocator_reset_size (allocator, group); } else { /* for capture, simply read the size */ for (i = 0; i < group->n_mem; i++) { gsize size, offset; GST_LOG_OBJECT (allocator, "Dequeued capture buffer, length: %u bytesused: %u data_offset: %u", group->planes[i].length, group->planes[i].bytesused, group->planes[i].data_offset); offset = group->planes[i].data_offset; if (group->planes[i].bytesused > group->planes[i].data_offset) { size = group->planes[i].bytesused - group->planes[i].data_offset; } else { GST_WARNING_OBJECT (allocator, "V4L2 provided buffer has bytesused %" G_GUINT32_FORMAT " which is too small to include data_offset %" G_GUINT32_FORMAT, group->planes[i].bytesused, group->planes[i].data_offset); size = group->planes[i].bytesused; } if (G_LIKELY (size + offset <= group->mem[i]->maxsize)) gst_memory_resize (group->mem[i], offset, size); else { GST_WARNING_OBJECT (allocator, "v4l2 provided buffer that is too big for the memory it was " "writing into. v4l2 claims %" G_GSIZE_FORMAT " bytes used but " "memory is only %" G_GSIZE_FORMAT "B. This is probably a driver " "bug.", size, group->mem[i]->maxsize); gst_memory_resize (group->mem[i], 0, group->mem[i]->maxsize); } } } /* Release the memory, possibly making it RW again */ for (i = 0; i < group->n_mem; i++) gst_memory_unref (group->mem[i]); *group_out = group; return GST_FLOW_OK; error: if (errno == EPIPE) { GST_DEBUG_OBJECT (allocator, "broken pipe signals last buffer"); return GST_FLOW_EOS; } GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s", memory_type_to_str (allocator->memory), g_strerror (errno)); switch (errno) { case EAGAIN: GST_WARNING_OBJECT (allocator, "Non-blocking I/O has been selected using O_NONBLOCK and" " no buffer was in the outgoing queue."); break; case EINVAL: GST_ERROR_OBJECT (allocator, "The buffer type is not supported, or the index is out of bounds, " "or no buffers have been allocated yet, or the userptr " "or length are invalid."); break; case ENOMEM: GST_ERROR_OBJECT (allocator, "insufficient memory to enqueue a user pointer buffer"); break; case EIO: GST_INFO_OBJECT (allocator, "VIDIOC_DQBUF failed due to an internal error." " Can also indicate temporary problems like signal loss." " Note the driver might dequeue an (empty) buffer despite" " returning an error, or even stop capturing."); /* have we de-queued a buffer ? */ if (!IS_QUEUED (buffer)) { GST_DEBUG_OBJECT (allocator, "reenqueueing buffer"); /* FIXME ... should we do something here? */ } break; case EINTR: GST_WARNING_OBJECT (allocator, "could not sync on a buffer on device"); break; default: GST_WARNING_OBJECT (allocator, "Grabbing frame got interrupted unexpectedly. %d: %s.", errno, g_strerror (errno)); break; } return GST_FLOW_ERROR; }