1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 19:30:21 +02:00

Precache next track to play when reaching end of current song

This commit is contained in:
Anton Romanov
2016-11-30 11:34:19 -08:00
parent 3b6ba36e83
commit 9a5750c6f4
7 changed files with 410 additions and 196 deletions

View File

@@ -685,7 +685,7 @@ Query::howSimilar( const Tomahawk::result_ptr& r )
qTrackname = queryTrack()->trackSortname();
}
static const QRegExp filterOutChars = QRegExp(QString::fromUtf8("[-`´~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]"));
static const QRegExp filterOutChars = QRegExp(QString::fromUtf8("[-`´~!@#$%^&*\\(\\)_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]"));
//Cleanup symbols for minor naming differences
qArtistname.remove(filterOutChars);

View File

@@ -572,10 +572,10 @@ AudioEngine::onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type )
void
AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
AudioEngine::loadTrack( const Tomahawk::result_ptr& result, bool preload )
{
Q_D( AudioEngine );
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() );
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() ) << " preload:" << preload;
if ( !d->audioOutput->isInitialized() )
@@ -589,18 +589,57 @@ AudioEngine::loadTrack( const Tomahawk::result_ptr& result )
return;
}
// We do this to stop the audio as soon as a user activated another track
// If we don't block the audioOutput signals, the state change will trigger
// loading yet another track
d->audioOutput->blockSignals( true );
d->audioOutput->stop();
d->audioOutput->blockSignals( false );
if (preload && d->preloadedTrack == result)
return;
setCurrentTrack( result );
if (preload)
tDebug( LOGEXTRA ) << Q_FUNC_INFO << "not preloaded yet, preloading";
if (preload)
{
setPreloadTrack( result );
}
else
{
// We do this to stop the audio as soon as a user activated another track
// If we don't block the audioOutput signals, the state change will trigger
// loading yet another track
d->audioOutput->blockSignals( true );
d->audioOutput->stop();
d->audioOutput->blockSignals( false );
setCurrentTrack( result );
if ( result == d->preloadedTrack )
{
setPreloadTrack( Tomahawk::result_ptr(nullptr) );
d->state = Loading;
emit loading( d->currentTrack );
d->audioOutput->switchToPreloadedMedia();
if ( !d->input.isNull() )
{
d->input->close();
d->input.clear();
}
d->input = d->inputPreloaded;
d->audioOutput->play();
if ( TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::FullyPrivate )
{
d->currentTrack->track()->startPlaying();
}
sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowPlaying );
return;
}
setPreloadTrack( Tomahawk::result_ptr(nullptr) );
}
ScriptJob* job = result->resolvedBy()->getStreamUrl( result );
connect( job, SIGNAL( done( QVariantMap ) ), SLOT( gotStreamUrl( QVariantMap ) ) );
job->setProperty( "result", QVariant::fromValue( result ) );
job->setProperty( "isPreload", QVariant::fromValue(preload) );
tDebug() << "preload:" << preload << ", for result:" << result;
job->start();
}
@@ -611,6 +650,9 @@ AudioEngine::gotStreamUrl( const QVariantMap& data )
QString streamUrl = data[ "url" ].toString();
QVariantMap headers = data[ "headers" ].toMap();
Tomahawk::result_ptr result = sender()->property( "result" ).value<result_ptr>();
bool isPreload = sender()->property( "isPreload" ).value<bool>();
tDebug() << Q_FUNC_INFO << " is preload:" << isPreload << ", for result:" << result;
if ( streamUrl.isEmpty() || headers.isEmpty() ||
!( TomahawkUtils::isHttpResult( streamUrl ) || TomahawkUtils::isHttpsResult( streamUrl ) ) )
@@ -618,7 +660,7 @@ AudioEngine::gotStreamUrl( const QVariantMap& data )
// We can't supply custom headers to VLC - but prefer using its HTTP streaming due to improved seeking ability
// Not an RTMP or HTTP-with-headers URL, get IO device
QSharedPointer< QIODevice > sp;
performLoadIODevice( result, streamUrl );
performLoadIODevice( result, streamUrl, isPreload );
}
else
{
@@ -654,26 +696,32 @@ AudioEngine::gotStreamUrl( const QVariantMap& data )
void
AudioEngine::gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply )
{
Q_D( AudioEngine );
// std::functions cannot accept temporaries as parameters
QSharedPointer< QIODevice > sp ( reply->reply(), &QObject::deleteLater );
QString url = reply->reply()->url().toString();
reply->disconnectFromReply();
reply->deleteLater();
performLoadTrack( result, url, sp );
bool isPreload = result == d->preloadedTrack;
tDebug() << Q_FUNC_INFO << " is preload:" << isPreload;
performLoadTrack( result, url, sp, isPreload );
}
void
AudioEngine::onPositionChanged( float new_position )
{
if ( new_position >= 0.90 )
loadNextTrack(true);
// tDebug() << Q_FUNC_INFO << new_position << state();
emit trackPosition( new_position );
}
void
AudioEngine::performLoadIODevice( const result_ptr& result, const QString& url )
AudioEngine::performLoadIODevice( const result_ptr& result, const QString& url, bool preload )
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : url );
@@ -683,37 +731,39 @@ AudioEngine::performLoadIODevice( const result_ptr& result, const QString& url )
std::function< void ( const QString, QSharedPointer< QIODevice > ) > callback =
std::bind( &AudioEngine::performLoadTrack, this, result,
std::placeholders::_1,
std::placeholders::_2 );
std::placeholders::_2,
preload );
Tomahawk::UrlHandler::getIODeviceForUrl( result, url, callback );
}
else
{
QSharedPointer< QIODevice > io;
performLoadTrack( result, url, io );
performLoadTrack( result, url, io, preload );
}
}
void
AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io )
AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io, bool preload )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "performLoadTrack", Qt::QueuedConnection,
Q_ARG( const Tomahawk::result_ptr, result ),
Q_ARG( const QString, url ),
Q_ARG( QSharedPointer< QIODevice >, io )
Q_ARG( QSharedPointer< QIODevice >, io ),
Q_ARG( bool, preload )
);
return;
}
Q_D( AudioEngine );
if ( currentTrack() != result )
if ( !preload && currentTrack() != result )
{
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Track loaded too late, skip.";
return;
}
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() );
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() ) << preload;
QSharedPointer< QIODevice > ioToKeep = io;
bool err = false;
@@ -727,9 +777,16 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString&
if ( !err )
{
tLog() << Q_FUNC_INFO << "Starting new song:" << url;
d->state = Loading;
emit loading( d->currentTrack );
if (preload)
{
tLog() << Q_FUNC_INFO << "Preloading new song:" << url;
}
else
{
tLog() << Q_FUNC_INFO << "Starting new song:" << url;
d->state = Loading;
emit loading( d->currentTrack );
}
if ( !TomahawkUtils::isLocalResult( url )
&& !( TomahawkUtils::isHttpResult( url ) && io.isNull() )
@@ -738,18 +795,18 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString&
QSharedPointer<QNetworkReply> qnr = io.objectCast<QNetworkReply>();
if ( !qnr.isNull() )
{
d->audioOutput->setCurrentSource( new QNR_IODeviceStream( qnr, this ) );
d->audioOutput->setCurrentSource( new QNR_IODeviceStream( qnr, this ), preload );
// We keep track of the QNetworkReply in QNR_IODeviceStream
// and AudioOutput handles the deletion of the
// QNR_IODeviceStream object
ioToKeep.clear();
d->audioOutput->setAutoDelete( true );
d->audioOutput->setAutoDelete( true, preload );
}
else
{
d->audioOutput->setCurrentSource( io.data() );
d->audioOutput->setCurrentSource( io.data(), preload);
// We handle the deletion via tracking in d->input
d->audioOutput->setAutoDelete( false );
d->audioOutput->setAutoDelete( false, preload);
}
}
else
@@ -768,7 +825,7 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString&
}
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Passing to VLC:" << furl;
d->audioOutput->setCurrentSource( furl );
d->audioOutput->setCurrentSource( furl, preload );
}
else
{
@@ -777,26 +834,37 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString&
furl = furl.right( furl.length() - 7 );
tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Passing to VLC:" << QUrl::fromLocalFile( furl );
d->audioOutput->setCurrentSource( QUrl::fromLocalFile( furl ) );
d->audioOutput->setCurrentSource( QUrl::fromLocalFile( furl ), preload );
}
d->audioOutput->setAutoDelete( true );
d->audioOutput->setAutoDelete( true, preload );
}
if ( !d->input.isNull() )
if ( preload ) {
if ( !d->inputPreloaded.isNull() )
{
d->inputPreloaded->close();
d->inputPreloaded.clear();
}
d->inputPreloaded = ioToKeep;
}
else
{
d->input->close();
d->input.clear();
}
d->input = ioToKeep;
d->audioOutput->play();
if ( !d->input.isNull() )
{
d->input->close();
d->input.clear();
}
d->input = ioToKeep;
d->audioOutput->play();
if ( TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::FullyPrivate )
{
d->currentTrack->track()->startPlaying();
}
if ( TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::FullyPrivate )
{
d->currentTrack->track()->startPlaying();
}
sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowPlaying );
sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowPlaying );
}
}
}
@@ -806,7 +874,10 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString&
return;
}
d->waitingOnNewTrack = false;
if ( !preload )
{
d->waitingOnNewTrack = false;
}
return;
}
@@ -838,18 +909,19 @@ AudioEngine::loadPreviousTrack()
}
if ( result )
loadTrack( result );
loadTrack( result, false );
else
stop();
}
void
AudioEngine::loadNextTrack()
AudioEngine::loadNextTrack( bool preload )
{
if ( QThread::currentThread() != thread() )
{
QMetaObject::invokeMethod( this, "loadNextTrack", Qt::QueuedConnection );
QMetaObject::invokeMethod( this, "loadNextTrack", Qt::QueuedConnection,
Q_ARG( bool, preload ));
return;
}
@@ -863,8 +935,11 @@ AudioEngine::loadNextTrack()
{
if ( d->stopAfterTrack->track()->equals( d->currentTrack->track() ) )
{
d->stopAfterTrack.clear();
stop();
if ( !preload )
{
d->stopAfterTrack.clear();
stop();
}
return;
}
}
@@ -882,17 +957,24 @@ AudioEngine::loadNextTrack()
if ( d->playlist.data()->nextResult() )
{
result = d->playlist.data()->setSiblingResult( 1 );
setCurrentTrackPlaylist( d->playlist );
if ( preload )
{
result = d->playlist.data()->nextResult();
}
else
{
result = d->playlist.data()->setSiblingResult( 1 );
setCurrentTrackPlaylist( d->playlist );
}
}
}
if ( result )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Got next item, loading track";
loadTrack( result );
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Got next item, loading track, preload:" << preload;
loadTrack( result, preload );
}
else
else if ( !preload )
{
if ( !d->playlist.isNull() && d->playlist.data()->retryMode() == Tomahawk::PlaylistModes::Retry )
d->waitingOnNewTrack = true;
@@ -961,7 +1043,7 @@ AudioEngine::playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk:
if ( result )
{
loadTrack( result );
loadTrack( result, false );
}
else if ( !d->playlist.isNull() && d->playlist.data()->retryMode() == PlaylistModes::Retry )
{
@@ -1236,6 +1318,14 @@ AudioEngine::setStopAfterTrack( const query_ptr& query )
}
void
AudioEngine::setPreloadTrack( const Tomahawk::result_ptr& result )
{
Q_D( AudioEngine );
d->preloadedTrack = result;
}
void
AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result )
{
@@ -1262,7 +1352,6 @@ AudioEngine::setCurrentTrack( const Tomahawk::result_ptr& result )
}
}
void
AudioEngine::setState( AudioState state )
{

View File

@@ -135,10 +135,10 @@ public slots:
void toggleMute();
void play( const QUrl& url );
void playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::result_ptr& result, const Tomahawk::query_ptr& fromQuery = Tomahawk::query_ptr() );
void playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::query_ptr& query );
void playItem( const Tomahawk::artist_ptr& artist );
void playItem( const Tomahawk::album_ptr& album );
void playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::result_ptr& result, const Tomahawk::query_ptr& fromQuery = Tomahawk::query_ptr());
void playItem( Tomahawk::playlistinterface_ptr playlist, const Tomahawk::query_ptr& query);
void playItem( const Tomahawk::artist_ptr& artist);
void playItem( const Tomahawk::album_ptr& album);
void playPlaylistInterface( const Tomahawk::playlistinterface_ptr& playlist );
void setPlaylist( Tomahawk::playlistinterface_ptr playlist );
void setQueue( const Tomahawk::playlistinterface_ptr& queue );
@@ -182,21 +182,22 @@ signals:
void error( AudioEngine::AudioErrorCode errorCode );
private slots:
void loadTrack( const Tomahawk::result_ptr& result ); //async!
void loadTrack( const Tomahawk::result_ptr& result, bool preload ); //async!
void gotStreamUrl( const QVariantMap& data );
void gotRedirectedStreamUrl( const Tomahawk::result_ptr& result, NetworkReply* reply );
void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url ); //only call from loadTrack kthxbi
void performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io ); //only call from loadTrack or performLoadIODevice kthxbi
void performLoadIODevice( const Tomahawk::result_ptr& result, const QString& url, bool preload ); //only call from loadTrack kthxbi
void performLoadTrack( const Tomahawk::result_ptr result, const QString& url, QSharedPointer< QIODevice > io, bool preload ); //only call from loadTrack or performLoadIODevice kthxbi
void loadPreviousTrack();
void loadNextTrack();
void loadNextTrack(bool preload = false);
void onVolumeChanged( qreal volume );
void timerTriggered( qint64 time );
void onPositionChanged( float new_position );
void setCurrentTrack( const Tomahawk::result_ptr& result );
void setPreloadTrack( const Tomahawk::result_ptr& result );
void onNowPlayingInfoReady( const Tomahawk::InfoSystem::InfoType type );
void onPlaylistNextTrackAvailable();

