v4l.c 45.4 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
40
#include <vlc_input.h>
#include <vlc_demux.h>
#include <vlc_access.h>
#include <vlc_vout.h>
41

42
43
#include <sys/ioctl.h>
#include <sys/mman.h>
44
#include <fcntl.h>
45
#include <arpa/inet.h>
46

dionoea's avatar
dionoea committed
47
48
#include <poll.h>

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

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

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

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

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

dionoea's avatar
dionoea committed
140
141
#define V4L_DEFAULT "/dev/video"

142
143
144
145
146
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
147

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

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

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

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

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#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
214
static const struct
gbazin's avatar
   
gbazin committed
215
216
217
218
219
220
221
222
223
224
225
226
{
    int i_v4l;
    int i_fourcc;

} v4lchroma_to_fourcc[] =
{
    { VIDEO_PALETTE_GREY, VLC_FOURCC( 'G', 'R', 'E', 'Y' ) },
    { VIDEO_PALETTE_HI240, VLC_FOURCC( 'I', '2', '4', '0' ) },
    { VIDEO_PALETTE_RGB565, VLC_FOURCC( 'R', 'V', '1', '6' ) },
    { VIDEO_PALETTE_RGB555, VLC_FOURCC( 'R', 'V', '1', '5' ) },
    { VIDEO_PALETTE_RGB24, VLC_FOURCC( 'R', 'V', '2', '4' ) },
    { VIDEO_PALETTE_RGB32, VLC_FOURCC( 'R', 'V', '3', '2' ) },
227
228
229
    { VIDEO_PALETTE_YUV422, VLC_FOURCC( 'Y', 'U', 'Y', '2' ) },
    { VIDEO_PALETTE_YUV422, VLC_FOURCC( 'Y', 'U', 'Y', 'V' ) },
    { VIDEO_PALETTE_YUYV, VLC_FOURCC( 'Y', 'U', 'Y', '2' ) },
gbazin's avatar
   
gbazin committed
230
231
232
233
234
235
236
237
238
239
240
    { VIDEO_PALETTE_YUYV, VLC_FOURCC( 'Y', 'U', 'Y', 'V' ) },
    { VIDEO_PALETTE_UYVY, VLC_FOURCC( 'U', 'Y', 'V', 'Y' ) },
    { 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' ) },
    { VIDEO_PALETTE_YUV422P, VLC_FOURCC( 'I', '4', '2', '2' ) },
    { VIDEO_PALETTE_YUV420P, VLC_FOURCC( 'I', '4', '2', '0' ) },
    { VIDEO_PALETTE_YUV411P, VLC_FOURCC( 'I', '4', '1', '1' ) },
    { 0, 0 }
};

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

    /* Video properties */
    picture_t pic;
249

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

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

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

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

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

    uint8_t *p_video_mmap;
    int     i_frame_pos;
277

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

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

285
/*****************************************************************************
286
 * Open: opens v4l device
287
288
289
290
 *****************************************************************************
 *
 * url: <video device>::::
 *
291
 *****************************************************************************/
292
static int Open( vlc_object_t *p_this )
293
{
294
295
    demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys;
296
    vlc_value_t val;
297

298
299
300
301
    /* Only when selected */
    if( *p_demux->psz_access == '\0' )
        return VLC_EGENERIC;

302
303
304
305
306
307
    /* 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
308
309
310
    p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) );
    if( !p_sys )
        return VLC_ENOMEM;
311

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
    var_Create( p_demux, "v4l-audio", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-audio", &val );
    p_sys->i_audio          = val.i_int;

    var_Create( p_demux, "v4l-channel", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-channel", &val );
    p_sys->i_channel        = val.i_int;

    var_Create( p_demux, "v4l-norm", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-norm", &val );
    p_sys->i_norm           = val.i_int;

    var_Create( p_demux, "v4l-tuner", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-tuner", &val );
    p_sys->i_tuner          = val.i_int;

    var_Create( p_demux, "v4l-frequency",
                                    VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-frequency", &val );
    p_sys->i_frequency      = val.i_int;

    var_Create( p_demux, "v4l-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-fps", &val );
    p_sys->f_fps            = val.f_float;

    var_Create( p_demux, "v4l-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-width", &val );
    p_sys->i_width          = val.i_int;

    var_Create( p_demux, "v4l-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-height", &val );
Laurent Aimar's avatar
Laurent Aimar committed
343
    p_sys->i_height         = val.i_int;
344

345
346
    p_sys->i_video_pts      = -1;

347
348
    var_Create( p_demux, "v4l-brightness", VLC_VAR_INTEGER |
                                                        VLC_VAR_DOINHERIT );
bigben's avatar
bigben committed
349
    var_Get( p_demux, "v4l-brightness", &val );
350
351
352
353
    p_sys->i_brightness     = val.i_int;

    var_Create( p_demux, "v4l-hue", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-hue", &val );
354
355
    p_sys->i_hue            = -1;

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
    var_Create( p_demux, "v4l-colour", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-colour", &val );
    p_sys->i_colour         = val.i_int;

    var_Create( p_demux, "v4l-contrast", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-contrast", &val );
    p_sys->i_contrast       = val.i_int;

    var_Create( p_demux, "v4l-mjpeg", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-mjpeg", &val );
    p_sys->b_mjpeg     = val.b_bool;

    var_Create( p_demux, "v4l-decimation", VLC_VAR_INTEGER |
                                                            VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-decimation", &val );
    p_sys->i_decimation = val.i_int;

    var_Create( p_demux, "v4l-quality", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    var_Get( p_demux, "v4l-quality", &val );
    p_sys->i_quality = val.i_int;

dionoea's avatar
dionoea committed
377
    p_sys->psz_device = NULL;
dionoea's avatar
dionoea committed
378
    p_sys->i_fd = -1;
gbazin's avatar
   
gbazin committed
379

dionoea's avatar
dionoea committed
380
    p_sys->p_es = NULL;
381
382

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

dionoea's avatar
dionoea committed
384
    msg_Dbg( p_this, "opening device '%s'", p_sys->psz_device );
dionoea's avatar
dionoea committed
385
386
    p_sys->i_fd = OpenVideoDev( p_demux, p_sys->psz_device );
    if( p_sys->i_fd < 0 )
gbazin's avatar
   
gbazin committed
387
    {
388
        Close( p_this );
gbazin's avatar
   
gbazin committed
389
390
391
        return VLC_EGENERIC;
    }

dionoea's avatar
dionoea committed
392

393
    msg_Dbg( p_demux, "v4l grabbing started" );
gbazin's avatar
   
gbazin committed
394

395
    /* Declare elementary streams */
dionoea's avatar
dionoea committed
396
397
398
399
400
401
402
403
    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
404
    {
dionoea's avatar
dionoea committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
        case VLC_FOURCC('R','V','1','5'):
            fmt.video.i_rmask = 0x001f;
            fmt.video.i_gmask = 0x03e0;
            fmt.video.i_bmask = 0x7c00;
            break;
        case VLC_FOURCC('R','V','1','6'):
            fmt.video.i_rmask = 0x001f;
            fmt.video.i_gmask = 0x07e0;
            fmt.video.i_bmask = 0xf800;
            break;
        case VLC_FOURCC('R','V','2','4'):
        case VLC_FOURCC('R','V','3','2'):
            fmt.video.i_rmask = 0x00ff0000;
            fmt.video.i_gmask = 0x0000ff00;
            fmt.video.i_bmask = 0x000000ff;
            break;
gbazin's avatar
   
gbazin committed
421
422
    }

dionoea's avatar
dionoea committed
423
424
425
    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
426

427
    /* Update default_pts to a suitable value for access */
428
    var_Create( p_demux, "v4l-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
429

gbazin's avatar
   
gbazin committed
430
431
432
    return VLC_SUCCESS;
}

433
/*****************************************************************************
434
 * Close: close device, free resources
435
 *****************************************************************************/
436
static void Close( vlc_object_t *p_this )
437
{
438
439
    demux_t     *p_demux = (demux_t *)p_this;
    demux_sys_t *p_sys   = p_demux->p_sys;
440

441
    free( p_sys->psz_device );
dionoea's avatar
dionoea committed
442
    if( p_sys->i_fd >= 0 ) close( p_sys->i_fd );
443
444
445
446

    if( p_sys->b_mjpeg )
    {
        int i_noframe = -1;
dionoea's avatar
dionoea committed
447
        ioctl( p_sys->i_fd, MJPIOC_QBUF_CAPT, &i_noframe );
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
    }

    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 );
}

/*****************************************************************************
463
 * Control:
464
 *****************************************************************************/
465
static int Control( demux_t *p_demux, int i_query, va_list args )
466
{
467
    bool *pb;
468
    int64_t    *pi64;
469
470
471

    switch( i_query )
    {
472
473
        /* Special for access_demux */
        case DEMUX_CAN_PAUSE:
474
        case DEMUX_CAN_SEEK:
475
476
        case DEMUX_SET_PAUSE_STATE:
        case DEMUX_CAN_CONTROL_PACE:
477
478
            pb = (bool*)va_arg( args, bool * );
            *pb = false;
479
480
481
482
483
484
485
            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;

486
487
488
489
490
        case DEMUX_GET_TIME:
            pi64 = (int64_t*)va_arg( args, int64_t * );
            *pi64 = mdate();
            return VLC_SUCCESS;

491
        /* TODO implement others */
492
493
494
        default:
            return VLC_EGENERIC;
    }
495
496

    return VLC_EGENERIC;
497
498
499
}

/*****************************************************************************
500
 * Demux:
501
 *****************************************************************************/
502
static int Demux( demux_t *p_demux )
503
{
504
    demux_sys_t *p_sys = p_demux->p_sys;
505

dionoea's avatar
dionoea committed
506
507
508
509
510
511
512
    struct pollfd fd;
    fd.fd = p_sys->i_fd;
    fd.events = POLLIN|POLLPRI;
    fd.revents = 0;

    /* Wait for data */
    if( poll( &fd, 1, 500 ) )
513
    {
dionoea's avatar
dionoea committed
514
515
516
517
518
519
520
521
522
        if( fd.revents & (POLLIN|POLLPRI) )
        {
            block_t *p_block = GrabVideo( p_demux );
            if( p_block )
            {
                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 );
            }
        }
523
524
    }

525
    return 1;
526
527
}

