qtinternetradio/ui/src/irplaycontroller.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 11 Jun 2010 13:30:40 +0300
changeset 8 3b03c28289e6
parent 5 0930554dc389
child 12 608f67c22514
permissions -rw-r--r--
Revision: 201021 Kit: 2010123

/*
 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description:
 *
 */
#include <hbmessagebox.h>
#include <QTimer>
#ifdef Q_CC_NOKIAX86
#include <QFile>
#include <QTextStream>
#endif

#include "irplaycontroller.h"
#include "irapplication.h"
#include "irqmediaplayer.h"
#include "irviewmanager.h"
#include "irqisdsdatastructure.h"
#include "irlastplayedstationinfo.h"
#include "irqnetworkcontroller.h" 
#include "irqsonghistoryengine.h"
#include "irqmetadata.h"
#include "irqsettings.h"
#include "irqfavoritesdb.h"
#include "irqstatisticsreporter.h"
#include "irenummapper.h" 
#include "irqlogger.h"

#ifdef Q_CC_NOKIAX86
void getRadioServerAddress(QString & aUrl);
#endif

BEGIN_ENUM_MAP( KTerminationTypeMap )
    ENUM_MAP_ITEM( EIRQUserTerminated, IRQStatisticsReporter::EIRUserTerminated ),
    ENUM_MAP_ITEM( EIRQNoConnectionToServer, IRQStatisticsReporter::EIRNoConnToServer ),
    ENUM_MAP_ITEM( EIRQNoConnectionToNetwork, IRQStatisticsReporter::EIRNoConnToNetwork )            
END_ENUM_MAP( KTerminationTypeMap )

#define MAP_TO_ENGINE_TerminationType(ui_enum) \
          MAP_TO_ENGINE_ENUM( IRQTerminatedType, \
             IRQStatisticsReporter::IRTerminatedType, \
             ui_enum, KTerminationTypeMap )
    
BEGIN_ENUM_MAP( KServerResultMap )
    ENUM_MAP_ITEM( EIRQPlayerErrorServerFull, IRQStatisticsReporter::EIRServerFull ),
    ENUM_MAP_ITEM( EIRQPlayerErrorTimeOut, IRQStatisticsReporter::EIRTimeOut ),
    ENUM_MAP_ITEM( EIRQPlayerErrorConnectingFailed, IRQStatisticsReporter::EIRConnectFailed )              
END_ENUM_MAP( KServerResultMap )

#define MAP_TO_ENGINE_ServerResult(ui_enum) \
          MAP_TO_ENGINE_ENUM( IRQError, \
             IRQStatisticsReporter::IRServerResult, \
             ui_enum, KServerResultMap )
             
BEGIN_ENUM_MAP( KConnectedFromMap )
    ENUM_MAP_ITEM( EIRQIsds, IRQStatisticsReporter::EIRIsds ),
    ENUM_MAP_ITEM( EIRQPresetAdhoc, IRQStatisticsReporter::EIRPresetAdhoc ),
    ENUM_MAP_ITEM( EIRQPresetIsds, IRQStatisticsReporter::EIRPresetIsds ),
    ENUM_MAP_ITEM( EIRQHistoryAdhoc, IRQStatisticsReporter::EIRHistoryAdhoc ),
    ENUM_MAP_ITEM( EIRQHistoryIsds, IRQStatisticsReporter::EIRHistoryIsds ),
    ENUM_MAP_ITEM( EIRQAdhocManual, IRQStatisticsReporter::EIRAdhocManual ),                
    ENUM_MAP_ITEM( EIRQAdhocExternal, IRQStatisticsReporter::EIRAdhocExternal )            
END_ENUM_MAP( KConnectedFromMap )

#define MAP_TO_ENGINE_ConnectedFrom(ui_enum) \
          MAP_TO_ENGINE_ENUM( IRQConnectedFrom, \
             IRQStatisticsReporter::IRConnectedFrom, \
             ui_enum, KConnectedFromMap )                 

//                                        public functions

/*
 * Description : constructor
 */
IRPlayController::IRPlayController(IRApplication* aApplication) :
    iApplication(aApplication),
    iMediaPlayer(new IRQMediaPlayer()),
    iStatisticsReporter(NULL),
    iConnectedFrom(EIRQIsds),
    iGetServerResult(false),
    iNowPlayingPreset(new IRQPreset()),
    iNowPlayingPresetBackup(new IRQPreset()),
    iMetaData(NULL),
    iSongHistoryEngine(IRQSongHistoryEngine::openInstance()),
    iPlayState(EIdle),
    iResuming(false),
    iTryingBitrate(0),
    iUrlArray(0),
    iRealBitrate(0),
    iLastError(EIRQErrorNone),
    iStopReason(EIRQUnknownTermination)
{
    connectSignalSlot(); 
    iStatisticsReporter = IRQStatisticsReporter::openInstance();
}

