v4l2.c 113 KB
Newer Older
1
2
3
/*****************************************************************************
 * v4l2.c : Video4Linux2 input module for vlc
 *****************************************************************************
dionoea's avatar
dionoea committed
4
 * Copyright (C) 2002-2009 the VideoLAN team
5
 * $Id$
6
 *
7
8
 * Authors: Benjamin Pracht <bigben at videolan dot org>
 *          Richard Hosking <richard at hovis dot net>
9
10
 *          Antoine Cellerier <dionoea at videolan d.t org>
 *          Dennis Lou <dlou99 at yahoo dot com>
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * 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.
 *
 * 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
24
25
26
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

27
/*
28
29
30
 * Sections based on the reference V4L2 capture example at
 * http://v4l2spec.bytesex.org/spec/capture-example.html
 */
31

32
33
34
35
/*****************************************************************************
 * Preamble
 *****************************************************************************/

36
37
38
39
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

40
#include <vlc_common.h>
41
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
42
#include <vlc_access.h>
43
#include <vlc_demux.h>
zorglub's avatar
zorglub committed
44
#include <vlc_input.h>
45

46
#include <ctype.h>
47
48
#include <fcntl.h>
#include <sys/ioctl.h>
49
#include <sys/mman.h>
50
51
52

#include <linux/videodev2.h>

53
54
#include <poll.h>

55
56
57
58
#ifdef HAVE_LIBV4L2
#   include <libv4l2.h>
#endif

59
60
/*****************************************************************************
 * Module descriptior
61
 *****************************************************************************/
62

63
64
65
66
static int  DemuxOpen ( vlc_object_t * );
static void DemuxClose( vlc_object_t * );
static int  AccessOpen ( vlc_object_t * );
static void AccessClose( vlc_object_t * );
67

68
69
70
71
72
73
#define STANDARD_TEXT N_( "Standard" )
#define STANDARD_LONGTEXT N_( \
    "Video standard (Default, SECAM, PAL, or NTSC)." )
#define CHROMA_TEXT N_("Video input chroma format")
#define CHROMA_LONGTEXT N_( \
    "Force the Video4Linux2 video device to use a specific chroma format " \
dionoea's avatar
dionoea committed
74
    "(eg. I420 or I422 for raw images, MJPG for M-JPEG compressed input) " \
75
76
    "(Complete list: GREY, I240, RV16, RV15, RV24, RV32, YUY2, YUYV, UYVY, " \
    "I41N, I422, I420, I411, I410, MJPG)")
bigben's avatar
bigben committed
77
78
#define INPUT_TEXT N_( "Input" )
#define INPUT_LONGTEXT N_( \
79
80
81
82
    "Input of the card to use (see debug)." )
#define AUDIO_INPUT_TEXT N_( "Audio input" )
#define AUDIO_INPUT_LONGTEXT N_( \
    "Audio input of the card to use (see debug)." )
83
84
85
#define IOMETHOD_TEXT N_( "IO Method" )
#define IOMETHOD_LONGTEXT N_( \
    "IO Method (READ, MMAP, USERPTR)." )
86
87
#define WIDTH_TEXT N_( "Width" )
#define WIDTH_LONGTEXT N_( \
88
    "Force width (-1 for autodetect, 0 for driver default)." )
89
90
#define HEIGHT_TEXT N_( "Height" )
#define HEIGHT_LONGTEXT N_( \
91
    "Force height (-1 for autodetect, 0 for driver default)." )
92
93
#define FPS_TEXT N_( "Framerate" )
#define FPS_LONGTEXT N_( "Framerate to capture, if applicable " \
94
    "(0 for autodetect)." )
95

dionoea's avatar
dionoea committed
96
97
98
99
100
101
#ifdef HAVE_LIBV4L2
#define LIBV4L2_TEXT N_( "Use libv4l2" )
#define LIBV4L2_LONGTEXT N_( \
    "Force usage of the libv4l2 wrapper." )
#endif

102
103
104
#define CTRL_RESET_TEXT N_( "Reset v4l2 controls" )
#define CTRL_RESET_LONGTEXT N_( \
    "Reset controls to defaults provided by the v4l2 driver." )
105
106
#define BRIGHTNESS_TEXT N_( "Brightness" )
#define BRIGHTNESS_LONGTEXT N_( \
107
    "Brightness of the video input (if supported by the v4l2 driver)." )
108
109
#define CONTRAST_TEXT N_( "Contrast" )
#define CONTRAST_LONGTEXT N_( \
110
    "Contrast of the video input (if supported by the v4l2 driver)." )
111
112
#define SATURATION_TEXT N_( "Saturation" )
#define SATURATION_LONGTEXT N_( \
113
    "Saturation of the video input (if supported by the v4l2 driver)." )
114
115
#define HUE_TEXT N_( "Hue" )
#define HUE_LONGTEXT N_( \
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    "Hue of the video input (if supported by the v4l2 driver)." )
#define BLACKLEVEL_TEXT N_( "Black level" )
#define BLACKLEVEL_LONGTEXT N_( \
    "Black level of the video input (if supported by the v4l2 driver)." )
#define AUTOWHITEBALANCE_TEXT N_( "Auto white balance" )
#define AUTOWHITEBALANCE_LONGTEXT N_( \
    "Automatically set the white balance of the video input " \
    "(if supported by the v4l2 driver)." )
#define DOWHITEBALANCE_TEXT N_( "Do white balance" )
#define DOWHITEBALANCE_LONGTEXT N_( \
    "Trigger a white balancing action, useless if auto white balance is " \
    "activated (if supported by the v4l2 driver)." )
#define REDBALANCE_TEXT N_( "Red balance" )
#define REDBALANCE_LONGTEXT N_( \
    "Red balance of the video input (if supported by the v4l2 driver)." )
#define BLUEBALANCE_TEXT N_( "Blue balance" )
#define BLUEBALANCE_LONGTEXT N_( \
    "Blue balance of the video input (if supported by the v4l2 driver)." )
