sout.cpp 20.9 KB
Newer Older
Clément Stenac's avatar
Clément Stenac committed
1
/*****************************************************************************
2
 * sout.cpp : Stream output dialog ( old-style )
Clément Stenac's avatar
Clément Stenac committed
3
 ****************************************************************************
4
 * Copyright (C) 2007-2008 the VideoLAN team
5 6 7
 * Copyright (C) 2007 Société des arts technologiques
 * Copyright (C) 2007 Savoir-faire Linux
 *
8
 * $Id$
Clément Stenac's avatar
Clément Stenac committed
9 10
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
11 12
 *          Jean-Baptiste Kempf <jb@videolan.org>
 *          Jean-François Massol <jf.massol -at- gmail.com>
13
 *          Pierre-Luc Beaudoin <pierre-luc.beaudoin@savoirfairelinux.com>
Clément Stenac's avatar
Clément Stenac committed
14 15 16 17
 *
 * 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
18
 * ( at your option ) any later version.
Clément Stenac's avatar
Clément Stenac committed
19 20 21 22 23 24 25 26 27 28
 *
 * 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
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
29

30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
Clément Stenac's avatar
Clément Stenac committed
33 34 35

#include "dialogs/sout.hpp"

36
#include <QString>
Clément Stenac's avatar
Clément Stenac committed
37 38
#include <QFileDialog>

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
struct streaming_account_t
{
    char *psz_username; /*< username of account */
    char *psz_password; /*< password of account */
};

struct sout_gui_descr_t
{
    /* Access types */
    bool b_local;   /*< local access module */
    bool b_file;    /*< file access module */
    bool b_http;    /*< http access module */
    bool b_mms;     /*< mms access module */
    bool b_rtp;     /*< rtp access module */
    bool b_udp;     /*< udp access module */
    bool b_dump;    /*< dump access module */
    bool b_icecast; /*< icecast access module */

    char *psz_file;     /*< filename */
    char *psz_http;     /*< HTTP servername or ipaddress */
    char *psz_mms;      /*< MMS servername or ipaddress */
    char *psz_rtp;      /*< RTP servername or ipaddress */
    char *psz_udp;      /*< UDP servername or ipaddress */
    char *psz_icecast;  /*< Icecast servername or ipaddress*/

    int32_t i_http;     /*< http port number */
    int32_t i_mms;      /*< mms port number */
    int32_t i_rtp;      /*< rtp port number */
67 68
    int32_t i_rtp_audio;      /*< rtp port number */
    int32_t i_rtp_video;      /*< rtp port number */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    int32_t i_udp;      /*< udp port number */
    int32_t i_icecast;  /*< icecast port number */

    /* Mux */
    char *psz_mux;      /*< name of muxer to use in streaming */

    /* Transcode */
    bool b_soverlay; /*< enable burning overlay in the video */
    char *psz_vcodec;   /*< video codec to use in transcoding */
    char *psz_acodec;   /*< audio codec to use in transcoding */
    char *psz_scodec;   /*< subtitle codec to use in transcoding */
    int32_t i_vb;       /*< video bitrate to use in transcoding */
    int32_t i_ab;       /*< audio bitrate to use in transcoding */
    int32_t i_channels; /*< number of audio channels to use in transcoding */
    float f_scale;      /*< scaling factor to use in transcoding */

    /* Misc */
    bool b_sap;   /*< send SAP announcement */
    bool b_all_es;/*< send all elementary streams from source stream */
    char *psz_group;    /*< SAP Group name */
    char *psz_name;     /*< SAP name */
    int32_t i_ttl;      /*< Time To Live (TTL) for network traversal */

    /* Icecast */
    char *psz_icecast_mountpoint;/*< path to Icecast mountpoint */
    struct streaming_account_t sa_icecast;  /*< Icecast account information */
};

97 98
SoutDialog* SoutDialog::instance = NULL;

99 100
SoutDialog::SoutDialog( QWidget *parent, intf_thread_t *_p_intf,
                     bool _transcode_only ) : QVLCDialog( parent,  _p_intf )
