FFmpeg
Loading...
Searching...
No Matches
v4l2_m2m.c
Go to the documentation of this file.
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
40static 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
52static 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
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
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
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
126done:
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
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
192error:
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 ******************************************************************************/
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
251static 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
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;
286
287 return 0;
288}
289
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
326}
327
329{
330 *s = av_refstruct_alloc_ext(sizeof(**s), 0, NULL,
332 if (!*s)
333 return AVERROR(ENOMEM);
334
335 /* assign the context */
336 priv->context = *s;
337 (*s)->priv = priv;
338
339 /* populate it */
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) {
348 *s = NULL; /* freed when unreferencing context */
349 return AVERROR(ENOMEM);
350 }
351
352 return 0;
353}
static int probe(const AVProbeData *p)
Definition act.c:39
#define entry
static av_cold void close(AVCodecParserContext *s)
Definition apv_parser.c:197
Libavcodec external API header.
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
#define atomic_load(object)
Definition stdatomic.h:93
#define atomic_init(obj, value)
Definition stdatomic.h:33
int av_codec_is_decoder(const AVCodec *codec)
Definition utils.c:85
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition packet.c:434
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition error.h:122
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:52
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
#define av_fourcc2str(fourcc)
Definition avutil.h:323
misc image utilities
#define av_cold
Definition attributes.h:117
Memory handling functions.
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition pixdesc.c:3380
pixel format definitions
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition refstruct.c:120
static void * av_refstruct_alloc_ext(size_t size, unsigned flags, void *opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
A wrapper around av_refstruct_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition refstruct.h:94
#define sem_wait(psem)
Definition semaphore.h:27
#define sem_destroy(psem)
Definition semaphore.h:29
#define sem_init
Definition semaphore.h:40
#define snprintf
Definition snprintf.h:34
int num_buffers
Readonly after init.
V4L2Context output
Definition v4l2_m2m.h:49
V4L2Context capture
Definition v4l2_m2m.h:48
struct V4L2m2mContext * self_ref
Definition v4l2_m2m.h:65
AVFrame * frame
Definition v4l2_m2m.h:62
int num_capture_buffers
Definition v4l2_m2m.h:77
V4L2m2mContext * context
RefStruct reference.
Definition v4l2_m2m.h:74
int num_output_buffers
Definition v4l2_m2m.h:76
#define av_log(a,...)
static void error(const char *err)
RefStruct is an API for creating reference-counted objects with minimal overhead.
Definition refstruct.h:58
void ff_v4l2_context_release(V4L2Context *ctx)
Releases a V4L2Context.
int ff_v4l2_context_get_format(V4L2Context *ctx, int probe)
Queries the driver for a valid v4l2 format and copies it to the context.
int ff_v4l2_context_set_status(V4L2Context *ctx, uint32_t cmd)
Sets the status of a V4L2Context.
int ff_v4l2_context_set_format(V4L2Context *ctx)
Sets the V4L2Context format in the v4l2 driver.
int ff_v4l2_context_init(V4L2Context *ctx)
Initializes a V4L2Context.
static av_cold int v4l2_configure_contexts(V4L2m2mContext *s)
Definition v4l2_m2m.c:137
av_cold int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
Probes the video nodes looking for the required codec capabilities.
Definition v4l2_m2m.c:290
static void v4l2_m2m_destroy_context(AVRefStructOpaque unused, void *context)
Definition v4l2_m2m.c:251
int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
Allocate a new context and references for a V4L2 M2M instance.
Definition v4l2_m2m.c:328
static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe)
Definition v4l2_m2m.c:64
av_cold int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)
Releases all the codec resources if all AVBufferRefs have been returned to the ctx.
Definition v4l2_m2m.c:264
static int v4l2_splane_video(struct v4l2_capability *cap)
Definition v4l2_m2m.c:40
static int v4l2_mplane_video(struct v4l2_capability *cap)
Definition v4l2_m2m.c:52
int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)
Reinitializes the V4L2m2mContext when the driver cannot continue processing with the capture paramete...
Definition v4l2_m2m.c:208
static int v4l2_probe_driver(V4L2m2mContext *s)
Definition v4l2_m2m.c:101