transcode.c 79.1 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin committed
2
 * transcode.c: transcoding stream output module
3
 *****************************************************************************
4
 * Copyright (C) 2003-2008 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
 *          Antoine Cellerier <dionoea at videolan dot org>
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
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
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

34
#include <vlc_common.h>
35
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
36
37
38
39
40
#include <vlc_input.h>
#include <vlc_sout.h>
#include <vlc_aout.h>
#include <vlc_vout.h>
#include <vlc_codec.h>
41
#include <vlc_meta.h>
zorglub's avatar
zorglub committed
42
#include <vlc_block.h>
43
44
#include <vlc_filter.h>
#include <vlc_osd.h>
45

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

48
49
#define MASTER_SYNC_MAX_DRIFT 100000

50
51
#include <assert.h>

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

gbazin's avatar
gbazin committed
94
95
#define AENC_TEXT N_("Audio encoder")
#define AENC_LONGTEXT N_( \
96
97
    "This is the audio encoder module that will be used (and its associated "\
    "options).")
98
99
#define ACODEC_TEXT N_("Destination audio codec")
#define ACODEC_LONGTEXT N_( \
100
    "This is the audio codec that will be used.")
101
102
#define AB_TEXT N_("Audio bitrate")
#define AB_LONGTEXT N_( \
103
    "Target bitrate of the transcoded audio stream." )
104
105
#define ARATE_TEXT N_("Audio sample rate")
#define ARATE_LONGTEXT N_( \
106
 "Sample rate of the transcoded audio stream (11250, 22500, 44100 or 48000).")
107
108
#define ACHANS_TEXT N_("Audio channels")
#define ACHANS_LONGTEXT N_( \
109
    "Number of audio channels in the transcoded streams." )
110
111
112
113
#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." )
114

115
116
#define SENC_TEXT N_("Subtitles encoder")
#define SENC_LONGTEXT N_( \
117
118
    "This is the subtitles encoder module that will be used (and its " \
    "associated options)." )
119
120
#define SCODEC_TEXT N_("Destination subtitles codec")
#define SCODEC_LONGTEXT N_( \
zorglub's avatar
zorglub committed
121
    "This is the subtitles codec that will be used." )
122
123

#define SFILTER_TEXT N_("Overlays")
124
#define SFILTER_LONGTEXT N_( \
125
126
127
128
    "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" )
129

130
131
#define OSD_TEXT N_("OSD menu")
#define OSD_LONGTEXT N_(\
132
    "Stream the On Screen Display menu (using the osdmenu subpicture module)." )
133

134
135
#define THREADS_TEXT N_("Number of threads")
#define THREADS_LONGTEXT N_( \
136
    "Number of threads used for the transcoding." )
137
138
139
140
#define HP_TEXT N_("High priority")
#define HP_LONGTEXT N_( \
    "Runs the optional encoder thread at the OUTPUT priority instead of " \
    "VIDEO." )
141

142
143
144
145
146
#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." )

147
#define HURRYUP_TEXT N_( "Hurry up" )
148
149
#define HURRYUP_LONGTEXT N_( "The transcoder will drop frames if your CPU " \
                "can't keep up with the encoding rate." )
150

151
static const char *const ppsz_deinterlace_type[] =
152
153
154
155
{
    "deinterlace", "ffmpeg-deinterlace"
};

156
157
158
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

159
160
#define SOUT_CFG_PREFIX "sout-transcode-"

161
162
163
164
165
166
167
168
169
vlc_module_begin ()
    set_shortname( N_("Transcode"))
    set_description( N_("Transcode stream output") )
    set_capability( "sout stream", 50 )
    add_shortcut( "transcode" )
    set_callbacks( Open, Close )
    set_category( CAT_SOUT )
    set_subcategory( SUBCAT_SOUT_STREAM )
    set_section( N_("Video"), NULL )
170
    add_module( SOUT_CFG_PREFIX "venc", "encoder", NULL, NULL, VENC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
171
                VENC_LONGTEXT, false )
172
    add_string( SOUT_CFG_PREFIX "vcodec", NULL, NULL, VCODEC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
173
                VCODEC_LONGTEXT, false )
