📄 qmmpg123player.cpp
字号:
/* qmmpg123player.cpp * * $Id: qmmpg123player.cpp,v 1.56.2.2 2002/10/11 06:39:03 kyllingstad Exp $ * * Apollo sound player: http://www.apolloplayer.org * Copyright(C) 2000-2002 Apollo Team. See CREDITS file. * * 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The GNU General Public License is also available online at: * * http://www.gnu.org/copyleft/gpl.html */#include <errno.h>#include <iostream>#include <signal.h>#include <stdio.h>#include <string.h>#include <sys/socket.h>#include <sys/time.h>#include <sys/wait.h>#include <unistd.h>#include <qfile.h>#include <qfileinfo.h>#include <qmessagebox.h>#include <qsocketnotifier.h>#include <qtextstream.h>#include <qtimer.h>#include "qmmpg123player.h"#include "qmconfig.h"bool QmMpg123Player::s_OurTermSignal = false;bool QmMpg123Player::s_FailFlag = false;/** * @file qmmpg123player.cpp * @brief Interface to mpg123*//*! \class QmMpg123Player qmmpg123player.h \brief Provides functionality for communicating with mpg123. This class is used to communicate with <a href="http://www.mpg123.com">mpg123</a> to play MP3 files. The communication is done through pipes. Apollo sends a set of commands to mpg123, and mpg123 provides Apollo with playing info. Some of the code in this class was taken from Andreas Neuhaus' IRMP3. \warning The current mpg123 version does not handle all MP3 files very well. With some MP3 files, mpg123 will provide faulty playing info but play the song correctly. The user interface will try to detect this, but may still act a little weird. \warning mpg123 is very buggy with a buffer enabled. Stop is not immediate (Pause seems to be, though), jump forward is not immediate (but jump backward seems to be) and not even quit is immediate.*//*! Initializes the player with some variables. The player is not started. Call init() for that.*/QmMpg123Player::QmMpg123Player() : QmPlayer(0, 0), m_ID3Format( "%1 - %2" ), m_Playing( false ), m_Paused( false ), m_NewSong( false ), m_GotNewSong( false ), m_BufferLeftovers( 0 ), m_SendFd( 0 ), m_RecvFd( 0 ), m_PlayerPid( 0 ), m_BufferPid( 0 ), m_FrameCount( 0 ), m_Seconds( 0 ){ s_OurTermSignal = false; s_FailFlag = false; m_pTimer = new QTimer( this ); connect( m_pTimer, SIGNAL( timeout() ), this, SLOT( releaseTimingInformation() ) );}/*! Sends a termination signal to the mpg123 process. \sa terminate()*/QmMpg123Player::~QmMpg123Player(){ terminate();}/*! Terminates the player. \sa init() */voidQmMpg123Player::terminate(){ s_OurTermSignal = true; strcpy(m_WriteBuffer, "QUIT"); sendCommand( m_WriteBuffer );//kill(m_PlayerPid, SIGQUIT);//kill(m_PlayerPid, SIGTERM);#ifdef ENABLE_MPG123_BUFFER kill(m_PlayerPid, SIGKILL);#endif delete m_pReadNotifier; close(m_SendFd); close(m_RecvFd);}/*! Starts mpg123. If a mpg123 process is already running, it will be terminated and another one restarted. \return -1 if failure, 0 otherwise. \sa terminate()*/intQmMpg123Player::init(){ // check if player is already running if(m_PlayerPid != 0) terminate(); // make socketpair to communicate with player if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd_send) < 0) return -1; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd_recv) < 0) return -1; // handle signals signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, QmMpg123Player::failHandler); struct sigaction *tmp = new struct sigaction; tmp->sa_handler = QmMpg123Player::childHandler; tmp->sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, tmp, 0); QmConfig *conf = QmConfig::instance(); QString mpgpath = conf->getString( "mpg123", "path" ); QFileInfo mpg123player(mpgpath); if( ! (mpg123player.exists() && mpg123player.isExecutable())) { QMessageBox::critical( 0, QObject::tr("Apollo Error"), QObject::tr("Apollo tried to start the mpg123 player as:<br>" "<center>\"%1\"</center><br>" "but failed." "<p>" "Does the file exists and is it executable?" "<p>" "You can specify the mpg123 path in the configuration dialog.").arg(mpgpath)); return -1; } m_PlayerPid = fork(); if (m_PlayerPid == 0) { // pipe in/output through socket to parent dup2(fd_send[0], STDIN_FILENO); close(fd_send[0]); close(fd_send[1]); dup2(fd_recv[0], STDOUT_FILENO); dup2(fd_recv[0], STDERR_FILENO); close(fd_recv[0]); close(fd_recv[1]); QString buffer = conf->getString( "mpg123", "buffer" ); int buffer_size = buffer.toInt()*1024; // Multiply to get Kb buffer.sprintf( "%d", buffer_size ); int rc = 0; QString channels = conf->getString( "mpg123", "channels" ); int downsample = conf->getInt( "mpg123", "downsample" ); if ( downsample < 0 || downsample > 2 ) downsample = 0; // Spawn player. -R for starting mpg123 in remote mode (i.e. we're using // mpg123 through commands via pipe). The last argument, '-', is necessary // otherwise mpg123 will quit immediately (dash is an alternative to an // URL or file name). const char *args[10]; int i = 0; QString artsdsp = conf->getString("mpg123", "artsdsp"); QFileInfo artsdspi(artsdsp); // debug: exists || warn user; executable || warn user if ( artsdspi.isExecutable() ) { args[i++] = artsdsp.latin1(); args[i++] = "-m"; args[i++] = mpgpath.latin1(); } else args[i++] = mpgpath.latin1(); if ( buffer != "0" ) { args[i++] = "-b"; args[i++] = buffer.ascii(); } if ( channels == "mono" ) args[i++] = "-m"; if ( downsample != 0 ) args[i++] = downsample == 1 ? "-2" : "-4"; args[i++] = "-R"; args[i++] = "-"; args[i++] = 0; rc = execvp(args[0], (char *const *)args); // Note that all std ouput is now piped to the parent.// const char *args[8];// int i = 0;// args[i++] = mpgpath.ascii();// if ( buffer != "0" )// {// args[i++] = "-b";// args[i++] = buffer.ascii();// }// if ( channels == "mono" )// args[i++] = "-m";// if ( downsample != 0 )// args[i++] = downsample == 1 ? "-2" : "-4";// args[i++] = "-R";// args[i++] = "-";// args[i++] = 0;// rc = execvp(mpgpath.ascii(), (char *const *)args); // never reached if exec was ok _exit(-1); } // parent (Apollo) continues here close(fd_send[0]); m_SendFd = fd_send[1]; close(fd_recv[0]); m_RecvFd = fd_recv[1]; m_pReadNotifier = new QSocketNotifier(m_RecvFd, QSocketNotifier::Read); QObject::connect(m_pReadNotifier, SIGNAL(activated(int)), this, SLOT(dataReceived())); FD_SET(m_RecvFd, &mpg123_fdset); s_OurTermSignal = false; // Check whether we have a child (player) process or not. int status; sleep(1); if(waitpid(-1, &status, WNOHANG) == m_PlayerPid) { m_Message = ""; QTextOStream ostr(&m_Message); ostr << "mpg123 process exited with status " << WIFEXITED(status) << ".\n"; emit playerMessage(m_Message); m_PlayerPid = 0; return -1; } return 0;}/*! Plays the song \a filename.*/voidQmMpg123Player::play( const QString &filename){ m_FrameCount = -1; m_Seconds = -1; m_TotalSeconds = -1; m_NewSong = true; m_GotNewSong = false; m_Filename = filename; QFileInfo fi( m_Filename ); m_FilePath = fi.absFilePath(); m_FileExtension = fi.extension(); sprintf(m_WriteBuffer, "LOAD %s", m_Filename.latin1()); sendCommand( m_WriteBuffer ); startTimer();}/*! Plays the current song from the beginning. \sa stop()*/voidQmMpg123Player::play(){ m_FrameCount = -1; m_TotalSeconds = m_Seconds = -1; m_NewSong = true; m_GotNewSong = false; restart(); startTimer();}/*! Starts the timer used for emitting time values to clients.*/voidQmMpg123Player::startTimer(){ m_pTimer->start( 500, false );}/*! Stops the player. \sa play()*/voidQmMpg123Player::stop(){ m_FrameCount = -1; m_Seconds = m_TotalSeconds = -1; strcpy(m_WriteBuffer, "STOP"); sendCommand( m_WriteBuffer ); m_Playing = false; m_pTimer->stop();}/*! Pauses the player.*/voidQmMpg123Player::pause(){ strcpy(m_WriteBuffer, "PAUSE"); sendCommand( m_WriteBuffer );}/*! Restarts the song. \sa jump(int)*/voidQmMpg123Player::restart(){ jump(0);}/*! Jumps to a specific position in the song. @param pos seconds into song \sa restart()*/voidQmMpg123Player::jump( int pos ){ sprintf(m_WriteBuffer, "JUMP %d", pos * m_TotalFrames / m_TotalSeconds); sendCommand( m_WriteBuffer );}/*! Reads the response from mpg123.*/voidQmMpg123Player::dataReceived(){// memset(m_ReadBuffer, 0, READ_BUF_SIZE); if(s_FailFlag) return; int bytes_read; do { bytes_read = read(m_RecvFd, m_ReadBuffer + m_BufferLeftovers, READ_BUF_SIZE - m_BufferLeftovers ); m_ReadBuffer[bytes_read + m_BufferLeftovers] = '\0';// cout << "$$ " << m_ReadBuffer << " $$"; char *buffer_pointers[50]; char **buffer_ptr = buffer_pointers; // Split lines into separate strings and store their pointers char *tmp = m_ReadBuffer; char *last_tmp = tmp; while ( *tmp != 0 ) { if ( *tmp == '\n' ) { *tmp = 0; *buffer_ptr = last_tmp; ++buffer_ptr; last_tmp = tmp + 1; } ++tmp; } *buffer_ptr = 0; // This ends the pointer list buffer_ptr = buffer_pointers; // Start from the top char *line_ptr = m_ReadBuffer; int line_counter = 1; while ( *buffer_ptr != 0 ) { line_ptr = *buffer_ptr; if( line_ptr[0] == '@') parseCodes( line_ptr ); else { m_Message = ""; QTextOStream ostr(&m_Message); ostr << "dataReceived(): Received " << bytes_read << " bytes, skipping line " << line_counter << ":\n" << line_ptr << " - Filename: " << m_Filename << "\n"; emit playerMessage(m_Message); } // Jump to next string, if any ++line_counter; ++buffer_ptr; } // There's some text without an endline if ( tmp <= last_tmp ) m_BufferLeftovers = 0; else { // Read all available data but line still not terminated: if ( bytes_read != READ_BUF_SIZE - m_BufferLeftovers) { m_Message = ""; QTextOStream ostr(&m_Message); ostr << m_BufferLeftovers << " bytes left over:\n" << '#' << last_tmp << "#\n"; emit playerMessage(m_Message); } int leftovers = tmp - last_tmp; m_BufferLeftovers = leftovers; // Copy it to the beginning of the buffer char *dst = m_ReadBuffer; for ( ; leftovers > 0; --leftovers ) { *dst++ = *last_tmp++; } } } while( bytes_read == READ_BUF_SIZE - m_BufferLeftovers ); }/*! Parses the response \a read_buffer from mpg123.*/voidQmMpg123Player::parseCodes( char *read_buffer ){ char code = read_buffer[1]; switch(code) { case 'F': parseFrameInfo( read_buffer ); break; case 'S':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -