v4l.c 43.7 KB
Newer Older
1
2
3
/*****************************************************************************
 * v4l.c : Video4Linux input module for vlc
 *****************************************************************************
4
 * Copyright (C) 2002-2004 the VideoLAN team
5
 * $Id$
6
 *
Sam Hocevar's avatar
Sam Hocevar committed
7
 * Author: Laurent Aimar <fenrir@via.ecp.fr>
8
 *         Paul Forgey <paulf at aphrodite dot com>
9
 *         Gildas Bazin <gbazin@videolan.org>
bigben's avatar
bigben committed
10
 *         Benjamin Pracht <bigben at videolan dot org>
11
12
13
14
15
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
16
 *
17
18
19
20
21
22
23
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
30

31
32
33
34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35
#include <vlc_common.h>
36
#include <vlc_plugin.h>
zorglub's avatar
Round 2    
zorglub committed
37
38
39
#include <vlc_input.h>
#include <vlc_demux.h>
#include <vlc_access.h>
40
#include <vlc_picture.h>
41
#include <vlc_fs.h>
42

43
44
#include <sys/ioctl.h>
#include <sys/mman.h>
45
#include <fcntl.h>
46
#include <arpa/inet.h>
Pierre Ynard's avatar
Pierre Ynard committed
47
#include <errno.h>
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

/* From GStreamer's v4l plugin:
 * Because of some really cool feature in video4linux1, also known as
 * 'not including sys/types.h and sys/time.h', we had to include it
 * ourselves. In all their intelligence, these people decided to fix
 * this in the next version (video4linux2) in such a cool way that it
 * breaks all compilations of old stuff...
 * The real problem is actually that linux/time.h doesn't use proper
 * macro checks before defining types like struct timeval. The proper
 * fix here is to either fuck the kernel header (which is what we do
 * by defining _LINUX_TIME_H, an innocent little hack) or by fixing it
 * upstream, which I'll consider doing later on. If you get compiler
 * errors here, check your linux/time.h && sys/time.h header setup.
*/
#define _LINUX_TIME_H

64
#include <linux/videodev.h>
65
#include "videodev_mjpeg.h"
66

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
67
68
69
#ifdef HAVE_LIBV4L1
#include <libv4l1.h>
#endif
70
/*****************************************************************************
71
 * Module descriptior
72
 *****************************************************************************/
73
74
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
75

Christophe Massiot's avatar
Christophe Massiot committed
76
#define CACHING_TEXT N_("Caching value in ms")
77
#define CACHING_LONGTEXT N_( \
78
    "Caching value for V4L captures. This " \
zorglub's avatar
zorglub committed
79
    "value should be set in milliseconds." )
gbazin's avatar
   
gbazin committed
80
81
#define VDEV_TEXT N_("Video device name")
#define VDEV_LONGTEXT N_( \
zorglub's avatar
zorglub committed
82
    "Name of the video device to use. " \
gbazin's avatar
   
gbazin committed
83
    "If you don't specify anything, no video device will be used.")
gbazin's avatar
   
gbazin committed
84
85
#define CHROMA_TEXT N_("Video input chroma format")
#define CHROMA_LONGTEXT N_( \
86
87
    "Force the Video4Linux video device to use a specific chroma format " \
    "(eg. I420 (default), RV24, etc.)")
bigben's avatar
bigben committed
88
89
#define FREQUENCY_TEXT N_( "Frequency" )
#define FREQUENCY_LONGTEXT N_( \
zorglub's avatar
zorglub committed
90
    "Frequency to capture (in kHz), if applicable." )
bigben's avatar
bigben committed
91
92
93
#define CHANNEL_TEXT N_( "Channel" )
#define CHANNEL_LONGTEXT N_( \
    "Channel of the card to use (Usually, 0 = tuner, " \
zorglub's avatar
zorglub committed
94
    "1 = composite, 2 = svideo)." )
bigben's avatar
bigben committed
95
96
#define NORM_TEXT N_( "Norm" )
#define NORM_LONGTEXT N_( \
zorglub's avatar
zorglub committed
97
    "Norm of the stream (Automatic, SECAM, PAL, or NTSC)." )
bigben's avatar
bigben committed
98
99
#define AUDIO_TEXT N_( "Audio Channel" )
#define AUDIO_LONGTEXT N_( \
zorglub's avatar
zorglub committed
100
    "Audio Channel to use, if there are several audio inputs." )
bigben's avatar
bigben committed
101
102
#define WIDTH_TEXT N_( "Width" )
#define WIDTH_LONGTEXT N_( "Width of the stream to capture " \
zorglub's avatar
zorglub committed
103
    "(-1 for autodetect)." )
bigben's avatar
bigben committed
104
105
#define HEIGHT_TEXT N_( "Height" )
#define HEIGHT_LONGTEXT N_( "Height of the stream to capture " \
zorglub's avatar
zorglub committed
106
    "(-1 for autodetect)." )
bigben's avatar
bigben committed
107
108
#define BRIGHTNESS_TEXT N_( "Brightness" )
#define BRIGHTNESS_LONGTEXT N_( \
zorglub's avatar
zorglub committed
109
    "Brightness of the video input." )
bigben's avatar
bigben committed
110
111
#define HUE_TEXT N_( "Hue" )
#define HUE_LONGTEXT N_( \
zorglub's avatar
zorglub committed
112
    "Hue of the video input." )
113
#define COLOUR_TEXT N_( "Color" )
bigben's avatar
bigben committed
114
#define COLOUR_LONGTEXT N_( \
zorglub's avatar
zorglub committed
115
    "Color of the video input." )