134
135
#define GAMMA_TEXT N_( "Gamma" )
#define GAMMA_LONGTEXT N_( \
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    "Gamma of the video input (if supported by the v4l2 driver)." )
#define EXPOSURE_TEXT N_( "Exposure" )
#define EXPOSURE_LONGTEXT N_( \
    "Exposure of the video input (if supported by the v4L2 driver)." )
#define AUTOGAIN_TEXT N_( "Auto gain" )
#define AUTOGAIN_LONGTEXT N_( \
    "Automatically set the video input's gain (if supported by the " \
    "v4l2 driver)." )
#define GAIN_TEXT N_( "Gain" )
#define GAIN_LONGTEXT N_( \
    "Video input's gain (if supported by the v4l2 driver)." )
#define HFLIP_TEXT N_( "Horizontal flip" )
#define HFLIP_LONGTEXT N_( \
    "Flip the video horizontally (if supported by the v4l2 driver)." )
#define VFLIP_TEXT N_( "Vertical flip" )
#define VFLIP_LONGTEXT N_( \
    "Flip the video vertically (if supported by the v4l2 driver)." )
#define HCENTER_TEXT N_( "Horizontal centering" )
#define HCENTER_LONGTEXT N_( \
    "Set the camera's horizontal centering (if supported by the v4l2 driver)." )
#define VCENTER_TEXT N_( "Vertical centering" )
#define VCENTER_LONGTEXT N_( \
    "Set the camera's vertical centering (if supported by the v4l2 driver)." )
159

160
161
#define AUDIO_VOLUME_TEXT N_( "Volume" )
#define AUDIO_VOLUME_LONGTEXT N_( \
162
    "Volume of the audio input (if supported by the v4l2 driver)." )
163
164
#define AUDIO_BALANCE_TEXT N_( "Balance" )
#define AUDIO_BALANCE_LONGTEXT N_( \
165
    "Balance of the audio input (if supported by the v4l2 driver)." )
166
167
#define AUDIO_MUTE_TEXT N_( "Mute" )
#define AUDIO_MUTE_LONGTEXT N_( \
168
169
170
171
172
173
174
175
176
177
178
    "Mute audio input (if supported by the v4l2 driver)." )
#define AUDIO_BASS_TEXT N_( "Bass" )
#define AUDIO_BASS_LONGTEXT N_( \
    "Bass level of the audio input (if supported by the v4l2 driver)." )
#define AUDIO_TREBLE_TEXT N_( "Treble" )
#define AUDIO_TREBLE_LONGTEXT N_( \
    "Treble level of the audio input (if supported by the v4l2 driver)." )
#define AUDIO_LOUDNESS_TEXT N_( "Loudness" )
#define AUDIO_LOUDNESS_LONGTEXT N_( \
    "Loudness of the audio input (if supported by the v4l2 driver)." )

179
180
181
182
#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
    "Caching value for V4L2 captures. This " \
    "value should be set in milliseconds." )
183
184
185
186
187
188
189
#define S_CTRLS_TEXT N_("v4l2 driver controls")
#define S_CTRLS_LONGTEXT N_( \
    "Set the v4l2 driver controls to the values specified using a comma " \
    "separated list optionally encapsulated by curly braces " \
    "(e.g.: {video_bitrate=6000000,audio_crc=0,stream_type=3} ). " \
    "To list available controls, increase verbosity (-vvv) " \
    "or use the v4l2-ctl application." )
190

191
192
193
194
195
196
197
198
199
200
#define TUNER_TEXT N_("Tuner id")
#define TUNER_LONGTEXT N_( \
    "Tuner id (see debug output)." )
#define FREQUENCY_TEXT N_("Frequency")
#define FREQUENCY_LONGTEXT N_( \
    "Tuner frequency in Hz or kHz (see debug output)" )
#define TUNER_AUDIO_MODE_TEXT N_("Audio mode")
#define TUNER_AUDIO_MODE_LONGTEXT N_( \
    "Tuner audio mono/stereo and track selection." )

201
202
#define AUDIO_DEPRECATED_ERROR N_( \
    "Alsa or OSS audio capture in the v4l2 access is deprecated. " \
203
204
    "please use 'v4l2:/""/ :input-slave=alsa:/""/' or " \
    "'v4l2:/""/ :input-slave=oss:/""/' instead." )
205

206
207
208
#define ASPECT_TEXT N_("Picture aspect-ratio n:m")
#define ASPECT_LONGTEXT N_("Define input picture aspect-ratio to use. Default is 4:3" )

209
210
211
212
213
214
typedef enum {
    IO_METHOD_READ,
    IO_METHOD_MMAP,
    IO_METHOD_USERPTR,
} io_method;

215
static const int i_standards_list[] =
216
    { V4L2_STD_UNKNOWN, V4L2_STD_SECAM, V4L2_STD_PAL, V4L2_STD_NTSC };
217
static const char *const psz_standards_list_text[] =
218
    { N_("Default"), N_("SECAM"), N_("PAL"),  N_("NTSC") };
219

220
static const int i_iomethod_list[] =
221
    { IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR };
222
static const char *const psz_iomethod_list_text[] =
223
    { N_("READ"), N_("MMAP"),  N_("USERPTR") };
224

225
static const int i_tuner_audio_modes_list[] =
226
227
228
    { V4L2_TUNER_MODE_MONO, V4L2_TUNER_MODE_STEREO,
      V4L2_TUNER_MODE_LANG1, V4L2_TUNER_MODE_LANG2,
      V4L2_TUNER_MODE_SAP, V4L2_TUNER_MODE_LANG1_LANG2 };
229
static const char *const psz_tuner_audio_modes_list_text[] =
230
231
232
233
234
235
236
    { N_( "Mono" ),
      N_( "Stereo" ),
      N_( "Primary language (Analog TV tuners only)" ),
      N_( "Secondary language (Analog TV tuners only)" ),
      N_( "Second audio program (Analog TV tuners only)" ),
      N_( "Primary language left, Secondary language right" ) };

