transcode.c 101 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
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>
zorglub's avatar
zorglub committed
30
31
32
33
34
35
#include <vlc_input.h>
#include <vlc_sout.h>
#include <vlc_aout.h>
#include <vlc_vout.h>
#include <vlc_codec.h>
#include <vlc_block.h>
36
37
#include <vlc_filter.h>
#include <vlc_osd.h>
38

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
39
40
#include <math.h>

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_( \
zorglub's avatar
zorglub committed
149
    "This is the subtitles codec that will be used." )
150
151

#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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
179
static const char *ppsz_deinterlace_type[] =
180
181
182
183
{
    "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( SOUT_CFG_PREFIX "vfilter", "video filter2",
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
    add_string( SOUT_CFG_PREFIX "acodec", NULL, NULL, ACODEC_TEXT,
                ACODEC_LONGTEXT, VLC_FALSE );
258
    add_integer( SOUT_CFG_PREFIX "ab", 0, NULL, AB_TEXT,
259
260
261
262
263
                 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",
dionoea's avatar
dionoea committed
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
    config_chain_t  *p_audio_cfg;
376
377
378
    int             i_sample_rate;
    int             i_channels;
    int             i_abitrate;
379
    char            *psz_afilters[TRANSCODE_FILTERS];
380
    config_chain_t  *p_afilters_cfg[TRANSCODE_FILTERS];
381
    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
    config_chain_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
    char            *psz_deinterlace;
394
    config_chain_t  *p_deinterlace_cfg;
395
    int             i_threads;
396
    vlc_bool_t      b_high_priority;
397
    vlc_bool_t      b_hurry_up;
398
    char            *psz_vfilters[TRANSCODE_FILTERS];
399
    config_chain_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
    /* SPU */
    vlc_fourcc_t    i_scodec;   /* codec spu (0 if not transcode) */
    char            *psz_senc;
    vlc_bool_t      b_soverlay;
431
    config_chain_t  *p_spu_cfg;
432
    spu_t           *p_spu;
433

434
435
436
    /* OSD Menu */
    vlc_fourcc_t    i_osdcodec; /* codec osd menu (0 if not transcode) */
    char            *psz_osdenc;
437
    config_chain_t  *p_osd_cfg;
438
    vlc_bool_t      b_osd;   /* VLC_TRUE when osd es is registered */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
439

440
    /* Sync */
441
    vlc_bool_t      b_master_sync;
442
    mtime_t         i_master_drift;
443
444
};

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

456
457
458
459
460
461
462
/*****************************************************************************
 * 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;
463
    vlc_value_t       val;
464

465
    p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) );
466

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

475
    p_sys->i_master_drift = 0;
476

477
    config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
gbazin's avatar
gbazin committed
478
                   p_stream->p_cfg );
479

gbazin's avatar
gbazin committed
480
481
482
483
484
    /* 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 )
485
    {
gbazin's avatar
gbazin committed
486
        char *psz_next;
487
488
        psz_next = config_ChainCreate( &p_sys->psz_aenc, &p_sys->p_audio_cfg,
                                       val.psz_string );
489
490
        if( psz_next ) free( psz_next );
    }
gbazin's avatar
gbazin committed
491
    if( val.psz_string ) free( val.psz_string );
492
493
494
495
496
497
498

    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 ) );
499
        p_sys->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
500
501
    }
    if( val.psz_string ) free( val.psz_string );
502

503
504
505
506
507
508
    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;
509

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

    if( p_sys->i_acodec )
    {
515
516
        if( p_sys->i_acodec == VLC_FOURCC('m','p','3',0) &&
            p_sys->i_channels > 2 )
517
518
519
520
        {
            msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
                      p_sys->i_channels );
            p_sys->i_channels = 2;
521
        }
522
523
524
        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 );
525
526
    }

527
528
529
530
531
532
533
534
535
    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) )
        {
536
            psz_parser = config_ChainCreate(
537
538
539
540
541
542
543
544
545
546
547
548
549
550
                                   &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;
    }

551
    /* Video transcoding parameters */
gbazin's avatar
gbazin committed
552
553
554
555
556
557
    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;
558
        psz_next = config_ChainCreate( &p_sys->psz_venc, &p_sys->p_video_cfg,
Laurent Aimar's avatar
Laurent Aimar committed
559
                                   val.psz_string );
gbazin's avatar
gbazin committed
560
561
562
563
        if( psz_next ) free( psz_next );
    }
    if( val.psz_string ) free( val.psz_string );