/*
 * Description : destructor
 */
IRPlayController::~IRPlayController()
{
    stop(EIRQUserTerminated);
    delete iMediaPlayer;
    iMediaPlayer = NULL;

    delete iNowPlayingPreset;
    iNowPlayingPreset = NULL;
    delete iNowPlayingPresetBackup;
    iNowPlayingPresetBackup = NULL;

    delete iUrlArray;
    iUrlArray = NULL;

    if (iSongHistoryEngine)
    {
        iSongHistoryEngine->closeInstance();
        iSongHistoryEngine = NULL;
    }
    
    if (iStatisticsReporter)
    {
        iStatisticsReporter->closeInstance();
    }
}

/*
 * Description : connect to a channel specified by aPreset.
 * Parameters  : aPreset : the preset of the channel
 */
void IRPlayController::connectToChannel(IRQPreset *aPreset, IRQConnectedFrom aConnectedFrom)
{
    iConnectedFrom = aConnectedFrom;
    if (!aPreset)
    {
        return;
    }

    if (iMediaPlayer)
    {
        // sort the URL by ascending order and get all available rates.
        // iAvailableBitrate is cleared in getAvailableBitrates().
        aPreset->sortURLArray();
        aPreset->getAvailableBitrates(iAvailableBitrate);
        if (iAvailableBitrate.count() == 0)
        {
            return;
        }
        
        int selectedBitRate = 0;
        IRQPreferredQuality preferredQuality = iApplication->getSettings()->getPreferredQuality();
        switch(preferredQuality)
        {
            case EIRQStandardQuality:
                selectedBitRate = iAvailableBitrate.first();
                break;
            case EIRQHighQuality:
                selectedBitRate = iAvailableBitrate.last();
                break;
            default:
                selectedBitRate = iAvailableBitrate.first();
                break;
        }

        // get URL to play
        iTryingBitrate = selectedBitRate;
        
        //reserve the info in nowplay view
        *iNowPlayingPresetBackup = *iNowPlayingPreset;
        *iNowPlayingPreset = *aPreset;
        
        delete iUrlArray;
        iUrlArray = NULL;
        iUrlArray = iNowPlayingPreset->getURLsForBitrate(selectedBitRate);
        if (iUrlArray)
        {
            QString url = iUrlArray->at(0);
#ifdef Q_CC_NOKIAX86
            if (iLastPlayedChannelName != aPreset->name)
            {
                emit initializeLogo();
            }
            url = "http://172.28.205.171:8000";
            getRadioServerAddress(url);
            iLastPlayedChannelName = aPreset->name;
#else
            if (iLastPlayedUrl != iUrlArray->at(0))
            {
                emit initializeLogo();
            }
#endif
            //reserve the info in nowplay view
            iLastPlayedUrlBackup = iLastPlayedUrl;
            iLastPlayedUrl = url;
            iResuming = false;
            doPlay(url);
        }
    }
}

/*
 * Description : RESUME playing after play is stopped.
 *               Use the last played url as station's url.
 */
void IRPlayController::resume()
{
    qDebug("IRPlayController::resume(), Entering");
    if (iMediaPlayer && (EStopped == iPlayState))
    {
        iResuming = true;
        
        if (iLastPlayedUrl != "")
        {
            qDebug("IRPlayController::resume(), iLastPlayedUrl is not empty, doPlay()");
            doPlay(iLastPlayedUrl);
        }
    }
    qDebug("IRPlayController::resume(), Exiting");
}

/*
 * Description : stop playing
 */
void IRPlayController::stop(IRQTerminatedType aStopReason)
{
    qDebug("IRPlayController::stop, Entering, aStopReason=%d", aStopReason);
    if (iMediaPlayer)
    {
        iMediaPlayer->disableStereoEffect();

        iMediaPlayer->stop();

        if (EPlaying == iPlayState)
        {
            iStopReason = aStopReason;
            // playingStarted is emitted while iPlaying is set to true,
            // so when stop() is called and iPlaying is true, playingStopped
            // should be emitted.
            qDebug("IRPlayController::stop, emit playingStopped()");
            emit playingStopped();
        }
        iPlayState = EStopped;
    }
	endSession(aStopReason);
	qDebug("IRPlayController::stop, Exiting");
}

/*
 * Description : End Session
 */
