FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/v4l2_m2m.c
Date: 2022-07-04 19:11:22
Exec Total Coverage
Lines: 0 184 0.0%
Branches: 0 98 0.0%

Line Branch Exec Source
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
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
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
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
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
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 ******************************************************************************/
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
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
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
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
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