v4l.c 43.3 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

42
43
#include <sys/ioctl.h>
#include <sys/mman.h>
44
#include <fcntl.h>
45
#include <arpa/inet.h>
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

/* 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

62
#include <linux/videodev.h>
63
#include "videodev_mjpeg.h"
64

65
/*****************************************************************************
66
 * Module descriptior
67
 *****************************************************************************/
68
69
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );
70

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

dionoea's avatar
dionoea committed
128
129
#define AUDIO_DEPRECATED_ERROR N_( \
    "Alsa or OSS audio capture in the v4l access is deprecated. " \
130
131
    "please use 'v4l:/""/ :input-slave=alsa:/""/' or " \
    "'v4l:/""/ :input-slave=oss:/""/' instead." )
dionoea's avatar
dionoea committed
132

hartman's avatar
hartman committed
133
static const int i_norm_list[] =
134
    { VIDEO_MODE_AUTO, VIDEO_MODE_SECAM, VIDEO_MODE_PAL, VIDEO_MODE_NTSC };
135
static const char *const psz_norm_list_text[] =
136
137
    { N_("Automatic"), N_("SECAM"), N_("PAL"),  N_("NTSC") };

dionoea's avatar
dionoea committed
138
139
#define V4L_DEFAULT "/dev/video"

140
141
142
143
144
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
145

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

181
182
183
184
    add_shortcut( "v4l" )
    set_capability( "access_demux", 10 )
    set_callbacks( Open, Close )
vlc_module_end ()
185

186
187
188
/*****************************************************************************
 * Access: local prototypes
 *****************************************************************************/
189
190
static int Demux  ( demux_t * );
static int Control( demux_t *, int, va_list );
191

192
193
194
static void ParseMRL    ( demux_t * );
static int  OpenVideoDev( demux_t *, char * );
static block_t *GrabVideo( demux_t * );
195

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#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
212
static const struct
gbazin's avatar
   
