qtinternetradio/ui/src/irplaycontroller.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 19 Apr 2010 14:01:53 +0300
changeset 0 09774dfdd46b
child 2 2e1adbfc62af
permissions -rw-r--r--
Revision: 201011 Kit: 201015

/*
* 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 <hbprogressdialog.h>
#include <hbmessagebox.h>
#include <QTimer>

#include "irplaycontroller.h"
#include "irapplication.h"
#include "irqmediaplayer.h"
#include "irabstractviewmanager.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"
//                                        public functions

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

/*
 * Description : destructor
 */
IRPlayController::~IRPlayController()
{
    iApplication = NULL;
    iPlayState = EStopped;

    delete iBufferingDialog;
    iBufferingDialog = NULL;

    stop(EIRQUserTerminated);
    delete iMediaPlayer;
    iMediaPlayer = NULL;

    delete iNowPlayingPreset;
    iNowPlayingPreset = NULL;

    delete iUrlArray;
    iUrlArray = NULL;

    iMetaData = NULL;

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

/*
 * 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;
        *iNowPlayingPreset = *aPreset;
        delete iUrlArray;
        iUrlArray = NULL;
        iUrlArray = iNowPlayingPreset->getURLsForBitrate(selectedBitRate);
        if (iUrlArray)
        {
            QString url = iUrlArray->at(0);
#ifdef __WINS__
            if (iLastPlayedChannelName != aPreset->name)
            {
                emit initializeLogo();
            }
            url = tr("http://172.28.189.104:8000");
            iLastPlayedChannelName = aPreset->name;
#else
            if (iLastPlayedUrl != iUrlArray->at(0))
            {
                emit initializeLogo();
            }
#endif
            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 && iSessionStarted)
    {
        if(!iGetServerResult)
        {
            iStatisticsReporter->logServerResult(iLastPlayedUrl,EIRQPlayerErrorConnectingFailed);
        }
        iStatisticsReporter->sessionEnded(aStopReason);
        iSessionStarted = false;
    }
}

/*
 * 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 : enable stereo effect
 */
void IRPlayController::enableStereo()
{
    if (iMediaPlayer)
    {
        iMediaPlayer->enableStereoEffect();
    }
}

/*
 * Description : disable stereo effect
 */
void IRPlayController::disableStereo()
{
    if (iMediaPlayer)
    {
        iMediaPlayer->disableStereoEffect();
    }
}

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

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

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

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

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

/*
 * Description : show a buffering dialog to inform user the buffering stage.
 *               If the dialog is not created yet, create first. 
 */
void IRPlayController::createBufferingDialog(const QObject *aReceiver, const char *aFunc)
{
    if (NULL == iBufferingDialog)
    {
        iBufferingDialog = new HbProgressDialog(HbProgressDialog::ProgressDialog);
        iBufferingDialog->setMinimum(0);
        iBufferingDialog->setMaximum(100);
        iBufferingDialog->setModal(true);
    }

    //disconnect everything connected to signal cancelled()
    iBufferingDialog->disconnect(SIGNAL(cancelled()));

    connect(iBufferingDialog, SIGNAL(cancelled()), aReceiver, aFunc);
    iBufferingDialog->setProgressValue(0);
    iBufferingDialog->setText("0%");
    iBufferingDialog->show();
}

/*
 * Description : close the buffering dialog
 */
void IRPlayController::closeBufferingDialog()
{
    if (iBufferingDialog)
    {
        iBufferingDialog->close();
        delete iBufferingDialog;
        iBufferingDialog = NULL;
    }
}

//                                           slot  functions

/*
 * Description : data connection has been established. Signal is emitted by media player.
 * 
 */
void IRPlayController::connectionEstablished(int aBitrate)
{
    if(iStatisticsReporter && iSessionStarted)
    {
        iStatisticsReporter->logServerResult(iLastPlayedUrl,EIRQErrorNone);
        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)
{
    iLastError = aError;

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

/*
 * Description : handle the error async.
 */
void IRPlayController::handleError()
{
    qDebug("IRPlayController::handleError(), Entering, iLastError - %d", iLastError);
    switch (iLastError)
    {
    case EIRQPlayerErrorServerFull:
    case EIRQPlayerErrorTimeOut:
    case EIRQPlayerErrorConnectingFailed:
        if(iStatisticsReporter)
        {
            iStatisticsReporter->logServerResult(iLastPlayedUrl,iLastError);
            iGetServerResult = true;
        }
		
        // if there's NO other URL to try, show warning.
        if (iNowPlayingPreset->getChannelURLCount() == 1)
        {
		    endSession(EIRQNoConnectionToServer);
            break;
        }

        if (iResuming)
        {
            HbMessageBox note(tr("Connecting failed, try next URL"), HbMessageBox::MessageTypeInformation);
            note.exec();
            connectToChannel(iNowPlayingPreset,iConnectedFrom);
            iResuming = false;
            return;
        }

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

    case EIRQPlayerErrorConnectionLost:
        qDebug("IRPlayController::handleError, connection lost");
        stop(EIRQNoConnectionToServer);
        break;
		
    case EIRQPlayerErrorGeneral:
    case EIRQPlayerErrorAudioDeviceLost:
    default:
        stop(EIRQUnknownTermination);
        break;
    }

    closeBufferingDialog();

    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)
{
    /* we added this condition for sometimes, the function will be called
     * when the state is playing. reference cr_9010
     */
    if( iBufferingDialog && EBuffering == iPlayState )
    {        
        iBufferingDialog->setProgressValue(aProgress);
        iBufferingDialog->setText(QString("%1%").arg(aProgress));       
    }
    
    if (100 == aProgress)
    {
        closeBufferingDialog();

        //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 according to settings
            if (1 == iApplication->getSettings()->getStereoMode())
            {
                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->musicStoreStatus);  
    }   

    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);
    if (!iResuming && EIRView_PlayingView == iApplication->getViewManager()->currentViewId())
    {
        iApplication->getViewManager()->backToPreviousView();
    }
}

//                                       private functions

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

/*
 * 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 note(tr("Connecting failed, try next URL"), HbMessageBox::MessageTypeInformation);
                note.exec();  
                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 note(tr("Connecting failed, try next URL"), HbMessageBox::MessageTypeInformation);
        note.exec();

        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();
    createBufferingDialog(this, SLOT(cancelBuffering()));
}

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

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

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