sout.cpp 23.8 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

#include "dialogs/sout.hpp"
35
#include "util/qt_dirs.hpp"
Clément Stenac's avatar
Clément Stenac committed
36

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
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 67
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 */
68 69
    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
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    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 */
89
    bool b_sout_keep;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
90 91 92 93 94 95 96 97 98
    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 */
};

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
class SoutMrl
{
public:
    SoutMrl( const QString head = "")
    {
        mrl = head;
        b_first = true;
        b_has_bracket = false;
    }

    QString getMrl()
    {
        return mrl;
    }

    void begin( QString module )
    {
        if( !b_first )
            mrl += ":";
        b_first = false;

        mrl += module;
        b_has_bracket = false;
    }
    void end()
    {
        if( b_has_bracket )
            mrl += "}";
    }
    void option( const QString option, const QString value = "" )
    {
        if( !b_has_bracket )
            mrl += "{";
        else
            mrl += ",";
        b_has_bracket = true;

        mrl += option;

        if( !value.isEmpty() )
        {
            char *psz = config_StringEscape( qta(value) );
            if( psz )
            {
                QString v = QString( psz );

                mrl += "=\"" + v + "\"";

                free( psz );
            }
        }
    }
    void option( const QString name, const int i_value, const int i_precision = 10 )
    {
        option( name, QString::number( i_value, i_precision ) );
    }
    void option( const QString name, const double f_value )
    {
        option( name, QString::number( f_value ) );
    }

    void option( const QString name, const QString base, const int i_value, const int i_precision = 10 )
    {
        option( name, base + ":" + QString::number( i_value, i_precision ) );
    }

private:
    QString mrl;
    bool b_has_bracket;
    bool b_first;
};

171 172
SoutDialog* SoutDialog::instance = NULL;

173 174
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
175
{
176
    setWindowTitle( qtr( "Stream Output" ) );
177

178 179
    b_transcode_only = _transcode_only;

Clément Stenac's avatar
Clément Stenac committed
180
    /* UI stuff */
181
    ui.setupUi( this );
182

183 184
    changeUDPandRTPmess( false );

185 186 187
/* ADD HERE for new profiles */
#define ADD_PROFILE( name, shortname ) ui.profileBox->addItem( qtr( name ), QVariant( QString( shortname ) ) );
    ADD_PROFILE( "Custom" , "Custom" )
188 189 190 191 192 193 194
    ADD_PROFILE( "Ogg / Theora", "theora" )
    ADD_PROFILE( "Ogg / Vorbis", "vorbis" )
    ADD_PROFILE( "MPEG-2", "mpeg2" )
    ADD_PROFILE( "MP3", "mp3" )
    ADD_PROFILE( "MPEG-4 audio AAC", "aac" )
    ADD_PROFILE( "MPEG-4 / DivX", "mp4" )
    ADD_PROFILE( "H264", "h264" )
195 196 197 198
    ADD_PROFILE( "IPod (mp4/aac)", "IPod" )
    ADD_PROFILE( "XBox", "XBox" )
    ADD_PROFILE( "Windows (wmv/asf)", "Windows" )
    ADD_PROFILE( "PSP", "PSP")
199

200
#define ADD_VCODEC( name, fourcc ) ui.vCodecBox->addItem( name, QVariant( fourcc ) );
201 202 203 204
    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
205 206
    ADD_VCODEC( "DIVX 2" , "DIV2" )
    ADD_VCODEC( "DIVX 3" , "DIV3" )
207 208 209 210 211 212 213
    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" )

214
#define ADD_ACODEC( name, fourcc ) ui.aCodecBox->addItem( name, QVariant( fourcc ) );
215 216
    ADD_ACODEC( "MPEG Audio", "mpga" )
    ADD_ACODEC( "MP3", "mp3" )
217 218
    ADD_ACODEC( "MPEG 4 Audio ( AAC )", "mp4a" )
    ADD_ACODEC( "A52/AC-3", "a52" )
219 220 221 222 223 224
    ADD_ACODEC( "Vorbis", "vorb" )
    ADD_ACODEC( "Flac", "flac" )
    ADD_ACODEC( "Speex", "spx" )
    ADD_ACODEC( "WAV", "s16l" )
    ADD_ACODEC( "WMA", "wma" )

225
#define ADD_SCALING( factor ) ui.vScaleBox->addItem( factor );
226
    ADD_SCALING( "1" )
227 228 229 230 231 232 233 234
    ADD_SCALING( "0.25" )
    ADD_SCALING( "0.5" )
    ADD_SCALING( "0.75" )
    ADD_SCALING( "1.25" )
    ADD_SCALING( "1.5" )
    ADD_SCALING( "1.75" )
    ADD_SCALING( "2" )

235 236 237
    ui.mrlEdit->setToolTip ( qtr( "Stream output string.\n"
                "This is automatically generated "
                 "when you change the above settings,\n"
238
                 "but you can change it manually." ) ) ;
239 240

//     /* Connect everything to the updateMRL function */
241
 #define CB( x ) CONNECT( ui.x, toggled( bool ), this, updateMRL() );
242 243 244
 #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() );
245
    /* Output */
246 247 248 249
    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 );
