FFmpeg coverage


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