transcode.c 83.5 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 )
gbazin's avatar
gbazin committed
170
    add_string( SOUT_CFG_PREFIX "venc", NULL, NULL, VENC_TEXT,
171
                VENC_LONGTEXT, false );
172
    add_string( SOUT_CFG_PREFIX "vcodec", NULL, NULL, VCODEC_TEXT,
173
                VCODEC_LONGTEXT, false );
174
    add_integer( SOUT_CFG_PREFIX "vb", 800 * 1000, NULL, VB_TEXT,
175
                 VB_LONGTEXT, false );
176
    add_float( SOUT_CFG_PREFIX "scale", 1, NULL, SCALE_TEXT,
177
               SCALE_LONGTEXT, false );
178
    add_float( SOUT_CFG_PREFIX "fps", 0, NULL, FPS_TEXT,
179
180
181
               FPS_LONGTEXT, false );
    add_bool( SOUT_CFG_PREFIX "hurry-up", true, NULL, HURRYUP_TEXT,
               HURRYUP_LONGTEXT, false );
182
    add_bool( SOUT_CFG_PREFIX "deinterlace", 0, NULL, DEINTERLACE_TEXT,
183
              DEINTERLACE_LONGTEXT, false );
184
185
    add_string( SOUT_CFG_PREFIX "deinterlace-module", "deinterlace", NULL,
                DEINTERLACE_MODULE_TEXT, DEINTERLACE_MODULE_LONGTEXT,
186
                false );
187
        change_string_list( ppsz_deinterlace_type, 0, 0 );
188
    add_integer( SOUT_CFG_PREFIX "width", 0, NULL, WIDTH_TEXT,
189
                 WIDTH_LONGTEXT, true );
190
    add_integer( SOUT_CFG_PREFIX "height", 0, NULL, HEIGHT_TEXT,
191
                 HEIGHT_LONGTEXT, true );
192
    add_integer( SOUT_CFG_PREFIX "maxwidth", 0, NULL, MAXWIDTH_TEXT,
193
                 MAXWIDTH_LONGTEXT, true );
194
    add_integer( SOUT_CFG_PREFIX "maxheight", 0, NULL, MAXHEIGHT_TEXT,
195
                 MAXHEIGHT_LONGTEXT, true );
196
    add_module_list( SOUT_CFG_PREFIX "vfilter", "video filter2",
197
                     NULL, NULL,
198
                     VFILTER_TEXT, VFILTER_LONGTEXT, false );
199

200
    set_section( N_("Audio"), NULL )
gbazin's avatar
gbazin committed
201
    add_string( SOUT_CFG_PREFIX "aenc", NULL, NULL, AENC_TEXT,
202
                AENC_LONGTEXT, false );
203
    add_string( SOUT_CFG_PREFIX "acodec", NULL, NULL, ACODEC_TEXT,
204
                ACODEC_LONGTEXT, false );
205
    add_integer( SOUT_CFG_PREFIX "ab", 0, NULL, AB_TEXT,
206
                 AB_LONGTEXT, false );
207
    add_integer( SOUT_CFG_PREFIX "channels", 0, NULL, ACHANS_TEXT,
208
                 ACHANS_LONGTEXT, false );
209
    add_integer( SOUT_CFG_PREFIX "samplerate", 0, NULL, ARATE_TEXT,
210
                 ARATE_LONGTEXT, true );
211
    add_bool( SOUT_CFG_PREFIX "audio-sync", 0, NULL, ASYNC_TEXT,
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,
215
                     AFILTER_TEXT, AFILTER_LONGTEXT, false );
216

217
    set_section( N_("Overlays/Subtitles"), NULL )
218
    add_string( SOUT_CFG_PREFIX "senc", NULL, NULL, SENC_TEXT,
219
                SENC_LONGTEXT, false );
220
    add_string( SOUT_CFG_PREFIX "scodec", NULL, NULL, SCODEC_TEXT,
221
                SCODEC_LONGTEXT, false );
222
    add_bool( SOUT_CFG_PREFIX "soverlay", 0, NULL, SCODEC_TEXT,
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,
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,
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,
234
                 THREADS_LONGTEXT, true );