174
    add_integer( SOUT_CFG_PREFIX "vb", 0, NULL, VB_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
175
                 VB_LONGTEXT, false )
176
    add_float( SOUT_CFG_PREFIX "scale", 1, NULL, SCALE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
177
               SCALE_LONGTEXT, false )
178
    add_float( SOUT_CFG_PREFIX "fps", 0, NULL, FPS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
179
               FPS_LONGTEXT, false )
180
    add_bool( SOUT_CFG_PREFIX "hurry-up", true, NULL, HURRYUP_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
181
               HURRYUP_LONGTEXT, false )
182
    add_bool( SOUT_CFG_PREFIX "deinterlace", 0, NULL, DEINTERLACE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
183
              DEINTERLACE_LONGTEXT, false )
184
185
    add_string( SOUT_CFG_PREFIX "deinterlace-module", "deinterlace", NULL,
                DEINTERLACE_MODULE_TEXT, DEINTERLACE_MODULE_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
186
                false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
187
        change_string_list( ppsz_deinterlace_type, 0, 0 )
188
    add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
189
                 WIDTH_LONGTEXT, true )
190
    add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
191
                 HEIGHT_LONGTEXT, true )
192
    add_integer( SOUT_CFG_PREFIX "maxwidth", 0, NULL, MAXWIDTH_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
193
                 MAXWIDTH_LONGTEXT, true )
194
    add_integer( SOUT_CFG_PREFIX "maxheight", 0, NULL, MAXHEIGHT_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
195
                 MAXHEIGHT_LONGTEXT, true )
196
    add_module_list( SOUT_CFG_PREFIX "vfilter", "video filter2",
197
                     NULL, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
198
                     VFILTER_TEXT, VFILTER_LONGTEXT, false )
199

200
    set_section( N_("Audio"), NULL )
201
    add_module( SOUT_CFG_PREFIX "aenc", "encoder", NULL, NULL, AENC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
202
                AENC_LONGTEXT, false )
203
    add_string( SOUT_CFG_PREFIX "acodec", NULL, NULL, ACODEC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
204
                ACODEC_LONGTEXT, false )
205
    add_integer( SOUT_CFG_PREFIX "ab", 0, NULL, AB_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
206
                 AB_LONGTEXT, false )
207
    add_integer( SOUT_CFG_PREFIX "channels", 0, NULL, ACHANS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
208
                 ACHANS_LONGTEXT, false )
209
    add_integer( SOUT_CFG_PREFIX "samplerate", 0, NULL, ARATE_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
210
                 ARATE_LONGTEXT, true )
211
    add_bool( SOUT_CFG_PREFIX "audio-sync", 0, NULL, ASYNC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
212
              ASYNC_LONGTEXT, false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
213
    add_module_list( SOUT_CFG_PREFIX "afilter",  "audio filter2",
214
                     NULL, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
215
                     AFILTER_TEXT, AFILTER_LONGTEXT, false )
216

217
    set_section( N_("Overlays/Subtitles"), NULL )
218
    add_module( SOUT_CFG_PREFIX "senc", "encoder", NULL, NULL, SENC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
219
                SENC_LONGTEXT, false )
220
    add_string( SOUT_CFG_PREFIX "scodec", NULL, NULL, SCODEC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
221
                SCODEC_LONGTEXT, false )
222
    add_bool( SOUT_CFG_PREFIX "soverlay", 0, NULL, SCODEC_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
223
               SCODEC_LONGTEXT, false )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
224
    add_module_list( SOUT_CFG_PREFIX "sfilter", "video filter",
225
                     NULL, NULL,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
226
                     SFILTER_TEXT, SFILTER_LONGTEXT, false )
227

228
    set_section( N_("On Screen Display"), NULL )
229
    add_bool( SOUT_CFG_PREFIX "osd", 0, NULL, OSD_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
230
              OSD_LONGTEXT, false )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
231

232
    set_section( N_("Miscellaneous"), NULL )
233
    add_integer( SOUT_CFG_PREFIX "threads", 0, NULL, THREADS_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
234
                 THREADS_LONGTEXT, true )
235
    add_bool( SOUT_CFG_PREFIX "high-priority", 0, NULL, HP_TEXT, HP_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
236
              true )
237

238
vlc_module_end ()
239

240
static const char *const ppsz_sout_options[] = {
241
    "venc", "vcodec", "vb",
242
243
    "scale", "fps", "width", "height", "vfilter", "deinterlace",
    "deinterlace-module", "threads", "hurry-up", "aenc", "acodec", "ab",
244
245
246
    "afilter", "samplerate", "channels", "senc", "scodec", "soverlay",
    "sfilter", "osd", "audio-sync", "high-priority", "maxwidth", "maxheight",
    NULL
gbazin's avatar
gbazin committed
247
248
};

249
250
251
/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
252
static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * );
253
static int               Del ( sout_stream_t *, sout_stream_id_t * );
254
static int               Send( sout_stream_t *, sout_stream_id_t *, block_t* );
255

