Add QMediaPlayer internal player back end (Qt5 only)

* add config and GUI support for internal player back end switching;
* make FFmpeg player disabling option consistent with other similar
  qmake options by using CONFIG;
* add a new qmake option that disables Qt Multimedia player. This is
  useful for GNU/Linux distributions where Qt WebKit and Qt Multimedia
  packages depend on different GStreamer versions and don't work
  correctly when combined in one application.

The existing FFmpeg+libao internal player back end has a relatively
low-level implementation, which is difficult to understand and improve.
There are at least 3 open internal player issues:
  1) many GNU/Linux users have to edit their libao configuration file to
     make Goldendict's internal player work (issue #412);
  2) libao's pulseaudio plugin does not support 32-bit audio, which
     means that many MediaWiki pronunciations don't work with the most
     popular GNU/Linux audio driver (issue #949);
  3) Ffmpeg::DecoderContext uses deprecated FFmpeg APIs, which causes
     compiler warnings and means that this internal player back end
     may not compile with a future FFmpeg library version (issue #978).

The Qt Multimedia back end implementation uses the highest-level
Qt audio API and is very simple.
This new back end works flawlessly on my GNU/Linux machine.
I'm not making it the default back end because I don't know how well
it will work on other platforms with different configurations.
This commit is contained in:
Igor Kushnir 2018-03-29 20:00:53 +03:00
parent d5998cabb7
commit 9aa3c44d4e
13 changed files with 309 additions and 66 deletions

View file

@ -28,7 +28,7 @@ This code has been run and tested on Windows XP/Vista/7, Ubuntu Linux, Mac OS X.
qtdeclarative5-dev libqtwebkit-dev libxtst-dev liblzo2-dev libbz2-dev \ qtdeclarative5-dev libqtwebkit-dev libxtst-dev liblzo2-dev libbz2-dev \
libao-dev libavutil-dev libavformat-dev libtiff5-dev libeb16-dev \ libao-dev libavutil-dev libavformat-dev libtiff5-dev libeb16-dev \
libqt5webkit5-dev libqt5svg5-dev libqt5x11extras5-dev qttools5-dev \ libqt5webkit5-dev libqt5svg5-dev libqt5x11extras5-dev qttools5-dev \
qttools5-dev-tools qttools5-dev-tools qtmultimedia5-dev libqt5multimedia5-plugins
## How to build ## How to build
@ -82,16 +82,21 @@ If you have problem building with libeb-dev package, you can pass
qmake "CONFIG+=no_epwing_support" qmake "CONFIG+=no_epwing_support"
### Building without internal audio player ### Building without internal audio players
If you have problem building with FFmpeg/libao (for example, Ubuntu older than 12.04), you can pass If you have problem building with FFmpeg/libao (for example, Ubuntu older than 12.04), you can pass
`"DISABLE_INTERNAL_PLAYER=1"` to `qmake` in order to disable internal audio player completely: `"CONFIG+=no_ffmpeg_player"` to `qmake` in order to disable FFmpeg+libao internal audio player back end:
qmake "DISABLE_INTERNAL_PLAYER=1" qmake "CONFIG+=no_ffmpeg_player"
If you have problem building with Qt5 Multimedia or experience GStreamer run-time errors (for example, Ubuntu 14.04), you can pass
`"CONFIG+=no_qtmultimedia_player"` to `qmake` in order to disable Qt Multimedia internal audio player back end:
qmake "CONFIG+=no_qtmultimedia_player"
<b>NB:</b> All additional settings for `qmake` that you need must be combined in one `qmake` launch, for example: <b>NB:</b> All additional settings for `qmake` that you need must be combined in one `qmake` launch, for example:
qmake "CONFIG+=zim_support" "CONFIG+=no_extra_tiff_handler" "DISABLE_INTERNAL_PLAYER=1" qmake "CONFIG+=zim_support" "CONFIG+=no_extra_tiff_handler" "CONFIG+=no_ffmpeg_player"
Then, invoke `make clean` before `make` because the setting change: Then, invoke `make clean` before `make` because the setting change:

View file

@ -5,11 +5,13 @@
#include <QObject> #include <QObject>
#include "audioplayerfactory.hh" #include "audioplayerfactory.hh"
#include "ffmpegaudioplayer.hh" #include "ffmpegaudioplayer.hh"
#include "multimediaaudioplayer.hh"
#include "externalaudioplayer.hh" #include "externalaudioplayer.hh"
#include "gddebug.hh" #include "gddebug.hh"
AudioPlayerFactory::AudioPlayerFactory( Config::Preferences const & p ) : AudioPlayerFactory::AudioPlayerFactory( Config::Preferences const & p ) :
useInternalPlayer( p.useInternalPlayer ), useInternalPlayer( p.useInternalPlayer ),
internalPlayerBackend( p.internalPlayerBackend ),
audioPlaybackProgram( p.audioPlaybackProgram ) audioPlaybackProgram( p.audioPlaybackProgram )
{ {
reset(); reset();
@ -20,10 +22,17 @@ void AudioPlayerFactory::setPreferences( Config::Preferences const & p )
if( p.useInternalPlayer != useInternalPlayer ) if( p.useInternalPlayer != useInternalPlayer )
{ {
useInternalPlayer = p.useInternalPlayer; useInternalPlayer = p.useInternalPlayer;
internalPlayerBackend = p.internalPlayerBackend;
audioPlaybackProgram = p.audioPlaybackProgram; audioPlaybackProgram = p.audioPlaybackProgram;
reset(); reset();
} }
else else
if( useInternalPlayer && p.internalPlayerBackend != internalPlayerBackend )
{
internalPlayerBackend = p.internalPlayerBackend;
reset();
}
else
if( !useInternalPlayer && p.audioPlaybackProgram != audioPlaybackProgram ) if( !useInternalPlayer && p.audioPlaybackProgram != audioPlaybackProgram )
{ {
audioPlaybackProgram = p.audioPlaybackProgram; audioPlaybackProgram = p.audioPlaybackProgram;
@ -38,16 +47,35 @@ void AudioPlayerFactory::setPreferences( Config::Preferences const & p )
void AudioPlayerFactory::reset() void AudioPlayerFactory::reset()
{ {
#ifndef DISABLE_INTERNAL_PLAYER
if( useInternalPlayer ) if( useInternalPlayer )
playerPtr.reset( new Ffmpeg::AudioPlayer );
else
#endif
{ {
QScopedPointer< ExternalAudioPlayer > externalPlayer( new ExternalAudioPlayer ); // qobject_cast checks below account for the case when an unsupported backend
setAudioPlaybackProgram( *externalPlayer ); // is stored in config. After this backend is replaced with the default one
playerPtr.reset( externalPlayer.take() ); // upon preferences saving, the code below does not reset playerPtr with
// another object of the same type.
#ifdef MAKE_FFMPEG_PLAYER
Q_ASSERT( Config::InternalPlayerBackend::defaultBackend().isFfmpeg()
&& "Adjust the code below after changing the default backend." );
if( !internalPlayerBackend.isQtmultimedia() )
{
if( qobject_cast< Ffmpeg::AudioPlayer * >( playerPtr.data() ) == 0 )
playerPtr.reset( new Ffmpeg::AudioPlayer );
return;
}
#endif
#ifdef MAKE_QTMULTIMEDIA_PLAYER
if( qobject_cast< MultimediaAudioPlayer * >( playerPtr.data() ) == 0 )
playerPtr.reset( new MultimediaAudioPlayer );
return;
#endif
} }
QScopedPointer< ExternalAudioPlayer > externalPlayer( new ExternalAudioPlayer );
setAudioPlaybackProgram( *externalPlayer );
playerPtr.reset( externalPlayer.take() );
} }
void AudioPlayerFactory::setAudioPlaybackProgram( ExternalAudioPlayer & externalPlayer ) void AudioPlayerFactory::setAudioPlaybackProgram( ExternalAudioPlayer & externalPlayer )

View file

@ -25,6 +25,7 @@ private:
void setAudioPlaybackProgram( ExternalAudioPlayer & externalPlayer ); void setAudioPlaybackProgram( ExternalAudioPlayer & externalPlayer );
bool useInternalPlayer; bool useInternalPlayer;
Config::InternalPlayerBackend internalPlayerBackend;
QString audioPlaybackProgram; QString audioPlaybackProgram;
AudioPlayerPtr playerPtr; AudioPlayerPtr playerPtr;
}; };

View file

@ -78,6 +78,56 @@ QKeySequence HotKey::toKeySequence() const
return QKeySequence( key1 | modifiers, v2 ); return QKeySequence( key1 | modifiers, v2 );
} }
bool InternalPlayerBackend::anyAvailable()
{
#if defined( MAKE_FFMPEG_PLAYER ) || defined( MAKE_QTMULTIMEDIA_PLAYER )
return true;
#else
return false;
#endif
}
InternalPlayerBackend InternalPlayerBackend::defaultBackend()
{
#if defined( MAKE_FFMPEG_PLAYER )
return ffmpeg();
#elif defined( MAKE_QTMULTIMEDIA_PLAYER )
return qtmultimedia();
#else
return InternalPlayerBackend( QString() );
#endif
}
QStringList InternalPlayerBackend::nameList()
{
QStringList result;
#ifdef MAKE_FFMPEG_PLAYER
result.push_back( ffmpeg().uiName() );
#endif
#ifdef MAKE_QTMULTIMEDIA_PLAYER
result.push_back( qtmultimedia().uiName() );
#endif
return result;
}
bool InternalPlayerBackend::isFfmpeg() const
{
#ifdef MAKE_FFMPEG_PLAYER
return *this == ffmpeg();
#else
return false;
#endif
}
bool InternalPlayerBackend::isQtmultimedia() const
{
#ifdef MAKE_QTMULTIMEDIA_PLAYER
return *this == qtmultimedia();
#else
return false;
#endif
}
Preferences::Preferences(): Preferences::Preferences():
newTabsOpenAfterCurrentOne( false ), newTabsOpenAfterCurrentOne( false ),
newTabsOpenInBackground( true ), newTabsOpenInBackground( true ),
@ -114,11 +164,8 @@ Preferences::Preferences():
#endif #endif
pronounceOnLoadMain( false ), pronounceOnLoadMain( false ),
pronounceOnLoadPopup( false ), pronounceOnLoadPopup( false ),
#ifndef DISABLE_INTERNAL_PLAYER useInternalPlayer( InternalPlayerBackend::anyAvailable() ),
useInternalPlayer( true ), internalPlayerBackend( InternalPlayerBackend::defaultBackend() ),
#else
useInternalPlayer( false ),
#endif
checkForNewReleases( true ), checkForNewReleases( true ),
disallowContentFromOtherSites( false ), disallowContentFromOtherSites( false ),
enableWebPlugins( false ), enableWebPlugins( false ),
@ -769,12 +816,16 @@ Class load() throw( exError )
c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" ); c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" );
c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" ); c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" );
#ifndef DISABLE_INTERNAL_PLAYER if ( InternalPlayerBackend::anyAvailable() )
if ( !preferences.namedItem( "useInternalPlayer" ).isNull() ) {
c.preferences.useInternalPlayer = ( preferences.namedItem( "useInternalPlayer" ).toElement().text() == "1" ); if ( !preferences.namedItem( "useInternalPlayer" ).isNull() )
#else c.preferences.useInternalPlayer = ( preferences.namedItem( "useInternalPlayer" ).toElement().text() == "1" );
c.preferences.useInternalPlayer = false; }
#endif else
c.preferences.useInternalPlayer = false;
if ( !preferences.namedItem( "internalPlayerBackend" ).isNull() )
c.preferences.internalPlayerBackend.setUiName( preferences.namedItem( "internalPlayerBackend" ).toElement().text() );
if ( !preferences.namedItem( "audioPlaybackProgram" ).isNull() ) if ( !preferences.namedItem( "audioPlaybackProgram" ).isNull() )
c.preferences.audioPlaybackProgram = preferences.namedItem( "audioPlaybackProgram" ).toElement().text(); c.preferences.audioPlaybackProgram = preferences.namedItem( "audioPlaybackProgram" ).toElement().text();
@ -1660,6 +1711,10 @@ void save( Class const & c ) throw( exError )
opt.appendChild( dd.createTextNode( c.preferences.useInternalPlayer ? "1" : "0" ) ); opt.appendChild( dd.createTextNode( c.preferences.useInternalPlayer ? "1" : "0" ) );
preferences.appendChild( opt ); preferences.appendChild( opt );
opt = dd.createElement( "internalPlayerBackend" );
opt.appendChild( dd.createTextNode( c.preferences.internalPlayerBackend.uiName() ) );
preferences.appendChild( opt );
opt = dd.createElement( "audioPlaybackProgram" ); opt = dd.createElement( "audioPlaybackProgram" );
opt.appendChild( dd.createTextNode( c.preferences.audioPlaybackProgram ) ); opt.appendChild( dd.createTextNode( c.preferences.audioPlaybackProgram ) );
preferences.appendChild( opt ); preferences.appendChild( opt );

