rendered paste body/***************************************************************************
* Copyright (C) 2005 - 2006 by *
* Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm> *
* Erik Jaelevik, Last.fm Ltd <erik@last.fm> *
* *
* 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., *
* 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <QDebug>
#include <QtGui>
#include <containerutils.h>
#include "httpinput.h"
#include "Loqqer.h"
HTTPInput::HTTPInput()
: m_http( 0 )
, m_streaming( false )
, m_skipToSync( false )
, m_skippedData( false )
, m_refillBuffer( true )
, m_bufferSize( 24576 )
{
gLogger.Init( savePath( "httpinput.log" ), false );
gLogger.SetLevel( 4 );
LOGL( 3, "Initialising HTTP Input" );
m_iWebService = webService();
connect( &m_http, SIGNAL( readyRead( QHttpResponseHeader ) ), this, SLOT( dataAvailable( QHttpResponseHeader ) ) );
connect( &m_http, SIGNAL( responseHeaderReceived( QHttpResponseHeader ) ), this, SLOT( responseHeaderReceived( QHttpResponseHeader ) ) );
connect( &m_http, SIGNAL( stateChanged( int ) ), this, SLOT( stateChanged( int ) ) );
connect( m_iWebService, SIGNAL( stationChanged( QString, QString ) ), this, SLOT( stationChanged() ) );
connect( m_iWebService, SIGNAL( streamingUrl( QUrl ) ), this, SLOT( setStreamingUrl( QUrl ) ) );
connect( m_iWebService, SIGNAL( metaDataResultRadio( MetaData ) ), this, SLOT( setMetaData( MetaData ) ) );
}
void
HTTPInput::startStreaming()
{
// Called from Container::start.
// TODO: this shouldn't be called directly, a relevant load should
// be done first depending on what's highlighted in the tree.
LOGL( 3, "Starting streaming from: " << m_url.toString() );
m_mutex.lock();
m_buffer.clear();
m_mutex.unlock();
emit clearBuffers();
m_http.abort();
m_http.setHost( m_url.host(), m_url.port() > 0 ? m_url.port() : 80 );
m_http.get( m_url.path() + "?" + QString( m_url.encodedQuery() ) );
m_skipToSync = true;
emit preparedStreaming();
}
void
HTTPInput::stopStreaming()
{
// Called from Container::stop
LOGL( 3, "Stopping streaming" );
setMetaData( MetaData() );
m_http.abort();
m_mutex.lock();
m_buffer.clear();
m_mutex.unlock();
emit stoppedStreaming();
emit listenerCommand( MetaData(), PCMD_STOP );
emit clearBuffers();
}
void
HTTPInput::load( const QString& url )
{
// Called by search or sidebar
LOGL( 3, "Loading: " << url );
m_skipToSync = true;
m_iWebService->changeStation( url );
}
void
HTTPInput::stationChanged()
{
if ( !isStreaming() )
startStreaming();
}
void
HTTPInput::requestData()
{
// When we completely run out of buffer, we should rather
// refill the initial buffer of 32k (m_refillBuffer)
if ( !m_refillBuffer && m_buffer.size() == 0 &&
m_http.state() != QHttp::Unconnected && m_http.state() != QHttp::Closing )
{
LOGL( 3, "Buffer empty, rebuffering " << m_bufferSize );
m_refillBuffer = true;
emit bufferEmpty();
}
// Did we initially fill the buffer up to m_bufferSize?
// If so, we can start emitting signals.
if ( m_refillBuffer )
{
if ( m_buffer.size() >= m_bufferSize )
{
LOGL( 3, "Buffer full, starting playback" );
m_refillBuffer = false;
emit bufferFull();
}
else
{
if ( m_buffer.size() > 0 )
emit bufferEmpty( m_buffer.size() );
return;
}
}
m_mutex.lock();
if ( m_skipToSync && m_buffer.contains( "SYNC" ) )
{
int i = m_buffer.indexOf( "SYNC" );
if ( ( m_buffer.size() - i ) > m_bufferSize )
{
LOGL( 3, "SYNC found, throwing away " << i << " bytes, skipping to next song" );
m_buffer.remove( 0, i );
m_skipToSync = false;
m_skippedData = true;
}
}
QByteArray buffer;
buffer.append( m_buffer.left( 1024 ) );
m_buffer.remove( 0, 1024 );
m_mutex.unlock();
// Check if the buffer contains a SYNC signal and if
// we need to request new metadata via the WebService.
if ( buffer.contains( "SYNC" ) )
{
if ( m_skippedData )
{
buffer.remove( 0, buffer.indexOf( "SYNC" ) );
emit clearBuffers();
}
m_skipToSync = false;
m_skippedData = false;
buffer.remove( buffer.indexOf( "SYNC" ), 4 );
LOGL( 3, "SYNC found, requesting metadata from web service" );
m_iWebService->requestMetaDataRadio();
}
emit newData( buffer );
}
void
HTTPInput::dataAvailable( const QHttpResponseHeader &resp )
{
char inBuf[8192];
m_mutex.lock();
memset( inBuf, 0, sizeof( inBuf ) );
int len = m_http.read( inBuf, sizeof( inBuf ) );
if ( strstr( inBuf, "HTTP/1.0 " ) != NULL )
{
if ( strstr( inBuf, "HTTP/1.0 401" ) != NULL ) responseHeaderReceived( QHttpResponseHeader( 401 ) );
if ( strstr( inBuf, "HTTP/1.0 503" ) != NULL ) responseHeaderReceived( QHttpResponseHeader( 503 ) );
if ( strstr( inBuf, "HTTP/1.0 666" ) != NULL ) responseHeaderReceived( QHttpResponseHeader( 666 ) );
if ( strstr( inBuf, "HTTP/1.0 667" ) != NULL ) responseHeaderReceived( QHttpResponseHeader( 667 ) );
}
for ( int i = 0; i < len; i++ )
m_buffer.append( inBuf[ i ] );
m_mutex.unlock();
}
void
HTTPInput::responseHeaderReceived( const QHttpResponseHeader &resp )
{
switch( resp.statusCode() )
{
case 401:
{
emit error( resp.statusCode(), tr( "Your session has timed out. Please try to login again." ) );
break;
}
case 503:
{
emit error( resp.statusCode(), tr( "The radio server is too busy. Please try again in a few minutes." ) );
break;
}
case 666:
{
emit error( resp.statusCode(), tr( "Radio server is down for maintenance. Please try again in a few minutes." ) );
break;
}
case 667:
{
emit error( resp.statusCode(), tr( "There is not enough content to play this station." ) );
break;
}
}
}
void
HTTPInput::stateChanged( int state )
{
switch( state )
{
case QHttp::Reading:
{
if ( !m_streaming )
{
m_streaming = true;
emit startedStreaming();
}
break;
}
case QHttp::Unconnected:
{
if ( m_streaming )
{
m_streaming = false;
stopStreaming(); // will emit stoppedStreaming
}
break;
}
}
}
Q_EXPORT_PLUGIN2( input, HTTPInput )