FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/v4l2_m2m.c
Date: 2024-05-03 15:42:48
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 "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 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(FFRefStructOpaque 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 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 ff_refstruct_unref(&priv->context);
286
287 return 0;
288 }
289
290 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 = ff_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 ff_refstruct_unref(&priv->context);
348 *s = NULL; /* freed when unreferencing context */
349 return AVERROR(ENOMEM);
350 }
351
352 return 0;
353 }
354