Commit | Line | Data |
---|---|---|
0a7b514f LA |
1 | /* |
2 | * Video4Linux2 grab interface | |
406792e7 DB |
3 | * Copyright (c) 2000,2001 Fabrice Bellard |
4 | * Copyright (c) 2006 Luca Abeni | |
0a7b514f LA |
5 | * |
6 | * Part of this file is based on the V4L2 video capture example | |
7 | * (http://v4l2spec.bytesex.org/v4l2spec/capture.c) | |
8 | * | |
9 | * Thanks to Michael Niedermayer for providing the mapping between | |
716d413c | 10 | * V4L2_PIX_FMT_* and AV_PIX_FMT_* |
0a7b514f LA |
11 | * |
12 | * | |
2912e87a | 13 | * This file is part of Libav. |
b78e7197 | 14 | * |
2912e87a | 15 | * Libav is free software; you can redistribute it and/or |
0a7b514f LA |
16 | * modify it under the terms of the GNU Lesser General Public |
17 | * License as published by the Free Software Foundation; either | |
b78e7197 | 18 | * version 2.1 of the License, or (at your option) any later version. |
0a7b514f | 19 | * |
2912e87a | 20 | * Libav is distributed in the hope that it will be useful, |
0a7b514f LA |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
23 | * Lesser General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU Lesser General Public | |
2912e87a | 26 | * License along with Libav; if not, write to the Free Software |
0a7b514f LA |
27 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
28 | */ | |
b0067549 | 29 | |
e9d4b8a5 | 30 | #undef __STRICT_ANSI__ //workaround due to broken kernel headers |
b0067549 | 31 | #include "config.h" |
245976da | 32 | #include "libavformat/avformat.h" |
c3f9ebf7 | 33 | #include "libavformat/internal.h" |
0a7b514f LA |
34 | #include <unistd.h> |
35 | #include <fcntl.h> | |
36 | #include <sys/ioctl.h> | |
37 | #include <sys/mman.h> | |
38 | #include <sys/time.h> | |
0efd48df | 39 | #include <poll.h> |
b250f9c6 | 40 | #if HAVE_SYS_VIDEOIO_H |
1b6e8b73 B |
41 | #include <sys/videoio.h> |
42 | #else | |
f743a062 | 43 | #include <linux/videodev2.h> |
1b6e8b73 | 44 | #endif |
1afddbe5 AK |
45 | #include "libavutil/atomic.h" |
46 | #include "libavutil/avassert.h" | |
737eb597 | 47 | #include "libavutil/imgutils.h" |
7950e519 | 48 | #include "libavutil/internal.h" |
b3da2692 AK |
49 | #include "libavutil/log.h" |
50 | #include "libavutil/opt.h" | |
8fe7b644 | 51 | #include "libavutil/parseutils.h" |
d576bbf3 | 52 | #include "libavutil/pixdesc.h" |
bb3244de | 53 | #include "libavutil/avstring.h" |
0efd48df | 54 | #include "libavutil/mathematics.h" |
0a7b514f LA |
55 | |
56 | static const int desired_video_buffers = 256; | |
57 | ||
a6a4793d LB |
58 | #define V4L_ALLFORMATS 3 |
59 | #define V4L_RAWFORMATS 1 | |
60 | #define V4L_COMPFORMATS 2 | |
61 | ||
0a7b514f | 62 | struct video_data { |
b3da2692 | 63 | AVClass *class; |
0a7b514f LA |
64 | int fd; |
65 | int frame_format; /* V4L2_PIX_FMT_* */ | |
0a7b514f | 66 | int width, height; |
0a7b514f | 67 | int frame_size; |
0efd48df | 68 | int timeout; |
af7123b2 | 69 | int interlaced; |
0a7b514f LA |
70 | int top_field_first; |
71 | ||
72 | int buffers; | |
1afddbe5 | 73 | volatile int buffers_queued; |
0a7b514f LA |
74 | void **buf_start; |
75 | unsigned int *buf_len; | |
b3da2692 | 76 | char *standard; |
a02fd06a | 77 | int channel; |
a6a4793d LB |
78 | char *video_size; /**< String describing video size, |
79 | set by a private option. */ | |
d576bbf3 | 80 | char *pixel_format; /**< Set by a private option. */ |
a6a4793d | 81 | int list_format; /**< Set by a private option. */ |
c21324ee | 82 | char *framerate; /**< Set by a private option. */ |
0a7b514f LA |
83 | }; |
84 | ||
41536a60 | 85 | struct buff_data { |
1afddbe5 | 86 | struct video_data *s; |
41536a60 LA |
87 | int index; |
88 | int fd; | |
89 | }; | |
90 | ||
0a7b514f | 91 | struct fmt_map { |
716d413c | 92 | enum AVPixelFormat ff_fmt; |
36ef5369 | 93 | enum AVCodecID codec_id; |
9202218e | 94 | uint32_t v4l2_fmt; |
0a7b514f LA |
95 | }; |
96 | ||
97 | static struct fmt_map fmt_conversion_table[] = { | |
01058893 | 98 | //ff_fmt codec_id v4l2_fmt |
716d413c AK |
99 | { AV_PIX_FMT_YUV420P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV420 }, |
100 | { AV_PIX_FMT_YUV422P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV422P }, | |
101 | { AV_PIX_FMT_YUYV422, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUYV }, | |
102 | { AV_PIX_FMT_UYVY422, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_UYVY }, | |
103 | { AV_PIX_FMT_YUV411P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV411P }, | |
104 | { AV_PIX_FMT_YUV410P, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_YUV410 }, | |
105 | { AV_PIX_FMT_RGB555, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB555 }, | |
106 | { AV_PIX_FMT_RGB565, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB565 }, | |
107 | { AV_PIX_FMT_BGR24, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_BGR24 }, | |
108 | { AV_PIX_FMT_RGB24, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_RGB24 }, | |
109 | { AV_PIX_FMT_BGRA, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_BGR32 }, | |
110 | { AV_PIX_FMT_GRAY8, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_GREY }, | |
111 | { AV_PIX_FMT_NV12, AV_CODEC_ID_RAWVIDEO, V4L2_PIX_FMT_NV12 }, | |
112 | { AV_PIX_FMT_NONE, AV_CODEC_ID_MJPEG, V4L2_PIX_FMT_MJPEG }, | |
113 | { AV_PIX_FMT_NONE, AV_CODEC_ID_MJPEG, V4L2_PIX_FMT_JPEG }, | |
0a7b514f LA |
114 | }; |
115 | ||
eb89b4fc | 116 | static int device_open(AVFormatContext *ctx) |
0a7b514f LA |
117 | { |
118 | struct v4l2_capability cap; | |
119 | int fd; | |
50f85218 | 120 | int res, err; |
653387d8 | 121 | int flags = O_RDWR; |
a1a25988 | 122 | char errbuf[128]; |
0a7b514f | 123 | |
653387d8 LA |
124 | if (ctx->flags & AVFMT_FLAG_NONBLOCK) { |
125 | flags |= O_NONBLOCK; | |
126 | } | |
a896d7f4 | 127 | |
71bf6b41 | 128 | fd = avpriv_open(ctx->filename, flags); |
0a7b514f | 129 | if (fd < 0) { |
a1a25988 TM |
130 | err = AVERROR(errno); |
131 | av_strerror(err, errbuf, sizeof(errbuf)); | |
eb89b4fc | 132 | |
c7238c72 | 133 | av_log(ctx, AV_LOG_ERROR, "Cannot open video device %s : %s\n", |
a1a25988 | 134 | ctx->filename, errbuf); |
0a7b514f | 135 | |
a1a25988 | 136 | return err; |
0a7b514f LA |
137 | } |
138 | ||
139 | res = ioctl(fd, VIDIOC_QUERYCAP, &cap); | |
140 | if (res < 0) { | |
09f25533 LB |
141 | err = AVERROR(errno); |
142 | av_strerror(err, errbuf, sizeof(errbuf)); | |
c7238c72 | 143 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QUERYCAP): %s\n", |
a1a25988 | 144 | errbuf); |
0a7b514f | 145 | |
eb89b4fc | 146 | goto fail; |
0a7b514f | 147 | } |
a896d7f4 | 148 | |
eb89b4fc LB |
149 | av_log(ctx, AV_LOG_VERBOSE, "[%d]Capabilities: %x\n", |
150 | fd, cap.capabilities); | |
151 | ||
152 | if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { | |
153 | av_log(ctx, AV_LOG_ERROR, "Not a video capture device.\n"); | |
09f25533 | 154 | err = AVERROR(ENODEV); |
0a7b514f | 155 | |
eb89b4fc | 156 | goto fail; |
0a7b514f | 157 | } |
a896d7f4 | 158 | |
eb89b4fc LB |
159 | if (!(cap.capabilities & V4L2_CAP_STREAMING)) { |
160 | av_log(ctx, AV_LOG_ERROR, | |
161 | "The device does not support the streaming I/O method.\n"); | |
09f25533 | 162 | err = AVERROR(ENOSYS); |
eb89b4fc LB |
163 | |
164 | goto fail; | |
165 | } | |
0a7b514f LA |
166 | |
167 | return fd; | |
eb89b4fc LB |
168 | |
169 | fail: | |
170 | close(fd); | |
09f25533 | 171 | return err; |
0a7b514f LA |
172 | } |
173 | ||
a896d7f4 LB |
174 | static int device_init(AVFormatContext *ctx, int *width, int *height, |
175 | uint32_t pix_fmt) | |
0a7b514f | 176 | { |
c7238c72 LA |
177 | struct video_data *s = ctx->priv_data; |
178 | int fd = s->fd; | |
b6db3859 | 179 | struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE }; |
a896d7f4 LB |
180 | struct v4l2_pix_format *pix = &fmt.fmt.pix; |
181 | ||
24300af4 | 182 | int res; |
0a7b514f | 183 | |
a896d7f4 LB |
184 | pix->width = *width; |
185 | pix->height = *height; | |
186 | pix->pixelformat = pix_fmt; | |
187 | pix->field = V4L2_FIELD_ANY; | |
188 | ||
24300af4 | 189 | res = ioctl(fd, VIDIOC_S_FMT, &fmt); |
a896d7f4 | 190 | |
24300af4 | 191 | if ((*width != fmt.fmt.pix.width) || (*height != fmt.fmt.pix.height)) { |
a896d7f4 LB |
192 | av_log(ctx, AV_LOG_INFO, |
193 | "The V4L2 driver changed the video from %dx%d to %dx%d\n", | |
194 | *width, *height, fmt.fmt.pix.width, fmt.fmt.pix.height); | |
24300af4 LA |
195 | *width = fmt.fmt.pix.width; |
196 | *height = fmt.fmt.pix.height; | |
197 | } | |
198 | ||
3d0d9a5e | 199 | if (pix_fmt != fmt.fmt.pix.pixelformat) { |
a896d7f4 LB |
200 | av_log(ctx, AV_LOG_DEBUG, |
201 | "The V4L2 driver changed the pixel format " | |
202 | "from 0x%08X to 0x%08X\n", | |
203 | pix_fmt, fmt.fmt.pix.pixelformat); | |
3d0d9a5e JR |
204 | res = -1; |
205 | } | |
206 | ||
af7123b2 LB |
207 | if (fmt.fmt.pix.field == V4L2_FIELD_INTERLACED) { |
208 | av_log(ctx, AV_LOG_DEBUG, "The V4L2 driver using the interlaced mode"); | |
209 | s->interlaced = 1; | |
210 | } | |
211 | ||
24300af4 | 212 | return res; |
0a7b514f LA |
213 | } |
214 | ||
215 | static int first_field(int fd) | |
216 | { | |
217 | int res; | |
218 | v4l2_std_id std; | |
219 | ||
220 | res = ioctl(fd, VIDIOC_G_STD, &std); | |
221 | if (res < 0) { | |
222 | return 0; | |
223 | } | |
224 | if (std & V4L2_STD_NTSC) { | |
225 | return 0; | |
226 | } | |
227 | ||
228 | return 1; | |
229 | } | |
230 | ||
716d413c | 231 | static uint32_t fmt_ff2v4l(enum AVPixelFormat pix_fmt, enum AVCodecID codec_id) |
0a7b514f LA |
232 | { |
233 | int i; | |
234 | ||
37d3e066 | 235 | for (i = 0; i < FF_ARRAY_ELEMS(fmt_conversion_table); i++) { |
36ef5369 | 236 | if ((codec_id == AV_CODEC_ID_NONE || |
3db77ccf | 237 | fmt_conversion_table[i].codec_id == codec_id) && |
716d413c | 238 | (pix_fmt == AV_PIX_FMT_NONE || |
3db77ccf | 239 | fmt_conversion_table[i].ff_fmt == pix_fmt)) { |
0a7b514f LA |
240 | return fmt_conversion_table[i].v4l2_fmt; |
241 | } | |
242 | } | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
716d413c | 247 | static enum AVPixelFormat fmt_v4l2ff(uint32_t v4l2_fmt, enum AVCodecID codec_id) |
0a7b514f LA |
248 | { |
249 | int i; | |
250 | ||
37d3e066 | 251 | for (i = 0; i < FF_ARRAY_ELEMS(fmt_conversion_table); i++) { |
3db77ccf LA |
252 | if (fmt_conversion_table[i].v4l2_fmt == v4l2_fmt && |
253 | fmt_conversion_table[i].codec_id == codec_id) { | |
0a7b514f LA |
254 | return fmt_conversion_table[i].ff_fmt; |
255 | } | |
256 | } | |
257 | ||
716d413c | 258 | return AV_PIX_FMT_NONE; |
0a7b514f LA |
259 | } |
260 | ||
36ef5369 | 261 | static enum AVCodecID fmt_v4l2codec(uint32_t v4l2_fmt) |
3db77ccf LA |
262 | { |
263 | int i; | |
264 | ||
265 | for (i = 0; i < FF_ARRAY_ELEMS(fmt_conversion_table); i++) { | |
266 | if (fmt_conversion_table[i].v4l2_fmt == v4l2_fmt) { | |
267 | return fmt_conversion_table[i].codec_id; | |
268 | } | |
269 | } | |
270 | ||
36ef5369 | 271 | return AV_CODEC_ID_NONE; |
3db77ccf LA |
272 | } |
273 | ||
a6a4793d LB |
274 | #if HAVE_STRUCT_V4L2_FRMIVALENUM_DISCRETE |
275 | static void list_framesizes(AVFormatContext *ctx, int fd, uint32_t pixelformat) | |
276 | { | |
277 | struct v4l2_frmsizeenum vfse = { .pixel_format = pixelformat }; | |
278 | ||
279 | while(!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) { | |
280 | switch (vfse.type) { | |
281 | case V4L2_FRMSIZE_TYPE_DISCRETE: | |
282 | av_log(ctx, AV_LOG_INFO, " %ux%u", | |
283 | vfse.discrete.width, vfse.discrete.height); | |
284 | break; | |
285 | case V4L2_FRMSIZE_TYPE_CONTINUOUS: | |
286 | case V4L2_FRMSIZE_TYPE_STEPWISE: | |
287 | av_log(ctx, AV_LOG_INFO, " {%u-%u, %u}x{%u-%u, %u}", | |
288 | vfse.stepwise.min_width, | |
289 | vfse.stepwise.max_width, | |
290 | vfse.stepwise.step_width, | |
291 | vfse.stepwise.min_height, | |
292 | vfse.stepwise.max_height, | |
293 | vfse.stepwise.step_height); | |
294 | } | |
295 | vfse.index++; | |
296 | } | |
297 | } | |
298 | #endif | |
299 | ||
300 | static void list_formats(AVFormatContext *ctx, int fd, int type) | |
301 | { | |
302 | struct v4l2_fmtdesc vfd = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE }; | |
303 | ||
304 | while(!ioctl(fd, VIDIOC_ENUM_FMT, &vfd)) { | |
36ef5369 | 305 | enum AVCodecID codec_id = fmt_v4l2codec(vfd.pixelformat); |
716d413c | 306 | enum AVPixelFormat pix_fmt = fmt_v4l2ff(vfd.pixelformat, codec_id); |
a6a4793d LB |
307 | |
308 | vfd.index++; | |
309 | ||
310 | if (!(vfd.flags & V4L2_FMT_FLAG_COMPRESSED) && | |
311 | type & V4L_RAWFORMATS) { | |
312 | const char *fmt_name = av_get_pix_fmt_name(pix_fmt); | |
313 | av_log(ctx, AV_LOG_INFO, "R : %9s : %20s :", | |
314 | fmt_name ? fmt_name : "Unsupported", | |
315 | vfd.description); | |
316 | } else if (vfd.flags & V4L2_FMT_FLAG_COMPRESSED && | |
317 | type & V4L_COMPFORMATS) { | |
619d5e7d | 318 | const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id); |
a6a4793d | 319 | av_log(ctx, AV_LOG_INFO, "C : %9s : %20s :", |
619d5e7d | 320 | desc ? desc->name : "Unsupported", |
a6a4793d LB |
321 | vfd.description); |
322 | } else { | |
323 | continue; | |
324 | } | |
325 | ||
f13a9ca9 | 326 | #ifdef V4L2_FMT_FLAG_EMULATED |
a6a4793d LB |
327 | if (vfd.flags & V4L2_FMT_FLAG_EMULATED) { |
328 | av_log(ctx, AV_LOG_WARNING, "%s", "Emulated"); | |
329 | continue; | |
330 | } | |
f13a9ca9 | 331 | #endif |
a6a4793d LB |
332 | #if HAVE_STRUCT_V4L2_FRMIVALENUM_DISCRETE |
333 | list_framesizes(ctx, fd, vfd.pixelformat); | |
334 | #endif | |
335 | av_log(ctx, AV_LOG_INFO, "\n"); | |
336 | } | |
337 | } | |
338 | ||
c7238c72 | 339 | static int mmap_init(AVFormatContext *ctx) |
0a7b514f | 340 | { |
0a7b514f | 341 | int i, res; |
b6db3859 LB |
342 | struct video_data *s = ctx->priv_data; |
343 | struct v4l2_requestbuffers req = { | |
344 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
345 | .count = desired_video_buffers, | |
346 | .memory = V4L2_MEMORY_MMAP | |
347 | }; | |
0a7b514f | 348 | |
680861ca | 349 | res = ioctl(s->fd, VIDIOC_REQBUFS, &req); |
0a7b514f | 350 | if (res < 0) { |
b776113e MS |
351 | res = AVERROR(errno); |
352 | if (res == AVERROR(EINVAL)) { | |
c7238c72 | 353 | av_log(ctx, AV_LOG_ERROR, "Device does not support mmap\n"); |
0a7b514f | 354 | } else { |
c7238c72 | 355 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_REQBUFS)\n"); |
0a7b514f LA |
356 | } |
357 | ||
b776113e | 358 | return res; |
0a7b514f LA |
359 | } |
360 | ||
361 | if (req.count < 2) { | |
c7238c72 | 362 | av_log(ctx, AV_LOG_ERROR, "Insufficient buffer memory\n"); |
0a7b514f | 363 | |
c57a8fef | 364 | return AVERROR(ENOMEM); |
0a7b514f LA |
365 | } |
366 | s->buffers = req.count; | |
367 | s->buf_start = av_malloc(sizeof(void *) * s->buffers); | |
f929ab05 | 368 | if (!s->buf_start) { |
c7238c72 | 369 | av_log(ctx, AV_LOG_ERROR, "Cannot allocate buffer pointers\n"); |
0a7b514f | 370 | |
c57a8fef | 371 | return AVERROR(ENOMEM); |
0a7b514f LA |
372 | } |
373 | s->buf_len = av_malloc(sizeof(unsigned int) * s->buffers); | |
f929ab05 | 374 | if (!s->buf_len) { |
c7238c72 | 375 | av_log(ctx, AV_LOG_ERROR, "Cannot allocate buffer sizes\n"); |
0a7b514f LA |
376 | av_free(s->buf_start); |
377 | ||
c57a8fef | 378 | return AVERROR(ENOMEM); |
0a7b514f LA |
379 | } |
380 | ||
381 | for (i = 0; i < req.count; i++) { | |
b6db3859 LB |
382 | struct v4l2_buffer buf = { |
383 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
384 | .index = i, | |
385 | .memory = V4L2_MEMORY_MMAP | |
386 | }; | |
0a7b514f | 387 | |
680861ca | 388 | res = ioctl(s->fd, VIDIOC_QUERYBUF, &buf); |
0a7b514f | 389 | if (res < 0) { |
09f25533 | 390 | res = AVERROR(errno); |
c7238c72 | 391 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QUERYBUF)\n"); |
0a7b514f | 392 | |
09f25533 | 393 | return res; |
0a7b514f LA |
394 | } |
395 | ||
396 | s->buf_len[i] = buf.length; | |
3db77ccf | 397 | if (s->frame_size > 0 && s->buf_len[i] < s->frame_size) { |
a896d7f4 LB |
398 | av_log(ctx, AV_LOG_ERROR, |
399 | "Buffer len [%d] = %d != %d\n", | |
400 | i, s->buf_len[i], s->frame_size); | |
0a7b514f LA |
401 | |
402 | return -1; | |
403 | } | |
a896d7f4 LB |
404 | s->buf_start[i] = mmap(NULL, buf.length, |
405 | PROT_READ | PROT_WRITE, MAP_SHARED, | |
406 | s->fd, buf.m.offset); | |
407 | ||
0a7b514f | 408 | if (s->buf_start[i] == MAP_FAILED) { |
a1a25988 | 409 | char errbuf[128]; |
09f25533 LB |
410 | res = AVERROR(errno); |
411 | av_strerror(res, errbuf, sizeof(errbuf)); | |
a1a25988 | 412 | av_log(ctx, AV_LOG_ERROR, "mmap: %s\n", errbuf); |
0a7b514f | 413 | |
09f25533 | 414 | return res; |
0a7b514f LA |
415 | } |
416 | } | |
417 | ||
418 | return 0; | |
419 | } | |
420 | ||
1afddbe5 AK |
421 | #if FF_API_DESTRUCT_PACKET |
422 | static void dummy_release_buffer(AVPacket *pkt) | |
423 | { | |
424 | av_assert0(0); | |
425 | } | |
426 | #endif | |
427 | ||
428 | static void mmap_release_buffer(void *opaque, uint8_t *data) | |
41536a60 | 429 | { |
b6db3859 | 430 | struct v4l2_buffer buf = { 0 }; |
41536a60 | 431 | int res, fd; |
1afddbe5 AK |
432 | struct buff_data *buf_descriptor = opaque; |
433 | struct video_data *s = buf_descriptor->s; | |
a1a25988 | 434 | char errbuf[128]; |
5449a787 | 435 | |
41536a60 LA |
436 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
437 | buf.memory = V4L2_MEMORY_MMAP; | |
438 | buf.index = buf_descriptor->index; | |
439 | fd = buf_descriptor->fd; | |
440 | av_free(buf_descriptor); | |
441 | ||
680861ca | 442 | res = ioctl(fd, VIDIOC_QBUF, &buf); |
a1a25988 TM |
443 | if (res < 0) { |
444 | av_strerror(AVERROR(errno), errbuf, sizeof(errbuf)); | |
a896d7f4 | 445 | av_log(NULL, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", |
a1a25988 TM |
446 | errbuf); |
447 | } | |
1afddbe5 | 448 | avpriv_atomic_int_add_and_fetch(&s->buffers_queued, 1); |
41536a60 LA |
449 | } |
450 | ||
451 | static int mmap_read_frame(AVFormatContext *ctx, AVPacket *pkt) | |
0a7b514f | 452 | { |
c7238c72 | 453 | struct video_data *s = ctx->priv_data; |
b6db3859 LB |
454 | struct v4l2_buffer buf = { |
455 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
456 | .memory = V4L2_MEMORY_MMAP | |
457 | }; | |
0efd48df | 458 | struct pollfd p = { .fd = s->fd, .events = POLLIN }; |
0a7b514f LA |
459 | int res; |
460 | ||
0efd48df LB |
461 | res = poll(&p, 1, s->timeout); |
462 | if (res < 0) | |
463 | return AVERROR(errno); | |
464 | ||
465 | if (!(p.revents & (POLLIN | POLLERR | POLLHUP))) | |
466 | return AVERROR(EAGAIN); | |
467 | ||
0a7b514f | 468 | /* FIXME: Some special treatment might be needed in case of loss of signal... */ |
653387d8 | 469 | while ((res = ioctl(s->fd, VIDIOC_DQBUF, &buf)) < 0 && (errno == EINTR)); |
0a7b514f | 470 | if (res < 0) { |
a1a25988 | 471 | char errbuf[128]; |
653387d8 LA |
472 | if (errno == EAGAIN) { |
473 | pkt->size = 0; | |
474 | ||
475 | return AVERROR(EAGAIN); | |
476 | } | |
09f25533 LB |
477 | res = AVERROR(errno); |
478 | av_strerror(res, errbuf, sizeof(errbuf)); | |
a896d7f4 | 479 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_DQBUF): %s\n", |
a1a25988 | 480 | errbuf); |
0a7b514f | 481 | |
09f25533 | 482 | return res; |
0a7b514f | 483 | } |
49dc82ee AK |
484 | |
485 | if (buf.index >= s->buffers) { | |
486 | av_log(ctx, AV_LOG_ERROR, "Invalid buffer index received.\n"); | |
487 | return AVERROR(EINVAL); | |
488 | } | |
1afddbe5 AK |
489 | avpriv_atomic_int_add_and_fetch(&s->buffers_queued, -1); |
490 | // always keep at least one buffer queued | |
491 | av_assert0(avpriv_atomic_int_get(&s->buffers_queued) >= 1); | |
49dc82ee | 492 | |
3db77ccf | 493 | if (s->frame_size > 0 && buf.bytesused != s->frame_size) { |
a896d7f4 LB |
494 | av_log(ctx, AV_LOG_ERROR, |
495 | "The v4l2 frame is %d bytes, but %d bytes are expected\n", | |
496 | buf.bytesused, s->frame_size); | |
7c7e7464 | 497 | |
c57a8fef | 498 | return AVERROR_INVALIDDATA; |
7c7e7464 LA |
499 | } |
500 | ||
0a7b514f | 501 | /* Image is at s->buff_start[buf.index] */ |
1afddbe5 | 502 | if (avpriv_atomic_int_get(&s->buffers_queued) == FFMAX(s->buffers / 8, 1)) { |
a5f88736 | 503 | /* when we start getting low on queued buffers, fall back on copying data */ |
1afddbe5 AK |
504 | res = av_new_packet(pkt, buf.bytesused); |
505 | if (res < 0) { | |
506 | av_log(ctx, AV_LOG_ERROR, "Error allocating a packet.\n"); | |
507 | return res; | |
508 | } | |
509 | memcpy(pkt->data, s->buf_start[buf.index], buf.bytesused); | |
510 | ||
680861ca | 511 | res = ioctl(s->fd, VIDIOC_QBUF, &buf); |
1afddbe5 | 512 | if (res < 0) { |
09f25533 | 513 | res = AVERROR(errno); |
1afddbe5 AK |
514 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF)\n"); |
515 | av_free_packet(pkt); | |
09f25533 | 516 | return res; |
1afddbe5 AK |
517 | } |
518 | avpriv_atomic_int_add_and_fetch(&s->buffers_queued, 1); | |
519 | } else { | |
520 | struct buff_data *buf_descriptor; | |
0a7b514f | 521 | |
1afddbe5 AK |
522 | pkt->data = s->buf_start[buf.index]; |
523 | pkt->size = buf.bytesused; | |
524 | #if FF_API_DESTRUCT_PACKET | |
7950e519 | 525 | FF_DISABLE_DEPRECATION_WARNINGS |
1afddbe5 | 526 | pkt->destruct = dummy_release_buffer; |
7950e519 | 527 | FF_ENABLE_DEPRECATION_WARNINGS |
1afddbe5 AK |
528 | #endif |
529 | ||
530 | buf_descriptor = av_malloc(sizeof(struct buff_data)); | |
f929ab05 | 531 | if (!buf_descriptor) { |
1afddbe5 AK |
532 | /* Something went wrong... Since av_malloc() failed, we cannot even |
533 | * allocate a buffer for memcpying into it | |
534 | */ | |
535 | av_log(ctx, AV_LOG_ERROR, "Failed to allocate a buffer descriptor\n"); | |
536 | res = ioctl(s->fd, VIDIOC_QBUF, &buf); | |
537 | ||
538 | return AVERROR(ENOMEM); | |
539 | } | |
540 | buf_descriptor->fd = s->fd; | |
541 | buf_descriptor->index = buf.index; | |
542 | buf_descriptor->s = s; | |
543 | ||
544 | pkt->buf = av_buffer_create(pkt->data, pkt->size, mmap_release_buffer, | |
545 | buf_descriptor, 0); | |
546 | if (!pkt->buf) { | |
547 | av_freep(&buf_descriptor); | |
548 | return AVERROR(ENOMEM); | |
549 | } | |
0a7b514f | 550 | } |
1afddbe5 | 551 | pkt->pts = buf.timestamp.tv_sec * INT64_C(1000000) + buf.timestamp.tv_usec; |
0a7b514f LA |
552 | |
553 | return s->buf_len[buf.index]; | |
554 | } | |
555 | ||
c7238c72 | 556 | static int mmap_start(AVFormatContext *ctx) |
0a7b514f | 557 | { |
c7238c72 | 558 | struct video_data *s = ctx->priv_data; |
0a7b514f | 559 | enum v4l2_buf_type type; |
a1a25988 TM |
560 | int i, res, err; |
561 | char errbuf[128]; | |
0a7b514f LA |
562 | |
563 | for (i = 0; i < s->buffers; i++) { | |
b6db3859 LB |
564 | struct v4l2_buffer buf = { |
565 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | |
566 | .index = i, | |
567 | .memory = V4L2_MEMORY_MMAP | |
568 | }; | |
0a7b514f | 569 | |
680861ca | 570 | res = ioctl(s->fd, VIDIOC_QBUF, &buf); |
0a7b514f | 571 | if (res < 0) { |
a1a25988 TM |
572 | err = AVERROR(errno); |
573 | av_strerror(err, errbuf, sizeof(errbuf)); | |
a896d7f4 | 574 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_QBUF): %s\n", |
a1a25988 | 575 | errbuf); |
0a7b514f | 576 | |
a1a25988 | 577 | return err; |
0a7b514f LA |
578 | } |
579 | } | |
1afddbe5 | 580 | s->buffers_queued = s->buffers; |
0a7b514f LA |
581 | |
582 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
680861ca | 583 | res = ioctl(s->fd, VIDIOC_STREAMON, &type); |
0a7b514f | 584 | if (res < 0) { |
a1a25988 TM |
585 | err = AVERROR(errno); |
586 | av_strerror(err, errbuf, sizeof(errbuf)); | |
a896d7f4 | 587 | av_log(ctx, AV_LOG_ERROR, "ioctl(VIDIOC_STREAMON): %s\n", |
a1a25988 | 588 | errbuf); |
0a7b514f | 589 | |
a1a25988 | 590 | return err; |
0a7b514f LA |
591 | } |
592 | ||
593 | return 0; | |
594 | } | |
595 | ||
596 | static void mmap_close(struct video_data *s) | |
597 | { | |
598 | enum v4l2_buf_type type; | |
599 | int i; | |
600 | ||
601 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
602 | /* We do not check for the result, because we could | |
603 | * not do anything about it anyway... | |
604 | */ | |
605 | ioctl(s->fd, VIDIOC_STREAMOFF, &type); | |
606 | for (i = 0; i < s->buffers; i++) { | |
607 | munmap(s->buf_start[i], s->buf_len[i]); | |
608 | } | |
609 | av_free(s->buf_start); | |
610 | av_free(s->buf_len); | |
611 | } | |
612 | ||
6e9651d1 | 613 | static int v4l2_set_parameters(AVFormatContext *s1) |
f5ad81f5 LW |
614 | { |
615 | struct video_data *s = s1->priv_data; | |
b6db3859 LB |
616 | struct v4l2_input input = { 0 }; |
617 | struct v4l2_standard standard = { 0 }; | |
5d3d238f SS |
618 | struct v4l2_streamparm streamparm = { 0 }; |
619 | struct v4l2_fract *tpf = &streamparm.parm.capture.timeperframe; | |
b6db3859 | 620 | AVRational framerate_q = { 0 }; |
c21324ee | 621 | int i, ret; |
f5ad81f5 | 622 | |
5d3d238f SS |
623 | streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
624 | ||
a896d7f4 LB |
625 | if (s->framerate && |
626 | (ret = av_parse_video_rate(&framerate_q, s->framerate)) < 0) { | |
627 | av_log(s1, AV_LOG_ERROR, "Could not parse framerate '%s'.\n", | |
628 | s->framerate); | |
c21324ee AK |
629 | return ret; |
630 | } | |
a02fd06a | 631 | |
3d2a4186 | 632 | /* set tv video input */ |
3d2a4186 AK |
633 | input.index = s->channel; |
634 | if (ioctl(s->fd, VIDIOC_ENUMINPUT, &input) < 0) { | |
635 | av_log(s1, AV_LOG_ERROR, "The V4L2 driver ioctl enum input failed:\n"); | |
636 | return AVERROR(EIO); | |
637 | } | |
f5ad81f5 | 638 | |
3d2a4186 AK |
639 | av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set input_id: %d, input: %s\n", |
640 | s->channel, input.name); | |
641 | if (ioctl(s->fd, VIDIOC_S_INPUT, &input.index) < 0) { | |
a896d7f4 LB |
642 | av_log(s1, AV_LOG_ERROR, |
643 | "The V4L2 driver ioctl set input(%d) failed\n", | |
3d2a4186 AK |
644 | s->channel); |
645 | return AVERROR(EIO); | |
646 | } | |
f5ad81f5 | 647 | |
b3da2692 | 648 | if (s->standard) { |
e4dd03f3 | 649 | av_log(s1, AV_LOG_DEBUG, "The V4L2 driver set standard: %s\n", |
b3da2692 | 650 | s->standard); |
e4dd03f3 | 651 | /* set tv standard */ |
e4dd03f3 LA |
652 | for(i=0;;i++) { |
653 | standard.index = i; | |
654 | if (ioctl(s->fd, VIDIOC_ENUMSTD, &standard) < 0) { | |
a896d7f4 LB |
655 | av_log(s1, AV_LOG_ERROR, |
656 | "The V4L2 driver ioctl set standard(%s) failed\n", | |
b3da2692 | 657 | s->standard); |
6f3e0b21 | 658 | return AVERROR(EIO); |
e4dd03f3 LA |
659 | } |
660 | ||
bb3244de | 661 | if (!av_strcasecmp(standard.name, s->standard)) { |
e4dd03f3 LA |
662 | break; |
663 | } | |
664 | } | |
665 | ||
a896d7f4 LB |
666 | av_log(s1, AV_LOG_DEBUG, |
667 | "The V4L2 driver set standard: %s, id: %"PRIu64"\n", | |
b3da2692 | 668 | s->standard, (uint64_t)standard.id); |
e4dd03f3 | 669 | if (ioctl(s->fd, VIDIOC_S_STD, &standard.id) < 0) { |
a896d7f4 LB |
670 | av_log(s1, AV_LOG_ERROR, |
671 | "The V4L2 driver ioctl set standard(%s) failed\n", | |
b3da2692 | 672 | s->standard); |
6f3e0b21 | 673 | return AVERROR(EIO); |
f5ad81f5 | 674 | } |
0d6c0732 | 675 | } |
f5ad81f5 | 676 | |
e60068ba | 677 | if (framerate_q.num && framerate_q.den) { |
70f77361 | 678 | av_log(s1, AV_LOG_DEBUG, "Setting time per frame to %d/%d\n", |
e60068ba SS |
679 | framerate_q.den, framerate_q.num); |
680 | tpf->numerator = framerate_q.den; | |
681 | tpf->denominator = framerate_q.num; | |
a896d7f4 | 682 | |
70f77361 JMG |
683 | if (ioctl(s->fd, VIDIOC_S_PARM, &streamparm) != 0) { |
684 | av_log(s1, AV_LOG_ERROR, | |
685 | "ioctl set time per frame(%d/%d) failed\n", | |
e60068ba | 686 | framerate_q.den, framerate_q.num); |
70f77361 JMG |
687 | return AVERROR(EIO); |
688 | } | |
689 | ||
e60068ba SS |
690 | if (framerate_q.num != tpf->denominator || |
691 | framerate_q.den != tpf->numerator) { | |
70f77361 | 692 | av_log(s1, AV_LOG_INFO, |
a896d7f4 LB |
693 | "The driver changed the time per frame from " |
694 | "%d/%d to %d/%d\n", | |
e60068ba | 695 | framerate_q.den, framerate_q.num, |
70f77361 | 696 | tpf->numerator, tpf->denominator); |
70f77361 | 697 | } |
8621a37d | 698 | } else { |
8621a37d | 699 | if (ioctl(s->fd, VIDIOC_G_PARM, &streamparm) != 0) { |
a1a25988 | 700 | char errbuf[128]; |
09f25533 LB |
701 | ret = AVERROR(errno); |
702 | av_strerror(ret, errbuf, sizeof(errbuf)); | |
a896d7f4 | 703 | av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_PARM): %s\n", |
a1a25988 | 704 | errbuf); |
09f25533 | 705 | return ret; |
8621a37d | 706 | } |
70f77361 | 707 | } |
838b849e AK |
708 | s1->streams[0]->avg_frame_rate.num = tpf->denominator; |
709 | s1->streams[0]->avg_frame_rate.den = tpf->numerator; | |
70f77361 | 710 | |
0efd48df | 711 | s->timeout = 100 + |
838b849e | 712 | av_rescale_q(1, s1->streams[0]->avg_frame_rate, |
0efd48df LB |
713 | (AVRational){1, 1000}); |
714 | ||
f5ad81f5 LW |
715 | return 0; |
716 | } | |
717 | ||
8040c3b2 | 718 | static uint32_t device_try_init(AVFormatContext *s1, |
716d413c | 719 | enum AVPixelFormat pix_fmt, |
8040c3b2 RT |
720 | int *width, |
721 | int *height, | |
36ef5369 | 722 | enum AVCodecID *codec_id) |
3db77ccf | 723 | { |
d576bbf3 | 724 | uint32_t desired_format = fmt_ff2v4l(pix_fmt, s1->video_codec_id); |
3db77ccf LA |
725 | |
726 | if (desired_format == 0 || | |
727 | device_init(s1, width, height, desired_format) < 0) { | |
728 | int i; | |
729 | ||
730 | desired_format = 0; | |
731 | for (i = 0; i<FF_ARRAY_ELEMS(fmt_conversion_table); i++) { | |
36ef5369 | 732 | if (s1->video_codec_id == AV_CODEC_ID_NONE || |
90d0379f | 733 | fmt_conversion_table[i].codec_id == s1->video_codec_id) { |
3db77ccf LA |
734 | desired_format = fmt_conversion_table[i].v4l2_fmt; |
735 | if (device_init(s1, width, height, desired_format) >= 0) { | |
736 | break; | |
737 | } | |
738 | desired_format = 0; | |
739 | } | |
740 | } | |
741 | } | |
a896d7f4 | 742 | |
3db77ccf LA |
743 | if (desired_format != 0) { |
744 | *codec_id = fmt_v4l2codec(desired_format); | |
36ef5369 | 745 | assert(*codec_id != AV_CODEC_ID_NONE); |
3db77ccf LA |
746 | } |
747 | ||
748 | return desired_format; | |
749 | } | |
750 | ||
6e9651d1 | 751 | static int v4l2_read_header(AVFormatContext *s1) |
0a7b514f LA |
752 | { |
753 | struct video_data *s = s1->priv_data; | |
754 | AVStream *st; | |
82b5aa0a | 755 | int res = 0; |
eb89b4fc | 756 | uint32_t desired_format; |
36ef5369 | 757 | enum AVCodecID codec_id; |
716d413c | 758 | enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; |
0a7b514f | 759 | |
3b3bbdd3 | 760 | st = avformat_new_stream(s1, NULL); |
246da0b1 AK |
761 | if (!st) |
762 | return AVERROR(ENOMEM); | |
a896d7f4 | 763 | |
eb89b4fc | 764 | s->fd = device_open(s1); |
246da0b1 AK |
765 | if (s->fd < 0) |
766 | return s->fd; | |
eb89b4fc | 767 | |
a6a4793d LB |
768 | if (s->list_format) { |
769 | list_formats(s1, s->fd, s->list_format); | |
246da0b1 | 770 | return AVERROR_EXIT; |
a6a4793d LB |
771 | } |
772 | ||
c3f9ebf7 | 773 | avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ |
0a7b514f | 774 | |
a896d7f4 LB |
775 | if (s->video_size && |
776 | (res = av_parse_video_size(&s->width, &s->height, s->video_size)) < 0) { | |
777 | av_log(s1, AV_LOG_ERROR, "Could not parse video size '%s'.\n", | |
778 | s->video_size); | |
246da0b1 | 779 | return res; |
8fe7b644 | 780 | } |
a6a4793d LB |
781 | |
782 | if (s->pixel_format) { | |
b8c310cb LB |
783 | AVCodec *codec = avcodec_find_decoder_by_name(s->pixel_format); |
784 | ||
785 | if (codec) | |
786 | s1->video_codec_id = codec->id; | |
a6a4793d LB |
787 | |
788 | pix_fmt = av_get_pix_fmt(s->pixel_format); | |
789 | ||
716d413c | 790 | if (pix_fmt == AV_PIX_FMT_NONE && !codec) { |
b8c310cb | 791 | av_log(s1, AV_LOG_ERROR, "No such input format: %s.\n", |
a6a4793d LB |
792 | s->pixel_format); |
793 | ||
246da0b1 | 794 | return AVERROR(EINVAL); |
a6a4793d | 795 | } |
d576bbf3 | 796 | } |
0a7b514f | 797 | |
932d775f SS |
798 | if (!s->width && !s->height) { |
799 | struct v4l2_format fmt; | |
800 | ||
a896d7f4 LB |
801 | av_log(s1, AV_LOG_VERBOSE, |
802 | "Querying the device for the current frame size\n"); | |
932d775f SS |
803 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
804 | if (ioctl(s->fd, VIDIOC_G_FMT, &fmt) < 0) { | |
a1a25988 | 805 | char errbuf[128]; |
09f25533 LB |
806 | res = AVERROR(errno); |
807 | av_strerror(res, errbuf, sizeof(errbuf)); | |
a896d7f4 | 808 | av_log(s1, AV_LOG_ERROR, "ioctl(VIDIOC_G_FMT): %s\n", |
a1a25988 | 809 | errbuf); |
09f25533 | 810 | return res; |
932d775f | 811 | } |
a896d7f4 | 812 | |
932d775f SS |
813 | s->width = fmt.fmt.pix.width; |
814 | s->height = fmt.fmt.pix.height; | |
a896d7f4 LB |
815 | av_log(s1, AV_LOG_VERBOSE, |
816 | "Setting frame size to %dx%d\n", s->width, s->height); | |
932d775f SS |
817 | } |
818 | ||
a896d7f4 LB |
819 | desired_format = device_try_init(s1, pix_fmt, &s->width, &s->height, |
820 | &codec_id); | |
0a7b514f | 821 | if (desired_format == 0) { |
3db77ccf | 822 | av_log(s1, AV_LOG_ERROR, "Cannot find a proper format for " |
fefa67d5 | 823 | "codec_id %d, pix_fmt %d.\n", s1->video_codec_id, pix_fmt); |
0a7b514f | 824 | close(s->fd); |
0a7b514f | 825 | |
246da0b1 | 826 | return AVERROR(EIO); |
0a7b514f | 827 | } |
a896d7f4 | 828 | |
82b5aa0a | 829 | if ((res = av_image_check_size(s->width, s->height, 0, s1) < 0)) |
246da0b1 | 830 | return res; |
a896d7f4 | 831 | |
0a7b514f LA |
832 | s->frame_format = desired_format; |
833 | ||
6e9651d1 | 834 | if ((res = v4l2_set_parameters(s1) < 0)) |
246da0b1 | 835 | return res; |
f5ad81f5 | 836 | |
3db77ccf | 837 | st->codec->pix_fmt = fmt_v4l2ff(desired_format, codec_id); |
a896d7f4 LB |
838 | s->frame_size = |
839 | avpicture_get_size(st->codec->pix_fmt, s->width, s->height); | |
840 | ||
eb89b4fc LB |
841 | if ((res = mmap_init(s1)) || |
842 | (res = mmap_start(s1)) < 0) { | |
0a7b514f | 843 | close(s->fd); |
246da0b1 | 844 | return res; |
0a7b514f | 845 | } |
eb89b4fc | 846 | |
0a7b514f LA |
847 | s->top_field_first = first_field(s->fd); |
848 | ||
72415b2a | 849 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
3db77ccf | 850 | st->codec->codec_id = codec_id; |
36ef5369 | 851 | if (codec_id == AV_CODEC_ID_RAWVIDEO) |
cd2bbad3 LB |
852 | st->codec->codec_tag = |
853 | avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt); | |
18225025 SS |
854 | st->codec->width = s->width; |
855 | st->codec->height = s->height; | |
838b849e | 856 | st->codec->bit_rate = s->frame_size * av_q2d(st->avg_frame_rate) * 8; |
0a7b514f | 857 | |
246da0b1 | 858 | return 0; |
0a7b514f LA |
859 | } |
860 | ||
861 | static int v4l2_read_packet(AVFormatContext *s1, AVPacket *pkt) | |
862 | { | |
863 | struct video_data *s = s1->priv_data; | |
a896d7f4 | 864 | AVFrame *frame = s1->streams[0]->codec->coded_frame; |
0a7b514f LA |
865 | int res; |
866 | ||
246007d3 LB |
867 | av_init_packet(pkt); |
868 | if ((res = mmap_read_frame(s1, pkt)) < 0) { | |
411f5c6a | 869 | return res; |
0a7b514f LA |
870 | } |
871 | ||
a896d7f4 LB |
872 | if (frame && s->interlaced) { |
873 | frame->interlaced_frame = 1; | |
874 | frame->top_field_first = s->top_field_first; | |
0a7b514f LA |
875 | } |
876 | ||
158aa9f2 | 877 | return pkt->size; |
0a7b514f LA |
878 | } |
879 | ||
880 | static int v4l2_read_close(AVFormatContext *s1) | |
881 | { | |
882 | struct video_data *s = s1->priv_data; | |
883 | ||
1afddbe5 AK |
884 | if (avpriv_atomic_int_get(&s->buffers_queued) != s->buffers) |
885 | av_log(s1, AV_LOG_WARNING, "Some buffers are still owned by the caller on " | |
886 | "close.\n"); | |
887 | ||
246007d3 | 888 | mmap_close(s); |
0a7b514f LA |
889 | |
890 | close(s->fd); | |
891 | return 0; | |
892 | } | |
893 | ||
8fe7b644 AK |
894 | #define OFFSET(x) offsetof(struct video_data, x) |
895 | #define DEC AV_OPT_FLAG_DECODING_PARAM | |
b3da2692 | 896 | static const AVOption options[] = { |
21aa6ae4 | 897 | { "standard", "TV standard, used only by analog frame grabber", OFFSET(standard), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC }, |
e6153f17 | 898 | { "channel", "TV channel, used only by frame grabber", OFFSET(channel), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, |
21aa6ae4 | 899 | { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, |
b8c310cb LB |
900 | { "pixel_format", "Preferred pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, |
901 | { "input_format", "Preferred pixel format (for raw video) or codec name", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, | |
21aa6ae4 | 902 | { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, |
e6153f17 | 903 | { "list_formats", "List available formats and exit", OFFSET(list_format), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC, "list_formats" }, |
124134e4 MS |
904 | { "all", "Show all available formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_ALLFORMATS }, 0, INT_MAX, DEC, "list_formats" }, |
905 | { "raw", "Show only non-compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_RAWFORMATS }, 0, INT_MAX, DEC, "list_formats" }, | |
906 | { "compressed", "Show only compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_COMPFORMATS }, 0, INT_MAX, DEC, "list_formats" }, | |
b3da2692 AK |
907 | { NULL }, |
908 | }; | |
909 | ||
910 | static const AVClass v4l2_class = { | |
911 | .class_name = "V4L2 indev", | |
912 | .item_name = av_default_item_name, | |
913 | .option = options, | |
914 | .version = LIBAVUTIL_VERSION_INT, | |
915 | }; | |
916 | ||
c6610a21 | 917 | AVInputFormat ff_v4l2_demuxer = { |
30b4ee79 DB |
918 | .name = "video4linux2", |
919 | .long_name = NULL_IF_CONFIG_SMALL("Video4Linux2 device grab"), | |
920 | .priv_data_size = sizeof(struct video_data), | |
921 | .read_header = v4l2_read_header, | |
922 | .read_packet = v4l2_read_packet, | |
923 | .read_close = v4l2_read_close, | |
924 | .flags = AVFMT_NOFILE, | |
925 | .priv_class = &v4l2_class, | |
0a7b514f | 926 | }; |