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

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
#include <string.h>
31
#include <math.h>
32
33
34
35

#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
gbazin's avatar
   
gbazin committed
36
37
#include <vlc/vout.h>
#include <vlc/decoder.h>
38
39
#include <vlc_filter.h>
#include <vlc_osd.h>
40

41
42
#define MASTER_SYNC_MAX_DRIFT 100000

43
/*****************************************************************************
44
 * Module descriptor
45
 *****************************************************************************/
gbazin's avatar
gbazin committed
46
47
#define VENC_TEXT N_("Video encoder")
#define VENC_LONGTEXT N_( \
48
49
    "This is the video encoder module that will be used (and its associated "\
    "options).")
50
51
#define VCODEC_TEXT N_("Destination video codec")
#define VCODEC_LONGTEXT N_( \
52
    "This is the video codec that will be used.")
53
54
#define VB_TEXT N_("Video bitrate")
#define VB_LONGTEXT N_( \
55
    "Target bitrate of the transcoded video stream." )
56
57
#define SCALE_TEXT N_("Video scaling")
#define SCALE_LONGTEXT N_( \
58
    "Scale factor to apply to the video while transcoding (eg: 0.25)")
59
60
#define FPS_TEXT N_("Video frame-rate")
#define FPS_LONGTEXT N_( \
61
    "Target output frame rate for the video stream." )
62
63
#define DEINTERLACE_TEXT N_("Deinterlace video")
#define DEINTERLACE_LONGTEXT N_( \
64
    "Deinterlace the video before encoding." )
65
66
#define DEINTERLACE_MODULE_TEXT N_("Deinterlace module")
#define DEINTERLACE_MODULE_LONGTEXT N_( \
67
    "Specify the deinterlace module to use." )
68
69
#define WIDTH_TEXT N_("Video width")
#define WIDTH_LONGTEXT N_( \
70
    "Output video width." )
71
72
#define HEIGHT_TEXT N_("Video height")
#define HEIGHT_LONGTEXT N_( \
73
    "Output video height." )
74
75
#define MAXWIDTH_TEXT N_("Maximum video width")
#define MAXWIDTH_LONGTEXT N_( \
76
    "Maximum output video width." )
77
78
#define MAXHEIGHT_TEXT N_("Maximum video height")
#define MAXHEIGHT_LONGTEXT N_( \
79
    "Maximum output video height." )
80
81
#define VFILTER_TEXT N_("Video filter")
#define VFILTER_LONGTEXT N_( \
82
83
    "Video filters will be applied to the video streams (after overlays " \
    "are applied). You must enter a comma-separated list of filters." )
84

85
#define CROPTOP_TEXT N_("Video crop (top)")
86
#define CROPTOP_LONGTEXT N_( \
87
88
    "Number of pixels to crop at the top of the video." )
#define CROPLEFT_TEXT N_("Video crop (left)")
89
#define CROPLEFT_LONGTEXT N_( \
90
91
    "Number of pixels to crop at the left of the video." )
#define CROPBOTTOM_TEXT N_("Video crop (bottom)")
92
#define CROPBOTTOM_LONGTEXT N_( \
93
94
    "Number of pixels to crop at the bottom of the video." )
#define CROPRIGHT_TEXT N_("Video crop (right)")
95
#define CROPRIGHT_LONGTEXT N_( \
96
    "Number of pixels to crop at the right of the video." )
97

98
#define PADDTOP_TEXT N_("Video padding (top)")
99
#define PADDTOP_LONGTEXT N_( \
100
101
    "Size of the black border to add at the top of the video." )
#define PADDLEFT_TEXT N_("Video padding (left)")
102
#define PADDLEFT_LONGTEXT N_( \
103
104
    "Size of the black border to add at the left of the video." )
#define PADDBOTTOM_TEXT N_("Video padding (bottom)")
105
#define PADDBOTTOM_LONGTEXT N_( \
106
107
    "Size of the black border to add at the bottom of the video." )
#define PADDRIGHT_TEXT N_("Video padding (right)")
108
#define PADDRIGHT_LONGTEXT N_( \
109
    "Size of the black border to add at the right of the video." )
110
111
112

#define CANVAS_WIDTH_TEXT N_("Video canvas width")
#define CANVAS_WIDTH_LONGTEXT N_( \
113
    "This will automatically crod and pad the video to a specified width." )
114
115
#define CANVAS_HEIGHT_TEXT N_("Video canvas height")
#define CANVAS_HEIGHT_LONGTEXT N_( \
116
    "This will automatically crod and pad the video to a specified height." )
117
118
#define CANVAS_ASPECT_TEXT N_("Video canvas aspect ratio")
#define CANVAS_ASPECT_LONGTEXT N_( \
119
120
    "This sets aspect (like 4:3) of the video canvas and letterbox the video "\
    "accordingly." )
121

gbazin's avatar
gbazin committed
122
123
#define AENC_TEXT N_("Audio encoder")
#define AENC_LONGTEXT N_( \
124
125
    "This is the audio encoder module that will be used (and its associated "\
    "options).")
126
127
#define ACODEC_TEXT N_("Destination audio codec")
#define ACODEC_LONGTEXT N_( \
128
    "This is the audio codec that will be used.")
129
130
#define AB_TEXT N_("Audio bitrate")
#define AB_LONGTEXT N_( \
131
    "Target bitrate of the transcoded audio stream." )
132
133
#define ARATE_TEXT N_("Audio sample rate")
#define ARATE_LONGTEXT N_( \
134
 "Sample rate of the transcoded audio stream (11250, 22500, 44100 or 48000).")
135
136
#define ACHANS_TEXT N_("Audio channels")
#define ACHANS_LONGTEXT N_( \
137
    "Number of audio channels in the transcoded streams." )
