transcode.c 88 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
2
 * transcode.c: transcoding stream output module
3
 *****************************************************************************
4
 * Copyright (C) 2003-2009 the VideoLAN team
5
 * $Id$
6 7
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Gildas Bazin <gbazin@videolan.org>
9
 *          Jean-Paul Saman <jean-paul.saman 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
Antoine Cellerier's avatar
Antoine Cellerier 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>
Clément Stenac's avatar
Clément Stenac 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>
Clément Stenac's avatar
Clément Stenac committed
42
#include <vlc_block.h>
43 44
#include <vlc_filter.h>
#include <vlc_osd.h>
45

46 47
#include <math.h>

48 49
#define MASTER_SYNC_MAX_DRIFT 100000

50 51
#include <assert.h>

52
/*****************************************************************************
53
 * Module descriptor
54
 *****************************************************************************/
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

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_( \
Clément Stenac's avatar
Clément Stenac 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", 800 * 1000, 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
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 * );
257
static void transcode_audio_close  ( sout_stream_t *, sout_stream_id_t * );
258 259 260
static int  transcode_audio_process( sout_stream_t *, sout_stream_id_t *,
                                     block_t *, block_t ** );

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 * );
Gildas Bazin's avatar
 
Gildas Bazin 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 290 291
static void* VideoEncoderThread( vlc_object_t * p_this );

static void* AudioThread( vlc_object_t * p_this );
292

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

304
#define PICTURE_RING_SIZE 64
305
#define SUBPICTURE_RING_SIZE 20
306

307 308 309
#define ENC_FRAMERATE (25 * 1000 + .5)
#define ENC_FRAMERATE_BASE 1000

310 311 312 313
typedef struct sout_stream_video_t sout_stream_video_t;
typedef struct sout_stream_audio_t sout_stream_audio_t;

struct sout_stream_video_t
314
{
315 316
    VLC_COMMON_MEMBERS

317 318 319 320 321 322 323 324
    sout_stream_id_t *id;
#if 0
    picture_fifo_t  *pic_empty;
    picture_fifo_t  *pic_fifo;

    block_fifo_t    *fifo_in;
    block_fifo_t    *fifo_out;
#endif
325
    block_t         *p_buffers;
326 327 328 329
    vlc_mutex_t     lock_out;
    vlc_cond_t      cond;
    picture_t *     pp_pics[PICTURE_RING_SIZE];
    int             i_first_pic, i_last_pic;
330

331
    vlc_fourcc_t    i_vcodec;   /* codec video (0 if not transcode) */
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

340
    bool            b_deinterlace;
341
    char            *psz_deinterlace;
342
    config_chain_t  *p_deinterlace_cfg;
343

344
    int             i_threads;
345 346

    char            *psz_vf2;
347

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    sout_stream_sys_t *p_sys;
};

struct sout_stream_audio_t
{
    VLC_COMMON_MEMBERS

    sout_stream_id_t *id;

    block_fifo_t    *fifo_in;
    block_fifo_t    *fifo_out;

    vlc_fourcc_t    i_acodec;   /* codec audio (0 if not transcode) */
    char            *psz_aenc;
    config_chain_t  *p_audio_cfg;
    uint32_t        i_sample_rate;
    uint32_t        i_channels;
    int             i_abitrate;

    char            *psz_af2;

    sout_stream_sys_t *p_sys;
};

struct sout_stream_sys_t
{
    sout_stream_t   *p_out;

    /* Video */
    sout_stream_video_t *p_video;

    /* Audio */
    sout_stream_audio_t *p_audio;

382 383 384
    /* SPU */
    vlc_fourcc_t    i_scodec;   /* codec spu (0 if not transcode) */
    char            *psz_senc;
385
    bool            b_soverlay;
386
    config_chain_t  *p_spu_cfg;
387
    spu_t           *p_spu;
388

389 390 391
    /* OSD Menu */
    vlc_fourcc_t    i_osdcodec; /* codec osd menu (0 if not transcode) */
    char            *psz_osdenc;
392
    config_chain_t  *p_osd_cfg;
393
    bool            b_osd;   /* true when osd es is registered */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
394

395 396 397 398
    /* Misc. */
    bool            b_high_priority;
    bool            b_hurry_up;

399
    /* Sync */
400
    bool            b_master_sync;
401
    mtime_t         i_master_drift;
402 403
};

404 405 406
struct decoder_owner_sys_t
{
    picture_t *pp_pics[PICTURE_RING_SIZE];
407
    sout_stream_sys_t *p_sys;
408
};
409

410 411 412
struct filter_owner_sys_t
{
    picture_t *pp_pics[PICTURE_RING_SIZE];
413
    sout_stream_sys_t *p_sys;
414 415
};

416 417 418 419 420 421 422
/*****************************************************************************
 * 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;
423 424
    sout_stream_audio_t  *p_audio;
    sout_stream_video_t  *p_video;
425
    vlc_value_t       val;
426

427 428
    p_sys = (sout_stream_sys_t *)calloc( 1, sizeof( sout_stream_sys_t ) );
    if( !p_sys ) return VLC_ENOMEM;
429

Laurent Aimar's avatar
Laurent Aimar committed
430
    p_sys->p_out = sout_StreamNew( p_stream->p_sout, p_stream->psz_next );
431 432 433
    if( !p_sys->p_out )
    {
        msg_Err( p_stream, "cannot create chain" );
434
        free( p_sys );
435 436
        return VLC_EGENERIC;
    }
437

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
    p_sys->p_audio = p_audio = vlc_object_create( p_this, sizeof( sout_stream_audio_t ) );
    if( !p_audio )
    {
        free( p_sys );
        return VLC_ENOMEM;
    }

    p_sys->p_video = p_video = vlc_object_create( p_this, sizeof( sout_stream_video_t ) );
    if( !p_video )
    {
        vlc_object_release( p_audio );
        free( p_sys );
        return VLC_ENOMEM;
    }

    p_video->p_sys = p_sys;
    p_audio->p_sys = p_sys;

456
    p_sys->i_master_drift = 0;
457

458
    config_ChainParse( p_stream, SOUT_CFG_PREFIX, ppsz_sout_options,
459
                   p_stream->p_cfg );
460

461 462
    /* Audio transcoding parameters */
    var_Get( p_stream, SOUT_CFG_PREFIX "aenc", &val );
463 464
    p_audio->psz_aenc = NULL;
    p_audio->p_audio_cfg = NULL;
465
    if( val.psz_string && *val.psz_string )
466
    {
467
        char *psz_next;
468
        psz_next = config_ChainCreate( &p_audio->psz_aenc, &p_audio->p_audio_cfg,
469
                                       val.psz_string );
470
        free( psz_next );
471
    }
472
    free( val.psz_string );
473 474

    var_Get( p_stream, SOUT_CFG_PREFIX "acodec", &val );
475
    p_audio->i_acodec = 0;
476 477 478 479
    if( val.psz_string && *val.psz_string )
    {
        char fcc[4] = "    ";
        memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
480
        p_audio->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
481
    }
482
    free( val.psz_string );
483

484
    var_Get( p_stream, SOUT_CFG_PREFIX "ab", &val );
485 486 487
    p_audio->i_abitrate = val.i_int;
    if( p_audio->i_abitrate < 4000 )
        p_audio->i_abitrate *= 1000;
488 489

    var_Get( p_stream, SOUT_CFG_PREFIX "samplerate", &val );
490
    p_audio->i_sample_rate = val.i_int;
491

492
    var_Get( p_stream, SOUT_CFG_PREFIX "channels", &val );
493
    p_audio->i_channels = val.i_int;
494

495
    if( p_audio->i_acodec )
496
    {
497 498
        if( p_audio->i_acodec == VLC_FOURCC('m','p','3',0) &&
            p_audio->i_channels > 2 )
499 500
        {
            msg_Warn( p_stream, "%d channels invalid for mp3, forcing to 2",
501 502
                      p_audio->i_channels );
            p_audio->i_channels = 2;
503
        }
504
        msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s",
505 506
                 (char *)&p_audio->i_acodec, p_audio->i_sample_rate,
                 p_audio->i_channels, p_audio->i_abitrate / 1000 );
507 508
    }

509 510
    var_Get( p_stream, SOUT_CFG_PREFIX "afilter", &val );
    if( val.psz_string && *val.psz_string )
511
        p_audio->psz_af2 = val.psz_string;
512
    else
513
    {
514
        free( val.psz_string );
515
        p_audio->psz_af2 = NULL;
516 517
    }

518
    /* Video transcoding parameters */
519
    var_Get( p_stream, SOUT_CFG_PREFIX "venc", &val );
520 521
    p_video->psz_venc = NULL;
    p_video->p_video_cfg = NULL;
522 523 524
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
525 526
        psz_next = config_ChainCreate( &p_video->psz_venc,
                            &p_video->p_video_cfg, val.psz_string );
527
        free( psz_next );
528
    }
529
    free( val.psz_string );
530

531
    var_Get( p_stream, SOUT_CFG_PREFIX "vcodec", &val );
532
    p_video->i_vcodec = 0;
533
    if( val.psz_string && *val.psz_string )
534 535
    {
        char fcc[4] = "    ";
536
        memcpy( fcc, val.psz_string, __MIN( strlen( val.psz_string ), 4 ) );
537
        p_video->i_vcodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] );
538
    }
539
    free( val.psz_string );
540

541
    var_Get( p_stream, SOUT_CFG_PREFIX "vb", &val );
542 543 544
    p_video->i_vbitrate = val.i_int;
    if( p_video->i_vbitrate < 16000 )
        p_video->i_vbitrate *= 1000;
545

546
    var_Get( p_stream, SOUT_CFG_PREFIX "scale", &val );
547
    p_video->f_scale = val.f_float;
548

549
    var_Get( p_stream, SOUT_CFG_PREFIX "fps", &val );
550
    p_video->f_fps = val.f_float;
551

552 553 554
    var_Get( p_stream, SOUT_CFG_PREFIX "hurry-up", &val );
    p_sys->b_hurry_up = val.b_bool;

555
    var_Get( p_stream, SOUT_CFG_PREFIX "width", &val );
556
    p_video->i_width = val.i_int;
557

558
    var_Get( p_stream, SOUT_CFG_PREFIX "height", &val );
559
    p_video->i_height = val.i_int;
560

561
    var_Get( p_stream, SOUT_CFG_PREFIX "maxwidth", &val );
562
    p_video->i_maxwidth = val.i_int;
563 564

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

567 568
    var_Get( p_stream, SOUT_CFG_PREFIX "vfilter", &val );
    if( val.psz_string && *val.psz_string )
569
        p_video->psz_vf2 = val.psz_string;
570
    else
571
    {
572
        free( val.psz_string );
573
        p_video->psz_vf2 = NULL;
574
    }
575

576
    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace", &val );
577
    p_video->b_deinterlace = val.b_bool;
578

579
    var_Get( p_stream, SOUT_CFG_PREFIX "deinterlace-module", &val );
580 581
    p_video->psz_deinterlace = NULL;
    p_video->p_deinterlace_cfg = NULL;
582 583 584
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
585 586
        psz_next = config_ChainCreate( &p_video->psz_deinterlace,
                                   &p_video->p_deinterlace_cfg,
587
                                   val.psz_string );
588
        free( psz_next );
589
    }
590
    free( val.psz_string );
591

592
    var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
593
    p_video->i_threads = val.i_int;
594 595
    var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val );
    p_sys->b_high_priority = val.b_bool;
596

597
    if( p_video->i_vcodec )
598
    {
599
        msg_Dbg( p_stream, "codec video=%4.4s %dx%d scaling: %f %dkb/s",
600 601
                 (char *)&p_video->i_vcodec, p_video->i_width, p_video->i_height,
                 p_video->f_scale, p_video->i_vbitrate / 1000 );
602
    }
603

604
    /* Subpictures transcoding parameters */
605
    p_sys->p_spu = NULL;
606 607
    p_sys->psz_senc = NULL;
    p_sys->p_spu_cfg = NULL;