gbazin committed
213
214
{
    int i_v4l;
215
    vlc_fourcc_t i_fourcc;
gbazin's avatar
   
gbazin committed
216
217
218

} v4lchroma_to_fourcc[] =
{
219
    { VIDEO_PALETTE_GREY, VLC_CODEC_GREY },
gbazin's avatar
   
gbazin committed
220
    { VIDEO_PALETTE_HI240, VLC_FOURCC( 'I', '2', '4', '0' ) },
221
222
223
224
225
226
227
    { 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
228
229
230
    { 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' ) },
231
232
233
    { VIDEO_PALETTE_YUV422P, VLC_CODEC_I422 },
    { VIDEO_PALETTE_YUV420P, VLC_CODEC_I420 },
    { VIDEO_PALETTE_YUV411P, VLC_CODEC_I411 },
gbazin's avatar
   
gbazin committed
234
235
236
    { 0, 0 }
};

237
struct demux_sys_t
238
{
gbazin's avatar
   
gbazin committed
239
    /* Devices */
dionoea's avatar
dionoea committed
240
    char *psz_device;         /* Main device from MRL */
dionoea's avatar
dionoea committed
241
    int  i_fd;
gbazin's avatar
   
gbazin committed
242
243
244

    /* Video properties */
    picture_t pic;
245

gbazin's avatar
   
gbazin committed
246
    int i_fourcc;
247
248
249
250
251
252
253
254
    int i_channel;
    int i_audio;
    int i_norm;
    int i_tuner;
    int i_frequency;
    int i_width;
    int i_height;

255
256
257
258
259
    int i_brightness;
    int i_hue;
    int i_colour;
    int i_contrast;

260
261
262
    float f_fps;            /* <= 0.0 mean to grab at full rate */
    mtime_t i_video_pts;    /* only used when f_fps > 0 */

263
    bool b_mjpeg;
264
265
266
    int i_decimation;
    int i_quality;

267
268
    struct video_capability vid_cap;
    struct video_mbuf       vid_mbuf;
269
    struct mjpeg_requestbuffers mjpeg_buffers;
270
271
272

    uint8_t *p_video_mmap;
    int     i_frame_pos;
273

274
    struct video_mmap   vid_mmap;
gbazin's avatar
   
gbazin committed
275
    struct video_picture vid_picture;
276

277
    int          i_video_frame_size;
dionoea's avatar
dionoea committed
278
    es_out_id_t  *p_es;
279
280
};

281
/*****************************************************************************
282
 * Open: opens v4l device
283
284
285
286
 *****************************************************************************
 *
 * url: <video device>::::
 *
287
 *****************************************************************************/
288
static int Open( vlc_object_t *p_this )
289
{
290
291
292
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;

293
294
295
296
    /* Only when selected */
    if( *p_demux->psz_access == '\0' )
        return VLC_EGENERIC;

297
298
299
300
301
302
    /* 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
303
304
305
    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;
306

ivoire's avatar
ivoire committed
307
308
309
310
311
    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" );
312

ivoire's avatar
ivoire committed
313
314
315
316
317
    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" );
318

319
    p_sys->i_hue        = var_CreateGetInteger( p_demux, "v4l-hue" );
ivoire's avatar
ivoire committed
320
321
    p_sys->i_colour     = var_CreateGetInteger( p_demux, "v4l-colour" );
    p_sys->i_contrast   = var_CreateGetInteger( p_demux, "v4l-contrast" );
322

ivoire's avatar
ivoire committed
323
324
325
    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" );
326

dionoea's avatar
dionoea committed
327
    p_sys->psz_device = NULL;
dionoea's avatar
dionoea committed
328
    p_sys->i_fd = -1;
gbazin's avatar
   
gbazin committed
329

dionoea's avatar
dionoea committed
330
    p_sys->p_es = NULL;
331
332

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

dionoea's avatar
dionoea committed
334
    msg_Dbg( p_this, "opening device '%s'", p_sys->psz_device );
dionoea's avatar
dionoea committed
335
336
    p_sys->i_fd = OpenVideoDev( p_demux, p_sys->psz_device );
    if( p_sys->i_fd < 0 )
gbazin's avatar
   
gbazin committed
337
    {
338
        Close( p_this );
gbazin's avatar
   
gbazin committed
339
340
341
        return VLC_EGENERIC;
    }

dionoea's avatar
dionoea committed
342

343
    msg_Dbg( p_demux, "v4l grabbing started" );
gbazin's avatar
   
gbazin committed
344

345
    /* Declare elementary streams */
dionoea's avatar
dionoea committed
346
347
348
349
350
351
352
353
    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;
    fmt.video.i_aspect = 4 * VOUT_ASPECT_FACTOR / 3;

    /* Setup rgb mask for RGB formats */
    switch( p_sys->i_fourcc )
gbazin's avatar
   
gbazin committed
354
    {
355
        case VLC_CODEC_RGB15:
dionoea's avatar
dionoea committed
356
357
358
359
            fmt.video.i_rmask = 0x001f;
            fmt.video.i_gmask = 0x03e0;
            fmt.video.i_bmask = 0x7c00;
            break;
360
        case VLC_CODEC_RGB16:
dionoea's avatar
dionoea committed
361
362
363
364
            fmt.video.i_rmask = 0x001f;
            fmt.video.i_gmask = 0x07e0;
            fmt.video.i_bmask = 0xf800;
            break;
365
366
        case VLC_CODEC_RGB24:
        case VLC_CODEC_RGB32:
dionoea's avatar
dionoea committed
367
368
369
370
            fmt.video.i_rmask = 0x00ff0000;
            fmt.video.i_gmask = 0x0000ff00;
            fmt.video.i_bmask = 0x000000ff;
            break;
gbazin's avatar
   
gbazin committed
371
372
    }

dionoea's avatar
dionoea committed
373
374
375
    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
376

377
    /* Update default_pts to a suitable value for access */
378
    var_Create( p_demux, "v4l-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
379

gbazin's avatar
   
gbazin committed
380
381
382
    return VLC_SUCCESS;
}

383
/*****************************************************************************
384
 * Close: close device, free resources
385
 *****************************************************************************/
386
static void Close( vlc_object_t *p_this )
387
{
388
389
    demux_t     *p_demux = (demux_t *)p_this;
    demux_sys_t *p_sys   = p_demux->p_sys;
390

391
    free( p_sys->psz_device );
dionoea's avatar
dionoea committed
392
    if( p_sys->i_fd >= 0 ) close( p_sys->i_fd );
393
394
395
396

    if( p_sys->b_mjpeg )
    {
        int i_noframe = -1;
dionoea's avatar
dionoea committed
397
        ioctl( p_sys->i_fd, MJPIOC_QBUF_CAPT, &i_noframe );
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    }

    if( p_sys->p_video_mmap && p_sys->p_video_mmap != MAP_FAILED )
    {
        if( p_sys->b_mjpeg )
            munmap( p_sys->p_video_mmap, p_sys->mjpeg_buffers.size *
                    p_sys->mjpeg_buffers.count );
        else
            munmap( p_sys->p_video_mmap, p_sys->vid_mbuf.size );
    }

    free( p_sys );
}

/*****************************************************************************
413
 * Control:
414
 *****************************************************************************/
415
static int Control( demux_t *p_demux, int i_query, va_list args )
416
{
417
    bool *pb;
418
    int64_t    *pi64;
419
420
421

    switch( i_query )
    {
422
423
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
424
        case DEMUX_CAN_SEEK:
425
        case DEMUX_CAN_CONTROL_PACE:
426
427
            pb = (bool*)va_arg( args, bool * );
            *pb = false;
428
429
430
431
432
433
434
            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;

435
436
437
438
439
        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = mdate();
            return VLC_SUCCESS;

440
        /* TODO implement others */
441
442
443
        default:
            return VLC_EGENERIC;
    }
444
445

    return VLC_EGENERIC;
446
447
448
}

/*****************************************************************************
449
 * Demux:
450
 *****************************************************************************/
451
static int Demux( demux_t *p_demux )
452
{
453
    demux_sys_t *p_sys = p_demux->p_sys;
454

455
    block_t *p_block = GrabVideo( p_demux );
dionoea's avatar
dionoea committed
456

457
    if( !p_block )
458
    {
459
460
        msleep( 10000 ); /* Unfortunately v4l doesn't allow polling */
        return 1;
461
462
    }

463
464
465
    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 );

466
    return 1;
467
468
}

gbazin's avatar
   
gbazin committed
469
470
471
/*****************************************************************************
 * ParseMRL: parse the options contained in the MRL
 *****************************************************************************/
472
static void ParseMRL( demux_t *p_demux )
gbazin's avatar
   
gbazin committed
473
{
474
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
475

476
    char *psz_dup = strdup( p_demux->psz_path );
gbazin's avatar
   
gbazin committed
477
    char *psz_parser = psz_dup;
478
479
480
481
482
483
484
485
486
487
488
489
490
491

    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
492
493
                p_sys->i_channel = strtol( psz_parser + strlen( "channel=" ),
                                           &psz_parser, 0 );
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
            }
            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
523
524
            else if( !strncmp( psz_parser, "frequency=",
                               strlen( "frequency=" ) ) )
525
526
            {
                p_sys->i_frequency =
gbazin's avatar
   
gbazin committed
527
528
                    strtol( psz_parser + strlen( "frequency=" ),
                            &psz_parser, 0 );
529
530
                if( p_sys->i_frequency < 30000 )
                {
531
                    msg_Warn( p_demux, "v4l syntax has changed : "
gbazin's avatar
   
gbazin committed
532
                              "'frequency' is now channel frequency in kHz");
533
                }
534
535
536
            }
            else if( !strncmp( psz_parser, "audio=", strlen( "audio=" ) ) )
            {
gbazin's avatar
   
gbazin committed
537
538
                p_sys->i_audio = strtol( psz_parser + strlen( "audio=" ),
                                         &psz_parser, 0 );
539
540
541
542
543
544
545
546
547
548
549
550
551
552
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
            }
            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
579
580
                        p_sys->i_height = strtol( psz_parser + 1,
                                                  &psz_parser, 0 );
581
                    }
582
                    msg_Dbg( p_demux, "WxH %dx%d", p_sys->i_width,
gbazin's avatar
   
gbazin committed
583
                             p_sys->i_height );
584
585
                }
            }
