FFmpeg  4.0
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 "libavcodec/internal.h"
32 #include "libavutil/pixdesc.h"
33 #include "libavutil/imgutils.h"
34 #include "libavutil/pixfmt.h"
35 #include "v4l2_context.h"
36 #include "v4l2_fmt.h"
37 #include "v4l2_m2m.h"
38 
39 static inline int v4l2_splane_video(struct v4l2_capability *cap)
40 {
41  if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
42  cap->capabilities & V4L2_CAP_STREAMING)
43  return 1;
44 
45  if (cap->capabilities & V4L2_CAP_VIDEO_M2M)
46  return 1;
47 
48  return 0;
49 }
50 
51 static inline int v4l2_mplane_video(struct v4l2_capability *cap)
52 {
53  if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
54  cap->capabilities & V4L2_CAP_STREAMING)
55  return 1;
56 
57  if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
58  return 1;
59 
60  return 0;
61 }
62 
64 {
65  struct v4l2_capability cap;
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(s->avctx, AV_LOG_INFO, "driver '%s' on card '%s'\n", cap.driver, cap.card);
80 
81  if (v4l2_mplane_video(&cap)) {
82  s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
83  s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
84  return 0;
85  }
86 
87  if (v4l2_splane_video(&cap)) {
88  s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
89  s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
90  return 0;
91  }
92 
93  return AVERROR(EINVAL);
94 }
95 
97 {
98  int ret;
99 
100  s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
101  if (s->fd < 0)
102  return AVERROR(errno);
103 
104  ret = v4l2_prepare_contexts(s);
105  if (ret < 0)
106  goto done;
107 
109  if (ret) {
110  av_log(s->avctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
111  goto done;
112  }
113 
115  if (ret) {
116  av_log(s->avctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
117  goto done;
118  }
119 
120 done:
121  if (close(s->fd) < 0) {
122  ret = AVERROR(errno);
123  av_log(s->avctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
124  }
125 
126  s->fd = -1;
127 
128  return ret;
129 }
130 
132 {
133  void *log_ctx = s->avctx;
134  int ret;
135 
136  s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
137  if (s->fd < 0)
138  return AVERROR(errno);
139 
140  ret = v4l2_prepare_contexts(s);
141  if (ret < 0)
142  goto error;
143 
145  if (ret) {
146  av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
147  goto error;
148  }
149 
151  if (ret) {
152  av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
153  goto error;
154  }
155 
156  ret = ff_v4l2_context_init(&s->output);
157  if (ret) {
158  av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
159  goto error;
160  }
161 
162  /* decoder's buffers need to be updated at a later stage */
163  if (!av_codec_is_decoder(s->avctx->codec)) {
164  ret = ff_v4l2_context_init(&s->capture);
165  if (ret) {
166  av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
167  goto error;
168  }
169  }
170 
171  return 0;
172 
173 error:
174  if (close(s->fd) < 0) {
175  ret = AVERROR(errno);
176  av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
177  s->devname, av_err2str(AVERROR(errno)));
178  }
179  s->fd = -1;
180 
181  return ret;
182 }
183 
184 /******************************************************************************
185  *
186  * V4L2 M2M Interface
187  *
188  ******************************************************************************/
190 {
191  int ret;
192 
193  av_log(s->avctx, AV_LOG_DEBUG, "reinit context\n");
194 
195  /* 1. streamoff */
196  ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
197  if (ret)
198  av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
199 
200  /* 2. unmap the capture buffers (v4l2 and ffmpeg):
201  * we must wait for all references to be released before being allowed
202  * to queue new buffers.
203  */
204  av_log(s->avctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
205  if (atomic_load(&s->refcount))
206  while(sem_wait(&s->refsync) == -1 && errno == EINTR);
207 
209 
210  /* 3. get the new capture format */
212  if (ret) {
213  av_log(s->avctx, AV_LOG_ERROR, "query the new capture format\n");
214  return ret;
215  }
216 
217  /* 4. set the capture format */
219  if (ret) {
220  av_log(s->avctx, AV_LOG_ERROR, "setting capture format\n");
221  return ret;
222  }
223 
224  /* 5. complete reinit */
225  s->draining = 0;
226  s->reinit = 0;
227 
228  return 0;
229 }
230 
232 {
233  void *log_ctx = s->avctx;
234  int ret;
235 
236  av_log(log_ctx, AV_LOG_DEBUG, "%s full reinit\n", s->devname);
237 
238  /* wait for pending buffer references */
239  if (atomic_load(&s->refcount))
240  while(sem_wait(&s->refsync) == -1 && errno == EINTR);
241 
242  ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
243  if (ret) {
244  av_log(s->avctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n");
245  goto error;
246  }
247 
248  ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
249  if (ret) {
250  av_log(s->avctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
251  goto error;
252  }
253 
254  /* release and unmmap the buffers */
257 
258  /* start again now that we know the stream dimensions */
259  s->draining = 0;
260  s->reinit = 0;
261 
263  if (ret) {
264  av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
265  goto error;
266  }
267 
269  if (ret) {
270  av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
271  goto error;
272  }
273 
275  if (ret) {
276  av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
277  goto error;
278  }
279 
281  if (ret) {
282  av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
283  goto error;
284  }
285 
286  ret = ff_v4l2_context_init(&s->output);
287  if (ret) {
288  av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
289  goto error;
290  }
291 
292  /* decoder's buffers need to be updated at a later stage */
293  if (!av_codec_is_decoder(s->avctx->codec)) {
294  ret = ff_v4l2_context_init(&s->capture);
295  if (ret) {
296  av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
297  goto error;
298  }
299  }
300 
301  return 0;
302 
303 error:
304  return ret;
305 }
306 
307 static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
308 {
309  V4L2m2mContext *s = (V4L2m2mContext*)context;
310 
312  sem_destroy(&s->refsync);
313 
314  close(s->fd);
315 
316  av_free(s);
317 }
318 
320 {
321  V4L2m2mPriv *priv = avctx->priv_data;
322  V4L2m2mContext* s = priv->context;
323  int ret;
324 
325  ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
326  if (ret)
327  av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
328 
329  ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
330  if (ret)
331  av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
332 
334 
335  s->self_ref = NULL;
337 
338  return 0;
339 }
340 
342 {
343  int ret = AVERROR(EINVAL);
344  struct dirent *entry;
345  char node[PATH_MAX];
346  DIR *dirp;
347 
348  V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
349  s->avctx = avctx;
350 
351  dirp = opendir("/dev");
352  if (!dirp)
353  return AVERROR(errno);
354 
355  for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
356 
357  if (strncmp(entry->d_name, "video", 5))
358  continue;
359 
360  snprintf(node, sizeof(node), "/dev/%s", entry->d_name);
361  av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", node);
362  strncpy(s->devname, node, strlen(node) + 1);
363  ret = v4l2_probe_driver(s);
364  if (!ret)
365  break;
366  }
367 
368  closedir(dirp);
369 
370  if (ret) {
371  av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
372  memset(s->devname, 0, sizeof(s->devname));
373 
374  return ret;
375  }
376 
377  av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", node);
378 
379  return v4l2_configure_contexts(s);
380 }
381 
383 {
384  V4L2m2mPriv *priv = avctx->priv_data;
385 
386  *s = av_mallocz(sizeof(V4L2m2mContext));
387  if (!*s)
388  return AVERROR(ENOMEM);
389 
390  priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
392  if (!priv->context_ref) {
393  av_freep(s);
394  return AVERROR(ENOMEM);
395  }
396 
397  /* assign the context */
398  priv->context = *s;
399 
400  /* populate it */
403  priv->context->self_ref = priv->context_ref;
404 
405  return 0;
406 }
#define NULL
Definition: coverity.c:32
const struct AVCodec * codec
Definition: avcodec.h:1527
const char * s
Definition: avisynth_c.h:768
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it...
Definition: buffer.c:125
const char * name
context name.
Definition: v4l2_context.h:40
AVCodecContext * avctx
Definition: v4l2_m2m.h:52
misc image utilities
int ff_v4l2_context_init(V4L2Context *ctx)
Initializes a V4L2Context.
Definition: v4l2_context.c:661
void ff_v4l2_context_release(V4L2Context *ctx)
Releases a V4L2Context.
Definition: v4l2_context.c:646
int ff_v4l2_m2m_codec_init(AVCodecContext *avctx)
Probes the video nodes looking for the required codec capabilities.
Definition: v4l2_m2m.c:341
int ff_v4l2_context_set_format(V4L2Context *ctx)
Sets the V4L2Context format in the v4l2 driver.
Definition: v4l2_context.c:641
int av_codec_is_decoder(const AVCodec *codec)
Definition: utils.c:99
uint8_t
#define sem_init
Definition: semaphore.h:40
int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)
Reinitializes the V4L2m2mContext when the driver cant continue processing with the capture parameters...
Definition: v4l2_m2m.c:189
#define av_log(a,...)
int done
Either no more buffers available or an unrecoverable error was notified by the V4L2 kernel driver: on...
Definition: v4l2_context.h:92
int ff_v4l2_m2m_create_context(AVCodecContext *avctx, V4L2m2mContext **s)
Allocate a new context and references for a V4L2 M2M instance.
Definition: v4l2_m2m.c:382
static int v4l2_probe_driver(V4L2m2mContext *s)
Definition: v4l2_m2m.c:96
static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
Definition: v4l2_m2m.c:307
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
#define atomic_load(object)
Definition: stdatomic.h:93
#define AVERROR(e)
Definition: error.h:43
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
#define sem_destroy(psem)
Definition: semaphore.h:29
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:236
V4L2m2mContext * context
Definition: v4l2_m2m.h:68
AVBufferRef * av_buffer_create(uint8_t *data, int size, void(*free)(void *opaque, uint8_t *data), void *opaque, int flags)
Create an AVBuffer from an existing array.
Definition: buffer.c:28
int ff_v4l2_m2m_codec_end(AVCodecContext *avctx)
Releases all the codec resources if all AVBufferRefs have been returned to the ctx.
Definition: v4l2_m2m.c:319
char devname[PATH_MAX]
Definition: v4l2_m2m.h:44
int num_output_buffers
Definition: v4l2_m2m.h:71
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:119
static void error(const char *err)
int ff_v4l2_context_set_status(V4L2Context *ctx, uint32_t cmd)
Sets the status of a V4L2Context.
Definition: v4l2_context.c:510
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
V4L2Context capture
Definition: v4l2_m2m.h:48
Libavcodec external API header.
atomic_uint refcount
Definition: v4l2_m2m.h:54
main external API structure.
Definition: avcodec.h:1518
static int v4l2_prepare_contexts(V4L2m2mContext *s)
Definition: v4l2_m2m.c:63
int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s)
Reinitializes the V4L2m2mContext when the driver cant continue processing with the any of the current...
Definition: v4l2_m2m.c:231
static int v4l2_configure_contexts(V4L2m2mContext *s)
Definition: v4l2_m2m.c:131
#define snprintf
Definition: snprintf.h:34
static int v4l2_splane_video(struct v4l2_capability *cap)
Definition: v4l2_m2m.c:39
V4L2Context output
Definition: v4l2_m2m.h:49
common internal api header.
int num_capture_buffers
Definition: v4l2_m2m.h:72
AVBufferRef * self_ref
Definition: v4l2_m2m.h:61
void * priv_data
Definition: avcodec.h:1545
pixel format definitions
#define av_free(p)
int ff_v4l2_context_get_format(V4L2Context *ctx)
Queries the driver for a valid v4l2 format and copies it to the context.
Definition: v4l2_context.c:614
#define sem_wait(psem)
Definition: semaphore.h:27
int num_buffers
Readonly after init.
Definition: v4l2_context.h:81
#define atomic_init(obj, value)
Definition: stdatomic.h:33
#define av_freep(p)
AVBufferRef * context_ref
Definition: v4l2_m2m.h:69
sem_t refsync
Definition: v4l2_m2m.h:53
enum v4l2_buf_type type
Type of this buffer context.
Definition: v4l2_context.h:47
static int v4l2_mplane_video(struct v4l2_capability *cap)
Definition: v4l2_m2m.c:51