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 "libavutil/pixdesc.h"
32 #include "libavutil/imgutils.h"
33 #include "libavutil/pixfmt.h"
34 #include "v4l2_context.h"
35 #include "v4l2_fmt.h"
36 #include "v4l2_m2m.h"
37 
v4l2_splane_video(struct v4l2_capability *cap)38 static inline int v4l2_splane_video(struct v4l2_capability *cap)
39 {
40     if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
41         cap->capabilities & V4L2_CAP_STREAMING)
42         return 1;
43 
44     if (cap->capabilities & V4L2_CAP_VIDEO_M2M)
45         return 1;
46 
47     return 0;
48 }
49 
v4l2_mplane_video(struct v4l2_capability *cap)50 static inline int v4l2_mplane_video(struct v4l2_capability *cap)
51 {
52     if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
53         cap->capabilities & V4L2_CAP_STREAMING)
54         return 1;
55 
56     if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
57         return 1;
58 
59     return 0;
60 }
61 
v4l2_prepare_contexts(V4L2m2mContext *s, int probe)62 static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe)
63 {
64     struct v4l2_capability cap;
65     void *log_ctx = s->avctx;
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(log_ctx, 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 
v4l2_probe_driver(V4L2m2mContext *s)99 static int v4l2_probe_driver(V4L2m2mContext *s)
100 {
101     void *log_ctx = s->avctx;
102     int ret;
103 
104     s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
105     if (s->fd < 0)
106         return AVERROR(errno);
107 
108     ret = v4l2_prepare_contexts(s, 1);
109     if (ret < 0)
110         goto done;
111 
112     ret = ff_v4l2_context_get_format(&s->output, 1);
113     if (ret) {
114         av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
115         goto done;
116     }
117 
118     ret = ff_v4l2_context_get_format(&s->capture, 1);
119     if (ret) {
120         av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
121         goto done;
122     }
123 
124 done:
125     if (close(s->fd) < 0) {
126         ret = AVERROR(errno);
127         av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
128     }
129 
130     s->fd = -1;
131 
132     return ret;
133 }
134 
v4l2_configure_contexts(V4L2m2mContext *s)135 static int v4l2_configure_contexts(V4L2m2mContext *s)
136 {
137     void *log_ctx = s->avctx;
138     int ret;
139     struct v4l2_format ofmt, cfmt;
140 
141     s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
142     if (s->fd < 0)
143         return AVERROR(errno);
144 
145     ret = v4l2_prepare_contexts(s, 0);
146     if (ret < 0)
147         goto error;
148 
149     ofmt = s->output.format;
150     cfmt = s->capture.format;
151     av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n",
152                                  av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ?
153                                                ofmt.fmt.pix_mp.pixelformat :
154                                                ofmt.fmt.pix.pixelformat),
155                                  av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ?
156                                                cfmt.fmt.pix_mp.pixelformat :
157                                                cfmt.fmt.pix.pixelformat));
158 
159     ret = ff_v4l2_context_set_format(&s->output);
160     if (ret) {
161         av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
162         goto error;
163     }
164 
165     ret = ff_v4l2_context_set_format(&s->capture);
166     if (ret) {
167         av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
168         goto error;
169     }
170 
171     ret = ff_v4l2_context_init(&s->output);
172     if (ret) {
173         av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
174         goto error;
175     }
176 
177     /* decoder's buffers need to be updated at a later stage */
178     if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) {
179         ret = ff_v4l2_context_init(&s->capture);
180         if (ret) {
181             av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
182             goto error;
183         }
184     }
185 
186     return 0;
187 
188 error:
189     if (close(s->fd) < 0) {
190         ret = AVERROR(errno);
191         av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
192             s->devname, av_err2str(AVERROR(errno)));
193     }
194     s->fd = -1;
195 
196     return ret;
197 }
198 
199 /******************************************************************************
200  *
201  *                  V4L2 M2M Interface
202  *
203  ******************************************************************************/
ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)204 int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)
205 {
206     void *log_ctx = s->avctx;
207     int ret;
208 
209     av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n");
210 
211     /* 1. streamoff */
212     ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
213     if (ret)
214         av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
215 
216     /* 2. unmap the capture buffers (v4l2 and ffmpeg):
217      *    we must wait for all references to be released before being allowed
218      *    to queue new buffers.
219      */
220     av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
221     if (atomic_load(&s->refcount))
222         while(sem_wait(&s->refsync) == -1 && errno == EINTR);
223 
224     ff_v4l2_context_release(&s->capture);
225 
226     /* 3. get the new capture format */
227     ret = ff_v4l2_context_get_format(&s->capture, 0);
228     if (ret) {
229         av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n");
230         return ret;
231     }
232 
233     /* 4. set the capture format */
234     ret = ff_v4l2_context_set_format(&s->capture);
235     if (ret) {
236         av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n");
237         return ret;
238     }
239 
240     /* 5. complete reinit */
241     s->draining = 0;
242     s->reinit = 0;
243 
244     return 0;
245 }
246 
v4l2_m2m_destroy_context(void *opaque, uint8_t *context)247 static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
248 {
249     V4L2m2mContext *s = (V4L2m2mContext*)context;
250 
251     ff_v4l2_context_release(&s->capture);
252     sem_destroy(&s->refsync);
253 
254     if (s->fd >= 0)
255         close(s->fd);
256     av_frame_unref(s->frame);
257     av_frame_free(&s->frame);
258     av_packet_unref(&s->buf_pkt);
259 
260     av_free(s);
261 }
262 
ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)263 int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)
264 {
265     V4L2m2mContext *s = priv->context;
266     int ret;
267 
268     if (!s)
269         return 0;
270 
271     if (s->fd >= 0) {
272         ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
273         if (ret)
274             av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
275 
276         ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
277         if (ret)
278             av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
279     }
280 
281     ff_v4l2_context_release(&s->output);
282 
283     s->self_ref = NULL;
284     av_buffer_unref(&priv->context_ref);
285 
286     return 0;
287 }
288 
ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)289 int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
290 {
291     int ret = AVERROR(EINVAL);
292     struct dirent *entry;
293     DIR *dirp;
294 
295     V4L2m2mContext *s = priv->context;
296 
297     dirp = opendir("/dev");
298     if (!dirp)
299         return AVERROR(errno);
300 
301     for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
302 
303         if (strncmp(entry->d_name, "video", 5))
304             continue;
305 
306         snprintf(s->devname, sizeof(s->devname), "/dev/%s", entry->d_name);
307         av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", s->devname);
308         ret = v4l2_probe_driver(s);
309         if (!ret)
310             break;
311     }
312 
313     closedir(dirp);
314 
315     if (ret) {
316         av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
317         memset(s->devname, 0, sizeof(s->devname));
318 
319         return ret;
320     }
321 
322     av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", s->devname);
323 
324     return v4l2_configure_contexts(s);
325 }
326 
ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)327 int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
328 {
329     *s = av_mallocz(sizeof(V4L2m2mContext));
330     if (!*s)
331         return AVERROR(ENOMEM);
332 
333     priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
334                                          &v4l2_m2m_destroy_context, NULL, 0);
335     if (!priv->context_ref) {
336         av_freep(s);
337         return AVERROR(ENOMEM);
338     }
339 
340     /* assign the context */
341     priv->context = *s;
342     (*s)->priv = priv;
343 
344     /* populate it */
345     priv->context->capture.num_buffers = priv->num_capture_buffers;
346     priv->context->output.num_buffers  = priv->num_output_buffers;
347     priv->context->self_ref = priv->context_ref;
348     priv->context->fd = -1;
349 
350     priv->context->frame = av_frame_alloc();
351     if (!priv->context->frame) {
352         av_buffer_unref(&priv->context_ref);
353         *s = NULL; /* freed when unreferencing context_ref */
354         return AVERROR(ENOMEM);
355     }
356 
357     return 0;
358 }
359