586
587
588
589
590
591
592
593
594
595
596
597
            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=" ) ) )
            {
598
                p_sys->i_hue = strtol( psz_parser + strlen( "hue=" ),
599
600
601
602
603
604
605
                                       &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "contrast=", strlen( "contrast=" ) ) )
            {
                p_sys->i_contrast = strtol( psz_parser + strlen( "contrast=" ),
                                            &psz_parser, 0 );
            }
606
607
            else if( !strncmp( psz_parser, "tuner=", strlen( "tuner=" ) ) )
            {
gbazin's avatar
   
gbazin committed
608
609
                p_sys->i_tuner = strtol( psz_parser + strlen( "tuner=" ),
                                         &psz_parser, 0 );
610
            }
611
612
613
614
            else if( !strncmp( psz_parser, "mjpeg", strlen( "mjpeg" ) ) )
            {
                psz_parser += strlen( "mjpeg" );

615
                p_sys->b_mjpeg = true;
616
            }
Sam Hocevar's avatar
Sam Hocevar committed
617
            else if( !strncmp( psz_parser, "decimation=",
618
619
                        strlen( "decimation=" ) ) )
            {
Sam Hocevar's avatar
Sam Hocevar committed
620
                p_sys->i_decimation =
621
622
623
624
625
626
627
628
629
630
                    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 );
            }
