Coverage Report

Created: 2025-11-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/v4l2_m2m.c
Line
Count
Source
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
40
static inline int v4l2_splane_video(struct v4l2_capability *cap)
41
0
{
42
0
    if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
43
0
        cap->capabilities & V4L2_CAP_STREAMING)
44
0
        return 1;
45
46
0
    if (cap->capabilities & V4L2_CAP_VIDEO_M2M)
47
0
        return 1;
48
49
0
    return 0;
50
0
}
51
52
static inline int v4l2_mplane_video(struct v4l2_capability *cap)
53
0
{
54
0
    if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
55
0
        cap->capabilities & V4L2_CAP_STREAMING)
56
0
        return 1;
57
58
0
    if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
59
0
        return 1;
60
61
0
    return 0;
62
0
}
63
64
static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe)
65
0
{
66
0
    struct v4l2_capability cap;
67
0
    void *log_ctx = s->avctx;
68
0
    int ret;
69
70
0
    s->capture.done = s->output.done = 0;
71
0
    s->capture.name = "capture";
72
0
    s->output.name = "output";
73
0
    atomic_init(&s->refcount, 0);
74
0
    sem_init(&s->refsync, 0, 0);
75
76
0
    memset(&cap, 0, sizeof(cap));
77
0
    ret = ioctl(s->fd, VIDIOC_QUERYCAP, &cap);
78
0
    if (ret < 0)
79
0
        return ret;
80
81
0
    av_log(log_ctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO,
82
0
                     "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card,
83
0
                     v4l2_mplane_video(&cap) ? "mplane" :
84
0
                     v4l2_splane_video(&cap) ? "splane" : "unknown");
85
86
0
    if (v4l2_mplane_video(&cap)) {
87
0
        s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
88
0
        s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
89
0
        return 0;
90
0
    }
91
92
0
    if (v4l2_splane_video(&cap)) {
93
0
        s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
94
0
        s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
95
0
        return 0;
96
0
    }
97
98
0
    return AVERROR(EINVAL);
99
0
}
100
101
static int v4l2_probe_driver(V4L2m2mContext *s)
102
0
{
103
0
    void *log_ctx = s->avctx;
104
0
    int ret;
105
106
0
    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
107
0
    if (s->fd < 0)
108
0
        return AVERROR(errno);
109
110
0
    ret = v4l2_prepare_contexts(s, 1);
111
0
    if (ret < 0)
112
0
        goto done;
113
114
0
    ret = ff_v4l2_context_get_format(&s->output, 1);
115
0
    if (ret) {
116
0
        av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
117
0
        goto done;
118
0
    }
119
120
0
    ret = ff_v4l2_context_get_format(&s->capture, 1);
121
0
    if (ret) {
122
0
        av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
123
0
        goto done;
124
0
    }
125
126
0
done:
127
0
    if (close(s->fd) < 0) {
128
0
        ret = AVERROR(errno);
129
0
        av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
130
0
    }
131
132
0
    s->fd = -1;
133
134
0
    return ret;
135
0
}
136
137
static av_cold int v4l2_configure_contexts(V4L2m2mContext *s)
138
0
{
139
0
    void *log_ctx = s->avctx;
140
0
    int ret;
141
0
    struct v4l2_format ofmt, cfmt;
142
143
0
    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
144
0
    if (s->fd < 0)
145
0
        return AVERROR(errno);
146
147
0
    ret = v4l2_prepare_contexts(s, 0);
148
0
    if (ret < 0)
149
0
        goto error;
150
151
0
    ofmt = s->output.format;
152
0
    cfmt = s->capture.format;
153
0
    av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s/%s capture=%s/%s\n",
154
0
                                 av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ?
155
0
                                               ofmt.fmt.pix_mp.pixelformat :
156
0
                                               ofmt.fmt.pix.pixelformat),
157
0
                                 av_get_pix_fmt_name(s->output.av_pix_fmt) ?: "none",
158
0
                                 av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ?
159
0
                                               cfmt.fmt.pix_mp.pixelformat :
160
0
                                               cfmt.fmt.pix.pixelformat),
161
0
                                 av_get_pix_fmt_name(s->capture.av_pix_fmt) ?: "none");
162
163
0
    ret = ff_v4l2_context_set_format(&s->output);
164
0
    if (ret) {
165
0
        av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
166
0
        goto error;
167
0
    }
168
169
0
    ret = ff_v4l2_context_set_format(&s->capture);
170
0
    if (ret) {
171
0
        av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
172
0
        goto error;
173
0
    }
174
175
0
    ret = ff_v4l2_context_init(&s->output);
176
0
    if (ret) {
177
0
        av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
178
0
        goto error;
179
0
    }
180
181
    /* decoder's buffers need to be updated at a later stage */
182
0
    if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) {
183
0
        ret = ff_v4l2_context_init(&s->capture);
184
0
        if (ret) {
185
0
            av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
186
0
            goto error;
187
0
        }
188
0
    }