Clément Stenac's avatar
Clément Stenac committed
101
{
102
    setWindowTitle( qtr( "Stream Output" ) );
103

104 105
    b_transcode_only = _transcode_only;

Clément Stenac's avatar
Clément Stenac committed
106
    /* UI stuff */
107
    ui.setupUi( this );
108

109 110
    changeUDPandRTPmess( false );

111 112 113 114 115 116 117 118
/* ADD HERE for new profiles */
#define ADD_PROFILE( name, shortname ) ui.profileBox->addItem( qtr( name ), QVariant( QString( shortname ) ) );
    ADD_PROFILE( "Custom" , "Custom" )
    ADD_PROFILE( "IPod (mp4/aac)", "IPod" )
    ADD_PROFILE( "XBox", "XBox" )
    ADD_PROFILE( "Windows (wmv/asf)", "Windows" )
    ADD_PROFILE( "PSP", "PSP")
    ADD_PROFILE( "GSM", "GSM" )
119

120
#define ADD_VCODEC( name, fourcc ) ui.vCodecBox->addItem( name, QVariant( fourcc ) );
121 122 123 124
    ADD_VCODEC( "MPEG-1", "mp1v" )
    ADD_VCODEC( "MPEG-2", "mp2v" )
    ADD_VCODEC( "MPEG-4", "mp4v" )
    ADD_VCODEC( "DIVX 1" , "DIV1" )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
125 126
    ADD_VCODEC( "DIVX 2" , "DIV2" )
    ADD_VCODEC( "DIVX 3" , "DIV3" )
127 128 129 130 131 132 133
    ADD_VCODEC( "H-263", "H263" )
    ADD_VCODEC( "H-264", "h264" )
    ADD_VCODEC( "WMV1", "WMV1" )
    ADD_VCODEC( "WMV2" , "WMV2" )
    ADD_VCODEC( "M-JPEG", "MJPG" )
    ADD_VCODEC( "Theora", "theo" )

134
#define ADD_ACODEC( name, fourcc ) ui.aCodecBox->addItem( name, QVariant( fourcc ) );
135 136
    ADD_ACODEC( "MPEG Audio", "mpga" )
    ADD_ACODEC( "MP3", "mp3" )
137 138
    ADD_ACODEC( "MPEG 4 Audio ( AAC )", "mp4a" )
    ADD_ACODEC( "A52/AC-3", "a52" )
139 140 141 142 143 144
    ADD_ACODEC( "Vorbis", "vorb" )
    ADD_ACODEC( "Flac", "flac" )
    ADD_ACODEC( "Speex", "spx" )
    ADD_ACODEC( "WAV", "s16l" )
    ADD_ACODEC( "WMA", "wma" )

145
#define ADD_SCALING( factor ) ui.vScaleBox->addItem( factor );
146 147 148 149 150 151 152 153 154
    ADD_SCALING( "0.25" )
    ADD_SCALING( "0.5" )
    ADD_SCALING( "0.75" )
    ADD_SCALING( "1" )
    ADD_SCALING( "1.25" )
    ADD_SCALING( "1.5" )
    ADD_SCALING( "1.75" )
    ADD_SCALING( "2" )

155 156 157 158
    ui.mrlEdit->setToolTip ( qtr( "Stream output string.\n"
                "This is automatically generated "
                 "when you change the above settings,\n"
                 "but you can update it manually." ) ) ;
159 160

//     /* Connect everything to the updateMRL function */
161
 #define CB( x ) CONNECT( ui.x, toggled( bool ), this, updateMRL() );
162 163 164
 #define CT( x ) CONNECT( ui.x, textChanged( const QString ), this, updateMRL() );
 #define CS( x ) CONNECT( ui.x, valueChanged( int ), this, updateMRL() );
 #define CC( x ) CONNECT( ui.x, currentIndexChanged( int ), this, updateMRL() );
165
    /* Output */
166 167 168 169
    CB( fileOutput ); CB( HTTPOutput ); CB( localOutput );
    CB( RTPOutput ); CB( MMSHOutput ); CB( rawInput ); CB( UDPOutput );
    CT( fileEdit ); CT( HTTPEdit ); CT( RTPEdit ); CT( MMSHEdit ); CT( UDPEdit );
    CT( IcecastEdit ); CT( IcecastMountpointEdit ); CT( IcecastNamePassEdit );
170 171
    CS( HTTPPort ); CS( RTPPort ); CS( RTPPort2 ); CS( MMSHPort ); CS( UDPPort );
    /* Transcode */
172 173
    CC( vCodecBox ); CC( subsCodecBox ); CC( aCodecBox ) ;
    CB( transcodeVideo ); CB( transcodeAudio ); CB( transcodeSubs );
174
    /*   CB( sOverlay ); */
175
    CS( vBitrateSpin ); CS( aBitrateSpin ); CS( aChannelsSpin ); CC( vScaleBox );
176
    /* Mux */
177 178
    CB( PSMux ); CB( TSMux ); CB( MPEG1Mux ); CB( OggMux ); CB( ASFMux );
    CB( MP4Mux ); CB( MOVMux ); CB( WAVMux ); CB( RAWMux ); CB( FLVMux );
179
    /* Misc */
180
    CB( soutAll ); CS( ttl ); CT( sapName ); CT( sapGroup );
181

182 183
    CONNECT( ui.profileBox, activated( const QString & ), this, setOptions() );
    CONNECT( ui.fileSelectButton, clicked() , this, fileBrowse()  );
184 185 186 187 188 189
    CONNECT( ui.transcodeVideo, toggled( bool ),
            this, setVTranscodeOptions( bool ) );
    CONNECT( ui.transcodeAudio, toggled( bool ),
            this, setATranscodeOptions( bool ) );
    CONNECT( ui.transcodeSubs, toggled( bool ),
            this, setSTranscodeOptions( bool ) );
190
    CONNECT( ui.rawInput, toggled( bool ), this, setRawOptions( bool ) );
191

192
    okButton = new QPushButton( qtr( "&Stream" ) );
193 194 195 196 197 198
    QPushButton *cancelButton = new QPushButton( qtr( "&Cancel" ) );

    okButton->setDefault( true );
    ui.acceptButtonBox->addButton( okButton, QDialogButtonBox::AcceptRole );
    ui.acceptButtonBox->addButton( cancelButton, QDialogButtonBox::RejectRole );

199 200
    BUTTONACT( okButton, ok() );
    BUTTONACT( cancelButton, cancel() );
201

202
    if( b_transcode_only ) toggleSout();
203

204 205 206
    CONNECT( ui.UDPOutput, toggled( bool ), this, changeUDPandRTPmess( bool ) );
    CONNECT( ui.RTPOutput, clicked(bool), this, RTPtoggled( bool ) );
}
Clément Stenac's avatar
Clément Stenac committed
207 208