631
632
633
634
635
            else if( !strncmp( psz_parser, "fps=", strlen( "fps=" ) ) )
            {
                p_sys->f_fps = strtof( psz_parser + strlen( "fps=" ),
                                       &psz_parser );
            }
dionoea's avatar
dionoea committed
636
637
638
639
640
641
642
643
644
645
646
            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 );
            }
647
648
            else
            {
649
                msg_Warn( p_demux, "unknown option" );
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
            }

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

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

    if( *psz_dup )
gbazin's avatar
   
gbazin committed
665
        p_sys->psz_device = strdup( psz_dup );
dionoea's avatar
dionoea committed
666
667
    else
        p_sys->psz_device = strdup( V4L_DEFAULT );
668
    free( psz_dup );
gbazin's avatar
   
gbazin committed
669
}
670

gbazin's avatar
   
gbazin committed
671
672
673
/*****************************************************************************
 * OpenVideoDev:
 *****************************************************************************/
674
static int OpenVideoDev( demux_t *p_demux, char *psz_device )
gbazin's avatar
   
gbazin committed
675
{
676
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
677
    int i_fd;
678

gbazin's avatar
   
gbazin committed
679
680
681
    struct video_channel vid_channel;
    struct mjpeg_params mjpeg;
    int i;
682

gbazin's avatar
   
gbazin committed
683
    if( ( i_fd = open( psz_device, O_RDWR ) ) < 0 )
684
    {
685
        msg_Err( p_demux, "cannot open device (%m)" );
gbazin's avatar
   
gbazin committed
686
        goto vdev_failed;
687
688
    }

gbazin's avatar
   
gbazin committed
689
    if( ioctl( i_fd, VIDIOCGCAP, &p_sys->vid_cap ) < 0 )
690
    {
691
        msg_Err( p_demux, "cannot get capabilities (%m)" );
gbazin's avatar
   
gbazin committed
692
        goto vdev_failed;
693
694
    }

695
    msg_Dbg( p_demux,
696
697
698
699
700
701
702
703
704
             "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 )
    {
705
        msg_Dbg( p_demux, "invalid channel, falling back on channel 0" );
706
707
        p_sys->i_channel = 0;
    }
708
    if( p_sys->vid_cap.audios && p_sys->i_audio >= p_sys->vid_cap.audios )
709
    {
710
        msg_Dbg( p_demux, "invalid audio, falling back with no audio" );
711
712
713
714
715
716
        p_sys->i_audio = -1;
    }

    if( p_sys->i_width < p_sys->vid_cap.minwidth ||
        p_sys->i_width > p_sys->vid_cap.maxwidth )
    {
717
        msg_Dbg( p_demux, "invalid width %i", p_sys->i_width );
718
719
720
721
722
        p_sys->i_width = 0;
    }
    if( p_sys->i_height < p_sys->vid_cap.minheight ||
        p_sys->i_height > p_sys->vid_cap.maxheight )
    {
723
        msg_Dbg( p_demux, "invalid height %i", p_sys->i_height );
724
725
726
        p_sys->i_height = 0;
    }

Sam Hocevar's avatar
Sam Hocevar committed
727
    if( !( p_sys->vid_cap.type & VID_TYPE_CAPTURE ) )
728
    {
729
        msg_Err( p_demux, "cannot grab" );
gbazin's avatar
   
gbazin committed
730
        goto vdev_failed;
731
732
733
    }

    vid_channel.channel = p_sys->i_channel;
gbazin's avatar
   
gbazin committed
734
    if( ioctl( i_fd, VIDIOCGCHAN, &vid_channel ) < 0 )
735
    {
736
        msg_Err( p_demux, "cannot get channel infos (%m)" );
gbazin's avatar
   
gbazin committed
737
        goto vdev_failed;
738
    }
739
    msg_Dbg( p_demux,
740
             "setting channel %s(%d) %d tuners flags=0x%x type=0x%x norm=0x%x",
741
742
             vid_channel.name, vid_channel.channel, vid_channel.tuners,
             vid_channel.flags, vid_channel.type, vid_channel.norm );
743
744
745

    if( p_sys->i_tuner >= vid_channel.tuners )
    {
746
        msg_Dbg( p_demux, "invalid tuner, falling back on tuner 0" );
747
748
749
750
        p_sys->i_tuner = 0;
    }

    vid_channel.norm = p_sys->i_norm;
gbazin's avatar
   
gbazin committed
751
    if( ioctl( i_fd, VIDIOCSCHAN, &vid_channel ) < 0 )
752
    {
753
        msg_Err( p_demux, "cannot set channel (%m)" );
gbazin's avatar
   
gbazin committed
754
        goto vdev_failed;
755
756
757
758
759
760
761
762
763
764
765
    }

    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;
gbazin's avatar
   
gbazin committed
766
            if( ioctl( i_fd, VIDIOCGTUNER, &vid_tuner ) < 0 )
767
            {
768
                msg_Err( p_demux, "cannot get tuner (%m)" );
gbazin's avatar
   
gbazin committed
769
                goto vdev_failed;
770
            }
771
            msg_Dbg( p_demux, "tuner %s low=%d high=%d, flags=0x%x "
gbazin's avatar
   
gbazin committed
772
773
774
775
                     "mode=0x%x signal=0x%x",
                     vid_tuner.name, vid_tuner.rangelow, vid_tuner.rangehigh,
                     vid_tuner.flags, vid_tuner.mode, vid_tuner.signal );

776
            msg_Dbg( p_demux, "setting tuner %s (%d)",
777
778
                     vid_tuner.name, vid_tuner.tuner );

gbazin's avatar
   
gbazin committed
779
780
781
            /* FIXME FIXME to be checked FIXME FIXME */
            //vid_tuner.mode = p_sys->i_norm;
            if( ioctl( i_fd, VIDIOCSTUNER, &vid_tuner ) < 0 )
782
            {
783
                msg_Err( p_demux, "cannot set tuner (%m)" );
gbazin's avatar
   
gbazin committed
784
                goto vdev_failed;
785
786
787
            }
        }
#endif
788

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

792

793
794
795
        /* set frequency */
        if( p_sys->i_frequency >= 0 )
        {
796
            int driver_frequency = p_sys->i_frequency * 16 /1000;
gbazin's avatar
   
gbazin committed
797
            if( ioctl( i_fd, VIDIOCSFREQ, &driver_frequency ) < 0 )
798
            {
799
                msg_Err( p_demux, "cannot set frequency (%m)" );
gbazin's avatar
   
gbazin committed
800
                goto vdev_failed;
801
            }
802
            msg_Dbg( p_demux, "frequency %d (%d)", p_sys->i_frequency,
Sam Hocevar's avatar
Sam Hocevar committed
803
                                                   driver_frequency );
804
805
806
807
808
809
810
811
812
813
814
815
        }
    }

    /* 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;
gbazin's avatar
   
gbazin committed
816
            if( ioctl( i_fd, VIDIOCGAUDIO, &vid_audio ) < 0 )
817
            {
818
                msg_Err( p_demux, "cannot get audio (%m)" );
gbazin's avatar
   
gbazin committed
819
                goto vdev_failed;
820
821
822
823
824
            }

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

gbazin's avatar
   
gbazin committed
825
            if( ioctl( i_fd, VIDIOCSAUDIO, &vid_audio ) < 0 )
826
            {
827
                msg_Err( p_demux, "cannot set audio (%m)" );
gbazin's avatar
   
gbazin committed
828
                goto vdev_failed;
829
            }
830
        }
831

832
    }
833

834
835
836
837
838
839
840
    /* establish basic params with input and norm before feeling width
     * or height */
    if( p_sys->b_mjpeg )
    {
        struct quicktime_mjpeg_app1 *p_app1;
        int32_t i_offset;

gbazin's avatar
   
gbazin committed
841
        if( ioctl( i_fd, MJPIOC_G_PARAMS, &mjpeg ) < 0 )
842
        {
843
            msg_Err( p_demux, "cannot get mjpeg params (%m)" );
gbazin's avatar
   
gbazin committed
844
            goto vdev_failed;
845
        }
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
        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 */
        p_app1 = (struct quicktime_mjpeg_app1 *)mjpeg.APP_data;
        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;
        /* 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;
        p_app1->i_DQT_offset = hton32( i_offset );
        i_offset = 0xb4;
        p_app1->i_DHT_offset = hton32( i_offset );
        i_offset = 0x258;
        p_app1->i_SOF_offset = hton32( i_offset );
        i_offset = 0x26b;
        p_app1->i_SOS_offset = hton32( i_offset );
        i_offset = 0x279;
        p_app1->i_data_offset = hton32( i_offset );

        /* 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;

gbazin's avatar
   
gbazin committed
891
        if( ioctl( i_fd, MJPIOC_S_PARAMS, &mjpeg ) < 0 )
892
        {
893
            msg_Err( p_demux, "cannot set mjpeg params (%m)" );
gbazin's avatar
   
gbazin committed
894
            goto vdev_failed;
895
896
897
898
899
        }

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

902
903
    /* fix width/height */
    if( !p_sys->b_mjpeg && ( p_sys->i_width == 0 || p_sys->i_height == 0 ) )
904
905
906
    {
        struct video_window vid_win;

gbazin's avatar
   
gbazin committed
907
        if( ioctl( i_fd, VIDIOCGWIN, &vid_win ) < 0 )
908
        {
909
            msg_Err( p_demux, "cannot get win (%m)" );
gbazin's avatar
   
gbazin committed
910
            goto vdev_failed;
911
912
913
914
        }
        p_sys->i_width  = vid_win.width;
        p_sys->i_height = vid_win.height;

915
916
917
918
919
920
921
922
923
924
925
926
927
        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;
        }

928
        msg_Dbg( p_demux, "will use %dx%d", p_sys->i_width, p_sys->i_height );
929
930
    }