256
static int  transcode_audio_new    ( sout_stream_t *, sout_stream_id_t * );
Rafaël Carré's avatar
Rafaël Carré committed
257
static void transcode_audio_close  ( sout_stream_id_t * );
258
259
260
static int  transcode_audio_process( sout_stream_t *, sout_stream_id_t *,
                                     block_t *, block_t ** );

gbazin's avatar
gbazin committed
261
262
263
static aout_buffer_t *audio_new_buffer( decoder_t *, int );
static void audio_del_buffer( decoder_t *, aout_buffer_t * );

264
265
static int  transcode_video_new    ( sout_stream_t *, sout_stream_id_t * );
static void transcode_video_close  ( sout_stream_t *, sout_stream_id_t * );
266
static void transcode_video_encoder_init( sout_stream_t *, sout_stream_id_t *);
267
268
269
270
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 ** );

271
272
273
274
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 * );
gbazin's avatar
   
gbazin committed
275

276
static int  transcode_spu_new    ( sout_stream_t *, sout_stream_id_t * );
Rafaël Carré's avatar
Rafaël Carré committed
277
static void transcode_spu_close  ( sout_stream_id_t * );
278
279
280
static int  transcode_spu_process( sout_stream_t *, sout_stream_id_t *,
                                   block_t *, block_t ** );

281
282
283
284
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
285

286
static void* EncoderThread( vlc_object_t * p_this );
287

288
static const int pi_channels_maps[6] =
gbazin's avatar
   
gbazin committed
289
290
291
292
293
294
295
296
297
298
{
    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
};

299
#define PICTURE_RING_SIZE 64
300
#define SUBPICTURE_RING_SIZE 20
301

302
303
304
#define ENC_FRAMERATE (25 * 1000 + .5)
#define ENC_FRAMERATE_BASE 1000

305
306
struct sout_stream_sys_t
{
307
308
    VLC_COMMON_MEMBERS

309
310
311
    sout_stream_t   *p_out;
    sout_stream_id_t *id_video;
    block_t         *p_buffers;
312
313
314
315
    vlc_mutex_t     lock_out;
    vlc_cond_t      cond;
    picture_t *     pp_pics[PICTURE_RING_SIZE];
    int             i_first_pic, i_last_pic;
316

317
    /* Audio */
318
    vlc_fourcc_t    i_acodec;   /* codec audio (0 if not transcode) */
gbazin's avatar
gbazin committed
319
    char            *psz_aenc;
320
    config_chain_t  *p_audio_cfg;
321
322
    uint32_t        i_sample_rate;
    uint32_t        i_channels;
323
    int             i_abitrate;
324
325

    char            *psz_af2;
326

327
328
    /* Video */
    vlc_fourcc_t    i_vcodec;   /* codec video (0 if not transcode) */
gbazin's avatar
gbazin committed
329
    char            *psz_venc;
330
    config_chain_t  *p_video_cfg;
331
    int             i_vbitrate;
332
    double          f_scale;
333
    double          f_fps;
334
335
    unsigned int    i_width, i_maxwidth;
    unsigned int    i_height, i_maxheight;
336
    bool            b_deinterlace;
337
    char            *psz_deinterlace;
338
    config_chain_t  *p_deinterlace_cfg;
339
    int             i_threads;
340
341
342
343
    bool            b_high_priority;
    bool            b_hurry_up;