608 609 610
    p_sys->i_scodec = 0;

    var_Get( p_stream, SOUT_CFG_PREFIX "senc", &val );
611 612 613
    if( val.psz_string && *val.psz_string )
    {
        char *psz_next;
614
        psz_next = config_ChainCreate( &p_sys->psz_senc, &p_sys->p_spu_cfg,
615
                                   val.psz_string );
616
        free( psz_next );
617
    }
618
    free( val.psz_string );
619 620 621 622 623 624 625 626

    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] );
    }
627
    free( val.psz_string );
628 629 630

    if( p_sys->i_scodec )
    {
631
        msg_Dbg( p_stream, "codec spu=%4.4s", (char *)&p_sys->i_scodec );
632 633 634 635
    }

    var_Get( p_stream, SOUT_CFG_PREFIX "soverlay", &val );
    p_sys->b_soverlay = val.b_bool;
636 637 638 639 640 641 642 643 644

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

647 648 649 650
    /* OSD menu transcoding parameters */
    p_sys->psz_osdenc = NULL;
    p_sys->p_osd_cfg  = NULL;
    p_sys->i_osdcodec = 0;
651
    p_sys->b_osd   = false;
652 653

    var_Get( p_stream, SOUT_CFG_PREFIX "osd", &val );
654
    if( val.b_bool )
655
    {
656
        vlc_value_t osd_val;
657
        char *psz_next;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
658

659
        psz_next = config_ChainCreate( &p_sys->psz_osdenc,
660
                                   &p_sys->p_osd_cfg, strdup( "dvbsub") );
661
        free( psz_next );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
662 663

        p_sys->i_osdcodec = VLC_FOURCC('Y','U','V','P' );
664 665 666 667 668

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

        if( !p_sys->p_spu )
        {
669
            osd_val.psz_string = strdup("osdmenu");
670 671
            p_sys->p_spu = spu_Create( p_stream );
            var_Create( p_sys->p_spu, "sub-filter", VLC_VAR_STRING );
672
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
673
            spu_Init( p_sys->p_spu );
674
            free( osd_val.psz_string );
675 676 677 678 679
        }
        else
        {
            osd_val.psz_string = strdup("osdmenu");
            var_Set( p_sys->p_spu, "sub-filter", osd_val );
680
            free( osd_val.psz_string );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
681
        }
682 683 684
    }

    /* Audio settings */
685
    var_Get( p_stream, SOUT_CFG_PREFIX "audio-sync", &val );
686
    p_sys->b_master_sync = val.b_bool;
687
    if( p_video->f_fps > 0 ) p_sys->b_master_sync = true;
688

689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
    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;
704 705
    sout_stream_audio_t *p_audio = p_sys->p_audio;
    sout_stream_video_t *p_video = p_sys->p_video;
706

Laurent Aimar's avatar
Laurent Aimar committed
707
    sout_StreamDelete( p_sys->p_out );
708

709
    free( p_audio->psz_af2 );
710

711 712
    config_ChainDestroy( p_audio->p_audio_cfg );
    free( p_audio->psz_aenc );
713

714
    free( p_video->psz_vf2 );
715

716 717
    config_ChainDestroy( p_video->p_video_cfg );
    free( p_video->psz_venc );
718

719 720
    config_ChainDestroy( p_video->p_deinterlace_cfg );
    free( p_video->psz_deinterlace );
721

722
    config_ChainDestroy( p_sys->p_spu_cfg );
723
    free( p_sys->psz_senc );
724

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

727
    config_ChainDestroy( p_sys->p_osd_cfg );
728
    free( p_sys->psz_osdenc );
729

730 731 732
    vlc_object_release( p_audio );
    vlc_object_release( p_video );
    free( p_sys );
733 734 735 736
}

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

739 740 741
    /* id of the out stream */
    void *id;

742 743 744
    /* Decoder */
    decoder_t       *p_decoder;

745
    /* Filters */
746
    filter_chain_t  *p_f_chain;
747
    /* User specified filters */
748
    filter_chain_t  *p_uf_chain;
749

Gildas Bazin's avatar
 
Gildas Bazin committed
750
    /* Encoder */
Gildas Bazin's avatar
 
Gildas Bazin committed
751
    encoder_t       *p_encoder;
Gildas Bazin's avatar
 
Gildas Bazin committed
752

753 754
    /* Sync */
    date_t          interpolated_pts;
755 756
};

757
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
758
{
759
    sout_stream_sys_t *p_sys = p_stream->p_sys;
760 761
    sout_stream_audio_t  *p_audio = p_sys->p_audio;
    sout_stream_video_t  *p_video = p_sys->p_video;
762
    sout_stream_id_t *id;
763

764
    id = calloc( 1, sizeof( sout_stream_id_t ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
765 766
    if( !id )
        goto error;
767

Gildas Bazin's avatar
 
Gildas Bazin committed
768
    id->id = NULL;
769
    id->p_decoder = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
770 771
    id->p_encoder = NULL;

772 773 774 775 776 777 778
    /* 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;
779
    id->p_decoder->b_pace_control = true;
780 781

    /* Create encoder object */
782
    id->p_encoder = sout_EncoderCreate( p_stream );
783 784 785 786
    if( !id->p_encoder )
        goto error;
    vlc_object_attach( id->p_encoder, p_stream );
    id->p_encoder->p_module = NULL;
787 788

    /* Create destination format */
789 790 791 792 793
    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 );
794

795 796
    if( p_fmt->i_cat == AUDIO_ES &&
        (p_audio->i_acodec || p_audio->psz_aenc) )
797 798 799
    {
        msg_Dbg( p_stream,
                 "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
800
                 (char*)&p_fmt->i_codec, (char*)&p_audio->i_acodec );
801 802

        /* Complete destination format */
803 804 805 806
        id->p_encoder->fmt_out.i_codec = p_audio->i_acodec;
        id->p_encoder->fmt_out.audio.i_rate = p_audio->i_sample_rate > 0 ?
            p_audio->i_sample_rate : p_fmt->audio.i_rate;
        id->p_encoder->fmt_out.i_bitrate = p_audio->i_abitrate;
807 808
        id->p_encoder->fmt_out.audio.i_bitspersample =
            p_fmt->audio.i_bitspersample;
809 810
        id->p_encoder->fmt_out.audio.i_channels = p_audio->i_channels > 0 ?
            p_audio->i_channels : p_fmt->audio.i_channels;
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
        /* 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];
        }
828 829 830

        /* Build decoder -> filter -> encoder chain */
        if( transcode_audio_new( p_stream, id ) )
831 832
        {
            msg_Err( p_stream, "cannot create audio chain" );
833
            goto error;
834 835
        }

836
        /* Open output stream */
837
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_encoder->fmt_out );
838
        id->b_transcode = true;
839

840 841
        if( !id->id )
        {
842
            transcode_audio_close( p_stream, id );
843 844
            goto error;
        }
845 846

        date_Init( &id->interpolated_pts, p_fmt->audio.i_rate, 1 );
847
    }
848
    else if( p_fmt->i_cat == VIDEO_ES &&
849
             (p_video->i_vcodec != 0 || p_video->psz_venc) )
850 851 852
    {
        msg_Dbg( p_stream,
                 "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'",
853
                 (char*)&p_fmt->i_codec, (char*)&p_video->i_vcodec );
854

855
        /* Complete destination format */
856 857 858 859
        id->p_encoder->fmt_out.i_codec = p_video->i_vcodec;
        id->p_encoder->fmt_out.video.i_width  = p_video->i_width & ~1;
        id->p_encoder->fmt_out.video.i_height = p_video->i_height & ~1;
        id->p_encoder->fmt_out.i_bitrate = p_video->i_vbitrate;
860

861 862
        /* Build decoder -> filter -> encoder chain */
        if( transcode_video_new( p_stream, id ) )
863 864
        {
            msg_Err( p_stream, "cannot create video chain" );
865
            goto error;
866
        }
867 868 869

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

872
        if( p_video->f_fps > 0 )
873
        {
874
            id->p_encoder->fmt_out.video.i_frame_rate =
875
                (p_video->f_fps * 1000) + 0.5;
876 877
            id->p_encoder->fmt_out.video.i_frame_rate_base =
                ENC_FRAMERATE_BASE;
878
        }
879
    }
880 881
    else if( ( p_fmt->i_cat == SPU_ES ) &&
             ( p_sys->i_scodec || p_sys->psz_senc ) )
882 883 884 885 886
    {
        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 );

887
        /* Complete destination format */
888
        id->p_encoder->fmt_out.i_codec = p_sys->i_scodec;
889 890 891 892 893

        /* build decoder -> filter -> encoder */
        if( transcode_spu_new( p_stream, id ) )
        {
            msg_Err( p_stream, "cannot create subtitles chain" );
894
            goto error;
895 896 897
        }

        /* open output stream */
898
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_encoder->fmt_out );
899
        id->b_transcode = true;
900

901 902
        if( !id->id )
        {
Rafaël Carré's avatar
Rafaël Carré committed
903
            transcode_spu_close( id );
904 905
            goto error;
        }
906 907 908 909 910 911
    }
    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 );

912
        id->b_transcode = true;
913

914
        /* Build decoder -> filter -> overlaying chain */
915 916 917
        if( transcode_spu_new( p_stream, id ) )
        {
            msg_Err( p_stream, "cannot create subtitles chain" );
918
            goto error;
919 920
        }
    }
921 922 923 924 925 926
    else if( !p_sys->b_osd && (p_sys->i_osdcodec != 0 || p_sys->psz_osdenc) )
    {
        msg_Dbg( p_stream, "creating osd transcoding from fcc=`%4.4s' "
                 "to fcc=`%4.4s'", (char*)&p_fmt->i_codec,
                 (char*)&p_sys->i_scodec );

927
        id->b_transcode = true;
928 929 930 931 932 933 934

        /* Create a fake OSD menu elementary stream */
        if( transcode_osd_new( p_stream, id ) )
        {
            msg_Err( p_stream, "cannot create osd chain" );
            goto error;
        }
935
        p_sys->b_osd = true;
936
    }
937 938
    else
    {
939 940
        msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')",
                 (char*)&p_fmt->i_codec );
941
        id->id = sout_StreamIdAdd( p_sys->p_out, p_fmt );
942
        id->b_transcode = false;
943

944
        if( !id->id ) goto error;
945 946 947
    }

    return id;
948

949 950
error:
    if( id )
951
    {
952 953 954 955 956 957
        if( id->p_decoder )
        {
            vlc_object_detach( id->p_decoder );
            vlc_object_release( id->p_decoder );
            id->p_decoder = NULL;
        }
958

959 960 961 962 963 964 965
        if( id->p_encoder )
        {
            vlc_object_detach( id->p_encoder );
            es_format_Clean( &id->p_encoder->fmt_out );
            vlc_object_release( id->p_encoder );
            id->p_encoder = NULL;
        }
966

967 968
        free( id );
    }
969
    return NULL;
970 971
}

972
static int Del( sout_stream_t *p_stream, sout_stream_id_t *id )
973
{
974
    sout_stream_sys_t *p_sys = p_stream->p_sys;
975 976 977

    if( id->b_transcode )
    {
978
        switch( id->p_decoder->fmt_in.i_cat )
979
        {
980
        case AUDIO_ES:
981
            transcode_audio_close( p_stream, id );
982 983 984 985 986
            break;
        case VIDEO_ES:
            transcode_video_close( p_stream, id );
            break;
        case SPU_ES:
987 988 989
            if( p_sys->b_osd )
                transcode_osd_close( p_stream, id );
            else
Rafaël Carré's avatar
Rafaël Carré committed
990
                transcode_spu_close( id );
991
            break;
992
        }
993 994
    }

995
    if( id->id ) sout_StreamIdDel( p_sys->p_out, id->id );
996 997 998 999

    if( id->p_decoder )
    {
        vlc_object_detach( id->p_decoder );
1000
        vlc_object_release( id->p_decoder );
1001
        id->p_decoder = NULL;
1002 1003 1004 1005 1006
    }

    if( id->p_encoder )
    {
        vlc_object_detach( id->p_encoder );
1007
        es_format_Clean( &id->p_encoder->fmt_out );
1008
        vlc_object_release( id->p_encoder );
1009
        id->p_encoder = NULL;
1010
    }
1011 1012 1013 1014 1015
    free( id );

    return VLC_SUCCESS;
}

