|
|
|
@ -46,6 +46,7 @@ |
|
|
|
|
#endif |
|
|
|
|
#include <linux/videodev2.h> |
|
|
|
|
#endif |
|
|
|
|
#include "libavutil/atomic.h" |
|
|
|
|
#include "libavutil/avassert.h" |
|
|
|
|
#include "libavutil/imgutils.h" |
|
|
|
|
#include "libavutil/log.h" |
|
|
|
@ -109,9 +110,9 @@ struct video_data { |
|
|
|
|
int64_t last_time_m; |
|
|
|
|
|
|
|
|
|
int buffers; |
|
|
|
|
volatile int buffers_queued; |
|
|
|
|
void **buf_start; |
|
|
|
|
unsigned int *buf_len; |
|
|
|
|
int *buf_dequeued; |
|
|
|
|
char *standard; |
|
|
|
|
v4l2_std_id std_id; |
|
|
|
|
int channel; |
|
|
|
@ -122,9 +123,9 @@ struct video_data { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct buff_data { |
|
|
|
|
struct video_data *s; |
|
|
|
|
int index; |
|
|
|
|
int fd; |
|
|
|
|
int *buf_dequeued; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct fmt_map { |
|
|
|
@ -438,11 +439,6 @@ static int mmap_init(AVFormatContext *ctx) |
|
|
|
|
av_free(s->buf_start); |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
s->buf_dequeued = av_mallocz(sizeof(int) * s->buffers); |
|
|
|
|
if (s->buf_dequeued == NULL) { |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, "Cannot allocate buffer array\n"); |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i = 0; i < req.count; i++) { |
|
|
|
|
struct v4l2_buffer buf = { |
|
|
|
@ -477,40 +473,33 @@ static int mmap_init(AVFormatContext *ctx) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int enqueue_buffer(int fd, int index) |
|
|
|
|
#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) |
|
|
|
|
{ |
|
|
|
|
int res; |
|
|
|
|
struct v4l2_buffer buf = { 0 }; |
|
|
|
|
int res, fd; |
|
|
|
|
struct buff_data *buf_descriptor = opaque; |
|
|
|
|
struct video_data *s = buf_descriptor->s; |
|
|
|
|
|
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
|
|
|
|
buf.memory = V4L2_MEMORY_MMAP; |
|
|
|
|
buf.index = index; |
|
|
|
|
buf.index = buf_descriptor->index; |
|
|
|
|
fd = buf_descriptor->fd; |
|
|
|
|
av_free(buf_descriptor); |
|
|
|
|
|
|
|
|
|
if (v4l2_ioctl(fd, VIDIOC_QBUF, &buf) < 0) { |
|
|
|
|
res = AVERROR(errno); |
|
|
|
|
av_log(NULL, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", av_err2str(res)); |
|
|
|
|
return res; |
|
|
|
|
av_log(NULL, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", |
|
|
|
|
av_err2str(res)); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void mmap_release_buffer(AVPacket *pkt) |
|
|
|
|
{ |
|
|
|
|
struct buff_data *buf_descriptor = pkt->priv; |
|
|
|
|
|
|
|
|
|
if (pkt->data == NULL) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
if (buf_descriptor->index == -1) { |
|
|
|
|
av_free(pkt->data); |
|
|
|
|
} else { |
|
|
|
|
if (!enqueue_buffer(buf_descriptor->fd, buf_descriptor->index)) |
|
|
|
|
buf_descriptor->buf_dequeued[buf_descriptor->index] = 0; |
|
|
|
|
} |
|
|
|
|
av_free(buf_descriptor); |
|
|
|
|
|
|
|
|
|
pkt->data = NULL; |
|
|
|
|
pkt->size = 0; |
|
|
|
|
avpriv_atomic_int_add_and_fetch(&s->buffers_queued, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if HAVE_CLOCK_GETTIME && defined(CLOCK_MONOTONIC) |
|
|
|
@ -580,14 +569,15 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) |
|
|
|
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE, |
|
|
|
|
.memory = V4L2_MEMORY_MMAP |
|
|
|
|
}; |
|
|
|
|
struct buff_data *buf_descriptor; |
|
|
|
|
int res, i, free_buffers; |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
/* FIXME: Some special treatment might be needed in case of loss of signal... */ |
|
|
|
|
while ((res = v4l2_ioctl(s->fd, VIDIOC_DQBUF, &buf)) < 0 && (errno == EINTR)); |
|
|
|
|
if (res < 0) { |
|
|
|
|
if (errno == EAGAIN) |
|
|
|
|
if (errno == EAGAIN) { |
|
|
|
|
pkt->size = 0; |
|
|
|
|
return AVERROR(EAGAIN); |
|
|
|
|
} |
|
|
|
|
res = AVERROR(errno); |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_DQBUF): %s\n", av_err2str(res)); |
|
|
|
|
return res; |
|
|
|
@ -597,6 +587,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); |
|
|
|
|
|
|
|
|
|
/* CPIA is a compressed format and we don't know the exact number of bytes
|
|
|
|
|
* used by a frame, so set it here as the driver announces it. |
|
|
|
@ -608,50 +601,58 @@ static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) |
|
|
|
|
av_log(ctx, AV_LOG_ERROR, |
|
|
|
|
"The v4l2 frame is %d bytes, but %d bytes are expected\n", |
|
|
|
|
buf.bytesused, s->frame_size); |
|
|
|
|
enqueue_buffer(s->fd, buf.index); |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 = v4l2_ioctl(s->fd, VIDIOC_QBUF, &buf); |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
buf_descriptor->fd = s->fd; |
|
|
|
|
buf_descriptor->buf_dequeued = s->buf_dequeued; |
|
|
|
|
|
|
|
|
|
free_buffers = -1; /* start from -1 because we just dequeued a buffer */ |
|
|
|
|
for (i = 0; i < s->buffers; i++) |
|
|
|
|
if (s->buf_dequeued[i] == 0) |
|
|
|
|
free_buffers++; |
|
|
|
|
|
|
|
|
|
if (free_buffers == 0) { |
|
|
|
|
if ((res = av_new_packet(pkt, buf.bytesused)) < 0) { |
|
|
|
|
enqueue_buffer(s->fd, buf.index); |
|
|
|
|
/* Image is at s->buff_start[buf.index] */ |
|
|
|
|
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); |
|
|
|
|
enqueue_buffer(s->fd, buf.index); |
|
|
|
|
buf_descriptor->index = -1; |
|
|
|
|
|
|
|
|
|
res = v4l2_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 { |
|
|
|
|
/* Image is at s->buff_start[buf.index] */ |
|
|
|
|
pkt->data = s->buf_start[buf.index]; |
|
|
|
|
struct buff_data *buf_descriptor; |
|
|
|
|
|
|
|
|
|
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 = v4l2_ioctl(s->fd, VIDIOC_QBUF, &buf); |
|
|
|
|
|
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
buf_descriptor->fd = s->fd; |
|
|
|
|
buf_descriptor->index = buf.index; |
|
|
|
|
buf_descriptor->buf_dequeued[buf.index] = 1; |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
pkt->size = buf.bytesused; |
|
|
|
|
pkt->priv = buf_descriptor; |
|
|
|
|
pkt->destruct = mmap_release_buffer; |
|
|
|
|
pkt->pts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec; |
|
|
|
|
res = convert_timestamp(ctx, &pkt->pts); |
|
|
|
|
if (res < 0) { |
|
|
|
|
mmap_release_buffer(pkt); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
convert_timestamp(ctx, &pkt->pts); |
|
|
|
|
|
|
|
|
|
return s->buf_len[buf.index]; |
|
|
|
|
} |
|
|
|
@ -675,6 +676,7 @@ static int mmap_start(AVFormatContext *ctx) |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
s->buffers_queued = s->buffers; |
|
|
|
|
|
|
|
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
|
|
|
|
if (v4l2_ioctl(s->fd, VIDIOC_STREAMON, &type) < 0) { |
|
|
|
@ -701,7 +703,6 @@ static void mmap_close(struct video_data *s) |
|
|
|
|
} |
|
|
|
|
av_free(s->buf_start); |
|
|
|
|
av_free(s->buf_len); |
|
|
|
|
av_free(s->buf_dequeued); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int v4l2_set_parameters(AVFormatContext *s1) |
|
|
|
@ -1003,8 +1004,6 @@ static int v4l2_read_packet(AVFormatContext *s1, AVPacket *pkt) |
|
|
|
|
int res; |
|
|
|
|
|
|
|
|
|
av_init_packet(pkt); |
|
|
|
|
pkt->data = NULL; |
|
|
|
|
pkt->size = 0; |
|
|
|
|
if ((res = mmap_read_frame(s1, pkt)) < 0) { |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
@ -1021,6 +1020,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); |
|
|
|
|
|
|
|
|
|
v4l2_close(s->fd); |
|
|
|
|