void IRPlayController::endSession(IRQTerminatedType aStopReason)
{
    if(iStatisticsReporter)
    {
        if(!iGetServerResult)
        {
            iStatisticsReporter->logServerResult(iLastPlayedUrl,IRQStatisticsReporter::EIRConnectFailed);
        }
        iStatisticsReporter->sessionEnded(MAP_TO_ENGINE_TerminationType(aStopReason));
    }
}

/*
 * Description : get current volume setting from media player or
 *               central repository
 * Return      : current volume
 */
int IRPlayController::getVolume() const
{
    if (EPlaying == iPlayState)
    {
        return iMediaPlayer->getVolume();
    }
    else
    {
        return iApplication->getSettings()->getVolumeSetting();
    }
}

/*
 * Description : set volume to media player and central repository
 * Parameters  : aVolume : volume value, between 0 and 100
 */
void IRPlayController::setVolume(int aVolume)
{
    if (EPlaying == iPlayState)
    {
        iMediaPlayer->setVolume(aVolume);
    }
    iApplication->getSettings()->setVolumeSetting(aVolume);
}

/*
 * Description : return the flag of playing state
 * Return      : true  : playing is ongoing
 *               false : playing is not ongoing
 */
bool IRPlayController::isPlaying() const
{
    return (EPlaying == iPlayState);
}

/*
 * Description : return the flag of stopped state
 * Return      : true  : playing is stopped
 *               false : playing is not stopped
 */
bool IRPlayController::isStopped() const
{
    return (EStopped == iPlayState);
}

/*
 * Description : return the flag of idle state
 * Return      : true  : playing is idle
 *               false : playing is not idle
 */
bool IRPlayController::isIdle() const
{
    return (EIdle == iPlayState);
}

/*
 * Description : return the now playing preset
 * Return      : pointer to the now playing preset
 */
IRQPreset * IRPlayController::getNowPlayingPreset() const
{
    return iNowPlayingPreset;
}

QString IRPlayController::getNowPlayingUrl() const
{
    return iLastPlayedUrl;
}

int IRPlayController::getNowPlayingBitRate() const
{
    return iRealBitrate;
}

/*
 * Description : return current metadata
 * Return      : pointer to current metadata
 */
IRQMetaData * IRPlayController::getMetaData() const
{
    return iMetaData;
}

IRQTerminatedType IRPlayController::getStopReason() const
{
    return iStopReason;
}

//                                           slot  functions

/*
 * Description : data connection has been established. Signal is emitted by media player.
 * 
 */
void IRPlayController::connectionEstablished(int aBitrate)
{
    if(iStatisticsReporter)
    {
        iStatisticsReporter->logServerResult(iLastPlayedUrl,IRQStatisticsReporter::EIRConnected);
        iGetServerResult = true;
        iStatisticsReporter->markSessionStart();
    }

    iMetaData = NULL;
    iRealBitrate = aBitrate;

    // update bitrate for user defined channel
    if (0 == iNowPlayingPreset->type)
    {
        if (iTryingBitrate != iRealBitrate)
        {
            iNowPlayingPreset->setUrlBitrate(0,iRealBitrate);
            //when bitrate is available, it should be written to favorites db
            iApplication->getFavoritesDB()->replaceUserDefinedPreset(*iNowPlayingPreset);
            iTryingBitrate = iRealBitrate;
        }
    }
}

/*
 * Description : error occurred in media player. Signal is emitted by media player.
 * Parameters  : aError : error value
 */
void IRPlayController::errorOccured(IRQError aError)
{
    LOG_METHOD;
    LOG_FORMAT("the error is occured %d",aError);
    iLastError = aError;

    QTimer::singleShot(1, this, SLOT(handleError()));
}

/*
 * Description : handle the error async.
 */