Gildas Bazin's avatar
 
Gildas Bazin committed
1016
static int Send( sout_stream_t *p_stream, sout_stream_id_t *id,
1017
                 block_t *p_buffer )
1018
{
1019
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1020
    block_t *p_out = NULL;
1021

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1022
    if( !id->b_transcode )
1023
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1024 1025 1026
        if( id->id )
            return sout_StreamIdSend( p_sys->p_out, id->id, p_buffer );

1027 1028 1029 1030 1031
        block_Release( p_buffer );
        return VLC_EGENERIC;
    }

    switch( id->p_decoder->fmt_in.i_cat )
1032
    {
1033 1034 1035
    case AUDIO_ES:
        transcode_audio_process( p_stream, id, p_buffer, &p_out );
        break;
1036

1037 1038 1039
    case VIDEO_ES:
        if( transcode_video_process( p_stream, id, p_buffer, &p_out )
            != VLC_SUCCESS )
1040 1041 1042
        {
            return VLC_EGENERIC;
        }
1043
        break;
1044

1045
    case SPU_ES:
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
        /* Transcode OSD menu pictures. */
        if( p_sys->b_osd )
        {
            if( transcode_osd_process( p_stream, id, p_buffer, &p_out ) !=
                VLC_SUCCESS )
            {
                return VLC_EGENERIC;
            }
        }
        else if ( transcode_spu_process( p_stream, id, p_buffer, &p_out ) !=
1056
            VLC_SUCCESS )
1057
        {
1058
            return VLC_EGENERIC;
1059
        }
1060
        break;
1061

1062
    default:
1063
        p_out = NULL;
1064
        block_Release( p_buffer );
1065
        break;
1066
    }
1067

1068 1069
    if( p_out )
        return sout_StreamIdSend( p_sys->p_out, id->id, p_out );
1070
    return VLC_SUCCESS;
1071 1072
}

1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
/****************************************************************************
 * decoder helper
 ****************************************************************************/
static inline void video_timer_start( encoder_t * p_encoder )
{
    stats_TimerStart( p_encoder, "encoding video frame",
                      STATS_TIMER_VIDEO_FRAME_ENCODING );
}

static inline void video_timer_stop( encoder_t * p_encoder )
{
    stats_TimerStop( p_encoder, STATS_TIMER_VIDEO_FRAME_ENCODING );
}

static inline void video_timer_close( encoder_t * p_encoder )
{
    stats_TimerDump(  p_encoder, STATS_TIMER_VIDEO_FRAME_ENCODING );
    stats_TimerClean( p_encoder, STATS_TIMER_VIDEO_FRAME_ENCODING );
}

static inline void audio_timer_start( encoder_t * p_encoder )
{
    stats_TimerStart( p_encoder, "encoding audio frame",
                      STATS_TIMER_AUDIO_FRAME_ENCODING );
}

static inline void audio_timer_stop( encoder_t * p_encoder )
{
    stats_TimerStop( p_encoder, STATS_TIMER_AUDIO_FRAME_ENCODING );
}

static inline void audio_timer_close( encoder_t * p_encoder )
{
    stats_TimerDump(  p_encoder, STATS_TIMER_AUDIO_FRAME_ENCODING );
    stats_TimerClean( p_encoder, STATS_TIMER_AUDIO_FRAME_ENCODING );
}

1110
/****************************************************************************
1111
 * decoder reencoder part
1112
 ****************************************************************************/
1113

1114
static block_t *transcode_audio_alloc( filter_t *p_filter, int size )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1115
{
1116 1117
    VLC_UNUSED( p_filter );
    return block_Alloc( size );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1118 1119
}

1120 1121
static int transcode_audio_filter_allocation_init( filter_t *p_filter,
                                                   void *data )
1122
{
1123
    VLC_UNUSED(data);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1124
    p_filter->pf_audio_buffer_new = transcode_audio_alloc;
1125
    return VLC_SUCCESS;
1126 1127
}

1128 1129 1130 1131 1132 1133 1134 1135
static bool transcode_audio_filter_needed( const es_format_t *p_fmt1, const es_format_t *p_fmt2 )
{
    if( p_fmt1->i_codec != p_fmt2->i_codec ||
        p_fmt1->audio.i_channels != p_fmt2->audio.i_channels ||
        p_fmt1->audio.i_rate != p_fmt2->audio.i_rate )
        return true;
    return false;
}
1136

1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
static int transcode_audio_filter_chain_build( sout_stream_t *p_stream, filter_chain_t *p_chain,
                                               const es_format_t *p_dst, const es_format_t *p_src )
{
    if( !transcode_audio_filter_needed( p_dst, p_src ) )
        return VLC_SUCCESS;

    es_format_t current = *p_src;

    msg_Dbg( p_stream, "Looking for filter "
             "(%4.4s->%4.4s, channels %d->%d, rate %d->%d)",
         (const char *)&p_src->i_codec,
         (const char *)&p_dst->i_codec,
         p_src->audio.i_channels,
         p_dst->audio.i_channels,
         p_src->audio.i_rate,
         p_dst->audio.i_rate );

1154 1155 1156 1157 1158
    vlc_fourcc_t fourcc = ( vlc_CPU() & CPU_CAPABILITY_FPU ) ?
                    VLC_FOURCC('f','l','3','2') : VLC_FOURCC('f','i','3','2');

    /* If any filter is needed, convert to fl32 or fi32 */
    if( current.i_codec != fourcc )
1159
    {
1160
        /* First step, convert to f{l,i}32 */
1161
        current.i_codec =
1162
        current.audio.i_format = fourcc;
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227

        if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, &current ) )
        {
            msg_Err( p_stream, "Failed to find conversion filter to fl32" );
            return VLC_EGENERIC;
        }
        current = *filter_chain_GetFmtOut( p_chain );
    }

    /* Fix sample rate */
    if( current.audio.i_rate != p_dst->audio.i_rate )
    {
        current.audio.i_rate = p_dst->audio.i_rate;
        if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, &current ) )
        {
            msg_Err( p_stream, "Failed to find conversion filter for resampling" );
            return VLC_EGENERIC;
        }
        current = *filter_chain_GetFmtOut( p_chain );
    }

    /* Fix channels */
    if( current.audio.i_channels != p_dst->audio.i_channels )
    {
        current.audio.i_channels = p_dst->audio.i_channels;
        current.audio.i_physical_channels = p_dst->audio.i_physical_channels;
        current.audio.i_original_channels = p_dst->audio.i_original_channels;

        if( ( !current.audio.i_physical_channels || !current.audio.i_original_channels ) &&
            current.audio.i_channels < 6 )
            current.audio.i_physical_channels =
            current.audio.i_original_channels = pi_channels_maps[current.audio.i_channels];

        if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, &current ) )
        {
            msg_Err( p_stream, "Failed to find conversion filter for channel mixing" );
            return VLC_EGENERIC;
        }
        current = *filter_chain_GetFmtOut( p_chain );
    }

    /* And last step, convert to the requested codec */
    if( current.i_codec != p_dst->i_codec )
    {
        current.i_codec = p_dst->i_codec;
        if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, &current ) )
        {
            msg_Err( p_stream, "Failed to find conversion filter to %4.4s",
                     (const char*)&p_dst->i_codec);
            return VLC_EGENERIC;
        }
        current = *filter_chain_GetFmtOut( p_chain );
    }

    if( transcode_audio_filter_needed( p_dst, &current ) )
    {
        /* Weird case, a filter has side effects, doomed */
        msg_Err( p_stream, "Failed to create a valid audio filter chain" );
        return VLC_EGENERIC;
    }

    msg_Dbg( p_stream, "Got complete audio filter chain" );
    return VLC_SUCCESS;
}

1228 1229
static int transcode_audio_new( sout_stream_t *p_stream,
                                sout_stream_id_t *id )
1230
{
1231
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1232
    sout_stream_audio_t *p_audio = p_sys->p_audio;
1233
    es_format_t fmt_last;
1234
    int i_priority;
1235

1236 1237 1238 1239
    /*
     * Open decoder
     */
    /* Initialization of decoder structures */
1240 1241
    id->p_decoder->fmt_out = id->p_decoder->fmt_in;
    id->p_decoder->fmt_out.i_extra = 0;
1242
    id->p_decoder->fmt_out.p_extra = NULL;
1243
    id->p_decoder->pf_decode_audio = NULL;
1244 1245
    id->p_decoder->pf_aout_buffer_new = audio_new_buffer;
    id->p_decoder->pf_aout_buffer_del = audio_del_buffer;
1246
    /* id->p_decoder->p_cfg = p_sys->p_audio_cfg; */
1247

1248
    id->p_decoder->p_module =
1249
        module_need( id->p_decoder, "decoder", "$codec", false );
1250 1251
    if( !id->p_decoder->p_module )
    {
1252
        msg_Err( p_stream, "cannot find audio decoder" );
1253
        return VLC_EGENERIC;
1254
    }
1255
    id->p_decoder->fmt_out.audio.i_bitspersample =
1256
        aout_BitsPerSample( id->p_decoder->fmt_out.i_codec );
1257

1258
    fmt_last = id->p_decoder->fmt_out;
1259

1260 1261 1262 1263 1264
    /* Fix AAC SBR changing number of channels and sampling rate */
    if( !(id->p_decoder->fmt_in.i_codec == VLC_FOURCC('m','p','4','a') &&
        fmt_last.audio.i_rate != id->p_encoder->fmt_in.audio.i_rate &&
        fmt_last.audio.i_channels != id->p_encoder->fmt_in.audio.i_channels) )
        fmt_last.audio.i_rate = id->p_decoder->fmt_in.audio.i_rate;
1265

1266 1267 1268 1269 1270 1271 1272
    /*
     * Open encoder
     */
    /* Initialization of encoder format structures */
    es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat,
                    id->p_decoder->fmt_out.i_codec );
    id->p_encoder->fmt_in.audio.i_format = id->p_decoder->fmt_out.i_codec;
Gildas Bazin's avatar
 
Gildas Bazin committed
1273

1274
    id->p_encoder->fmt_in.audio.i_rate = id->p_encoder->fmt_out.audio.i_rate;
Gildas Bazin's avatar
 
Gildas Bazin committed
1275
    id->p_encoder->fmt_in.audio.i_physical_channels =
1276
        id->p_encoder->fmt_out.audio.i_physical_channels;
1277
    id->p_encoder->fmt_in.audio.i_original_channels =
1278
        id->p_encoder->fmt_out.audio.i_original_channels;
1279
    id->p_encoder->fmt_in.audio.i_channels =
1280
        id->p_encoder->fmt_out.audio.i_channels;
1281
    id->p_encoder->fmt_in.audio.i_bitspersample =
1282
        aout_BitsPerSample( id->p_encoder->fmt_in.i_codec );
Gildas Bazin's avatar
 
Gildas Bazin committed
1283

1284
    id->p_encoder->p_cfg = p_audio->p_audio_cfg;
Gildas Bazin's avatar
 
Gildas Bazin committed
1285
    id->p_encoder->p_module =
1286
        module_need( id->p_encoder, "encoder", p_audio->psz_aenc, true );
Gildas Bazin's avatar
 
Gildas Bazin committed
1287
    if( !id->p_encoder->p_module )
Laurent Aimar's avatar
Laurent Aimar committed
1288
    {
1289
        msg_Err( p_stream, "cannot find audio encoder (module:%s fourcc:%4.4s)",
1290 1291
                 p_audio->psz_aenc ? p_audio->psz_aenc : "any",
                 (char *)&p_audio->i_acodec );
Gildas Bazin's avatar
 
Gildas Bazin committed
1292
        return VLC_EGENERIC;
Laurent Aimar's avatar
Laurent Aimar committed
1293
    }
1294
    id->p_encoder->fmt_in.audio.i_format = id->p_encoder->fmt_in.i_codec;