    char            *psz_vf2;
344

345
346
347
    /* SPU */
    vlc_fourcc_t    i_scodec;   /* codec spu (0 if not transcode) */
    char            *psz_senc;
348
    bool            b_soverlay;
349
    config_chain_t  *p_spu_cfg;
350
    spu_t           *p_spu;
351

352
353
354
    /* OSD Menu */
    vlc_fourcc_t    i_osdcodec; /* codec osd menu (0 if not transcode) */
    char            *psz_osdenc;
355
    config_chain_t  *p_osd_cfg;
356
    bool            b_osd;   /* true when osd es is registered */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
357

358
    /* Sync */
359
    bool            b_master_sync;
360
    mtime_t         i_master_drift;
361
362
};

363
364
struct decoder_owner_sys_t
{
365
    sout_stream_sys_t *p_sys;
366
367
};

368
369
370
371
372
373
374
/*****************************************************************************
 * 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;
375
    vlc_value_t       val;
376

377
    p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) );
378

Laurent Aimar's avatar
Laurent Aimar committed
379
    p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
380
381
382
    if( !p_sys->p_out )
    {
        msg_Err( p_stream, "cannot create chain" );
383
        vlc_object_release( p_sys );
384
385
        return VLC_EGENERIC;
    }
386

387
    p_sys->i_master_drift = 0;
388

389
    config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
gbazin's avatar
gbazin committed
390
                   p_stream->p_cfg );
391

gbazin's avatar
gbazin committed
392
393
394
395
396
    /* 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 )
397
    {
gbazin's avatar
gbazin committed
398
        char *psz_next;
399
400
        psz_next = config_ChainCreate( &p_sys->psz_aenc, &p_sys->p_audio_cfg,
                                       val.psz_string );
401
        free( psz_next );
402
    }
403
    free( val.psz_string );
404
405
406
407
408
409
410

    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 ) );
411
        p_sys->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
412
    }
413
    free( val.psz_string );
414

415
416
417
418
419
420
    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;
421

422
423
424
425
426
    var_Get( p_stream, SOUT_CFG_PREFIX "channels", &val );
    p_sys->i_channels = val.i_int;

    if( p_sys->i_acodec )
    {
427
428
        if( ( p_sys->i_acodec == VLC_CODEC_MP3 ||
              p_sys->i_acodec == VLC_CODEC_MPGA ) && p_sys->i_channels > 2 )
429
430
431
432
        {
            msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
                      p_sys->i_channels );
            p_sys->i_channels = 2;
433
        }
434
435
436
        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 );
437
438
    }

439
440
    var_Get( p_stream, SOUT_CFG_PREFIX "afilter", &val );
    if( val.psz_string && *val.psz_string )
441
442
        p_sys->psz_af2 = val.psz_string;
    else
443
    {
444
445
        free( val.psz_string );
        p_sys->psz_af2 = NULL;
446
447
    }

448
    /* Video transcoding parameters */
gbazin's avatar
gbazin committed
449
450
451
452
453
454
    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;
455
        psz_next = config_ChainCreate( &p_sys->psz_venc, &p_sys->p_video_cfg,
Laurent Aimar's avatar
Laurent Aimar committed
456
                                   val.psz_string );
457
        free( psz_next );
gbazin's avatar
gbazin committed
458
    }
459
    free( val.psz_string );
gbazin's avatar
gbazin committed
460

461
462
463
    var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val );
    p_sys->i_vcodec = 0;
    if( val.psz_string && *val.psz_string )
464
465
    {
        char fcc[4] = "    ";
466
467
468
        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] );
    }
469
    free( val.psz_string );
470

471
472
473
    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;
474

475
476
    var_Get( p_stream, SOUT_CFG_PREFIX "scale", &val );
    p_sys->f_scale = val.f_float;
477

