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 "libavutil/pixdesc.h"
32 #include "libavutil/imgutils.h"
33 #include "libavutil/pixfmt.h"
34 #include "v4l2_context.h"
35 #include "v4l2_fmt.h"
36 #include "v4l2_m2m.h"
37 
38 static inline int v4l2_splane_video(struct v4l2_capability *cap)
39 {
40  if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
41  cap->capabilities & V4L2_CAP_STREAMING)
42  return 1;
43 
44  if (cap->capabilities & V4L2_CAP_VIDEO_M2M)
45  return 1;
46 
47  return 0;
48 }
49 
50 static inline int v4l2_mplane_video(struct v4l2_capability *cap)
51 {
52  if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
53  cap->capabilities & V4L2_CAP_STREAMING)
54  return 1;
55 
56  if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
57  return 1;
58 
59  return 0;
60 }
61 
63 {
64  struct v4l2_capability cap;
65  void *log_ctx = s->avctx;
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(log_ctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO,
80  "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card,
81  v4l2_mplane_video(&cap) ? "mplane" :
82  v4l2_splane_video(&cap) ? "splane" : "unknown");
83 
84  if (v4l2_mplane_video(&cap)) {
85  s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
86  s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
87  return 0;
88  }
89 
90  if (v4l2_splane_video(&cap)) {
91  s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
92  s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
93  return 0;
94  }
95 
96  return AVERROR(EINVAL);
97 }
98 
100 {
101  void *log_ctx = s->avctx;
102  int ret;
103 
104  s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
105  if (s->fd < 0)
106  return AVERROR(errno);
107 
109  if (ret < 0)
110  goto done;
111 
112  ret = ff_v4l2_context_get_format(&s->output, 1);
113  if (ret) {
114  av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
115  goto done;
116  }
117 
118  ret = ff_v4l2_context_get_format(&s->capture, 1);
119  if (ret) {
120  av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
121  goto done;
122  }
123 
124 done:
125  if (close(s->fd) < 0) {
126  ret = AVERROR(errno);
127  av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
128  }
129 
130  s->fd = -1;
131 
132  return ret;
133 }
134 
136 {
137  void *log_ctx = s->avctx;
138  int ret;
139  struct v4l2_format ofmt, cfmt;
140 
141  s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
142  if (s->fd < 0)
143  return AVERROR(errno);
144 
146  if (ret < 0)
147  goto error;
148 
149  ofmt = s->output.format;
150  cfmt = s->capture.format;
151  av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n",
152  av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ?
153  ofmt.fmt.pix_mp.pixelformat :
154  ofmt.fmt.pix.pixelformat),
155  av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ?
156  cfmt.fmt.pix_mp.pixelformat :
157  cfmt.fmt.pix.pixelformat));
158 
159  ret = ff_v4l2_context_set_format(&s->output);
160  if (ret) {
161  av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
162  goto error;
163  }
164 
165  ret = ff_v4l2_context_set_format(&s->capture);
166  if (ret) {
167  av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
168  goto error;
169  }
170 
171  ret = ff_v4l2_context_init(&s->output);
172  if (ret) {
173  av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
174  goto error;
175  }
176 
177  /* decoder's buffers need to be updated at a later stage */
178  if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) {
179  ret = ff_v4l2_context_init(&s->capture);
180  if (ret) {
181  av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
182  goto error;
183  }
184  }
185 
186  return 0;
187 
188 error:
189  if (close(s->fd) < 0) {
190  ret = AVERROR(errno);
191  av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
192  s->devname, av_err2str(AVERROR(errno)));
193  }
194  s->fd = -1;
195 
196  return ret;
197 }
198 
199 /******************************************************************************
200  *
201  * V4L2 M2M Interface
202  *
203  ******************************************************************************/
205 {
206  void *log_ctx = s->avctx;
207  int ret;
208 
209  av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n");
210 
211  /* 1. streamoff */
212  ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
213  if (ret)
214  av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
215 
216  /* 2. unmap the capture buffers (v4l2 and ffmpeg):
217  * we must wait for all references to be released before being allowed
218  * to queue new buffers.
219  */
220  av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
221  if (atomic_load(&s->refcount))
222  while(sem_wait(&s->refsync) == -1 && errno == EINTR);
223 
224  ff_v4l2_context_release(&s->capture);
225 
226  /* 3. get the new capture format */
227  ret = ff_v4l2_context_get_format(&s->capture, 0);
228  if (ret) {
229  av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n");
230  return ret;
231  }
232 
233  /* 4. set the capture format */
234  ret = ff_v4l2_context_set_format(&s->capture);
235  if (ret) {
236  av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n");
237  return ret;
238  }
239 
240  /* 5. complete reinit */
241  s->draining = 0;
242  s->reinit = 0;
243 
244  return 0;
245 }
246 
247 static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
248 {
250 
251  ff_v4l2_context_release(&s->capture);
252  sem_destroy(&s->refsync);
253 
254  if (s->fd >= 0)
255  close(s->fd);
256  av_frame_unref(s->frame);
257  av_frame_free(&s->frame);
258  av_packet_unref(&s->buf_pkt);
259 
260  av_free(s);
261 }
262 
264 {
265  V4L2m2mContext *s = priv->context;
266  int ret;
267 
268  if (!s)
269  return 0;
270 
271  if (s->fd >= 0) {
272  ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
273  if (ret)
274  av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
275 
276  ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
277  if (ret)
278  av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
279  }
280 
281  ff_v4l2_context_release(&s->output);
282 
283  s->self_ref = NULL;
285 
286  return 0;
287 }
288 
290 {
291  int ret = AVERROR(EINVAL);
292  struct dirent *entry;
293  DIR *dirp;
294 
295  V4L2m2mContext *s = priv->context;
296 
297  dirp = opendir("/dev");
298  if (!dirp)
299  return AVERROR(errno);
300 
301  for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
302 
303  if (strncmp(entry->d_name, "video", 5))
304  continue;
305 
306  snprintf(s->devname, sizeof(s->devname), "/dev/%s", entry->d_name);
307  av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", s->devname);
309  if (!ret)
310  break;
311  }
312 
313  closedir(dirp);
314 
315  if (ret) {
316  av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
317  memset(s->devname, 0, sizeof(s->devname));
318 
319  return ret;
320  }
321 
322  av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", s->devname);
323 
324  return v4l2_configure_contexts(s);
325 }
326 
328 {
329  *s = av_mallocz(sizeof(V4L2m2mContext));
330  if (!*s)
331  return AVERROR(ENOMEM);
332 
333  priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
335  if (!priv->context_ref) {
336  av_freep(s);
337  return AVERROR(ENOMEM);
338  }
339 
340  /* assign the context */
341  priv->context = *s;
342  (*s)->priv = priv;
343 
344  /* populate it */
347  priv->context->self_ref = priv->context_ref;
348  priv->context->fd = -1;
349 
350  priv->context->frame = av_frame_alloc();
351  if (!priv->context->frame) {
353  *s = NULL; /* freed when unreferencing context_ref */
354  return AVERROR(ENOMEM);
355  }
356 
357  return 0;
358 }
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:31
ff_v4l2_context_init
int ff_v4l2_context_init(V4L2Context *ctx)
Initializes a V4L2Context.
Definition: v4l2_context.c:708
av_packet_unref
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:422
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
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:204
V4L2m2mContext
Definition: v4l2_m2m.h:43
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:111
pixdesc.h
v4l2_mplane_video
static int v4l2_mplane_video(struct v4l2_capability *cap)
Definition: v4l2_m2m.c:50
ff_v4l2_context_release
void ff_v4l2_context_release(V4L2Context *ctx)
Releases a V4L2Context.
Definition: v4l2_context.c:694
v4l2_probe_driver
static int v4l2_probe_driver(V4L2m2mContext *s)
Definition: v4l2_m2m.c:99
V4L2m2mPriv::context
V4L2m2mContext * context
Definition: v4l2_m2m.h:74
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:99
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:180
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:689
v4l2_splane_video
static int v4l2_splane_video(struct v4l2_capability *cap)
Definition: v4l2_m2m.c:38
ff_v4l2_context_get_format
int ff_v4l2_context_get_format(V4L2Context *ctx, int probe)
Queries the driver for a valid v4l2 format and copies it to the context.
Definition: v4l2_context.c:662
v4l2_fmt.h
s
#define s(width, name)
Definition: cbs_vp9.c:256
V4L2m2mContext::capture
V4L2Context capture
Definition: v4l2_m2m.h:48
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:201
v4l2_m2m_destroy_context
static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
Definition: v4l2_m2m.c:247
ff_v4l2_m2m_codec_init
int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
Probes the video nodes looking for the required codec capabilities.
Definition: v4l2_m2m.c:289
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:139
v4l2_configure_contexts
static int v4l2_configure_contexts(V4L2m2mContext *s)
Definition: v4l2_m2m.c:135
v4l2_prepare_contexts
static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe)
Definition: v4l2_m2m.c:62
ff_v4l2_m2m_create_context
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:327
V4L2m2mPriv::num_output_buffers
int num_output_buffers
Definition: v4l2_m2m.h:77
V4L2m2mContext::output
V4L2Context output
Definition: v4l2_m2m.h:49
av_buffer_create
AVBufferRef * av_buffer_create(uint8_t *data, size_t size, void(*free)(void *opaque, uint8_t *data), void *opaque, int flags)
Create an AVBuffer from an existing array.
Definition: buffer.c:55
av_codec_is_decoder
int av_codec_is_decoder(const AVCodec *codec)
Definition: utils.c:82
ff_v4l2_m2m_codec_end
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:263
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:121
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:191
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:558
V4L2Context::num_buffers
int num_buffers
Readonly after init.
Definition: v4l2_context.h:83
av_frame_unref
void av_frame_unref(AVFrame *frame)
Unreference all the buffers referenced by frame and reset the frame fields.
Definition: frame.c:477
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:264
V4L2m2mPriv::context_ref
AVBufferRef * context_ref
Definition: v4l2_m2m.h:75
v4l2_context.h
avcodec.h
sem_destroy
#define sem_destroy(psem)
Definition: semaphore.h:29
ret
ret
Definition: filter_design.txt:187
pixfmt.h
probe
static int probe(const AVProbeData *p)
Definition: act.c:38
V4L2m2mPriv::num_capture_buffers
int num_capture_buffers
Definition: v4l2_m2m.h:78
V4L2m2mPriv
Definition: v4l2_m2m.h:71
V4L2m2mContext::fd
int fd
Definition: v4l2_m2m.h:45
av_free
#define av_free(p)
Definition: tableprint_vlc.h:33
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
v4l2_m2m.h
imgutils.h
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
V4L2m2mContext::self_ref
AVBufferRef * self_ref
Definition: v4l2_m2m.h:65
atomic_init
#define atomic_init(obj, value)
Definition: stdatomic.h:33
snprintf
#define snprintf
Definition: snprintf.h:34
V4L2m2mContext::frame
AVFrame * frame
Definition: v4l2_m2m.h:62
sem_init
#define sem_init
Definition: semaphore.h:40
av_fourcc2str
#define av_fourcc2str(fourcc)
Definition: avutil.h:354