1295
    id->p_encoder->fmt_in.audio.i_bitspersample =
1296
        aout_BitsPerSample( id->p_encoder->fmt_in.i_codec );
1297

1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323
    i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT :
                        VLC_THREAD_PRIORITY_AUDIO;
    p_audio->id = id;
    p_audio->b_die = p_audio->b_error = 0;
    p_audio->fifo_in = block_FifoNew();
    p_audio->fifo_out = block_FifoNew();
    if( !p_audio->fifo_in || !p_audio->fifo_out )
    {
        module_unneed( id->p_decoder, id->p_decoder->p_module );
        id->p_decoder->p_module = NULL;
        free( id->p_decoder->p_owner );
        if( p_audio->fifo_in )
            block_FifoRelease( p_audio->fifo_in );
        return VLC_ENOMEM;
    }
    if( vlc_thread_create( p_audio, "audio", AudioThread, i_priority ) )
    {
        msg_Err( p_stream, "cannot spawn encoder thread" );
        module_unneed( id->p_decoder, id->p_decoder->p_module );
        id->p_decoder->p_module = NULL;
        free( id->p_decoder->p_owner );
        block_FifoRelease( p_audio->fifo_in );
        block_FifoRelease( p_audio->fifo_out );
        return VLC_EGENERIC;
    }

1324
    /* Load user specified audio filters */
1325
    if( p_audio->psz_af2 )
1326
    {
1327 1328
        es_format_t fmt_fl32 = fmt_last;
        fmt_fl32.i_codec =
1329 1330
        fmt_fl32.audio.i_format = (vlc_CPU() & CPU_CAPABILITY_FPU) ?
                            VLC_FOURCC('f','l','3','2') : VLC_FOURCC('f','i','3','2');
1331 1332
        if( transcode_audio_filter_chain_build( p_stream, id->p_uf_chain,
                                                &fmt_fl32, &fmt_last ) )
1333
        {
1334 1335 1336 1337 1338
            vlc_object_kill( p_audio );
            block_FifoWake( p_audio->fifo_in );
            block_FifoWake( p_audio->fifo_out );
            vlc_thread_join( p_audio );
            transcode_audio_close( p_stream, id );
1339 1340
            return VLC_EGENERIC;
        }
1341
        fmt_last = fmt_fl32;
1342

1343 1344 1345
        id->p_uf_chain = filter_chain_New( p_stream, "audio filter2", false,
                                           transcode_audio_filter_allocation_init, NULL, NULL );
        filter_chain_Reset( id->p_uf_chain, &fmt_last, &fmt_fl32 );
1346
        if( filter_chain_AppendFromString( id->p_uf_chain, p_audio->psz_af2 ) > 0 )
1347
            fmt_last = *filter_chain_GetFmtOut( id->p_uf_chain );
1348 1349
    }

1350 1351 1352 1353 1354 1355 1356
    /* Load conversion filters */
    id->p_f_chain = filter_chain_New( p_stream, "audio filter2", true,
                    transcode_audio_filter_allocation_init, NULL, NULL );
    filter_chain_Reset( id->p_f_chain, &fmt_last, &id->p_encoder->fmt_in );

    if( transcode_audio_filter_chain_build( p_stream, id->p_f_chain,
                                            &id->p_encoder->fmt_in, &fmt_last ) )
1357
    {
1358
        transcode_audio_close( p_stream, id );
1359
        return VLC_EGENERIC;
1360
    }
1361
    fmt_last = id->p_encoder->fmt_in;
1362

1363 1364 1365
    /* FIXME: Hack for mp3 transcoding support */
    if( id->p_encoder->fmt_out.i_codec == VLC_FOURCC( 'm','p','3',' ' ) )
        id->p_encoder->fmt_out.i_codec = VLC_FOURCC( 'm','p','g','a' );
Gildas Bazin's avatar
 
Gildas Bazin committed
1366

1367 1368 1369
    return VLC_SUCCESS;
}

1370
static void transcode_audio_close( sout_stream_t *p_stream, sout_stream_id_t *id )
1371
{
1372 1373
    sout_stream_sys_t *p_sys = p_stream->p_sys;

1374 1375
    audio_timer_close( id->p_encoder );

1376 1377 1378 1379 1380
    vlc_object_kill( p_sys->p_audio );
    block_FifoWake( p_sys->p_audio->fifo_in );
    block_FifoWake( p_sys->p_audio->fifo_out );
    vlc_thread_join( p_sys->p_audio );

1381 1382
    /* Close decoder */
    if( id->p_decoder->p_module )
1383
        module_unneed( id->p_decoder, id->p_decoder->p_module );
1384
    id->p_decoder->p_module = NULL;
1385

1386 1387 1388 1389
    if( id->p_decoder->p_description )
        vlc_meta_Delete( id->p_decoder->p_description );
    id->p_decoder->p_description = NULL;

1390 1391
    /* Close encoder */
    if( id->p_encoder->p_module )
1392
        module_unneed( id->p_encoder, id->p_encoder->p_module );
1393
    id->p_encoder->p_module = NULL;
1394

1395
    /* Close filters */
1396 1397
    if( id->p_uf_chain )
        filter_chain_Delete( id->p_uf_chain );
1398 1399
    if( id->p_f_chain )
        filter_chain_Delete( id->p_f_chain );
1400 1401 1402 1403

    /* Release fifo's */
    block_FifoRelease( p_sys->p_audio->fifo_in );
    block_FifoRelease( p_sys->p_audio->fifo_out );
1404 1405
}

1406 1407 1408
static int transcode_audio_process( sout_stream_t *p_stream,
                                    sout_stream_id_t *id,
                                    block_t *in, block_t **out )
1409
{
1410
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1411
    VLC_UNUSED(id);
1412 1413
    *out = NULL;

1414 1415
    block_FifoPut( p_sys->p_audio->fifo_in, in );
    while( block_FifoCount( p_sys->p_audio->fifo_out ) > 0 )
1416
    {
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446
        block_t *p_block = block_FifoGet( p_sys->p_audio->fifo_out );
        if( !p_block )
            break;
        block_ChainAppend( out, p_block );
    }

    return VLC_SUCCESS;
}

static void* AudioThread( vlc_object_t* p_this )
{
    sout_stream_audio_t *p_audio = (sout_stream_audio_t*)p_this;
    sout_stream_sys_t *p_sys = p_audio->p_sys;
    sout_stream_t *p_stream = p_sys->p_out;
    sout_stream_id_t *id = p_audio->id;
    int canc = vlc_savecancel ();

    while( vlc_object_alive (p_audio) && !p_audio->b_error )
    {
        aout_buffer_t *p_audio_buf;
        block_t *p_block, *p_audio_block;
        block_t *in;

        do {
            in = block_FifoGet( p_audio->fifo_in );
            if( !vlc_object_alive (p_audio) || p_audio->b_error )
                goto cleanup;
        } while( !in );

        while( (p_audio_buf = id->p_decoder->pf_decode_audio( id->p_decoder, &in )) )
1447
        {
1448
            if( !vlc_object_alive (p_audio) || p_audio->b_error )
1449
            {
1450 1451
                free( p_audio_buf );
                goto cleanup;
1452
            }
1453

1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469
            sout_UpdateStatistic( p_stream->p_sout, SOUT_STATISTIC_DECODED_AUDIO, 1 );
            if( p_sys->b_master_sync )
            {
                mtime_t i_dts = date_Get( &id->interpolated_pts ) + 1;
                if ( p_audio_buf->start_date - i_dts > MASTER_SYNC_MAX_DRIFT
                    || p_audio_buf->start_date - i_dts < -MASTER_SYNC_MAX_DRIFT )
                {
                    msg_Dbg( p_this, "drift is too high, resetting master sync" );
                    date_Set( &id->interpolated_pts, p_audio_buf->start_date );
                    i_dts = p_audio_buf->start_date + 1;
                }
                p_sys->i_master_drift = p_audio_buf->start_date - i_dts;
                date_Increment( &id->interpolated_pts, p_audio_buf->i_nb_samples );
                p_audio_buf->start_date -= p_sys->i_master_drift;
                p_audio_buf->end_date -= p_sys->i_master_drift;
            }
1470

1471 1472 1473 1474 1475 1476 1477
            p_audio_block = p_audio_buf->p_sys;
            p_audio_block->i_buffer = p_audio_buf->i_nb_bytes;
            p_audio_block->i_dts = p_audio_block->i_pts =
                p_audio_buf->start_date;
            p_audio_block->i_length = p_audio_buf->end_date -
                p_audio_buf->start_date;
            p_audio_block->i_samples = p_audio_buf->i_nb_samples;
1478

1479 1480 1481 1482 1483 1484
            /* Run filter chain */
            if( id->p_uf_chain )
            {
                p_audio_block = filter_chain_AudioFilter( id->p_uf_chain, p_audio_block );
                assert( p_audio_block );
            }
1485

1486 1487
            p_audio_block = filter_chain_AudioFilter( id->p_f_chain, p_audio_block );
            assert( p_audio_block );
1488

1489 1490 1491 1492 1493
            p_audio_buf->p_buffer = p_audio_block->p_buffer;
            p_audio_buf->i_nb_bytes = p_audio_block->i_buffer;
            p_audio_buf->i_nb_samples = p_audio_block->i_samples;
            p_audio_buf->start_date = p_audio_block->i_dts;
            p_audio_buf->end_date = p_audio_block->i_dts + p_audio_block->i_length;
1494

1495 1496 1497 1498 1499 1500 1501 1502
            audio_timer_start( id->p_encoder );
            p_block = id->p_encoder->pf_encode_audio( id->p_encoder, p_audio_buf );
            audio_timer_stop( id->p_encoder );

            block_FifoPut( p_audio->fifo_out, p_block );
            block_Release( p_audio_block );
            free( p_audio_buf );
        }
1503
    }
1504

1505 1506 1507 1508 1509 1510
cleanup:
    block_FifoEmpty( p_audio->fifo_in );
    block_FifoEmpty( p_audio->fifo_out );

    vlc_restorecancel (canc);
    return NULL;
1511
}
1512

1513 1514 1515
static void audio_release_buffer( aout_buffer_t *p_buffer )
{
    if( p_buffer && p_buffer->p_sys ) block_Release( p_buffer->p_sys );
1516
    free( p_buffer );
1517
}
1518