View file

@ -177,6 +177,52 @@ struct FullTextSearch
{} {}
}; };
/// This class encapsulates supported backend preprocessor logic,
/// discourages duplicating backend names in code, which is error-prone.
class InternalPlayerBackend
{
public:
/// Returns true if at least one backend is available.
static bool anyAvailable();
/// Returns the default backend or null backend if none is available.
static InternalPlayerBackend defaultBackend();
/// Returns the name list of supported backends.
static QStringList nameList();
/// Returns true if built with FFmpeg player support and the name matches.
bool isFfmpeg() const;
/// Returns true if built with Qt Multimedia player support and the name matches.
bool isQtmultimedia() const;
QString const & uiName() const
{ return name; }
void setUiName( QString const & name_ )
{ name = name_; }
bool operator == ( InternalPlayerBackend const & other ) const
{ return name == other.name; }
bool operator != ( InternalPlayerBackend const & other ) const
{ return ! operator == ( other ); }
private:
#ifdef MAKE_FFMPEG_PLAYER
static InternalPlayerBackend ffmpeg()
{ return InternalPlayerBackend( "FFmpeg+libao" ); }
#endif
#ifdef MAKE_QTMULTIMEDIA_PLAYER
static InternalPlayerBackend qtmultimedia()
{ return InternalPlayerBackend( "Qt Multimedia" ); }
#endif
explicit InternalPlayerBackend( QString const & name_ ) : name( name_ )
{}
QString name;
};
/// Various user preferences /// Various user preferences
struct Preferences struct Preferences
{ {
@ -222,8 +268,9 @@ struct Preferences
// Whether the word should be pronounced on page load, in main window/popup // Whether the word should be pronounced on page load, in main window/popup
bool pronounceOnLoadMain, pronounceOnLoadPopup; bool pronounceOnLoadMain, pronounceOnLoadPopup;
QString audioPlaybackProgram;
bool useInternalPlayer; bool useInternalPlayer;
InternalPlayerBackend internalPlayerBackend;
QString audioPlaybackProgram;
ProxyServer proxyServer; ProxyServer proxyServer;

View file

@ -1,4 +1,4 @@
#ifndef DISABLE_INTERNAL_PLAYER #ifdef MAKE_FFMPEG_PLAYER
#include "ffmpegaudio.hh" #include "ffmpegaudio.hh"
@ -589,4 +589,4 @@ void DecoderThread::cancel( bool waitUntilFinished )
} }
#endif //DISABLE_INTERNAL_PLAYER #endif // MAKE_FFMPEG_PLAYER