478
479
480
    var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val );
    p_sys->f_fps = val.f_float;

481
482
483
    var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val );
    p_sys->b_hurry_up = val.b_bool;

484
485
    var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
    p_sys->i_width = val.i_int;
486

487
488
    var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
    p_sys->i_height = val.i_int;
489

490
491
492
493
494
495
    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;

496
497
    var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val );
    if( val.psz_string && *val.psz_string )
498
499
        p_sys->psz_vf2 = val.psz_string;
    else
500
    {
501
502
        free( val.psz_string );
        p_sys->psz_vf2 = NULL;
503
    }
504

505
506
507
    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val );
    p_sys->b_deinterlace = val.b_bool;

508
509
510
511
512
513
    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;
514
        psz_next = config_ChainCreate( &p_sys->psz_deinterlace,
515
516
                                   &p_sys->p_deinterlace_cfg,
                                   val.psz_string );
517
        free( psz_next );
518
    }
519
    free( val.psz_string );
520

521
522
    var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
    p_sys->i_threads = val.i_int;
523
524
    var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val );
    p_sys->b_high_priority = val.b_bool;
525
526

    if( p_sys->i_vcodec )
527
    {
528
529
530
        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 );
531
    }
532

533
    /* Subpictures transcoding parameters */
534
    p_sys->p_spu = NULL;
535
536
    p_sys->psz_senc = NULL;
    p_sys->p_spu_cfg = NULL;
537
538
539
    p_sys->i_scodec = 0;

    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
540
541
542
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
543
        psz_next = config_ChainCreate( &p_sys->psz_senc, &p_sys->p_spu_cfg,
544
                                   val.psz_string );
545
        free( psz_next );
546
    }
547
    free( val.psz_string );
548
549
550
551
552
553
554
555

    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] );
    }
556
    free( val.psz_string );
557
558
559

    if( p_sys->i_scodec )
    {
560
        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_scodec );
561
562
563
564
    }

    var_Get( p_stream, SOUT_CFG_PREFIX "soverlay", &val );
    p_sys->b_soverlay = val.b_bool;
565
566
567
568
569
570
571
572
573

    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 );
    }
574
    free( val.psz_string );
575

576
577
578
579
    /* OSD menu transcoding parameters */
    p_sys->psz_osdenc = NULL;
    p_sys->p_osd_cfg  = NULL;
    p_sys->i_osdcodec = 0;
580
    p_sys->b_osd   = false;
581
582

    var_Get( p_stream, SOUT_CFG_PREFIX "osd", &val );
583
    if( val.b_bool )
584
    {
585
        vlc_value_t osd_val;
586
        char *psz_next;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
587

588
        psz_next = config_ChainCreate( &p_sys->psz_osdenc,
589
                                   &p_sys->p_osd_cfg, strdup( "dvbsub") );
590
        free( psz_next );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
591

592
        p_sys->i_osdcodec = VLC_CODEC_YUVP;
593
594
595
596
597

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

        if( !p_sys->p_spu )
        {
598
            osd_val.psz_string = strdup("osdmenu");
599
600
            p_sys->p_spu = spu_Create( p_stream );
            var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
601
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
602
            spu_Init( p_sys->p_spu );
603
            free( osd_val.psz_string );
604
605
606
607
608
        }
        else
        {
            osd_val.psz_string = strdup("osdmenu");
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
609
            free( osd_val.psz_string );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
610
        }
611
612
613
    }

    /* Audio settings */
614
    var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
615
    p_sys->b_master_sync = val.b_bool;
616
    if( p_sys->f_fps > 0 ) p_sys->b_master_sync = true;
617

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
    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
634
    sout_StreamDelete( p_sys->p_out );
635

636
    free( p_sys->psz_af2 );
637

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
638
    config_ChainDestroy( p_sys->p_audio_cfg );
639
    free( p_sys->psz_aenc );
640

641
    free( p_sys->psz_vf2 );
642

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
643
    config_ChainDestroy( p_sys->p_video_cfg );
644
    free( p_sys->psz_venc );
645

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
646
    config_ChainDestroy( p_sys->p_deinterlace_cfg );