bigben's avatar
bigben committed
116
117
#define CONTRAST_TEXT N_( "Contrast" )
#define CONTRAST_LONGTEXT N_( \
zorglub's avatar
zorglub committed
118
    "Contrast of the video input." )
bigben's avatar
bigben committed
119
#define TUNER_TEXT N_( "Tuner" )
zorglub's avatar
zorglub committed
120
#define TUNER_LONGTEXT N_( "Tuner to use, if there are several ones." )
bigben's avatar
bigben committed
121
122
123
124
125
#define MJPEG_TEXT N_( "MJPEG" )
#define MJPEG_LONGTEXT N_(  \
    "Set this option if the capture device outputs MJPEG" )
#define DECIMATION_TEXT N_( "Decimation" )
#define DECIMATION_LONGTEXT N_( \
zorglub's avatar
zorglub committed
126
    "Decimation level for MJPEG streams" )
bigben's avatar
bigben committed
127
#define QUALITY_TEXT N_( "Quality" )
zorglub's avatar
zorglub committed
128
#define QUALITY_LONGTEXT N_( "Quality of the stream." )
bigben's avatar
bigben committed
129
130
#define FPS_TEXT N_( "Framerate" )
#define FPS_LONGTEXT N_( "Framerate to capture, if applicable " \
zorglub's avatar
zorglub committed
131
    "(-1 for autodetect)." )
132

dionoea's avatar
dionoea committed
133
134
#define AUDIO_DEPRECATED_ERROR N_( \
    "Alsa or OSS audio capture in the v4l access is deprecated. " \
135
136
    "please use 'v4l:/""/ :input-slave=alsa:/""/' or " \
    "'v4l:/""/ :input-slave=oss:/""/' instead." )
dionoea's avatar
dionoea committed
137

hartman's avatar
hartman committed
138
static const int i_norm_list[] =
139
    { VIDEO_MODE_AUTO, VIDEO_MODE_SECAM, VIDEO_MODE_PAL, VIDEO_MODE_NTSC };
140
static const char *const psz_norm_list_text[] =
141
142
    { N_("Automatic"), N_("SECAM"), N_("PAL"),  N_("NTSC") };

dionoea's avatar
dionoea committed
143
144
#define V4L_DEFAULT "/dev/video"

145
146
147
148
149
vlc_module_begin ()
    set_shortname( N_("Video4Linux") )
    set_description( N_("Video4Linux input") )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
gbazin's avatar
   
gbazin committed
150

gbazin's avatar
   
gbazin committed
151
    add_integer( "v4l-caching", DEFAULT_PTS_DELAY / 1000, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
152
                 CACHING_TEXT, CACHING_LONGTEXT, true )
dionoea's avatar
dionoea committed
153
154
    add_obsolete_string( "v4l-vdev" );
    add_obsolete_string( "v4l-adev" );
gbazin's avatar
   
gbazin committed
155
    add_string( "v4l-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
156
                true )
157
    add_float( "v4l-fps", -1.0, NULL, FPS_TEXT, FPS_LONGTEXT, true )
dionoea's avatar
dionoea committed
158
    add_obsolete_integer( "v4l-samplerate" );
bigben's avatar
bigben committed
159
    add_integer( "v4l-channel", 0, NULL, CHANNEL_TEXT, CHANNEL_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
160
                true )
161
    add_integer( "v4l-tuner", -1, NULL, TUNER_TEXT, TUNER_LONGTEXT, true )
bigben's avatar
bigben committed
162
    add_integer( "v4l-norm", VIDEO_MODE_AUTO, NULL, NORM_TEXT, NORM_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
163
                false )
164
        change_integer_list( i_norm_list, psz_norm_list_text, NULL );
bigben's avatar
bigben committed
165
    add_integer( "v4l-frequency", -1, NULL, FREQUENCY_TEXT, FREQUENCY_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
166
                false )
167
    add_integer( "v4l-audio", -1, NULL, AUDIO_TEXT, AUDIO_LONGTEXT, true )
dionoea's avatar
dionoea committed
168
    add_obsolete_bool( "v4l-stereo" );
169
    add_integer( "v4l-width", 0, NULL, WIDTH_TEXT, WIDTH_LONGTEXT, true )
bigben's avatar
bigben committed
170
    add_integer( "v4l-height", 0, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
171
                true )
bigben's avatar
bigben committed
172
    add_integer( "v4l-brightness", -1, NULL, BRIGHTNESS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
173
                BRIGHTNESS_LONGTEXT, true )
bigben's avatar
bigben committed
174
    add_integer( "v4l-colour", -1, NULL, COLOUR_TEXT, COLOUR_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
175
                true )
176
    add_integer( "v4l-hue", -1, NULL, HUE_TEXT, HUE_LONGTEXT, true )
bigben's avatar
bigben committed
177
    add_integer( "v4l-contrast", -1, NULL, CONTRAST_TEXT, CONTRAST_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
178
                true )
179
    add_bool( "v4l-mjpeg", false, NULL, MJPEG_TEXT, MJPEG_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
180
            true )
bigben's avatar
bigben committed
181
    add_integer( "v4l-decimation", 1, NULL, DECIMATION_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
182
            DECIMATION_LONGTEXT, true )
bigben's avatar
bigben committed
183
    add_integer( "v4l-quality", 100, NULL, QUALITY_TEXT, QUALITY_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
184
            true )
gbazin's avatar
   
gbazin committed
185

186
187
188
189
    add_shortcut( "v4l" )
    set_capability( "access_demux", 10 )
    set_callbacks( Open, Close )
vlc_module_end ()
190

191
192
193
/*****************************************************************************
 * Access: local prototypes
 *****************************************************************************/