gbazin's avatar
   
gbazin committed
931
    if( !p_sys->b_mjpeg )
932
    {
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
        /* set hue/color/.. */
        if( ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 )
        {
            struct video_picture vid_picture = p_sys->vid_picture;

            if( p_sys->i_brightness >= 0 && p_sys->i_brightness < 65536 )
            {
                vid_picture.brightness = p_sys->i_brightness;
            }
            if( p_sys->i_colour >= 0 && p_sys->i_colour < 65536 )
            {
                vid_picture.colour = p_sys->i_colour;
            }
            if( p_sys->i_hue >= 0 && p_sys->i_hue < 65536 )
            {
                vid_picture.hue = p_sys->i_hue;
            }
            if( p_sys->i_contrast  >= 0 && p_sys->i_contrast < 65536 )
            {
                vid_picture.contrast = p_sys->i_contrast;
            }
            if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 )
            {
956
957
958
959
960
961
962
                msg_Dbg( p_demux, "v4l device uses brightness: %d",
                         vid_picture.brightness );
                msg_Dbg( p_demux, "v4l device uses colour: %d",
                         vid_picture.colour );
                msg_Dbg( p_demux, "v4l device uses hue: %d", vid_picture.hue );
                msg_Dbg( p_demux, "v4l device uses contrast: %d",
                         vid_picture.contrast );
963
964
965
966
                p_sys->vid_picture = vid_picture;
            }
        }