1519 1520 1521 1522 1523
static aout_buffer_t *audio_new_buffer( decoder_t *p_dec, int i_samples )
{
    aout_buffer_t *p_buffer;
    block_t *p_block;
    int i_size;
1524

1525 1526
    if( p_dec->fmt_out.audio.i_bitspersample )
    {
1527
        i_size = ((i_samples * p_dec->fmt_out.audio.i_bitspersample) >> 3) *
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
            p_dec->fmt_out.audio.i_channels;
    }
    else if( p_dec->fmt_out.audio.i_bytes_per_frame &&
             p_dec->fmt_out.audio.i_frame_length )
    {
        i_size = i_samples * p_dec->fmt_out.audio.i_bytes_per_frame /
            p_dec->fmt_out.audio.i_frame_length;
    }
    else
    {
        i_size = i_samples * 4 * p_dec->fmt_out.audio.i_channels;
    }
1540

1541
    p_buffer = malloc( sizeof(aout_buffer_t) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1542
    if( !p_buffer ) return NULL;
1543
    p_buffer->b_discontinuity = false;
1544 1545
    p_buffer->pf_release = audio_release_buffer;
    p_buffer->p_sys = p_block = block_New( p_dec, i_size );
1546 1547 1548 1549 1550
    if( !p_block )
    {
        free( p_buffer );
        return NULL;
    }
1551 1552 1553 1554
    p_buffer->p_buffer = p_block->p_buffer;
    p_buffer->i_size = p_buffer->i_nb_bytes = p_block->i_buffer;
    p_buffer->i_nb_samples = i_samples;
    p_block->i_samples = i_samples;
1555

1556
    return p_buffer;
1557 1558
}

1559 1560
static void audio_del_buffer( decoder_t *p_dec, aout_buffer_t *p_buffer )
{
Rafaël Carré's avatar
Rafaël Carré committed
1561
    VLC_UNUSED(p_dec);
1562
    if( p_buffer && p_buffer->p_sys ) block_Release( p_buffer->p_sys );
Rafaël Carré's avatar
Rafaël Carré committed
1563
    free( p_buffer );
1564
}
1565 1566 1567 1568

/*
 * video
 */
1569

1570 1571
static int transcode_video_filter_allocation_init( filter_t *p_filter,
                                                   void *p_data )
1572
{
1573
    sout_stream_sys_t *p_sys = (sout_stream_sys_t*)p_data;
1574 1575 1576 1577 1578 1579 1580
    int i;

    p_filter->pf_vout_buffer_new = video_new_buffer_filter;
    p_filter->pf_vout_buffer_del = video_del_buffer_filter;

    p_filter->p_owner = malloc( sizeof(filter_owner_sys_t) );
    if( !p_filter->p_owner )
1581
        return VLC_EGENERIC;
1582 1583 1584 1585 1586

    for( i = 0; i < PICTURE_RING_SIZE; i++ )
        p_filter->p_owner->pp_pics[i] = 0;
    p_filter->p_owner->p_sys = p_sys;

1587
    return VLC_SUCCESS;
1588 1589
}

1590
static void transcode_video_filter_allocation_clear( filter_t *p_filter )
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603
{
    int j;

    /* Clean-up pictures ring buffer */
    for( j = 0; j < PICTURE_RING_SIZE; j++ )
    {
        if( p_filter->p_owner->pp_pics[j] )
            video_del_buffer( VLC_OBJECT(p_filter),
                              p_filter->p_owner->pp_pics[j] );
    }
    free( p_filter->p_owner );
}

1604
static int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id )
1605
{
1606
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1607
    sout_stream_video_t *p_video = p_sys->p_video;
1608
    int i;
1609

1610 1611
    /* Open decoder
     * Initialization of decoder structures
1612
     */
1613 1614
    id->p_decoder->fmt_out = id->p_decoder->fmt_in;
    id->p_decoder->fmt_out.i_extra = 0;
1615
    id->p_decoder->fmt_out.p_extra = NULL;
1616 1617
    id->p_decoder->pf_decode_video = NULL;
    id->p_decoder->pf_get_cc = NULL;
1618
    id->p_decoder->pf_get_cc = 0;
1619 1620 1621 1622 1623
    id->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder;
    id->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder;
    id->p_decoder->pf_picture_link    = video_link_picture_decoder;
    id->p_decoder->pf_picture_unlink  = video_unlink_picture_decoder;
    id->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) );
1624 1625 1626
    if( !id->p_decoder->p_owner )
        return VLC_EGENERIC;

1627
    for( i = 0; i < PICTURE_RING_SIZE; i++ )
1628
        id->p_decoder->p_owner->pp_pics[i] = NULL;
1629
    id->p_decoder->p_owner->p_sys = p_sys;
1630
    /* id->p_decoder->p_cfg = p_video->p_video_cfg; */
1631

1632
    id->p_decoder->p_module =
1633
        module_need( id->p_decoder, "decoder", "$codec", false );
1634

1635 1636
    if( !id->p_decoder->p_module )
    {
1637
        msg_Err( p_stream, "cannot find video decoder" );
1638
        free( id->p_decoder->p_owner );
1639
        return VLC_EGENERIC;
1640
    }
1641

1642 1643 1644 1645 1646 1647
    /*
     * Open encoder.
     * Because some info about the decoded input will only be available
     * once the first frame is decoded, we actually only test the availability
     * of the encoder here.
     */
Gildas Bazin's avatar
 
Gildas Bazin committed
1648

Gildas Bazin's avatar
 
Gildas Bazin committed
1649
    /* Initialization of encoder format structures */
1650 1651 1652
    es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat,
                    id->p_decoder->fmt_out.i_codec );
    id->p_encoder->fmt_in.video.i_chroma = id->p_decoder->fmt_out.i_codec;
Gildas Bazin's avatar
 
Gildas Bazin committed
1653

1654
    /* The dimensions will be set properly later on.
1655
     * Just put sensible values so we can test an encoder is available. */
1656
    id->p_encoder->fmt_in.video.i_width =
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1657 1658 1659 1660
        id->p_encoder->fmt_out.video.i_width
          ? id->p_encoder->fmt_out.video.i_width
          : id->p_decoder->fmt_in.video.i_width
            ? id->p_decoder->fmt_in.video.i_width : 16;
1661
    id->p_encoder->fmt_in.video.i_height =
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1662 1663 1664 1665
        id->p_encoder->fmt_out.video.i_height
          ? id->p_encoder->fmt_out.video.i_height
          : id->p_decoder->fmt_in.video.i_height
            ? id->p_decoder->fmt_in.video.i_height : 16;
1666 1667
    id->p_encoder->fmt_in.video.i_frame_rate = ENC_FRAMERATE;
    id->p_encoder->fmt_in.video.i_frame_rate_base = ENC_FRAMERATE_BASE;
Gildas Bazin's avatar
 
Gildas Bazin committed
1668

1669 1670
    id->p_encoder->i_threads = p_video->i_threads;
    id->p_encoder->p_cfg = p_video->p_video_cfg;
1671

Gildas Bazin's avatar
 
Gildas Bazin committed
1672
    id->p_encoder->p_module =
1673
        module_need( id->p_encoder, "encoder", p_video->psz_venc, true );
Gildas Bazin's avatar
 
Gildas Bazin committed
1674
    if( !id->p_encoder->p_module )
1675
    {
1676
        msg_Err( p_stream, "cannot find video encoder (module:%s fourcc:%4.4s)",
1677 1678
                 p_video->psz_venc ? p_video->psz_venc : "any",
                 (char *)&p_video->i_vcodec );
1679
        module_unneed( id->p_decoder, id->p_decoder->p_module );
1680
        id->p_decoder->p_module = NULL;
1681
        free( id->p_decoder->p_owner );
Gildas Bazin's avatar
 
Gildas Bazin committed
1682
        return VLC_EGENERIC;
1683
    }
Laurent Aimar's avatar
Laurent Aimar committed
1684

Gildas Bazin's avatar
 
Gildas Bazin committed
1685
    /* Close the encoder.
1686
     * We'll open it only when we have the first frame. */
1687
    module_unneed( id->p_encoder, id->p_encoder->p_module );
1688
    if( id->p_encoder->fmt_out.p_extra )
1689
    {
1690
        free( id->p_encoder->fmt_out.p_extra );
1691 1692 1693
        id->p_encoder->fmt_out.p_extra = NULL;
        id->p_encoder->fmt_out.i_extra = 0;
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
1694
    id->p_encoder->p_module = NULL;
1695

1696
    if( p_video->i_threads >= 1 )
1697
    {
1698 1699
        int i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT :
                           VLC_THREAD_PRIORITY_VIDEO;
1700 1701 1702 1703 1704 1705 1706 1707 1708
        p_video->id = id;
        vlc_mutex_init( &p_video->lock_out );
        vlc_cond_init( &p_video->cond );
        memset( p_video->pp_pics, 0, sizeof(p_video->pp_pics) );
        p_video->i_first_pic = 0;
        p_video->i_last_pic = 0;
        p_video->p_buffers = NULL;
        p_video->b_die = p_video->b_error = 0;
        if( vlc_thread_create( p_video, "video encoder", VideoEncoderThread, i_priority ) )
1709 1710
        {
            msg_Err( p_stream, "cannot spawn encoder thread" );
1711
            module_unneed( id->p_decoder, id->p_decoder->p_module );
1712
            id->p_decoder->p_module = NULL;
1713
            free( id->p_decoder->p_owner );
1714 1715 1716 1717
            return VLC_EGENERIC;
        }
    }

1718 1719 1720
    return VLC_SUCCESS;
}

1721 1722
static void transcode_video_encoder_init( sout_stream_t *p_stream,
                                          sout_stream_id_t *id )
1723
{
1724
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1725
    sout_stream_video_t  *p_video = p_sys->p_video;
1726

1727 1728 1729 1730
    /* Calculate scaling
     * width/height of source */
    int i_src_width = id->p_decoder->fmt_out.video.i_width;
    int i_src_height = id->p_decoder->fmt_out.video.i_height;
1731

1732 1733 1734
    /* with/height scaling */
    float f_scale_width = 1;
    float f_scale_height = 1;
1735

1736 1737 1738
    /* width/height of output stream */
    int i_dst_width;
    int i_dst_height;
1739

1740 1741 1742
    /* aspect ratio */
    float f_aspect = (float)id->p_decoder->fmt_out.video.i_aspect /
                            VOUT_ASPECT_FACTOR;
1743

1744 1745
    msg_Dbg( p_stream, "decoder aspect is %i:%i",
                 id->p_decoder->fmt_out.video.i_aspect, VOUT_ASPECT_FACTOR );
1746

1747 1748 1749
    /* Change f_aspect from source frame to source pixel */
    f_aspect = f_aspect * i_src_height / i_src_width;
    msg_Dbg( p_stream, "source pixel aspect is %f:1", f_aspect );
1750 1751

    /* Calculate scaling factor for specified parameters */
1752
    if( id->p_encoder->fmt_out.video.i_width <= 0 &&
1753
        id->p_encoder->fmt_out.video.i_height <= 0 && p_video->f_scale )
1754
    {
1755
        /* Global scaling. Make sure width will remain a factor of 16 */
1756
        float f_real_scale;
1757
        int  i_new_height;
1758
        int i_new_width = i_src_width * p_video->f_scale;
1759 1760 1761

        if( i_new_width % 16 <= 7 && i_new_width >= 16 )
            i_new_width -= i_new_width % 16;
1762
        else
1763 1764 1765
            i_new_width += 16 - i_new_width % 16;

        f_real_scale = (float)( i_new_width ) / (float) i_src_width;
1766

1767
        i_new_height = __MAX( 16, i_src_height * (float)f_real_scale );
1768

1769 1770
        f_scale_width = f_real_scale;
        f_scale_height = (float) i_new_height / (float) i_src_height;
1771 1772 1773 1774
    }
    else if( id->p_encoder->fmt_out.video.i_width > 0 &&
             id->p_encoder->fmt_out.video.i_height <= 0 )
    {
1775
        /* Only width specified */
1776
        f_scale_width = (float)id->p_encoder->fmt_out.video.i_width/i_src_width;
1777
        f_scale_height = f_scale_width;
1778 1779 1780 1781
    }
    else if( id->p_encoder->fmt_out.video.i_width <= 0 &&
             id->p_encoder->fmt_out.video.i_height > 0 )
    {
1782
         /* Only height specified */
1783
         f_scale_height = (float)id->p_encoder->fmt_out.video.i_height/i_src_height;
1784 1785 1786 1787 1788 1789
         f_scale_width = f_scale_height;
     }
     else if( id->p_encoder->fmt_out.video.i_width > 0 &&
              id->p_encoder->fmt_out.video.i_height > 0 )
     {
         /* Width and height specified */
1790 1791
         f_scale_width = (float)id->p_encoder->fmt_out.video.i_width/i_src_width;
         f_scale_height = (float)id->p_encoder->fmt_out.video.i_height/i_src_height;
1792 1793
     }

1794 1795
     /* check maxwidth and maxheight
      */
1796 1797
     if( p_video->i_maxwidth && f_scale_width >
            (float)p_video->i_maxwidth / i_src_width )
1798
     {
1799
         f_scale_width = (float)p_video->i_maxwidth / i_src_width;
1800
     }
1801

1802 1803
     if( p_video->i_maxheight && f_scale_height >
            (float)p_video->i_maxheight / i_src_height )
1804
     {
1805
         f_scale_height = (float)p_video->i_maxheight / i_src_height;
1806 1807 1808 1809 1810 1811 1812
     }

     /* Change aspect ratio from source pixel to scaled pixel */
     f_aspect = f_aspect * f_scale_height / f_scale_width;
     msg_Dbg( p_stream, "scaled pixel aspect is %f:1", f_aspect );

     /* f_scale_width and f_scale_height are now final */
1813 1814 1815
     /* Calculate width, height from scaling
      * Make sure its multiple of 2
      */
1816 1817
     i_dst_width =  2 * (int)(f_scale_width*i_src_width/2+0.5);
     i_dst_height = 2 * (int)(f_scale_height*i_src_height/2+0.5);
1818 1819 1820 1821 1822

     /* Change aspect ratio from scaled pixel to output frame */
     f_aspect = f_aspect * i_dst_width / i_dst_height;

     /* Store calculated values */
1823 1824 1825 1826 1827 1828 1829 1830 1831
     id->p_encoder->fmt_out.video.i_width =
     id->p_encoder->fmt_out.video.i_visible_width = i_dst_width;
     id->p_encoder->fmt_out.video.i_height =
     id->p_encoder->fmt_out.video.i_visible_height = i_dst_height;

     id->p_encoder->fmt_in.video.i_width =
     id->p_encoder->fmt_in.video.i_visible_width = i_dst_width;
     id->p_encoder->fmt_in.video.i_height =
     id->p_encoder->fmt_in.video.i_visible_height = i_dst_height;
1832

1833
     msg_Dbg( p_stream, "source %ix%i, destination %ix%i",
1834 1835 1836 1837 1838
         i_src_width, i_src_height,
         i_dst_width, i_dst_height
     );

    /* Handle frame rate conversion */
1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852
    if( !id->p_encoder->fmt_out.video.i_frame_rate ||
        !id->p_encoder->fmt_out.video.i_frame_rate_base )
    {
        if( id->p_decoder->fmt_out.video.i_frame_rate &&
            id->p_decoder->fmt_out.video.i_frame_rate_base )
        {
            id->p_encoder->fmt_out.video.i_frame_rate =
                id->p_decoder->fmt_out.video.i_frame_rate;
            id->p_encoder->fmt_out.video.i_frame_rate_base =
                id->p_decoder->fmt_out.video.i_frame_rate_base;
        }
        else
        {
            /* Pick a sensible default value */
1853 1854
            id->p_encoder->fmt_out.video.i_frame_rate = ENC_FRAMERATE;
            id->p_encoder->fmt_out.video.i_frame_rate_base = ENC_FRAMERATE_BASE;
1855 1856 1857 1858 1859 1860 1861
        }
    }

    id->p_encoder->fmt_in.video.i_frame_rate =
        id->p_encoder->fmt_out.video.i_frame_rate;
    id->p_encoder->fmt_in.video.i_frame_rate_base =
        id->p_encoder->fmt_out.video.i_frame_rate_base;
1862

1863 1864 1865 1866 1867 1868
    date_Init( &id->interpolated_pts,
               id->p_encoder->fmt_out.video.i_frame_rate,
               id->p_encoder->fmt_out.video.i_frame_rate_base );

    /* Check whether a particular aspect ratio was requested */
    if( !id->p_encoder->fmt_out.video.i_aspect )
1869
    {
1870 1871
        id->p_encoder->fmt_out.video.i_aspect =
                (int)( f_aspect * VOUT_ASPECT_FACTOR + 0.5 );
1872
    }
1873 1874
    id->p_encoder->fmt_in.video.i_aspect =
        id->p_encoder->fmt_out.video.i_aspect;
1875

1876 1877
    msg_Dbg( p_stream, "encoder aspect is %i:%i",
             id->p_encoder->fmt_out.video.i_aspect, VOUT_ASPECT_FACTOR );
1878

1879 1880 1881 1882 1883 1884 1885
    id->p_encoder->fmt_in.video.i_chroma = id->p_encoder->fmt_in.i_codec;
}