250 251
    CS( HTTPPort ); CS( RTPPort ); CS( RTPPort2 ); CS( MMSHPort ); CS( UDPPort );
    /* Transcode */
252 253
    CC( vCodecBox ); CC( subsCodecBox ); CC( aCodecBox ) ;
    CB( transcodeVideo ); CB( transcodeAudio ); CB( transcodeSubs );
254
    /*   CB( sOverlay ); */
255
    CS( vBitrateSpin ); CS( aBitrateSpin ); CS( aChannelsSpin ); CC( vScaleBox );
256
    /* Mux */
257 258
    CB( PSMux ); CB( TSMux ); CB( MPEG1Mux ); CB( OggMux ); CB( ASFMux );
    CB( MP4Mux ); CB( MOVMux ); CB( WAVMux ); CB( RAWMux ); CB( FLVMux );
259
    /* Misc */
260
    CB( soutAll ); CB( soutKeep );  CS( ttl ); CT( sapName ); CT( sapGroup );
261

262 263
    CONNECT( ui.profileBox, activated( const QString & ), this, setOptions() );
    CONNECT( ui.fileSelectButton, clicked() , this, fileBrowse()  );
264 265 266 267 268 269
    CONNECT( ui.transcodeVideo, toggled( bool ),
            this, setVTranscodeOptions( bool ) );
    CONNECT( ui.transcodeAudio, toggled( bool ),
            this, setATranscodeOptions( bool ) );
    CONNECT( ui.transcodeSubs, toggled( bool ),
            this, setSTranscodeOptions( bool ) );
270
    CONNECT( ui.rawInput, toggled( bool ), this, setRawOptions( bool ) );
271

272
    okButton = new QPushButton( qtr( "&Stream" ) );
273 274 275 276 277 278
    QPushButton *cancelButton = new QPushButton( qtr( "&Cancel" ) );

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

279 280
    BUTTONACT( okButton, ok() );
    BUTTONACT( cancelButton, cancel() );
281

282 283
    CONNECT( ui.UDPOutput, toggled( bool ), this, changeUDPandRTPmess( bool ) );
    CONNECT( ui.RTPOutput, clicked(bool), this, RTPtoggled( bool ) );
284 285

    if( b_transcode_only ) toggleSout();
286
}
Clément Stenac's avatar
Clément Stenac committed
287 288

void SoutDialog::fileBrowse()
289
{
290 291 292
    QString fileName = QFileDialog::getSaveFileName( this, qtr( "Save file..." ),
            "", qtr( "Containers (*.ps *.ts *.mpg *.ogg *.asf *.mp4 *.mov *.wav *.raw *.flv)" ) );
    ui.fileEdit->setText( toNativeSeparators( fileName ) );
Clément Stenac's avatar
Clément Stenac committed
293 294 295
    updateMRL();
}

