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