237
#define V4L2_DEFAULT "/dev/video0"
238
239
#define CFG_PREFIX "v4l2-"

240
241
242
243
244
vlc_module_begin ()
    set_shortname( N_("Video4Linux2") )
    set_description( N_("Video4Linux2 input") )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
245

246
    set_section( N_( "Video input" ), NULL )
247
    add_integer( CFG_PREFIX "standard", 0, NULL, STANDARD_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
248
249
                 STANDARD_LONGTEXT, false )
        change_integer_list( i_standards_list, psz_standards_list_text, NULL )
250
    add_string( CFG_PREFIX "chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
251
                true )
252
    add_integer( CFG_PREFIX "input", 0, NULL, INPUT_TEXT, INPUT_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
253
                true )
254
    add_integer( CFG_PREFIX "audio-input", 0, NULL, AUDIO_INPUT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
255
                 AUDIO_INPUT_LONGTEXT, true )
256
    add_integer( CFG_PREFIX "io", IO_METHOD_MMAP, NULL, IOMETHOD_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
257
258
                 IOMETHOD_LONGTEXT, true )
        change_integer_list( i_iomethod_list, psz_iomethod_list_text, NULL )
259
    add_integer( CFG_PREFIX "width", -1, NULL, WIDTH_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
260
                WIDTH_LONGTEXT, true )
261
    add_integer( CFG_PREFIX "height", -1, NULL, HEIGHT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
262
                HEIGHT_LONGTEXT, true )
263
264
    add_string( CFG_PREFIX "aspect-ratio", "4:3", NULL, ASPECT_TEXT,
              ASPECT_LONGTEXT, true )
265
    add_float( CFG_PREFIX "fps", 0, NULL, FPS_TEXT, FPS_LONGTEXT, true )
266
    add_integer( CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
267
                CACHING_TEXT, CACHING_LONGTEXT, true )
dionoea's avatar
dionoea committed
268
269
270
#ifdef HAVE_LIBV4L2
    add_bool( CFG_PREFIX "use-libv4l2", false, NULL, LIBV4L2_TEXT, LIBV4L2_LONGTEXT, true );
#endif
271

272
    set_section( N_( "Tuner" ), NULL )
273
    add_integer( CFG_PREFIX "tuner", 0, NULL, TUNER_TEXT, TUNER_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
274
                 true )
275
    add_integer( CFG_PREFIX "tuner-frequency", -1, NULL, FREQUENCY_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
276
                 FREQUENCY_LONGTEXT, true )
277
    add_integer( CFG_PREFIX "tuner-audio-mode", -1, NULL, TUNER_AUDIO_MODE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
278
                 TUNER_AUDIO_MODE_LONGTEXT, true )
279
        change_integer_list( i_tuner_audio_modes_list,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
280
                             psz_tuner_audio_modes_list_text, 0 )
281

282
    set_section( N_( "Controls" ),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
283
                 N_( "v4l2 driver controls, if supported by your v4l2 driver." ) )
284
    add_bool( CFG_PREFIX "controls-reset", false, NULL, CTRL_RESET_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
285
              CTRL_RESET_LONGTEXT, true )
286
    add_integer( CFG_PREFIX "brightness", -1, NULL, BRIGHTNESS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
287
                 BRIGHTNESS_LONGTEXT, true )
288
    add_integer( CFG_PREFIX "contrast", -1, NULL, CONTRAST_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
289
                 CONTRAST_LONGTEXT, true )
290
    add_integer( CFG_PREFIX "saturation", -1, NULL, SATURATION_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
291
                 SATURATION_LONGTEXT, true )
292
    add_integer( CFG_PREFIX "hue", -1, NULL, HUE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
293
                 HUE_LONGTEXT, true )
294
    add_integer( CFG_PREFIX "black-level", -1, NULL, BLACKLEVEL_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
295
                 BLACKLEVEL_LONGTEXT, true )
296
    add_integer( CFG_PREFIX "auto-white-balance", -1, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
297
                 AUTOWHITEBALANCE_TEXT, AUTOWHITEBALANCE_LONGTEXT, true )
298
    add_integer( CFG_PREFIX "do-white-balance", -1, NULL, DOWHITEBALANCE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
299
                 DOWHITEBALANCE_LONGTEXT, true )
300
    add_integer( CFG_PREFIX "red-balance", -1, NULL, REDBALANCE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
301
                 REDBALANCE_LONGTEXT, true )
302
    add_integer( CFG_PREFIX "blue-balance", -1, NULL, BLUEBALANCE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
303
                 BLUEBALANCE_LONGTEXT, true )
304
    add_integer( CFG_PREFIX "gamma", -1, NULL, GAMMA_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
305
                 GAMMA_LONGTEXT, true )
306
    add_integer( CFG_PREFIX "exposure", -1, NULL, EXPOSURE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
307
                 EXPOSURE_LONGTEXT, true )
308
    add_integer( CFG_PREFIX "autogain", -1, NULL, AUTOGAIN_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
309
                 AUTOGAIN_LONGTEXT, true )
310
    add_integer( CFG_PREFIX "gain", -1, NULL, GAIN_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
311
                 GAIN_LONGTEXT, true )
312
    add_integer( CFG_PREFIX "hflip", -1, NULL, HFLIP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
313
                 HFLIP_LONGTEXT, true )
314
    add_integer( CFG_PREFIX "vflip", -1, NULL, VFLIP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
315
                 VFLIP_LONGTEXT, true )
316
    add_integer( CFG_PREFIX "hcenter", -1, NULL, HCENTER_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
317
                 HCENTER_LONGTEXT, true )
318
    add_integer( CFG_PREFIX "vcenter", -1, NULL, VCENTER_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
319
                 VCENTER_LONGTEXT, true )
