demux.c 21.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * demux.c : V4L2 raw video demux module for vlc
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002-2011 VLC authors and VideoLAN
5 6 7 8 9 10
 *
 * Authors: Benjamin Pracht <bigben at videolan dot org>
 *          Richard Hosking <richard at hovis dot net>
 *          Antoine Cellerier <dionoea at videolan d.t org>
 *          Dennis Lou <dlou99 at yahoo dot com>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
13
 * the Free Software Foundation; either version 2.1 of the License, or
14 15 16 17
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
19
 * GNU Lesser General Public License for more details.
20
 *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
21 22 23
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 25 26 27 28 29
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30
#include <math.h>
31
#include <errno.h>
32
#include <assert.h>
33
#include <sys/ioctl.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
34
#include <sys/mman.h>
Rafaël Carré's avatar
Rafaël Carré committed
35 36 37
#ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
#endif
38 39
#include <poll.h>

40
#include <vlc_common.h>
41 42
#include <vlc_demux.h>

43 44
#include "v4l2.h"

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
struct demux_sys_t
{
    int fd;
    vlc_thread_t thread;

    struct buffer_t *bufv;
    union
    {
        uint32_t bufc;
        uint32_t blocksize;
    };
    uint32_t block_flags;

    es_out_id_t *es;
    vlc_v4l2_ctrl_t *controls;
60
    mtime_t start;
61 62

#ifdef ZVBI_COMPILED
63
    vlc_v4l2_vbi_t *vbi;
64
#endif
65 66
};

67 68
static void *UserPtrThread (void *);
static void *MmapThread (void *);
69
static void *ReadThread (void *);
70
static int DemuxControl( demux_t *, int, va_list );
71
static int InitVideo (demux_t *, int fd, uint32_t caps);
72 73 74 75

int DemuxOpen( vlc_object_t *obj )
{
    demux_t *demux = (demux_t *)obj;
76

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
77 78
    demux_sys_t *sys = malloc (sizeof (*sys));
    if (unlikely(sys == NULL))
79 80
        return VLC_ENOMEM;
    demux->p_sys = sys;
81
#ifdef ZVBI_COMPILED
82
    sys->vbi = NULL;
83
#endif
84 85

    ParseMRL( obj, demux->psz_location );
86 87 88 89 90

    char *path = var_InheritString (obj, CFG_PREFIX"dev");
    if (unlikely(path == NULL))
        goto error; /* probably OOM */

91 92
    uint32_t caps;
    int fd = OpenDevice (obj, path, &caps);
93 94
    free (path);
    if (fd == -1)
95
        goto error;
96
    sys->fd = fd;
97

98
    if (InitVideo (demux, fd, caps))
99
    {
100 101
        v4l2_close (fd);
        goto error;
102 103
    }

104
    sys->controls = ControlsInit (VLC_OBJECT(demux), fd);
105
    sys->start = mdate ();
106
    demux->pf_demux = NULL;
107 108 109 110 111
    demux->pf_control = DemuxControl;
    demux->info.i_update = 0;
    demux->info.i_title = 0;
    demux->info.i_seekpoint = 0;
    return VLC_SUCCESS;
112 113 114
error:
    free (sys);
    return VLC_EGENERIC;
115 116
}

117
typedef struct
118
{
119 120
    uint32_t v4l2;
    vlc_fourcc_t vlc;
121
    uint8_t bpp; /**< Bytes per pixel (largest plane) */
122 123 124 125 126
    uint32_t red;
    uint32_t green;
    uint32_t blue;
} vlc_v4l2_fmt_t;

127 128
/* NOTE: Currently vlc_v4l2_fmt_rank() assumes format are sorted in order of
 * decreasing preference. */