235
    add_bool( SOUT_CFG_PREFIX "high-priority", 0, NULL, HP_TEXT, HP_LONGTEXT,
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
275
276
277
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
278

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

284
285
286
287
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
288

289
static void* EncoderThread( vlc_object_t * p_this );
290

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

302
#define PICTURE_RING_SIZE 64
303
#define SUBPICTURE_RING_SIZE 20
304

305
306
307
#define ENC_FRAMERATE (25 * 1000 + .5)
#define ENC_FRAMERATE_BASE 1000

308
309
struct sout_stream_sys_t
{
310
311
    VLC_COMMON_MEMBERS

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

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

    char            *psz_af2;
329

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

    char            *psz_vf2;
347

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

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

361
    /* Sync */
362
    bool            b_master_sync;
363
    mtime_t         i_master_drift;
364
365
};

366
367
368
struct decoder_owner_sys_t
{
    picture_t *pp_pics[PICTURE_RING_SIZE];
369
    sout_stream_sys_t *p_sys;
370
371
372
373
};
struct filter_owner_sys_t
{
    picture_t *pp_pics[PICTURE_RING_SIZE];
374
    sout_stream_sys_t *p_sys;
375
376
};

377
378
379
380
381
382
383
/*****************************************************************************
 * 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;
384
    vlc_value_t       val;
385

386
    p_sys = vlc_object_create( p_this, sizeof( sout_stream_sys_t ) );
387

Laurent Aimar's avatar
Laurent Aimar committed
388
    p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
389
390
391
    if( !p_sys->p_out )
    {
        msg_Err( p_stream, "cannot create chain" );
392
        vlc_object_release( p_sys );
393
394
        return VLC_EGENERIC;
    }
395

396
    p_sys->i_master_drift = 0;
397

398
    config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
gbazin's avatar
gbazin committed
399
                   p_stream->p_cfg );
400

gbazin's avatar
gbazin committed
401
402
403
404
405
    /* 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 )
406
    {
gbazin's avatar
gbazin committed
407
        char *psz_next;
408
409
        psz_next = config_ChainCreate( &p_sys->psz_aenc, &p_sys->p_audio_cfg,
                                       val.psz_string );
410
        free( psz_next );
411
    }
412
    free( val.psz_string );
413
414
415
416
417
418
419

    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 ) );
420
        p_sys->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
421
    }
422
    free( val.psz_string );
423

424
425
426
427
428
429
    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;
430

431
432
433
434
435
    var_Get( p_stream, SOUT_CFG_PREFIX "channels", &val );
    p_sys->i_channels = val.i_int;

    if( p_sys->i_acodec )
    {
436
437
        if( p_sys->i_acodec == VLC_FOURCC('m','p','3',0) &&
            p_sys->i_channels > 2 )
438
439
440
441
        {
            msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
                      p_sys->i_channels );
            p_sys->i_channels = 2;
442
        }
443
444
445
        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 );
446
447
    }

448
449
    var_Get( p_stream, SOUT_CFG_PREFIX "afilter", &val );
    if( val.psz_string && *val.psz_string )
450
451
        p_sys->psz_af2 = val.psz_string;
    else
452
    {
453
454
        free( val.psz_string );
        p_sys->psz_af2 = NULL;
455
456
    }

457
    /* Video transcoding parameters */
gbazin's avatar
gbazin committed
458
459
460
461
462
463
    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;
464
        psz_next = config_ChainCreate( &p_sys->psz_venc, &p_sys->p_video_cfg,
Laurent Aimar's avatar
Laurent Aimar committed
465
                                   val.psz_string );
466
        free( psz_next );
gbazin's avatar
gbazin committed
467
    }
468
    free( val.psz_string );
gbazin's avatar
gbazin committed
469

470
471
472
    var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val );
    p_sys->i_vcodec = 0;
    if( val.psz_string && *val.psz_string )
473
474
    {
        char fcc[4] = "    ";
475
476
477
        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] );
    }
478
    free( val.psz_string );
479

480
481
482
    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;
483

484
485
    var_Get( p_stream, SOUT_CFG_PREFIX "scale", &val );
    p_sys->f_scale = val.f_float;
486

487
488
489
    var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val );
    p_sys->f_fps = val.f_float;

490
491
492
    var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val );
    p_sys->b_hurry_up = val.b_bool;

493
494
    var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
    p_sys->i_width = val.i_int;
495

496
497
    var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
    p_sys->i_height = val.i_int;
498

499
500
501
502
503
504
    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;

505
506
    var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val );
    if( val.psz_string && *val.psz_string )
