GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/v4l2_m2m.c Lines: 0 228 0.0 %
Date: 2020-10-23 17:01:47 Branches: 0 122 0.0 %

Line Branch Exec 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 "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
63
static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe)
64
{
65
    struct v4l2_capability cap;
66
    void *log_ctx = s->avctx;
67
    int ret;
68
69
    s->capture.done = s->output.done = 0;
70
    s->capture.name = "capture";
71
    s->output.name = "output";
72
    atomic_init(&s->refcount, 0);
73
    sem_init(&s->refsync, 0, 0);
74
75
    memset(&cap, 0, sizeof(cap));
76
    ret = ioctl(s->fd, VIDIOC_QUERYCAP, &cap);
77
    if (ret < 0)
78
        return ret;
79
80
    av_log(log_ctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO,
81
                     "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card,
82
                     v4l2_mplane_video(&cap) ? "mplane" :
83
                     v4l2_splane_video(&cap) ? "splane" : "unknown");
84
85
    if (v4l2_mplane_video(&cap)) {
86
        s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
87
        s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
88
        return 0;
89
    }
90
91
    if (v4l2_splane_video(&cap)) {
92
        s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
93
        s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
94
        return 0;
95
    }
96
97
    return AVERROR(EINVAL);
98
}
99
100
static int v4l2_probe_driver(V4L2m2mContext *s)
101
{
102
    void *log_ctx = s->avctx;
103
    int ret;
104
105
    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
106
    if (s->fd < 0)
107
        return AVERROR(errno);
108
109
    ret = v4l2_prepare_contexts(s, 1);
110
    if (ret < 0)
111
        goto done;
112
113
    ret = ff_v4l2_context_get_format(&s->output, 1);
114
    if (ret) {
115
        av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
116
        goto done;
117
    }
118
119
    ret = ff_v4l2_context_get_format(&s->capture, 1);
120
    if (ret) {
121
        av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
122
        goto done;
123
    }
124
125
done:
126
    if (close(s->fd) < 0) {
127
        ret = AVERROR(errno);
128
        av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
129
    }
130
131
    s->fd = -1;
132
133
    return ret;
134
}
135
136
static int v4l2_configure_contexts(V4L2m2mContext *s)
137
{
138
    void *log_ctx = s->avctx;
139
    int ret;
140
    struct v4l2_format ofmt, cfmt;
141
142
    s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
143
    if (s->fd < 0)
144
        return AVERROR(errno);
145
146
    ret = v4l2_prepare_contexts(s, 0);
147
    if (ret < 0)
148
        goto error;
149
150
    ofmt = s->output.format;
151
    cfmt = s->capture.format;
152
    av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n",
153
                                 av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ?
154
                                               ofmt.fmt.pix_mp.pixelformat :
155
                                               ofmt.fmt.pix.pixelformat),
156
                                 av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ?
157
                                               cfmt.fmt.pix_mp.pixelformat :
158
                                               cfmt.fmt.pix.pixelformat));
159
160
    ret = ff_v4l2_context_set_format(&s->output);
161
    if (ret) {
162
        av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
163
        goto error;
164
    }
165
166
    ret = ff_v4l2_context_set_format(&s->capture);
167
    if (ret) {
168
        av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
169
        goto error;
170
    }
171
172
    ret = ff_v4l2_context_init(&s->output);
173
    if (ret) {
174
        av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
175
        goto error;
176
    }
177
178
    /* decoder's buffers need to be updated at a later stage */
179
    if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) {
180
        ret = ff_v4l2_context_init(&s->capture);
181
        if (ret) {
182
            av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
183
            goto error;
184
        }
185
    }
186
187
    return 0;
188
189
error:
190
    if (close(s->fd) < 0) {
191
        ret = AVERROR(errno);
192
        av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
193
            s->devname, av_err2str(AVERROR(errno)));
194
    }
195
    s->fd = -1;
196
197
    return ret;
198
}
199
200
/******************************************************************************
201
 *
202
 *                  V4L2 M2M Interface
203
 *
204
 ******************************************************************************/