564
565
566
    var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val );
    p_sys->i_vcodec = 0;
    if( val.psz_string && *val.psz_string )
567
568
    {
        char fcc[4] = "    ";
569
570
571
572
        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 );
573

574
575
576
    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;
577

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

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

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

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

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

593
594
595
596
597
598
    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;

599
600
601
602
603
604
    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;

605
606
        while( (psz_parser != NULL) && (*psz_parser != '\0')
                && (p_sys->i_vfilters < TRANSCODE_FILTERS) )
607
        {
608
            psz_parser = config_ChainCreate(
609
610
611
612
613
614
615
616
                                   &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 );
617
618
619
620
621
    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;
    }
622

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

626
627
628
629
630
631
    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;
632
        psz_next = config_ChainCreate( &p_sys->psz_deinterlace,
633
634
635
636
637
638
                                   &p_sys->p_deinterlace_cfg,
                                   val.psz_string );
        if( psz_next ) free( psz_next );
    }
    if( val.psz_string ) free( val.psz_string );

639
640
641
642
643
644
645
646
647
    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;

648
649
650
651
652
653
654
655
    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
656

657
658
659
660
    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
661

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

    }
676
    if( val.psz_string ) free( val.psz_string );
677

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

    if( p_sys->i_vcodec )
684
    {
685
686
687
        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 );
688
    }
689

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

    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
697
698
699
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
700
        psz_next = config_ChainCreate( &p_sys->psz_senc, &p_sys->p_spu_cfg,
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
                                   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 )
    {
717
        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_scodec );
718
719
720
721
    }

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

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

733
734
735
736
    /* OSD menu transcoding parameters */
    p_sys->psz_osdenc = NULL;
    p_sys->p_osd_cfg  = NULL;
    p_sys->i_osdcodec = 0;
737
    p_sys->b_osd   = VLC_FALSE;
738
739

    var_Get( p_stream, SOUT_CFG_PREFIX "osd", &val );
740
    if( val.b_bool )
741
    {
742
        vlc_value_t osd_val;
743
        char *psz_next;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
744

745
        psz_next = config_ChainCreate( &p_sys->psz_osdenc,
746
747
                                   &p_sys->p_osd_cfg, strdup( "dvbsub") );
        if( psz_next ) free( psz_next );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
748
749

        p_sys->i_osdcodec = VLC_FOURCC('Y','U','V','P' );
750
751
752
753
754

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

        if( !p_sys->p_spu )
        {
755
            osd_val.psz_string = strdup("osdmenu");
756
757
            p_sys->p_spu = spu_Create( p_stream );
            var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
758
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
759
            spu_Init( p_sys->p_spu );
760
761
762
763
764
765
766
            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
767
        }
768
769
770
    }

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

775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
    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
791
    sout_StreamDelete( p_sys->p_out );
792

793
794
795
796
797
798
799
800
801
    while( p_sys->i_afilters )
    {
        p_sys->i_afilters--;
        if( p_sys->psz_afilters[p_sys->i_afilters] )
            free( p_sys->psz_afilters[p_sys->i_afilters] );
        if( p_sys->p_afilters_cfg[p_sys->i_afilters] )
            free( p_sys->p_afilters_cfg[p_sys->i_afilters] );
    }

