/* ColdFish Music Player
* Copyright (C) 2003 Kristian Van Der Vliet
* Copyright (C) 2003 Arno Klenke
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU Library
* General Public License as published by the Free Software
* Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
#include <iostream>
#include <fstream>
#include <queue>
#include <util/settings.h>
#include <atheos/threads.h>
#include <media/packet.h>
#include <storage/path.h>
#include "CFApp.h"
#include "CFWindow.h"
#include "SelectWin.h"
#include "messages.h"
#define DEBUG 0
//----------------------------------------------------------------------------------
static inline void secs_to_ms( uint64 nTime, uint32 *nM, uint32 *nS )
{
if ( nTime > 59 )
{
*nM = ( nTime / 60 );
*nS = nTime - ( *nM * 60 );
}
else
{
*nM = 0;
*nS = (uint32)nTime;
}
}
int32 play_thread_entry( void *pData )
{
CFApp *pcApp = (CFApp*)pData;
pcApp->PlayThread();
return ( 0 );
}
int32 play_next_entry( void *pData )
{
CFApp *pcApp = (CFApp*)pData;
pcApp->PlayNext();
return ( 0 );
}
int32 play_prev_entry( void *pData)
{
CFApp *pcApp = (CFApp*)pData;
pcApp->PlayPrevious();
return ( 0 );
}
//----------------------------------------------------------------------------------
CFApp::CFApp( ):os::Looper( "cf_app" )
{
/* Set default values */
m_nState = CF_STATE_STOPPED;
m_zListName = os::String ( getenv( "HOME" ) ) + "/Default Playlist.plst";
m_pcInput = NULL;
m_bLockedInput = false;
m_zAudioFile = "";
m_nAudioTrack = 0;
m_nAudioStream = 0;
m_pcAudioCodec = NULL;
m_pcAudioOutput = NULL;
m_bPlayThread = false;
m_bListShown = true;
m_zTrackName = "Unknown";
m_pcInputSelector = NULL;
m_nLastPosition = 0;
m_zAudioName = "";
m_bPacket = false;
m_bStream = false;
m_hPlayThread = 0;
}
void CFApp::Start( os::String zFileName, bool bLoad )
{
SetPublic(true);
/* Load settings */
os::Settings * pcSettings = new os::Settings();
if ( pcSettings->Load() == 0 )
{
m_zListName = pcSettings->GetString( "playlist", m_zListName.c_str() );
}
delete( pcSettings );
/* Create registrar manager */
m_pcRegManager = NULL;
try
{
/* Register playlist type */
m_pcRegManager = os::RegistrarManager::Get();
m_pcRegManager->RegisterType( "application/x-coldfish-playlist", "ColdFish Playlist" );
m_pcRegManager->RegisterTypeIconFromRes( "application/x-coldfish-playlist", "application_coldfish_playlist.png" );
m_pcRegManager->RegisterTypeExtension( "application/x-coldfish-playlist", "plst" );
m_pcRegManager->RegisterAsTypeHandler( "application/x-coldfish-playlist" );
}
catch(...)
{
}
/* Create media manager */
m_pcManager = os::MediaManager::Get();
if ( !m_pcManager->IsValid() )
{
if (DEBUG)
std::cout << "Media server is not running" << std::endl;
PostMessage( os::M_QUIT );
return;
}
/* Create window */
m_pcWin = new CFWindow( os::Rect( 0, 0, 500, 350 ), "cf_window", "Default Playlist.plst - ColdFish",0);
m_pcWin->CenterInScreen();
m_pcWin->Show();
m_pcWin->MakeFocus( true );
/* Open list */
if ( bLoad )
{
if ( !OpenList( zFileName ) )
OpenList( m_zListName );
}
else
{
if ( !OpenList( m_zListName ) )
{
OpenList( os::String ( getenv( "HOME" ) ) + "/Default Playlist.plst" );
}
}
}
CFApp::~CFApp()
{
/* Close and delete everything */
if (m_pcRegManager)
{
m_pcRegManager->Put();
}
CloseCurrentFile();
m_pcWin->Terminate();
if ( m_pcManager->IsValid() )
{
m_pcManager->Put();
}
}
CFWindow* CFApp::GetWindow()
{
return( m_pcWin );
}
/* Thread which is responsible to play the file */
void CFApp::PlayThread()
{
bigtime_t nTime = get_system_time();
m_bPlayThread = true;
os::MediaPacket_s sPacket;
os::MediaPacket_s sCurrentAudioPacket;
std::queue cAudioPackets;
uint64 nAudioBytes = 0;
uint8 nErrorCount = 0;
bool bError = false;
if (DEBUG)
std::cout << "Play thread running" << std::endl;
/* Seek to last position */
if ( !m_bStream )
m_pcInput->Seek( m_nLastPosition );
/* Create audio output packet */
if ( m_bPacket )
{
m_pcAudioCodec->CreateAudioOutputPacket( &sCurrentAudioPacket );
}
while ( m_bPlayThread )
{
if ( m_bPacket )
{
if ( m_pcAudioOutput->GetDelay() < m_pcAudioOutput->GetBufferSize() )
{
/* Grab data */
if ( m_pcInput->ReadPacket( &sPacket ) == 0 )
{
nErrorCount = 0;
/* Decode audio data */
if ( sPacket.nStream == m_nAudioStream )
{
if ( m_pcAudioCodec->DecodePacket( &sPacket, &sCurrentAudioPacket ) == 0 )
{
if ( sCurrentAudioPacket.nSize[0] > 0 )
{
sCurrentAudioPacket.nTimeStamp = ~0;
m_pcAudioOutput->WritePacket( 0, &sCurrentAudioPacket );
nAudioBytes += sCurrentAudioPacket.nSize[0];
/* Put the packet in the queue and allocate a new one */
if( cAudioPackets.size() < 20 ) // FIX: after short time app hangs, test cAudioPackets size
{
cAudioPackets.push( sCurrentAudioPacket );
m_pcAudioCodec->CreateAudioOutputPacket( &sCurrentAudioPacket );
}
}
}
}
m_pcInput->FreePacket( &sPacket );
}
else
{
/* Increase error count */
nErrorCount++;
if ( m_pcAudioOutput->GetDelay( true ) > 0 ) // FIX: end of track hang bug, from GetDelay() to GetDelay( true )
nErrorCount--;
if ( nErrorCount > 10 && !bError )
{
os::Application::GetInstance()->PostMessage( CF_PLAY_NEXT, os::Application::GetInstance() );
bError = true;
}
}
}
}
snooze( 1000 );
if ( !m_bStream && get_system_time() > nTime + 1000000 )
{
/* Move slider */
m_pcWin->GetLCD()->SetValue( os::Variant( ( int )( m_pcInput->GetCurrentPosition() * 1000 / m_pcInput->GetLength() ) ), false );
m_pcWin->GetLCD()->UpdateTime( m_pcInput->GetCurrentPosition() );
//cout<<"Position "<GetCurrentPosition()<GetCurrentPosition() )
{
nErrorCount++;
if ( nErrorCount > 2 && !bError )
{
os::Application::GetInstance()->PostMessage( CF_PLAY_NEXT, os::Application::GetInstance() );
bError = true;
}
}
else
nErrorCount = 0;
m_nLastPosition = m_pcInput->GetCurrentPosition();
}
}
/* Stop tread */
if (DEBUG)
std::cout << "Stop thread" << std::endl;
if ( !m_bPacket )
{
m_pcInput->StopTrack();
}
if ( m_bPacket )
{
m_pcAudioOutput->Clear();
/* Clear packets */
while( !cAudioPackets.empty() )
{
os::MediaPacket_s sPacket = cAudioPackets.front();
m_pcAudioCodec->DeleteAudioOutputPacket( &sPacket );
cAudioPackets.pop();
}
m_pcAudioCodec->DeleteAudioOutputPacket( &sCurrentAudioPacket );
}
}
/* Open one playlist */
bool CFApp::OpenList( os::String zFileName )
{
char zTemp[PATH_MAX];
char zTemp2[PATH_MAX];
m_bLockedInput = false;
m_pcWin->GetPlaylist()->Clear();
m_pcWin->GetLCD()->SetValue( os::Variant( 0 ) );
m_pcWin->GetLCD()->UpdateTime( 0 );
m_pcWin->GetLCD()->SetTrackName( "Unknown" );
m_pcWin->GetLCD()->SetTrackNumber( 0 );
std::ifstream hIn;
hIn.open( zFileName.c_str() );
if ( !hIn.is_open() )
{
if (DEBUG)
std::cout << "Could not open playlist!" << std::endl;
return ( false );
}
/* Read header */
if ( hIn.getline( zTemp, 255, '\n' ) < 0 )
return ( false );
if ( strcmp( zTemp, "" ) )
{
if (DEBUG)
std::cout << "Invalid playlist!" << std::endl;
return ( false );
}
/* Read entries */
bool bInEntry = false;
os::String zFile;
int nTrack = 0;
int nStream = 0;
m_pcWin->Lock();
m_pcWin->SetTitle( "Loading... - ColdFish" );
m_pcWin->Unlock();
while ( !hIn.eof() )
{
if ( hIn.getline( zTemp, 255, '\n' ) < 0 )
return ( false );
if ( !strcmp( zTemp, "" ) )
{
goto finished;
}
if ( !strcmp( zTemp, "" ) )
{
bInEntry = true;
zFile = "";
nTrack = 0;
nStream = 0;
continue;
}
if ( !strcmp( zTemp, "" ) )
{
uint32 nM = 0, nS = 0;
int64 nLength = 0;
bInEntry = false;
/* Try to read the length attribute */
bool bReadLengthFromInput = true;
try
{
os::FSNode cNode( zFile );
if( cNode.ReadAttr( "Media::Length", ATTR_TYPE_INT64, &nLength, 0, sizeof( int64 ) ) == sizeof( int64 ) )
{
bReadLengthFromInput = false;
secs_to_ms( nLength, &nM, &nS );
}
cNode.Unset();
} catch(...)
{
bReadLengthFromInput = true;
}
/* Read length from input if required */
if( bReadLengthFromInput )
{
os::MediaInput * pcInput = m_pcManager->GetBestInput( zFile );
if ( pcInput == NULL )
continue;
pcInput->SelectTrack( nTrack );
nLength = pcInput->GetLength();
secs_to_ms( pcInput->GetLength(), &nM, &nS );
try {
os::FSNode cNode( zFile );
cNode.WriteAttr( "Media::Length", O_TRUNC, ATTR_TYPE_INT64, &nLength, 0, sizeof( int64 ) );
cNode.Unset();
} catch( ... ) { }
pcInput->Release();
}
/* Add new row */
CFListItem * pcRow = new CFListItem();
pcRow->AppendString( os::Path( zFile ).GetLeaf() );
pcRow->zPath = zFile;
sprintf( zTemp, "%i", ( int )nTrack + 1 );
pcRow->AppendString( zTemp );
pcRow->nTrack = nTrack;
pcRow->nStream = nStream;
sprintf( zTemp, "%.2li:%.2li", (long int)nM, (long int)nS );
pcRow->AppendString( zTemp );
m_pcWin->Lock();
m_pcWin->GetPlaylist()->InsertRow( pcRow );
if ( m_pcWin->GetPlaylist()->GetRowCount() == 1 )
m_pcWin->GetPlaylist()->Select( 0, 0 );
m_pcWin->Unlock();
continue;
}
/* We expect a second line with data */
if ( hIn.getline( zTemp2, 255, '\n' ) < 0 )
return ( false );
if ( !strcmp( zTemp, "" ) )
{
zFile = zTemp2;
}
else if ( !strcmp( zTemp, "