gbazin's avatar
   
gbazin committed
528
529
530
/*****************************************************************************
 * ParseMRL: parse the options contained in the MRL
 *****************************************************************************/
531
static void ParseMRL( demux_t *p_demux )
gbazin's avatar
   
gbazin committed
532
{
533
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
534

535
    char *psz_dup = strdup( p_demux->psz_path );
gbazin's avatar
   
gbazin committed
536
    char *psz_parser = psz_dup;
537
538
539
540
541
542
543
544
545
546
547
548
549
550

    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
551
552
                p_sys->i_channel = strtol( psz_parser + strlen( "channel=" ),
                                           &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
            }
            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
582
583
            else if( !strncmp( psz_parser, "frequency=",
                               strlen( "frequency=" ) ) )
584
585
            {
                p_sys->i_frequency =
gbazin's avatar
   
gbazin committed
586
587
                    strtol( psz_parser + strlen( "frequency=" ),
                            &psz_parser, 0 );
588
589
                if( p_sys->i_frequency < 30000 )
                {
590
                    msg_Warn( p_demux, "v4l syntax has changed : "
gbazin's avatar
   
gbazin committed
591
                              "'frequency' is now channel frequency in kHz");
592
                }
593
594
595
            }
            else if( !strncmp( psz_parser, "audio=", strlen( "audio=" ) ) )
            {
gbazin's avatar
   
gbazin committed
596
597
                p_sys->i_audio = strtol( psz_parser + strlen( "audio=" ),
                                         &psz_parser, 0 );
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
            }
            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
638
639
                        p_sys->i_height = strtol( psz_parser + 1,
                                                  &psz_parser, 0 );
640
                    }