967
        /* Find out video format used by device */
gbazin's avatar
   
gbazin committed
968
        if( ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 )
gbazin's avatar
   
gbazin committed
969
        {
970
            struct video_picture vid_picture = p_sys->vid_picture;
971
            char *psz;
gbazin's avatar
   
gbazin committed
972
            int i;
973

gbazin's avatar
   
gbazin committed
974
975
            p_sys->i_fourcc = 0;

976
            psz = var_CreateGetString( p_demux, "v4l-chroma" );
977
978
979
980

            const vlc_fourcc_t i_chroma =
                vlc_fourcc_GetCodecFromString( VIDEO_ES, psz );
            if( i_chroma )
gbazin's avatar
   
gbazin committed
981
            {
982
                vid_picture.palette = 0;
gbazin's avatar
   
gbazin committed
983
984
985
986
987
988
989
990
991
992
993

                /* Find out v4l chroma code */
                for( i = 0; v4lchroma_to_fourcc[i].i_v4l != 0; i++ )
                {
                    if( v4lchroma_to_fourcc[i].i_fourcc == i_chroma )
                    {
                        vid_picture.palette = v4lchroma_to_fourcc[i].i_v4l;
                        break;
                    }
                }
            }
994
            free( psz );
gbazin's avatar
   
gbazin committed
995
996
997

            if( vid_picture.palette &&
                !ioctl( i_fd, VIDIOCSPICT, &vid_picture ) )
gbazin's avatar
   
gbazin committed
998
999
1000
            {
                p_sys->vid_picture = vid_picture;
            }
<