void SoutDialog::fileBrowse()
209
{
210
    QString fileName = QFileDialog::getSaveFileName( this, qtr( "Save file" ), "",
211 212
        qtr( "Containers (*.ps *.ts *.mpg *.ogg *.asf *.mp4 *.mov *.wav *.raw *.flv)" ) );
    ui.fileEdit->setText( fileName );
Clément Stenac's avatar
Clément Stenac committed
213 214 215
    updateMRL();
}

216
void SoutDialog::setVTranscodeOptions( bool b_trans )
217
{
218 219 220 221
    ui.vCodecLabel->setEnabled( b_trans );
    ui.vCodecBox->setEnabled( b_trans );
    ui.vBitrateLabel->setEnabled( b_trans );
    ui.vBitrateSpin->setEnabled( b_trans );
222
    ui.vScaleLabel->setEnabled( b_trans );
223
    ui.vScaleBox->setEnabled( b_trans );
224 225
}

226
void SoutDialog::setATranscodeOptions( bool b_trans )
227
{
228 229 230 231 232 233
    ui.aCodecLabel->setEnabled( b_trans );
    ui.aCodecBox->setEnabled( b_trans );
    ui.aBitrateLabel->setEnabled( b_trans );
    ui.aBitrateSpin->setEnabled( b_trans );
    ui.aChannelsLabel->setEnabled( b_trans );
    ui.aChannelsSpin->setEnabled( b_trans );
234 235
}