320
    add_integer( CFG_PREFIX "audio-volume", -1, NULL, AUDIO_VOLUME_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
321
                AUDIO_VOLUME_LONGTEXT, true )
322
    add_integer( CFG_PREFIX "audio-balance", -1, NULL, AUDIO_BALANCE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
323
                AUDIO_BALANCE_LONGTEXT, true )
324
    add_bool( CFG_PREFIX "audio-mute", false, NULL, AUDIO_MUTE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
325
              AUDIO_MUTE_LONGTEXT, true )
326
    add_integer( CFG_PREFIX "audio-bass", -1, NULL, AUDIO_BASS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
327
                AUDIO_BASS_LONGTEXT, true )
328
    add_integer( CFG_PREFIX "audio-treble", -1, NULL, AUDIO_TREBLE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
329
                AUDIO_TREBLE_LONGTEXT, true )
330
    add_integer( CFG_PREFIX "audio-loudness", -1, NULL, AUDIO_LOUDNESS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
331
                AUDIO_LOUDNESS_LONGTEXT, true )
332
    add_string( CFG_PREFIX "set-ctrls", NULL, NULL, S_CTRLS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
333
              S_CTRLS_LONGTEXT, true )
334

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
335
    add_obsolete_string( CFG_PREFIX "dev" )
336

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
337
338
339
340
    add_obsolete_string( CFG_PREFIX "adev" )
    add_obsolete_integer( CFG_PREFIX "audio-method" )
    add_obsolete_bool( CFG_PREFIX "stereo" )
    add_obsolete_integer( CFG_PREFIX "samplerate" )
341

342
343
344
    add_shortcut( "v4l2" )
    set_capability( "access_demux", 10 )
    set_callbacks( DemuxOpen, DemuxClose )
345

346
    add_submodule ()
347
    add_shortcut( "v4l2" )
348
349
350
    add_shortcut( "v4l2c" )
    set_description( N_("Video4Linux2 Compressed A/V") )
    set_capability( "access", 0 )
351
    /* use these when open as access_demux fails; VLC will use another demux */
352
    set_callbacks( AccessOpen, AccessClose )
353

354
vlc_module_end ()
355
356
357
358
359

/*****************************************************************************
 * Access: local prototypes
 *****************************************************************************/

360
361
static void CommonClose( vlc_object_t *, demux_sys_t * );
static void ParseMRL( demux_sys_t *, char *, vlc_object_t * );
362
static void GetV4L2Params( demux_sys_t *, vlc_object_t * );
363
static void SetAvailControlsByString( vlc_object_t *, demux_sys_t *, int );
364

365
static int DemuxControl( demux_t *, int, va_list );
366
static int AccessControl( access_t *, int, va_list );
367

368
static int Demux( demux_t * );
369
static block_t *AccessRead( access_t * );
370

371
372
static block_t* GrabVideo( vlc_object_t *p_demux, demux_sys_t *p_sys );
static block_t* ProcessVideoFrame( vlc_object_t *p_demux, uint8_t *p_frame, size_t );
bigben's avatar
bigben committed
373

374
static bool IsPixelFormatSupported( demux_t *p_demux,
375
                                          unsigned int i_pixelformat );
376

377
378
static int OpenVideoDev( vlc_object_t *, demux_sys_t *, bool );
static bool ProbeVideoDev( vlc_object_t *, demux_sys_t *,
379
                                 const char *psz_device );
380

dionoea's avatar
dionoea committed
381
382
static int ControlList( vlc_object_t *, demux_sys_t *, int , bool, bool );
static int Control( vlc_object_t *, demux_sys_t *, int i_fd,
383
                    const char *psz_name, int i_cid, int i_value );
384
385
386
387
388
389
390
391
392
393

static int DemuxControlCallback( vlc_object_t *p_this, const char *psz_var,
                                 vlc_value_t oldval, vlc_value_t newval,
                                 void *p_data );
static int DemuxControlResetCallback( vlc_object_t *p_this, const char *psz_var,
                                      vlc_value_t oldval, vlc_value_t newval,
                                      void *p_data );
static int AccessControlCallback( vlc_object_t *p_this, const char *psz_var,
                                  vlc_value_t oldval, vlc_value_t newval,
                                  void *p_data );
394
static int AccessControlResetCallback( vlc_object_t *p_this,
395
396
                                       const char *psz_var, vlc_value_t oldval,
                                       vlc_value_t newval, void *p_data );
397

