xref: /third_party/ffmpeg/libavcodec/v4l2_m2m.c (revision cabdff1a)
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
38static 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
50static 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
62static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe)
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
99static int v4l2_probe_driver(V4L2m2mContext *s)
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
108    ret = v4l2_prepare_contexts(s, 1);
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
124done:
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
135static int v4l2_configure_contexts(V4L2m2mContext *s)
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
145    ret = v4l2_prepare_contexts(s, 0);
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
188error:
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 ******************************************************************************/
204int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)
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
247static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
248{
249    V4L2m2mContext *s = (V4L2m2mContext*)context;
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
263int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)
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;
284    av_buffer_unref(&priv->context_ref);
285
286    return 0;
287}
288
289int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
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);
308        ret = v4l2_probe_driver(s);
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
327int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
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),
334                                         &v4l2_m2m_destroy_context, NULL, 0);
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 */
345    priv->context->capture.num_buffers = priv->num_capture_buffers;
346    priv->context->output.num_buffers  = priv->num_output_buffers;
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) {
352        av_buffer_unref(&priv->context_ref);
353        *s = NULL; /* freed when unreferencing context_ref */
354        return AVERROR(ENOMEM);
355    }
356
357    return 0;
358}
359