1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-01-17 22:38:33 +01:00

first draft to get rid of phonon and directly use LibVLC

This commit is contained in:
dridri 2014-09-29 23:22:39 +02:00 committed by Uwe L. Korn
parent 9bb7a06256
commit f25a3715da
12 changed files with 802 additions and 182 deletions

View File

@ -346,6 +346,9 @@ endif()
macro_log_feature(LIBSNORE_FOUND "Libsnore" "Library for notifications" "https://github.com/TheOneRing/Snorenotify" FALSE "" "")
endif()
find_package(LIBVLC REQUIRED)
macro_log_feature(LIBVLC_FOUND "LibVLC" "Provides audio output" TRUE "" "")
set(QXTWEB_FOUND TRUE)
set(QXTWEB_LIBRARIES qxtweb-standalone)
set(QXTWEB_INCLUDE_DIRS ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/web ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/network ${THIRDPARTY_DIR}/qxt/qxtweb-standalone/core ${CMAKE_CURRENT_BINARY_DIR})
@ -377,15 +380,15 @@ if (WITH_KDE4)
endif(WITH_KDE4)
macro_log_feature(KDE4_FOUND "KDE4" "Provides support for configuring Telepathy Accounts from inside Tomahawk" "https://www.kde.org" FALSE "" "")
if(NOT Phonon_FOUND)
macro_optional_find_package(Phonon 4.5.0)
endif()
macro_log_feature(Phonon_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "")
if(Phonon_FOUND)
message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4")
endif()
# if(NOT Phonon_FOUND)
# macro_optional_find_package(Phonon 4.5.0)
# endif()
# macro_log_feature(Phonon_FOUND "Phonon" "The Phonon multimedia library" "http://phonon.kde.org" TRUE "" "")
# if(Phonon_FOUND)
# message(STATUS "Phonon found; ensure that phonon-vlc is at least 0.4")
# endif()
#
IF( KDE4_FOUND OR Phonon_FOUND )
IF( CMAKE_C_FLAGS )
# KDE4 adds and removes some compiler flags that we don't like

View File

@ -0,0 +1,93 @@
# CMake module to search for LIBVLC (VLC library)
# Authors: Rohit Yadav <rohityadav89@gmail.com>
# Harald Sitter <apachelogger@ubuntu.com>
#
# If it's found it sets LIBVLC_FOUND to TRUE
# and following variables are set:
# LIBVLC_INCLUDE_DIR
# LIBVLC_LIBRARY
# LIBVLC_VERSION
if(NOT LIBVLC_MIN_VERSION)
set(LIBVLC_MIN_VERSION "0.0")
endif(NOT LIBVLC_MIN_VERSION)
# find_path and find_library normally search standard locations
# before the specified paths. To search non-standard paths first,
# FIND_* is invoked first with specified paths and NO_DEFAULT_PATH
# and then again with no specified paths to search the default
# locations. When an earlier FIND_* succeeds, subsequent FIND_*s
# searching for the same item do nothing.
if (NOT WIN32)
find_package(PkgConfig)
pkg_check_modules(PC_LIBVLC libvlc)
set(LIBVLC_DEFINITIONS ${PC_LIBVLC_CFLAGS_OTHER})
endif (NOT WIN32)
#Put here path to custom location
#example: /home/user/vlc/include etc..
find_path(LIBVLC_INCLUDE_DIR vlc/vlc.h
HINTS "$ENV{LIBVLC_INCLUDE_PATH}"
PATHS
"$ENV{LIB_DIR}/include"
"$ENV{LIB_DIR}/include/vlc"
"/usr/include"
"/usr/include/vlc"
"/usr/local/include"
"/usr/local/include/vlc"
#mingw
c:/msys/local/include
)
find_path(LIBVLC_INCLUDE_DIR PATHS "${CMAKE_INCLUDE_PATH}/vlc" NAMES vlc.h
HINTS ${PC_LIBVLC_INCLUDEDIR} ${PC_LIBVLC_INCLUDE_DIRS})
#Put here path to custom location
#example: /home/user/vlc/lib etc..
find_library(LIBVLC_LIBRARY NAMES vlc libvlc
HINTS "$ENV{LIBVLC_LIBRARY_PATH}" ${PC_LIBVLC_LIBDIR} ${PC_LIBVLC_LIBRARY_DIRS}
PATHS
"$ENV{LIB_DIR}/lib"
#mingw
c:/msys/local/lib
)
find_library(LIBVLC_LIBRARY NAMES vlc libvlc)
find_library(LIBVLCCORE_LIBRARY NAMES vlccore libvlccore
HINTS "$ENV{LIBVLC_LIBRARY_PATH}" ${PC_LIBVLC_LIBDIR} ${PC_LIBVLC_LIBRARY_DIRS}
PATHS
"$ENV{LIB_DIR}/lib"
#mingw
c:/msys/local/lib
)
find_library(LIBVLCCORE_LIBRARY NAMES vlccore libvlccore)
set(LIBVLC_VERSION ${PC_LIBVLC_VERSION})
if (NOT LIBVLC_VERSION)
# TODO: implement means to detect version on windows (vlc --version && regex? ... ultimately we would get it from a header though...)
endif (NOT LIBVLC_VERSION)
if (LIBVLC_INCLUDE_DIR AND LIBVLC_LIBRARY AND LIBVLCCORE_LIBRARY)
set(LIBVLC_FOUND TRUE)
endif (LIBVLC_INCLUDE_DIR AND LIBVLC_LIBRARY AND LIBVLCCORE_LIBRARY)
if (LIBVLC_VERSION STRLESS "${LIBVLC_MIN_VERSION}")
message(WARNING "LibVLC version not found: version searched: ${LIBVLC_MIN_VERSION}, found ${LIBVLC_VERSION}\nUnless you are on Windows this is bound to fail.")
# TODO: only activate once version detection can be garunteed (which is currently not the case on windows)
# set(LIBVLC_FOUND FALSE)
endif (LIBVLC_VERSION STRLESS "${LIBVLC_MIN_VERSION}")
if (LIBVLC_FOUND)
if (NOT LIBVLC_FIND_QUIETLY)
message(STATUS "Found LibVLC include-dir path: ${LIBVLC_INCLUDE_DIR}")
message(STATUS "Found LibVLC library path:${LIBVLC_LIBRARY}")
message(STATUS "Found LibVLCcore library path:${LIBVLCCORE_LIBRARY}")
message(STATUS "Found LibVLC version: ${LIBVLC_VERSION} (searched for: ${LIBVLC_MIN_VERSION})")
endif (NOT LIBVLC_FIND_QUIETLY)
else (LIBVLC_FOUND)
if (LIBVLC_FIND_REQUIRED)
message(FATAL_ERROR "Could not find LibVLC")
endif (LIBVLC_FIND_REQUIRED)
endif (LIBVLC_FOUND)