138
139
140
141
#define AFILTER_TEXT N_("Audio filter")
#define AFILTER_LONGTEXT N_( \
    "Audio filters will be applied to the audio streams (after conversion " \
    "filters are applied). You must enter a comma-separated list of filters." )
142

143
144
#define SENC_TEXT N_("Subtitles encoder")
#define SENC_LONGTEXT N_( \
145
146
    "This is the subtitles encoder module that will be used (and its " \
    "associated options)." )
147
148
#define SCODEC_TEXT N_("Destination subtitles codec")
#define SCODEC_LONGTEXT N_( \
149
150
151
    "This is the subtitles coded that will be used." )

#define SFILTER_TEXT N_("Overlays")
152
#define SFILTER_LONGTEXT N_( \
153
154
155
156
    "This allows you to add overlays (also known as \"subpictures\" on the "\
    "transcoded video stream. The subpictures produced by the filters will "\
    "be overlayed directly onto the video. You must specify a comma-separated "\
    "list of subpicture modules" )
157

158
159
#define OSD_TEXT N_("OSD menu")
#define OSD_LONGTEXT N_(\
160
    "Stream the On Screen Display menu (using the osdmenu subpicture module)." )
161

162
163
#define THREADS_TEXT N_("Number of threads")
#define THREADS_LONGTEXT N_( \
164
    "Number of threads used for the transcoding." )
165
166
167
168
#define HP_TEXT N_("High priority")
#define HP_LONGTEXT N_( \
    "Runs the optional encoder thread at the OUTPUT priority instead of " \
    "VIDEO." )
169

170
171
172
173
174
#define ASYNC_TEXT N_("Synchronise on audio track")
#define ASYNC_LONGTEXT N_( \
    "This option will drop/duplicate video frames to synchronise the video " \
    "track on the audio track." )

175
#define HURRYUP_TEXT N_( "Hurry up" )
176
177
#define HURRYUP_LONGTEXT N_( "The transcoder will drop frames if your CPU " \
                "can't keep up with the encoding rate." )
178

179
180
181
182
183
static char *ppsz_deinterlace_type[] =
{
    "deinterlace", "ffmpeg-deinterlace"
};

184
185
186
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

187
188
#define SOUT_CFG_PREFIX "sout-transcode-"

189
vlc_module_begin();
190
    set_shortname( _("Transcode"));
191
192
193
194
    set_description( _("Transcode stream output") );
    set_capability( "sout stream", 50 );
    add_shortcut( "transcode" );
    set_callbacks( Open, Close );
zorglub's avatar
zorglub committed
195
196
    set_category( CAT_SOUT );
    set_subcategory( SUBCAT_SOUT_STREAM );
197
    set_section( N_("Video"), NULL );
gbazin's avatar
gbazin committed
198
199
    add_string( SOUT_CFG_PREFIX "venc", NULL, NULL, VENC_TEXT,
                VENC_LONGTEXT, VLC_FALSE );
200
201
202
203
204
205
    add_string( SOUT_CFG_PREFIX "vcodec", NULL, NULL, VCODEC_TEXT,
                VCODEC_LONGTEXT, VLC_FALSE );
    add_integer( SOUT_CFG_PREFIX "vb", 800 * 1000, NULL, VB_TEXT,
                 VB_LONGTEXT, VLC_FALSE );
    add_float( SOUT_CFG_PREFIX "scale", 1, NULL, SCALE_TEXT,
               SCALE_LONGTEXT, VLC_FALSE );
206
207
    add_float( SOUT_CFG_PREFIX "fps", 0, NULL, FPS_TEXT,
               FPS_LONGTEXT, VLC_FALSE );
208
209
    add_bool( SOUT_CFG_PREFIX "hurry-up", VLC_TRUE, NULL, HURRYUP_TEXT,
               HURRYUP_LONGTEXT, VLC_FALSE );
210
211
    add_bool( SOUT_CFG_PREFIX "deinterlace", 0, NULL, DEINTERLACE_TEXT,
              DEINTERLACE_LONGTEXT, VLC_FALSE );
212
213
214
    add_string( SOUT_CFG_PREFIX "deinterlace-module", "deinterlace", NULL,
                DEINTERLACE_MODULE_TEXT, DEINTERLACE_MODULE_LONGTEXT,
                VLC_FALSE );
215
        change_string_list( ppsz_deinterlace_type, 0, 0 );
216
217
218
219
    add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
                 WIDTH_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
                 HEIGHT_LONGTEXT, VLC_TRUE );
220
221
222
223
    add_integer( SOUT_CFG_PREFIX "maxwidth", 0, NULL, MAXWIDTH_TEXT,
                 MAXWIDTH_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "maxheight", 0, NULL, MAXHEIGHT_TEXT,
                 MAXHEIGHT_LONGTEXT, VLC_TRUE );
224
    add_module_list_cat( SOUT_CFG_PREFIX "vfilter", SUBCAT_VIDEO_VFILTER2,
225
226
                     NULL, NULL,
                     VFILTER_TEXT, VFILTER_LONGTEXT, VLC_FALSE );
