xine-engine.cpp

来自「Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 」· C++ 代码 · 共 1,343 行 · 第 1/3 页

CPP
1,343
字号
/*************************************************************************** *   Copyright (C) 2005   Christophe Thommeret <hftom@free.fr>             * *             (C) 2005   Ian Monroe <ian@monroe.nu>                       * *             (C) 2005,6 Mark Kretschmann <markey@web.de>                 * *             (C) 2004,5 Max Howell <max.howell@methylblue.com>           * *             (C) 2003,4 J. Kofler <kaffeine@gmx.net>                     * *                                                                         * *   This program is free software; you can redistribute it and/or modify  * *   it under the terms of the GNU General Public License as published by  * *   the Free Software Foundation; either version 2 of the License, or     * *   (at your option) any later version.                                   * *                                                                         * ***************************************************************************/#define DEBUG_PREFIX "xine-engine"#include "xine-config.h"#include "xinecfg.h"#include "xine-engine.h"#include "amarok.h"#include "amarokconfig.h"//these files are from libamarok#include "playlist.h"#include "enginecontroller.h"AMAROK_EXPORT_PLUGIN( XineEngine )#include <climits>#include <cstdlib>#include <cmath>#include "debug.h"#include <klocale.h>#include <kmessagebox.h>#include <kstandarddirs.h>#include <qapplication.h>#include <qdir.h>extern "C"{    #include <unistd.h>    #include "xine-scope.h"}#ifndef LLONG_MAX#define LLONG_MAX 9223372036854775807LL#endif//define this to use xine in a more standard way//#define XINE_SAFE_MODE///some logging static globalsnamespace Log{    static uint bufferCount = 0;    static uint scopeCallCount = 1; //prevent divideByZero    static uint noSuitableBuffer = 0;}///returns the configuration we will use. there is no KInstance, so using this hacked up method.//static inline QCString configPath() { return QFile::encodeName(KStandardDirs().localkdedir() + KStandardDirs::kde_default("data") + "amarok/xine-config"); }static inline QCString configPath() { return QFile::encodeName(locate( "data", "amarok/") + "xine-config" ); }static Fader *s_fader = 0;static OutFader *s_outfader = 0;XineEngine::XineEngine()        : EngineBase()        , m_xine( 0 )        , m_stream( 0 )        , m_audioPort( 0 )        , m_eventQueue( 0 )        , m_post( 0 )        , m_preamp( 1.0 )        , m_stopFader( false )        , m_fadeOutRunning ( false )        , m_equalizerEnabled( false ){    addPluginProperty( "HasConfigure", "true" );    addPluginProperty( "HasEqualizer", "true" );    #ifndef __NetBSD__  // NetBSD does not offer audio mixing    addPluginProperty( "HasCrossfade", "true" );    #endif    addPluginProperty("HasCDDA", "true"); // new property    debug() << "hello" << endl;}XineEngine::~XineEngine(){    // Wait until the fader thread is done    if( s_fader ) {        m_stopFader = true;        s_fader->resume(); // safety call if the engine is in the pause state        s_fader->wait();    }    delete s_fader;    delete s_outfader;    if( AmarokConfig::fadeoutOnExit() ) {        bool terminateFader = false;        fadeOut( AmarokConfig::fadeoutLength(), &terminateFader, true ); // true == exiting    }    if( m_xine )       xine_config_save( m_xine, configPath() );    if( m_stream )     xine_close( m_stream );    if( m_eventQueue ) xine_event_dispose_queue( m_eventQueue );    if( m_stream )     xine_dispose( m_stream );    if( m_audioPort )  xine_close_audio_driver( m_xine, m_audioPort );    if( m_post )       xine_post_dispose( m_xine, m_post );    if( m_xine )       xine_exit( m_xine );    debug() << "xine closed\n";    debug() << "Scope statistics:\n"            << "  Average list size: " << Log::bufferCount / Log::scopeCallCount << endl            << "  Buffer failure:    " << double(Log::noSuitableBuffer*100) / Log::scopeCallCount << "%\n";}boolXineEngine::init(){   DEBUG_BLOCK   debug() << "'Bringing joy to small mexican gerbils, a few weeks at a time.'\n";   m_xine = xine_new();   if( !m_xine ) {      KMessageBox::error( 0, i18n("Amarok could not initialize xine.") );      return false;   }   #ifdef XINE_SAFE_MODE   xine_engine_set_param( m_xine, XINE_ENGINE_PARAM_VERBOSITY, 99 );   #endif   xine_config_load( m_xine, configPath() );   debug() << "w00t" << configPath() << endl;   xine_init( m_xine );   makeNewStream();   #ifndef XINE_SAFE_MODE   startTimer( 200 ); //prunes the scope   #endif   return true;}boolXineEngine::makeNewStream(){   m_currentAudioPlugin = XineCfg::outputPlugin();   m_audioPort = xine_open_audio_driver( m_xine, XineCfg::outputPlugin().local8Bit(), NULL );   if( !m_audioPort ) {      //TODO make engine method that is the same but parents the dialog for us      KMessageBox::error( 0, i18n("xine was unable to initialize any audio drivers.") );      return false;   }   m_stream = xine_stream_new( m_xine, m_audioPort, NULL );   if( !m_stream ) {      xine_close_audio_driver( m_xine, m_audioPort );      m_audioPort = NULL;      KMessageBox::error( 0, i18n("Amarok could not create a new xine stream.") );      return false;   }   if( m_eventQueue )      xine_event_dispose_queue( m_eventQueue );   xine_event_create_listener_thread(         m_eventQueue = xine_event_new_queue( m_stream ),         &XineEngine::XineEventListener,         (void*)this );   #ifndef XINE_SAFE_MODE   //implemented in xine-scope.h   m_post = scope_plugin_new( m_xine, m_audioPort );   xine_set_param( m_stream, XINE_PARAM_METRONOM_PREBUFFER, 6000 );   xine_set_param( m_stream, XINE_PARAM_IGNORE_VIDEO, 1 );   #endif#ifdef XINE_PARAM_EARLY_FINISHED_EVENT    if ( xine_check_version(1,1,1) && !(m_xfadeLength > 0) ) {        // enable gapless playback        debug() << "gapless playback enabled." << endl;        //xine_set_param(m_stream, XINE_PARAM_EARLY_FINISHED_EVENT, 1 );    }#endif   return true;}// Makes sure an audio port and a stream exist.boolXineEngine::ensureStream(){   if( !m_stream )      return makeNewStream();   return true;}boolXineEngine::load( const KURL &url, bool isStream ){    DEBUG_BLOCK    if( !ensureStream() )        return false;    Engine::Base::load( url, isStream );    if( s_outfader ) {        s_outfader->finish();        delete s_outfader;    }    if( m_xfadeLength > 0 && xine_get_status( m_stream ) == XINE_STATUS_PLAY &&         url.isLocalFile() &&         xine_get_param( m_stream, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE &&        ( m_xfadeNextTrack || //set by engine controller when switching tracks automatically         (uint) AmarokConfig::crossfadeType() == 0 ||  //crossfade always         (uint) AmarokConfig::crossfadeType() == 2 ) ) //crossfade when switching tracks manually    {       m_xfadeNextTrack = false;       // Stop a probably running fader       if( s_fader ) {           m_stopFader = true;           s_fader->finish(); // makes the fader stop abruptly           delete s_fader;       }       s_fader = new Fader( this, m_xfadeLength );       setEqualizerParameters( m_intPreamp, m_equalizerGains );    }   // for users who stubbonly refuse to use DMIX or buy a good soundcard   // why doesn't xine do this? I cannot say.   xine_close( m_stream );   debug() << "Before xine_open() *****" << endl;   if( xine_open( m_stream, QFile::encodeName( url.url() ) ) )   {      debug() << "After xine_open() *****" << endl;      #ifndef XINE_SAFE_MODE      //we must ensure the scope is pruned of old buffers      timerEvent( 0 );      xine_post_out_t *source = xine_get_audio_source( m_stream );      xine_post_in_t  *target = (xine_post_in_t*)xine_post_input( m_post, const_cast<char*>("audio in") );      xine_post_wire( source, target );      #endif      playlistChanged();      return true;   }   else   {      #ifdef XINE_PARAM_GAPLESS_SWITCH        if ( xine_check_version(1,1,1) && !(m_xfadeLength > 0) )            xine_set_param( m_stream, XINE_PARAM_GAPLESS_SWITCH, 0);      #endif   }   // FAILURE to load!   //s_fader will delete itself   determineAndShowErrorMessage();   return false;}boolXineEngine::play( uint offset ){    DEBUG_BLOCK    if( !ensureStream() )        return false;    const bool has_audio     = xine_get_stream_info( m_stream, XINE_STREAM_INFO_HAS_AUDIO );    const bool audio_handled = xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_HANDLED );    if (has_audio && audio_handled && xine_play( m_stream, 0, offset ))    {        if( s_fader )           s_fader->start( QThread::LowestPriority );        emit stateChanged( Engine::Playing );        return true;    }    //we need to stop the track that is prepped for crossfade    delete s_fader;    emit stateChanged( Engine::Empty );    determineAndShowErrorMessage();    xine_close( m_stream );    return false;}#include "statusbar/statusbar.h"voidXineEngine::determineAndShowErrorMessage(){    DEBUG_BLOCK    QString body;    debug() << "xine_get_error()\n";    switch (xine_get_error( m_stream )) {        case XINE_ERROR_NO_INPUT_PLUGIN:            body = i18n("No suitable input plugin. This often means that the url's protocol is not supported. Network failures are other possible causes.");        break;        case XINE_ERROR_NO_DEMUX_PLUGIN:            body = i18n("No suitable demux plugin. This often means that the file format is not supported.");        break;        case XINE_ERROR_DEMUX_FAILED:            body = i18n("Demuxing failed.");        break;        case XINE_ERROR_INPUT_FAILED:            body = i18n("Could not open file.");        break;        case XINE_ERROR_MALFORMED_MRL:            body = i18n("The location is malformed.");        break;        case XINE_ERROR_NONE:            // xine is thick. xine doesn't think there is an error            // but there may be! We check for other errors below.        default:            if (!xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_HANDLED ))            {                // xine can read the plugin but it didn't find any codec                // THUS xine=daft for telling us it could handle the format in canDecode!                body = i18n("There is no available decoder.");                QString const ext = Amarok::extension( m_url.url() ).lower();                if (ext == "mp3" && EngineController::installDistroCodec( "xine-engine" ))                    return;            }            else if (!xine_get_stream_info( m_stream, XINE_STREAM_INFO_HAS_AUDIO ))                body = i18n("There is no audio channel!");        break;    }    Amarok::StatusBar::instance()->longMessage(            "<b>" + i18n("Error Loading Media") + "</b><p>" + body + "<p>" + m_url.prettyURL(),            KDE::StatusBar::Error );}voidXineEngine::stop(){    if( s_fader && s_fader->running() )        s_fader->resume(); // safety call if the engine is in the pause state    if ( !m_stream )       return;    if( AmarokConfig::fadeout() && !m_fadeOutRunning || state() == Engine::Paused )    {        s_outfader = new OutFader( this, AmarokConfig::fadeoutLength() );        s_outfader->start();        ::usleep( 100 ); //to be sure engine state won't be changed before it is checked in fadeOut()        m_url = KURL(); //to ensure we return Empty from state()        std::fill( m_scope.begin(), m_scope.end(), 0 );    }    else if( !m_fadeOutRunning )    {        xine_stop( m_stream );        xine_close( m_stream );        xine_set_param( m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);    }    emit stateChanged( Engine::Empty );}voidXineEngine::pause(){    if ( !m_stream )        return;    if( xine_get_param( m_stream, XINE_PARAM_SPEED ) != XINE_SPEED_PAUSE )    {        if( s_fader && s_fader->running() )            s_fader->pause();        xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE );        xine_set_param( m_stream, XINE_PARAM_AUDIO_CLOSE_DEVICE, 1);        emit stateChanged( Engine::Paused );    }}voidXineEngine::unpause(){    if ( !m_stream )        return;        if( xine_get_param( m_stream, XINE_PARAM_SPEED ) == XINE_SPEED_PAUSE )    {        if( s_fader && s_fader->running() )            s_fader->resume();        xine_set_param( m_stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL );        emit stateChanged( Engine::Playing );    }}Engine::StateXineEngine::state() const{    if ( !m_stream || m_fadeOutRunning )       return Engine::Empty;    switch( xine_get_status( m_stream ) )    {    case XINE_STATUS_PLAY: return xine_get_param( m_stream, XINE_PARAM_SPEED )  != XINE_SPEED_PAUSE ? Engine::Playing : Engine::Paused;    case XINE_STATUS_IDLE: return Engine::Empty;    case XINE_STATUS_STOP:    default:               return m_url.isEmpty() ? Engine::Empty : Engine::Idle;    }}uint

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?