void IRPlayController::handleError()
{
    LOG_METHOD;
    LOG_FORMAT("the last error is %d", iLastError);
    qDebug("IRPlayController::handleError(), Entering, iLastError - %d", iLastError);
    switch (iLastError)
    {
    case EIRQPlayerErrorServerFull:
    case EIRQPlayerErrorTimeOut:
    case EIRQPlayerErrorConnectingFailed:
        if(iStatisticsReporter)
        {
            iStatisticsReporter->logServerResult(iLastPlayedUrl,MAP_TO_ENGINE_ServerResult(iLastError));
            iGetServerResult = true;
        }
		
        // if there's NO other URL to try, show warning.
        if (iNowPlayingPreset->getChannelURLCount() == 1)
        {
            //here recover info in nowplay view, only for cases:
            //1/a channel has one url. 2/ invoked by "go to station" view.
            //if a channel has more than one url, here can't be reached.
            iLastPlayedUrl = iLastPlayedUrlBackup;
            *iNowPlayingPreset = *iNowPlayingPresetBackup;
		    stop(EIRQNoConnectionToServer);
            break;
        }

        if (iResuming)
        {
            HbMessageBox::information(tr("Connecting failed, try next URL"), (QObject*)NULL, NULL);
            connectToChannel(iNowPlayingPreset,iConnectedFrom);
            iResuming = false;
            return;
        }

        // try next
        if (true == playNextUrl())
        {
            return;
        }
        else
        {
            stop(EIRQNoConnectionToServer);
        }
        break;

    case EIRQPlayerErrorConnectionLost:
        qDebug("IRPlayController::handleError, connection lost");
        stop(EIRQNoConnectionToServer);
        break;
        
    case EIRQPlayerErrorAudioDeviceLost:
        //this is a temporary way to handle the plug-out event
        iApplication->stopLoadingAnimation();
		stop(EIRQCallIsActivated);
        return;
        
    case EIRQPlayerErrorGeneral:
    default:
        stop(EIRQUnknownTermination);
        break;
    }

    iApplication->stopLoadingAnimation();
    createNote();
    qDebug("IRPlayController::handleError(), Exiting");
}

/*
 * Description : buffering process has updated, change the display of buffering dialog.
 *               If aProress is 100, buffering is complete, now playing view needs to 
 *               be shown to user.
 * Parameters  : aProgress : progress value, between 0 and 100.
 */
void IRPlayController::updateProgress(int aProgress)
{
    if (100 == aProgress)
    {
        iApplication->stopLoadingAnimation();

        //updateProgress(100) sometimes can be called more than one time, to improve performance,
        //we only need to do the following work once.
        if (EBuffering == iPlayState)
        {
            iApplication->getViewManager()->activateView(EIRView_PlayingView);
            iPlayState = EPlaying;

            //update last played station
            IRLastPlayedStationInfo *lastPlayedStationInfo = iApplication->getLastPlayedStationInfo();
            lastPlayedStationInfo->updateLastPlayedStation(iNowPlayingPreset,iConnectedFrom);
            lastPlayedStationInfo->commitLastPlayedStation();

            //increase the played times of current preset
            iApplication->getFavoritesDB()->increasePlayedTimes(*iNowPlayingPreset);

            emit playingStarted();

            // if the metadata is available, show it.
            emit metaDataAvailable(iMetaData);

            // Save the station information to database
            IRQMetaData tmpMetaData;
            tmpMetaData.setBitrate(iRealBitrate);
            tmpMetaData.setStreamUrl(iLastPlayedUrl);
            iSongHistoryEngine->handleMetaDataReceived(tmpMetaData, *iNowPlayingPreset);
            // open stereo defaultly
            iMediaPlayer->enableStereoEffect();
        }
    }
}

/*
 * Description : get volume value from application setting. media player use the value
 *               to set volume.
 * Parameters  : aVolume : volume value
 */
void IRPlayController::fetchVolume(int &aVolume)
{
    aVolume = iApplication->getSettings()->getVolumeSetting();
}

/*
 * Description : handle the receiption of metadata. Notify now playing view to update display
 * Parameters  : aIRmetaData : the metadata object
 */
void IRPlayController::handleMetaDataReceived(IRQMetaData& aIRmetaData)
{
    iMetaData = &aIRmetaData;
    //TO DO: there maybe a potential bug when the user cancel the play, 	
    if ((aIRmetaData.getSongName().trimmed() != "")
            || (aIRmetaData.getArtistName().trimmed() != ""))
    {
        //when we are play the musicplayer and get the metadata from lower layer, we save the 
        //song's metadata into the db.  After we save it to db, we emit the next signal to notify the UI         
        iSongHistoryEngine->handleSongMetaDataReceived(*iMetaData,
                *iNowPlayingPreset);  
    }   

    if (EPlaying == iPlayState)
    {
        // This signal will cause addBanner() work. Sometimes the metadata will come before
        // the buffering is 100%, we need to avoid to show playing banner before 100% buffering.
        emit metaDataAvailable(iMetaData);        
    }    
}

/*
 * Description : during buffering stage, cancel playing request
 */
void IRPlayController::cancelBuffering()
{
    stop(EIRQUserTerminated);
    iApplication->stopLoadingAnimation();
}

//                                       private functions

/*
 * Description : show a note to user to inform that error occured.
 *                
 */