static int transcode_video_encoder_open( sout_stream_t *p_stream,
                                         sout_stream_id_t *id )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1886
    sout_stream_video_t  *p_video = p_sys->p_video;
1887 1888 1889 1890 1891

    msg_Dbg( p_stream, "destination (after video filters) %ix%i",
             id->p_encoder->fmt_in.video.i_width,
             id->p_encoder->fmt_in.video.i_height );

1892
    id->p_encoder->p_module =
1893
        module_need( id->p_encoder, "encoder", p_video->psz_venc, true );
1894
    if( !id->p_encoder->p_module )
1895
    {
1896
        msg_Err( p_stream, "cannot find video encoder (module:%s fourcc:%4.4s)",
1897 1898
                 p_video->psz_venc ? p_video->psz_venc : "any",
                 (char *)&p_video->i_vcodec );
1899
        return VLC_EGENERIC;
1900
    }
1901 1902 1903 1904 1905 1906

    id->p_encoder->fmt_in.video.i_chroma = id->p_encoder->fmt_in.i_codec;

    /* Hack for mp2v/mp1v transcoding support */
    if( id->p_encoder->fmt_out.i_codec == VLC_FOURCC('m','p','1','v') ||
        id->p_encoder->fmt_out.i_codec == VLC_FOURCC('m','p','2','v') )
1907
    {
1908
        id->p_encoder->fmt_out.i_codec = VLC_FOURCC('m','p','g','v');
1909
    }
1910

1911
    id->id = sout_StreamIdAdd( p_sys->p_out,
1912
                               &id->p_encoder->fmt_out );
1913
    if( !id->id )
1914
    {
1915 1916
        msg_Err( p_stream, "cannot add this stream" );
        return VLC_EGENERIC;
1917
    }
1918 1919 1920 1921 1922 1923 1924

    return VLC_SUCCESS;
}

static void transcode_video_close( sout_stream_t *p_stream,
                                   sout_stream_id_t *id )
{
1925 1926
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    sout_stream_video_t  *p_video = p_sys->p_video;
1927
    int i;
1928

1929
    if( p_video->i_threads >= 1 )
1930
    {
1931 1932 1933 1934 1935 1936 1937
        vlc_mutex_lock( &p_video->lock_out );
        vlc_object_kill( p_video );
        vlc_cond_signal( &p_video->cond );
        vlc_mutex_unlock( &p_video->lock_out );
        vlc_thread_join( p_video );
        vlc_mutex_destroy( &p_video->lock_out );
        vlc_cond_destroy( &p_video->cond );
1938
    }
1939

1940 1941
    video_timer_close( id->p_encoder );

1942 1943
    /* Close decoder */
    if( id->p_decoder->p_module )
1944
        module_unneed( id->p_decoder, id->p_decoder->p_module );
1945 1946
    if( id->p_decoder->p_description )
        vlc_meta_Delete( id->p_decoder->p_description );
1947

1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
    if( id->p_decoder->p_owner )
    {
        /* Clean-up pictures ring buffer */
        for( i = 0; i < PICTURE_RING_SIZE; i++ )
        {
            if( id->p_decoder->p_owner->pp_pics[i] )
                video_del_buffer( VLC_OBJECT(id->p_decoder),
                                  id->p_decoder->p_owner->pp_pics[i] );
        }
        free( id->p_decoder->p_owner );
    }

1960 1961
    /* Close encoder */
    if( id->p_encoder->p_module )
1962
        module_unneed( id->p_encoder, id->p_encoder->p_module );
1963 1964

    /* Close filters */
1965 1966 1967 1968
    if( id->p_f_chain )
        filter_chain_Delete( id->p_f_chain );
    if( id->p_uf_chain )
        filter_chain_Delete( id->p_uf_chain );
1969 1970
}

1971 1972 1973
static int transcode_video_process( sout_stream_t *p_stream,
                                    sout_stream_id_t *id,
                                    block_t *in, block_t **out )