View file

@ -1,7 +1,7 @@
#ifndef __FFMPEGAUDIO_HH_INCLUDED__ #ifndef __FFMPEGAUDIO_HH_INCLUDED__
#define __FFMPEGAUDIO_HH_INCLUDED__ #define __FFMPEGAUDIO_HH_INCLUDED__
#ifndef DISABLE_INTERNAL_PLAYER #ifdef MAKE_FFMPEG_PLAYER
#include <QObject> #include <QObject>
#include <QMutex> #include <QMutex>
@ -52,6 +52,6 @@ signals:
} }
#endif // DISABLE_INTERNAL_PLAYER #endif // MAKE_FFMPEG_PLAYER
#endif // __FFMPEGAUDIO_HH_INCLUDED__ #endif // __FFMPEGAUDIO_HH_INCLUDED__

View file

@ -7,7 +7,7 @@
#include "audioplayerinterface.hh" #include "audioplayerinterface.hh"
#include "ffmpegaudio.hh" #include "ffmpegaudio.hh"
#ifndef DISABLE_INTERNAL_PLAYER #ifdef MAKE_FFMPEG_PLAYER
namespace Ffmpeg namespace Ffmpeg
{ {
@ -36,6 +36,6 @@ public:
} }
#endif // DISABLE_INTERNAL_PLAYER #endif // MAKE_FFMPEG_PLAYER
#endif // FFMPEGAUDIOPLAYER_HH_INCLUDED #endif // FFMPEGAUDIOPLAYER_HH_INCLUDED