129
static const vlc_v4l2_fmt_t v4l2_fmts[] =
130
{
131
    /* Planar YUV 4:2:0 */
132 133 134
    { V4L2_PIX_FMT_YUV420,  VLC_CODEC_I420, 1, 0, 0, 0 },
    { V4L2_PIX_FMT_YVU420,  VLC_CODEC_YV12, 1, 0, 0, 0 },
    { V4L2_PIX_FMT_YUV422P, VLC_CODEC_I422, 1, 0, 0, 0 },
135
    /* Packed YUV 4:2:2 */
136 137 138 139
    { V4L2_PIX_FMT_YUYV,    VLC_CODEC_YUYV, 2, 0, 0, 0 },
    { V4L2_PIX_FMT_UYVY,    VLC_CODEC_UYVY, 2, 0, 0, 0 },
    { V4L2_PIX_FMT_YVYU,    VLC_CODEC_YVYU, 2, 0, 0, 0 },
    { V4L2_PIX_FMT_VYUY,    VLC_CODEC_VYUY, 2, 0, 0, 0 },
140

141
    { V4L2_PIX_FMT_YUV411P, VLC_CODEC_I411, 1, 0, 0, 0 },
142

143
    { V4L2_PIX_FMT_YUV410,  VLC_CODEC_I410, 1, 0, 0, 0 },
144 145 146 147
//  { V4L2_PIX_FMT_YVU410     },

//  { V4L2_PIX_FMT_NV24,      },
//  { V4L2_PIX_FMT_NV42,      },
148 149 150 151
//  { V4L2_PIX_FMT_NV16,    VLC_CODEC_NV16, 1, 0, 0, 0 },
//  { V4L2_PIX_FMT_NV61,    VLC_CODEC_NV61, 1, 0, 0, 0 },
    { V4L2_PIX_FMT_NV12,    VLC_CODEC_NV12, 1, 0, 0, 0 },
    { V4L2_PIX_FMT_NV21,    VLC_CODEC_NV21, 1, 0, 0, 0 },
152 153 154 155 156 157 158 159

    /* V4L2-documented but VLC-unsupported misc. YUV formats */
//  { V4L2_PIX_FMT_Y41P       },
//  { V4L2_PIX_FMT_NV12MT,    },
//  { V4L2_PIX_FMT_M420,      },

    /* Packed RGB */
#ifdef WORDS_BIGENDIAN
160 161 162 163
    { V4L2_PIX_FMT_RGB32,   VLC_CODEC_RGB32, 4, 0xFF00, 0xFF0000, 0xFF000000 },
    { V4L2_PIX_FMT_BGR32,   VLC_CODEC_RGB32, 4, 0xFF000000, 0xFF0000, 0xFF00 },
    { V4L2_PIX_FMT_RGB24,   VLC_CODEC_RGB24, 3, 0xFF0000, 0x00FF00, 0x0000FF },
    { V4L2_PIX_FMT_BGR24,   VLC_CODEC_RGB24, 3, 0x0000FF, 0x00FF00, 0xFF0000 },
164 165
//  { V4L2_PIX_FMT_BGR666,    },
//  { V4L2_PIX_FMT_RGB565,    },
166
    { V4L2_PIX_FMT_RGB565X, VLC_CODEC_RGB16, 2,  0x001F,   0x07E0,   0xF800 },
167
//  { V4L2_PIX_FMT_RGB555,    },
168 169
    { V4L2_PIX_FMT_RGB555X, VLC_CODEC_RGB15, 2,  0x001F,   0x03E0,   0x7C00 },
//  { V4L2_PIX_FMT_RGB444,  VLC_CODEC_RGB12, 2,  0x000F,   0xF000,   0x0F00 },
170
#else
171 172 173 174
    { V4L2_PIX_FMT_RGB32,   VLC_CODEC_RGB32, 4, 0x0000FF, 0x00FF00, 0xFF0000 },
    { V4L2_PIX_FMT_BGR32,   VLC_CODEC_RGB32, 4, 0xFF0000, 0x00FF00, 0x0000FF },
    { V4L2_PIX_FMT_RGB24,   VLC_CODEC_RGB24, 3, 0x0000FF, 0x00FF00, 0xFF0000 },
    { V4L2_PIX_FMT_BGR24,   VLC_CODEC_RGB24, 3, 0xFF0000, 0x00FF00, 0x0000FF },
175
//  { V4L2_PIX_FMT_BGR666,    },
176
    { V4L2_PIX_FMT_RGB565,  VLC_CODEC_RGB16, 2,   0x001F,   0x07E0,   0xF800 },
177
//  { V4L2_PIX_FMT_RGB565X,   },
178
    { V4L2_PIX_FMT_RGB555,  VLC_CODEC_RGB15, 2,   0x001F,   0x03E0,   0x7C00 },
179
//  { V4L2_PIX_FMT_RGB555X,   },
180
//  { V4L2_PIX_FMT_RGB444,  VLC_CODEC_RGB12, 2,   0x0F00,   0x00F0,   0x000F },
181
#endif
182
//  { V4L2_PIX_FMT_RGB332,  VLC_CODEC_RGB8,  1,      0xC0,     0x38,     0x07 },
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

    /* Bayer (sub-sampled RGB). Not supported. */
//  { V4L2_PIX_FMT_SBGGR16,  }
//  { V4L2_PIX_FMT_SRGGB12,  }
//  { V4L2_PIX_FMT_SGRBG12,  }
//  { V4L2_PIX_FMT_SGBRG12,  }
//  { V4L2_PIX_FMT_SBGGR12,  }
//  { V4L2_PIX_FMT_SRGGB10,  }
//  { V4L2_PIX_FMT_SGRBG10,  }
//  { V4L2_PIX_FMT_SGBRG10,  }
//  { V4L2_PIX_FMT_SBGGR10,  }
//  { V4L2_PIX_FMT_SBGGR8,   }
//  { V4L2_PIX_FMT_SGBRG8,   }
//  { V4L2_PIX_FMT_SGRBG8,   }
//  { V4L2_PIX_FMT_SRGGB8,   }
198 199

    /* Compressed data types */
200 201
    { V4L2_PIX_FMT_JPEG,    VLC_CODEC_MJPG, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_H264,    VLC_CODEC_H264, 0, 0, 0, 0 },
202
    /* FIXME: fill p_extra for avc1... */
203 204 205 206 207 208 209 210
//  { V4L2_PIX_FMT_H264_NO_SC, VLC_FOURCC('a','v','c','1'), 0, 0, 0, 0 }
    { V4L2_PIX_FMT_MPEG4,   VLC_CODEC_MP4V, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_XVID,    VLC_CODEC_MP4V, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_H263,    VLC_CODEC_H263, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_MPEG2,   VLC_CODEC_MPGV, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_MPEG1,   VLC_CODEC_MPGV, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_VC1_ANNEX_G, VLC_CODEC_VC1, 0, 0, 0, 0 },
    { V4L2_PIX_FMT_VC1_ANNEX_L, VLC_CODEC_VC1, 0, 0, 0, 0 },
211 212 213
    //V4L2_PIX_FMT_MPEG -> use access

    /* Reserved formats */
214
    { V4L2_PIX_FMT_MJPEG,   VLC_CODEC_MJPG, 0, 0, 0, 0 },
215 216 217 218 219 220 221
    //V4L2_PIX_FMT_DV -> use access

    /* Grey scale */
//  { V4L2_PIX_FMT_Y16,       },
//  { V4L2_PIX_FMT_Y12,       },
//  { V4L2_PIX_FMT_Y10,       },
//  { V4L2_PIX_FMT_Y10BPACK,  },
222
    { V4L2_PIX_FMT_GREY,    VLC_CODEC_GREY, 1, 0, 0, 0 },
223 224
};

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
static const vlc_v4l2_fmt_t *vlc_from_v4l2_fourcc (uint32_t fourcc)
{
     for (size_t i = 0; i < sizeof (v4l2_fmts) / sizeof (v4l2_fmts[0]); i++)
         if (v4l2_fmts[i].v4l2 == fourcc)
             return v4l2_fmts + i;
     return NULL;
}