236
void SoutDialog::setSTranscodeOptions( bool b_trans )
237
{
238 239
    ui.subsCodecBox->setEnabled( b_trans );
    ui.subsOverlay->setEnabled( b_trans );
240 241
}

242
void SoutDialog::setRawOptions( bool b_raw )
243
{
244
    if( b_raw )
245 246 247
        ui.tabWidget->setDisabled( true );
    else
        SoutDialog::setOptions();
248 249
}

250 251
void SoutDialog::setOptions()
{
252 253
    QString profileString =
        ui.profileBox->itemData( ui.profileBox->currentIndex() ).toString();
254 255 256 257 258 259 260 261
    msg_Dbg( p_intf, "Profile Used: %s",  qta( profileString ));
    int index;

#define setProfile( muxName, hasVideo, vCodecName, hasAudio, aCodecName ) \
    { \
        ui.muxName ##Mux->setChecked( true ); \
        \
        ui.transcodeAudio->setChecked( hasAudio ); \
262
        index = ui.aCodecBox->findText( vCodecName );  \
263 264 265
        if( index >= 0 ) ui.aCodecBox->setCurrentIndex( index ); \
        \
        ui.transcodeVideo->setChecked( hasVideo ); \
266
        index = ui.aCodecBox->findText( vCodecName );  \
267
        if( index >=0 ) ui.vCodecBox->setCurrentIndex( index ); \
268
    }
269 270

    /* ADD HERE the profiles you want and need */
271
    /* FIXME */
272 273 274 275 276 277 278 279 280 281 282
    if( profileString == "IPod" ) setProfile( MP4, true, "mp4a", true, "mp4v" )
    else if( profileString == "XBox" ) setProfile( ASF, true, "wma", true, "WMV2" )

        /* If the profile is not a custom one, then disable the tabWidget */
        if ( profileString == "Custom" )
            ui.tabWidget->setEnabled( true );
        else
            ui.tabWidget->setDisabled( true );

    /* Update the MRL !! */
    updateMRL();
283 284
}

285
//FIXME
286 287
void SoutDialog::toggleSout()
{
288
    //Toggle all the streaming options.
289 290 291 292 293
    TOGGLEV( ui.HTTPOutput ) ; TOGGLEV( ui.RTPOutput ) ; TOGGLEV( ui.MMSHOutput ) ; TOGGLEV( ui.UDPOutput ) ;
    TOGGLEV( ui.HTTPEdit ) ; TOGGLEV( ui.RTPEdit ) ; TOGGLEV( ui.MMSHEdit ) ; TOGGLEV( ui.UDPEdit ) ;
    TOGGLEV( ui.HTTPLabel ) ; TOGGLEV( ui.RTPLabel ) ; TOGGLEV( ui.MMSHLabel ) ; TOGGLEV( ui.UDPLabel ) ;
    TOGGLEV( ui.HTTPPortLabel ) ; TOGGLEV( ui.RTPPortLabel ) ; TOGGLEV( ui.MMSHPortLabel ) ; TOGGLEV( ui.UDPPortLabel )
    TOGGLEV( ui.HTTPPort ) ; TOGGLEV( ui.RTPPort ) ; TOGGLEV( ui.MMSHPort ) ; TOGGLEV( ui.UDPPort ) ;
294 295 296 297

    TOGGLEV( ui.sap ); TOGGLEV( ui.sapName );
    TOGGLEV( ui.sapGroup ); TOGGLEV( ui.sapGroupLabel );
    TOGGLEV( ui.ttlLabel ); TOGGLEV( ui.ttl );
298 299 300 301

    if( b_transcode_only ) okButton->setText( "&Save" );
    else okButton->setText( "&Stream" );

302
    updateGeometry();
303 304
}

305 306 307 308 309 310 311 312
void SoutDialog::changeUDPandRTPmess( bool b_udp )
{
    ui.RTPEdit->setVisible( !b_udp );
    ui.RTPLabel->setVisible( !b_udp );
    ui.RTPPort->setVisible( !b_udp );
    ui.RTPPortLabel->setVisible( !b_udp );
    ui.UDPEdit->setVisible( b_udp );
    ui.UDPLabel->setVisible( b_udp );
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
    ui.UDPPortLabel->setText( b_udp ? qtr( "Port:") : qtr( "Audio Port:" ) );
    ui.RTPPort2->setVisible( !b_udp );
    ui.RTPPortLabel2->setVisible( !b_udp );
}