205
int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s)
206
{
207
    void *log_ctx = s->avctx;
208
    int ret;
209
210
    av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n");
211
212
    /* 1. streamoff */
213
    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
214
    if (ret)
215
        av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
216
217
    /* 2. unmap the capture buffers (v4l2 and ffmpeg):
218
     *    we must wait for all references to be released before being allowed
219
     *    to queue new buffers.
220
     */
221
    av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
222
    if (atomic_load(&s->refcount))
223
        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
224
225
    ff_v4l2_context_release(&s->capture);
226
227
    /* 3. get the new capture format */
228
    ret = ff_v4l2_context_get_format(&s->capture, 0);
229
    if (ret) {
230
        av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n");
231
        return ret;
232
    }
233
234
    /* 4. set the capture format */
235
    ret = ff_v4l2_context_set_format(&s->capture);
236
    if (ret) {
237
        av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n");
238
        return ret;
239
    }
240
241
    /* 5. complete reinit */
242
    s->draining = 0;
243
    s->reinit = 0;
244
245
    return 0;
246
}
247
248
int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s)
249
{
250
    void *log_ctx = s->avctx;
251
    int ret;
252
253
    av_log(log_ctx, AV_LOG_DEBUG, "%s full reinit\n", s->devname);
254
255
    /* wait for pending buffer references */
256
    if (atomic_load(&s->refcount))
257
        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
258
259
    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
260
    if (ret) {
261
        av_log(log_ctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n");
262
        goto error;
263
    }
264
265
    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
266
    if (ret) {
267
        av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
268
        goto error;
269
    }
270
271
    /* release and unmmap the buffers */
272
    ff_v4l2_context_release(&s->output);
273
    ff_v4l2_context_release(&s->capture);
274
275
    /* start again now that we know the stream dimensions */
276
    s->draining = 0;
277
    s->reinit = 0;
278
279
    ret = ff_v4l2_context_get_format(&s->output, 0);
280
    if (ret) {
281
        av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
282
        goto error;
283
    }
284
285
    ret = ff_v4l2_context_get_format(&s->capture, 0);
286
    if (ret) {
287
        av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
288
        goto error;
289
    }
290
291
    ret = ff_v4l2_context_set_format(&s->output);
292
    if (ret) {
293
        av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
294
        goto error;
295
    }
296
297
    ret = ff_v4l2_context_set_format(&s->capture);
298
    if (ret) {
299
        av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
300
        goto error;
301
    }
302
303
    ret = ff_v4l2_context_init(&s->output);
304
    if (ret) {
305
        av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
306
        goto error;
307
    }
308
309
    /* decoder's buffers need to be updated at a later stage */
310
    if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) {
311
        ret = ff_v4l2_context_init(&s->capture);
312
        if (ret) {
313
            av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
314
            goto error;
315
        }
316
    }
317
318
    return 0;
319
320
error:
321
    return ret;
322
}
323
324
static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
325
{
326
    V4L2m2mContext *s = (V4L2m2mContext*)context;
327
328
    ff_v4l2_context_release(&s->capture);
329
    sem_destroy(&s->refsync);
330
331
    close(s->fd);
332
    av_frame_unref(s->frame);
333
    av_frame_free(&s->frame);
334
    av_packet_unref(&s->buf_pkt);
335
336
    av_free(s);
337
}
338
339
int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)
340
{
341
    V4L2m2mContext *s = priv->context;
342
    int ret;
343
344
    if (!s)
345
        return 0;
346
347
    if (s->fd >= 0) {
348
        ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
349
        if (ret)
350
            av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
351
352
        ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
353
        if (ret)
354
            av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
355
    }
356
357
    ff_v4l2_context_release(&s->output);
358
359
    s->self_ref = NULL;
360
    av_buffer_unref(&priv->context_ref);
361
362
    return 0;
363
}
364
365
int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
366
{
367
    int ret = AVERROR(EINVAL);
368
    struct dirent *entry;
369
    DIR *dirp;
370
371
    V4L2m2mContext *s = priv->context;
372
373
    dirp = opendir("/dev");
374
    if (!dirp)
375
        return AVERROR(errno);
376
377
    for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
378
379
        if (strncmp(entry->d_name, "video", 5))
380
            continue;
381
382
        snprintf(s->devname, sizeof(s->devname), "/dev/%s", entry->d_name);
383
        av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", s->devname);
384
        ret = v4l2_probe_driver(s);
385
        if (!ret)
386
            break;
387
    }
388
389
    closedir(dirp);
390
391
    if (ret) {
392
        av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n");
393
        memset(s->devname, 0, sizeof(s->devname));
394
395
        return ret;
396
    }
397
398
    av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", s->devname);
399
400
    return v4l2_configure_contexts(s);
401
}
402
403
int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
404
{
405
    *s = av_mallocz(sizeof(V4L2m2mContext));
406
    if (!*s)
407
        return AVERROR(ENOMEM);
408
409
    priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
410
                                         &v4l2_m2m_destroy_context, NULL, 0);
411
    if (!priv->context_ref) {
412
        av_freep(s);
413
        return AVERROR(ENOMEM);
414
    }
415
416
    /* assign the context */
417
    priv->context = *s;
418
    (*s)->priv = priv;
419
420
    /* populate it */
421
    priv->context->capture.num_buffers = priv->num_capture_buffers;
422
    priv->context->output.num_buffers  = priv->num_output_buffers;
423
    priv->context->self_ref = priv->context_ref;
424
    priv->context->fd = -1;
425
426
    priv->context->frame = av_frame_alloc();
427
    if (!priv->context->frame) {
428
        av_buffer_unref(&priv->context_ref);
429
        *s = NULL; /* freed when unreferencing context_ref */
430
        return AVERROR(ENOMEM);
431
    }
432
433
    return 0;
434
}