void IRPlayController::createNote(const QString &aNote)
{
    HbMessageBox::warning(aNote, (QObject*)NULL, NULL);
}

/*
 * Description : establish the signal&slot connection between media player and play controller
 */
void IRPlayController::connectSignalSlot()
{
    connect(iMediaPlayer, SIGNAL(connectionEstablished(int)), this, SLOT(connectionEstablished(int)));
    connect(iMediaPlayer, SIGNAL(errorOccured(IRQError)), this, SLOT(errorOccured(IRQError)));
    connect(iMediaPlayer, SIGNAL(percentageBuffered(int)), this, SLOT(updateProgress(int)));
    connect(iMediaPlayer, SIGNAL(volumeExpected(int&)), this, SLOT(fetchVolume(int&)));
    connect(iMediaPlayer, SIGNAL(metaDataReceived(IRQMetaData&)),
            this, SLOT(handleMetaDataReceived(IRQMetaData&)));
}

/*
 * Description : try to play the next url in the preset.
 * return value: true: it will play next URL; false, not.
 */
bool IRPlayController::playNextUrl()
{
    // remove the first url from iUrlArray
    iUrlArray->removeFirst();

    if (iUrlArray->isEmpty()) // try next bitrate
    {
        int index = iAvailableBitrate.indexOf(iTryingBitrate);
        if (-1 != index)
        {
            bool tryingContinue = true;
            IRQPreferredQuality preferredQuality = iApplication->getSettings()->getPreferredQuality();
            switch(preferredQuality)
            {
                case EIRQHighQuality:
                    if(index > 0)
                    {
                        iTryingBitrate = iAvailableBitrate.at(--index);
                    }
                    else
                    {
                        tryingContinue = false;
                    }
                    break;
                    
                case EIRQStandardQuality:
                default:
                    if(index < (iAvailableBitrate.count()-1))
                    {
                        iTryingBitrate = iAvailableBitrate.at(++index);
                    }
                    else
                    {
                        tryingContinue = false;
                    }
                    break;
            }        
            
            if(tryingContinue)
            {
                HbMessageBox::information(tr("Connecting failed, try next URL"), (QObject*)NULL, NULL); 
                delete iUrlArray;
                iUrlArray = iNowPlayingPreset->getURLsForBitrate(iTryingBitrate);
                iLastPlayedUrl = iUrlArray->at(0);
                doPlay(iLastPlayedUrl);
                return true;
            }
        }

    }
    else // try next url in iUrlArray
    {
        iLastPlayedUrl = iUrlArray->at(0);
        doPlay(iLastPlayedUrl);

        HbMessageBox::information(tr("Connecting failed, try next URL"), (QObject*)NULL, NULL);
        return true;
    }
    
    return false;
}

/*
 * Description : complete the play action
 */
void IRPlayController::doPlay(const QString& aUrl)
{
    // stop player, disable stereo effect, emit playstopped signal 
    stop(EIRQUserTerminated);

    //call getIAPId() every time before refering to it, because in ALR, the access point can 
    //be changed
    unsigned long apId = 0;
    iApplication->getNetworkController()->getIAPId(apId);
    qDebug("IRPlayController::doPlay, access point : %d", apId);
    iMediaPlayer->playStation(aUrl, apId);
    iPlayState = EBuffering;
    startSession();
    iApplication->startLoadingAnimation(this, SLOT(cancelBuffering()));
}

/*
 * Description : start a session
 */
void IRPlayController::startSession()
{
	iGetServerResult = false;

    int channelId = 0;
    if(iNowPlayingPreset)
    {
        channelId = iNowPlayingPreset->presetId;
    }
	      
    if(iStatisticsReporter)
    {
        iStatisticsReporter->sessionStarted(channelId,MAP_TO_ENGINE_ConnectedFrom(iConnectedFrom));
    }
}

#ifdef _DEBUG
int IRPlayController::bitrateTrying() const
{
    return iTryingBitrate;
}
#endif 

//get IP address configuration of test radio server
#ifdef Q_CC_NOKIAX86
void getRadioServerAddress(QString & aUrl)
{
    QFile file("C:\\data\\QTIRConfigure.txt");
    
    if (file.open(QIODevice::ReadOnly)) 
    {
        QTextStream stream(&file);
        QString line;
        QStringList parameter;
        while (!stream.atEnd())
        {
            line = stream.readLine();
            parameter = line.split("=");
            if (parameter.count() == 2)
            {
                if (parameter.first() == "RadioServerAddress")
                {
                    aUrl = parameter.last();
                    break;
                }
            }
        }
        file.close();
    }
}
#endif