From 1afddbe59e96af75f1c07605afc95615569f388f Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 31 Oct 2012 08:53:18 +0100 Subject: avpacket: use AVBuffer to allow refcounting the packets. This will allow us to avoid copying the packets in many cases. This breaks ABI. --- libavdevice/v4l2.c | 90 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 23 deletions(-) (limited to 'libavdevice') diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c index d57bd755d5..9e5aa5e660 100644 --- a/libavdevice/v4l2.c +++ b/libavdevice/v4l2.c @@ -42,6 +42,8 @@ #else #include #endif +#include "libavutil/atomic.h" +#include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "libavutil/log.h" #include "libavutil/opt.h" @@ -67,6 +69,7 @@ struct video_data { int top_field_first; int buffers; + volatile int buffers_queued; void **buf_start; unsigned int *buf_len; char *standard; @@ -79,6 +82,7 @@ struct video_data { }; struct buff_data { + struct video_data *s; int index; int fd; }; @@ -405,14 +409,19 @@ static int mmap_init(AVFormatContext *ctx) return 0; } -static void mmap_release_buffer(AVPacket *pkt) +#if FF_API_DESTRUCT_PACKET +static void dummy_release_buffer(AVPacket *pkt) +{ + av_assert0(0); +} +#endif + +static void mmap_release_buffer(void *opaque, uint8_t *data) { struct v4l2_buffer buf = { 0 }; int res, fd; - struct buff_data *buf_descriptor = pkt->priv; - - if (pkt->data == NULL) - return; + struct buff_data *buf_descriptor = opaque; + struct video_data *s = buf_descriptor->s; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; @@ -424,9 +433,7 @@ static void mmap_release_buffer(AVPacket *pkt) if (res < 0) av_log(NULL, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", strerror(errno)); - - pkt->data = NULL; - pkt->size = 0; + avpriv_atomic_int_add_and_fetch(&s->buffers_queued, 1); } static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) @@ -436,7 +443,6 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP }; - struct buff_data *buf_descriptor; struct pollfd p = { .fd = s->fd, .events = POLLIN }; int res; @@ -465,6 +471,9 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) av_log(ctx, AV_LOG_ERROR, "Invalid buffer index received.\n"); return AVERROR(EINVAL); } + avpriv_atomic_int_add_and_fetch(&s->buffers_queued, -1); + // always keep at least one buffer queued + av_assert0(avpriv_atomic_int_get(&s->buffers_queued) >= 1); if (s->frame_size > 0 && buf.bytesused != s->frame_size) { av_log(ctx, AV_LOG_ERROR, @@ -475,23 +484,53 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) } /* Image is at s->buff_start[buf.index] */ - pkt->data= s->buf_start[buf.index]; - pkt->size = buf.bytesused; - pkt->pts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec; - pkt->destruct = mmap_release_buffer; - buf_descriptor = av_malloc(sizeof(struct buff_data)); - if (buf_descriptor == NULL) { - /* Something went wrong... Since av_malloc() failed, we cannot even - * allocate a buffer for memcopying into it - */ - av_log(ctx, AV_LOG_ERROR, "Failed to allocate a buffer descriptor\n"); + if (avpriv_atomic_int_get(&s->buffers_queued) == FFMAX(s->buffers / 8, 1)) { + /* when we start getting low on queued buffers, fallback to copying data */ + res = av_new_packet(pkt, buf.bytesused); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "Error allocating a packet.\n"); + return res; + } + memcpy(pkt->data, s->buf_start[buf.index], buf.bytesused); + res = ioctl(s->fd, VIDIOC_QBUF, &buf); + if (res < 0) { + av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF)\n"); + av_free_packet(pkt); + return AVERROR(errno); + } + avpriv_atomic_int_add_and_fetch(&s->buffers_queued, 1); + } else { + struct buff_data *buf_descriptor; - return AVERROR(ENOMEM); + pkt->data = s->buf_start[buf.index]; + pkt->size = buf.bytesused; +#if FF_API_DESTRUCT_PACKET + pkt->destruct = dummy_release_buffer; +#endif + + buf_descriptor = av_malloc(sizeof(struct buff_data)); + if (buf_descriptor == NULL) { + /* Something went wrong... Since av_malloc() failed, we cannot even + * allocate a buffer for memcpying into it + */ + av_log(ctx, AV_LOG_ERROR, "Failed to allocate a buffer descriptor\n"); + res = ioctl(s->fd, VIDIOC_QBUF, &buf); + + return AVERROR(ENOMEM); + } + buf_descriptor->fd = s->fd; + buf_descriptor->index = buf.index; + buf_descriptor->s = s; + + pkt->buf = av_buffer_create(pkt->data, pkt->size, mmap_release_buffer, + buf_descriptor, 0); + if (!pkt->buf) { + av_freep(&buf_descriptor); + return AVERROR(ENOMEM); + } } - buf_descriptor->fd = s->fd; - buf_descriptor->index = buf.index; - pkt->priv = buf_descriptor; + pkt->pts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec; return s->buf_len[buf.index]; } @@ -517,6 +556,7 @@ static int mmap_start(AVFormatContext *ctx) return AVERROR(errno); } } + s->buffers_queued = s->buffers; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; res = ioctl(s->fd, VIDIOC_STREAMON, &type); @@ -812,6 +852,10 @@ static int v4l2_read_close(AVFormatContext *s1) { struct video_data *s = s1->priv_data; + if (avpriv_atomic_int_get(&s->buffers_queued) != s->buffers) + av_log(s1, AV_LOG_WARNING, "Some buffers are still owned by the caller on " + "close.\n"); + mmap_close(s); close(s->fd); -- cgit v1.2.3