View File

@ -214,6 +214,7 @@ list(APPEND libSources
accounts/spotify/SpotifyInfoPlugin.cpp
audio/AudioEngine.cpp
audio/AudioOutput.cpp
collection/Collection.cpp
collection/ArtistsRequest.cpp
@ -353,6 +354,7 @@ list(APPEND libSources
utils/WeakObjectHash.cpp
utils/WeakObjectList.cpp
utils/PluginLoader.cpp
utils/MediaStream.cpp
)
add_subdirectory( accounts/configstorage )
@ -390,6 +392,7 @@ include_directories(
${ECHONEST_INCLUDE_DIR}
${LUCENEPP_INCLUDE_DIRS}
${PHONON_INCLUDES}
${LIBVLC_INCLUDES}
${Boost_INCLUDE_DIR}
${LIBPORTFWD_INCLUDE_DIR}
@ -502,6 +505,7 @@ ENDIF( UNIX AND NOT APPLE )
TARGET_LINK_LIBRARIES( tomahawklib
LINK_PRIVATE
${PHONON_LIBRARY}
${LIBVLC_LIBRARY}
# Thirdparty shipped with tomahawk
${LIBPORTFWD_LIBRARIES}

View File

@ -53,16 +53,16 @@ static QString s_aeInfoIdentifier = QString( "AUDIOENGINE" );
void
AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldState )
AudioEnginePrivate::onStateChanged( AudioOutput::AudioState newState, AudioOutput::AudioState oldState )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << oldState << newState << expectStop << q_ptr->state();
tDebug() << Q_FUNC_INFO << oldState << newState << expectStop << q_ptr->state();
if ( newState == Phonon::LoadingState )
if ( newState == AudioOutput::Loading )
{
// We don't emit this state to listeners - yet.
state = AudioEngine::Loading;
}
if ( newState == Phonon::BufferingState )
if ( newState == AudioOutput::Buffering )
{
if ( underrunCount > UNDERRUNTHRESHOLD && !underrunNotified )
{
@ -72,16 +72,16 @@ AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldSta
else
underrunCount++;
}
if ( newState == Phonon::ErrorState )
if ( newState == AudioOutput::Error )
{
q_ptr->stop( AudioEngine::UnknownError );
tDebug() << "Phonon Error:" << mediaObject->errorString() << mediaObject->errorType();
//TODO tDebug() << "Phonon Error:" << audioOutput->errorString() << audioOutput->errorType();
emit q_ptr->error( AudioEngine::UnknownError );
q_ptr->setState( AudioEngine::Error );
}
if ( newState == Phonon::PlayingState )
if ( newState == AudioOutput::Playing )
{
bool emitSignal = false;
if ( q_ptr->state() != AudioEngine::Paused && q_ptr->state() != AudioEngine::Playing )
@ -95,23 +95,23 @@ AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldSta
if ( emitSignal )
emit q_ptr->started( currentTrack );
}
if ( newState == Phonon::StoppedState && oldState == Phonon::PausedState )
if ( newState == AudioOutput::Stopped && oldState == AudioOutput::Paused )
{
// GStreamer backend hack: instead of going from PlayingState to StoppedState, it traverses PausedState
q_ptr->setState( AudioEngine::Stopped );
}
if ( oldState == Phonon::PlayingState )
if ( oldState == AudioOutput::Playing )
{
bool stopped = false;
switch ( newState )
{
case Phonon::PausedState:
case AudioOutput::Paused:
{
if ( mediaObject && currentTrack )
{
qint64 duration = mediaObject->totalTime() > 0 ? mediaObject->totalTime() : currentTrack->track()->duration() * 1000;
stopped = ( duration - 1000 < mediaObject->currentTime() );
if ( audioOutput && currentTrack )
{/* TODO
qint64 duration = audioOutput->totalTime() > 0 ? audioOutput->totalTime() : currentTrack->track()->duration() * 1000;
stopped = ( duration - 1000 < audioOutput->currentTime() ); */
}
else
stopped = true;
@ -121,7 +121,7 @@ AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldSta
break;
}
case Phonon::StoppedState:
case AudioOutput::Stopped:
{
stopped = true;
break;
@ -158,40 +158,12 @@ AudioEnginePrivate::onStateChanged( Phonon::State newState, Phonon::State oldSta
}
}
/*
void
AudioEnginePrivate::onAudioDataArrived( QMap<Phonon::AudioDataOutput::Channel, QVector<qint16> > data )
{
QMap< AudioEngine::AudioChannel, QVector< qint16 > > result;
if( data.contains( Phonon::AudioDataOutput::LeftChannel ) )
{
result[ AudioEngine::LeftChannel ] = QVector< qint16 >( data[ Phonon::AudioDataOutput::LeftChannel ] );
}
if( data.contains( Phonon::AudioDataOutput::LeftSurroundChannel ) )
{
result[ AudioEngine::LeftChannel ] = QVector< qint16 >( data[ Phonon::AudioDataOutput::LeftSurroundChannel ] );
}
if( data.contains( Phonon::AudioDataOutput::RightChannel ) )
{
result[ AudioEngine::RightChannel ] = QVector< qint16 >( data[ Phonon::AudioDataOutput::RightChannel ] );
}
if( data.contains( Phonon::AudioDataOutput::RightSurroundChannel ) )
{
result[ AudioEngine::LeftChannel ] = QVector< qint16 >( data[ Phonon::AudioDataOutput::RightSurroundChannel ] );
}
if( data.contains( Phonon::AudioDataOutput::CenterChannel ) )
{
result[ AudioEngine::LeftChannel ] = QVector< qint16 >( data[ Phonon::AudioDataOutput::CenterChannel ] );
}
if( data.contains( Phonon::AudioDataOutput::SubwooferChannel ) )
{
result[ AudioEngine::LeftChannel ] = QVector< qint16 >( data[ Phonon::AudioDataOutput::SubwooferChannel ] );
}
s_instance->audioDataArrived( result );
}
*/
AudioEngine* AudioEnginePrivate::s_instance = 0;
@ -214,30 +186,16 @@ AudioEngine::AudioEngine()
d->waitingOnNewTrack = false;
d->state = Stopped;
d->coverTempFile = 0;
d->audioEffect = 0;
d->dspPluginCallback = 0;
d->s_instance = this;
tDebug() << "Init AudioEngine";
d->audioOutput = new AudioOutput(this);
QObject::connect( d->audioOutput, SIGNAL( stateChanged( AudioOutput::AudioState, AudioOutput::AudioState ) ), d, SLOT( onStateChanged( AudioOutput::AudioState, AudioOutput::AudioState ) ) );
getchar();
qRegisterMetaType< AudioErrorCode >("AudioErrorCode");
qRegisterMetaType< AudioState >("AudioState");
d->mediaObject = new Phonon::MediaObject( this );
d->audioOutput = new Phonon::AudioOutput( Phonon::MusicCategory, this );
d->audioPath = Phonon::createPath( d->mediaObject, d->audioOutput );
d->mediaObject->setTickInterval( 150 );
connect( d->mediaObject, SIGNAL( stateChanged( Phonon::State, Phonon::State ) ), d_func(), SLOT( onStateChanged( Phonon::State, Phonon::State ) ) );
connect( d->mediaObject, SIGNAL( tick( qint64 ) ), SLOT( timerTriggered( qint64 ) ) );
connect( d->mediaObject, SIGNAL( aboutToFinish() ), SLOT( onAboutToFinish() ) );
connect( d->audioOutput, SIGNAL( volumeChanged( qreal ) ), SLOT( onVolumeChanged( qreal ) ) );
connect( d->audioOutput, SIGNAL( mutedChanged( bool ) ), SIGNAL( mutedChanged( bool ) ) );
onVolumeChanged( d->audioOutput->volume() );
setVolume( TomahawkSettings::instance()->volume() );
d->mediaObject->setCurrentSource(Phonon::MediaSource("PhononDSP::effectCallback=" + QString::number((qulonglong)&AudioEngine::dspCallback, 16)));
}
@ -245,7 +203,6 @@ AudioEngine::~AudioEngine()
{
tDebug() << Q_FUNC_INFO;
d_func()->mediaObject->stop();
TomahawkSettings::instance()->setVolume( volume() );
@ -258,13 +215,11 @@ AudioEngine::supportedMimeTypes() const
{
if ( d_func()->supportedMimeTypes.isEmpty() )
{
d_func()->supportedMimeTypes = Phonon::BackendCapabilities::availableMimeTypes();
d_func()->supportedMimeTypes << "audio/*";
d_func()->supportedMimeTypes << "audio/basic";
return d_func()->supportedMimeTypes;
}
else
return d_func()->supportedMimeTypes;
return d_func()->supportedMimeTypes;
}
@ -300,7 +255,7 @@ AudioEngine::play()
if ( isPaused() )
{
d->mediaObject->play();
d->audioOutput->play();
emit resumed();
sendNowPlayingNotification( Tomahawk::InfoSystem::InfoNowResumed );
@ -330,7 +285,7 @@ AudioEngine::pause()
tDebug( LOGEXTRA ) << Q_FUNC_INFO;
d->mediaObject->pause();
d->audioOutput->pause();
emit paused();
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( Tomahawk::InfoSystem::InfoPushData( s_aeInfoIdentifier, Tomahawk::InfoSystem::InfoNowPaused, QVariant(), Tomahawk::InfoSystem::PushNoFlag ) );
@ -358,8 +313,8 @@ AudioEngine::stop( AudioErrorCode errorCode )
else
setState( Error );
if ( d->mediaObject->state() != Phonon::StoppedState )
d->mediaObject->stop();
if ( d->audioOutput->state() != AudioOutput::Stopped )
d->audioOutput->stop();
emit stopped();
@ -382,11 +337,7 @@ bool AudioEngine::activateDataOutput()
{
Q_D( AudioEngine );
d->audioDataPath = Phonon::createPath( d->mediaObject, d->audioDataOutput );
connect( d->audioDataOutput, SIGNAL( dataReady( QMap< Phonon::AudioDataOutput::Channel, QVector< qint16 > > ) ),
d_func(), SLOT( onAudioDataArrived( QMap< Phonon::AudioDataOutput::Channel, QVector< qint16 > > ) ) );
return d->audioDataPath.isValid();
return true;
}
@ -395,15 +346,9 @@ bool AudioEngine::deactivateDataOutput()
{
Q_D( AudioEngine );
return d->audioDataPath.disconnect();
return true;
}
void AudioEngine::audioDataArrived( QMap< AudioEngine::AudioChannel, QVector< qint16 > >& data )
{
emit audioDataReady( data );
}
void
AudioEngine::previous()
{
@ -490,16 +435,11 @@ bool
AudioEngine::canSeek()
{
Q_D( AudioEngine );
bool phononCanSeek = true;
/* TODO: When phonon properly reports this, re-enable it
if ( d->mediaObject && d->mediaObject->isValid() )
phononCanSeek = d->mediaObject->isSeekable();
*/
/*
if ( d->playlist.isNull() )
return phononCanSeek;
return !d->playlist.isNull() && ( d->playlist.data()->seekRestrictions() != PlaylistModes::NoSeek ) && phononCanSeek;
return true;
*/
return !d->playlist.isNull() && ( d->playlist.data()->seekRestrictions() != PlaylistModes::NoSeek );
}
@ -517,7 +457,7 @@ AudioEngine::seek( qint64 ms )
if ( isPlaying() || isPaused() )
{
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << ms;
d->mediaObject->seek( ms );
d->audioOutput->seek( ms );
emit seeked( ms );
}
}
@ -542,6 +482,7 @@ AudioEngine::setVolume( int percentage )
if ( percentage > 0 && d->audioOutput->isMuted() )
d->audioOutput->setMuted( false );
emit volumeChanged( percentage );
}
@ -564,6 +505,7 @@ bool
AudioEngine::isMuted() const
{
return d_func()->audioOutput->isMuted();
return 0;
}
@ -766,18 +708,22 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString
QSharedPointer<QNetworkReply> qnr = io.objectCast<QNetworkReply>();
if ( !qnr.isNull() )
{
d->mediaObject->setCurrentSource( new QNR_IODeviceStream( qnr, this ) );
tLog() << "CASE 1";
d->audioOutput->setCurrentSource( new QNR_IODeviceStream( qnr, this ) );
// We keep track of the QNetworkReply in QNR_IODeviceStream
// and Phonon handles the deletion of the
// QNR_IODeviceStream object
ioToKeep.clear();
d->mediaObject->currentSource().setAutoDelete( true );
// TODO d->audioOutput->setAutoDelete( true );
}
else
{
d->mediaObject->setCurrentSource( io.data() );
tLog() << "CASE 2";
/* TODO
d->audioOutput->setCurrentSource( io.data() );
// We handle the deletion via tracking in d->input
d->mediaObject->currentSource().setAutoDelete( false );
d->audioOutput->setAutoDelete( false );
*/
}
}
else
@ -788,6 +734,7 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString
*/
if ( !TomahawkUtils::isLocalResult( url ) )
{
tLog() << "CASE 3";
QUrl furl = url;
if ( url.contains( "?" ) )
{
@ -796,19 +743,22 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString
}
tLog( LOGVERBOSE ) << "Passing to Phonon:" << furl;
d->mediaObject->setCurrentSource( furl );
d->audioOutput->setCurrentSource( furl );
}
else
{
tLog() << "CASE 4";
QString furl = url;
if ( furl.startsWith( "file://" ) )
furl = furl.right( furl.length() - 7 );
tLog( LOGVERBOSE ) << "Passing to Phonon:" << QUrl::fromLocalFile( furl );
d->mediaObject->setCurrentSource( QUrl::fromLocalFile( furl ) );
d->audioOutput->setCurrentSource( QUrl::fromLocalFile( furl ) );
}
d->mediaObject->currentSource().setAutoDelete( true );
/* TODO
d->audioOutput->setAutoDelete( true );
*/
}
if ( !d->input.isNull() )
@ -817,7 +767,7 @@ AudioEngine::performLoadTrack( const Tomahawk::result_ptr result, const QString
d->input.clear();
}
d->input = ioToKeep;
d->mediaObject->play();
d->audioOutput->play();
if ( TomahawkSettings::instance()->privateListeningMode() != TomahawkSettings::FullyPrivate )
{
@ -1290,14 +1240,16 @@ AudioEngine::setState( AudioState state )
qint64
AudioEngine::currentTime() const
{
return d_func()->mediaObject->currentTime();
// TODO return d_func()->mediaObject->currentTime();
return 0;
}
qint64
AudioEngine::currentTrackTotalTime() const
{
return d_func()->mediaObject->totalTime();
// TODO return d_func()->mediaObject->totalTime();
return 0;
}
@ -1384,29 +1336,10 @@ AudioEngine::setCurrentTrackPlaylist( const playlistinterface_ptr& playlist )
}
void
AudioEngine::dspCallback( signed short* samples, int nb_channels, int nb_samples )
{
AudioEngine::instance()->dspCallbackInternal( samples, nb_channels, nb_samples );
}
void
AudioEngine::dspCallbackInternal( signed short* samples, int nb_channels, int nb_samples )
{
Q_D( AudioEngine );
if ( d->dspPluginCallback != 0 )
{
d->dspPluginCallback( samples, nb_channels, nb_samples );
}
}
void
AudioEngine::setDspCallback( void ( *cb ) ( signed short*, int, int ) )
{
Q_D( AudioEngine );
d->dspPluginCallback = cb;
d->audioOutput->setDspCallback( cb );
}

View File

@ -156,7 +156,7 @@ signals:
void paused();
void resumed();
void audioDataReady( QMap< AudioEngine::AudioChannel, QVector<qint16> > data );
// void audioDataReady( QMap< AudioEngine::AudioChannel, QVector<qint16> > data );
void stopAfterTrackChanged();
@ -200,9 +200,7 @@ private:
void setState( AudioState state );
void setCurrentTrackPlaylist( const Tomahawk::playlistinterface_ptr& playlist );
static void dspCallback( signed short* samples, int nb_channels, int nb_samples );
void dspCallbackInternal( signed short* samples, int nb_channels, int nb_samples );
void audioDataArrived( QMap< AudioEngine::AudioChannel, QVector< qint16 > >& data );
// void audioDataArrived( QMap< AudioEngine::AudioChannel, QVector< qint16 > >& data );
Q_DECLARE_PRIVATE( AudioEngine )

View File

@ -1,10 +1,12 @@
/*
#include <phonon/MediaObject>
#include <phonon/AudioOutput>
#include <phonon/Path>
#include <phonon/BackendCapabilities>
#include <phonon/Effect>
#include <phonon/EffectParameter>
*/
#include "AudioOutput.h"
#include <stdint.h>
@ -29,8 +31,8 @@ public:
public slots:
void onStateChanged( Phonon::State newState, Phonon::State oldState );
void onAudioDataArrived( QMap< Phonon::AudioDataOutput::Channel, QVector< qint16 > > data );
void onStateChanged( AudioOutput::AudioState newState, AudioOutput::AudioState oldState );
// void onAudioDataArrived( QMap< Phonon::AudioDataOutput::Channel, QVector< qint16 > > data );
private:
QSharedPointer<QIODevice> input;
@ -41,6 +43,8 @@ private:
Tomahawk::playlistinterface_ptr currentTrackPlaylist;
Tomahawk::playlistinterface_ptr queue;
AudioOutput* audioOutput;
/*
Phonon::MediaObject* mediaObject;
Phonon::AudioOutput* audioOutput;
@ -49,7 +53,7 @@ private:
Phonon::AudioDataOutput* audioDataOutput;
Phonon::Path audioDataPath;
*/
unsigned int timeElapsed;
bool expectStop;

View File

@ -0,0 +1,320 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AudioOutput.h"
#include "utils/Logger.h"
#include <QVarLengthArray>
#include <QFile>
#include <QDir>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <vlc/libvlc.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_player.h>
#include <vlc/libvlc_version.h>
static QString s_aeInfoIdentifier = QString( "AUDIOOUTPUT" );
AudioOutput* AudioOutput::s_instance = 0;
AudioOutput*
AudioOutput::instance()
{
return AudioOutput::s_instance;
}
AudioOutput::AudioOutput( QObject* parent )
: QObject( parent )
, dspPluginCallback( 0 )
, currentState( Stopped )
, muted( false )
, m_volume( 1.0 )
{
tDebug() << Q_FUNC_INFO;
AudioOutput::s_instance = this;
currentStream = 0;
QList<QByteArray> args;
args << "--ignore-config";
args << "--verbose=11111";
args << "--no-plugins-cache";
args << "--extraintf=logger";
args << "--no-media-library";
args << "--no-osd";
args << "--no-stats";
args << "--no-video-title-show";
args << "--no-snapshot-preview";
args << "--no-xlib";
args << "--services-discovery=''";
args << "--no-one-instance";
args << "--no-video";
// args << "--audio-filter=dsp";
// args << QString("--dsp-callback=%1").arg((quint64)&AudioOutput::s_dspCallback, 0, 16).toAscii();
QVarLengthArray< const char * , 64 > vlcArgs( args.size() );
for ( int i = 0 ; i < args.size() ; ++i ) {
vlcArgs[i] = args.at( i ).constData();
tDebug() << args.at( i );
}
// Create and initialize a libvlc instance (it should be done only once)
if ( !( vlcInstance = libvlc_new( vlcArgs.size(), vlcArgs.constData() ) ) ) {
tDebug() << "libVLC: could not initialize";
}
vlcPlayer = libvlc_media_player_new( vlcInstance );
getchar();
tDebug() << "AudioOutput::AudioOutput OK !\n";
}
AudioOutput::~AudioOutput()
{
tDebug() << Q_FUNC_INFO;
}
void
AudioOutput::setCurrentSource(MediaStream stream)
{
setCurrentSource( new MediaStream(stream) );
}
void
AudioOutput::setCurrentSource(MediaStream* stream)
{
tDebug() << Q_FUNC_INFO;
currentStream = stream;
currentState = Loading;
QByteArray url;
switch (stream->type()) {
case MediaStream::Unknown:
tDebug() << "MediaStream Type is Invalid:" << stream->type();
break;
case MediaStream::Empty:
tDebug() << "MediaStream is empty.";
break;
case MediaStream::Url:
tDebug() << "MediaStream::Url:" << stream->url();
if (stream->url().scheme().isEmpty()) {
url = "file:///";
if (stream->url().isRelative())
url.append(QFile::encodeName(QDir::currentPath()) + '/');
}
url += stream->url().toEncoded();
break;
case MediaStream::Stream:
url = QByteArray("imem://");
break;
}
tDebug() << "MediaStream::Final Url:" << url;
vlcMedia = libvlc_media_new_location( vlcInstance, url.constData() );
libvlc_media_player_set_media( vlcPlayer, vlcMedia );
if ( stream->type() == MediaStream::Stream ) {
libvlc_media_add_option_flag(vlcMedia, "imem-cat=4", libvlc_media_option_trusted);
libvlc_media_add_option_flag(vlcMedia, (QString("imem-data=") + QString::number((quint64)stream)).toUtf8().data(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(vlcMedia, (QString("imem-get=") + QString::number((quint64)&MediaStream::readCallback)).toUtf8().data(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(vlcMedia, (QString("imem-release=") + QString::number((quint64)&MediaStream::readDoneCallback)).toUtf8().data(), libvlc_media_option_trusted);
libvlc_media_add_option_flag(vlcMedia, (QString("imem-seek=") + QString::number((quint64)&MediaStream::seekCallback)).toUtf8().data(), libvlc_media_option_trusted);
}
libvlc_media_add_option(vlcMedia, "audio-filter=dsp");
libvlc_media_add_option(vlcMedia, ":audio-filter=dsp");
libvlc_media_add_option(vlcMedia, "--audio-filter=dsp");
libvlc_media_add_option(vlcMedia, "audio-filter dsp");
libvlc_media_add_option(vlcMedia, ":audio-filter dsp");
libvlc_media_add_option(vlcMedia, "--audio-filter dsp");
currentState = Stopped;
}
AudioOutput::AudioState
AudioOutput::state()
{
tDebug() << Q_FUNC_INFO;
return currentState;
}
void
AudioOutput::setState( AudioState state )
{
tDebug() << Q_FUNC_INFO;
emit stateChanged ( state, currentState );
currentState = state;
}
void
AudioOutput::play()
{
tDebug() << Q_FUNC_INFO;
if ( libvlc_media_player_is_playing ( vlcPlayer ) ) {
libvlc_media_player_set_pause ( vlcPlayer, 0 );
} else {
libvlc_media_player_play ( vlcPlayer );
}
setState( Playing );
}
void
AudioOutput::pause()
{
tDebug() << Q_FUNC_INFO;
// libvlc_media_player_pause( vlcPlayer );
libvlc_media_player_set_pause ( vlcPlayer, 1 );
setState( Paused );
}
void
AudioOutput::stop()
{
tDebug() << Q_FUNC_INFO;
libvlc_media_player_stop ( vlcPlayer );
setState( Stopped );
}
void
AudioOutput::seek( qint64 milliseconds )
{
tDebug() << Q_FUNC_INFO;
switch ( currentState ) {
case Playing:
case Paused:
case Loading:
case Buffering:
break;
default:
// Seeking while not being in a playingish state is cached for later.
// TODO m_seekpoint = milliseconds;
return;
}
tDebug() << "AudioOutput:: seeking" << milliseconds << "msec";
libvlc_media_player_set_time ( vlcPlayer, milliseconds );
/*
const qint64 time = currentTime();
const qint64 total = totalTime();
*/
/*
// Reset last tick marker so we emit time even after seeking
if (time < m_lastTick)
m_lastTick = time;
if (time < total - m_prefinishMark)
m_prefinishEmitted = false;
if (time < total - ABOUT_TO_FINISH_TIME)
m_aboutToFinishEmitted = false;
*/
}
bool
AudioOutput::isMuted()
{
tDebug() << Q_FUNC_INFO;
return muted;
}
void
AudioOutput::setMuted(bool m)
{
tDebug() << Q_FUNC_INFO;
muted = m;
if ( muted == true ) {
libvlc_audio_set_volume( vlcPlayer, 0 );
} else {
libvlc_audio_set_volume( vlcPlayer, m_volume * 100.0 );
}
}
qreal
AudioOutput::volume()
{
tDebug() << Q_FUNC_INFO;
return muted ? 0 : m_volume;
}
void
AudioOutput::setVolume(qreal vol)
{
tDebug() << Q_FUNC_INFO;
m_volume = vol;
if ( !muted ) {
libvlc_audio_set_volume( vlcPlayer, m_volume * 100.0 );
}
}
void
AudioOutput::s_dspCallback( signed short* samples, int nb_channels, int nb_samples )
{
tDebug() << Q_FUNC_INFO;
AudioOutput::instance()->dspCallback( samples, nb_channels, nb_samples );
}
void
AudioOutput::dspCallback( signed short* samples, int nb_channels, int nb_samples )
{
if ( dspPluginCallback != 0 )
{
dspPluginCallback( samples, nb_channels, nb_samples );
}
}
void
AudioOutput::setDspCallback( void ( *cb ) ( signed short*, int, int ) )
{
dspPluginCallback = cb;
}

View File

@ -0,0 +1,87 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef AUDIOOUTPUT_H
#define AUDIOOUTPUT_H
#include "../Typedefs.h"
#include "DllMacro.h"
#include "utils/MediaStream.h"
struct libvlc_instance_t;
struct libvlc_media_player_t;
struct libvlc_media_t;
class DLLEXPORT AudioOutput : public QObject
{
Q_OBJECT
public:
enum AudioState { Stopped = 0, Playing = 1, Paused = 2, Error = 3, Loading = 4, Buffering = 5 };
explicit AudioOutput(QObject* parent = 0);
~AudioOutput();
AudioState state();
void setCurrentSource(MediaStream stream);
void setCurrentSource(MediaStream* stream);
void play();
void pause();
void stop();
void seek(qint64 milliseconds);
bool isMuted();
void setMuted(bool m);
void setVolume(qreal vol);
qreal volume();
void setDspCallback( void ( *cb ) ( signed short*, int, int ) );
static AudioOutput* instance();
public slots:
signals:
void stateChanged( AudioOutput::AudioState, AudioOutput::AudioState );
private:
void setState( AudioState state );
static void s_dspCallback( signed short* samples, int nb_channels, int nb_samples );
void dspCallback( signed short* samples, int nb_channels, int nb_samples );
void ( *dspPluginCallback ) ( signed short* samples, int nb_channels, int nb_samples );
static AudioOutput* s_instance;
AudioState currentState;
MediaStream* currentStream;
bool muted;
qreal m_volume;
libvlc_instance_t* vlcInstance;
libvlc_media_player_t* vlcPlayer;
libvlc_media_t* vlcMedia;
};
#endif // AUDIOOUTPUT_H

View File

@ -0,0 +1,128 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "MediaStream.h"
#include "utils/Logger.h"
static QString s_aeInfoIdentifier = QString( "MEDIASTREAM" );
MediaStream::MediaStream()
: m_type( Unknown )
, m_url( QUrl() )
, m_pos( 0 )
, m_streamSize( 0 )
{
tDebug() << Q_FUNC_INFO;
}
MediaStream::MediaStream( const QUrl &url )
: m_type(Url)
{
tDebug() << Q_FUNC_INFO;
m_url = url;
}
MediaStream::~MediaStream()
{
tDebug() << Q_FUNC_INFO;
}
MediaStream::MediaType
MediaStream::type()
{
tDebug() << Q_FUNC_INFO;
return m_type;
}
QUrl
MediaStream::url()
{
tDebug() << Q_FUNC_INFO;
return m_url;
}
qint64
MediaStream::streamSize()
{
return m_streamSize;
}
void
MediaStream::setStreamSize( qint64 size )
{
m_streamSize = size;
}
int
MediaStream::readCallback ( void* data, const char* cookie, int64_t* dts, int64_t* pts, unsigned* flags, size_t* bufferSize, void** buffer )
{
tDebug() << Q_FUNC_INFO;
Q_UNUSED(cookie);
Q_UNUSED(dts);
Q_UNUSED(pts);
Q_UNUSED(flags);
MediaStream* that = static_cast < MediaStream * > ( data );
*bufferSize = that->needData(buffer);
return 0;
}
int
MediaStream::readDoneCallback ( void *data, const char *cookie, size_t bufferSize, void *buffer )
{
tDebug() << Q_FUNC_INFO;
Q_UNUSED(data);
Q_UNUSED(cookie);
Q_UNUSED(bufferSize);
delete static_cast<char *>(buffer);
return 0;
}
int
MediaStream::seekCallback ( void *data, const uint64_t pos )
{
tDebug() << Q_FUNC_INFO;
MediaStream* that = static_cast < MediaStream * > ( data );
if ( static_cast < int64_t > ( pos ) > that->streamSize() ) {
return -1;
}
that->m_pos = pos;
return 0;
}

View File

@ -0,0 +1,63 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2014, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MEDIASTREAM_H
#define MEDIASTREAM_H
#include "../Typedefs.h"
#include "DllMacro.h"
#include "utils/Logger.h"
#include <stdint.h>
#include <QUrl>
class DLLEXPORT MediaStream
{
public:
enum MediaType { Unknown = -1, Empty = 0, Url = 1, Stream = 2 };
MediaStream();
MediaStream( const QUrl &url );
virtual ~MediaStream();
MediaType type();
QUrl url();
void setStreamSize( qint64 size );
qint64 streamSize();
virtual void seekStream( qint64 offset ) { (void)offset; }
virtual size_t needData ( void** buffer ) { (void)buffer; tDebug() << Q_FUNC_INFO; return 0; }
static int readCallback ( void* data, const char* cookie, int64_t* dts, int64_t* pts, unsigned* flags, size_t* bufferSize, void** buffer );
static int readDoneCallback ( void *data, const char *cookie, size_t bufferSize, void *buffer );
static int seekCallback ( void *data, const uint64_t pos );
protected:
MediaType m_type;
QUrl m_url;
qint64 m_pos;
qint64 m_streamSize;
};
#endif // MEDIASTREAM_H

View File

@ -33,11 +33,15 @@ using namespace Tomahawk;
#define BLOCK_SIZE 1048576
QNR_IODeviceStream::QNR_IODeviceStream( const QSharedPointer<QNetworkReply>& reply, QObject* parent )
: Phonon::AbstractMediaStream( parent )
: QObject( parent )
, MediaStream()
, m_networkReply( reply )
, m_pos( 0 )
, m_timer( new QTimer( this ) )
{
tDebug() << Q_FUNC_INFO;
m_type = MediaStream::Stream;
if ( !m_networkReply->isOpen() ) {
m_networkReply->open(QIODevice::ReadOnly);
}
@ -50,7 +54,7 @@ QNR_IODeviceStream::QNR_IODeviceStream( const QSharedPointer<QNetworkReply>& rep
// All data is ready, read it!
m_data = m_networkReply->readAll();
Q_ASSERT( m_networkReply->atEnd() );
setStreamSeekable( true );
//TODO setStreamSeekable( true );
setStreamSize( m_data.size() );
}
else
@ -59,7 +63,7 @@ QNR_IODeviceStream::QNR_IODeviceStream( const QSharedPointer<QNetworkReply>& rep
QVariant contentLength = m_networkReply->header( QNetworkRequest::ContentLengthHeader );
if ( contentLength.isValid() && contentLength.toLongLong() > 0 )
{
setStreamSize( contentLength.toLongLong() );
//TODO setStreamSize( contentLength.toLongLong() );
}
// Just consume all data that is already available.
m_data = m_networkReply->readAll();
@ -73,53 +77,38 @@ QNR_IODeviceStream::QNR_IODeviceStream( const QSharedPointer<QNetworkReply>& rep
QNR_IODeviceStream::~QNR_IODeviceStream()
{
}
void
QNR_IODeviceStream::enoughData()
{
m_timer->stop();
}
void
QNR_IODeviceStream::needData()
{
m_timer->start();
moreData();
}
void
QNR_IODeviceStream::reset()
{
m_pos = 0;
tDebug() << Q_FUNC_INFO;
}
void
QNR_IODeviceStream::seekStream( qint64 offset )
{
tDebug() << Q_FUNC_INFO;
m_pos = offset;
}
void
QNR_IODeviceStream::moreData()
size_t
QNR_IODeviceStream::needData ( void** buffer )
{
// tDebug() << Q_FUNC_INFO;
QByteArray data = m_data.mid( m_pos, BLOCK_SIZE );
m_pos += data.size();
if ( ( data.size() == 0 ) && m_networkReply->atEnd() && m_networkReply->isFinished() )
{
// We're done.
endOfData();
m_timer->stop();
//TODO endOfData();
return 0;
}
else
{
writeData( data );
}
}
*buffer = new char[data.size()];
memcpy(*buffer, data.data(), data.size());
tDebug() << Q_FUNC_INFO << " Returning buffer with size " << data.size();
return data.size();
}
void
QNR_IODeviceStream::readyRead()

View File

@ -25,37 +25,35 @@
#include "DllMacro.h"
#include <phonon/abstractmediastream.h>
//#include <phonon/abstractmediastream.h>
#include <QByteArray>
#include <QNetworkReply>
#include <QSharedPointer>
#include "MediaStream.h"
class QIODevice;
class QTimer;
namespace Tomahawk
{
class DLLEXPORT QNR_IODeviceStream : public Phonon::AbstractMediaStream
class DLLEXPORT QNR_IODeviceStream : public QObject, public MediaStream
{
Q_OBJECT
public:
explicit QNR_IODeviceStream( const QSharedPointer<QNetworkReply>& reply, QObject *parent = 0 );
~QNR_IODeviceStream();
virtual void enoughData();
virtual void needData();
virtual void reset();
virtual void seekStream( qint64 offset );
virtual size_t needData ( void** buffer );
private slots:
void moreData();
void readyRead();
private:
QByteArray m_data;
QSharedPointer<QNetworkReply> m_networkReply;
qint64 m_pos;
QTimer* m_timer;
};