194
195
static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );
196

197
198
199
static void ParseMRL    ( demux_t * );
static int  OpenVideoDev( demux_t *, char * );
static block_t *GrabVideo( demux_t * );
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#define MJPEG_BUFFER_SIZE (256*1024)

struct quicktime_mjpeg_app1
{
    uint32_t    i_reserved;             /* set to 0 */
    uint32_t    i_tag;                  /* 'mjpg' */
    uint32_t    i_field_size;           /* offset following EOI */
    uint32_t    i_padded_field_size;    /* offset following EOI+pad */
    uint32_t    i_next_field;           /* offset to next field */
    uint32_t    i_DQT_offset;
    uint32_t    i_DHT_offset;
    uint32_t    i_SOF_offset;
    uint32_t    i_SOS_offset;
    uint32_t    i_data_offset;          /* following SOS marker data */
};

hartman's avatar
hartman committed
217
static const struct
gbazin's avatar
   
gbazin committed
218
219
{
    int i_v4l;
220
    vlc_fourcc_t i_fourcc;
gbazin's avatar
   
gbazin committed
221
222
223

} v4lchroma_to_fourcc[] =
{
224
    { VIDEO_PALETTE_GREY, VLC_CODEC_GREY },
gbazin's avatar
   
gbazin committed
225
    { VIDEO_PALETTE_HI240, VLC_FOURCC( 'I', '2', '4', '0' ) },
226
227
228
229
230
231
232
    { VIDEO_PALETTE_RGB565, VLC_CODEC_RGB16 },
    { VIDEO_PALETTE_RGB555, VLC_CODEC_RGB15 },
    { VIDEO_PALETTE_RGB24, VLC_CODEC_RGB24 },
    { VIDEO_PALETTE_RGB32, VLC_CODEC_RGB32 },
    { VIDEO_PALETTE_YUV422, VLC_CODEC_YUYV },
    { VIDEO_PALETTE_YUYV, VLC_CODEC_YUYV },
    { VIDEO_PALETTE_UYVY, VLC_CODEC_UYVY },
gbazin's avatar
   
gbazin committed
233
234
235
    { VIDEO_PALETTE_YUV420, VLC_FOURCC( 'I', '4', '2', 'N' ) },
    { VIDEO_PALETTE_YUV411, VLC_FOURCC( 'I', '4', '1', 'N' ) },
    { VIDEO_PALETTE_RAW, VLC_FOURCC( 'G', 'R', 'A', 'W' ) },
236
237
238
    { VIDEO_PALETTE_YUV422P, VLC_CODEC_I422 },
    { VIDEO_PALETTE_YUV420P, VLC_CODEC_I420 },
    { VIDEO_PALETTE_YUV411P, VLC_CODEC_I411 },
gbazin's avatar
   
gbazin committed
239
240
241
    { 0, 0 }
};

242
struct demux_sys_t
243
{
gbazin's avatar
   
gbazin committed
244
    /* Devices */
dionoea's avatar
dionoea committed
245
    char *psz_device;         /* Main device from MRL */
dionoea's avatar
dionoea committed
246
    int  i_fd;
gbazin's avatar
   
gbazin committed
247
248
249

    /* Video properties */
    picture_t pic;
250

gbazin's avatar
   
gbazin committed
251
    int i_fourcc;
252
253
254
255
256
257
258
259
    int i_channel;
    int i_audio;
    int i_norm;
    int i_tuner;
    int i_frequency;
    int i_width;
    int i_height;

260
261
262
263
264
    int i_brightness;
    int i_hue;
    int i_colour;
    int i_contrast;

265
266
267
    float f_fps;            /* <= 0.0 mean to grab at full rate */
    mtime_t i_video_pts;    /* only used when f_fps > 0 */

268
    bool b_mjpeg;
269
270
271
    int i_decimation;
    int i_quality;

272
273
    struct video_capability vid_cap;
    struct video_mbuf       vid_mbuf;
274
    struct mjpeg_requestbuffers mjpeg_buffers;
275
276
277

    uint8_t *p_video_mmap;
    int     i_frame_pos;
278

279
    struct video_mmap   vid_mmap;
gbazin's avatar
   
gbazin committed
280
    struct video_picture vid_picture;
281

282
    int          i_video_frame_size;
dionoea's avatar
dionoea committed
283
    es_out_id_t  *p_es;
284
285
};

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
286
287
288
289
290
291
292
293
#ifndef HAVE_LIBV4L1
#   define v4l1_close close
#   define v4l1_ioctl ioctl
#   define v4l1_mmap mmap
#   define v4l1_munmap munmap
#   define v4l1_open utf8_open
#endif

294
/*****************************************************************************
295
 * Open: opens v4l device
296
297
298
299
 *****************************************************************************
 *
 * url: <video device>::::
 *
300
 *****************************************************************************/