507
508
        p_sys->psz_vf2 = val.psz_string;
    else
509
    {
510
511
        free( val.psz_string );
        p_sys->psz_vf2 = NULL;
512
    }
513

514
515
516
    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val );
    p_sys->b_deinterlace = val.b_bool;

517
518
519
520
521
522
    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;
523
        psz_next = config_ChainCreate( &p_sys->psz_deinterlace,
524
525
                                   &p_sys->p_deinterlace_cfg,
                                   val.psz_string );
526
        free( psz_next );
527
    }
528
    free( val.psz_string );
529

530
531
    var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
    p_sys->i_threads = val.i_int;
532
533
    var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val );
    p_sys->b_high_priority = val.b_bool;
534
535

    if( p_sys->i_vcodec )
536
    {
537
538
539
        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 );
540
    }
541

542
    /* Subpictures transcoding parameters */
543
    p_sys->p_spu = NULL;
544
545
    p_sys->psz_senc = NULL;
    p_sys->p_spu_cfg = NULL;
546
547
548
    p_sys->i_scodec = 0;

    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
549
550
551
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
552
        psz_next = config_ChainCreate( &p_sys->psz_senc, &p_sys->p_spu_cfg,
553
                                   val.psz_string );
554
        free( psz_next );
555
    }
556
    free( val.psz_string );
557
558
559
560
561
562
563
564

    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] );
    }
565
    free( val.psz_string );
566
567
568

    if( p_sys->i_scodec )
    {
569
        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_scodec );
570
571
572
573
    }

    var_Get( p_stream, SOUT_CFG_PREFIX "soverlay", &val );
    p_sys->b_soverlay = val.b_bool;
574
575
576
577
578
579
580
581
582

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

585
586
587
588
    /* OSD menu transcoding parameters */
    p_sys->psz_osdenc = NULL;
    p_sys->p_osd_cfg  = NULL;
    p_sys->i_osdcodec = 0;
589
    p_sys->b_osd   = false;
590
591

    var_Get( p_stream, SOUT_CFG_PREFIX "osd", &val );
592
    if( val.b_bool )
593
    {
594
        vlc_value_t osd_val;
595
        char *psz_next;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
596

597
        psz_next = config_ChainCreate( &p_sys->psz_osdenc,
598
                                   &p_sys->p_osd_cfg, strdup( "dvbsub") );
599
        free( psz_next );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
600
601

        p_sys->i_osdcodec = VLC_FOURCC('Y','U','V','P' );
602
603
604
605
606

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

        if( !p_sys->p_spu )
        {
607
            osd_val.psz_string = strdup("osdmenu");
608
609
            p_sys->p_spu = spu_Create( p_stream );
            var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
610
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
611
            spu_Init( p_sys->p_spu );
612
            free( osd_val.psz_string );
613
614
615
616
617
        }
        else
        {
            osd_val.psz_string = strdup("osdmenu");
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
618
            free( osd_val.psz_string );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
619
        }
620
621
622
    }

    /* Audio settings */
623
    var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
624
    p_sys->b_master_sync = val.b_bool;
625
    if( p_sys->f_fps > 0 ) p_sys->b_master_sync = true;
626

627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
    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
643
    sout_StreamDelete( p_sys->p_out );
644

645
    free( p_sys->psz_af2 );
646

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
647
    config_ChainDestroy( p_sys->p_audio_cfg );
648
    free( p_sys->psz_aenc );
649

650
    free( p_sys->psz_vf2 );
651

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
652
    config_ChainDestroy( p_sys->p_video_cfg );
653
    free( p_sys->psz_venc );
654

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
655
    config_ChainDestroy( p_sys->p_deinterlace_cfg );
656
    free( p_sys->psz_deinterlace );
657

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
658
    config_ChainDestroy( p_sys->p_spu_cfg );
659
    free( p_sys->psz_senc );
660

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
663
    config_ChainDestroy( p_sys->p_osd_cfg );
664
    free( p_sys->psz_osdenc );
665

666
    vlc_object_release( p_sys );
667
668
669
670
}

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

673
674
675
    /* id of the out stream */
    void *id;

676
677
678
    /* Decoder */
    decoder_t       *p_decoder;

679
    /* Filters */
680
    filter_chain_t  *p_f_chain;
681
    /* User specified filters */
682
    filter_chain_t  *p_uf_chain;
683

gbazin's avatar
   