View file

@ -28,11 +28,21 @@ greaterThan(QT_MAJOR_VERSION, 4) {
webkitwidgets \ webkitwidgets \
printsupport \ printsupport \
help help
# QMediaPlayer is not available in Qt4.
!CONFIG( no_qtmultimedia_player ) {
QT += multimedia
DEFINES += MAKE_QTMULTIMEDIA_PLAYER
}
} else { } else {
QT += webkit QT += webkit
CONFIG += help CONFIG += help
} }
!CONFIG( no_ffmpeg_player ) {
DEFINES += MAKE_FFMPEG_PLAYER
}
QT += sql QT += sql
CONFIG += exceptions \ CONFIG += exceptions \
rtti \ rtti \
@ -46,8 +56,6 @@ LIBS += \
-lbz2 \ -lbz2 \
-llzo2 -llzo2
!isEmpty(DISABLE_INTERNAL_PLAYER): DEFINES += DISABLE_INTERNAL_PLAYER
win32 { win32 {
TARGET = GoldenDict TARGET = GoldenDict
@ -97,7 +105,7 @@ win32 {
LIBS += -lvorbisfile \ LIBS += -lvorbisfile \
-lvorbis \ -lvorbis \
-logg -logg
isEmpty(DISABLE_INTERNAL_PLAYER) { !CONFIG( no_ffmpeg_player ) {
LIBS += -lao \ LIBS += -lao \
-lavutil-gd \ -lavutil-gd \
-lavformat-gd \ -lavformat-gd \
@ -143,7 +151,7 @@ unix:!mac {
vorbis \ vorbis \
ogg \ ogg \
hunspell hunspell
isEmpty(DISABLE_INTERNAL_PLAYER) { !CONFIG( no_ffmpeg_player ) {
PKGCONFIG += ao \ PKGCONFIG += ao \
libavutil \ libavutil \
libavformat \ libavformat \
@ -193,7 +201,7 @@ mac {
-logg \ -logg \
-lhunspell-1.6.1 \ -lhunspell-1.6.1 \
-llzo2 -llzo2
isEmpty(DISABLE_INTERNAL_PLAYER) { !CONFIG( no_ffmpeg_player ) {
LIBS += -lao \ LIBS += -lao \
-lavutil-gd \ -lavutil-gd \
-lavformat-gd \ -lavformat-gd \
@ -267,6 +275,7 @@ HEADERS += folding.hh \
audioplayerinterface.hh \ audioplayerinterface.hh \
audioplayerfactory.hh \ audioplayerfactory.hh \
ffmpegaudioplayer.hh \ ffmpegaudioplayer.hh \
multimediaaudioplayer.hh \
externalaudioplayer.hh \ externalaudioplayer.hh \
externalviewer.hh \ externalviewer.hh \
wordfinder.hh \ wordfinder.hh \
@ -399,6 +408,7 @@ SOURCES += folding.cc \
scanpopup.cc \ scanpopup.cc \
articleview.cc \ articleview.cc \
audioplayerfactory.cc \ audioplayerfactory.cc \
multimediaaudioplayer.cc \
externalaudioplayer.cc \ externalaudioplayer.cc \
externalviewer.cc \ externalviewer.cc \
wordfinder.cc \ wordfinder.cc \

43
multimediaaudioplayer.cc Normal file
View file

@ -0,0 +1,43 @@
/* This file is (c) 2018 Igor Kushnir <igorkuo@gmail.com>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifdef MAKE_QTMULTIMEDIA_PLAYER
#include <QByteArray>
#include <QMediaContent>
#include "multimediaaudioplayer.hh"
MultimediaAudioPlayer::MultimediaAudioPlayer() :
player( 0, QMediaPlayer::StreamPlayback )
{
typedef void( QMediaPlayer::* ErrorSignal )( QMediaPlayer::Error );
connect( &player, static_cast< ErrorSignal >( &QMediaPlayer::error ),
this, &MultimediaAudioPlayer::onMediaPlayerError );
}
QString MultimediaAudioPlayer::play( const char * data, int size )
{
stop();
audioBuffer.setData( data, size );
if( !audioBuffer.open( QIODevice::ReadOnly ) )
return tr( "Couldn't open audio buffer for reading." );
player.setMedia( QMediaContent(), &audioBuffer );
player.play();
return QString();
}
void MultimediaAudioPlayer::stop()
{
player.setMedia( QMediaContent() ); // Forget about audioBuffer.
audioBuffer.close();
audioBuffer.setData( QByteArray() ); // Free memory.
}
void MultimediaAudioPlayer::onMediaPlayerError()
{
emit error( player.errorString() );
}
#endif // MAKE_QTMULTIMEDIA_PLAYER

32
multimediaaudioplayer.hh Normal file
View file

@ -0,0 +1,32 @@
/* This file is (c) 2018 Igor Kushnir <igorkuo@gmail.com>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef MULTIMEDIAAUDIOPLAYER_HH_INCLUDED
#define MULTIMEDIAAUDIOPLAYER_HH_INCLUDED
#ifdef MAKE_QTMULTIMEDIA_PLAYER
#include <QBuffer>
#include <QMediaPlayer>
#include "audioplayerinterface.hh"
class MultimediaAudioPlayer : public AudioPlayerInterface
{
Q_OBJECT
public:
MultimediaAudioPlayer();
virtual QString play( const char * data, int size );
virtual void stop();
private slots:
void onMediaPlayerError();
private:
QBuffer audioBuffer;
QMediaPlayer player; ///< Depends on audioBuffer.
};
#endif // MAKE_QTMULTIMEDIA_PLAYER
#endif // MULTIMEDIAAUDIOPLAYER_HH_INCLUDED

View file

@ -243,14 +243,28 @@ Preferences::Preferences( QWidget * parent, Config::Class & cfg_ ):
ui.pronounceOnLoadMain->setChecked( p.pronounceOnLoadMain ); ui.pronounceOnLoadMain->setChecked( p.pronounceOnLoadMain );
ui.pronounceOnLoadPopup->setChecked( p.pronounceOnLoadPopup ); ui.pronounceOnLoadPopup->setChecked( p.pronounceOnLoadPopup );
#ifdef DISABLE_INTERNAL_PLAYER ui.internalPlayerBackend->addItems( Config::InternalPlayerBackend::nameList() );
ui.useInternalPlayer->hide();
#else // Make sure that exactly one radio button in the group is checked and that
if ( p.useInternalPlayer ) // on_useExternalPlayer_toggled() is called.
ui.useInternalPlayer->setChecked( true ); ui.useExternalPlayer->setChecked( true );
if( ui.internalPlayerBackend->count() > 0 )
{
// Checking ui.useInternalPlayer automatically unchecks ui.useExternalPlayer.
ui.useInternalPlayer->setChecked( p.useInternalPlayer );
int index = ui.internalPlayerBackend->findText( p.internalPlayerBackend.uiName() );
if( index < 0 ) // The specified backend is unavailable.
index = ui.internalPlayerBackend->findText( Config::InternalPlayerBackend::defaultBackend().uiName() );
Q_ASSERT( index >= 0 && "Logic error: the default backend must be present in the backend name list." );
ui.internalPlayerBackend->setCurrentIndex( index );
}
else else
#endif {
ui.useExternalPlayer->setChecked( true ); ui.useInternalPlayer->hide();
ui.internalPlayerBackend->hide();
}
ui.audioPlaybackProgram->setText( p.audioPlaybackProgram ); ui.audioPlaybackProgram->setText( p.audioPlaybackProgram );
@ -400,6 +414,7 @@ Config::Preferences Preferences::getPreferences()
p.pronounceOnLoadMain = ui.pronounceOnLoadMain->isChecked(); p.pronounceOnLoadMain = ui.pronounceOnLoadMain->isChecked();
p.pronounceOnLoadPopup = ui.pronounceOnLoadPopup->isChecked(); p.pronounceOnLoadPopup = ui.pronounceOnLoadPopup->isChecked();
p.useInternalPlayer = ui.useInternalPlayer->isChecked(); p.useInternalPlayer = ui.useInternalPlayer->isChecked();
p.internalPlayerBackend.setUiName( ui.internalPlayerBackend->currentText() );
p.audioPlaybackProgram = ui.audioPlaybackProgram->text(); p.audioPlaybackProgram = ui.audioPlaybackProgram->text();
p.proxyServer.enabled = ui.useProxyServer->isChecked(); p.proxyServer.enabled = ui.useProxyServer->isChecked();
@ -592,6 +607,7 @@ void Preferences::on_buttonBox_accepted()
void Preferences::on_useExternalPlayer_toggled( bool enabled ) void Preferences::on_useExternalPlayer_toggled( bool enabled )
{ {
ui.internalPlayerBackend->setEnabled( !enabled );
ui.audioPlaybackProgram->setEnabled( enabled ); ui.audioPlaybackProgram->setEnabled( enabled );
} }

View file

@ -902,37 +902,43 @@ p, li { white-space: pre-wrap; }
<property name="title"> <property name="title">
<string>Playback</string> <string>Playback</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_16"> <layout class="QGridLayout" name="gridLayout_audioPlayer">
<item> <item row="0" column="0">
<widget class="QRadioButton" name="useInternalPlayer"> <widget class="QRadioButton" name="useInternalPlayer">
<property name="toolTip"> <property name="toolTip">
<string>Play audio files via FFmpeg(libav) and libao</string> <string>Play audio files via built-in audio support</string>
</property> </property>
<property name="text"> <property name="text">
<string>Use internal player</string> <string>Use internal player:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_12"> <widget class="QComboBox" name="internalPlayerBackend">
<item> <property name="toolTip">
<widget class="QRadioButton" name="useExternalPlayer"> <string>Choose audio back end</string>
<property name="toolTip"> </property>
<string>Use any external program to play audio files</string> </widget>
</property> </item>
<property name="text"> <item row="1" column="0">
<string>Use external program:</string> <widget class="QRadioButton" name="useExternalPlayer">
</property> <property name="toolTip">
</widget> <string>Use any external program to play audio files</string>
</item> </property>
<item> <property name="text">
<widget class="QLineEdit" name="audioPlaybackProgram"> <string>Use external program:</string>
<property name="enabled"> </property>
<bool>false</bool> </widget>
</property> </item>
</widget> <item row="1" column="1">
</item> <widget class="QLineEdit" name="audioPlaybackProgram">
</layout> <property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Enter audio player command line</string>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>