227
228
229
230
231
232
233
234
235
236

    add_integer( SOUT_CFG_PREFIX "croptop", 0, NULL, CROPTOP_TEXT,
                 CROPTOP_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "cropleft", 0, NULL, CROPLEFT_TEXT,
                 CROPLEFT_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "cropbottom", 0, NULL, CROPBOTTOM_TEXT,
                 CROPBOTTOM_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "cropright", 0, NULL, CROPRIGHT_TEXT,
                 CROPRIGHT_LONGTEXT, VLC_TRUE );

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
    add_integer( SOUT_CFG_PREFIX "paddtop", 0, NULL, PADDTOP_TEXT,
                 PADDTOP_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "paddleft", 0, NULL, PADDLEFT_TEXT,
                 PADDLEFT_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "paddbottom", 0, NULL, PADDBOTTOM_TEXT,
                 PADDBOTTOM_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "paddright", 0, NULL, PADDRIGHT_TEXT,
                 PADDRIGHT_LONGTEXT, VLC_TRUE );

    add_integer( SOUT_CFG_PREFIX "canvas-width", 0, NULL, CANVAS_WIDTH_TEXT,
                 CANVAS_WIDTH_LONGTEXT, VLC_TRUE );
    add_integer( SOUT_CFG_PREFIX "canvas-height", 0, NULL, CANVAS_HEIGHT_TEXT,
                 CANVAS_HEIGHT_LONGTEXT, VLC_TRUE );
    add_string( SOUT_CFG_PREFIX "canvas-aspect", NULL, NULL, CANVAS_ASPECT_TEXT,
                CANVAS_ASPECT_LONGTEXT, VLC_FALSE );

253
    set_section( N_("Audio"), NULL );
gbazin's avatar
gbazin committed
254
255
    add_string( SOUT_CFG_PREFIX "aenc", NULL, NULL, AENC_TEXT,
                AENC_LONGTEXT, VLC_FALSE );
256
257
258
259
260
261
262
263
    add_string( SOUT_CFG_PREFIX "acodec", NULL, NULL, ACODEC_TEXT,
                ACODEC_LONGTEXT, VLC_FALSE );
    add_integer( SOUT_CFG_PREFIX "ab", 64000, NULL, AB_TEXT,
                 AB_LONGTEXT, VLC_FALSE );
    add_integer( SOUT_CFG_PREFIX "channels", 0, NULL, ACHANS_TEXT,
                 ACHANS_LONGTEXT, VLC_FALSE );
    add_integer( SOUT_CFG_PREFIX "samplerate", 0, NULL, ARATE_TEXT,
                 ARATE_LONGTEXT, VLC_TRUE );
264
265
    add_bool( SOUT_CFG_PREFIX "audio-sync", 0, NULL, ASYNC_TEXT,
              ASYNC_LONGTEXT, VLC_FALSE );
266
267
268
    add_module_list_cat( SOUT_CFG_PREFIX "afilter", SUBCAT_AUDIO_MISC,
                     NULL, NULL,
                     AFILTER_TEXT, AFILTER_LONGTEXT, VLC_FALSE );
269

270
    set_section( N_("Overlays/Subtitles"), NULL );
271
272
273
274
275
276
    add_string( SOUT_CFG_PREFIX "senc", NULL, NULL, SENC_TEXT,
                SENC_LONGTEXT, VLC_FALSE );
    add_string( SOUT_CFG_PREFIX "scodec", NULL, NULL, SCODEC_TEXT,
                SCODEC_LONGTEXT, VLC_FALSE );
    add_bool( SOUT_CFG_PREFIX "soverlay", 0, NULL, SCODEC_TEXT,
               SCODEC_LONGTEXT, VLC_FALSE );
277
278
279
    add_module_list_cat( SOUT_CFG_PREFIX "sfilter", SUBCAT_VIDEO_SUBPIC,
                     NULL, NULL,
                     SFILTER_TEXT, SFILTER_LONGTEXT, VLC_FALSE );
280

281
282
283
    set_section( N_("On Screen Display"), NULL );
    add_bool( SOUT_CFG_PREFIX "osd", 0, NULL, OSD_TEXT,
              OSD_LONGTEXT, VLC_FALSE );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
284

285
    set_section( N_("Miscellaneous"), NULL );
286
287
    add_integer( SOUT_CFG_PREFIX "threads", 0, NULL, THREADS_TEXT,
                 THREADS_LONGTEXT, VLC_TRUE );
288
289
    add_bool( SOUT_CFG_PREFIX "high-priority", 0, NULL, HP_TEXT, HP_LONGTEXT,
              VLC_TRUE );
290

291
vlc_module_end();
292

gbazin's avatar
gbazin committed
293
294
static const char *ppsz_sout_options[] = {
    "venc", "vcodec", "vb", "croptop", "cropbottom", "cropleft", "cropright",
295
296
    "paddtop", "paddbottom", "paddleft", "paddright", 
    "canvas-width", "canvas-height", "canvas-aspect", 
297
298
    "scale", "fps", "width", "height", "vfilter", "deinterlace",
    "deinterlace-module", "threads", "hurry-up", "aenc", "acodec", "ab",
299
300
301
    "afilter", "samplerate", "channels", "senc", "scodec", "soverlay",
    "sfilter", "osd", "audio-sync", "high-priority", "maxwidth", "maxheight",
    NULL
gbazin's avatar
gbazin committed
302
303
};

304
305
306
/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
307
static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
308
static int               Del ( sout_stream_t *, sout_stream_id_t * );
309
static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );
310

311
312
313
314
315
static int  transcode_audio_new    ( sout_stream_t *, sout_stream_id_t * );
static void transcode_audio_close  ( sout_stream_t *, sout_stream_id_t * );
static int  transcode_audio_process( sout_stream_t *, sout_stream_id_t *,
                                     block_t *, block_t ** );

gbazin's avatar
gbazin committed
316
317
318
static aout_buffer_t *audio_new_buffer( decoder_t *, int );
static void audio_del_buffer( decoder_t *, aout_buffer_t * );

319
320
321
322
323
324
static int  transcode_video_new    ( sout_stream_t *, sout_stream_id_t * );
static void transcode_video_close  ( sout_stream_t *, sout_stream_id_t * );
static int  transcode_video_encoder_open( sout_stream_t *, sout_stream_id_t *);
static int  transcode_video_process( sout_stream_t *, sout_stream_id_t *,
                                     block_t *, block_t ** );