296
void SoutDialog::setVTranscodeOptions( bool b_trans )
297
{
298 299 300 301
    ui.vCodecLabel->setEnabled( b_trans );
    ui.vCodecBox->setEnabled( b_trans );
    ui.vBitrateLabel->setEnabled( b_trans );
    ui.vBitrateSpin->setEnabled( b_trans );
302
    ui.vScaleLabel->setEnabled( b_trans );
303
    ui.vScaleBox->setEnabled( b_trans );
304 305
}

306
void SoutDialog::setATranscodeOptions( bool b_trans )
307
{
308 309 310 311 312 313
    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 );
314 315
}

316
void SoutDialog::setSTranscodeOptions( bool b_trans )
317
{
318 319
    ui.subsCodecBox->setEnabled( b_trans );
    ui.subsOverlay->setEnabled( b_trans );
320 321
}

322
void SoutDialog::setRawOptions( bool b_raw )
323
{
324 325 326 327 328 329 330 331
    ui.localOutput->setEnabled( !b_raw );
    ui.HTTPOutput->setEnabled( !b_raw );
    ui.MMSHOutput->setEnabled( !b_raw );
    ui.UDPOutput->setEnabled( !b_raw );
    ui.RTPOutput->setEnabled( !b_raw );
    ui.IcecastOutput->setEnabled( !b_raw );
    ui.UDPRTPLabel->setEnabled( !b_raw );

332
    if( b_raw )
333 334
        ui.tabWidget->setDisabled( true );
    else
335
        setOptions();
336 337
}

338 339
void SoutDialog::setOptions()
{
340 341
    QString profileString =
        ui.profileBox->itemData( ui.profileBox->currentIndex() ).toString();
342 343 344 345 346 347 348 349
    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 ); \
350
        index = ui.aCodecBox->findData( aCodecName );  \
351 352 353
        if( index >= 0 ) ui.aCodecBox->setCurrentIndex( index ); \
        \
        ui.transcodeVideo->setChecked( hasVideo ); \
354
        index = ui.vCodecBox->findData( vCodecName );  \
355
        if( index >=0 ) ui.vCodecBox->setCurrentIndex( index ); \
356
    }
357 358

    /* ADD HERE the profiles you want and need */
359 360 361 362 363 364 365 366 367 368
    if( profileString == "IPod" ) setProfile( MP4, true, "mp4v", true, "mp4a" )
    else if( profileString == "theora" ) setProfile( Ogg, true, "theo", true, "vorb" )
    else if( profileString == "vorbis" ) setProfile( Ogg, false, "", true, "vorb" )
    else if( profileString == "mpeg2" ) setProfile( TS, true, "mp2v", true, "mpga" )
    else if( profileString == "mp3" ) setProfile( RAW, false, "", true, "mp3" )
    else if( profileString == "aac" ) setProfile( MP4, false, "", true, "mp4a" )
    else if( profileString == "mp4" ) setProfile( MP4, true, "mp4v", true, "mp4a" )
    else if( profileString == "h264" ) setProfile( TS, true, "h264", true, "mp4a" )
    else if( profileString == "XBox" ) setProfile( ASF, true, "WMV2", true, "wma" )
    else if( profileString == "Windows" ) setProfile( ASF, true, "WMV2", true, "wma" )
369
    else if( profileString == "PSP" ) setProfile( MP4, true, "mp4v", true, "mp4a" )