189
190
0
    return 0;
191
192
0
error:
193
0
    if (close(s->fd) < 0) {
194
0
        ret = AVERROR(errno);
195
0
        av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
196
0
            s->devname, av_err2str(AVERROR(errno)));
197
0
    }
198
0
    s->fd = -1;
199
200
0
    return ret;
201
0
}
202
203
/******************************************************************************
204
 *
205
 *                  V4L2 M2M Interface
206
 *
207
 ******************************************************************************/
208
int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)
209
0
{
210
0
    void *log_ctx = s->avctx;
211
0
    int ret;
212
213
0
    av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n");
214
215
    /* 1. streamoff */
216
0
    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
217
0
    if (ret)
218
0
        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
0
    av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
225
0
    if (atomic_load(&s->refcount))
226
0
        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
227
228
0
    ff_v4l2_context_release(&s->capture);
229
230
    /* 3. get the new capture format */
231
0
    ret = ff_v4l2_context_get_format(&s->capture, 0);
232
0
    if (ret) {
233
0
        av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n");
234
0
        return ret;
235
0
    }
236
237
    /* 4. set the capture format */
238
0
    ret = ff_v4l2_context_set_format(&s->capture);
239
0
    if (ret) {
240
0
        av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n");
241
0
        return ret;
242
0
    }
243
244
    /* 5. complete reinit */
245
0
    s->draining = 0;
246
0
    s->reinit = 0;
247
248
0
    return 0;
249
0
}
250
251
static void v4l2_m2m_destroy_context(AVRefStructOpaque unused, void *context)
252
2.35k
{
253
2.35k
    V4L2m2mContext *s = context;
254
255
2.35k
    ff_v4l2_context_release(&s->capture);
256
2.35k
    sem_destroy(&s->refsync);
257
258
2.35k
    if (s->fd >= 0)
259
0
        close(s->fd);
260
2.35k
    av_frame_free(&s->frame);
261
2.35k
    av_packet_unref(&s->buf_pkt);
262
2.35k
}
263
264
av_cold int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)
265
2.35k
{
266
2.35k
    V4L2m2mContext *s = priv->context;
267
2.35k
    int ret;
268
269
2.35k
    if (!s)
270
0
        return 0;
271
272
2.35k
    if (s->fd >= 0) {
273
0
        ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
274
0
        if (ret)
275
0
            av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
276
277
0
        ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
278
0
        if (ret)
279
0
            av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
280
0
    }
281
282
2.35k
    ff_v4l2_context_release(&s->output);
283
284
2.35k
    s->self_ref = NULL;
285
2.35k
    av_refstruct_unref(&priv->context);
286
287
2.35k
    return 0;
288
2.35k
}
289
290
av_cold int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
291
2.35k
{
292
2.35k
    int ret = AVERROR(EINVAL);
293
2.35k
    struct dirent *entry;
294
2.35k
    DIR *dirp;
295
296
2.35k
    V4L2m2mContext *s = priv->context;
297
298
2.35k
    dirp = opendir("/dev");
299
2.35k
    if (!dirp)
300
0
        return AVERROR(errno);
301
302
336k
    for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
303
304
334k
        if (strncmp(entry->d_name, "video", 5))
305
334k
            continue;
306
307
0
        snprintf(s->devname, sizeof(s->devname), "/dev/%s", entry->d_name);
308
0
        av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", s->devname);
309
0
        ret = v4l2_probe_driver(s);
310
0
        if (!ret)
311
0
            break;
312
0
    }
313
314
2.35k
    closedir(dirp);
315
316
2.35k
    if (ret) {
317
2.35k
        av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
318
2.35k
        memset(s->devname, 0, sizeof(s->devname));
319
320
2.35k
        return ret;
321
2.35k
    }
322
323
0
    av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", s->devname);
324
325
0
    return v4l2_configure_contexts(s);
326
2.35k
}
327
328
int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
329
2.35k
{
330
2.35k
    *s = av_refstruct_alloc_ext(sizeof(**s), 0, NULL,
331
2.35k
                                &v4l2_m2m_destroy_context);
332
2.35k
    if (!*s)
333
0
        return AVERROR(ENOMEM);
334
335
    /* assign the context */
336
2.35k
    priv->context = *s;
337
2.35k
    (*s)->priv = priv;
338
339
    /* populate it */
340
2.35k
    priv->context->capture.num_buffers = priv->num_capture_buffers;
341
2.35k
    priv->context->output.num_buffers  = priv->num_output_buffers;
342
2.35k
    priv->context->self_ref = priv->context;
343
2.35k
    priv->context->fd = -1;
344
345
2.35k
    priv->context->frame = av_frame_alloc();
346
2.35k
    if (!priv->context->frame) {
347
0
        av_refstruct_unref(&priv->context);
348
0
        *s = NULL; /* freed when unreferencing context */
349
0
        return AVERROR(ENOMEM);
350
0
    }
351
352
2.35k
    return 0;
353
2.35k
}