325
326
327
328
329
330
331
static void video_del_buffer( vlc_object_t *, picture_t * );
static picture_t *video_new_buffer_decoder( decoder_t * );
static void video_del_buffer_decoder( decoder_t *, picture_t * );
static void video_link_picture_decoder( decoder_t *, picture_t * );
static void video_unlink_picture_decoder( decoder_t *, picture_t * );
static picture_t *video_new_buffer_filter( filter_t * );
static void video_del_buffer_filter( filter_t *, picture_t * );
gbazin's avatar
   
gbazin committed
332

333
334
335
336
337
static int  transcode_spu_new    ( sout_stream_t *, sout_stream_id_t * );
static void transcode_spu_close  ( sout_stream_t *, sout_stream_id_t * );
static int  transcode_spu_process( sout_stream_t *, sout_stream_id_t *,
                                   block_t *, block_t ** );

338
339
340
341
static int  transcode_osd_new    ( sout_stream_t *, sout_stream_id_t * );
static void transcode_osd_close  ( sout_stream_t *, sout_stream_id_t * );
static int  transcode_osd_process( sout_stream_t *, sout_stream_id_t *,
                                   block_t *, block_t ** );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
342

343
344
static int  EncoderThread( struct sout_stream_sys_t * p_sys );

gbazin's avatar
   
gbazin committed
345
346
347
348
349
350
351
352
353
354
355
static int pi_channels_maps[6] =
{
    0,
    AOUT_CHAN_CENTER,   AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
    AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
    AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
     | AOUT_CHAN_REARRIGHT,
    AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
     | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
};

356
#define PICTURE_RING_SIZE 64
357
#define SUBPICTURE_RING_SIZE 20
358
#define TRANSCODE_FILTERS 10
359

360
361
struct sout_stream_sys_t
{
362
363
    VLC_COMMON_MEMBERS

364
365
366
    sout_stream_t   *p_out;
    sout_stream_id_t *id_video;
    block_t         *p_buffers;
367
368
369
370
    vlc_mutex_t     lock_out;
    vlc_cond_t      cond;
    picture_t *     pp_pics[PICTURE_RING_SIZE];
    int             i_first_pic, i_last_pic;
371

372
    /* Audio */
373
    vlc_fourcc_t    i_acodec;   /* codec audio (0 if not transcode) */
gbazin's avatar
gbazin committed
374
    char            *psz_aenc;
375
    sout_cfg_t      *p_audio_cfg;
376
377
378
    int             i_sample_rate;
    int             i_channels;
    int             i_abitrate;
379
380
381
    char            *psz_afilters[TRANSCODE_FILTERS];
    sout_cfg_t      *p_afilters_cfg[TRANSCODE_FILTERS];
    int             i_afilters;
382

383
384
    /* Video */
    vlc_fourcc_t    i_vcodec;   /* codec video (0 if not transcode) */
gbazin's avatar
gbazin committed
385
    char            *psz_venc;
386
    sout_cfg_t      *p_video_cfg;
387
    int             i_vbitrate;
388
    double          f_scale;
389
    double          f_fps;
390
391
    unsigned int    i_width, i_maxwidth;
    unsigned int    i_height, i_maxheight;
392
    vlc_bool_t      b_deinterlace;
393
394
    char            *psz_deinterlace;
    sout_cfg_t      *p_deinterlace_cfg;
395
    int             i_threads;
396
    vlc_bool_t      b_high_priority;
397
    vlc_bool_t      b_hurry_up;
398
399
    char            *psz_vfilters[TRANSCODE_FILTERS];
    sout_cfg_t      *p_vfilters_cfg[TRANSCODE_FILTERS];
400
    int             i_vfilters;
401
402
403
404
405

    int             i_crop_top;
    int             i_crop_bottom;
    int             i_crop_right;
    int             i_crop_left;
gbazin's avatar
   
gbazin committed
406

407
408
409
410
411
412
413
414
    int             i_padd_top;
    int             i_padd_bottom;
    int             i_padd_right;
    int             i_padd_left;

    int             i_canvas_width;
    int             i_canvas_height;
    int             i_canvas_aspect;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
415

416
417
418
419
420
421
422
423
424
425
426
    /* Video, calculated */
    int             i_src_x_offset;
    int             i_src_y_offset;
    int             i_crop_width;
    int             i_crop_height;

    int             i_dst_x_offset;
    int             i_dst_y_offset;
    int             i_nopadd_width;
    int             i_nopadd_height;

427
428
429
430
431
    /* SPU */
    vlc_fourcc_t    i_scodec;   /* codec spu (0 if not transcode) */
    char            *psz_senc;
    vlc_bool_t      b_soverlay;
    sout_cfg_t      *p_spu_cfg;
432
    spu_t           *p_spu;
433

434
435
436
437
438
    /* OSD Menu */
    sout_stream_id_t *id_osd;   /* extension for streaming OSD menus */
    vlc_fourcc_t    i_osdcodec; /* codec osd menu (0 if not transcode) */
    char            *psz_osdenc;
    sout_cfg_t      *p_osd_cfg;
439
440
    vlc_bool_t      b_es_osd;      /* VLC_TRUE when osd es is registered */
    vlc_bool_t      b_sout_osd;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
441

442
    /* Sync */
443
    vlc_bool_t      b_master_sync;
444
    mtime_t         i_master_drift;
445
446
};

447
448
449
struct decoder_owner_sys_t
{
    picture_t *pp_pics[PICTURE_RING_SIZE];
450
    sout_stream_sys_t *p_sys;
451
452
453
454
};
struct filter_owner_sys_t
{
    picture_t *pp_pics[PICTURE_RING_SIZE];
455
    sout_stream_sys_t *p_sys;
456
457
};