641
                    msg_Dbg( p_demux, "WxH %dx%d", p_sys->i_width,
gbazin's avatar
   
gbazin committed
642
                             p_sys->i_height );
643
644
                }
            }
645
646
647
648
649
650
651
652
653
654
655
656
            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=" ) ) )
            {
657
                p_sys->i_hue = strtol( psz_parser + strlen( "hue=" ),
658
659
660
661
662
663
664
                                       &psz_parser, 0 );
            }
            else if( !strncmp( psz_parser, "contrast=", strlen( "contrast=" ) ) )
            {
                p_sys->i_contrast = strtol( psz_parser + strlen( "contrast=" ),
                                            &psz_parser, 0 );
            }
665
666
            else if( !strncmp( psz_parser, "tuner=", strlen( "tuner=" ) ) )
            {
gbazin's avatar
   
gbazin committed
667
668
                p_sys->i_tuner = strtol( psz_parser + strlen( "tuner=" ),
                                         &psz_parser, 0 );
669
            }
670
671
672
673
            else if( !strncmp( psz_parser, "mjpeg", strlen( "mjpeg" ) ) )
            {
                psz_parser += strlen( "mjpeg" );

674
                p_sys->b_mjpeg = true;
675
            }
Sam Hocevar's avatar
Sam Hocevar committed
676
            else if( !strncmp( psz_parser, "decimation=",
677
678
                        strlen( "decimation=" ) ) )
            {
Sam Hocevar's avatar
Sam Hocevar committed
679
                p_sys->i_decimation =
680
681
682
683
684
685
686
687
688
689
                    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 );
            }