static size_t vlc_v4l2_fmt_rank (const vlc_v4l2_fmt_t *fmt)
{
    if (fmt == NULL)
        return SIZE_MAX;

    ptrdiff_t d = fmt - v4l2_fmts;
    assert (d >= 0);
    assert (d < (ptrdiff_t)(sizeof (v4l2_fmts) / sizeof (v4l2_fmts[0])));
    return d;
}

static vlc_fourcc_t var_InheritFourCC (vlc_object_t *obj, const char *varname)
{
    char *str = var_InheritString (obj, varname);
    if (str == NULL)
        return 0;

    vlc_fourcc_t fourcc = vlc_fourcc_GetCodecFromString (VIDEO_ES, str);
    if (fourcc == 0)
        msg_Err (obj, "invalid codec %s", str);
    free (str);
    return fourcc;
}
#define var_InheritFourCC(o, v) var_InheritFourCC(VLC_OBJECT(o), v)

258 259 260 261 262 263 264 265 266 267 268 269 270 271
static void GetAR (int fd, unsigned *restrict num, unsigned *restrict den)
{
    struct v4l2_cropcap cropcap = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };

    /* TODO: get CROPCAP only once (see ResetCrop()). */
    if (v4l2_ioctl (fd, VIDIOC_CROPCAP, &cropcap) < 0)
    {
        *num = *den = 1;
        return;
    }
    *num = cropcap.pixelaspect.numerator;
    *den = cropcap.pixelaspect.denominator;
}