458
459
460
461
462
463
464
/*****************************************************************************
 * Open:
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    sout_stream_t     *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t *p_sys;
465
    vlc_value_t       val;
466

467
    p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) );
468

Laurent Aimar's avatar
Laurent Aimar committed
469
    p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
470
471
472
    if( !p_sys->p_out )
    {
        msg_Err( p_stream, "cannot create chain" );
473
        vlc_object_destroy( p_sys );
474
475
        return VLC_EGENERIC;
    }
476

477
    p_sys->i_master_drift = 0;
478

Laurent Aimar's avatar
Laurent Aimar committed
479
    sout_CfgParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
gbazin's avatar
gbazin committed
480
                   p_stream->p_cfg );
481

gbazin's avatar
gbazin committed
482
483
484
485
486
    /* Audio transcoding parameters */
    var_Get( p_stream, SOUT_CFG_PREFIX "aenc", &val );
    p_sys->psz_aenc = NULL;
    p_sys->p_audio_cfg = NULL;
    if( val.psz_string && *val.psz_string )
487
    {
gbazin's avatar
gbazin committed
488
        char *psz_next;
Laurent Aimar's avatar
Laurent Aimar committed
489
490
        psz_next = sout_CfgCreate( &p_sys->psz_aenc, &p_sys->p_audio_cfg,
                                   val.psz_string );
491
492
        if( psz_next ) free( psz_next );
    }
gbazin's avatar
gbazin committed
493
    if( val.psz_string ) free( val.psz_string );
494
495
496
497
498
499
500

    var_Get( p_stream, SOUT_CFG_PREFIX "acodec", &val );
    p_sys->i_acodec = 0;
    if( val.psz_string && *val.psz_string )
    {
        char fcc[4] = "    ";
        memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
501
        p_sys->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
502
503
    }
    if( val.psz_string ) free( val.psz_string );
504

505
506
507
508
509
510
    var_Get( p_stream, SOUT_CFG_PREFIX "ab", &val );
    p_sys->i_abitrate = val.i_int;
    if( p_sys->i_abitrate < 4000 ) p_sys->i_abitrate *= 1000;

    var_Get( p_stream, SOUT_CFG_PREFIX "samplerate", &val );
    p_sys->i_sample_rate = val.i_int;
511

512
513
514
515
516
    var_Get( p_stream, SOUT_CFG_PREFIX "channels", &val );
    p_sys->i_channels = val.i_int;

    if( p_sys->i_acodec )
    {
517
518
        if( p_sys->i_acodec == VLC_FOURCC('m','p','3',0) &&
            p_sys->i_channels > 2 )
519
520
521
522
        {
            msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
                      p_sys->i_channels );
            p_sys->i_channels = 2;
523
        }
524
525
526
        msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s",
                 (char *)&p_sys->i_acodec, p_sys->i_sample_rate,
                 p_sys->i_channels, p_sys->i_abitrate / 1000 );
527
528
    }

529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
    var_Get( p_stream, SOUT_CFG_PREFIX "afilter", &val );
    p_sys->i_afilters = 0;
    if( val.psz_string && *val.psz_string )
    {
        char *psz_parser = val.psz_string;

        while( (psz_parser != NULL) && (*psz_parser != '\0')
                && (p_sys->i_afilters < TRANSCODE_FILTERS) )
        {
            psz_parser = sout_CfgCreate(
                                   &p_sys->psz_afilters[p_sys->i_afilters],
                                   &p_sys->p_afilters_cfg[p_sys->i_afilters],
                                   psz_parser );
            p_sys->i_afilters++;
            if( (psz_parser != NULL) && (*psz_parser != '\0') ) psz_parser++;
        }
    }
    if( val.psz_string ) free( val.psz_string );
    if( p_sys->i_afilters < TRANSCODE_FILTERS-1 )
    {
        p_sys->psz_afilters[p_sys->i_afilters] = NULL;
        p_sys->p_afilters_cfg[p_sys->i_afilters] = NULL;
    }

553
    /* Video transcoding parameters */
gbazin's avatar
gbazin committed
554
555
556
557
558
559
    var_Get( p_stream, SOUT_CFG_PREFIX "venc", &val );
    p_sys->psz_venc = NULL;
    p_sys->p_video_cfg = NULL;
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
Laurent Aimar's avatar
Laurent Aimar committed
560
561
        psz_next = sout_CfgCreate( &p_sys->psz_venc, &p_sys->p_video_cfg,
                                   val.psz_string );
gbazin's avatar
gbazin committed
562
563
564
565
        if( psz_next ) free( psz_next );
    }
    if( val.psz_string ) free( val.psz_string );

566
567
568
    var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val );
    p_sys->i_vcodec = 0;
    if( val.psz_string && *val.psz_string )
569
570
    {
        char fcc[4] = "    ";
571
572
573
574
        memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
        p_sys->i_vcodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
    }
    if( val.psz_string ) free( val.psz_string );
575

576
577
578
    var_Get( p_stream, SOUT_CFG_PREFIX "vb", &val );
    p_sys->i_vbitrate = val.i_int;
    if( p_sys->i_vbitrate < 16000 ) p_sys->i_vbitrate *= 1000;
579

580
581
    var_Get( p_stream, SOUT_CFG_PREFIX "scale", &val );
    p_sys->f_scale = val.f_float;
582

583
584
585
    var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val );
    p_sys->f_fps = val.f_float;

586
587
588
    var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val );
    p_sys->b_hurry_up = val.b_bool;

589
590
    var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
    p_sys->i_width = val.i_int;
591

592
593
    var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
    p_sys->i_height = val.i_int;
594

595
596
597
598
599
600
    var_Get( p_stream, SOUT_CFG_PREFIX "maxwidth", &val );
    p_sys->i_maxwidth = val.i_int;

    var_Get( p_stream, SOUT_CFG_PREFIX "maxheight", &val );
    p_sys->i_maxheight = val.i_int;