gbazin committed
684
    /* Encoder */
gbazin's avatar
   
gbazin committed
685
    encoder_t       *p_encoder;
gbazin's avatar
   
gbazin committed
686

687
688
    /* Sync */
    date_t          interpolated_pts;
689
690
};

691
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
692
{
693
694
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_id_t *id;
695

ivoire's avatar
ivoire committed
696
    id = calloc( 1, sizeof( sout_stream_id_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
697
698
    if( !id )
        goto error;
699

gbazin's avatar
   
gbazin committed
700
    id->id = NULL;
701
    id->p_decoder = NULL;
gbazin's avatar
   
gbazin committed
702
703
    id->p_encoder = NULL;

704
705
706
707
708
709
710
    /* 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;
711
    id->p_decoder->b_pace_control = true;
712
713

    /* Create encoder object */
714
    id->p_encoder = sout_EncoderCreate( p_stream );
715
716
717
718
    if( !id->p_encoder )
        goto error;
    vlc_object_attach( id->p_encoder, p_stream );
    id->p_encoder->p_module = NULL;
719
720

    /* Create destination format */
721
722
723
724
725
    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 );
726

727
    if( p_fmt->i_cat == AUDIO_ES && (p_sys->i_acodec || p_sys->psz_aenc) )
728
729
730
    {
        msg_Dbg( p_stream,
                 "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
731
732
733
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec );

        /* Complete destination format */
734
735
        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 ?
736
            p_sys->i_sample_rate : p_fmt->audio.i_rate;
737
738
739
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate;
        id->p_encoder->fmt_out.audio.i_bitspersample =
            p_fmt->audio.i_bitspersample;
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
        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];
        }
759
760
761

        /* Build decoder -> filter -> encoder chain */
        if( transcode_audio_new( p_stream, id ) )
762
763
        {
            msg_Err( p_stream, "cannot create audio chain" );
764
            goto error;
765
766
        }

767
        /* Open output stream */
768
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_encoder->fmt_out );
769
        id->b_transcode = true;
770

771
772
        if( !id->id )
        {
Rafaël Carré's avatar
Rafaël Carré committed
773
            transcode_audio_close( id );
774
775
            goto error;
        }
776
777

        date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 );
778
    }
779
780
    else if( p_fmt->i_cat == VIDEO_ES &&
             (p_sys->i_vcodec != 0 || p_sys->psz_venc) )
781
782
783
    {
        msg_Dbg( p_stream,
                 "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
784
                 (char*)&p_fmt->i_codec, (char*)&p_sys->i_vcodec );
785

786
        /* Complete destination format */
787
        id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec;
788
789
        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;
790
        id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate;
791

792
793
        /* Build decoder -> filter -> encoder chain */
        if( transcode_video_new( p_stream, id ) )
794
795
        {
            msg_Err( p_stream, "cannot create video chain" );
796
            goto error;
797
        }
798
799
800

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

803
        if( p_sys->f_fps > 0 )
804
        {
805
            id->p_encoder->fmt_out.video.i_frame_rate =
806
807
808
                (p_sys->f_fps * 1000) + 0.5;
            id->p_encoder->fmt_out.video.i_frame_rate_base =
                ENC_FRAMERATE_BASE;
809
        }
810
    }
811
812
    else if( ( p_fmt->i_cat == SPU_ES ) &&
             ( p_sys->i_scodec || p_sys->psz_senc ) )
813
814
815
816
817
    {
        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 );

818
        /* Complete destination format */
819
        id->p_encoder->fmt_out.i_codec = p_sys->i_scodec;
820
821
822
823
824

        /* build decoder -> filter -> encoder */
        if( transcode_spu_new( p_stream, id ) )
        {
            msg_Err( p_stream, "cannot create subtitles chain" );
825
            goto error;
826
827
828
        }

        /* open output stream */
829
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_encoder->fmt_out );
830
        id->b_transcode = true;
831

832
833
        if( !id->id )
        {
Rafaël Carré's avatar
Rafaël Carré committed
834
            transcode_spu_close( id );
835
836
            goto error;
        }
837
838
839
840
841
842
    }
    else if( p_fmt->i_cat == SPU_ES && p_sys->b_soverlay )
    {
        msg_Dbg( p_stream, "subtitles (fcc=`%4.4s') overlaying",
                 (char*)&p_fmt->i_codec );

843
        id->b_transcode = true;