370 371 372 373 374 375 376 377 378

        /* 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();
379 380
}

381 382
void SoutDialog::toggleSout()
{
383
    //Toggle all the streaming options.
384 385 386 387 388
#define HIDEORSHOW(x) if( b_transcode_only ) x->hide(); else x->show();
    HIDEORSHOW( ui.HTTPOutput ) ; HIDEORSHOW( ui.RTPOutput ) ; HIDEORSHOW( ui.MMSHOutput ) ; HIDEORSHOW( ui.UDPOutput ) ;
    HIDEORSHOW( ui.HTTPEdit ) ; HIDEORSHOW( ui.RTPEdit ) ; HIDEORSHOW( ui.MMSHEdit ) ; HIDEORSHOW( ui.UDPEdit ) ;
    HIDEORSHOW( ui.HTTPLabel ) ; HIDEORSHOW( ui.RTPLabel ) ; HIDEORSHOW( ui.MMSHLabel ) ; HIDEORSHOW( ui.UDPLabel ) ;
    HIDEORSHOW( ui.HTTPPortLabel ) ; HIDEORSHOW( ui.RTPPortLabel ) ; HIDEORSHOW( ui.MMSHPortLabel ) ; HIDEORSHOW( ui.UDPPortLabel )
389
    HIDEORSHOW( ui.HTTPPort ) ; HIDEORSHOW( ui.RTPPort ) ; HIDEORSHOW( ui.MMSHPort ) ; HIDEORSHOW( ui.UDPPort ) ; HIDEORSHOW( ui.RTPPortLabel2 ); HIDEORSHOW( ui.RTPPort2 ); HIDEORSHOW( ui.UDPRTPLabel )
390 391 392 393

    HIDEORSHOW( ui.sap ); HIDEORSHOW( ui.sapName );
    HIDEORSHOW( ui.sapGroup ); HIDEORSHOW( ui.sapGroupLabel );
    HIDEORSHOW( ui.ttlLabel ); HIDEORSHOW( ui.ttl );
394
    HIDEORSHOW( ui.soutKeep );
395 396 397 398 399 400 401

    HIDEORSHOW( ui.IcecastOutput ); HIDEORSHOW( ui.IcecastEdit );
    HIDEORSHOW( ui.IcecastNamePassEdit ); HIDEORSHOW( ui.IcecastMountpointEdit );
    HIDEORSHOW( ui.IcecastPort ); HIDEORSHOW( ui.IcecastLabel );
    HIDEORSHOW( ui.IcecastPortLabel );
    HIDEORSHOW( ui.IcecastMountpointLabel ); HIDEORSHOW( ui.IcecastNameLabel );
#undef HIDEORSHOW
402 403 404 405

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

406 407
    setMinimumHeight( 500 );
    resize( width(), sizeHint().height() );
408 409
}

410 411 412 413 414 415 416 417
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 );
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
    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 );
442
    ui.UDPRTPLabel->setEnabled( b_en );
443
    ui.UDPEdit->setEnabled( b_en );
444 445 446 447
    ui.UDPPort->setEnabled( b_en );
    ui.UDPPortLabel->setEnabled( b_en );
    ui.RTPPort2->setEnabled( b_en );
    ui.RTPPortLabel2->setEnabled( b_en );
448 449
}

Clément Stenac's avatar
Clément Stenac committed
450 451
void SoutDialog::ok()
{
452 453
    mrl = ui.mrlEdit->text();
    accept();
Clément Stenac's avatar
Clément Stenac committed
454
}
455

Clément Stenac's avatar
Clément Stenac committed
456 457
void SoutDialog::cancel()
{
458
    mrl.clear();
459
    reject();
Clément Stenac's avatar
Clément Stenac committed
460 461 462 463
}

void SoutDialog::updateMRL()
{
464 465
    sout_gui_descr_t sout;
    memset( &sout, 0, sizeof( sout_gui_descr_t ) );
466
    unsigned int counter = 0;
467 468 469 470 471

    sout.b_local = ui.localOutput->isChecked();
    sout.b_file = ui.fileOutput->isChecked();
    sout.b_http = ui.HTTPOutput->isChecked();
    sout.b_mms = ui.MMSHOutput->isChecked();
472
    sout.b_icecast = ui.IcecastOutput->isChecked();
473
    sout.b_rtp = ui.RTPOutput->isChecked();
474
    sout.b_udp = ui.UDPOutput->isChecked();
475
    sout.b_dump = ui.rawInput->isChecked();
476 477
    sout.b_sap = ui.sap->isChecked();
    sout.b_all_es = ui.soutAll->isChecked();
478
    sout.b_sout_keep = ui.soutKeep->isChecked();
479 480 481
    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() ) );
482 483 484
    sout.psz_file = strdup( qtu( ui.fileEdit->text() ) );
    sout.psz_http = strdup( qtu( ui.HTTPEdit->text() ) );
    sout.psz_mms = strdup( qtu( ui.MMSHEdit->text() ) );
485
    sout.psz_rtp = strdup( qtu( ui.RTPEdit->text() ) );
486
    sout.psz_udp = strdup( qtu( ui.UDPEdit->text() ) );
487 488 489 490
    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() ) );
491 492
    sout.i_http = ui.HTTPPort->value();
    sout.i_mms = ui.MMSHPort->value();
493
    sout.i_rtp = ui.RTPPort->value();
494 495
    sout.i_rtp_audio = sout.i_udp = ui.UDPPort->value();
    sout.i_rtp_video = ui.RTPPort2->value();
496
    sout.i_icecast = ui.IcecastPort->value();
497 498 499 500
    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() ) );
501 502 503
    sout.psz_group = strdup( qtu( ui.sapGroup->text() ) );
    sout.psz_name = strdup( qtu( ui.sapName->text() ) );

504 505 506 507 508 509
    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 ++;
510
    if ( sout.b_icecast ) counter ++;
511

512
#define SMUX( x, txt ) if( ui.x->isChecked() ) sout.psz_mux = strdup( txt );
513 514
    SMUX( PSMux, "ps" );
    SMUX( TSMux, "ts" );
515
    SMUX( MPEG1Mux, "mpeg1" );
516 517 518 519 520 521 522
    SMUX( OggMux, "ogg" );
    SMUX( ASFMux, "asf" );
    SMUX( MP4Mux, "mp4" );
    SMUX( MOVMux, "mov" );
    SMUX( WAVMux, "wav" );
    SMUX( RAWMux, "raw" );
    SMUX( FLVMux, "flv" );
523
    SMUX( MKVMux, "mkv" );
524 525 526 527

    bool trans = false;
    bool more = false;

528 529
    SoutMrl smrl( ":sout=#" );

530 531
    if ( ui.transcodeVideo->isChecked() || ui.transcodeAudio->isChecked()
         && !ui.rawInput->isChecked() /*demuxdump speciality*/ )