601
602
603
604
605
606
    var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val );
    p_sys->i_vfilters = 0;
    if( val.psz_string && *val.psz_string )
    {
        char *psz_parser = val.psz_string;

607
608
        while( (psz_parser != NULL) && (*psz_parser != '\0')
                && (p_sys->i_vfilters < TRANSCODE_FILTERS) )
609
610
611
612
613
614
615
616
617
618
        {
            psz_parser = sout_CfgCreate(
                                   &p_sys->psz_vfilters[p_sys->i_vfilters],
                                   &p_sys->p_vfilters_cfg[p_sys->i_vfilters],
                                   psz_parser );
            p_sys->i_vfilters++;
            if( psz_parser != NULL && *psz_parser != '\0' ) psz_parser++;
        }
    }
    if( val.psz_string ) free( val.psz_string );
619
620
621
622
623
    if( p_sys->i_vfilters < TRANSCODE_FILTERS-1 )
    {
        p_sys->psz_vfilters[p_sys->i_vfilters] = NULL;
        p_sys->p_vfilters_cfg[p_sys->i_vfilters] = NULL;
    }
624

625
626
627
    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val );
    p_sys->b_deinterlace = val.b_bool;

628
629
630
631
632
633
634
635
636
637
638
639
640
    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace-module", &val );
    p_sys->psz_deinterlace = NULL;
    p_sys->p_deinterlace_cfg = NULL;
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
        psz_next = sout_CfgCreate( &p_sys->psz_deinterlace,
                                   &p_sys->p_deinterlace_cfg,
                                   val.psz_string );
        if( psz_next ) free( psz_next );
    }
    if( val.psz_string ) free( val.psz_string );

641
642
643
644
645
646
647
648
649
    var_Get( p_stream, SOUT_CFG_PREFIX "croptop", &val );
    p_sys->i_crop_top = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "cropbottom", &val );
    p_sys->i_crop_bottom = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "cropleft", &val );
    p_sys->i_crop_left = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "cropright", &val );
    p_sys->i_crop_right = val.i_int;

650
651
652
653
654
655
656
657
    var_Get( p_stream, SOUT_CFG_PREFIX "paddtop", &val );
    p_sys->i_padd_top = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "paddbottom", &val );
    p_sys->i_padd_bottom = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "paddleft", &val );
    p_sys->i_padd_left = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "paddright", &val );
    p_sys->i_padd_right = val.i_int;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
658

659
660
661
662
    var_Get( p_stream, SOUT_CFG_PREFIX "canvas-width", &val );
    p_sys->i_canvas_width = val.i_int;
    var_Get( p_stream, SOUT_CFG_PREFIX "canvas-height", &val );
    p_sys->i_canvas_height = val.i_int;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
663

664
    var_Get( p_stream, SOUT_CFG_PREFIX "canvas-aspect", &val );
665
666
    p_sys->i_canvas_aspect = 0;
    if( val.psz_string && *val.psz_string )
667
668
669
670
671
    {
        char *psz_parser = strchr( val.psz_string, ':' );
        if( psz_parser )
        {
            *psz_parser++ = '\0';
672
673
            p_sys->i_canvas_aspect = atoi( val.psz_string ) *
                VOUT_ASPECT_FACTOR / atoi( psz_parser );
674
        }
675
        else msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string );
676
677

    }
678
    if( val.psz_string ) free( val.psz_string );
679

680
681
    var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
    p_sys->i_threads = val.i_int;
682
683
    var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val );
    p_sys->b_high_priority = val.b_bool;
684
685

    if( p_sys->i_vcodec )
686
    {
687
688
689
        msg_Dbg( p_stream, "codec video=%4.4s %dx%d scaling: %f %dkb/s",
                 (char *)&p_sys->i_vcodec, p_sys->i_width, p_sys->i_height,
                 p_sys->f_scale, p_sys->i_vbitrate / 1000 );
690
    }
691

692
    /* Subpictures transcoding parameters */
693
    p_sys->p_spu = NULL;
694
695
    p_sys->psz_senc = NULL;
    p_sys->p_spu_cfg = NULL;
696
697
698
    p_sys->i_scodec = 0;

    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
        psz_next = sout_CfgCreate( &p_sys->psz_senc, &p_sys->p_spu_cfg,
                                   val.psz_string );
        if( psz_next ) free( psz_next );
    }
    if( val.psz_string ) free( val.psz_string );

    var_Get( p_stream, SOUT_CFG_PREFIX "scodec", &val );
    if( val.psz_string && *val.psz_string )
    {
        char fcc[4] = "    ";
        memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
        p_sys->i_scodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
    }
    if( val.psz_string ) free( val.psz_string );

    if( p_sys->i_scodec )
    {
719
        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_scodec );
720
721
722
723
    }

    var_Get( p_stream, SOUT_CFG_PREFIX "soverlay", &val );
    p_sys->b_soverlay = val.b_bool;
724
725
726
727
728
729
730
731
732
733

    var_Get( p_stream, SOUT_CFG_PREFIX "sfilter", &val );
    if( val.psz_string && *val.psz_string )
    {
        p_sys->p_spu = spu_Create( p_stream );
        var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
        var_Set( p_sys->p_spu, "sub-filter", val );
        spu_Init( p_sys->p_spu );
    }
    if( val.psz_string ) free( val.psz_string );
734

735
736
737
738
    /* OSD menu transcoding parameters */
    p_sys->psz_osdenc = NULL;
    p_sys->p_osd_cfg  = NULL;
    p_sys->i_osdcodec = 0;
739
740
741
742
743
    p_sys->b_es_osd   = VLC_FALSE;

    var_Get( p_stream, SOUT_CFG_PREFIX "osd", &val );
    p_sys->b_sout_osd = val.b_bool;
    if( p_sys->b_sout_osd )
