]> git.sesse.net Git - ffmpeg/blob - libavcodec/v4l2_m2m.c
avcodec/v4l2_m2m: fix av_pix_fmt changing when multiple /dev/video* devices are probed
[ffmpeg] / libavcodec / v4l2_m2m.c
1 /*
2  * V4L mem2mem
3  *
4  * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
5  * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org>
6  *
7  * This file is part of FFmpeg.
8  *
9  * FFmpeg is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * FFmpeg is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with FFmpeg; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  */
23
24 #include <linux/videodev2.h>
25 #include <sys/ioctl.h>
26 #include <sys/mman.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <fcntl.h>
30 #include "libavcodec/avcodec.h"
31 #include "libavcodec/internal.h"
32 #include "libavutil/pixdesc.h"
33 #include "libavutil/imgutils.h"
34 #include "libavutil/pixfmt.h"
35 #include "v4l2_context.h"
36 #include "v4l2_fmt.h"
37 #include "v4l2_m2m.h"
38
39 static inline int v4l2_splane_video(struct v4l2_capability *cap)
40 {
41     if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
42         cap->capabilities & V4L2_CAP_STREAMING)
43         return 1;
44
45     if (cap->capabilities & V4L2_CAP_VIDEO_M2M)
46         return 1;
47
48     return 0;
49 }
50
51 static inline int v4l2_mplane_video(struct v4l2_capability *cap)
52 {
53     if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
54         cap->capabilities & V4L2_CAP_STREAMING)
55         return 1;
56
57     if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
58         return 1;
59
60     return 0;
61 }
62
63 static int v4l2_prepare_contexts(V4L2m2mContext* s, int probe)
64 {
65     struct v4l2_capability cap;
66     int ret;
67
68     s->capture.done = s->output.done = 0;
69     s->capture.name = "capture";
70     s->output.name = "output";
71     atomic_init(&s->refcount, 0);
72     sem_init(&s->refsync, 0, 0);
73
74     memset(&cap, 0, sizeof(cap));
75     ret = ioctl(s->fd, VIDIOC_QUERYCAP, &cap);
76     if (ret < 0)
77         return ret;
78
79     av_log(s->avctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO,
80                      "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card,
81                      v4l2_mplane_video(&cap) ? "mplane" :
82                      v4l2_splane_video(&cap) ? "splane" : "unknown");
83
84     if (v4l2_mplane_video(&cap)) {
85         s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
86         s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
87         return 0;
88     }
89
90     if (v4l2_splane_video(&cap)) {
91         s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
92         s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
93         return 0;
94     }
95
96     return AVERROR(EINVAL);
97 }
98
99 static int v4l2_probe_driver(V4L2m2mContext* s)
100 {
101     int ret;
102
103     s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
104     if (s->fd < 0)
105         return AVERROR(errno);
106
107     ret = v4l2_prepare_contexts(s, 1);
108     if (ret < 0)
109         goto done;
110
111     ret = ff_v4l2_context_get_format(&s->output, 1);
112     if (ret) {
113         av_log(s->avctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
114         goto done;
115     }
116
117     ret = ff_v4l2_context_get_format(&s->capture, 1);
118     if (ret) {
119         av_log(s->avctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
120         goto done;
121     }
122
123 done:
124     if (close(s->fd) < 0) {
125         ret = AVERROR(errno);
126         av_log(s->avctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
127     }
128
129     s->fd = -1;
130
131     return ret;
132 }
133
134 static int v4l2_configure_contexts(V4L2m2mContext* s)
135 {
136     void *log_ctx = s->avctx;
137     int ret;
138     struct v4l2_format ofmt, cfmt;
139
140     s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
141     if (s->fd < 0)
142         return AVERROR(errno);
143
144     ret = v4l2_prepare_contexts(s, 0);
145     if (ret < 0)
146         goto error;
147
148     ofmt = s->output.format;
149     cfmt = s->capture.format;
150     av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n",
151                                  av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ?
152                                                ofmt.fmt.pix_mp.pixelformat :
153                                                ofmt.fmt.pix.pixelformat),
154                                  av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ?
155                                                cfmt.fmt.pix_mp.pixelformat :
156                                                cfmt.fmt.pix.pixelformat));
157
158     ret = ff_v4l2_context_set_format(&s->output);
159     if (ret) {
160         av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
161         goto error;
162     }
163
164     ret = ff_v4l2_context_set_format(&s->capture);
165     if (ret) {
166         av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
167         goto error;
168     }
169
170     ret = ff_v4l2_context_init(&s->output);
171     if (ret) {
172         av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
173         goto error;
174     }
175
176     /* decoder's buffers need to be updated at a later stage */
177     if (!av_codec_is_decoder(s->avctx->codec)) {
178         ret = ff_v4l2_context_init(&s->capture);
179         if (ret) {
180             av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
181             goto error;
182         }
183     }
184
185     return 0;
186
187 error:
188     if (close(s->fd) < 0) {
189         ret = AVERROR(errno);
190         av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
191             s->devname, av_err2str(AVERROR(errno)));
192     }
193     s->fd = -1;
194
195     return ret;
196 }
197
198 /******************************************************************************
199  *
200  *                  V4L2 M2M Interface
201  *
202  ******************************************************************************/
203 int ff_v4l2_m2m_codec_reinit(V4L2m2mContext* s)
204 {
205     int ret;
206
207     av_log(s->avctx, AV_LOG_DEBUG, "reinit context\n");
208
209     /* 1. streamoff */
210     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
211     if (ret)
212         av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
213
214     /* 2. unmap the capture buffers (v4l2 and ffmpeg):
215      *    we must wait for all references to be released before being allowed
216      *    to queue new buffers.
217      */
218     av_log(s->avctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
219     if (atomic_load(&s->refcount))
220         while(sem_wait(&s->refsync) == -1 && errno == EINTR);
221
222     ff_v4l2_context_release(&s->capture);
223
224     /* 3. get the new capture format */
225     ret = ff_v4l2_context_get_format(&s->capture, 0);
226     if (ret) {
227         av_log(s->avctx, AV_LOG_ERROR, "query the new capture format\n");
228         return ret;
229     }
230
231     /* 4. set the capture format */
232     ret = ff_v4l2_context_set_format(&s->capture);
233     if (ret) {
234         av_log(s->avctx, AV_LOG_ERROR, "setting capture format\n");
235         return ret;
236     }
237
238     /* 5. complete reinit */
239     s->draining = 0;
240     s->reinit = 0;
241
242     return 0;
243 }
244
245 int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s)
246 {
247     void *log_ctx = s->avctx;
248     int ret;
249
250     av_log(log_ctx, AV_LOG_DEBUG, "%s full reinit\n", s->devname);
251
252     /* wait for pending buffer references */
253     if (atomic_load(&s->refcount))
254         while(sem_wait(&s->refsync) == -1 && errno == EINTR);
255
256     ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
257     if (ret) {
258         av_log(log_ctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n");
259         goto error;
260     }
261
262     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
263     if (ret) {
264             av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
265             goto error;
266     }
267
268     /* release and unmmap the buffers */
269     ff_v4l2_context_release(&s->output);
270     ff_v4l2_context_release(&s->capture);
271
272     /* start again now that we know the stream dimensions */
273     s->draining = 0;
274     s->reinit = 0;
275
276     ret = ff_v4l2_context_get_format(&s->output, 0);
277     if (ret) {
278         av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
279         goto error;
280     }
281
282     ret = ff_v4l2_context_get_format(&s->capture, 0);
283     if (ret) {
284         av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
285         goto error;
286     }
287
288     ret = ff_v4l2_context_set_format(&s->output);
289     if (ret) {
290         av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
291         goto error;
292     }
293
294     ret = ff_v4l2_context_set_format(&s->capture);
295     if (ret) {
296         av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
297         goto error;
298     }
299
300     ret = ff_v4l2_context_init(&s->output);
301     if (ret) {
302         av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
303         goto error;
304     }
305
306     /* decoder's buffers need to be updated at a later stage */
307     if (!av_codec_is_decoder(s->avctx->codec)) {
308         ret = ff_v4l2_context_init(&s->capture);
309         if (ret) {
310             av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
311             goto error;
312         }
313     }
314
315     return 0;
316
317 error:
318     return ret;
319 }
320
321 static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
322 {
323     V4L2m2mContext *s = (V4L2m2mContext*)context;
324
325     ff_v4l2_context_release(&s->capture);
326     sem_destroy(&s->refsync);
327
328     close(s->fd);
329
330     av_free(s);
331 }
332
333 int ff_v4l2_m2m_codec_end(AVCodecContext *avctx)
334 {
335     V4L2m2mPriv *priv = avctx->priv_data;
336     V4L2m2mContext* s = priv->context;
337     int ret;
338
339     ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
340     if (ret)
341         av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
342
343     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
344     if (ret)
345         av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
346
347     ff_v4l2_context_release(&s->output);
348
349     s->self_ref = NULL;
350     av_buffer_unref(&priv->context_ref);
351
352     return 0;
353 }
354
355 int ff_v4l2_m2m_codec_init(AVCodecContext *avctx)
356 {
357     int ret = AVERROR(EINVAL);
358     struct dirent *entry;
359     char node[PATH_MAX];
360     DIR *dirp;
361
362     V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
363     s->avctx = avctx;
364
365     dirp = opendir("/dev");
366     if (!dirp)
367         return AVERROR(errno);
368
369     for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
370
371         if (strncmp(entry->d_name, "video", 5))
372             continue;
373
374         snprintf(node, sizeof(node), "/dev/%s", entry->d_name);
375         av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", node);
376         strncpy(s->devname, node, strlen(node) + 1);
377         ret = v4l2_probe_driver(s);
378         if (!ret)
379                 break;
380     }
381
382     closedir(dirp);
383
384     if (ret) {
385         av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
386         memset(s->devname, 0, sizeof(s->devname));
387
388         return ret;
389     }
390
391     av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", node);
392
393     return v4l2_configure_contexts(s);
394 }
395
396 int ff_v4l2_m2m_create_context(AVCodecContext *avctx, V4L2m2mContext **s)
397 {
398     V4L2m2mPriv *priv = avctx->priv_data;
399
400     *s = av_mallocz(sizeof(V4L2m2mContext));
401     if (!*s)
402         return AVERROR(ENOMEM);
403
404     priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
405                                          &v4l2_m2m_destroy_context, NULL, 0);
406     if (!priv->context_ref) {
407         av_freep(s);
408         return AVERROR(ENOMEM);
409     }
410
411     /* assign the context */
412     priv->context = *s;
413
414     /* populate it */
415     priv->context->capture.num_buffers = priv->num_capture_buffers;
416     priv->context->output.num_buffers  = priv->num_output_buffers;
417     priv->context->self_ref = priv->context_ref;
418     priv->context->fd = -1;
419
420     return 0;
421 }