1974
{
1975
    sout_stream_sys_t *p_sys = p_stream->p_sys;
1976
    sout_stream_video_t  *p_video = p_sys->p_video;
1977
    int i_duplicate = 1;
1978
    picture_t *p_pic, *p_pic2 = NULL;
1979
    *out = NULL;
Clément Stenac's avatar
Clément Stenac committed
1980

1981
    while( (p_pic = id->p_decoder->pf_decode_video( id->p_decoder, &in )) )
1982
    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
1983
        subpicture_t *p_subpic = NULL;
1984 1985

        sout_UpdateStatistic( p_stream->p_sout, SOUT_STATISTIC_DECODED_VIDEO, 1 );
1986

1987 1988 1989
        if( p_stream->p_sout->i_out_pace_nocontrol && p_sys->b_hurry_up )
        {
            mtime_t current_date = mdate();
1990
            if( current_date + 50000 > p_pic->date )
1991
            {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1992
                msg_Dbg( p_stream, "late picture skipped (%"PRId64")",
1993
                         current_date + 50000 - p_pic->date );
1994 1995 1996 1997 1998
                p_pic->pf_release( p_pic );
                continue;
            }
        }

1999
        if( p_sys->b_master_sync )
2000 2001
        {
            mtime_t i_video_drift;
2002
            mtime_t i_master_drift = p_sys->i_master_drift;
2003
            mtime_t i_pts;
2004

2005
            i_pts = date_Get( &id->interpolated_pts ) + 1;
2006 2007
            if ( p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT
                  || p_pic->date - i_pts < -MASTER_SYNC_MAX_DRIFT )
2008
            {
2009
                msg_Dbg( p_stream, "drift is too high, resetting master sync" );
2010 2011
                date_Set( &id->interpolated_pts, p_pic->date );
                i_pts = p_pic->date + 1;
2012
            }
2013
            i_video_drift = p_pic->date - i_pts;
2014 2015
            i_duplicate = 1;

2016 2017 2018
            /* Set the pts of the frame being encoded */
            p_pic->date = i_pts;

Jean-Paul Saman's avatar
Jean-Paul Saman committed
2019
            if( i_video_drift < (i_master_drift - 50000) )
2020
            {
2021
#if 0
2022
                msg_Dbg( p_stream, "dropping frame (%i)",
2023
                         (int)(i_video_drift - i_master_drift) );
2024
#endif
2025
                p_pic->pf_release( p_pic );
2026
                continue;
2027
            }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2028
            else if( i_video_drift > (i_master_drift + 50000) )
2029
            {
2030
#if 0
2031
                msg_Dbg( p_stream, "adding frame (%i)",
2032
                         (int)(i_video_drift - i_master_drift) );
2033
#endif
2034 2035 2036 2037
                i_duplicate = 2;
            }
        }

2038
        if( !id->p_encoder->p_module )
2039
        {
2040
            transcode_video_encoder_init( p_stream, id );
Gildas Bazin's avatar
 
Gildas Bazin committed
2041

2042 2043 2044 2045 2046 2047
            id->p_f_chain = filter_chain_New( p_stream, "video filter2",
                                              false,
                               transcode_video_filter_allocation_init,
                               transcode_video_filter_allocation_clear,
                               p_stream->p_sys );

2048
            /* Deinterlace */
2049
            if( p_video->b_deinterlace )
2050
            {
2051
                filter_chain_AppendFilter( id->p_f_chain,
2052 2053
                                           p_video->psz_deinterlace,
                                           p_video->p_deinterlace_cfg,
2054 2055
                                           &id->p_decoder->fmt_out,
                                           &id->p_decoder->fmt_out );
2056 2057
            }

2058
            /* Take care of the scaling and chroma conversions */
2059 2060 2061
            if( ( id->p_decoder->fmt_out.video.i_chroma !=
                  id->p_encoder->fmt_in.video.i_chroma ) ||
                ( id->p_decoder->fmt_out.video.i_width !=
2062
                  id->p_encoder->fmt_in.video.i_width ) ||
2063
                ( id->p_decoder->fmt_out.video.i_height !=
2064
                  id->p_encoder->fmt_in.video.i_height ) )
2065
            {
2066 2067 2068 2069
                filter_chain_AppendFilter( id->p_f_chain,
                                           NULL, NULL,
                                           &id->p_decoder->fmt_out,
                                           &id->p_encoder->fmt_in );
Gildas Bazin's avatar
 
Gildas Bazin committed
2070
            }
2071

2072
            if( p_video->psz_vf2 )
2073
            {
2074
                const es_format_t *p_fmt_out;
2075 2076 2077 2078 2079
                id->p_uf_chain = filter_chain_New( p_stream, "video filter2",
                                                   true,
                                   transcode_video_filter_allocation_init,
                                   transcode_video_filter_allocation_clear,
                                   p_stream->p_sys );
2080
                filter_chain_Reset( id->p_uf_chain, &id->p_encoder->fmt_in,
2081
                                    &id->p_encoder->fmt_in );
2082
                filter_chain_AppendFromString( id->p_uf_chain, p_video->psz_vf2 );
2083 2084 2085 2086 2087 2088
                p_fmt_out = filter_chain_GetFmtOut( id->p_uf_chain );
                es_format_Copy( &id->p_encoder->fmt_in, p_fmt_out );
                id->p_encoder->fmt_out.video.i_width =
                    id->p_encoder->fmt_in.video.i_width;
                id->p_encoder->fmt_out.video.i_height =
                    id->p_encoder->fmt_in.video.i_height;
2089 2090
                id->p_encoder->fmt_out.video.i_aspect =
                    id->p_encoder->fmt_in.video.i_aspect;
2091 2092 2093 2094 2095 2096 2097 2098
            }

            if( transcode_video_encoder_open( p_stream, id ) != VLC_SUCCESS )
            {
                p_pic->pf_release( p_pic );
                transcode_video_close( p_stream, id );
                id->b_transcode = false;
                return VLC_EGENERIC;
2099
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
2100
        }
2101

2102
        /* Run filter chain */
2103 2104
        if( id->p_f_chain )
            p_pic = filter_chain_VideoFilter( id->p_f_chain, p_pic );
2105

2106 2107 2108
        /*
         * Encoding
         */
Gildas Bazin's avatar
 
Gildas Bazin committed
2109

2110
        /* Check if we have a subpicture to overlay */
2111
        if( p_sys->p_spu )
2112
        {
2113
            p_subpic = spu_SortSubpicturesNew( p_sys->p_spu, p_pic->date, false );
2114
            /* TODO: get another pic */
2115
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
2116

2117 2118 2119
        /* Overlay subpicture */
        if( p_subpic )
        {
2120
            video_format_t fmt;
2121

2122
            if( p_pic->i_refcount && !filter_chain_GetLength( id->p_f_chain ) )
2123 2124
            {
                /* We can't modify the picture, we need to duplicate it */
2125
                picture_t *p_tmp = video_new_buffer_decoder( id->p_decoder );
2126 2127
                if( p_tmp )
                {
2128
                    picture_Copy( p_tmp, p_pic );
2129 2130 2131 2132 2133
                    p_pic->pf_release( p_pic );
                    p_pic = p_tmp;
                }
            }

2134 2135 2136 2137
            if( filter_chain_GetLength( id->p_f_chain ) > 0 )
                fmt = filter_chain_GetFmtOut( id->p_f_chain )->video;
            else
                fmt = id->p_decoder->fmt_out.video;
2138

2139
            /* FIXME (shouldn't have to be done here) */
2140 2141
            fmt.i_sar_num = fmt.i_aspect * fmt.i_height / fmt.i_width;
            fmt.i_sar_den = VOUT_ASPECT_FACTOR;
2142

2143 2144 2145
            /* FIXME the mdate() seems highly suspicious */
            spu_RenderSubpicturesNew( p_sys->p_spu, p_pic, &fmt,
                                   p_subpic, &id->p_decoder->fmt_out.video, mdate() );
2146 2147
        }

2148
        /* Run user specified filter chain */
2149 2150
        if( id->p_uf_chain )
            p_pic = filter_chain_VideoFilter( id->p_uf_chain, p_pic );
2151

2152
        if( p_video->i_threads == 0 )
2153 2154
        {
            block_t *p_block;
2155 2156

            video_timer_start( id->p_encoder );
2157
            p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
2158 2159
            video_timer_stop( id->p_encoder );

2160
            block_ChainAppend( out, p_block );
2161
        }
2162

2163 2164 2165
        if( p_sys->b_master_sync )
        {
            mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1;
2166 2167
            if ( p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT
                  || p_pic->date - i_pts < -MASTER_SYNC_MAX_DRIFT )
2168
            {
2169
                msg_Dbg( p_stream, "drift is too high, resetting master sync" );
2170 2171 2172 2173 2174
                date_Set( &id->interpolated_pts, p_pic->date );
                i_pts = p_pic->date + 1;
            }
            date_Increment( &id->interpolated_pts, 1 );
        }
2175

2176 2177 2178
        if( p_sys->b_master_sync && i_duplicate > 1 )
        {
            mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2179 2180
            if( (p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT)
                 || ((p_pic->date - i_pts) < -MASTER_SYNC_MAX_DRIFT) )
2181
            {
2182
                msg_Dbg( p_stream, "drift is too high, resetting master sync" );
2183 2184 2185 2186 2187
                date_Set( &id->interpolated_pts, p_pic->date );
                i_pts = p_pic->date + 1;
            }
            date_Increment( &id->interpolated_pts, 1 );

2188
            if( p_video->i_threads >= 1 )
2189 2190
            {
                /* We can't modify the picture, we need to duplicate it */
2191 2192
                p_pic2 = video_new_buffer_decoder( id->p_decoder );
                if( p_pic2 != NULL )
2193
                {
2194
                    picture_Copy( p_pic2, p_pic );
2195
                    p_pic2->date = i_pts;
2196 2197 2198
                }
            }
            else
2199
            {
2200
                block_t *p_block;
2201
                p_pic->date = i_pts;
2202
                video_timer_start( id->p_encoder );
2203
                p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
2204
                video_timer_stop( id->p_encoder );
2205 2206
                block_ChainAppend( out, p_block );
            }
2207
        }
2208

2209
        if( p_video->i_threads == 0 )
2210
        {
2211
            p_pic->pf_release( p_pic );
2212 2213 2214
        }
        else
        {
2215 2216 2217 2218 2219
            vlc_mutex_lock( &p_video->lock_out );
            p_video->pp_pics[p_video->i_last_pic++] = p_pic;
            p_video->i_last_pic %= PICTURE_RING_SIZE;
            *out = p_video->p_buffers;
            p_video->p_buffers = NULL;
2220 2221
            if( p_pic2 != NULL )
            {
2222 2223
                p_video->pp_pics[p_video->i_last_pic++] = p_pic2;
                p_video->i_last_pic %= PICTURE_RING_SIZE;
2224
            }
2225 2226
            vlc_cond_signal( &p_video->cond );
            vlc_mutex_unlock( &p_video->lock_out );
2227
        }
2228 2229 2230 2231 2232
    }

    return VLC_SUCCESS;
}

2233
static void* VideoEncoderThread( vlc_object_t* p_this )
2234
{
2235 2236
    sout_stream_video_t *p_video = (sout_stream_video_t*)p_this;
    sout_stream_id_t *id = p_video->id;
2237
    picture_t *p_pic;
2238
    int canc = vlc_savecancel ();
2239

2240
    while( vlc_object_alive (p_video) && !p_video->b_error )
2241 2242 2243
    {
        block_t *p_block;

2244 2245
        vlc_mutex_lock( &p_video->lock_out );
        while( p_video->i_last_pic == p_video->i_first_pic )
2246
        {
2247 2248
            vlc_cond_wait( &p_video->cond, &p_video->lock_out );
            if( !vlc_object_alive (p_video) || p_video->b_error ) break;
2249
        }
2250
        if( !vlc_object_alive (p_video) || p_video->b_error )
2251
        {
2252
            vlc_mutex_unlock( &p_video->lock_out );
2253 2254 2255
            break;
        }

2256 2257 2258
        p_pic = p_video->pp_pics[p_video->i_first_pic++];
        p_video->i_first_pic %= PICTURE_RING_SIZE;
        vlc_mutex_unlock( &p_video->lock_out );
2259

2260
        video_timer_start( id->p_encoder );
2261
        p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic );
2262 2263
        video_timer_stop( id->p_encoder );

2264 2265
        vlc_mutex_lock( &p_video->lock_out );
        block_ChainAppend( &p_video->p_buffers, p_block );
2266

2267
        vlc_mutex_unlock( &p_video->lock_out );
2268
        p_pic->pf_release( p_pic );
2269 2270
    }

2271
    while( p_video->i_last_pic != p_video->i_first_pic )
2272
    {
2273 2274
        p_pic = p_video->pp_pics[p_video->i_first_pic++];
        p_video->i_first_pic %= PICTURE_RING_SIZE;
2275
        p_pic->pf_release( p_pic );
2276
    }
2277
    block_ChainRelease( p_video->p_buffers );
2278

2279
    vlc_restorecancel (canc);
2280
    return NULL;
2281
}
Gildas Bazin's avatar
 
Gildas Bazin committed
2282

2283
struct picture_sys_t
Gildas Bazin's avatar
 
Gildas Bazin committed
2284
{
2285
    vlc_object_t *p_owner;
2286
};
Gildas Bazin's avatar
 
Gildas Bazin committed
2287

2288 2289 2290
static void video_release_buffer( picture_t *p_pic )
{
    if( p_pic && !p_pic->i_refcount && p_pic->pf_release && p_pic->p_sys )
2291
    {
2292
        video_del_buffer_decoder( (decoder_t *)p_pic->p_sys->p_owner, p_pic );
2293
    }
2294 2295
    else if( p_pic && p_pic->i_refcount > 0 ) p_pic->i_refcount--;
}
2296

2297 2298
static picture_t *video_new_buffer( vlc_object_t *p_this, picture_t **pp_ring,
                                    sout_stream_sys_t *p_sys )
2299
{
2300
    decoder_t *p_dec = (decoder_t *)p_this;
2301
    sout_stream_video_t *p_video = p_sys->p_video;
2302 2303
    picture_t *p_pic;
    int i;
2304

2305 2306 2307
    /* Find an empty space in the picture ring buffer */
    for( i = 0; i < PICTURE_RING_SIZE; i++ )
    {
2308
        if( pp_ring[i] != NULL && pp_ring[i]->i_status == DESTROYED_PICTURE )
2309 2310 2311 2312 2313 2314 2315
        {
            pp_ring[i]->i_status = RESERVED_PICTURE;
            return pp_ring[i];
        }
    }
    for( i = 0; i < PICTURE_RING_SIZE; i++ )
    {
2316
        if( pp_ring[i] == NULL ) break;
2317 2318
    }

2319
    if( i == PICTURE_RING_SIZE && p_video->i_threads >= 1 )
2320
    {
2321
        int i_first_pic = p_video->i_first_pic;
2322

2323
        if( p_video->i_first_pic != p_video->i_last_pic )
2324 2325
        {
            /* Encoder still has stuff to encode, wait to clear-up the list */
2326
            while( p_video->i_first_pic == i_first_pic )
2327
                msleep( 5000 );
2328 2329 2330 2331 2332
        }

        /* Find an empty space in the picture ring buffer */
        for( i = 0; i < PICTURE_RING_SIZE; i++ )
        {
2333
            if( pp_ring[i] != NULL && pp_ring[i]->i_status == DESTROYED_PICTURE )
2334 2335 2336 2337 2338 2339 2340
            {
                pp_ring[i]->i_status = RESERVED_PICTURE;
                return pp_ring[i];
            }
        }
        for( i = 0; i < PICTURE_RING_SIZE; i++ )
        {
2341
            if( pp_ring[i] == NULL ) break;
2342 2343 2344
        }
    }

2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357
    if( i == PICTURE_RING_SIZE )
    {
        msg_Err( p_this, "decoder/filter is leaking pictures, "
                 "resetting its ring buffer" );

        for( i = 0; i < PICTURE_RING_SIZE; i++ )
        {
            pp_ring[i]->pf_release( pp_ring[i] );
        }

        i = 0;
    }

2358
    p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec;
2359 2360 2361 2362
    p_pic = picture_New( p_dec->fmt_out.video.i_chroma,
                         p_dec->fmt_out.video.i_width,
                         p_dec->fmt_out.video.i_height,
                         p_dec->fmt_out.video.i_aspect );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2363
    if( !p_pic ) return NULL;
2364
    p_pic->p_sys = calloc( 1, sizeof(picture_sys_t) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2365 2366
    if( !p_pic->p_sys )
    {
2367
        picture_Release( p_pic );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2368 2369
        return NULL;
    }
2370 2371
    p_pic->pf_release = video_release_buffer;
    p_pic->i_refcount = 0;
2372 2373

    pp_ring[i] = p_pic;
2374 2375 2376
    return p_pic;
}

2377 2378 2379
static picture_t *video_new_buffer_decoder( decoder_t *p_dec )
{
    return video_new_buffer( VLC_OBJECT(p_dec),
2380
                             p_dec->p_owner->pp_pics, p_dec->p_owner->p_sys );
2381 2382 2383 2384 2385
}

static picture_t *video_new_buffer_filter( filter_t *p_filter )
{
    return video_new_buffer( VLC_OBJECT(p_filter),
2386 2387
                             p_filter->p_owner->pp_pics,
                             p_filter->p_owner->p_sys );
2388 2389 2390
}

static void video_del_buffer( vlc_object_t *p_this, picture_t *p_pic )
2391
{
Rafaël Carré's avatar
Rafaël Carré committed
2392 2393 2394
    VLC_UNUSED(p_this);
    if( p_pic )
    {
2395
        free( p_pic->p_q );
Rafaël Carré's avatar
Rafaël Carré committed
2396 2397 2398 2399
        free( p_pic->p_data_orig );
        free( p_pic->p_sys );
        free( p_pic );
    }
2400 2401
}

2402 2403
static void video_del_buffer_decoder( decoder_t *p_decoder, picture_t *p_pic )
{
Rafaël Carré's avatar
Rafaël Carré committed
2404
    VLC_UNUSED(p_decoder);
2405 2406
    p_pic->i_refcount = 0;
    p_pic->i_status = DESTROYED_PICTURE;
2407
    picture_CleanupQuant( p_pic );
2408 2409 2410 2411
}

static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic )
{
Rafaël Carré's avatar
Rafaël Carré committed
2412
    VLC_UNUSED(p_filter);
2413 2414
    p_pic->i_refcount = 0;
    p_pic->i_status = DESTROYED_PICTURE;
2415
    picture_CleanupQuant( p_pic );
2416 2417 2418
}