744
    {
745
        vlc_value_t osd_val;
746
        char *psz_next;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
747

748
749
750
        psz_next = sout_CfgCreate( &p_sys->psz_osdenc,
                                   &p_sys->p_osd_cfg, strdup( "dvbsub") );
        if( psz_next ) free( psz_next );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
751
752

        p_sys->i_osdcodec = VLC_FOURCC('Y','U','V','P' );
753
754
755
756
757

        msg_Dbg( p_stream, "codec osd=%4.4s", (char *)&p_sys->i_osdcodec );

        if( !p_sys->p_spu )
        {
758
            osd_val.psz_string = strdup("osdmenu");
759
760
            p_sys->p_spu = spu_Create( p_stream );
            var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
761
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
762
            spu_Init( p_sys->p_spu );
763
764
765
766
767
768
769
            if( osd_val.psz_string ) free( osd_val.psz_string );
        }
        else
        {
            osd_val.psz_string = strdup("osdmenu");
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
            if( osd_val.psz_string ) free( osd_val.psz_string );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
770
        }
771
772
773
    }

    /* Audio settings */
774
    var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
775
776
    p_sys->b_master_sync = val.b_bool;
    if( p_sys->f_fps > 0 ) p_sys->b_master_sync = VLC_TRUE;
777

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
    p_stream->pf_add    = Add;
    p_stream->pf_del    = Del;
    p_stream->pf_send   = Send;
    p_stream->p_sys     = p_sys;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    sout_stream_t       *p_stream = (sout_stream_t*)p_this;
    sout_stream_sys_t   *p_sys = p_stream->p_sys;

Laurent Aimar's avatar
Laurent Aimar committed
794
    sout_StreamDelete( p_sys->p_out );
795
796
797
798
799
800
801
802
803
804
805
806
807

    while( p_sys->p_audio_cfg != NULL )
    {
        sout_cfg_t *p_next = p_sys->p_audio_cfg->p_next;

        if( p_sys->p_audio_cfg->psz_name )
            free( p_sys->p_audio_cfg->psz_name );
        if( p_sys->p_audio_cfg->psz_value )
            free( p_sys->p_audio_cfg->psz_value );
        free( p_sys->p_audio_cfg );

        p_sys->p_audio_cfg = p_next;
    }
gbazin's avatar
gbazin committed
808
    if( p_sys->psz_aenc ) free( p_sys->psz_aenc );
809
810
811
812
813
814
815
816
817
818
819
820
821

    while( p_sys->p_video_cfg != NULL )
    {
        sout_cfg_t *p_next = p_sys->p_video_cfg->p_next;

        if( p_sys->p_video_cfg->psz_name )
            free( p_sys->p_video_cfg->psz_name );
        if( p_sys->p_video_cfg->psz_value )
            free( p_sys->p_video_cfg->psz_value );
        free( p_sys->p_video_cfg );

        p_sys->p_video_cfg = p_next;
    }
gbazin's avatar
gbazin committed
822
    if( p_sys->psz_venc ) free( p_sys->psz_venc );
823

824
825
826
827
828
829
830
831
832
833
834
835
836
837
    while( p_sys->p_deinterlace_cfg != NULL )
    {
        sout_cfg_t *p_next = p_sys->p_deinterlace_cfg->p_next;

        if( p_sys->p_deinterlace_cfg->psz_name )
            free( p_sys->p_deinterlace_cfg->psz_name );
        if( p_sys->p_deinterlace_cfg->psz_value )
            free( p_sys->p_deinterlace_cfg->psz_value );
        free( p_sys->p_deinterlace_cfg );

        p_sys->p_deinterlace_cfg = p_next;
    }
    if( p_sys->psz_deinterlace ) free( p_sys->psz_deinterlace );

838
839
840
841
842
843
844
845
846
847
848
849
850
851
    while( p_sys->p_spu_cfg != NULL )
    {
        sout_cfg_t *p_next = p_sys->p_spu_cfg->p_next;

        if( p_sys->p_spu_cfg->psz_name )
            free( p_sys->p_spu_cfg->psz_name );
        if( p_sys->p_spu_cfg->psz_value )
            free( p_sys->p_spu_cfg->psz_value );
        free( p_sys->p_spu_cfg );

        p_sys->p_spu_cfg = p_next;
    }
    if( p_sys->psz_senc ) free( p_sys->psz_senc );

852
    if( p_sys->p_spu ) spu_Destroy( p_sys->p_spu );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
853

854
855
856
857
858
859
860
861
862
863
864
865
    while( p_sys->p_osd_cfg != NULL )
    {
        sout_cfg_t *p_next = p_sys->p_osd_cfg->p_next;

        if( p_sys->p_osd_cfg->psz_name )
            free( p_sys->p_osd_cfg->psz_name );
        if( p_sys->p_osd_cfg->psz_value )
            free( p_sys->p_osd_cfg->psz_value );
        free( p_sys->p_osd_cfg );

        p_sys->p_osd_cfg = p_next;
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
866
    if( p_sys->psz_osdenc ) free( p_sys->psz_osdenc );
867

868
    vlc_object_destroy( p_sys );
869
870
871
872
873
}

struct sout_stream_id_t
{
    vlc_fourcc_t  b_transcode;
874

875
876
877
    /* id of the out stream */
    void *id;

878
879
880
    /* Decoder */
    decoder_t       *p_decoder;

881
    /* Filters */
882
    filter_t        *pp_filter[TRANSCODE_FILTERS];
883
    int             i_filter;
884
885
886
    /* User specified filters */
    filter_t        *pp_ufilter[TRANSCODE_FILTERS];
    int             i_ufilter;
887

gbazin's avatar
   
gbazin committed
888
    /* Encoder */
gbazin's avatar
   
gbazin committed
889
    encoder_t       *p_encoder;
gbazin's avatar
   
gbazin committed
890

891
892
    /* Sync */
    date_t          interpolated_pts;
893
894
};