532
    {
533 534
        smrl.begin( "transcode" );

535
        if ( ui.transcodeVideo->isChecked() )
536
        {
537 538 539
            smrl.option( "vcodec", sout.psz_vcodec );
            smrl.option( "vb", sout.i_vb );
            smrl.option( "scale", sout.f_scale );
540
            trans = true;
541
        }
542 543

        if ( ui.transcodeAudio->isChecked() )
544
        {
545 546 547
            smrl.option( "acodec", sout.psz_acodec );
            smrl.option( "ab", sout.i_ab );
            smrl.option( "channels", sout.i_channels );
548
            trans = true;
549
        }
550 551 552 553

        smrl.end();

        mrl = smrl.getMrl();
554
    }
555

556 557 558 559 560 561 562 563 564
    /* Special case for demuxdump */
    if ( sout.b_file && sout.b_dump )
    {
        mrl = ":demux=dump :demuxdump-file=";
        mrl.append( sout.psz_file );
    }
    else


565
    /* Protocol output */
566
    if ( sout.b_local || sout.b_file || sout.b_http ||
567
         sout.b_mms || sout.b_rtp || sout.b_udp || sout.b_icecast )
568
    {
569 570
        if( counter > 1 )
            smrl.begin( "duplicate" );
571

572 573 574 575 576 577
#define ADD(m) do { if( counter > 1 ) { \
                smrl.option( "dst", m.getMrl() ); \
            } else { \
                smrl.begin( m.getMrl() ); \
                smrl.end(); \
            } } while(0)
