FFmpeg
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 
105  if (ret < 0)
106  goto done;
107 
108  ret = ff_v4l2_context_get_format(&s->output);
109  if (ret) {
110  av_log(s->avctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
111  goto done;
112  }
113 
114  ret = ff_v4l2_context_get_format(&s->capture);
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 
141  if (ret < 0)
142  goto error;
143 
144  ret = ff_v4l2_context_set_format(&s->output);
145  if (ret) {
146  av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
147  goto error;
148  }
149 
150  ret = ff_v4l2_context_set_format(&s->capture);
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 
208  ff_v4l2_context_release(&s->capture);
209 
210  /* 3. get the new capture format */
211  ret = ff_v4l2_context_get_format(&s->capture);
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 */
218  ret = ff_v4l2_context_set_format(&s->capture);
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 */
255  ff_v4l2_context_release(&s->output);
256  ff_v4l2_context_release(&s->capture);
257 
258  /* start again now that we know the stream dimensions */
259  s->draining = 0;
260  s->reinit = 0;
261 
262  ret = ff_v4l2_context_get_format(&s->output);
263  if (ret) {
264  av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
265  goto error;
266  }
267 
268  ret = ff_v4l2_context_get_format(&s->capture);
269  if (ret) {
270  av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
271  goto error;
272  }
273 
274  ret = ff_v4l2_context_set_format(&s->output);
275  if (ret) {
276  av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
277  goto error;
278  }
279 
280  ret = ff_v4l2_context_set_format(&s->capture);
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 {
310 
311  ff_v4l2_context_release(&s->capture);
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 
333  ff_v4l2_context_release(&s->output);
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);
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 }
ff_v4l2_context_init
int ff_v4l2_context_init(V4L2Context *ctx)
Initializes a V4L2Context.
Definition: v4l2_context.c:661
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
av_codec_is_decoder
int av_codec_is_decoder(const AVCodec *codec)
Definition: utils.c:99
ff_v4l2_m2m_codec_reinit
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:189
V4L2m2mContext
Definition: v4l2_m2m.h:43
pixdesc.h
internal.h
v4l2_mplane_video
static int v4l2_mplane_video(struct v4l2_capability *cap)
Definition: v4l2_m2m.c:51
ff_v4l2_context_release
void ff_v4l2_context_release(V4L2Context *ctx)
Releases a V4L2Context.
Definition: v4l2_context.c:646
v4l2_probe_driver
static int v4l2_probe_driver(V4L2m2mContext *s)
Definition: v4l2_m2m.c:96
av_buffer_create
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
V4L2m2mPriv::context
V4L2m2mContext * context
Definition: v4l2_m2m.h:68
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
ff_v4l2_context_set_format
int ff_v4l2_context_set_format(V4L2Context *ctx)
Sets the V4L2Context format in the v4l2 driver.
Definition: v4l2_context.c:641
v4l2_splane_video
static int v4l2_splane_video(struct v4l2_capability *cap)
Definition: v4l2_m2m.c:39
v4l2_fmt.h
s
#define s(width, name)
Definition: cbs_vp9.c:257
V4L2m2mContext::capture
V4L2Context capture
Definition: v4l2_m2m.h:48
ff_v4l2_m2m_create_context
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
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:197
v4l2_m2m_destroy_context
static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
Definition: v4l2_m2m.c:307
atomic_load
#define atomic_load(object)
Definition: stdatomic.h:93
context
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option keep it simple and lowercase description are in without and describe what they for example set the foo of the bar offset is the offset of the field in your context
Definition: writing_filters.txt:91
NULL
#define NULL
Definition: coverity.c:32
av_buffer_unref
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
v4l2_configure_contexts
static int v4l2_configure_contexts(V4L2m2mContext *s)
Definition: v4l2_m2m.c:131
V4L2m2mPriv::num_output_buffers
int num_output_buffers
Definition: v4l2_m2m.h:71
V4L2m2mContext::output
V4L2Context output
Definition: v4l2_m2m.h:49
error
static void error(const char *err)
Definition: target_dec_fuzzer.c:61
sem_wait
#define sem_wait(psem)
Definition: semaphore.h:27
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:119
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:187
ff_v4l2_m2m_codec_full_reinit
int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s)
Reinitializes the V4L2m2mContext when the driver cannot continue processing with the any of the curre...
Definition: v4l2_m2m.c:231
ff_v4l2_context_set_status
int ff_v4l2_context_set_status(V4L2Context *ctx, uint32_t cmd)
Sets the status of a V4L2Context.
Definition: v4l2_context.c:510
V4L2Context::num_buffers
int num_buffers
Readonly after init.
Definition: v4l2_context.h:81
uint8_t
uint8_t
Definition: audio_convert.c:194
av_mallocz
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
V4L2m2mPriv::context_ref
AVBufferRef * context_ref
Definition: v4l2_m2m.h:69
v4l2_context.h
avcodec.h
sem_destroy
#define sem_destroy(psem)
Definition: semaphore.h:29
ret
ret
Definition: filter_design.txt:187
pixfmt.h
ff_v4l2_m2m_codec_init
int ff_v4l2_m2m_codec_init(AVCodecContext *avctx)
Probes the video nodes looking for the required codec capabilities.
Definition: v4l2_m2m.c:341
AVCodecContext
main external API structure.
Definition: avcodec.h:1565
V4L2m2mPriv::num_capture_buffers
int num_capture_buffers
Definition: v4l2_m2m.h:72
V4L2m2mPriv
Definition: v4l2_m2m.h:64
ff_v4l2_m2m_codec_end
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
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:1592
v4l2_prepare_contexts
static int v4l2_prepare_contexts(V4L2m2mContext *s)
Definition: v4l2_m2m.c:63
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
ff_v4l2_context_get_format
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
v4l2_m2m.h
imgutils.h
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
V4L2m2mContext::self_ref
AVBufferRef * self_ref
Definition: v4l2_m2m.h:61
atomic_init
#define atomic_init(obj, value)
Definition: stdatomic.h:33
snprintf
#define snprintf
Definition: snprintf.h:34
sem_init
#define sem_init
Definition: semaphore.h:40