895
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
896
{
897
898
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_id_t *id;
899
900

    id = malloc( sizeof( sout_stream_id_t ) );
901
902
    memset( id, 0, sizeof(sout_stream_id_t) );

gbazin's avatar
   
gbazin committed
903
    id->id = NULL;
904
    id->p_decoder = NULL;
gbazin's avatar
   
gbazin committed
905
906
    id->p_encoder = NULL;

907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
    /* Create decoder object */
    id->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
    if( !id->p_decoder )
    {
        msg_Err( p_stream, "out of memory" );
        goto error;
    }
    vlc_object_attach( id->p_decoder, p_stream );
    id->p_decoder->p_module = NULL;
    id->p_decoder->fmt_in = *p_fmt;
    id->p_decoder->b_pace_control = VLC_TRUE;

    /* Create encoder object */
    id->p_encoder = vlc_object_create( p_stream, VLC_OBJECT_ENCODER );
    if( !id->p_encoder )
    {
        msg_Err( p_stream, "out of memory" );
        goto error;
    }
    vlc_object_attach( id->p_encoder, p_stream );
    id->p_encoder->p_module = NULL;
928
929

    /* Create destination format */
930
931
932
933
934
    es_format_Init( &id->p_encoder->fmt_out, p_fmt->i_cat, 0 );
    id->p_encoder->fmt_out.i_id    = p_fmt->i_id;
    id->p_encoder->fmt_out.i_group = p_fmt->i_group;
    if( p_fmt->psz_language )
        id->p_encoder->fmt_out.psz_language = strdup( p_fmt->psz_language );
935

936
    if( p_fmt->i_cat == AUDIO_ES && (p_sys->i_acodec || p_sys->psz_aenc) )
937
938
939
    {
        msg_Dbg( p_stream,
                 "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
940
941
942
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec );

        /* Complete destination format */
943
944
945
946
947
948
        id->p_encoder->fmt_out.i_codec = p_sys->i_acodec;
        id->p_encoder->fmt_out.audio.i_rate = p_sys->i_sample_rate > 0 ?
            p_sys->i_sample_rate : (int)p_fmt->audio.i_rate;
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate;
        id->p_encoder->fmt_out.audio.i_bitspersample =
            p_fmt->audio.i_bitspersample;
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
        id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ?
            p_sys->i_channels : p_fmt->audio.i_channels;
        /* Sanity check for audio channels */
        id->p_encoder->fmt_out.audio.i_channels =
            __MIN( id->p_encoder->fmt_out.audio.i_channels,
                   id->p_decoder->fmt_in.audio.i_channels );
        id->p_encoder->fmt_out.audio.i_original_channels =
            id->p_decoder->fmt_in.audio.i_physical_channels;
        if( id->p_decoder->fmt_in.audio.i_channels ==
            id->p_encoder->fmt_out.audio.i_channels )
        {
            id->p_encoder->fmt_out.audio.i_physical_channels =
                id->p_decoder->fmt_in.audio.i_physical_channels;
        }
        else
        {
            id->p_encoder->fmt_out.audio.i_physical_channels =
                pi_channels_maps[id->p_encoder->fmt_out.audio.i_channels];
        }
968
969
970

        /* Build decoder -> filter -> encoder chain */
        if( transcode_audio_new( p_stream, id ) )
971
972
        {
            msg_Err( p_stream, "cannot create audio chain" );
973
            goto error;
974
975
        }

976
977
        /* Open output stream */
        id->id = p_sys->p_out->pf_add( p_sys->p_out, &id->p_encoder->fmt_out );
978
        id->b_transcode = VLC_TRUE;
979

980
981
982
983
984
        if( !id->id )
        {
            transcode_audio_close( p_stream, id );
            goto error;
        }
985
986

        date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 );
987
    }
988
989
    else if( p_fmt->i_cat == VIDEO_ES &&
             (p_sys->i_vcodec != 0 || p_sys->psz_venc) )
990
991
992
    {
        msg_Dbg( p_stream,
                 "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
993
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_vcodec );
994

995
        /* Complete destination format */
996
        id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec;
997
998
        id->p_encoder->fmt_out.video.i_width  = p_sys->i_width & ~1;
        id->p_encoder->fmt_out.video.i_height = p_sys->i_height & ~1;
999
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate;
1000

1001
1002
        /* Build decoder -> filter -> encoder chain */
        if( transcode_video_new( p_stream, id ) )
1003
1004
        {
            msg_Err( p_stream, "cannot create video chain" );
1005
            goto error;
1006
        }
1007
1008
1009

        /* Stream will be added later on because we don't know
         * all the characteristics of the decoded stream yet */
1010
        id->b_transcode = VLC_TRUE;
1011

1012
        if( p_sys->f_fps > 0 )
1013
        {
1014
            id->p_encoder->fmt_out.video.i_frame_rate =
1015
                (p_sys->f_fps * 1001) + 0.5;
1016
            id->p_encoder->fmt_out.video.i_frame_rate_base = 1001;
1017
        }
1018
    }
1019
1020
1021
1022
1023
1024
    else if( p_fmt->i_cat == SPU_ES && (p_sys->i_scodec || p_sys->psz_senc) )
    {
        msg_Dbg( p_stream, "creating subtitles transcoding from fcc=`%4.4s' "
                 "to fcc=`%4.4s'", (char*)&p_fmt->i_codec,
                 (char*)&p_sys->i_scodec );

1025
        /* Complete destination format */
1026
        id->p_encoder->fmt_out.i_codec = p_sys->i_scodec;
1027
1028
1029
1030
1031

        /* build decoder -> filter -> encoder */
        if( transcode_spu_new( p_stream, id ) )
        {
            msg_Err( p_stream, "cannot create subtitles chain" );
1032
            goto error;
1033
1034
1035
        }

        /* open output stream */