void SoutDialog::RTPtoggled( bool b_en )
{
    if( !b_en )
    {
        if( ui.RTPPort->value() == ui.UDPPort->value() )
        {
            ui.UDPPort->setValue( ui.UDPPort->value() + 1 );
        }

        while( ui.RTPPort2->value() == ui.UDPPort->value() ||
                ui.RTPPort2->value() == ui.RTPPort->value() )
        {
            ui.RTPPort2->setValue( ui.RTPPort2->value() + 1 );
        }
    }
    ui.sap->setEnabled( b_en );
    ui.RTPLabel->setEnabled( b_en );
    ui.RTPEdit->setEnabled( b_en );
    ui.UDPOutput->setEnabled( b_en );
    ui.UDPPort->setEnabled( b_en );
    ui.UDPPortLabel->setEnabled( b_en );
    ui.RTPPort2->setEnabled( b_en );
    ui.RTPPortLabel2->setEnabled( b_en );
341 342
}

Clément Stenac's avatar
Clément Stenac committed
343 344
void SoutDialog::ok()
{
345 346
    mrl = ui.mrlEdit->text();
    accept();
Clément Stenac's avatar
Clément Stenac committed
347
}
348

Clément Stenac's avatar
Clément Stenac committed
349 350
void SoutDialog::cancel()
{
351
    mrl.clear();
352
    reject();
Clément Stenac's avatar
Clément Stenac committed
353 354 355 356
}