View File

@@ -28,9 +28,11 @@ public slots:
private:
QSharedPointer<QIODevice> input;
QSharedPointer<QIODevice> inputPreloaded;
Tomahawk::query_ptr stopAfterTrack;
Tomahawk::result_ptr currentTrack;
Tomahawk::result_ptr preloadedTrack;
Tomahawk::playlistinterface_ptr playlist;
Tomahawk::playlistinterface_ptr currentTrackPlaylist;
Tomahawk::playlistinterface_ptr queue;

View File

@@ -56,9 +56,11 @@ AudioOutput::AudioOutput( QObject* parent )
: QObject( parent )
, m_currentState( Stopped )
, m_currentStream( nullptr )
, m_preloadedStream( nullptr )
, m_seekable( true )
, m_muted( false )
, m_autoDelete( true )
, m_preloadedAutoDelete( true )
, m_volume( 1.0 )
, m_currentTime( 0 )
, m_totalTime( 0 )
@@ -68,6 +70,8 @@ AudioOutput::AudioOutput( QObject* parent )
, m_vlcInstance( nullptr )
, m_vlcPlayer( nullptr )
, m_vlcMedia( nullptr )
, m_vlcPreloadedPlayer( nullptr )
, m_vlcPreloadedMedia( nullptr )
{
tDebug() << Q_FUNC_INFO;
@@ -123,37 +127,46 @@ AudioOutput::AudioOutput( QObject* parent )
#endif
m_vlcPlayer = libvlc_media_player_new( m_vlcInstance );
libvlc_event_manager_t* manager = libvlc_media_player_event_manager( m_vlcPlayer );
libvlc_event_type_t events[] = {
libvlc_MediaPlayerMediaChanged,
libvlc_MediaPlayerNothingSpecial,
libvlc_MediaPlayerOpening,
libvlc_MediaPlayerBuffering,
libvlc_MediaPlayerPlaying,
libvlc_MediaPlayerPaused,
libvlc_MediaPlayerStopped,
libvlc_MediaPlayerForward,
libvlc_MediaPlayerBackward,
libvlc_MediaPlayerEndReached,
libvlc_MediaPlayerEncounteredError,
libvlc_MediaPlayerTimeChanged,
libvlc_MediaPlayerPositionChanged,
libvlc_MediaPlayerSeekableChanged,
libvlc_MediaPlayerPausableChanged,
libvlc_MediaPlayerTitleChanged,
libvlc_MediaPlayerSnapshotTaken,
//libvlc_MediaPlayerLengthChanged,
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0))
libvlc_MediaPlayerAudioVolume,
libvlc_MediaPlayerMuted,
libvlc_MediaPlayerUnmuted,
#endif
libvlc_MediaPlayerVout
};
const int eventCount = sizeof(events) / sizeof( *events );
for ( int i = 0; i < eventCount; i++ )
m_vlcPreloadedPlayer = libvlc_media_player_new( m_vlcInstance );
for( auto player : { m_vlcPlayer, m_vlcPreloadedPlayer } )
{
libvlc_event_attach( manager, events[ i ], &AudioOutput::vlcEventCallback, this );
libvlc_audio_set_mute( player, 0 );
{
libvlc_event_manager_t* current_manager = libvlc_media_player_event_manager( player );
libvlc_event_manager_t* new_manager = libvlc_media_player_event_manager( player );
static libvlc_event_type_t events[] = {
libvlc_MediaPlayerMediaChanged,
libvlc_MediaPlayerNothingSpecial,
libvlc_MediaPlayerOpening,
libvlc_MediaPlayerBuffering,
libvlc_MediaPlayerPlaying,
libvlc_MediaPlayerPaused,
libvlc_MediaPlayerStopped,
libvlc_MediaPlayerForward,
libvlc_MediaPlayerBackward,
libvlc_MediaPlayerEndReached,
libvlc_MediaPlayerEncounteredError,
libvlc_MediaPlayerTimeChanged,
libvlc_MediaPlayerPositionChanged,
libvlc_MediaPlayerSeekableChanged,
libvlc_MediaPlayerPausableChanged,
libvlc_MediaPlayerTitleChanged,
libvlc_MediaPlayerSnapshotTaken,
//libvlc_MediaPlayerLengthChanged,
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0))
libvlc_MediaPlayerAudioVolume,
libvlc_MediaPlayerMuted,
libvlc_MediaPlayerUnmuted,
#endif
libvlc_MediaPlayerVout
};
const int eventCount = sizeof(events) / sizeof( *events );
for ( int i = 0; i < eventCount; i++ )
{
libvlc_event_attach( new_manager, events[ i ], &AudioOutput::vlcEventCallback, this );
}
}
}
// HACK: play silent ogg file and set volume on that to workaround vlc not allowing to set volume before a file is played
@@ -161,7 +174,9 @@ AudioOutput::AudioOutput( QObject* parent )
Q_ASSERT( m_silenceFile.exists() );
Q_ASSERT( m_silenceFile.open( QIODevice::ReadOnly ) );
setCurrentSource( new MediaStream( &m_silenceFile, true ) );
setCurrentSource( new MediaStream( &m_silenceFile, true ), false );
setCurrentSource( new MediaStream( &m_silenceFile, true ), true );
libvlc_media_player_play( m_vlcPlayer );
#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
@@ -169,24 +184,71 @@ AudioOutput::AudioOutput( QObject* parent )
QTimer::singleShot( 15000, [&]()
{
if ( !m_initialized ) {
m_initialized = true;
m_initialized = 2;
emit initialized();
}
} );
#endif
}
void AudioOutput::switchToPreloadedMedia( void )
{
//Swap
auto tempPlayer = m_vlcPreloadedPlayer;
m_vlcPreloadedPlayer = m_vlcPlayer;
m_vlcPlayer = tempPlayer;
libvlc_media_player_stop( m_vlcPreloadedPlayer );
if ( m_vlcMedia != nullptr )
{
libvlc_media_release( m_vlcMedia );
}
//Now Media
{
if ( m_autoDelete && m_currentStream != nullptr )
{
delete m_currentStream;
}
m_vlcMedia = m_vlcPreloadedMedia;
m_vlcPreloadedMedia = nullptr;
m_currentStream = m_preloadedStream;
m_preloadedStream = nullptr;
m_autoDelete = m_preloadedAutoDelete;
m_totalTime = libvlc_media_get_duration( m_vlcMedia );
m_currentTime = 0;
m_justSeeked = false;
m_seekable = true;
}
libvlc_media_player_set_position( m_vlcPlayer, 0.0 );
}
AudioOutput::~AudioOutput()
{
tDebug() << Q_FUNC_INFO;
if ( m_vlcPreloadedPlayer != nullptr )
{
libvlc_media_player_stop( m_vlcPreloadedPlayer );
libvlc_media_player_release( m_vlcPreloadedPlayer );
m_vlcPreloadedPlayer = nullptr;
}
if ( m_vlcPlayer != nullptr )
{
libvlc_media_player_stop( m_vlcPlayer );
libvlc_media_player_release( m_vlcPlayer );
m_vlcPlayer = nullptr;
}
if ( m_vlcPreloadedMedia != nullptr )
{
libvlc_media_release( m_vlcPreloadedMedia );
m_vlcPreloadedMedia = nullptr;
}
if ( m_vlcMedia != nullptr )
{
libvlc_media_release( m_vlcMedia );
@@ -208,11 +270,19 @@ AudioOutput::onInitVlcEvent( const libvlc_event_t* event )
setVolume( volume() );
setMuted( isMuted() );
m_initialized = true;
m_silenceFile.close();
m_initialized ++;
tDebug() << Q_FUNC_INFO << "Init OK";
emit initialized();
if (m_initialized >=2)
{
m_silenceFile.close();
emit initialized();
}
else
{
switchToPreloadedMedia();
libvlc_media_player_play( m_vlcPlayer );
}
break;
default:
@@ -222,23 +292,26 @@ AudioOutput::onInitVlcEvent( const libvlc_event_t* event )
void
AudioOutput::setAutoDelete( bool ad )
AudioOutput::setAutoDelete( bool ad, bool preload )
{
m_autoDelete = ad;
if (preload)
m_preloadedAutoDelete = ad;
else
m_autoDelete = ad;
}
void
AudioOutput::setCurrentSource( const QUrl& stream )
AudioOutput::setCurrentSource( const QUrl& stream, bool preload )
{
setCurrentSource( new MediaStream( stream ) );
setCurrentSource( new MediaStream( stream ), preload );
}
void
AudioOutput::setCurrentSource( QIODevice* stream )
AudioOutput::setCurrentSource( QIODevice* stream, bool preload )
{
setCurrentSource( new MediaStream( stream ) );
setCurrentSource( new MediaStream( stream ), preload );
}
@@ -258,29 +331,24 @@ readDoneCallback( void* data, const char* cookie, size_t bufferSize, void* buffe
void
AudioOutput::setCurrentSource( MediaStream* stream )
AudioOutput::setCurrentSource( MediaStream* stream, bool preload )
{
tDebug() << Q_FUNC_INFO;
tDebug() << Q_FUNC_INFO << ", preload = " << preload;
setState( Loading );
if ( !preload )
setState( Loading );
if ( m_vlcMedia != nullptr )
{
// Ensure playback is stopped, then release media
libvlc_media_player_stop( m_vlcPlayer );
libvlc_media_release( m_vlcMedia );
m_vlcMedia = nullptr;
if ( m_vlcPreloadedMedia ) {
libvlc_media_player_stop( m_vlcPreloadedPlayer );
libvlc_media_release( m_vlcPreloadedMedia );
m_vlcPreloadedMedia = nullptr;
}
if ( m_autoDelete && m_currentStream != nullptr )
if ( m_preloadedAutoDelete && m_preloadedStream != nullptr )
{
delete m_currentStream;
delete m_preloadedStream;
}
m_currentStream = stream;
m_totalTime = 0;
m_currentTime = 0;
m_justSeeked = false;
m_seekable = true;
m_preloadedStream = stream;
QByteArray url;
switch ( stream->type() )
@@ -314,23 +382,19 @@ AudioOutput::setCurrentSource( MediaStream* stream )
tDebug() << Q_FUNC_INFO << "MediaStream::Final Url:" << url;
m_vlcMedia = libvlc_media_new_location( m_vlcInstance, url.constData() );
if ( stream->type() == MediaStream::Url )
{
m_totalTime = libvlc_media_get_duration( m_vlcMedia );
}
else if ( stream->type() == MediaStream::Stream || stream->type() == MediaStream::IODevice )
m_vlcPreloadedMedia = libvlc_media_new_location( m_vlcInstance, url.constData() );
if ( stream->type() == MediaStream::Stream || stream->type() == MediaStream::IODevice )
{
QString tempString;
libvlc_media_add_option_flag(m_vlcMedia, "imem-cat=4", libvlc_media_option_trusted);
libvlc_media_add_option_flag(m_vlcPreloadedMedia, "imem-cat=4", libvlc_media_option_trusted);
tempString = QString( "imem-data=%1" ).arg( (uintptr_t)stream );
libvlc_media_add_option_flag(m_vlcMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(m_vlcPreloadedMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
tempString = QString( "imem-get=%1" ).arg( (uintptr_t)&readCallback );
libvlc_media_add_option_flag(m_vlcMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(m_vlcPreloadedMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
tempString = QString( "imem-release=%1" ).arg( (uintptr_t)&readDoneCallback );
libvlc_media_add_option_flag(m_vlcMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(m_vlcPreloadedMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
tempString = QString( "imem-seek=%1" ).arg( (uintptr_t)&MediaStream::seekCallback );
libvlc_media_add_option_flag(m_vlcMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(m_vlcPreloadedMedia, tempString.toLatin1().constData(), libvlc_media_option_trusted);
}
if ( qApp->arguments().contains( "--chromecast-ip" ) )
{
@@ -346,7 +410,7 @@ AudioOutput::setCurrentSource( MediaStream* stream )
{
QString castIP = qApp->arguments().at( qApp->arguments().indexOf( "--chromecast-ip" ) + 1 );
QString sout( ":sout=#transcode{vcodec=none,acodec=vorb,ab=320,channels=2,samplerate=44100}:chromecast{ip=%1,mux=webm}" );
libvlc_media_add_option( m_vlcMedia, sout.arg( castIP ).toLatin1().constData() );
libvlc_media_add_option( m_vlcPreloadedMedia, sout.arg( castIP ).toLatin1().constData() );
}
else
{
@@ -354,8 +418,8 @@ AudioOutput::setCurrentSource( MediaStream* stream )
}
}
libvlc_event_manager_t* manager = libvlc_media_event_manager( m_vlcMedia );
libvlc_event_type_t events[] = {
libvlc_event_manager_t* manager = libvlc_media_event_manager( m_vlcPreloadedMedia );
static libvlc_event_type_t events[] = {
libvlc_MediaDurationChanged,
};
const int eventCount = sizeof(events) / sizeof( *events );
@@ -364,7 +428,13 @@ AudioOutput::setCurrentSource( MediaStream* stream )
libvlc_event_attach( manager, events[ i ], &AudioOutput::vlcEventCallback, this );
}
libvlc_media_player_set_media( m_vlcPlayer, m_vlcMedia );
libvlc_media_player_set_media( m_vlcPreloadedPlayer, m_vlcPreloadedMedia );
libvlc_audio_set_volume( m_vlcPreloadedPlayer, 0.0 );
libvlc_media_player_play( m_vlcPreloadedPlayer );
if ( !preload )
switchToPreloadedMedia();
// setState( Stopped );
}
@@ -373,7 +443,7 @@ AudioOutput::setCurrentSource( MediaStream* stream )
bool
AudioOutput::isInitialized() const
{
return m_initialized;
return m_initialized > 1;
}
@@ -526,7 +596,7 @@ AudioOutput::seek( qint64 milliseconds )
bool
AudioOutput::isSeekable() const
{
// tDebug() << Q_FUNC_INFO << m_seekable << m_havePosition << m_totalTime << libvlc_media_player_is_seekable( m_vlcPlayer );
tDebug() << Q_FUNC_INFO << m_seekable << m_havePosition << m_totalTime << libvlc_media_player_is_seekable( m_vlcPlayer );
return m_havePosition || (libvlc_media_player_is_seekable( m_vlcPlayer ) && m_totalTime > 0 );
}
@@ -576,64 +646,107 @@ AudioOutput::setVolume( qreal vol )
void
AudioOutput::onVlcEvent( const libvlc_event_t* event )
{
switch ( event->type )
if ( event->p_obj == m_vlcPlayer || event->p_obj == m_vlcMedia )
{
case libvlc_MediaPlayerTimeChanged:
setCurrentTime( event->u.media_player_time_changed.new_time );
break;
case libvlc_MediaPlayerPositionChanged:
setCurrentPosition( event->u.media_player_position_changed.new_position );
break;
case libvlc_MediaPlayerSeekableChanged:
// tDebug() << Q_FUNC_INFO << " : seekable changed : " << event->u.media_player_seekable_changed.new_seekable;
break;
case libvlc_MediaDurationChanged:
setTotalTime( event->u.media_duration_changed.new_duration );
break;
case libvlc_MediaPlayerLengthChanged:
// tDebug() << Q_FUNC_INFO << " : length changed : " << event->u.media_player_length_changed.new_length;
break;
case libvlc_MediaPlayerPlaying:
setState( Playing );
break;
case libvlc_MediaPlayerPaused:
setState( Paused );
break;
case libvlc_MediaPlayerEndReached:
setState( Stopped );
break;
case libvlc_MediaPlayerEncounteredError:
tDebug() << Q_FUNC_INFO << "LibVLC error: MediaPlayerEncounteredError. Stopping";
// Don't call stop() here - it will deadlock libvlc
setState( Error );
break;
switch ( event->type )
{
case libvlc_MediaPlayerTimeChanged:
setCurrentTime( event->u.media_player_time_changed.new_time );
break;
case libvlc_MediaPlayerPositionChanged:
setCurrentPosition( event->u.media_player_position_changed.new_position );
break;
case libvlc_MediaPlayerSeekableChanged:
// tDebug() << Q_FUNC_INFO << " : seekable changed : " << event->u.media_player_seekable_changed.new_seekable;
break;
case libvlc_MediaDurationChanged:
setTotalTime( event->u.media_duration_changed.new_duration );
break;
case libvlc_MediaPlayerLengthChanged:
// tDebug() << Q_FUNC_INFO << " : length changed : " << event->u.media_player_length_changed.new_length;
break;
case libvlc_MediaPlayerPlaying:
setState( Playing );
break;
case libvlc_MediaPlayerPaused:
setState( Paused );
break;
case libvlc_MediaPlayerEndReached:
setState( Stopped );
break;
case libvlc_MediaPlayerEncounteredError:
tDebug() << Q_FUNC_INFO << "LibVLC error: MediaPlayerEncounteredError. Stopping";
// Don't call stop() here - it will deadlock libvlc
setState( Error );
break;
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0))
case libvlc_MediaPlayerAudioVolume:
m_volume = event->u.media_player_audio_volume.volume;
emit volumeChanged( volume() );
break;
case libvlc_MediaPlayerMuted:
m_muted = true;
emit mutedChanged( true );
break;
case libvlc_MediaPlayerUnmuted:
m_muted = false;
emit mutedChanged( false );
break;
case libvlc_MediaPlayerAudioVolume:
m_volume = event->u.media_player_audio_volume.volume;
tDebug() << Q_FUNC_INFO << "Got signal in current player that volume changed to:" << m_volume;
emit volumeChanged( volume() );
break;
case libvlc_MediaPlayerMuted:
m_muted = true;
emit mutedChanged( true );
break;
case libvlc_MediaPlayerUnmuted:
m_muted = false;
emit mutedChanged( false );
break;
#endif
case libvlc_MediaPlayerNothingSpecial:
case libvlc_MediaPlayerOpening:
case libvlc_MediaPlayerBuffering:
case libvlc_MediaPlayerStopped:
case libvlc_MediaPlayerVout:
case libvlc_MediaPlayerMediaChanged:
case libvlc_MediaPlayerForward:
case libvlc_MediaPlayerBackward:
case libvlc_MediaPlayerPausableChanged:
case libvlc_MediaPlayerTitleChanged:
case libvlc_MediaPlayerSnapshotTaken:
default:
break;
case libvlc_MediaPlayerNothingSpecial:
case libvlc_MediaPlayerOpening:
case libvlc_MediaPlayerBuffering:
case libvlc_MediaPlayerStopped:
case libvlc_MediaPlayerVout:
case libvlc_MediaPlayerMediaChanged:
case libvlc_MediaPlayerForward:
case libvlc_MediaPlayerBackward:
case libvlc_MediaPlayerPausableChanged:
case libvlc_MediaPlayerTitleChanged:
case libvlc_MediaPlayerSnapshotTaken:
default:
break;
}
}
else
{
tDebug() << "Event for preloaded: " << libvlc_event_type_name(event->type);
switch ( event->type )
{
case libvlc_MediaPlayerPausableChanged:
case libvlc_MediaPlayerPlaying:
case libvlc_MediaPlayerMediaChanged:
case libvlc_MediaPlayerTitleChanged:
case libvlc_MediaPlayerSeekableChanged:
case libvlc_MediaDurationChanged:
case libvlc_MediaPlayerPositionChanged:
case libvlc_MediaPlayerPaused:
case libvlc_MediaPlayerBuffering:
libvlc_media_player_set_pause( m_vlcPreloadedPlayer, 1 );
libvlc_audio_set_volume( m_vlcPreloadedPlayer, 0.0 );
libvlc_media_player_set_position( m_vlcPreloadedPlayer, 0.0 );
break;
case libvlc_MediaPlayerTimeChanged:
case libvlc_MediaPlayerLengthChanged:
case libvlc_MediaPlayerOpening:
case libvlc_MediaPlayerEndReached:
case libvlc_MediaPlayerEncounteredError:
//TODO
#if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0))
case libvlc_MediaPlayerAudioVolume:
case libvlc_MediaPlayerMuted:
case libvlc_MediaPlayerUnmuted:
#endif
case libvlc_MediaPlayerNothingSpecial:
case libvlc_MediaPlayerStopped:
case libvlc_MediaPlayerVout:
case libvlc_MediaPlayerForward:
case libvlc_MediaPlayerBackward:
case libvlc_MediaPlayerSnapshotTaken:
default:
break;
}
}
}

View File

@@ -49,9 +49,10 @@ public:
bool isInitialized() const;
AudioState state() const;
void setCurrentSource( const QUrl& stream );
void setCurrentSource( QIODevice* stream );
void setCurrentSource( MediaStream* stream );
void setCurrentSource( const QUrl& stream, bool preload );
void setCurrentSource( QIODevice* stream, bool preload );
void setCurrentSource( MediaStream* stream, bool preload );
void setPreloadedSourceAsCurrent( void );
void play();
void pause();
@@ -65,13 +66,15 @@ public:
qreal volume() const;
qint64 currentTime() const;
qint64 totalTime() const;
void setAutoDelete ( bool ad );
void setAutoDelete ( bool ad, bool preload );
void setDspCallback( std::function< void( int, int, float*, int, int ) > cb );
static AudioOutput* instance();
libvlc_instance_t* vlcInstance() const;
void switchToPreloadedMedia( void );
public slots:
signals:
@@ -90,6 +93,7 @@ private:
void setCurrentPosition( float position );
void setTotalTime( qint64 time );
void onVlcEvent( const libvlc_event_t* event );
static void vlcEventCallback( const libvlc_event_t* event, void* opaque );
static void s_dspCallback( int frameNumber, float* samples, int nb_channels, int nb_samples );
@@ -97,9 +101,11 @@ private:
static AudioOutput* s_instance;
AudioState m_currentState;
MediaStream* m_currentStream;
MediaStream* m_preloadedStream;
bool m_seekable;
bool m_muted;
bool m_autoDelete;
bool m_preloadedAutoDelete;
bool m_havePosition;
bool m_haveTiming;
qreal m_volume;
@@ -107,7 +113,7 @@ private:
qint64 m_totalTime;
bool m_justSeeked;
bool m_initialized;
int m_initialized;
QFile m_silenceFile;
std::function< void( int state, int frameNumber, float* samples, int nb_channels, int nb_samples ) > dspPluginCallback;
@@ -115,6 +121,9 @@ private:
libvlc_instance_t* m_vlcInstance;
libvlc_media_player_t* m_vlcPlayer;
libvlc_media_t* m_vlcMedia;
libvlc_media_player_t* m_vlcBackPlayer;
libvlc_media_player_t* m_vlcPreloadedPlayer;
libvlc_media_t* m_vlcPreloadedMedia;
};
#endif // AUDIOOUTPUT_H

View File

@@ -190,7 +190,7 @@ JSAccount::reportNativeScriptJobResult( int resultId, const QVariantMap& result
.arg( serializeQVariantMap( result ) );
// Remove when new scripting api turned out to work reliably
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
//tDebug( LOGVERBOSE ) << Q_FUNC_INFO << eval;
evaluateJavaScript( eval );
}