301
static int Open( vlc_object_t *p_this )
302
{
303
304
305
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;

306
307
308
309
    /* Only when selected */
    if( *p_demux->psz_access == '\0' )
        return VLC_EGENERIC;

310
311
312
313
314
315
    /* Set up p_demux */
    p_demux->pf_demux = Demux;
    p_demux->pf_control = Control;
    p_demux->info.i_update = 0;
    p_demux->info.i_title = 0;
    p_demux->info.i_seekpoint = 0;
ivoire's avatar
ivoire committed
316
317
318
    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;
319

ivoire's avatar
ivoire committed
320
321
322
323
324
    p_sys->i_audio      = var_CreateGetInteger( p_demux, "v4l-audio" );
    p_sys->i_channel    = var_CreateGetInteger( p_demux, "v4l-channel" );
    p_sys->i_norm       = var_CreateGetInteger( p_demux, "v4l-norm" );
    p_sys->i_tuner      = var_CreateGetInteger( p_demux, "v4l-tuner" );
    p_sys->i_frequency  = var_CreateGetInteger( p_demux, "v4l-frequency" );
325

ivoire's avatar
ivoire committed
326
327
328
329
330
    p_sys->f_fps        = var_CreateGetFloat( p_demux, "v4l-fps" );
    p_sys->i_width      = var_CreateGetInteger( p_demux, "v4l-width" );
    p_sys->i_height     = var_CreateGetInteger( p_demux, "v4l-height" );
    p_sys->i_video_pts  = -1;
    p_sys->i_brightness = var_CreateGetInteger( p_demux, "v4l-brightness" );
331

332
    p_sys->i_hue        = var_CreateGetInteger( p_demux, "v4l-hue" );
ivoire's avatar
ivoire committed
333
334
    p_sys->i_colour     = var_CreateGetInteger( p_demux, "v4l-colour" );
    p_sys->i_contrast   = var_CreateGetInteger( p_demux, "v4l-contrast" );
335

ivoire's avatar
ivoire committed
336
337
338
    p_sys->b_mjpeg      = var_CreateGetBool( p_demux, "v4l-mjpeg" );
    p_sys->i_decimation = var_CreateGetInteger( p_demux, "v4l-decimation" );
    p_sys->i_quality    = var_CreateGetInteger( p_demux, "v4l-quality" );
339

dionoea's avatar
dionoea committed
340
    p_sys->psz_device = NULL;
dionoea's avatar
dionoea committed
341
    p_sys->i_fd = -1;
gbazin's avatar
   
gbazin committed
342

dionoea's avatar
dionoea committed
343
    p_sys->p_es = NULL;
344
345

    ParseMRL( p_demux );
gbazin's avatar
   
gbazin committed
346

dionoea's avatar
dionoea committed
347
    msg_Dbg( p_this, "opening device '%s'", p_sys->psz_device );
dionoea's avatar
dionoea committed
348
349
    p_sys->i_fd = OpenVideoDev( p_demux, p_sys->psz_device );
    if( p_sys->i_fd < 0 )
gbazin's avatar
   
gbazin committed
350
    {
351
        Close( p_this );
gbazin's avatar
   
gbazin committed
352
353
354
        return VLC_EGENERIC;
    }

dionoea's avatar
dionoea committed
355

356
    msg_Dbg( p_demux, "v4l grabbing started" );
gbazin's avatar
   
gbazin committed
357

358
    /* Declare elementary streams */
dionoea's avatar
dionoea committed
359
360
361
362
    es_format_t fmt;
    es_format_Init( &fmt, VIDEO_ES, p_sys->i_fourcc );
    fmt.video.i_width  = p_sys->i_width;
    fmt.video.i_height = p_sys->i_height;
Laurent Aimar's avatar
Laurent Aimar committed
363
364
    fmt.video.i_sar_num = 4 * fmt.video.i_height;
    fmt.video.i_sar_den = 3 * fmt.video.i_width;
dionoea's avatar
dionoea committed
365
366
367

    /* Setup rgb mask for RGB formats */
    switch( p_sys->i_fourcc )
gbazin's avatar
   
gbazin committed
368
    {
369
        case VLC_CODEC_RGB15:
dionoea's avatar
dionoea committed
370
371
372
373
            fmt.video.i_rmask = 0x001f;
            fmt.video.i_gmask = 0x03e0;
            fmt.video.i_bmask = 0x7c00;
            break;
374
        case VLC_CODEC_RGB16:
dionoea's avatar
dionoea committed
375
376
377
378
            fmt.video.i_rmask = 0x001f;
            fmt.video.i_gmask = 0x07e0;
            fmt.video.i_bmask = 0xf800;
            break;
379
380
        case VLC_CODEC_RGB24:
        case VLC_CODEC_RGB32:
dionoea's avatar
dionoea committed
381
382
383
384
            fmt.video.i_rmask = 0x00ff0000;
            fmt.video.i_gmask = 0x0000ff00;
            fmt.video.i_bmask = 0x000000ff;
            break;
gbazin's avatar
   
gbazin committed
385
386
    }

dionoea's avatar
dionoea committed
387
388
389
    msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
             (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height );
    p_sys->p_es = es_out_Add( p_demux->out, &fmt );
gbazin's avatar
   
gbazin committed
390

391
    /* Update default_pts to a suitable value for access */
392
    var_Create( p_demux, "v4l-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
393

gbazin's avatar
   
gbazin committed
394
395
396
    return VLC_SUCCESS;
}

397
/*****************************************************************************
398
 * Close: close device, free resources
399
 *****************************************************************************/
400
static void Close( vlc_object_t *p_this )
401
{
402
403
    demux_t     *p_demux = (demux_t *)p_this;
    demux_sys_t *p_sys   = p_demux->p_sys;
404

405
    free( p_sys->psz_device );
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
406
    if( p_sys->i_fd >= 0 ) v4l1_close( p_sys->i_fd );
407
408
409
410

    if( p_sys->b_mjpeg )
    {
        int i_noframe = -1;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
411
        v4l1_ioctl( p_sys->i_fd, MJPIOC_QBUF_CAPT, &i_noframe );
412
413
414
415
416
    }

    if( p_sys->p_video_mmap && p_sys->p_video_mmap != MAP_FAILED )
    {
        if( p_sys->b_mjpeg )
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
417
            v4l1_munmap( p_sys->p_video_mmap, p_sys->mjpeg_buffers.size *
418
419
                    p_sys->mjpeg_buffers.count );
        else
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
420
            v4l1_munmap( p_sys->p_video_mmap, p_sys->vid_mbuf.size );
421
422
423
424
425
426
    }

    free( p_sys );
}

/*****************************************************************************
427
 * Control:
428
 *****************************************************************************/
429
static int Control( demux_t *p_demux, int i_query, va_list args )
430
{
431
    bool *pb;
432
    int64_t    *pi64;
433
434
435

    switch( i_query )
    {
436
437
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
438
        case DEMUX_CAN_SEEK:
439
        case DEMUX_CAN_CONTROL_PACE:
440
441
            pb = (bool*)va_arg( args, bool * );
            *pb = false;
442
443
444
445
446
447
448
            return VLC_SUCCESS;

        case DEMUX_GET_PTS_DELAY:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = (int64_t)var_GetInteger( p_demux, "v4l-caching" ) * 1000;
            return VLC_SUCCESS;

449
450
451
452
453
        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = mdate();
            return VLC_SUCCESS;

454
        /* TODO implement others */
455
456
457
        default:
            return VLC_EGENERIC;
    }
458
459

    return VLC_EGENERIC;
460
461
462
}

/*****************************************************************************
463
 * Demux:
464
 *****************************************************************************/
465
static int Demux( demux_t *p_demux )
466
{
467
    demux_sys_t *p_sys = p_demux->p_sys;
468

469
    block_t *p_block = GrabVideo( p_demux );
dionoea's avatar
dionoea committed
470

471
    if( !p_block )
472
    {
473
474
        msleep( 10000 ); /* Unfortunately v4l doesn't allow polling */
        return 1;
475
476
    }

477
478
479
    es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
    es_out_Send( p_demux->out, p_sys->p_es, p_block );

480
    return 1;
481
482
}

gbazin's avatar
   
gbazin committed
483
484
485
/*****************************************************************************
 * ParseMRL: parse the options contained in the MRL
 *****************************************************************************/
486
static void ParseMRL( demux_t *p_demux )
gbazin's avatar
   
gbazin committed
487
{
488
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
489

490
    char *psz_dup = strdup( p_demux->psz_path );
gbazin's avatar
   
gbazin committed
491
    char *psz_parser = psz_dup;
492
493
494
495
496
497
498
499
500
501
502
503
504
505

    while( *psz_parser && *psz_parser != ':' )
    {
        psz_parser++;
    }

    if( *psz_parser == ':' )
    {
        /* read options */
        for( ;; )
        {
            *psz_parser++ = '\0';
            if( !strncmp( psz_parser, "channel=", strlen( "channel=" ) ) )
            {
gbazin's avatar
   
gbazin committed
506
507
                p_sys->i_channel = strtol( psz_parser + strlen( "channel=" ),
                                           &psz_parser, 0 );
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
535
536
            }
            else if( !strncmp( psz_parser, "norm=", strlen( "norm=" ) ) )
            {
                psz_parser += strlen( "norm=" );
                if( !strncmp( psz_parser, "pal", strlen( "pal" ) ) )
                {
                    p_sys->i_norm = VIDEO_MODE_PAL;
                    psz_parser += strlen( "pal" );
                }
                else if( !strncmp( psz_parser, "ntsc", strlen( "ntsc" ) ) )
                {
                    p_sys->i_norm = VIDEO_MODE_NTSC;
                    psz_parser += strlen( "ntsc" );
                }
                else if( !strncmp( psz_parser, "secam", strlen( "secam" ) ) )
                {
                    p_sys->i_norm = VIDEO_MODE_SECAM;
                    psz_parser += strlen( "secam" );
                }
                else if( !strncmp( psz_parser, "auto", strlen( "auto" ) ) )
                {
                    p_sys->i_norm = VIDEO_MODE_AUTO;
                    psz_parser += strlen( "auto" );
                }
                else
                {
                    p_sys->i_norm = strtol( psz_parser, &psz_parser, 0 );
                }
            }
gbazin's avatar
   
gbazin committed
537
538
            else if( !strncmp( psz_parser, "frequency=",
                               strlen( "frequency=" ) ) )
539
540
            {
                p_sys->i_frequency =
gbazin's avatar
   
gbazin committed
541
542
                    strtol( psz_parser + strlen( "frequency=" ),
                            &psz_parser, 0 );
543
544
                if( p_sys->i_frequency < 30000 )
                {
545
                    msg_Warn( p_demux, "v4l syntax has changed : "
gbazin's avatar
   
gbazin committed
546
                              "'frequency' is now channel frequency in kHz");
547
                }
548
549
550
            }
            else if( !strncmp( psz_parser, "audio=", strlen( "audio=" ) ) )
            {
gbazin's avatar
   
gbazin committed
551
552
                p_sys->i_audio = strtol( psz_parser + strlen( "audio=" ),
                                         &psz_parser, 0 );
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
            }
            else if( !strncmp( psz_parser, "size=", strlen( "size=" ) ) )
            {
                psz_parser += strlen( "size=" );
                if( !strncmp( psz_parser, "subqcif", strlen( "subqcif" ) ) )
                {
                    p_sys->i_width  = 128;
                    p_sys->i_height = 96;
                }
                else if( !strncmp( psz_parser, "qsif", strlen( "qsif" ) ) )
                {
                    p_sys->i_width  = 160;
                    p_sys->i_height = 120;
                }
                else if( !strncmp( psz_parser, "qcif", strlen( "qcif" ) ) )
                {
                    p_sys->i_width  = 176;
                    p_sys->i_height = 144;
                }
                else if( !strncmp( psz_parser, "sif", strlen( "sif" ) ) )
                {
                    p_sys->i_width  = 320;
                    p_sys->i_height = 244;
                }
                else if( !strncmp( psz_parser, "cif", strlen( "cif" ) ) )
                {
                    p_sys->i_width  = 352;
                    p_sys->i_height = 288;
                }
                else if( !strncmp( psz_parser, "vga", strlen( "vga" ) ) )
                {
                    p_sys->i_width  = 640;
                    p_sys->i_height = 480;
                }
                else
                {
                    /* widthxheight */
                    p_sys->i_width = strtol( psz_parser, &psz_parser, 0 );
                    if( *psz_parser == 'x' || *psz_parser == 'X')
                    {
gbazin's avatar
   
gbazin committed
593
594
                        p_sys->i_height = strtol( psz_parser + 1,
                                                  &psz_parser, 0 );
595
                    }
596
                    msg_Dbg( p_demux, "WxH %dx%d", p_sys->i_width,
gbazin's avatar
   
gbazin committed
597
                             p_sys->i_height );
598
599
                }
            }
600
601
602
603
604
605
606
607
608
609
610
611
            else if( !strncmp( psz_parser, "brightness=", strlen( "brightness=" ) ) )
            {
                p_sys->i_brightness = strtol( psz_parser + strlen( "brightness=" ),
                                              &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "colour=", strlen( "colour=" ) ) )
            {
                p_sys->i_colour = strtol( psz_parser + strlen( "colour=" ),
                                          &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "hue=", strlen( "hue=" ) ) )
            {
612
                p_sys->i_hue = strtol( psz_parser + strlen( "hue=" ),
613
614
615
616
617
618
619
                                       &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "contrast=", strlen( "contrast=" ) ) )
            {
                p_sys->i_contrast = strtol( psz_parser + strlen( "contrast=" ),
                                            &psz_parser, 0 );
            }
620
621
            else if( !strncmp( psz_parser, "tuner=", strlen( "tuner=" ) ) )
            {
gbazin's avatar
   
gbazin committed
622
623
                p_sys->i_tuner = strtol( psz_parser + strlen( "tuner=" ),
                                         &psz_parser, 0 );
624
            }
625
626
627
628
            else if( !strncmp( psz_parser, "mjpeg", strlen( "mjpeg" ) ) )
            {
                psz_parser += strlen( "mjpeg" );

629
                p_sys->b_mjpeg = true;
630
            }
Sam Hocevar's avatar
Sam Hocevar committed
631
            else if( !strncmp( psz_parser, "decimation=",
632
633
                        strlen( "decimation=" ) ) )
            {
Sam Hocevar's avatar
Sam Hocevar committed
634
                p_sys->i_decimation =
635
636
637
638
639
640
641
642
643
644
                    strtol( psz_parser + strlen( "decimation=" ),
                            &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "quality=",
                        strlen( "quality=" ) ) )
            {
                p_sys->i_quality =
                    strtol( psz_parser + strlen( "quality=" ),
                            &psz_parser, 0 );
            }
645
646
            else if( !strncmp( psz_parser, "fps=", strlen( "fps=" ) ) )
            {
647
648
                p_sys->f_fps = us_strtof( psz_parser + strlen( "fps=" ),
                                          &psz_parser );
649
            }
dionoea's avatar
dionoea committed
650
651
652
653
654
655
656
657
658
659
660
            else if( !strncmp( psz_parser, "adev=", strlen( "adev=" ) )
             || !strncmp( psz_parser, "samplerate=", strlen( "samplerate=" ) )
             || !strncmp( psz_parser, "stereo", strlen( "stereo" ) )
             || !strncmp( psz_parser, "mono", strlen( "mono" ) ) )
            {
                if( strchr( psz_parser, ':' ) )
                    psz_parser = strchr( psz_parser, ':' );
                else
                    psz_parser += strlen( psz_parser );
                msg_Err( p_demux, AUDIO_DEPRECATED_ERROR );
            }
661
662
            else
            {
663
                msg_Warn( p_demux, "unknown option" );
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
            }

            while( *psz_parser && *psz_parser != ':' )
            {
                psz_parser++;
            }

            if( *psz_parser == '\0' )
            {
                break;
            }
        }
    }

    if( *psz_dup )
gbazin's avatar
   
gbazin committed
679
        p_sys->psz_device = strdup( psz_dup );
dionoea's avatar
dionoea committed
680
681
    else
        p_sys->psz_device = strdup( V4L_DEFAULT );
682
    free( psz_dup );
gbazin's avatar
   
gbazin committed
683
}
684

gbazin's avatar
   
gbazin committed
685
686
687
/*****************************************************************************
 * OpenVideoDev:
 *****************************************************************************/
688
static int OpenVideoDev( demux_t *p_demux, char *psz_device )
gbazin's avatar
   
gbazin committed
689
{
690
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
691
    int i_fd;
692

gbazin's avatar
   
gbazin committed
693
694
695
    struct video_channel vid_channel;
    struct mjpeg_params mjpeg;
    int i;
696

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
697
    if( ( i_fd = v4l1_open( psz_device, O_RDWR ) ) < 0 )
698
    {
699
        msg_Err( p_demux, "cannot open device (%m)" );
gbazin's avatar
   
gbazin committed
700
        goto vdev_failed;
701
702
    }

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
703
    if( v4l1_ioctl( i_fd, VIDIOCGCAP, &p_sys->vid_cap ) < 0 )
704
    {
705
        msg_Err( p_demux, "cannot get capabilities (%m)" );
gbazin's avatar
   
gbazin committed
706
        goto vdev_failed;
707
708
    }

709
    msg_Dbg( p_demux,
710
711
712
713
714
715
716
717
718
             "V4L device %s %d channels %d audios %d < w < %d %d < h < %d",
             p_sys->vid_cap.name,
             p_sys->vid_cap.channels,
             p_sys->vid_cap.audios,
             p_sys->vid_cap.minwidth,  p_sys->vid_cap.maxwidth,
             p_sys->vid_cap.minheight, p_sys->vid_cap.maxheight );

    if( p_sys->i_channel < 0 || p_sys->i_channel >= p_sys->vid_cap.channels )
    {
719
        msg_Dbg( p_demux, "invalid channel, falling back on channel 0" );
720
721
        p_sys->i_channel = 0;
    }
722
    if( p_sys->vid_cap.audios && p_sys->i_audio >= p_sys->vid_cap.audios )
723
    {
724
        msg_Dbg( p_demux, "invalid audio, falling back with no audio" );
725
726
727
728
729
730
        p_sys->i_audio = -1;
    }

    if( p_sys->i_width < p_sys->vid_cap.minwidth ||
        p_sys->i_width > p_sys->vid_cap.maxwidth )
    {
731
        msg_Dbg( p_demux, "invalid width %i", p_sys->i_width );
732
733
734
735
736
        p_sys->i_width = 0;
    }
    if( p_sys->i_height < p_sys->vid_cap.minheight ||
        p_sys->i_height > p_sys->vid_cap.maxheight )
    {
737
        msg_Dbg( p_demux, "invalid height %i", p_sys->i_height );
738
739
740
        p_sys->i_height = 0;
    }

Sam Hocevar's avatar
Sam Hocevar committed
741
    if( !( p_sys->vid_cap.type & VID_TYPE_CAPTURE ) )
742
    {
743
        msg_Err( p_demux, "cannot grab" );
gbazin's avatar
   
gbazin committed
744
        goto vdev_failed;
745
746
747
    }

    vid_channel.channel = p_sys->i_channel;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
748
    if( v4l1_ioctl( i_fd, VIDIOCGCHAN, &vid_channel ) < 0 )
749
    {
750
        msg_Err( p_demux, "cannot get channel infos (%m)" );
gbazin's avatar
   
gbazin committed
751
        goto vdev_failed;
752
    }
753
    msg_Dbg( p_demux,
754
             "setting channel %s(%d) %d tuners flags=0x%x type=0x%x norm=0x%x",
755
756
             vid_channel.name, vid_channel.channel, vid_channel.tuners,
             vid_channel.flags, vid_channel.type, vid_channel.norm );
757
758
759

    if( p_sys->i_tuner >= vid_channel.tuners )
    {
760
        msg_Dbg( p_demux, "invalid tuner, falling back on tuner 0" );
761
762
763
764
        p_sys->i_tuner = 0;
    }

    vid_channel.norm = p_sys->i_norm;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
765
    if( v4l1_ioctl( i_fd, VIDIOCSCHAN, &vid_channel ) < 0 )
766
    {
767
        msg_Err( p_demux, "cannot set channel (%m)" );
gbazin's avatar
   
gbazin committed
768
        goto vdev_failed;
769
770
771
772
773
774
775
776
777
778
779
    }

    if( vid_channel.flags & VIDEO_VC_TUNER )
    {

        /* set tuner */
#if 0
        struct video_tuner vid_tuner;
        if( p_sys->i_tuner >= 0 )
        {
            vid_tuner.tuner = p_sys->i_tuner;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
780
            if( v4l1_ioctl( i_fd, VIDIOCGTUNER, &vid_tuner ) < 0 )
781
            {
782
                msg_Err( p_demux, "cannot get tuner (%m)" );
gbazin's avatar
   
gbazin committed
783
                goto vdev_failed;
784
            }
785
            msg_Dbg( p_demux, "tuner %s low=%d high=%d, flags=0x%x "
gbazin's avatar
   
gbazin committed
786
787
788
789
                     "mode=0x%x signal=0x%x",
                     vid_tuner.name, vid_tuner.rangelow, vid_tuner.rangehigh,
                     vid_tuner.flags, vid_tuner.mode, vid_tuner.signal );

790
            msg_Dbg( p_demux, "setting tuner %s (%d)",
791
792
                     vid_tuner.name, vid_tuner.tuner );

gbazin's avatar
   
gbazin committed
793
794
            /* FIXME FIXME to be checked FIXME FIXME */
            //vid_tuner.mode = p_sys->i_norm;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
795
            if( v4l1_ioctl( i_fd, VIDIOCSTUNER, &vid_tuner ) < 0 )
796
            {
797
                msg_Err( p_demux, "cannot set tuner (%m)" );
gbazin's avatar
   
gbazin committed
798
                goto vdev_failed;
799
800
801
            }
        }
#endif
802

gbazin's avatar
   
gbazin committed
803
804
        /* Show a warning if frequency is < than 30000.
         * User is certainly usint old syntax. */
Sam Hocevar's avatar
Sam Hocevar committed
805

806

807
808
809
        /* set frequency */
        if( p_sys->i_frequency >= 0 )
        {
810
            int driver_frequency = p_sys->i_frequency * 16 /1000;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
811
            if( v4l1_ioctl( i_fd, VIDIOCSFREQ, &driver_frequency ) < 0 )
812
            {
813
                msg_Err( p_demux, "cannot set frequency (%m)" );
gbazin's avatar
   
gbazin committed
814
                goto vdev_failed;
815
            }
816
            msg_Dbg( p_demux, "frequency %d (%d)", p_sys->i_frequency,
Sam Hocevar's avatar
Sam Hocevar committed
817
                                                   driver_frequency );
818
819
820
821
822
823
824
825
826
827
828
829
        }
    }

    /* set audio */
    if( vid_channel.flags & VIDEO_VC_AUDIO )
    {
        struct video_audio      vid_audio;

        /* XXX TODO volume, balance, ... */
        if( p_sys->i_audio >= 0 )
        {
            vid_audio.audio = p_sys->i_audio;
Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
830
            if( v4l1_ioctl( i_fd, VIDIOCGAUDIO, &vid_audio ) < 0 )
831
            {
832
                msg_Err( p_demux, "cannot get audio (%m)" );
gbazin's avatar
   
gbazin committed
833
                goto vdev_failed;
834
835
836
837
838
            }

            /* unmute audio */
            vid_audio.flags &= ~VIDEO_AUDIO_MUTE;

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
839
            if( v4l1_ioctl( i_fd, VIDIOCSAUDIO, &vid_audio ) < 0 )
840
            {
841
                msg_Err( p_demux, "cannot set audio (%m)" );
gbazin's avatar
   
gbazin committed
842
                goto vdev_failed;
843
            }
844
        }
845

846
    }
847

848
849
850
851
    /* establish basic params with input and norm before feeling width
     * or height */
    if( p_sys->b_mjpeg )
    {
852
        struct quicktime_mjpeg_app1 p_app1;
853
854
        int32_t i_offset;

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
855
        if( v4l1_ioctl( i_fd, MJPIOC_G_PARAMS, &mjpeg ) < 0 )
856
        {
857
            msg_Err( p_demux, "cannot get mjpeg params (%m)" );
gbazin's avatar
   
gbazin committed
858
            goto vdev_failed;
859
        }
860
861
862
863
864
865
866
867
868
869
870
871
872
873
        mjpeg.input = p_sys->i_channel;
        mjpeg.norm  = p_sys->i_norm;
        mjpeg.decimation = p_sys->i_decimation;

        if( p_sys->i_width )
            mjpeg.img_width = p_sys->i_width / p_sys->i_decimation;
        if( p_sys->i_height )
            mjpeg.img_height = p_sys->i_height / p_sys->i_decimation;

        /* establish Quicktime APP1 marker while we are here */
        mjpeg.APPn = 1;
        mjpeg.APP_len = 40;

        /* aligned */
874
875
876
877
878
        p_app1.i_reserved = 0;
        p_app1.i_tag = VLC_FOURCC( 'm','j','p','g' );
        p_app1.i_field_size = 0;
        p_app1.i_padded_field_size = 0;
        p_app1.i_next_field = 0;
879
880
881
882
883
884
885
886
887
888
889
        /* XXX WARNING XXX */
        /* these's nothing magic about these values.  We are dangerously
         * assuming the encoder card is encoding mjpeg-a and is not throwing
         * in marker tags we aren't expecting.  It's bad enough we have to
         * search through the jpeg output for every frame we grab just to
         * find the first field's end marker, so we take this risk to boost
         * performance.
         * This is really something the driver could do for us because this
         * does conform to standards outside of Apple Quicktime.
         */
        i_offset = 0x2e;
890
        p_app1.i_DQT_offset = hton32( i_offset );
891
        i_offset = 0xb4;
892
        p_app1.i_DHT_offset = hton32( i_offset );
893
        i_offset = 0x258;
894
        p_app1.i_SOF_offset = hton32( i_offset );
895
        i_offset = 0x26b;
896
        p_app1.i_SOS_offset = hton32( i_offset );
897
        i_offset = 0x279;
898
899
        p_app1.i_data_offset = hton32( i_offset );
        memcpy(mjpeg.APP_data, &p_app1, sizeof(struct quicktime_mjpeg_app1));
900
901
902
903
904

        /* SOF and SOS aren't specified by the mjpeg API because they aren't
         * optional.  They will be present in the output. */
        mjpeg.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT;

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
905
        if( v4l1_ioctl( i_fd, MJPIOC_S_PARAMS, &mjpeg ) < 0 )
906
        {
907
            msg_Err( p_demux, "cannot set mjpeg params (%m)" );
gbazin's avatar
   
gbazin committed
908
            goto vdev_failed;
909
910
911
912
913
        }

        p_sys->i_width = mjpeg.img_width * mjpeg.HorDcm;
        p_sys->i_height = mjpeg.img_height * mjpeg.VerDcm *
            mjpeg.field_per_buff;
914
915
    }

916
917
    /* fix width/height */
    if( !p_sys->b_mjpeg && ( p_sys->i_width == 0 || p_sys->i_height == 0 ) )
918
919
920
    {
        struct video_window vid_win;

Kuan-Chung Chiu's avatar
Kuan-Chung Chiu committed
921
        if( v4l1_ioctl( i_fd, VIDIOCGWIN, &vid_win ) < 0 )
922
        {
923
            msg_Err( p_demux, "cannot get win (%m)" );
gbazin's avatar
   
gbazin committed
924
            goto vdev_failed;
925
926
927
928
        }
        p_sys->i_width  = vid_win.width;
        p_sys->i_height = vid_win.height;

929
930
931
932
933
934
935
936
937
938
939
940
941
        if( !p_sys->i_width || !p_sys->i_height )
        {
            p_sys->i_width = p_sys->vid_cap.maxwidth;
            p_sys->i_height = p_sys->vid_cap.maxheight;
        }

        if( !p_sys->i_width || !p_sys->i_height )
        {
            msg_Err( p_demux, "invalid video size (%ix%i)",
                     p_sys->i_width, p_sys->i_height );
            goto vdev_failed;
        }