398
static const struct
399
{
400
    unsigned int i_v4l2;
401
    vlc_fourcc_t i_fourcc;
dionoea's avatar
dionoea committed
402
403
404
    int i_rmask;
    int i_gmask;
    int i_bmask;
405
406
} v4l2chroma_to_fourcc[] =
{
407
    /* Raw data types */
408
    { V4L2_PIX_FMT_GREY,    VLC_CODEC_GREY, 0, 0, 0 },
dionoea's avatar
dionoea committed
409
    { V4L2_PIX_FMT_HI240,   VLC_FOURCC('I','2','4','0'), 0, 0, 0 },
410
411
    { V4L2_PIX_FMT_RGB555,  VLC_CODEC_RGB15, 0x001f,0x03e0,0x7c00 },
    { V4L2_PIX_FMT_RGB565,  VLC_CODEC_RGB16, 0x001f,0x07e0,0xf800 },
dionoea's avatar
dionoea committed
412
413
    /* Won't work since we don't know how to handle such gmask values
     * correctly
414
415
    { V4L2_PIX_FMT_RGB555X, VLC_CODEC_RGB15, 0x007c,0xe003,0x1f00 },
    { V4L2_PIX_FMT_RGB565X, VLC_CODEC_RGB16, 0x00f8,0xe007,0x1f00 },
dionoea's avatar
dionoea committed
416
    */
417
418
419
420
421
422
    { V4L2_PIX_FMT_BGR24,   VLC_CODEC_RGB24, 0xff0000,0xff00,0xff },
    { V4L2_PIX_FMT_RGB24,   VLC_CODEC_RGB24, 0xff,0xff00,0xff0000 },
    { V4L2_PIX_FMT_BGR32,   VLC_CODEC_RGB32, 0xff0000,0xff00,0xff },
    { V4L2_PIX_FMT_RGB32,   VLC_CODEC_RGB32, 0xff,0xff00,0xff0000 },
    { V4L2_PIX_FMT_YUYV,    VLC_CODEC_YUYV, 0, 0, 0 },
    { V4L2_PIX_FMT_UYVY,    VLC_CODEC_UYVY, 0, 0, 0 },
dionoea's avatar
dionoea committed
423
    { V4L2_PIX_FMT_Y41P,    VLC_FOURCC('I','4','1','N'), 0, 0, 0 },
424
425
426
427
    { V4L2_PIX_FMT_YUV422P, VLC_CODEC_I422, 0, 0, 0 },
    { V4L2_PIX_FMT_YVU420,  VLC_CODEC_YV12, 0, 0, 0 },
    { V4L2_PIX_FMT_YUV411P, VLC_CODEC_I411, 0, 0, 0 },
    { V4L2_PIX_FMT_YUV410,  VLC_CODEC_I410, 0, 0, 0 },
428
429
430

    /* Raw data types, not in V4L2 spec but still in videodev2.h and supported
     * by VLC */
431
432
    { V4L2_PIX_FMT_YUV420,  VLC_CODEC_I420, 0, 0, 0 },
    /* FIXME { V4L2_PIX_FMT_RGB444,  VLC_CODEC_RGB32 }, */
433

434
    /* Compressed data types */
435
    { V4L2_PIX_FMT_MJPEG,   VLC_CODEC_MJPG, 0, 0, 0 },
dionoea's avatar
dionoea committed
436
    { V4L2_PIX_FMT_JPEG,    VLC_CODEC_JPEG, 0, 0, 0 },
437
438
439
440
#if 0
    { V4L2_PIX_FMT_DV,      VLC_FOURCC('?','?','?','?') },
    { V4L2_PIX_FMT_MPEG,    VLC_FOURCC('?','?','?','?') },
#endif
dionoea's avatar
dionoea committed
441
    { 0, 0, 0, 0, 0 }
442
};
443

444
445
446
/**
 * List of V4L2 chromas were confident enough to use as fallbacks if the
 * user hasn't provided a --v4l2-chroma value.
dionoea's avatar
dionoea committed
447
448
 *
 * Try YUV chromas first, then RGB little endian and MJPEG as last resort.
449
450
451
 */
static const __u32 p_chroma_fallbacks[] =
{ V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV422P,
dionoea's avatar
dionoea committed
452
  V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_BGR24,
dionoea's avatar
dionoea committed
453
  V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG };
454