690
691
692
693
694
            else if( !strncmp( psz_parser, "fps=", strlen( "fps=" ) ) )
            {
                p_sys->f_fps = strtof( psz_parser + strlen( "fps=" ),
                                       &psz_parser );
            }
dionoea's avatar
dionoea committed
695
696
697
698
699
700
701
702
703
704
705
            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 );
            }
706
707
            else
            {
708
                msg_Warn( p_demux, "unknown option" );
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
            }

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

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

    if( *psz_dup )
gbazin's avatar
   
gbazin committed
724
        p_sys->psz_device = strdup( psz_dup );
dionoea's avatar
dionoea committed
725
726
    else
        p_sys->psz_device = strdup( V4L_DEFAULT );
727
    free( psz_dup );
gbazin's avatar
   
gbazin committed
728
}
729

gbazin's avatar
   
gbazin committed
730
731
732
/*****************************************************************************
 * OpenVideoDev:
 *****************************************************************************/
733
static int OpenVideoDev( demux_t *p_demux, char *psz_device )
gbazin's avatar
   
gbazin committed
734
{
735
    demux_sys_t *p_sys = p_demux->p_sys;
gbazin's avatar
   
gbazin committed
736
    int i_fd;
737

gbazin's avatar
   
gbazin committed
738
739
740
    struct video_channel vid_channel;
    struct mjpeg_params mjpeg;
    int i;
741

gbazin's avatar
   
gbazin committed
742
    if( ( i_fd = open( psz_device, O_RDWR ) ) < 0 )
743
    {
744
        msg_Err( p_demux, "cannot open device (%m)" );
gbazin's avatar
   
gbazin committed
745
        goto vdev_failed;
746
747
    }

gbazin's avatar
   
gbazin committed
748
    if( ioctl( i_fd, VIDIOCGCAP, &p_sys->vid_cap ) < 0 )
749
    {
750
        msg_Err( p_demux, "cannot get capabilities (%m)" );
gbazin's avatar
   
gbazin committed
751
        goto vdev_failed;
752
753
    }

754
    msg_Dbg( p_demux,
755
756
757
758
759
760
761
762
763
             "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 )
    {
764
        msg_Dbg( p_demux, "invalid channel, falling back on channel 0" );
765
766
        p_sys->i_channel = 0;
    }
767
    if( p_sys->vid_cap.audios && p_sys->i_audio >= p_sys->vid_cap.audios )
768
    {
769
        msg_Dbg( p_demux, "invalid audio, falling back with no audio" );
770
771
772
773
774
775
        p_sys->i_audio = -1;
    }

    if( p_sys->i_width < p_sys->vid_cap.minwidth ||
        p_sys->i_width > p_sys->vid_cap.maxwidth )
    {
776
        msg_Dbg( p_demux, "invalid width %i", p_sys->i_width );
777
778
779
780
781
        p_sys->i_width = 0;
    }
    if( p_sys->i_height < p_sys->vid_cap.minheight ||
        p_sys->i_height > p_sys->vid_cap.maxheight )
    {
782
        msg_Dbg( p_demux, "invalid height %i", p_sys->i_height );
783
784
785
        p_sys->i_height = 0;
    }

Sam Hocevar's avatar
Sam Hocevar committed
786
    if( !( p_sys->vid_cap.type & VID_TYPE_CAPTURE ) )
787
    {
788
        msg_Err( p_demux, "cannot grab" );
gbazin's avatar
   
gbazin committed
789
        goto vdev_failed;
790
791
792
    }

    vid_channel.channel = p_sys->i_channel;
gbazin's avatar
   
gbazin committed
793
    if( ioctl( i_fd, VIDIOCGCHAN, &vid_channel ) < 0 )
794
    {
795
        msg_Err( p_demux, "cannot get channel infos (%m)" );
gbazin's avatar
   
gbazin committed
796
        goto vdev_failed;
797
    }
798
    msg_Dbg( p_demux,
799
             "setting channel %s(%d) %d tuners flags=0x%x type=0x%x norm=0x%x",
800
801
             vid_channel.name, vid_channel.channel, vid_channel.tuners,
             vid_channel.flags, vid_channel.type, vid_channel.norm );
802
803
804

    if( p_sys->i_tuner >= vid_channel.tuners )
    {
805
        msg_Dbg( p_demux, "invalid tuner, falling back on tuner 0" );
806
807
808
809
        p_sys->i_tuner = 0;
    }

    vid_channel.norm = p_sys->i_norm;
gbazin's avatar
   
gbazin committed
810
    if( ioctl( i_fd, VIDIOCSCHAN, &vid_channel ) < 0 )
811
    {
812
        msg_Err( p_demux, "cannot set channel (%m)" );
gbazin's avatar
   
gbazin committed
813
        goto vdev_failed;
814
815
816
817
818
819
820
821
822
823
824
    }

    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
825
            if( ioctl( i_fd, VIDIOCGTUNER, &vid_tuner ) < 0 )
826
            {
827
                msg_Err( p_demux, "cannot get tuner (%m)" );
gbazin's avatar
   
gbazin committed
828
                goto vdev_failed;
829
            }
830
            msg_Dbg( p_demux, "tuner %s low=%d high=%d, flags=0x%x "
gbazin's avatar
   
gbazin committed
831
832
833
834
                     "mode=0x%x signal=0x%x",
                     vid_tuner.name, vid_tuner.rangelow, vid_tuner.rangehigh,
                     vid_tuner.flags, vid_tuner.mode, vid_tuner.signal );

835
            msg_Dbg( p_demux, "setting tuner %s (%d)",
836
837
                     vid_tuner.name, vid_tuner.tuner );

gbazin's avatar
   
gbazin committed
838
839
840
            /* FIXME FIXME to be checked FIXME FIXME */
            //vid_tuner.mode = p_sys->i_norm;
            if( ioctl( i_fd, VIDIOCSTUNER, &vid_tuner ) < 0 )
841
            {
842
                msg_Err( p_demux, "cannot set tuner (%m)" );
gbazin's avatar
   
gbazin committed
843
                goto vdev_failed;
844
845
846
            }
        }
#endif
847

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

851

852
853
854
        /* set frequency */
        if( p_sys->i_frequency >= 0 )
        {
855
            int driver_frequency = p_sys->i_frequency * 16 /1000;
gbazin's avatar
   
gbazin committed
856
            if( ioctl( i_fd, VIDIOCSFREQ, &driver_frequency ) < 0 )
857
            {
858
                msg_Err( p_demux, "cannot set frequency (%m)" );
gbazin's avatar
   
gbazin committed
859
                goto vdev_failed;
860
            }
861
            msg_Dbg( p_demux, "frequency %d (%d)", p_sys->i_frequency,
Sam Hocevar's avatar
Sam Hocevar committed
862
                                                   driver_frequency );
863
864
865
866
867
868
869
870
871
872
873
874
        }
    }

    /* 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
875
            if( ioctl( i_fd, VIDIOCGAUDIO, &vid_audio ) < 0 )
876
            {
877
                msg_Err( p_demux, "cannot get audio (%m)" );
gbazin's avatar
   
gbazin committed
878
                goto vdev_failed;
879
880
881
882
883
            }

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

gbazin's avatar
   
gbazin committed
884
            if( ioctl( i_fd, VIDIOCSAUDIO, &vid_audio ) < 0 )
885
            {
886
                msg_Err( p_demux, "cannot set audio (%m)" );
gbazin's avatar
   
gbazin committed
887
                goto vdev_failed;
888
            }
889
        }
890

891
    }
892

893
894
895
896
897
898
899
    /* 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
900
        if( ioctl( i_fd, MJPIOC_G_PARAMS, &mjpeg ) < 0 )
901
        {
902
            msg_Err( p_demux, "cannot get mjpeg params (%m)" );
gbazin's avatar
   
gbazin committed
903
            goto vdev_failed;
904
        }
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
        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
950
        if( ioctl( i_fd, MJPIOC_S_PARAMS, &mjpeg ) < 0 )
951
        {
952
            msg_Err( p_demux, "cannot set mjpeg params (%m)" );
gbazin's avatar
   
gbazin committed
953
            goto vdev_failed;
954
955
956
957
958
        }

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

961
962
    /* fix width/height */
    if( !p_sys->b_mjpeg && ( p_sys->i_width == 0 || p_sys->i_height == 0 ) )