static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
2419
{
Rafaël Carré's avatar
Rafaël Carré committed
2420
    VLC_UNUSED(p_dec);
2421 2422 2423
    p_pic->i_refcount++;
}

2424
static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic )
2425
{
Rafaël Carré's avatar
Rafaël Carré committed
2426
    VLC_UNUSED(p_dec);
2427
    video_release_buffer( p_pic );
Gildas Bazin's avatar
 
Gildas Bazin committed
2428
}
2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439

/*
 * SPU
 */
static subpicture_t *spu_new_buffer( decoder_t * );
static void spu_del_buffer( decoder_t *, subpicture_t * );

static int transcode_spu_new( sout_stream_t *p_stream, sout_stream_id_t *id )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;

2440 2441 2442
    /*
     * Open decoder
     */
2443

2444
    /* Initialization of decoder structures */
2445
    id->p_decoder->pf_decode_sub = NULL;
2446 2447
    id->p_decoder->pf_spu_buffer_new = spu_new_buffer;
    id->p_decoder->pf_spu_buffer_del = spu_del_buffer;
2448
    id->p_decoder->p_owner = (decoder_owner_sys_t *)p_stream;
2449
    /* id->p_decoder->p_cfg = p_sys->p_spu_cfg; */
2450 2451

    id->p_decoder->p_module =
2452
        module_need( id->p_decoder, "decoder", "$codec", false );
2453 2454 2455

    if( !id->p_decoder->p_module )
    {
2456
        msg_Err( p_stream, "cannot find spu decoder" );
2457 2458 2459 2460 2461
        return VLC_EGENERIC;
    }

    if( !p_sys->b_soverlay )
    {
2462
        /* Open encoder */
2463
        /* Initialization of encoder format structures */
2464 2465
        es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat,
                        id->p_decoder->fmt_in.i_codec );
2466 2467 2468 2469

        id->p_encoder->p_cfg = p_sys->p_spu_cfg;

        id->p_encoder->p_module =
2470
            module_need( id->p_encoder, "encoder", p_sys->psz_senc, true );
2471 2472 2473

        if( !id->p_encoder->p_module )
        {
2474
            module_unneed( id->p_decoder, id->p_decoder->p_module );
2475
            msg_Err( p_stream, "cannot find spu encoder (%s)", p_sys->psz_senc );
2476 2477 2478
            return VLC_EGENERIC;
        }
    }
2479 2480

    if( !p_sys->p_spu )
2481
    {
2482 2483
        p_sys->p_spu = spu_Create( p_stream );
        spu_Init( p_sys->p_spu );
2484 2485 2486 2487 2488
    }

    return VLC_SUCCESS;
}

Rafaël Carré's avatar
Rafaël Carré committed
2489
static void transcode_spu_close( sout_stream_id_t *id)
2490 2491 2492
{
    /* Close decoder */
    if( id->p_decoder->p_module )
2493
        module_unneed( id->p_decoder, id->p_decoder->p_module );
2494 2495
    if( id->p_decoder->p_description )
        vlc_meta_Delete( id->p_decoder->p_description );
2496 2497

    /* Close encoder */
2498
    if( id->p_encoder->p_module )
2499
        module_unneed( id->p_encoder, id->p_encoder->p_module );
2500 2501 2502 2503 2504 2505 2506 2507
}

static int transcode_spu_process( sout_stream_t *p_stream,
                                  sout_stream_id_t *id,
                                  block_t *in, block_t **out )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    subpicture_t *p_subpic;
2508
    *out = NULL;
2509 2510

    p_subpic = id->p_decoder->pf_decode_sub( id->p_decoder, &in );
2511 2512 2513 2514
    if( !p_subpic )
        return VLC_EGENERIC;

    sout_UpdateStatistic( p_stream->p_sout, SOUT_STATISTIC_DECODED_SUBTITLE, 1 );
2515

2516
    if( p_sys->b_master_sync && p_sys->i_master_drift )
2517
    {
2518 2519
        p_subpic->i_start -= p_sys->i_master_drift;
        if( p_subpic->i_stop ) p_subpic->i_stop -= p_sys->i_master_drift;
2520 2521
    }

2522 2523 2524 2525 2526
    if( p_sys->b_soverlay )
    {
        spu_DisplaySubpicture( p_sys->p_spu, p_subpic );
    }
    else
2527 2528
    {
        block_t *p_block;
2529

2530
        p_block = id->p_encoder->pf_encode_sub( id->p_encoder, p_subpic );
2531 2532 2533 2534 2535 2536
        spu_del_buffer( id->p_decoder, p_subpic );
        if( p_block )
        {
            block_ChainAppend( out, p_block );
            return VLC_SUCCESS;
        }
2537
    }
2538

2539 2540 2541 2542 2543
    return VLC_EGENERIC;
}

static subpicture_t *spu_new_buffer( decoder_t *p_dec )
{
2544 2545
    VLC_UNUSED( p_dec );
    return subpicture_New();
2546 2547 2548 2549
}

static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic )
{
2550 2551
    VLC_UNUSED( p_dec );
    subpicture_Delete( p_subpic );
2552
}
2553 2554 2555 2556 2557 2558 2559

/*
 * OSD menu
 */
static int transcode_osd_new( sout_stream_t *p_stream, sout_stream_id_t *id )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
2560

2561 2562
    id->p_decoder->fmt_in.i_cat = SPU_ES;
    id->p_encoder->fmt_out.psz_language = strdup( "osd" );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2563

2564 2565 2566
    if( p_sys->i_osdcodec != 0 || p_sys->psz_osdenc )
    {
        msg_Dbg( p_stream, "creating osdmenu transcoding from fcc=`%4.4s' "
2567
                 "to fcc=`%4.4s'", (char*)&id->p_encoder->fmt_out.i_codec,
2568 2569 2570 2571
                 (char*)&p_sys->i_osdcodec );

        /* Complete destination format */
        id->p_encoder->fmt_out.i_codec = p_sys->i_osdcodec;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2572

2573
        /* Open encoder */
2574 2575 2576
        es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat,
                        VLC_FOURCC('Y','U','V','A') );
        id->p_encoder->fmt_in.psz_language = strdup( "osd" );
2577 2578 2579 2580

        id->p_encoder->p_cfg = p_sys->p_osd_cfg;

        id->p_encoder->p_module =
2581
            module_need( id->p_encoder, "encoder", p_sys->psz_osdenc, true );
2582 2583 2584

        if( !id->p_encoder->p_module )
        {
2585
            msg_Err( p_stream, "cannot find spu encoder (%s)", p_sys->psz_osdenc );
2586 2587
            goto error;
        }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2588

2589
        /* open output stream */
2590
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_encoder->fmt_out );
2591
        id->b_transcode = true;
2592 2593 2594 2595 2596 2597

        if( !id->id ) goto error;
    }
    else
    {
        msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')",
2598
                 (char*)&id->p_decoder->fmt_out.i_codec );
2599
        id->id = sout_StreamIdAdd( p_sys->p_out, &id->p_decoder->fmt_out );
2600
        id->b_transcode = false;
2601 2602 2603 2604 2605 2606 2607

        if( !id->id ) goto error;
    }

    if( !p_sys->p_spu )
    {
        p_sys->p_spu = spu_Create( p_stream );
2608
        spu_Init( p_sys->p_spu );
2609
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2610

2611
    return VLC_SUCCESS;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2612

2613 2614 2615
 error:
    msg_Err( p_stream, "starting osd encoding thread failed" );
    if( id->p_encoder->p_module )
2616
            module_unneed( id->p_encoder, id->p_encoder->p_module );
2617
    p_sys->b_osd = false;
2618 2619
    return VLC_EGENERIC;
}
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2620

2621
static void transcode_osd_close( sout_stream_t *p_stream, sout_stream_id_t *id)
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2622
{
2623
    sout_stream_sys_t *p_sys = p_stream->p_sys;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2624

2625
    /* Close encoder */
2626
    if( id )
2627 2628
    {
        if( id->p_encoder->p_module )
2629
            module_unneed( id->p_encoder, id->p_encoder->p_module );
2630
    }
2631
    p_sys->b_osd = false;
2632 2633 2634 2635 2636 2637 2638 2639
}

static int transcode_osd_process( sout_stream_t *p_stream,
                                  sout_stream_id_t *id,
                                  block_t *in, block_t **out )
{
    sout_stream_sys_t *p_sys = p_stream->p_sys;
    subpicture_t *p_subpic = NULL;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2640

2641 2642 2643
    /* Check if we have a subpicture to send */
    if( p_sys->p_spu && in->i_dts > 0)
    {
2644
        p_subpic = spu_SortSubpicturesNew( p_sys->p_spu, in->i_dts, false );
2645 2646 2647 2648 2649 2650 2651
    }
    else
    {
        msg_Warn( p_stream, "spu channel not initialized, doing it now" );
        if( !p_sys->p_spu )
        {
            p_sys->p_spu = spu_Create( p_stream );
2652
            spu_Init( p_sys->p_spu );
2653 2654
        }
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2655

2656 2657 2658
    if( p_subpic )
    {
        block_t *p_block = NULL;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2659

2660 2661 2662 2663 2664
        if( p_sys->b_master_sync && p_sys->i_master_drift )
        {
            p_subpic->i_start -= p_sys->i_master_drift;
            if( p_subpic->i_stop ) p_subpic->i_stop -= p_sys->i_master_drift;
        }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2665

2666
        p_block = id->p_encoder->pf_encode_sub( id->p_encoder, p_subpic );
2667
        subpicture_Delete( p_subpic );
2668 2669 2670 2671
        if( p_block )
        {
            p_block->i_dts = p_block->i_pts = in->i_dts;
            block_ChainAppend( out, p_block );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
2672
            return VLC_SUCCESS;
2673 2674 2675 2676
        }
    }
    return VLC_EGENERIC;
}