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