int ff_v4l2_context_init(V4L2Context* ctx) { V4L2m2mContext *s = ctx_to_m2mctx(ctx); struct v4l2_requestbuffers req; int ret, i; if (!v4l2_type_supported(ctx)) { av_log(logger(ctx), AV_LOG_ERROR, "type %i not supported\n", ctx->type); return AVERROR_PATCHWELCOME; } ret = ioctl(s->fd, VIDIOC_G_FMT, &ctx->format); if (ret) av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT failed\n", ctx->name); memset(&req, 0, sizeof(req)); req.count = ctx->num_buffers; req.memory = V4L2_MEMORY_MMAP; req.type = ctx->type; ret = ioctl(s->fd, VIDIOC_REQBUFS, &req); if (ret < 0) return AVERROR(errno); ctx->num_buffers = req.count; ctx->buffers = av_mallocz(ctx->num_buffers * sizeof(V4L2Buffer)); if (!ctx->buffers) { av_log(logger(ctx), AV_LOG_ERROR, "%s malloc enomem\n", ctx->name); return AVERROR(ENOMEM); } for (i = 0; i < req.count; i++) { ctx->buffers[i].context = ctx; ret = ff_v4l2_buffer_initialize(&ctx->buffers[i], i); if (ret < 0) { av_log(logger(ctx), AV_LOG_ERROR, "%s buffer initialization (%s)\n", ctx->name, av_err2str(ret)); av_free(ctx->buffers); return ret; } } av_log(logger(ctx), AV_LOG_DEBUG, "%s: %s %02d buffers initialized: %04ux%04u, sizeimage %08u, bytesperline %08u\n", ctx->name, V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? av_fourcc2str(ctx->format.fmt.pix_mp.pixelformat) : av_fourcc2str(ctx->format.fmt.pix.pixelformat), req.count, v4l2_get_width(&ctx->format), v4l2_get_height(&ctx->format), V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[0].sizeimage : ctx->format.fmt.pix.sizeimage, V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[0].bytesperline : ctx->format.fmt.pix.bytesperline); return 0; }
/** * __verify_length() - Verify that the bytesused value for each plane fits in * the plane length and that the data offset doesn't exceed the bytesused value. */ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) { unsigned int length; unsigned int bytesused; unsigned int plane; if (!V4L2_TYPE_IS_OUTPUT(b->type)) return 0; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { for (plane = 0; plane < vb->num_planes; ++plane) { length = (b->memory == VB2_MEMORY_USERPTR || b->memory == VB2_MEMORY_DMABUF) ? b->m.planes[plane].length : vb->planes[plane].length; bytesused = b->m.planes[plane].bytesused ? b->m.planes[plane].bytesused : length; if (b->m.planes[plane].bytesused > length) return -EINVAL; if (b->m.planes[plane].data_offset > 0 && b->m.planes[plane].data_offset >= bytesused) return -EINVAL; } } else { length = (b->memory == VB2_MEMORY_USERPTR) ? b->length : vb->planes[0].length; if (b->bytesused > length) return -EINVAL; } return 0; }
/** * vb2_queue_init() - initialize a videobuf2 queue * @q: videobuf2 queue; this structure should be allocated in driver * * The vb2_queue structure should be allocated by the driver. The driver is * responsible of clearing it's content and setting initial values for some * required entries before calling this function. * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer * to the struct vb2_queue description in include/media/videobuf2-core.h * for more information. */ int vb2_queue_init(struct vb2_queue *q) { /* * Sanity check */ if (WARN_ON(!q) || WARN_ON(q->timestamp_flags & ~(V4L2_BUF_FLAG_TIMESTAMP_MASK | V4L2_BUF_FLAG_TSTAMP_SRC_MASK))) return -EINVAL; /* Warn that the driver should choose an appropriate timestamp type */ WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); /* Warn that vb2_memory should match with v4l2_memory */ if (WARN_ON(VB2_MEMORY_MMAP != (int)V4L2_MEMORY_MMAP) || WARN_ON(VB2_MEMORY_USERPTR != (int)V4L2_MEMORY_USERPTR) || WARN_ON(VB2_MEMORY_DMABUF != (int)V4L2_MEMORY_DMABUF)) return -EINVAL; if (q->buf_struct_size == 0) q->buf_struct_size = sizeof(struct vb2_v4l2_buffer); q->buf_ops = &v4l2_buf_ops; q->is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); q->is_output = V4L2_TYPE_IS_OUTPUT(q->type); q->copy_timestamp = (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_COPY; return vb2_core_queue_init(q); }
static void gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { gsize size; gboolean imported = FALSE; switch (allocator->memory) { case V4L2_MEMORY_USERPTR: case V4L2_MEMORY_DMABUF: imported = TRUE; break; } if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { gint i; for (i = 0; i < group->n_mem; i++) { size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage; if (imported) group->mem[i]->maxsize = size; gst_memory_resize (group->mem[i], 0, size); } } else { size = allocator->format.fmt.pix.sizeimage; if (imported) group->mem[0]->maxsize = size; gst_memory_resize (group->mem[0], 0, size); } }
static inline void v4l2_save_to_context(V4L2Context* ctx, struct v4l2_format_update *fmt) { ctx->format.type = ctx->type; if (fmt->update_avfmt) ctx->av_pix_fmt = fmt->av_fmt; if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { /* update the sizes to handle the reconfiguration of the capture stream at runtime */ ctx->format.fmt.pix_mp.height = ctx->height; ctx->format.fmt.pix_mp.width = ctx->width; if (fmt->update_v4l2) { ctx->format.fmt.pix_mp.pixelformat = fmt->v4l2_fmt; /* s5p-mfc requires the user to specify a buffer size */ ctx->format.fmt.pix_mp.plane_fmt[0].sizeimage = v4l2_get_framesize_compressed(ctx, ctx->width, ctx->height); } } else { ctx->format.fmt.pix.height = ctx->height; ctx->format.fmt.pix.width = ctx->width; if (fmt->update_v4l2) { ctx->format.fmt.pix.pixelformat = fmt->v4l2_fmt; /* s5p-mfc requires the user to specify a buffer size */ ctx->format.fmt.pix.sizeimage = v4l2_get_framesize_compressed(ctx, ctx->width, ctx->height); } } }
static void gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { GstV4l2Memory *mem; gint i; g_return_if_fail (allocator->memory == V4L2_MEMORY_USERPTR); for (i = 0; i < group->n_mem; i++) { mem = (GstV4l2Memory *) group->mem[i]; GST_LOG_OBJECT (allocator, "clearing USERPTR %p plane %d size %" G_GSIZE_FORMAT, mem->data, i, mem->mem.size); mem->mem.maxsize = 0; mem->mem.size = 0; mem->data = NULL; group->planes[i].length = 0; group->planes[i].bytesused = 0; group->planes[i].m.userptr = 0; } if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { group->buffer.bytesused = 0; group->buffer.length = 0; group->buffer.m.userptr = 0; } }
/** * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be * returned to userspace */ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; int ret = 0; /* Copy back data such as timestamp, flags, input, etc. */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); b->input = vb->v4l2_buf.input; b->reserved = vb->v4l2_buf.reserved; if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { ret = __verify_planes_array(vb, b); if (ret) return ret; /* * Fill in plane-related data if userspace provided an array * for it. The memory and size is verified above. */ memcpy(b->m.planes, vb->v4l2_planes, b->length * sizeof(struct v4l2_plane)); } else { /* * We use length and offset in v4l2_planes array even for * single-planar buffers, but userspace does not. */ b->length = vb->v4l2_planes[0].length; b->bytesused = vb->v4l2_planes[0].bytesused; if (q->memory == V4L2_MEMORY_MMAP) b->m.offset = vb->v4l2_planes[0].m.mem_offset; else if (q->memory == V4L2_MEMORY_USERPTR) b->m.userptr = vb->v4l2_planes[0].m.userptr; } /* * Clear any buffer state related flags. */ b->flags &= ~V4L2_BUFFER_STATE_FLAGS; switch (vb->state) { case VB2_BUF_STATE_QUEUED: case VB2_BUF_STATE_ACTIVE: b->flags |= V4L2_BUF_FLAG_QUEUED; break; case VB2_BUF_STATE_ERROR: b->flags |= V4L2_BUF_FLAG_ERROR; /* fall through */ case VB2_BUF_STATE_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; case VB2_BUF_STATE_DEQUEUED: /* nothing */ break; } if (vb->num_planes_mapped == vb->num_planes) b->flags |= V4L2_BUF_FLAG_MAPPED; return ret; }
/** * __fill_vb2_buffer() - fill a vb2_buffer with information provided in * a v4l2_buffer by the userspace */ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, struct v4l2_plane *v4l2_planes) { unsigned int plane; int ret; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { /* * Verify that the userspace gave us a valid array for * plane information. */ ret = __verify_planes_array(vb, b); if (ret) return ret; /* Fill in driver-provided information for OUTPUT types */ if (V4L2_TYPE_IS_OUTPUT(b->type)) { /* * Will have to go up to b->length when API starts * accepting variable number of planes. */ for (plane = 0; plane < vb->num_planes; ++plane) { v4l2_planes[plane].bytesused = b->m.planes[plane].bytesused; v4l2_planes[plane].data_offset = b->m.planes[plane].data_offset; } } if (b->memory == V4L2_MEMORY_USERPTR) { for (plane = 0; plane < vb->num_planes; ++plane) { v4l2_planes[plane].m.userptr = b->m.planes[plane].m.userptr; v4l2_planes[plane].length = b->m.planes[plane].length; } } } else { /* * Single-planar buffers do not use planes array, * so fill in relevant v4l2_buffer struct fields instead. * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. */ if (V4L2_TYPE_IS_OUTPUT(b->type)) v4l2_planes[0].bytesused = b->bytesused; if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; v4l2_planes[0].length = b->length; } } vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; vb->v4l2_buf.input = b->input; vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; return 0; }
static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, unsigned int num_buffers, unsigned int num_planes, unsigned long plane_sizes[]) { unsigned int buffer; struct vb2_buffer *vb; int ret; for (buffer = 0; buffer < num_buffers; ++buffer) { vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { dprintk(1, "Memory alloc for buffer struct failed\n"); break; } if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) vb->v4l2_buf.length = num_planes; vb->state = VB2_BUF_STATE_DEQUEUED; vb->vb2_queue = q; vb->num_planes = num_planes; vb->v4l2_buf.index = buffer; vb->v4l2_buf.type = q->type; vb->v4l2_buf.memory = memory; if (memory == V4L2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb, plane_sizes); if (ret) { dprintk(1, "Failed allocating memory for " "buffer %d\n", buffer); kfree(vb); break; } ret = call_qop(q, buf_init, vb); if (ret) { dprintk(1, "Buffer %d %p initialization" " failed\n", buffer, vb); __vb2_buf_mem_free(vb); kfree(vb); break; } } q->bufs[buffer] = vb; } q->num_buffers = buffer; __setup_offsets(q); dprintk(1, "Allocated %d buffers, %d plane(s) each\n", q->num_buffers, num_planes); return buffer; }
bool v4l2::s_fmt(v4l2_format &fmt) { if (V4L2_TYPE_IS_MULTIPLANAR(fmt.type)) { fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 0; fmt.fmt.pix_mp.plane_fmt[1].bytesperline = 0; } else { fmt.fmt.pix.bytesperline = 0; } return ioctl("Set Capture Format", VIDIOC_S_FMT, &fmt); }
static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int num, unsigned int den) { struct v4l2_streamparm parm = { 0 }; parm.type = V4L2_TYPE_IS_MULTIPLANAR(s->output.type) ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT; parm.parm.output.timeperframe.denominator = den; parm.parm.output.timeperframe.numerator = num; if (ioctl(s->fd, VIDIOC_S_PARM, &parm) < 0) av_log(s->avctx, AV_LOG_WARNING, "Failed to set timeperframe"); }
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; }
static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; int ret = 0; memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); b->input = vb->v4l2_buf.input; b->reserved = vb->v4l2_buf.reserved; if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { ret = __verify_planes_array(vb, b); if (ret) return ret; memcpy(b->m.planes, vb->v4l2_planes, b->length * sizeof(struct v4l2_plane)); } else { b->length = vb->v4l2_planes[0].length; b->bytesused = vb->v4l2_planes[0].bytesused; if (q->memory == V4L2_MEMORY_MMAP) b->m.offset = vb->v4l2_planes[0].m.mem_offset; else if (q->memory == V4L2_MEMORY_USERPTR) b->m.userptr = vb->v4l2_planes[0].m.userptr; } b->flags &= ~V4L2_BUFFER_STATE_FLAGS; switch (vb->state) { case VB2_BUF_STATE_QUEUED: case VB2_BUF_STATE_ACTIVE: b->flags |= V4L2_BUF_FLAG_QUEUED; break; case VB2_BUF_STATE_ERROR: b->flags |= V4L2_BUF_FLAG_ERROR; case VB2_BUF_STATE_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; case VB2_BUF_STATE_DEQUEUED: break; } if (vb->num_planes_mapped == vb->num_planes) b->flags |= V4L2_BUF_FLAG_MAPPED; return ret; }
static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, struct v4l2_plane *v4l2_planes) { unsigned int plane; int ret; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { ret = __verify_planes_array(vb, b); if (ret) return ret; if (V4L2_TYPE_IS_OUTPUT(b->type)) { for (plane = 0; plane < vb->num_planes; ++plane) { v4l2_planes[plane].bytesused = b->m.planes[plane].bytesused; v4l2_planes[plane].data_offset = b->m.planes[plane].data_offset; } } if (b->memory == V4L2_MEMORY_USERPTR) { for (plane = 0; plane < vb->num_planes; ++plane) { v4l2_planes[plane].m.userptr = b->m.planes[plane].m.userptr; v4l2_planes[plane].length = b->m.planes[plane].length; } } } else { if (V4L2_TYPE_IS_OUTPUT(b->type)) v4l2_planes[0].bytesused = b->bytesused; if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; v4l2_planes[0].length = b->length; } } vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; vb->v4l2_buf.input = b->input; vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; return 0; }
static inline unsigned int v4l2_resolution_changed(V4L2Context *ctx, struct v4l2_format *fmt2) { struct v4l2_format *fmt1 = &ctx->format; int ret = V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? fmt1->fmt.pix_mp.width != fmt2->fmt.pix_mp.width || fmt1->fmt.pix_mp.height != fmt2->fmt.pix_mp.height : fmt1->fmt.pix.width != fmt2->fmt.pix.width || fmt1->fmt.pix.height != fmt2->fmt.pix.height; if (ret) av_log(logger(ctx), AV_LOG_DEBUG, "%s changed (%dx%d) -> (%dx%d)\n", ctx->name, v4l2_get_width(fmt1), v4l2_get_height(fmt1), v4l2_get_width(fmt2), v4l2_get_height(fmt2)); return ret; }
/** * __verify_planes_array() - verify that the planes array passed in struct * v4l2_buffer from userspace can be safely used */ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b) { if (!V4L2_TYPE_IS_MULTIPLANAR(b->type)) return 0; /* Is memory for copying plane information present? */ if (b->m.planes == NULL) { dprintk(1, "multi-planar buffer passed but planes array not provided\n"); return -EINVAL; } if (b->length < vb->num_planes || b->length > VB2_MAX_PLANES) { dprintk(1, "incorrect planes array length, expected %d, got %d\n", vb->num_planes, b->length); return -EINVAL; } return 0; }
static void gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { GstV4l2Object *obj = allocator->obj; GstV4l2Memory *mem; gint i; g_return_if_fail (allocator->memory == V4L2_MEMORY_DMABUF); for (i = 0; i < group->n_mem; i++) { mem = (GstV4l2Memory *) group->mem[i]; GST_LOG_OBJECT (allocator, "clearing DMABUF import, fd %i plane %d", mem->dmafd, i); if (mem->dmafd >= 0) close (mem->dmafd); /* Update memory */ mem->mem.maxsize = 0; mem->mem.offset = 0; mem->mem.size = 0; mem->dmafd = -1; /* Update v4l2 structure */ group->planes[i].length = 0; group->planes[i].bytesused = 0; group->planes[i].m.fd = -1; group->planes[i].data_offset = 0; } if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.bytesused = 0; group->buffer.length = 0; group->buffer.m.fd = -1; } }
bool v4l2::qbuf_user(int index, __u32 buftype, void *ptr[], size_t length[]) { v4l2_plane planes[VIDEO_MAX_PLANES]; v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = buftype; buf.memory = V4L2_MEMORY_USERPTR; if (V4L2_TYPE_IS_MULTIPLANAR(buftype)) { buf.length = 2; buf.m.planes = planes; planes[0].length = length[0]; planes[0].m.userptr = (unsigned long)ptr[0]; planes[1].length = length[1]; planes[1].m.userptr = (unsigned long)ptr[1]; } else { buf.m.userptr = (unsigned long)ptr[0]; buf.length = length[0]; } buf.index = index; return qbuf(buf); }
/** * vb2_queue_init() - initialize a videobuf2 queue * @q: videobuf2 queue; this structure should be allocated in driver * * The vb2_queue structure should be allocated by the driver. The driver is * responsible of clearing it's content and setting initial values for some * required entries before calling this function. * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer * to the struct vb2_queue description in include/media/videobuf2-core.h * for more information. */ int vb2_queue_init(struct vb2_queue *q) { /* * Sanity check */ if (WARN_ON(!q) || WARN_ON(q->timestamp_flags & ~(V4L2_BUF_FLAG_TIMESTAMP_MASK | V4L2_BUF_FLAG_TSTAMP_SRC_MASK))) return -EINVAL; /* Warn that the driver should choose an appropriate timestamp type */ WARN_ON((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN); /* Warn that vb2_memory should match with v4l2_memory */ if (WARN_ON(VB2_MEMORY_MMAP != (int)V4L2_MEMORY_MMAP) || WARN_ON(VB2_MEMORY_USERPTR != (int)V4L2_MEMORY_USERPTR) || WARN_ON(VB2_MEMORY_DMABUF != (int)V4L2_MEMORY_DMABUF)) return -EINVAL; if (q->buf_struct_size == 0) q->buf_struct_size = sizeof(struct vb2_v4l2_buffer); q->buf_ops = &v4l2_buf_ops; q->is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type); q->is_output = V4L2_TYPE_IS_OUTPUT(q->type); q->copy_timestamp = (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_COPY; /* * For compatibility with vb1: if QBUF hasn't been called yet, then * return POLLERR as well. This only affects capture queues, output * queues will always initialize waiting_for_buffers to false. */ q->quirk_poll_must_check_waiting_for_buffers = true; return vb2_core_queue_init(q); }
/** * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer * * See v4l2_m2m_mmap() documentation for details. */ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { struct vb2_queue *vq; int ret = 0; unsigned int i; vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); ret = vb2_querybuf(vq, buf); /* Adjust MMAP memory offsets for the CAPTURE queue */ if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { for (i = 0; i < buf->length; ++i) buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE; } else { buf->m.offset += DST_QUEUE_OFF_BASE; } } return ret; }
int rot_v4l2_try_fmt_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct rot_ctx *ctx = priv; struct rot_fmt *rot_fmt; struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; int i; if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) { rot_err("not supported format type\n"); return -EINVAL; } rot_fmt = rot_find_format(f); if (!rot_fmt) { rot_err("not supported format values\n"); return -EINVAL; } rot_bound_align_image(ctx, rot_fmt, &pixm->width, &pixm->height); pixm->num_planes = rot_fmt->num_planes; pixm->colorspace = 0; for (i = 0; i < pixm->num_planes; ++i) { pixm->plane_fmt[i].bytesperline = (pixm->width * rot_fmt->bitperpixel[i]) >> 3; pixm->plane_fmt[i].sizeimage = pixm->plane_fmt[i].bytesperline * pixm->height; rot_dbg("[%d] plane: bytesperline %d, sizeimage %d\n", i, pixm->plane_fmt[i].bytesperline, pixm->plane_fmt[i].sizeimage); } return 0; }
/** * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type) * video buffer memory for all buffers/planes on the queue and initializes the * queue * * Returns the number of buffers successfully allocated. */ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, unsigned int num_buffers, unsigned int num_planes, unsigned long plane_sizes[]) { unsigned int buffer; struct vb2_buffer *vb; int ret; for (buffer = 0; buffer < num_buffers; ++buffer) { /* Allocate videobuf buffer structures */ vb = kzalloc(q->buf_struct_size, GFP_KERNEL); if (!vb) { dprintk(1, "Memory alloc for buffer struct failed\n"); break; } /* Length stores number of planes for multiplanar buffers */ if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) vb->v4l2_buf.length = num_planes; vb->state = VB2_BUF_STATE_DEQUEUED; vb->vb2_queue = q; vb->num_planes = num_planes; vb->v4l2_buf.index = buffer; vb->v4l2_buf.type = q->type; vb->v4l2_buf.memory = memory; /* Allocate video buffer memory for the MMAP type */ if (memory == V4L2_MEMORY_MMAP) { ret = __vb2_buf_mem_alloc(vb, plane_sizes); if (ret) { dprintk(1, "Failed allocating memory for " "buffer %d\n", buffer); kfree(vb); break; } /* * Call the driver-provided buffer initialization * callback, if given. An error in initialization * results in queue setup failure. */ ret = call_qop(q, buf_init, vb); if (ret) { dprintk(1, "Buffer %d %p initialization" " failed\n", buffer, vb); __vb2_buf_mem_free(vb); kfree(vb); break; } } q->bufs[buffer] = vb; } q->num_buffers = buffer; __setup_offsets(q); dprintk(1, "Allocated %d buffers, %d plane(s) each\n", q->num_buffers, num_planes); return buffer; }
static inline unsigned int v4l2_get_height(struct v4l2_format *fmt) { return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; }
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; }
static GstV4l2MemoryGroup * gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index) { gint video_fd = allocator->video_fd; guint32 memory = allocator->memory; struct v4l2_format *format = &allocator->format; GstV4l2MemoryGroup *group; gsize img_size, buf_size; group = g_slice_new0 (GstV4l2MemoryGroup); group->buffer.type = format->type; group->buffer.index = index; group->buffer.memory = memory; if (V4L2_TYPE_IS_MULTIPLANAR (format->type)) { group->n_mem = group->buffer.length = format->fmt.pix_mp.num_planes; group->buffer.m.planes = group->planes; } else { group->n_mem = 1; } if (v4l2_ioctl (video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0) goto querybuf_failed; if (group->buffer.index != index) { GST_ERROR_OBJECT (allocator, "Buffer index returned by VIDIOC_QUERYBUF " "didn't match, this indicate the presence of a bug in your driver or " "libv4l2"); g_slice_free (GstV4l2MemoryGroup, group); return NULL; } /* Check that provided size matches the format we have negotiation. Failing * there usually means a driver of libv4l bug. */ if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { gint i; for (i = 0; i < group->n_mem; i++) { img_size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage; buf_size = group->planes[i].length; if (buf_size < img_size) goto buffer_too_short; } } else { img_size = allocator->format.fmt.pix.sizeimage; buf_size = group->buffer.length; if (buf_size < img_size) goto buffer_too_short; } /* We save non planar buffer information into the multi-planar plane array * to avoid duplicating the code later */ if (!V4L2_TYPE_IS_MULTIPLANAR (format->type)) { 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)); } GST_LOG_OBJECT (allocator, "Got %s buffer", memory_type_to_str (memory)); GST_LOG_OBJECT (allocator, " index: %u", group->buffer.index); GST_LOG_OBJECT (allocator, " type: %d", group->buffer.type); GST_LOG_OBJECT (allocator, " flags: %08x", group->buffer.flags); GST_LOG_OBJECT (allocator, " field: %d", group->buffer.field); GST_LOG_OBJECT (allocator, " memory: %d", group->buffer.memory); GST_LOG_OBJECT (allocator, " planes: %d", group->n_mem); #ifndef GST_DISABLE_GST_DEBUG if (memory == V4L2_MEMORY_MMAP) { gint i; for (i = 0; i < group->n_mem; i++) { GST_LOG_OBJECT (allocator, " [%u] bytesused: %u, length: %u", i, group->planes[i].bytesused, group->planes[i].length); GST_LOG_OBJECT (allocator, " [%u] MMAP offset: %u", i, group->planes[i].m.mem_offset); } } #endif return group; querybuf_failed: { GST_ERROR ("error querying buffer %d: %s", index, g_strerror (errno)); goto failed; } buffer_too_short: { GST_ERROR ("buffer size %" G_GSIZE_FORMAT " is smaller then negotiated size %" G_GSIZE_FORMAT ", this is usually the result of a bug in the v4l2 driver or libv4l.", buf_size, img_size); goto failed; } failed: gst_v4l2_memory_group_free (group); return NULL; }
gboolean gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group, gsize img_size, int n_planes, gpointer * data, gsize * size) { GstV4l2Memory *mem; gint i; g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE); /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */ if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type) && n_planes != group->n_mem) goto n_mem_missmatch; for (i = 0; i < group->n_mem; i++) { gsize maxsize, psize; if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { struct v4l2_pix_format_mplane *pix = &allocator->format.fmt.pix_mp; maxsize = pix->plane_fmt[i].sizeimage; psize = size[i]; } else { maxsize = allocator->format.fmt.pix.sizeimage; psize = img_size; } g_assert (psize <= img_size); GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %" G_GSIZE_FORMAT, data[i], i, psize); mem = (GstV4l2Memory *) group->mem[i]; mem->mem.maxsize = maxsize; mem->mem.size = psize; mem->data = data[i]; group->planes[i].length = maxsize; group->planes[i].bytesused = psize; group->planes[i].m.userptr = (unsigned long) data[i]; group->planes[i].data_offset = 0; } /* Copy into buffer structure if not using planes */ if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { group->buffer.bytesused = group->planes[0].bytesused; group->buffer.length = group->planes[0].length; group->buffer.m.userptr = group->planes[0].m.userptr; } else { group->buffer.length = group->n_mem; } return TRUE; n_mem_missmatch: { GST_ERROR_OBJECT (allocator, "Got %i userptr plane while driver need %i", n_planes, group->n_mem); return FALSE; } }
gboolean gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem) { GstV4l2Memory *mem; gint i; g_return_val_if_fail (allocator->memory == V4L2_MEMORY_DMABUF, FALSE); if (group->n_mem != n_mem) goto n_mem_missmatch; for (i = 0; i < group->n_mem; i++) { gint dmafd; gsize size, offset, maxsize; if (!gst_is_dmabuf_memory (dma_mem[i])) goto not_dmabuf; size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize); if ((dmafd = dup (gst_dmabuf_memory_get_fd (dma_mem[i]))) < 0) goto dup_failed; GST_LOG_OBJECT (allocator, "imported DMABUF as fd %i plane %d", dmafd, i); mem = (GstV4l2Memory *) group->mem[i]; /* Update memory */ mem->mem.maxsize = maxsize; mem->mem.offset = offset; mem->mem.size = size; mem->dmafd = dmafd; /* Update v4l2 structure */ group->planes[i].length = maxsize; group->planes[i].bytesused = size; group->planes[i].m.fd = dmafd; group->planes[i].data_offset = offset; } /* Copy into buffer structure if not using planes */ if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { group->buffer.bytesused = group->planes[0].bytesused; group->buffer.length = group->planes[0].length; group->buffer.m.fd = group->planes[0].m.userptr; } else { group->buffer.length = group->n_mem; } return TRUE; n_mem_missmatch: { GST_ERROR_OBJECT (allocator, "Got %i dmabuf but needed %i", n_mem, group->n_mem); return FALSE; } not_dmabuf: { GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i); return FALSE; } dup_failed: { GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s", g_strerror (errno)); return FALSE; } }
int fimc_is_video_s_ctrl(struct file *file, struct fimc_is_video_ctx *vctx, struct v4l2_control *ctrl) { int ret = 0; /* hack for 64bit addr */ ulong value_to_addr = 0; struct fimc_is_video *video; struct fimc_is_device_ischain *device; struct fimc_is_resourcemgr *resourcemgr; BUG_ON(!vctx); BUG_ON(!GET_DEVICE(vctx)); BUG_ON(!GET_VIDEO(vctx)); BUG_ON(!ctrl); device = GET_DEVICE(vctx); video = GET_VIDEO(vctx); resourcemgr = device->resourcemgr; switch (ctrl->id) { case V4L2_CID_IS_END_OF_STREAM: ret = fimc_is_ischain_open_wrap(device, true); if (ret) { merr("fimc_is_ischain_open_wrap is fail(%d)", device, ret); goto p_err; } break; case V4L2_CID_IS_SET_SETFILE: if (test_bit(FIMC_IS_ISCHAIN_START, &device->state)) { merr("device is already started, setfile applying is fail", device); ret = -EINVAL; goto p_err; } device->setfile = ctrl->value; break; case V4L2_CID_IS_HAL_VERSION: if (ctrl->value < 0 || ctrl->value >= IS_HAL_VER_MAX) { merr("hal version(%d) is invalid", device, ctrl->value); ret = -EINVAL; goto p_err; } resourcemgr->hal_version = ctrl->value; break; case V4L2_CID_IS_DEBUG_DUMP: info("Print fimc-is info dump by HAL"); fimc_is_hw_logdump(device->interface); fimc_is_hw_regdump(device->interface); CALL_POPS(device, print_clk); if (ctrl->value) panic("intentional panic from camera HAL"); break; case V4L2_CID_IS_DVFS_CLUSTER0: case V4L2_CID_IS_DVFS_CLUSTER1: fimc_is_resource_ioctl(resourcemgr, ctrl); break; case V4L2_CID_IS_DEBUG_SYNC_LOG: fimc_is_logsync(device->interface, ctrl->value, IS_MSG_TEST_SYNC_LOG); break; case V4L2_CID_IS_MAP_BUFFER: { struct fimc_is_queue *queue; struct fimc_is_framemgr *framemgr; struct fimc_is_frame *frame; struct dma_buf *dmabuf; struct dma_buf_attachment *attachment; dma_addr_t dva; struct v4l2_buffer *buf; struct v4l2_plane *planes; size_t size; u32 plane, group_id; size = sizeof(struct v4l2_buffer); buf = kmalloc(size, GFP_KERNEL); if (!buf) { mverr("kmalloc is fail(%p)", device, video, buf); ret = -EINVAL; goto p_err; } /* hack for 64bit addr */ value_to_addr = ctrl->value; ret = copy_from_user(buf, (void __user *)value_to_addr, size); if (ret) { mverr("copy_from_user is fail(%d)", device, video, ret); kfree(buf); ret = -EINVAL; goto p_err; } if (!V4L2_TYPE_IS_OUTPUT(buf->type)) { mverr("capture video type is not supported", device, video); kfree(buf); ret = -EINVAL; goto p_err; } if (!V4L2_TYPE_IS_MULTIPLANAR(buf->type)) { mverr("single plane is not supported", device, video); kfree(buf); ret = -EINVAL; goto p_err; } if (buf->index >= FRAMEMGR_MAX_REQUEST) { mverr("buffer index is invalid(%d)", device, video, buf->index); kfree(buf); ret = -EINVAL; goto p_err; } if (buf->length > VIDEO_MAX_PLANES) { mverr("planes[%d] is invalid", device, video, buf->length); kfree(buf); ret = -EINVAL; goto p_err; } queue = GET_QUEUE(vctx); if (queue->vbq->memory != V4L2_MEMORY_DMABUF) { mverr("memory type(%d) is not supported", device, video, queue->vbq->memory); kfree(buf); ret = -EINVAL; goto p_err; } size = sizeof(struct v4l2_plane) * buf->length; planes = kmalloc(size, GFP_KERNEL); if (!planes) { mverr("kmalloc is fail(%p)", device, video, planes); kfree(buf); ret = -EINVAL; goto p_err; } ret = copy_from_user(planes, (void __user *)buf->m.planes, size); if (ret) { mverr("copy_from_user is fail(%d)", device, video, ret); kfree(planes); kfree(buf); ret = -EINVAL; goto p_err; } framemgr = &queue->framemgr; frame = &framemgr->frame[buf->index]; if (test_bit(FRAME_MAP_MEM, &frame->memory)) { mverr("this buffer(%d) is already mapped", device, video, buf->index); kfree(planes); kfree(buf); ret = -EINVAL; goto p_err; } /* only last buffer need to map */ if (buf->length <= 1) { mverr("this buffer(%d) have no meta plane", device, video, buf->length); kfree(planes); kfree(buf); ret = -EINVAL; goto p_err; } plane = buf->length - 1; dmabuf = dma_buf_get(planes[plane].m.fd); if (IS_ERR(dmabuf)) { mverr("dma_buf_get is fail(%p)", device, video, dmabuf); kfree(planes); kfree(buf); ret = -EINVAL; goto p_err; } attachment = dma_buf_attach(dmabuf, &device->pdev->dev); if (IS_ERR(attachment)) { mverr("dma_buf_attach is fail(%p)", device, video, attachment); kfree(planes); kfree(buf); dma_buf_put(dmabuf); ret = -EINVAL; goto p_err; } /* only support output(read) video node */ dva = ion_iovmm_map(attachment, 0, dmabuf->size, 0, plane); if (IS_ERR_VALUE(dva)) { mverr("ion_iovmm_map is fail(%pa)", device, video, &dva); kfree(planes); kfree(buf); dma_buf_detach(dmabuf, attachment); dma_buf_put(dmabuf); ret = -EINVAL; goto p_err; } group_id = GROUP_ID(device->group_3aa.id); ret = fimc_is_itf_map(device, group_id, dva, dmabuf->size); if (ret) { mverr("fimc_is_itf_map is fail(%d)", device, video, ret); kfree(planes); kfree(buf); dma_buf_detach(dmabuf, attachment); dma_buf_put(dmabuf); goto p_err; } mvinfo(" B%d.P%d MAP\n", device, video, buf->index, plane); set_bit(FRAME_MAP_MEM, &frame->memory); dma_buf_detach(dmabuf, attachment); dma_buf_put(dmabuf); kfree(planes); kfree(buf); } break; default: err("unsupported ioctl(0x%X)", ctrl->id); ret = -EINVAL; break; } p_err: return ret; }
/** * __fill_vb2_buffer() - fill a vb2_buffer with information provided in a * v4l2_buffer by the userspace. It also verifies that struct * v4l2_buffer has a valid number of planes. */ static int __fill_vb2_buffer(struct vb2_buffer *vb, const void *pb, struct vb2_plane *planes) { struct vb2_queue *q = vb->vb2_queue; const struct v4l2_buffer *b = pb; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); unsigned int plane; int ret; ret = __verify_length(vb, b); if (ret < 0) { dprintk(1, "plane parameters verification failed: %d\n", ret); return ret; } if (b->field == V4L2_FIELD_ALTERNATE && q->is_output) { /* * If the format's field is ALTERNATE, then the buffer's field * should be either TOP or BOTTOM, not ALTERNATE since that * makes no sense. The driver has to know whether the * buffer represents a top or a bottom field in order to * program any DMA correctly. Using ALTERNATE is wrong, since * that just says that it is either a top or a bottom field, * but not which of the two it is. */ dprintk(1, "the field is incorrectly set to ALTERNATE " "for an output buffer\n"); return -EINVAL; } vb->timestamp = 0; vbuf->sequence = 0; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { if (b->memory == VB2_MEMORY_USERPTR) { for (plane = 0; plane < vb->num_planes; ++plane) { planes[plane].m.userptr = b->m.planes[plane].m.userptr; planes[plane].length = b->m.planes[plane].length; } } if (b->memory == VB2_MEMORY_DMABUF) { for (plane = 0; plane < vb->num_planes; ++plane) { planes[plane].m.fd = b->m.planes[plane].m.fd; planes[plane].length = b->m.planes[plane].length; } } /* Fill in driver-provided information for OUTPUT types */ if (V4L2_TYPE_IS_OUTPUT(b->type)) { /* * Will have to go up to b->length when API starts * accepting variable number of planes. * * If bytesused == 0 for the output buffer, then fall * back to the full buffer size. In that case * userspace clearly never bothered to set it and * it's a safe assumption that they really meant to * use the full plane sizes. * * Some drivers, e.g. old codec drivers, use bytesused == 0 * as a way to indicate that streaming is finished. * In that case, the driver should use the * allow_zero_bytesused flag to keep old userspace * applications working. */ for (plane = 0; plane < vb->num_planes; ++plane) { struct vb2_plane *pdst = &planes[plane]; struct v4l2_plane *psrc = &b->m.planes[plane]; if (psrc->bytesused == 0) vb2_warn_zero_bytesused(vb); if (vb->vb2_queue->allow_zero_bytesused) pdst->bytesused = psrc->bytesused; else pdst->bytesused = psrc->bytesused ? psrc->bytesused : pdst->length; pdst->data_offset = psrc->data_offset; } } } else { /* * Single-planar buffers do not use planes array, * so fill in relevant v4l2_buffer struct fields instead. * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. * * If bytesused == 0 for the output buffer, then fall back * to the full buffer size as that's a sensible default. * * Some drivers, e.g. old codec drivers, use bytesused == 0 as * a way to indicate that streaming is finished. In that case, * the driver should use the allow_zero_bytesused flag to keep * old userspace applications working. */ if (b->memory == VB2_MEMORY_USERPTR) { planes[0].m.userptr = b->m.userptr; planes[0].length = b->length; } if (b->memory == VB2_MEMORY_DMABUF) { planes[0].m.fd = b->m.fd; planes[0].length = b->length; } if (V4L2_TYPE_IS_OUTPUT(b->type)) { if (b->bytesused == 0) vb2_warn_zero_bytesused(vb); if (vb->vb2_queue->allow_zero_bytesused) planes[0].bytesused = b->bytesused; else planes[0].bytesused = b->bytesused ? b->bytesused : planes[0].length; } else planes[0].bytesused = 0; } /* Zero flags that the vb2 core handles */ vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) { /* * Non-COPY timestamps and non-OUTPUT queues will get * their timestamp and timestamp source flags from the * queue. */ vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; } if (V4L2_TYPE_IS_OUTPUT(b->type)) { /* * For output buffers mask out the timecode flag: * this will be handled later in vb2_internal_qbuf(). * The 'field' is valid metadata for this output buffer * and so that needs to be copied here. */ vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE; vbuf->field = b->field; } else { /* Zero any output buffer flags as this is a capture buffer */ vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS; } return 0; }
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; }