272
static int InitVideo (demux_t *demux, int fd, uint32_t caps)
273 274
{
    demux_sys_t *sys = demux->p_sys;
275
    v4l2_std_id std;
276

277
    if (!(caps & V4L2_CAP_VIDEO_CAPTURE))
278 279 280 281 282
    {
        msg_Err (demux, "not a video capture device");
        return -1;
    }

283
    if (SetupInput (VLC_OBJECT(demux), fd, &std))
284 285
        return -1;

286 287 288 289
    /* Picture format negotiation */
    const vlc_v4l2_fmt_t *selected = NULL;
    vlc_fourcc_t reqfourcc = var_InheritFourCC (demux, CFG_PREFIX"chroma");
    bool native = false;
290

291 292 293 294 295
    for (struct v4l2_fmtdesc codec = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
         v4l2_ioctl (fd, VIDIOC_ENUM_FMT, &codec) >= 0;
         codec.index++)
    {   /* Enumerate available chromas */
        const vlc_v4l2_fmt_t *dsc = vlc_from_v4l2_fourcc (codec.pixelformat);
296

297 298 299 300 301 302
        msg_Dbg (demux, " %s %s format %4.4s (%4.4s): %s",
              (codec.flags & V4L2_FMT_FLAG_EMULATED) ? "emulates" : "supports",
              (codec.flags & V4L2_FMT_FLAG_COMPRESSED) ? "compressed" : "raw",
                 (char *)&codec.pixelformat,
                 (dsc != NULL) ? (const char *)&dsc->vlc : "N.A.",
                 codec.description);
303

304 305
        if (dsc == NULL)
            continue; /* ignore VLC-unsupported codec */
306

307 308 309 310 311 312 313
        if (dsc->vlc == reqfourcc)
        {
            msg_Dbg (demux, "  matches the requested format");
            selected = dsc;
            break; /* always select the requested format if found */
        }

314 315 316 317
        if (codec.flags & V4L2_FMT_FLAG_EMULATED)
        {
            if (native)
                continue; /* ignore emulated format if possible */
318
        }
319 320
        else
            native = true;
321

322 323
        if (vlc_v4l2_fmt_rank (dsc) > vlc_v4l2_fmt_rank (selected))
            continue; /* ignore if rank is worse */
324

325
        selected = dsc;
326
    }
327 328

    if (selected == NULL)
329
    {
330 331
        msg_Err (demux, "cannot negotiate supported video format");
        return -1;
332
    }
333 334
    msg_Dbg (demux, "selected format %4.4s (%4.4s)",
             (const char *)&selected->v4l2, (const char *)&selected->vlc);
335

336 337 338 339
    /* Find best resolution and frame rate available */
    struct v4l2_format fmt;
    struct v4l2_streamparm parm;
    if (SetupFormat (demux, fd, selected->v4l2, &fmt, &parm))
340
        return -1;
341 342 343 344 345

    /* Print extra info */
    msg_Dbg (demux, "%d bytes maximum for complete image",
             fmt.fmt.pix.sizeimage);
    /* Check interlacing */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
346
    sys->block_flags = 0;
347 348 349 350 351 352 353 354 355 356 357 358 359 360
    switch (fmt.fmt.pix.field)
    {
        case V4L2_FIELD_NONE:
            msg_Dbg (demux, "Interlacing setting: progressive");
            break;
        case V4L2_FIELD_TOP:
            msg_Dbg (demux, "Interlacing setting: top field only");
            break;
        case V4L2_FIELD_BOTTOM:
            msg_Dbg (demux, "Interlacing setting: bottom field only");
            break;
        case V4L2_FIELD_INTERLACED:
            msg_Dbg (demux, "Interlacing setting: interleaved");
            /*if (NTSC)
361
                sys->block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
362
            else*/
363
                sys->block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
364 365 366 367 368 369 370 371 372
            break;
        case V4L2_FIELD_SEQ_TB:
            msg_Dbg (demux, "Interlacing setting: sequential top bottom (TODO)");
            break;
        case V4L2_FIELD_SEQ_BT:
            msg_Dbg (demux, "Interlacing setting: sequential bottom top (TODO)");
            break;
        case V4L2_FIELD_ALTERNATE:
            msg_Dbg (demux, "Interlacing setting: alternate fields (TODO)");
373
            fmt.fmt.pix.height *= 2;
374 375 376
            break;
        case V4L2_FIELD_INTERLACED_TB:
            msg_Dbg (demux, "Interlacing setting: interleaved top bottom");
377
            sys->block_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
378 379 380
            break;
        case V4L2_FIELD_INTERLACED_BT:
            msg_Dbg (demux, "Interlacing setting: interleaved bottom top");
381
            sys->block_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
382 383 384 385 386 387 388
            break;
        default:
            msg_Warn (demux, "Interlacing setting: unknown type (%d)",
                      fmt.fmt.pix.field);
            break;
    }

389
    /* Declare our unique elementary (video) stream */
390
    es_format_t es_fmt;
391

392 393 394 395
    es_format_Init (&es_fmt, VIDEO_ES, selected->vlc);
    es_fmt.video.i_rmask = selected->red;
    es_fmt.video.i_gmask = selected->green;
    es_fmt.video.i_bmask = selected->blue;
396 397 398 399
    es_fmt.video.i_width = fmt.fmt.pix.width;
    es_fmt.video.i_height = fmt.fmt.pix.height;
    es_fmt.video.i_frame_rate = parm.parm.capture.timeperframe.denominator;
    es_fmt.video.i_frame_rate_base = parm.parm.capture.timeperframe.numerator;
400
    GetAR (fd, &es_fmt.video.i_sar_num, &es_fmt.video.i_sar_den);
401

402
    msg_Dbg (demux, "added new video ES %4.4s %ux%u", (char *)&es_fmt.i_codec,
403 404 405
             es_fmt.video.i_width, es_fmt.video.i_height);
    msg_Dbg (demux, " frame rate: %u/%u", es_fmt.video.i_frame_rate,
             es_fmt.video.i_frame_rate_base);
406 407
    msg_Dbg (demux, " aspect ratio: %u/%u", es_fmt.video.i_sar_num,
             es_fmt.video.i_sar_den);
408
    sys->es = es_out_Add (demux->out, &es_fmt);
409 410

    /* Init I/O method */
411 412
    void *(*entry) (void *);
    if (caps & V4L2_CAP_STREAMING)
413
    {
414
        if (0 /* BROKEN */ && StartUserPtr (VLC_OBJECT(demux), fd) == 0)
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
        {
            /* In principles, mmap() will pad the length to a multiple of the
             * page size, so there is no need to care. Nevertheless with the
             * page size, block->i_size can be set optimally. */
            const long pagemask = sysconf (_SC_PAGE_SIZE) - 1;

            sys->blocksize = (fmt.fmt.pix.sizeimage + pagemask) & ~pagemask;
            sys->bufv = NULL;
            entry = UserPtrThread;
            msg_Dbg (demux, "streaming with %"PRIu32"-bytes user buffers",
                     sys->blocksize);
        }
        else /* fall back to memory map */
        {
            sys->bufc = 4;
            sys->bufv = StartMmap (VLC_OBJECT(demux), fd, &sys->bufc);
            if (sys->bufv == NULL)
                return -1;
            entry = MmapThread;
            msg_Dbg (demux, "streaming with %"PRIu32" memory-mapped buffers",
                     sys->bufc);
        }
437 438 439 440
    }
    else if (caps & V4L2_CAP_READWRITE)
    {
        sys->blocksize = fmt.fmt.pix.sizeimage;
441
        sys->bufv = NULL;
442
        entry = ReadThread;
Rémi Duraffort's avatar
Rémi Duraffort committed
443
        msg_Dbg (demux, "reading %"PRIu32" bytes at a time", sys->blocksize);
444 445 446
    }
    else
    {
447
        msg_Err (demux, "no supported capture method");
448
        return -1;
449 450
    }

451
#ifdef ZVBI_COMPILED
452
    if (std & V4L2_STD_NTSC_M)
453
    {
454 455 456 457
        char *vbi_path = var_InheritString (demux, CFG_PREFIX"vbidev");
        if (vbi_path != NULL)
            sys->vbi = OpenVBI (demux, vbi_path);
        free(vbi_path);
458 459 460
    }
#endif

461
    if (vlc_clone (&sys->thread, entry, demux, VLC_THREAD_PRIORITY_INPUT))
462
    {
463 464 465 466
#ifdef ZVBI_COMPILED
        if (sys->vbi != NULL)
            CloseVBI (sys->vbi);
#endif
467 468
        if (sys->bufv != NULL)
            StopMmap (sys->fd, sys->bufv, sys->bufc);
469
        return -1;
470
    }
471 472 473
    return 0;
}

