diff --git a/libavformat/v4l2.c b/libavformat/v4l2.c index b169066df4..5f669bd66c 100644 --- a/libavformat/v4l2.c +++ b/libavformat/v4l2.c @@ -59,6 +59,11 @@ struct video_data { unsigned int *buf_len; }; +struct buff_data { + int index; + int fd; +}; + struct fmt_map { enum PixelFormat ff_fmt; int32_t v4l2_fmt; @@ -293,10 +298,32 @@ static int read_init(AVFormatContext *ctx) return -1; } -static int mmap_read_frame(AVFormatContext *ctx, void *frame, int64_t *ts) +static void mmap_release_buffer(AVPacket *pkt) +{ + struct v4l2_buffer buf; + int res, fd; + struct buff_data *buf_descriptor = pkt->priv; + + memset(&buf, 0, sizeof(struct v4l2_buffer)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = buf_descriptor->index; + fd = buf_descriptor->fd; + av_free(buf_descriptor); + + res = ioctl (fd, VIDIOC_QBUF, &buf); + if (res < 0) { + av_log(NULL, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF)\n"); + } + pkt->data = NULL; + pkt->size = 0; +} + +static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) { struct video_data *s = ctx->priv_data; struct v4l2_buffer buf; + struct buff_data *buf_descriptor; int res; memset(&buf, 0, sizeof(struct v4l2_buffer)); @@ -319,20 +346,28 @@ static int mmap_read_frame(AVFormatContext *ctx, void *frame, int64_t *ts) } /* Image is at s->buff_start[buf.index] */ - memcpy(frame, s->buf_start[buf.index], buf.bytesused); - *ts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec; - - res = ioctl (s->fd, VIDIOC_QBUF, &buf); - if (res < 0) { - av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF)\n"); + 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"); + res = ioctl (s->fd, VIDIOC_QBUF, &buf); return -1; } + buf_descriptor->fd = s->fd; + buf_descriptor->index = buf.index; + pkt->priv = buf_descriptor; return s->buf_len[buf.index]; } -static int read_frame(AVFormatContext *ctx, void *frame, int64_t *ts) +static int read_frame(AVFormatContext *ctx, AVPacket *pkt) { return -1; } @@ -500,13 +535,14 @@ static int v4l2_read_packet(AVFormatContext *s1, AVPacket *pkt) struct video_data *s = s1->priv_data; int res; - if (av_new_packet(pkt, s->frame_size) < 0) - return AVERROR_IO; - if (s->io_method == io_mmap) { - res = mmap_read_frame(s1, pkt->data, &pkt->pts); + av_init_packet(pkt); + res = mmap_read_frame(s1, pkt); } else if (s->io_method == io_read) { - res = read_frame(s1, pkt->data, &pkt->pts); + if (av_new_packet(pkt, s->frame_size) < 0) + return AVERROR_IO; + + res = read_frame(s1, pkt); } else { return AVERROR_IO; }