963
964
965
    {
        struct video_window vid_win;

gbazin's avatar
   
gbazin committed
966
        if( ioctl( i_fd, VIDIOCGWIN, &vid_win ) < 0 )
967
        {
968
            msg_Err( p_demux, "cannot get win (%m)" );
gbazin's avatar
   
gbazin committed
969
            goto vdev_failed;
970
971
972
973
        }
        p_sys->i_width  = vid_win.width;
        p_sys->i_height = vid_win.height;

974
975
976
977
978
979
980
981
982
983
984
985
986
        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;
        }

987
        msg_Dbg( p_demux, "will use %dx%d", p_sys->i_width, p_sys->i_height );
988
989
    }

gbazin's avatar
   
gbazin committed
990
    if( !p_sys->b_mjpeg )
991
    {
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
        /* 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 )
            {
1015
1016
1017
1018
1019
1020
1021
                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 );
1022
1023
1024
1025
                p_sys->vid_picture = vid_picture;
            }
        }

1026
        /* Find out video format used by device */
gbazin's avatar
   
gbazin committed
1027
        if( ioctl( i_fd, VIDIOCGPICT, &p_sys->vid_picture ) == 0 )
gbazin's avatar
   
gbazin committed
1028
        {
1029
            struct video_picture vid_picture = p_sys->vid_picture;
1030
            char *psz;
gbazin's avatar
   
gbazin committed
1031
            int i;
1032

gbazin's avatar
   
gbazin committed
1033
1034
            p_sys->i_fourcc = 0;

1035
            psz = var_CreateGetString( p_demux, "v4l-chroma" );
1036
            if( strlen( psz ) >= 4 )
gbazin's avatar
   
gbazin committed
1037
            {
1038
                vid_picture.palette = 0;
1039
                int i_chroma = VLC_FOURCC( psz[0], psz[1], psz[2], psz[3] );
gbazin's avatar
   
gbazin committed
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050

                /* 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;
                    }
                }
            }
1051
            free( psz );
gbazin's avatar
   
gbazin committed
1052
1053
1054

            if( vid_picture.palette &&
                !ioctl( i_fd, VIDIOCSPICT, &vid_picture ) )
gbazin's avatar
   
gbazin committed
1055
1056
1057
            {
                p_sys->vid_picture = vid_picture;
            }
1058
1059
            else
            {
gbazin's avatar
   
gbazin committed
1060
1061
                /* Try to set the format to something easy to encode */
                vid_picture.palette = VIDEO_PALETTE_YUV420P;
gbazin's avatar
   
gbazin committed
1062
                if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 )
1063
1064
1065
                {
                    p_sys->vid_picture = vid_picture;
                }
gbazin's avatar
   
gbazin committed
1066
1067
1068
1069
1070
1071
1072
1073
                else
                {
                    vid_picture.palette = VIDEO_PALETTE_YUV422P;
                    if( ioctl( i_fd, VIDIOCSPICT, &vid_picture ) == 0 )
                    {
                        p_sys->vid_picture = vid_picture;
                    }
                }
1074
1075
1076
            }

            /* Find out final format */
gbazin's avatar
   
gbazin committed
1077
            for( i = 0; v4lchroma_to_fourcc[i].i_v4l != 0; i++ )
1078
            {
gbazin's avatar
   
gbazin committed
1079
1080
1081
1082
1083
                if( v4lchroma_to_fourcc[i].i_v4l == p_sys->vid_picture.palette)
                {
                    p_sys->i_fourcc = v4lchroma_to_fourcc[i].i_fourcc;
                    break;
                }