802
803
    while( p_sys->p_audio_cfg != NULL )
    {
804
        config_chain_t *p_next = p_sys->p_audio_cfg->p_next;
805
806
807
808
809
810
811
812
813

        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
814
    if( p_sys->psz_aenc ) free( p_sys->psz_aenc );
815

816
817
818
819
820
821
822
823
824
    while( p_sys->i_vfilters )
    {
        p_sys->i_vfilters--;
        if( p_sys->psz_vfilters[p_sys->i_vfilters] )
            free( p_sys->psz_vfilters[p_sys->i_vfilters] );
        if( p_sys->p_vfilters_cfg[p_sys->i_vfilters] )
            free( p_sys->p_vfilters_cfg[p_sys->i_vfilters] );
    }

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

        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
837
    if( p_sys->psz_venc ) free( p_sys->psz_venc );
838

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

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

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

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

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

869
870
    while( p_sys->p_osd_cfg != NULL )
    {
871
        config_chain_t *p_next = p_sys->p_osd_cfg->p_next;
872
873
874
875
876
877
878
879
880

        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
881
    if( p_sys->psz_osdenc ) free( p_sys->psz_osdenc );
882

883
    vlc_object_destroy( p_sys );
884
885
886
887
888
}

struct sout_stream_id_t
{
    vlc_fourcc_t  b_transcode;
889

890
891
892
    /* id of the out stream */
    void *id;

893
894
895
    /* Decoder */
    decoder_t       *p_decoder;

896
    /* Filters */
897
    filter_t        *pp_filter[TRANSCODE_FILTERS];
898
    int             i_filter;
899
900
901
    /* User specified filters */
    filter_t        *pp_ufilter[TRANSCODE_FILTERS];
    int             i_ufilter;
902

gbazin's avatar
   
gbazin committed
903
    /* Encoder */
gbazin's avatar
   
gbazin committed
904
    encoder_t       *p_encoder;
gbazin's avatar
   
gbazin committed
905

906
907
    /* Sync */
    date_t          interpolated_pts;
908
909
};

910
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
911
{
912
913
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_id_t *id;
914
915

    id = malloc( sizeof( sout_stream_id_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
916
917
918
919
920
    if( !id )
    {
        msg_Err( p_stream, "out of memory" );
        goto error;
    }
921
922
    memset( id, 0, sizeof(sout_stream_id_t) );

gbazin's avatar
   
gbazin committed
923
    id->id = NULL;
924
    id->p_decoder = NULL;
gbazin's avatar
   
gbazin committed
925
926
    id->p_encoder = NULL;

927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
    /* 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;
948
949

    /* Create destination format */
950
951
952
953
954
    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 );
955

956
    if( p_fmt->i_cat == AUDIO_ES && (p_sys->i_acodec || p_sys->psz_aenc) )
957
958
959
    {
        msg_Dbg( p_stream,
                 "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
960
961
962
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec );

        /* Complete destination format */
963
964
965
966
967
968
        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;
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
        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];
        }
988
989
990

        /* Build decoder -> filter -> encoder chain */
        if( transcode_audio_new( p_stream, id ) )
991
992
        {
            msg_Err( p_stream, "cannot create audio chain" );
993
            goto error;
994
995
        }

996
997
        /* Open output stream */
        id->id = p_sys->p_out->pf_add( p_sys->p_out, &id->p_encoder->fmt_out );
998
        id->b_transcode = VLC_TRUE;
999

1000
1001
1002
1003
1004
        if( !id->id )
        {
            transcode_audio_close( p_stream, id );
            goto error;
        }
1005
1006

        date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 );
1007
    }
1008
1009
    else if( p_fmt->i_cat == VIDEO_ES &&
             (p_sys->i_vcodec != 0 || p_sys->psz_venc) )
1010
1011
1012
    {
        msg_Dbg( p_stream,
                 "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
1013
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_vcodec );
1014

1015
        /* Complete destination format */
1016
        id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec;
1017
1018
        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;
1019
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate;
1020

1021
1022
        /* Build decoder -> filter -> encoder chain */
        if( transcode_video_new( p_stream, id ) )
1023
1024
        {
            msg_Err( p_stream, "cannot create video chain" );
1025
            goto error;
1026
        }