647
    free( p_sys->psz_deinterlace );
648

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
649
    config_ChainDestroy( p_sys->p_spu_cfg );
650
    free( p_sys->psz_senc );
651

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
654
    config_ChainDestroy( p_sys->p_osd_cfg );
655
    free( p_sys->psz_osdenc );
656

657
    vlc_object_release( p_sys );
658
659
660
661
}

struct sout_stream_id_t
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
662
    bool            b_transcode;
663

664
665
666
    /* id of the out stream */
    void *id;

667
668
669
    /* Decoder */
    decoder_t       *p_decoder;

670
    /* Filters */
671
    filter_chain_t  *p_f_chain;
672
    /* User specified filters */
673
    filter_chain_t  *p_uf_chain;
674

gbazin's avatar
   
gbazin committed
675
    /* Encoder */
gbazin's avatar
   
gbazin committed
676
    encoder_t       *p_encoder;
gbazin's avatar
   
gbazin committed
677

678
679
    /* Sync */
    date_t          interpolated_pts;
680
681
};

682
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
683
{
684
685
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_id_t *id;
686

ivoire's avatar
ivoire committed
687
    id = calloc( 1, sizeof( sout_stream_id_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
688
689
    if( !id )
        goto error;
690

gbazin's avatar
   
gbazin committed
691
    id->id = NULL;
692
    id->p_decoder = NULL;
gbazin's avatar
   
gbazin committed
693
694
    id->p_encoder = NULL;

695
696
697
698
699
700
701
    /* Create decoder object */
    id->p_decoder = vlc_object_create( p_stream, VLC_OBJECT_DECODER );
    if( !id->p_decoder )
        goto error;
    vlc_object_attach( id->p_decoder, p_stream );
    id->p_decoder->p_module = NULL;
    id->p_decoder->fmt_in = *p_fmt;
702
    id->p_decoder->b_pace_control = true;
703
704

    /* Create encoder object */
705
    id->p_encoder = sout_EncoderCreate( p_stream );
706
707
708
709
    if( !id->p_encoder )
        goto error;
    vlc_object_attach( id->p_encoder, p_stream );
    id->p_encoder->p_module = NULL;
710
711

    /* Create destination format */
712
713
714
715
716
    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 );
717

718
    if( p_fmt->i_cat == AUDIO_ES && (p_sys->i_acodec || p_sys->psz_aenc) )
719
720
721
    {
        msg_Dbg( p_stream,
                 "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
722
723
724
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec );

        /* Complete destination format */
725
726
        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 ?
727
            p_sys->i_sample_rate : p_fmt->audio.i_rate;
728
729
730
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate;
        id->p_encoder->fmt_out.audio.i_bitspersample =
            p_fmt->audio.i_bitspersample;
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
        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];
        }
750
751
752

        /* Build decoder -> filter -> encoder chain */
        if( transcode_audio_new( p_stream, id ) )
753
754
        {
            msg_Err( p_stream, "cannot create audio chain" );
755
            goto error;
756
757
        }

758
        /* Open output stream */
759
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_encoder->fmt_out );
760
        id->b_transcode = true;
761

762
763
        if( !id->id )
        {
Rafaël Carré's avatar
Rafaël Carré committed
764
            transcode_audio_close( id );
765
766
            goto error;
        }
767
768

        date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 );
769
    }
770
771
    else if( p_fmt->i_cat == VIDEO_ES &&
             (p_sys->i_vcodec != 0 || p_sys->psz_venc) )
772
773
774
    {
        msg_Dbg( p_stream,
                 "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
775
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_vcodec );
776

777
        /* Complete destination format */
778
        id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec;
779
780
        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;
781
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate;
782

783
784
        /* Build decoder -> filter -> encoder chain */
        if( transcode_video_new( p_stream, id ) )
785
786
        {
            msg_Err( p_stream, "cannot create video chain" );
787
            goto error;
788
        }
789
790
791

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

794
        if( p_sys->f_fps > 0 )
795
        {
796
            id->p_encoder->fmt_out.video.i_frame_rate =