void SoutDialog::updateMRL()
{
357 358
    sout_gui_descr_t sout;
    memset( &sout, 0, sizeof( sout_gui_descr_t ) );
359
    unsigned int counter = 0;
360 361 362 363 364

    sout.b_local = ui.localOutput->isChecked();
    sout.b_file = ui.fileOutput->isChecked();
    sout.b_http = ui.HTTPOutput->isChecked();
    sout.b_mms = ui.MMSHOutput->isChecked();
365
    sout.b_icecast = ui.IcecastOutput->isChecked();
366
    sout.b_rtp = ui.RTPOutput->isChecked();
367
    sout.b_udp = ui.UDPOutput->isChecked();
368 369
    sout.b_sap = ui.sap->isChecked();
    sout.b_all_es = ui.soutAll->isChecked();
370 371 372
    sout.psz_vcodec = strdup( qtu( ui.vCodecBox->itemData( ui.vCodecBox->currentIndex() ).toString() ) );
    sout.psz_acodec = strdup( qtu( ui.aCodecBox->itemData( ui.aCodecBox->currentIndex() ).toString() ) );
    sout.psz_scodec = strdup( qtu( ui.subsCodecBox->itemData( ui.subsCodecBox->currentIndex() ).toString() ) );
373 374 375
    sout.psz_file = strdup( qtu( ui.fileEdit->text() ) );
    sout.psz_http = strdup( qtu( ui.HTTPEdit->text() ) );
    sout.psz_mms = strdup( qtu( ui.MMSHEdit->text() ) );
376
    sout.psz_rtp = strdup( qtu( ui.RTPEdit->text() ) );
377
    sout.psz_udp = strdup( qtu( ui.UDPEdit->text() ) );
378 379 380 381
    sout.psz_icecast = strdup( qtu( ui.IcecastEdit->text() ) );
    sout.sa_icecast.psz_username = strdup( qtu( ui.IcecastNamePassEdit->text() ) );
    sout.sa_icecast.psz_password = strdup( qtu( ui.IcecastNamePassEdit->text() ) );
    sout.psz_icecast_mountpoint = strdup( qtu( ui.IcecastMountpointEdit->text() ) );
382 383
    sout.i_http = ui.HTTPPort->value();
    sout.i_mms = ui.MMSHPort->value();
384
    sout.i_rtp = ui.RTPPort->value();
385 386
    sout.i_rtp_audio = sout.i_udp = ui.UDPPort->value();
    sout.i_rtp_video = ui.RTPPort2->value();
387
    sout.i_icecast = ui.IcecastPort->value();
388 389 390 391
    sout.i_ab = ui.aBitrateSpin->value();
    sout.i_vb = ui.vBitrateSpin->value();
    sout.i_channels = ui.aChannelsSpin->value();
    sout.f_scale = atof( qta( ui.vScaleBox->currentText() ) );
392 393 394
    sout.psz_group = strdup( qtu( ui.sapGroup->text() ) );
    sout.psz_name = strdup( qtu( ui.sapName->text() ) );

395 396 397 398 399 400
    if ( sout.b_local ) counter++ ;
    if ( sout.b_file ) counter++ ;
    if ( sout.b_http ) counter++ ;
    if ( sout.b_mms ) counter++ ;
    if ( sout.b_rtp ) counter++ ;
    if ( sout.b_udp ) counter ++;
401
    if ( sout.b_icecast ) counter ++;
402

403
#define SMUX( x, txt ) if( ui.x->isChecked() ) sout.psz_mux = strdup( txt );
404 405 406 407 408 409 410 411 412 413
    SMUX( PSMux, "ps" );
    SMUX( TSMux, "ts" );
    SMUX( MPEG1Mux, "mpeg" );
    SMUX( OggMux, "ogg" );
    SMUX( ASFMux, "asf" );
    SMUX( MP4Mux, "mp4" );
    SMUX( MOVMux, "mov" );
    SMUX( WAVMux, "wav" );
    SMUX( RAWMux, "raw" );
    SMUX( FLVMux, "flv" );
414
    SMUX( MKVMux, "mkv" );
415 416 417 418

    bool trans = false;
    bool more = false;

419
    if ( ui.transcodeVideo->isChecked() || ui.transcodeAudio->isChecked() )
420
    {
421
        if ( ui.transcodeVideo->isChecked() )
422
        {
423 424 425 426 427 428
            mrl = ":sout=#transcode{";
            mrl.append( "vcodec=" );
            mrl.append( sout.psz_vcodec );
            mrl.append( "," );
            mrl.append( "vb=" );
            mrl.append( QString::number( sout.i_vb,10 ) );
429
            mrl.append( "," );
430 431 432
            mrl.append( "scale=" );
            mrl.append( QString::number( sout.f_scale ) );
            trans = true;
433
        }
434 435

        if ( ui.transcodeAudio->isChecked() )
436
        {
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
            if ( trans )
            {
                mrl.append( "," );
            }
            else
            {
                mrl = ":sout=#transcode{";
            }
            mrl.append( "acodec=" );
            mrl.append( sout.psz_acodec );
            mrl.append( "," );
            mrl.append( "ab=" );
            mrl.append( QString::number( sout.i_ab,10 ) );
            mrl.append( "," );
            mrl.append( "channels=" );
            mrl.append( QString::number( sout.i_channels,10 ) );
            trans = true;
454
        }
455
        mrl.append( "}" );
456
    }
457

458
    /* Protocol output */
459
    if ( sout.b_local || sout.b_file || sout.b_http ||
460
         sout.b_mms || sout.b_rtp || sout.b_udp || sout.b_icecast )
461 462
    {

463 464
#define ISMORE() if ( more ) mrl.append( "," )
#define ATLEASTONE() if ( counter ) mrl.append( "dst=" )
465

466 467 468 469 470 471 472
#define CHECKMUX() \
       if( sout.psz_mux ) \
       {                  \
         mrl.append( ",mux=");\
         mrl.append( sout.psz_mux ); \
       }

473 474 475
        if ( trans )
            mrl.append( ":" );
        else
476 477
            mrl = ":sout=#";

478
        if ( counter )
479 480
            mrl.append( "duplicate{" );

481 482 483
        if ( sout.b_local )
        {
            ISMORE();
484 485
            ATLEASTONE();
            mrl.append( "display" );
486 487 488 489 490 491
            more = true;
        }

        if ( sout.b_file )
        {
            ISMORE();
492 493
            ATLEASTONE();
            mrl.append( "std{access=file" );
494
            CHECKMUX();
495 496 497 498 499 500 501 502 503
            mrl.append( ",dst=" );
            mrl.append( sout.psz_file );
            mrl.append( "}" );
            more = true;
        }

        if ( sout.b_http )
        {
            ISMORE();
504 505
            ATLEASTONE();
            mrl.append( "std{access=http" );
506
            CHECKMUX();
507 508 509 510 511 512 513 514 515 516 517
            mrl.append( ",dst=" );
            mrl.append( sout.psz_http );
            mrl.append( ":" );
            mrl.append( QString::number( sout.i_http,10 ) );
            mrl.append( "}" );
            more = true;
        }

        if ( sout.b_mms )
        {
            ISMORE();
518 519
            ATLEASTONE();
            mrl.append( "std{access=mmsh" );
520
            CHECKMUX();
521 522 523 524 525 526 527 528
            mrl.append( ",dst=" );
            mrl.append( sout.psz_mms );
            mrl.append( ":" );
            mrl.append( QString::number( sout.i_mms,10 ) );
            mrl.append( "}" );
            more = true;
        }

529
        if ( sout.b_rtp )
530 531
        {
            ISMORE();
532 533 534 535 536 537 538 539 540 541 542 543
            ATLEASTONE();
            if ( sout.b_udp )
            {
                mrl.append( "std{access=udp" );
                CHECKMUX();
                mrl.append( ",dst=" );
                mrl.append( sout.psz_udp );
                mrl.append( ":" );
                mrl.append( QString::number( sout.i_udp,10 ) );
            }
            else
            {
544
                mrl.append( "rtp{" );
545 546 547 548 549 550 551 552 553 554 555 556 557
                mrl.append( "dst=" );
                mrl.append( sout.psz_rtp );
                CHECKMUX();
                mrl.append( ",port=" );
                mrl.append( QString::number( sout.i_rtp,10 ) );
                if( !sout.psz_mux || strncmp( sout.psz_mux, "ts", 2 ) )
                {
                    mrl.append( ",port-audio=" );
                    mrl.append( QString::number( sout.i_rtp_audio, 10 ) );
                    mrl.append( ",port-video=" );
                    mrl.append( QString::number( sout.i_rtp_video, 10 ) );
                }
            }
558

559
            /* SAP */
560 561 562 563 564 565 566 567 568 569
            if ( sout.b_sap )
            {
                mrl.append( ",sap," );
                mrl.append( "group=\"" );
                mrl.append( sout.psz_group );
                mrl.append( "\"," );
                mrl.append( "name=\"" );
                mrl.append( sout.psz_name );
                mrl.append( "\"" );
            }
570

571 572 573 574
            mrl.append( "}" );
            more = true;
        }

575 576
        if( sout.b_icecast )
        {
577 578 579 580 581 582 583 584 585 586 587 588 589
            ISMORE();
            ATLEASTONE();
            mrl.append( "std{access=shout,mux=ogg" );
            mrl.append( ",dst=" );
            mrl.append( sout.sa_icecast.psz_username );
            mrl.append( "@" );
            mrl.append( sout.psz_icecast );
            mrl.append( ":" );
            mrl.append( QString::number( sout.i_icecast, 10 ) );
            mrl.append( "/" );
            mrl.append( sout.psz_icecast_mountpoint );
            mrl.append( "}" );
            more = true;
590
        }
591

592
        if ( counter )
593 594 595 596 597
        {
            mrl.append( "}" );
        }
    }

598 599
#undef CHECKMUX

600
    if ( sout.b_all_es )
601
        mrl.append( ":sout-all" );
602 603 604 605

    ui.mrlEdit->setText( mrl );
    free( sout.psz_acodec ); free( sout.psz_vcodec ); free( sout.psz_scodec );
    free( sout.psz_file );free( sout.psz_http ); free( sout.psz_mms );
606
    free( sout.psz_rtp ); free( sout.psz_udp ); free( sout.psz_mux );
607
    free( sout.psz_name ); free( sout.psz_group );
608 609
    free( sout.psz_icecast ); free( sout.psz_icecast_mountpoint );
    free( sout.sa_icecast.psz_password ); free( sout.sa_icecast.psz_username );
610
}