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 + -
显示快捷键?