578

579 580
        if ( sout.b_local )
        {
581 582 583 584 585
            SoutMrl m;
            m.begin( "display" );
            m.end();

            ADD( m );
586 587 588 589 590
            more = true;
        }

        if ( sout.b_file )
        {
591 592 593 594 595 596 597 598 599 600
            SoutMrl m;

            m.begin( "std" );
            m.option( "access", "file" );
            if( sout.psz_mux )
                m.option( "mux", sout.psz_mux );
            m.option( "dst", sout.psz_file );
            m.end();

            ADD( m );
601 602 603 604 605
            more = true;
        }

        if ( sout.b_http )
        {
606 607 608 609 610 611 612 613 614 615
            SoutMrl m;

            m.begin( "std" );
            m.option(  "access", "http" );
            if( sout.psz_mux )
                m.option( "mux", sout.psz_mux );
            m.option( "dst", sout.psz_http, sout.i_http );
            m.end();

            ADD( m );
616 617 618 619 620
            more = true;
        }

        if ( sout.b_mms )
        {
621 622 623 624 625 626 627 628 629
            SoutMrl m;

            m.begin( "std" );
            m.option(  "access", "mmsh" );
            m.option( "mux", "asfh" );
            m.option( "dst", sout.psz_mms, sout.i_mms );
            m.end();

            ADD( m );
630 631 632
            more = true;
        }

633
        if ( sout.b_rtp )
634
        {
635
            SoutMrl m;
636 637
            if ( sout.b_udp )
            {
638 639 640 641 642
                m.begin( "std" );
                m.option(  "access", "udp" );
                if( sout.psz_mux )
                    m.option( "mux", sout.psz_mux );
                m.option( "dst", sout.psz_udp, sout.i_udp );
643 644 645
            }
            else
            {
646 647 648 649 650 651 652 653
                m.begin( "rtp" );

                if( sout.psz_rtp && *sout.psz_rtp )
                    m.option( "dst", sout.psz_rtp );
                if( sout.psz_mux )
                    m.option( "mux", sout.psz_mux );

                m.option( "port", sout.i_rtp );
654 655
                if( !sout.psz_mux || strncmp( sout.psz_mux, "ts", 2 ) )
                {
656 657
                    m.option( "port-audio", sout.i_rtp_audio );
                    m.option( "port-video", sout.i_rtp_video );
658 659
                }
            }
660

661
            /* SAP */
662 663
            if ( sout.b_sap )
            {
664 665 666
                m.option( "sap" );
                m.option( "group", sout.psz_group );
                m.option( "name", sout.psz_name );
667
            }
668

669 670
            m.end();
            ADD( m );
671 672 673
            more = true;
        }

674 675
        if( sout.b_icecast )
        {
676 677 678 679 680 681 682 683 684 685 686 687 688
            SoutMrl m;
            QString url;

            url = QString(sout.sa_icecast.psz_username) + "@" + sout.psz_icecast + ":" +
                  QString::number( sout.i_icecast, 10 ) + "/" + sout.psz_icecast_mountpoint;

            m.begin( "std" );
            m.option( "access", "shout" );
            m.option( "mux", "ogg" );
            m.option( "dst", url );
            m.end();

            ADD( m );
689
            more = true;
690
        }
691

692
        if ( counter )
693
            smrl.end();
694

695 696
        mrl = smrl.getMrl();
    }
697

698
    if ( sout.b_all_es )
699
        mrl.append( " :sout-all" );
700

701 702 703
    if ( sout.b_sout_keep )
        mrl.append( " :sout-keep" );

704 705 706
    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 );
707
    free( sout.psz_rtp ); free( sout.psz_udp ); free( sout.psz_mux );
708
    free( sout.psz_name ); free( sout.psz_group );
709 710
    free( sout.psz_icecast ); free( sout.psz_icecast_mountpoint );
    free( sout.sa_icecast.psz_password ); free( sout.sa_icecast.psz_username );
711
}