]> git.sesse.net Git - ffmpeg/blob - libavcodec/v4l2_m2m.c
Merge commit 'c93e92f2b25f4174350ded3f59ad117ec8eb1fe4'
[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)
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, AV_LOG_INFO, "driver '%s' on card '%s'\n", cap.driver, cap.card);
80
81     if (v4l2_mplane_video(&cap)) {
82         s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
83         s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
84         return 0;
85     }
86
87     if (v4l2_splane_video(&cap)) {
88         s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
89         s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
90         return 0;
91     }
92
93     return AVERROR(EINVAL);
94 }
95
96 static int v4l2_probe_driver(V4L2m2mContext* s)
97 {
98     int ret;
99
100     s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
101     if (s->fd < 0)
102         return AVERROR(errno);
103
104     ret = v4l2_prepare_contexts(s);
105     if (ret < 0)
106         goto done;
107
108     ret = ff_v4l2_context_get_format(&s->output);
109     if (ret) {
110         av_log(s->avctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
111         goto done;
112     }
113
114     ret = ff_v4l2_context_get_format(&s->capture);
115     if (ret) {
116         av_log(s->avctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
117         goto done;
118     }
119
120 done:
121     if (close(s->fd) < 0) {
122         ret = AVERROR(errno);
123         av_log(s->avctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
124     }
125
126     s->fd = -1;
127
128     return ret;
129 }
130
131 static int v4l2_configure_contexts(V4L2m2mContext* s)
132 {
133     void *log_ctx = s->avctx;
134     int ret;
135
136     s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
137     if (s->fd < 0)
138         return AVERROR(errno);
139
140     ret = v4l2_prepare_contexts(s);
141     if (ret < 0)
142         goto error;
143
144     ret = ff_v4l2_context_set_format(&s->output);
145     if (ret) {
146         av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
147         goto error;
148     }
149
150     ret = ff_v4l2_context_set_format(&s->capture);
151     if (ret) {
152         av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
153         goto error;
154     }
155
156     ret = ff_v4l2_context_init(&s->output);
157     if (ret) {
158         av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
159         goto error;
160     }
161
162     /* decoder's buffers need to be updated at a later stage */
163     if (!av_codec_is_decoder(s->avctx->codec)) {
164         ret = ff_v4l2_context_init(&s->capture);
165         if (ret) {
166             av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
167             goto error;
168         }
169     }
170
171     return 0;
172
173 error:
174     if (close(s->fd) < 0) {
175         ret = AVERROR(errno);
176         av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
177             s->devname, av_err2str(AVERROR(errno)));
178     }
179     s->fd = -1;
180
181     return ret;
182 }
183
184 /******************************************************************************
185  *
186  *                  V4L2 M2M Interface
187  *
188  ******************************************************************************/
189 int ff_v4l2_m2m_codec_reinit(V4L2m2mContext* s)
190 {
191     int ret;
192
193     av_log(s->avctx, AV_LOG_DEBUG, "reinit context\n");
194
195     /* 1. streamoff */
196     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
197     if (ret)
198         av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
199
200     /* 2. unmap the capture buffers (v4l2 and ffmpeg):
201      *    we must wait for all references to be released before being allowed
202      *    to queue new buffers.
203      */
204     av_log(s->avctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
205     if (atomic_load(&s->refcount))
206         while(sem_wait(&s->refsync) == -1 && errno == EINTR);
207
208     ff_v4l2_context_release(&s->capture);
209
210     /* 3. get the new capture format */
211     ret = ff_v4l2_context_get_format(&s->capture);
212     if (ret) {
213         av_log(s->avctx, AV_LOG_ERROR, "query the new capture format\n");
214         return ret;
215     }
216
217     /* 4. set the capture format */
218     ret = ff_v4l2_context_set_format(&s->capture);
219     if (ret) {
220         av_log(s->avctx, AV_LOG_ERROR, "setting capture format\n");
221         return ret;
222     }
223
224     /* 5. complete reinit */
225     s->draining = 0;
226     s->reinit = 0;
227
228     return 0;
229 }
230
231 int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s)
232 {
233     void *log_ctx = s->avctx;
234     int ret;
235
236     av_log(log_ctx, AV_LOG_DEBUG, "%s full reinit\n", s->devname);
237
238     /* wait for pending buffer references */
239     if (atomic_load(&s->refcount))
240         while(sem_wait(&s->refsync) == -1 && errno == EINTR);
241
242     ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
243     if (ret) {
244         av_log(s->avctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n");
245         goto error;
246     }
247
248     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
249     if (ret) {
250             av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
251             goto error;
252     }
253
254     /* release and unmmap the buffers */
255     ff_v4l2_context_release(&s->output);
256     ff_v4l2_context_release(&s->capture);
257
258     /* start again now that we know the stream dimensions */
259     s->draining = 0;
260     s->reinit = 0;
261
262     ret = ff_v4l2_context_get_format(&s->output);
263     if (ret) {
264         av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
265         goto error;
266     }
267
268     ret = ff_v4l2_context_get_format(&s->capture);
269     if (ret) {
270         av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
271         goto error;
272     }
273
274     ret = ff_v4l2_context_set_format(&s->output);
275     if (ret) {
276         av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
277         goto error;
278     }
279
280     ret = ff_v4l2_context_set_format(&s->capture);
281     if (ret) {
282         av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
283         goto error;
284     }
285
286     ret = ff_v4l2_context_init(&s->output);
287     if (ret) {
288         av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
289         goto error;
290     }
291
292     /* decoder's buffers need to be updated at a later stage */
293     if (!av_codec_is_decoder(s->avctx->codec)) {
294         ret = ff_v4l2_context_init(&s->capture);
295         if (ret) {
296             av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
297             goto error;
298         }
299     }
300
301     return 0;
302
303 error:
304     return ret;
305 }
306
307 static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
308 {
309     V4L2m2mContext *s = (V4L2m2mContext*)context;
310
311     ff_v4l2_context_release(&s->capture);
312     sem_destroy(&s->refsync);
313
314     close(s->fd);
315
316     av_free(s);
317 }
318
319 int ff_v4l2_m2m_codec_end(AVCodecContext *avctx)
320 {
321     V4L2m2mPriv *priv = avctx->priv_data;
322     V4L2m2mContext* s = priv->context;
323     int ret;
324
325     ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
326     if (ret)
327             av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
328
329     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
330     if (ret)
331         av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
332
333     ff_v4l2_context_release(&s->output);
334
335     s->self_ref = NULL;
336     av_buffer_unref(&priv->context_ref);
337
338     return 0;
339 }
340
341 int ff_v4l2_m2m_codec_init(AVCodecContext *avctx)
342 {
343     int ret = AVERROR(EINVAL);
344     struct dirent *entry;
345     char node[PATH_MAX];
346     DIR *dirp;
347
348     V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
349     s->avctx = avctx;
350
351     dirp = opendir("/dev");
352     if (!dirp)
353         return AVERROR(errno);
354
355     for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
356
357         if (strncmp(entry->d_name, "video", 5))
358             continue;
359
360         snprintf(node, sizeof(node), "/dev/%s", entry->d_name);
361         av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", node);
362         strncpy(s->devname, node, strlen(node) + 1);
363         ret = v4l2_probe_driver(s);
364         if (!ret)
365                 break;
366     }
367
368     closedir(dirp);
369
370     if (ret) {
371         av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
372         memset(s->devname, 0, sizeof(s->devname));
373
374         return ret;
375     }
376
377     av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", node);
378
379     return v4l2_configure_contexts(s);
380 }
381
382 int ff_v4l2_m2m_create_context(AVCodecContext *avctx, V4L2m2mContext **s)
383 {
384     V4L2m2mPriv *priv = avctx->priv_data;
385
386     *s = av_mallocz(sizeof(V4L2m2mContext));
387     if (!*s)
388         return AVERROR(ENOMEM);
389
390     priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
391                                          &v4l2_m2m_destroy_context, NULL, 0);
392     if (!priv->context_ref) {
393         av_freep(s);
394         return AVERROR(ENOMEM);
395     }
396
397     /* assign the context */
398     priv->context = *s;
399
400     /* populate it */
401     priv->context->capture.num_buffers = priv->num_capture_buffers;
402     priv->context->output.num_buffers  = priv->num_output_buffers;
403     priv->context->self_ref = priv->context_ref;
404
405     return 0;
406 }