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