474 475 476 477 478
void DemuxClose( vlc_object_t *obj )
{
    demux_t *demux = (demux_t *)obj;
    demux_sys_t *sys = demux->p_sys;

479 480
    vlc_cancel (sys->thread);
    vlc_join (sys->thread, NULL);
481
    if (sys->bufv != NULL)
482
        StopMmap (sys->fd, sys->bufv, sys->bufc);
483
    ControlsDeinit( obj, sys->controls );
484
    v4l2_close (sys->fd);
485 486

#ifdef ZVBI_COMPILED
487 488
    if (sys->vbi != NULL)
        CloseVBI (sys->vbi);
489 490
#endif

491 492 493
    free( sys );
}

494 495 496 497
/** Allocates and queue a user buffer using mmap(). */
static block_t *UserPtrQueue (vlc_object_t *obj, int fd, size_t length)
{
    void *ptr = mmap (NULL, length, PROT_READ | PROT_WRITE,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
498
                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
    if (ptr == MAP_FAILED)
    {
        msg_Err (obj, "cannot allocate %zu-bytes buffer: %m", length);
        return NULL;
    }

    block_t *block = block_mmap_Alloc (ptr, length);
    if (unlikely(block == NULL))
    {
        munmap (ptr, length);
        return NULL;
    }

    struct v4l2_buffer buf = {
        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
        .memory = V4L2_MEMORY_USERPTR,
        .m = {
            .userptr = (uintptr_t)ptr,
        },
        .length = length,
    };

    if (v4l2_ioctl (fd, VIDIOC_QBUF, &buf) < 0)
    {
        msg_Err (obj, "cannot queue buffer: %m");
        block_Release (block);
        return NULL;
    }
    return block;
}

static void *UserPtrThread (void *data)
{
    demux_t *demux = data;
    demux_sys_t *sys = demux->p_sys;
    int fd = sys->fd;
535 536
    struct pollfd ufd[2];
    nfds_t numfds = 1;
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554

    ufd[0].fd = fd;
    ufd[0].events = POLLIN;

    int canc = vlc_savecancel ();
    for (;;)
    {
        struct v4l2_buffer buf = {
            .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
            .memory = V4L2_MEMORY_USERPTR,
        };
        block_t *block = UserPtrQueue (VLC_OBJECT(demux), fd, sys->blocksize);
        if (block == NULL)
            break;

        /* Wait for data */
        vlc_restorecancel (canc);
        block_cleanup_push (block);
555
        while (poll (ufd, numfds, -1) == -1)
556 557 558 559 560
           if (errno != EINTR)
               msg_Err (demux, "poll error: %m");
        vlc_cleanup_pop ();
        canc = vlc_savecancel ();

561
        if (v4l2_ioctl (fd, VIDIOC_DQBUF, &buf) < 0)
562
        {
563 564 565
            msg_Err (demux, "cannot dequeue buffer: %m");
            block_Release (block);
            continue;
566
        }
567 568 569

        assert (block->p_buffer == (void *)buf.m.userptr);
        block->i_buffer = buf.length;
570
        block->i_pts = block->i_dts = GetBufferPTS (&buf);
571 572 573
        block->i_flags |= sys->block_flags;
        es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
        es_out_Send (demux->out, sys->es, block);
574 575 576 577 578 579
    }
    vlc_restorecancel (canc); /* <- hmm, this is purely cosmetic */
    return NULL;
}

static void *MmapThread (void *data)
580 581 582
{
    demux_t *demux = data;
    demux_sys_t *sys = demux->p_sys;
583
    int fd = sys->fd;
584 585
    struct pollfd ufd[2];
    nfds_t numfds = 1;
586 587

    ufd[0].fd = fd;
588
    ufd[0].events = POLLIN;
589

590
#ifdef ZVBI_COMPILED
591
    if (sys->vbi != NULL)
592
    {
593
        ufd[1].fd = GetFdVBI (sys->vbi);
594 595 596 597 598
        ufd[1].events = POLLIN;
        numfds++;
    }
#endif

599 600 601
    for (;;)
    {
        /* Wait for data */
602
        if (poll (ufd, numfds, -1) == -1)
603 604 605 606 607 608
        {
           if (errno != EINTR)
               msg_Err (demux, "poll error: %m");
           continue;
        }

609
        if( ufd[0].revents )
610
        {
611 612 613 614 615 616 617 618 619
            int canc = vlc_savecancel ();
            block_t *block = GrabVideo (VLC_OBJECT(demux), fd, sys->bufv);
            if (block != NULL)
            {
                block->i_flags |= sys->block_flags;
                es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
                es_out_Send (demux->out, sys->es, block);
            }
            vlc_restorecancel (canc);
620
        }
621
#ifdef ZVBI_COMPILED
622 623
        if (sys->vbi != NULL && ufd[1].revents)
            GrabVBI (demux, sys->vbi);
624
#endif
625 626 627 628 629 630 631 632 633
    }

    assert (0);
}

static void *ReadThread (void *data)
{
    demux_t *demux = data;
    demux_sys_t *sys = demux->p_sys;
634
    int fd = sys->fd;
635 636
    struct pollfd ufd[2];
    nfds_t numfds = 1;
637 638

    ufd[0].fd = fd;
639
    ufd[0].events = POLLIN;
640

641
#ifdef ZVBI_COMPILED
642
    if (sys->vbi != NULL)
643
    {
644
        ufd[1].fd = GetFdVBI (sys->vbi);
645 646 647 648 649
        ufd[1].events = POLLIN;
        numfds++;
    }
#endif

650 651 652
    for (;;)
    {
        /* Wait for data */
653
        if (poll (ufd, numfds, -1) == -1)
654 655 656 657 658 659
        {
           if (errno != EINTR)
               msg_Err (demux, "poll error: %m");
           continue;
        }

660
        if( ufd[0].revents )
661
        {
662 663 664 665 666 667 668 669 670
            block_t *block = block_Alloc (sys->blocksize);
            if (unlikely(block == NULL))
            {
                msg_Err (demux, "read error: %m");
                v4l2_read (fd, NULL, 0); /* discard frame */
                continue;
            }
            block->i_pts = block->i_dts = mdate ();
            block->i_flags |= sys->block_flags;
671

672 673 674 675 676 677 678 679 680 681 682
            int canc = vlc_savecancel ();
            ssize_t val = v4l2_read (fd, block->p_buffer, block->i_buffer);
            if (val != -1)
            {
                block->i_buffer = val;
                es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts);
                es_out_Send (demux->out, sys->es, block);
            }
            else
                block_Release (block);
            vlc_restorecancel (canc);
683
        }
684
#ifdef ZVBI_COMPILED
685 686
        if (sys->vbi != NULL && ufd[1].revents)
            GrabVBI (demux, sys->vbi);
687
#endif
688 689 690 691
    }
    assert (0);
}

692 693
static int DemuxControl( demux_t *demux, int query, va_list args )
{
694 695
    demux_sys_t *sys = demux->p_sys;

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
    switch( query )
    {
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_SEEK:
        case DEMUX_CAN_CONTROL_PACE:
            *va_arg( args, bool * ) = false;
            return VLC_SUCCESS;

        case DEMUX_GET_PTS_DELAY:
            *va_arg(args,int64_t *) = INT64_C(1000)
                * var_InheritInteger( demux, "live-caching" );
            return VLC_SUCCESS;

        case DEMUX_GET_TIME:
711
            *va_arg (args, int64_t *) = mdate() - sys->start;
712 713 714 715 716 717 718 719 720
            return VLC_SUCCESS;

        /* TODO implement others */
        default:
            return VLC_EGENERIC;
    }

    return VLC_EGENERIC;
}