455
static const struct
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
{
    const char *psz_name;
    unsigned int i_cid;
} controls[] =
{
    { "brightness", V4L2_CID_BRIGHTNESS },
    { "contrast", V4L2_CID_CONTRAST },
    { "saturation", V4L2_CID_SATURATION },
    { "hue", V4L2_CID_HUE },
    { "audio-volume", V4L2_CID_AUDIO_VOLUME },
    { "audio-balance", V4L2_CID_AUDIO_BALANCE },
    { "audio-bass", V4L2_CID_AUDIO_BASS },
    { "audio-treble", V4L2_CID_AUDIO_TREBLE },
    { "audio-mute", V4L2_CID_AUDIO_MUTE },
    { "audio-loudness", V4L2_CID_AUDIO_LOUDNESS },
    { "black-level", V4L2_CID_BLACK_LEVEL },
    { "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE },
    { "do-white-balance", V4L2_CID_DO_WHITE_BALANCE },
    { "red-balance", V4L2_CID_RED_BALANCE },
    { "blue-balance", V4L2_CID_BLUE_BALANCE },
    { "gamma", V4L2_CID_GAMMA },
    { "exposure", V4L2_CID_EXPOSURE },
    { "autogain", V4L2_CID_AUTOGAIN },
    { "gain", V4L2_CID_GAIN },
    { "hflip", V4L2_CID_HFLIP },
    { "vflip", V4L2_CID_VFLIP },
    { "hcenter", V4L2_CID_HCENTER },
    { "vcenter", V4L2_CID_VCENTER },
    { NULL, 0 }
};

487
struct buffer_t
488
489
490
491
492
493
494
{
    void *  start;
    size_t  length;
};

struct demux_sys_t
{
495
496
    char *psz_device;  /* Main device from MRL */
    int  i_fd;
497

498
    char *psz_requested_chroma;
499

500
501
    /* Video */
    io_method io;
502

dionoea's avatar
dionoea committed
503
    int i_cache;
504
505
506
507
508

    struct v4l2_capability dev_cap;

    int i_input;
    struct v4l2_input *p_inputs;
bigben's avatar
bigben committed
509
    int i_selected_input;
510

511
512
513
    int i_standard;
    struct v4l2_standard *p_standards;
    v4l2_std_id i_selected_standard_id;
514
515
516
517

    int i_audio;
    /* V4L2 devices cannot have more than 32 audio inputs */
    struct v4l2_audio p_audios[32];
518
    int i_selected_audio_input;
519
520
521

    int i_tuner;
    struct v4l2_tuner *p_tuners;
bigben's avatar
bigben committed
522
523
524

    int i_codec;
    struct v4l2_fmtdesc *p_codecs;
525

526
527
    struct buffer_t *p_buffers;
    unsigned int i_nbuffers;
528

529
530
    int i_width;
    int i_height;
531
    unsigned int i_aspect;
532
533
534
    float f_fps;            /* <= 0.0 mean to grab at full rate */
    mtime_t i_video_pts;    /* only used when f_fps > 0 */
    int i_fourcc;
535

536
    es_out_id_t *p_es;
537

538
539
540
541
542
543
    /* Tuner */
    int i_cur_tuner;
    int i_frequency;
    int i_audio_mode;

    /* Controls */
544
    char *psz_set_ctrls;
dionoea's avatar
dionoea committed
545
546
547
548
549
550
551
552
553
554


#ifdef HAVE_LIBV4L2
    /* */
    int (*pf_close)( int );
    int (*pf_dup)( int );
    int (*pf_ioctl)( int, unsigned long int, ... );
    ssize_t (*pf_read)( int, void *, size_t );
    void *(*pf_mmap)( void *, size_t, int, int, int, off_t );
    int (*pf_munmap)( void *, size_t );
dionoea's avatar
dionoea committed
555
    bool b_libv4l2;
dionoea's avatar
dionoea committed
556
#endif
557
558
};

dionoea's avatar
dionoea committed
559
560
561
562
563
564
565
566
567
#ifdef HAVE_LIBV4L2
static void use_kernel_v4l2( demux_sys_t *p_sys )
{
    p_sys->pf_close = close;
    p_sys->pf_dup = dup;
    p_sys->pf_ioctl = ioctl;
    p_sys->pf_read = read;
    p_sys->pf_mmap = mmap;
    p_sys->pf_munmap = munmap;
dionoea's avatar
dionoea committed
568
    p_sys->b_libv4l2 = false;
dionoea's avatar
dionoea committed
569
570
571
572
573
574
575
576
577
578
}

static void use_libv4l2( demux_sys_t *p_sys )
{
    p_sys->pf_close = v4l2_close;
    p_sys->pf_dup = v4l2_dup;
    p_sys->pf_ioctl = v4l2_ioctl;
    p_sys->pf_read = v4l2_read;
    p_sys->pf_mmap = v4l2_mmap;
    p_sys->pf_munmap = v4l2_munmap;
dionoea's avatar
dionoea committed
579
    p_sys->b_libv4l2 = true;
dionoea's avatar
dionoea committed
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
}

#   define v4l2_close (p_sys->pf_close)
#   define v4l2_dup (p_sys->pf_dup)
#   define v4l2_ioctl (p_sys->pf_ioctl)
#   define v4l2_read (p_sys->pf_read)
#   define v4l2_mmap (p_sys->pf_mmap)
#   define v4l2_munmap (p_sys->pf_munmap)
#else
#   define v4l2_close close
#   define v4l2_dup dup
#   define v4l2_ioctl ioctl
#   define v4l2_read read
#   define v4l2_mmap mmap
#   define v4l2_munmap munmap
#endif

597
static int FindMainDevice( vlc_object_t *p_this, demux_sys_t *p_sys,
598
                           bool b_demux )
599
{
600
601
602
603
    /* TODO: if using default device, loop through all /dev/video* until
     * one works */
    msg_Dbg( p_this, "opening device '%s'", p_sys->psz_device );
    if( ProbeVideoDev( p_this, p_sys, p_sys->psz_device ) )
604
    {
605
606
        msg_Dbg( p_this, "'%s' is a video device", p_sys->psz_device );
        p_sys->i_fd = OpenVideoDev( p_this, p_sys, b_demux );
607
608
    }

609
    if( p_sys->i_fd < 0 ) return VLC_EGENERIC;
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DemuxOpen: opens v4l2 device, access_demux callback
 *****************************************************************************
 *
 * url: <video device>::::
 *
 *****************************************************************************/
static int DemuxOpen( vlc_object_t *p_this )
{
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;

    /* Only when selected */
626
627
    if( strcmp( p_demux->psz_access, "v4l2" ) )
        return VLC_EGENERIC;
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642

    /* Set up p_demux */
    p_demux->pf_control = DemuxControl;
    p_demux->pf_demux = Demux;
    p_demux->info.i_update = 0;
    p_demux->info.i_title = 0;
    p_demux->info.i_seekpoint = 0;

    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if( p_sys == NULL ) return VLC_ENOMEM;

    GetV4L2Params(p_sys, (vlc_object_t *) p_demux);

    ParseMRL( p_sys, p_demux->psz_path, (vlc_object_t *) p_demux );

dionoea's avatar
dionoea committed
643
644
#ifdef HAVE_LIBV4L2
    if( !config_GetInt( p_this, CFG_PREFIX "use-libv4l2" ) )
645
    {
dionoea's avatar
dionoea committed
646
647
648
649
        msg_Dbg( p_this, "Trying direct kernel v4l2" );
        use_kernel_v4l2( p_sys );
        if( FindMainDevice( p_this, p_sys, true ) == VLC_SUCCESS)
            return VLC_SUCCESS;
650
    }
651

dionoea's avatar
dionoea committed
652
653
654
655
656
657
658
659
    msg_Dbg( p_this, "Trying libv4l2 wrapper" );
    use_libv4l2( p_sys );
#endif
    if( FindMainDevice( p_this, p_sys, true ) == VLC_SUCCESS)
        return VLC_SUCCESS;

    DemuxClose( p_this );
    return VLC_EGENERIC;
660
661
}

662
/*****************************************************************************
663
 * GetV4L2Params: fill in p_sys parameters (shared by DemuxOpen and AccessOpen)
664
 *****************************************************************************/
665
static void GetV4L2Params( demux_sys_t *p_sys, vlc_object_t *p_obj )
666
{
667
668
    p_sys->i_video_pts = -1;

669
670
    p_sys->i_selected_standard_id =
        i_standards_list[var_CreateGetInteger( p_obj, "v4l2-standard" )];
671
672

    p_sys->i_selected_input = var_CreateGetInteger( p_obj, "v4l2-input" );
673
674
    p_sys->i_selected_audio_input =
        var_CreateGetInteger( p_obj, "v4l2-audio-input" );
675
676
677
678
679
680

    p_sys->io = var_CreateGetInteger( p_obj, "v4l2-io" );

    p_sys->i_width = var_CreateGetInteger( p_obj, "v4l2-width" );
    p_sys->i_height = var_CreateGetInteger( p_obj, "v4l2-height" );

681
    var_Create( p_obj, "v4l2-controls-reset", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
682

683
    p_sys->f_fps = var_CreateGetFloat( p_obj, "v4l2-fps" );
684
685
    p_sys->psz_requested_chroma = var_CreateGetString( p_obj, "v4l2-chroma" );

dionoea's avatar
dionoea committed
686
    p_sys->i_cache = var_CreateGetInteger( p_obj, "v4l2-caching" );
687

688
689
690
691
    p_sys->i_cur_tuner = var_CreateGetInteger( p_obj, "v4l2-tuner" );
    p_sys->i_frequency = var_CreateGetInteger( p_obj, "v4l2-tuner-frequency" );
    p_sys->i_audio_mode = var_CreateGetInteger( p_obj, "v4l2-tuner-audio-mode" );

692
693
    p_sys->psz_set_ctrls = var_CreateGetString( p_obj, "v4l2-set-ctrls" );

694
695
696
697
698
699
700
701
702
703
704
705
706
    char *psz_aspect = var_CreateGetString( p_obj, "v4l2-aspect-ratio" );
    if( psz_aspect && *psz_aspect && strchr( psz_aspect, ":" ) )
    {
        char psz_delim = strchr( psz_aspect, ":" );
        p_sys->i_aspect = atoi( psz_aspect ) * VOUT_ASPECT_FACTOR / atoi( psz_delim + 1 );
    }
    else
    {
        p_sys->i_aspect = 4 * VOUT_ASPECT_FACTOR / 3 ;

    }
    free( psz_aspect );

707
708
    p_sys->psz_device = NULL;
    p_sys->i_fd = -1;
709

710
    p_sys->p_es = NULL;
711
712
713
714
715
716
717
718
}

/*****************************************************************************
 * ParseMRL: parse the options contained in the MRL
 *****************************************************************************/
static void ParseMRL( demux_sys_t *p_sys, char *psz_path, vlc_object_t *p_obj )
{
    char *psz_dup = strdup( psz_path );
719
720
721
722
723
724
725
726
727
728
729
730
731
    char *psz_parser = psz_dup;

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

    if( *psz_parser == ':' )
    {
        /* read options */
        for( ;; )
        {
            *psz_parser++ = '\0';
732

733
            if( !strncmp( psz_parser, "standard=", strlen( "standard=" ) ) )
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
            {
                psz_parser += strlen( "standard=" );
                if( !strncmp( psz_parser, "pal", strlen( "pal" ) ) )
                {
                    p_sys->i_selected_standard_id = V4L2_STD_PAL;
                    psz_parser += strlen( "pal" );
                }
                else if( !strncmp( psz_parser, "ntsc", strlen( "ntsc" ) ) )
                {
                    p_sys->i_selected_standard_id = V4L2_STD_NTSC;
                    psz_parser += strlen( "ntsc" );
                }
                else if( !strncmp( psz_parser, "secam", strlen( "secam" ) ) )
                {
                    p_sys->i_selected_standard_id = V4L2_STD_SECAM;
                    psz_parser += strlen( "secam" );
                }
                else if( !strncmp( psz_parser, "default", strlen( "default" ) ) )
                {
                    p_sys->i_selected_standard_id = V4L2_STD_UNKNOWN;
                    psz_parser += strlen( "default" );
                }
                else
                {
                    p_sys->i_selected_standard_id = i_standards_list[strtol( psz_parser, &psz_parser, 0 )];
                }
            }
            else if( !strncmp( psz_parser, "chroma=", strlen( "chroma=" ) ) )
            {
                int  i_len;

                psz_parser += strlen( "chroma=" );
                if( strchr( psz_parser, ':' ) )
                {
                    i_len = strchr( psz_parser, ':' ) - psz_parser;
                }
                else
                {
                    i_len = strlen( psz_parser );
                }

775
                free( p_sys->psz_requested_chroma );
776
                p_sys->psz_requested_chroma = strndup( psz_parser, i_len );
777

778
                psz_parser += i_len;
779
780
781
782
783
            }
            else if( !strncmp( psz_parser, "input=", strlen( "input=" ) ) )
            {
                p_sys->i_selected_input = strtol( psz_parser + strlen( "input=" ),
                                       &psz_parser, 0 );
784
            }
785
786
787
788
789
            else if( !strncmp( psz_parser, "audio-input=", strlen( "audio-input=" ) ) )
            {
                p_sys->i_selected_audio_input = strtol( psz_parser + strlen( "audio-input=" ),
                                       &psz_parser, 0 );
            }
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
            else if( !strncmp( psz_parser, "fps=", strlen( "fps=" ) ) )
            {
                p_sys->f_fps = strtof( psz_parser + strlen( "fps=" ),
                                       &psz_parser );
            }
            else if( !strncmp( psz_parser, "io=", strlen( "io=" ) ) )
            {
                psz_parser += strlen( "io=" );
                if( !strncmp( psz_parser, "read", strlen( "read" ) ) )
                {
                    p_sys->io = IO_METHOD_READ;
                    psz_parser += strlen( "read" );
                }
                else if( !strncmp( psz_parser, "mmap", strlen( "mmap" ) ) )
                {
                    p_sys->io = IO_METHOD_MMAP;
                    psz_parser += strlen( "mmap" );
                }
                else if( !strncmp( psz_parser, "userptr", strlen( "userptr" ) ) )
                {
                    p_sys->io = IO_METHOD_USERPTR;
                    psz_parser += strlen( "userptr" );
                }
                else
                {
                    p_sys->io = strtol( psz_parser, &psz_parser, 0 );
                }
            }
818
819
820
821
822
823
824
825
826
827
828
829
830
            else if( !strncmp( psz_parser, "width=",
                               strlen( "width=" ) ) )
            {
                p_sys->i_width =
                    strtol( psz_parser + strlen( "width=" ),
                            &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "height=",
                               strlen( "height=" ) ) )
            {
                p_sys->i_height =
                    strtol( psz_parser + strlen( "height=" ),
                            &psz_parser, 0 );
831
            }
832
833
834
835
836
837
838
839
840
841
842
            else if( !strncmp( psz_parser, "aspect-ratio=",
                               strlen( "aspect-ratio=" ) ) )
            {
                unsigned int num,den;
                num = strtol( psz_parser + strlen( "aspect-ratio=" ),
                              &psz_parser, 0 );
                den = strtol( psz_parser + strlen( ":" ),
                              &psz_parser, 0 );
                if( num && den )
                    p_sys->i_aspect = num * VOUT_ASPECT_FACTOR / den;
            }
843
844
            else if( !strncmp( psz_parser, "controls-reset",
                               strlen( "controls-reset" ) ) )
dionoea's avatar
dionoea committed
845
            {
846
                var_SetBool( p_obj, "v4l2-controls-reset", true );
847
                psz_parser += strlen( "controls-reset" );
dionoea's avatar
dionoea committed
848
            }
849
#if 0
850
851
852
            else if( !strncmp( psz_parser, "brightness=",
                               strlen( "brightness=" ) ) )
            {
853
                var_SetInteger( p_obj, "brightness",
854
                    strtol( psz_parser + strlen( "brightness=" ),
855
                            &psz_parser, 0 ) );
856
857
858
859
            }
            else if( !strncmp( psz_parser, "contrast=",
                               strlen( "contrast=" ) ) )
            {
860
                var_SetInteger( p_obj, "contrast",
861
                    strtol( psz_parser + strlen( "contrast=" ),
862
                            &psz_parser, 0 ) );
863
864
865
866
            }
            else if( !strncmp( psz_parser, "saturation=",
                               strlen( "saturation=" ) ) )
            {
867
                var_SetInteger( p_obj, "saturation",
868
                    strtol( psz_parser + strlen( "saturation=" ),
869
                            &psz_parser, 0 ) );
870
871
872
873
            }
            else if( !strncmp( psz_parser, "hue=",
                               strlen( "hue=" ) ) )
            {
874
                var_SetInteger( p_obj, "hue",
875
                    strtol( psz_parser + strlen( "hue=" ),
876
                            &psz_parser, 0 ) );
877
            }
878
879
880
            else if( !strncmp( psz_parser, "gamma=",
                               strlen( "gamma=" ) ) )
            {
881
                var_SetInteger( p_obj, "gamma",
882
                    strtol( psz_parser + strlen( "gamma=" ),
883
                            &psz_parser, 0 ) );
884
            }
885
#endif
886
887
            else if( !strncmp( psz_parser, "caching=", strlen( "caching=" ) ) )
            {
dionoea's avatar
dionoea committed
888
889
                p_sys->i_cache = strtol( psz_parser + strlen( "caching=" ),
                                         &psz_parser, 0 );
890
            }
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
            else if( !strncmp( psz_parser, "tuner=", strlen( "tuner=" ) ) )
            {
                p_sys->i_cur_tuner = strtol( psz_parser + strlen( "tuner=" ),
                                         &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "tuner-frequency=", strlen( "tuner-frequency=" ) ) )
            {
                p_sys->i_frequency = strtol( psz_parser
                                          + strlen( "tuner-frequency=" ),
                                          &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "tuner-audio-mode=", strlen( "tuner-audio-mode=" ) ) )
            {
                p_sys->i_audio_mode = strtol( psz_parser
                                          + strlen( "tuner-audio-mode=" ),
                                          &psz_parser, 0 );
            }
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
            else if( !strncmp( psz_parser, "set-ctrls=", strlen( "set-ctrls=" )) )
            {
                int  i_len;

                psz_parser += strlen( "set-ctrls=" );
                if( strchr( psz_parser, ':' ) )
                {
                    i_len = strchr( psz_parser, ':' ) - psz_parser;
                }
                else
                {
                    i_len = strlen( psz_parser );
                }

                p_sys->psz_set_ctrls = strndup( psz_parser, i_len );

                psz_parser += i_len;
            }
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
            else if( !strncmp( psz_parser, "adev=", strlen( "adev=" ) )
             || !strncmp( psz_parser, "samplerate=", strlen( "samplerate=" ) )
             || !strncmp( psz_parser, "audio-method", strlen( "audio-method" ) )
             || !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_obj, AUDIO_DEPRECATED_ERROR );
            }
943
944
            else
            {
945
946
947
948
949
950
951
                char *psz_unk = strchr( psz_parser, ':' );
                if (psz_unk)
                    psz_unk = strndup( psz_parser, psz_unk - psz_parser );
                else
                    psz_unk = strdup( psz_parser);
                msg_Warn( p_obj, "unknown option %s", psz_unk );
                free (psz_unk);
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
            }

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

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

    /* Main device */
    if( *psz_dup )
        p_sys->psz_device = strdup( psz_dup );
969
970
    else
        p_sys->psz_device = strdup( V4L2_DEFAULT );
971
    free( psz_dup );
972
973
}

974
975
976
/*****************************************************************************
 * Close: close device, free resources
 *****************************************************************************/
977
978
979
980
981
982
983
984
985
static void AccessClose( vlc_object_t *p_this )
{
    access_t    *p_access = (access_t *)p_this;
    demux_sys_t *p_sys   = (demux_sys_t *) p_access->p_sys;

    CommonClose( p_this, p_sys );
}

static void DemuxClose( vlc_object_t *p_this )
986
{
987
988
989
    struct v4l2_buffer buf;
    enum v4l2_buf_type buf_type;
    unsigned int i;
990

991
992
993
    demux_t     *p_demux = (demux_t *)p_this;
    demux_sys_t *p_sys   = p_demux->p_sys;

994
    /* Stop video capture */
995
    if( p_sys->i_fd >= 0 )
996
997
998
999
1000
    {
        switch( p_sys->io )
        {
        case IO_METHOD_READ:
            /* Nothing to do */