/*
* Copyright (c) 2006-2007 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: Implementation file of CUpnpResResolver class.
* CUpnpResResolver is a class used for resolving res field
* attributes for a given file.
*
*/
// system includes
#include <f32file.h> // RFile
#include <apgcli.h> // RApaLsSession
#include <upnpdlnaprotocolinfo.h> // CUpnpDlnaProtocolInfo
#include <imageconversion.h> // CImageDecoder
#include <caf/caftypes.h> // ContentAccess
#include <3gplibrary/mp4lib.h> // MP4ParseRequestAudioDescription
#include <escapeutils.h> // EscapeUtils::ConvertFromUnicodeToUtf8L
// user includes
#include "upnpresresolver.h"
#include "upnpdlnaprofiler.h"
#include "upnpresparameters.h"
_LIT( KComponentLogfile, "dlnaprofiler.txt");
#include "upnplog.h"
// constants
_LIT8( KHttpGet8, "http-get" );
_LIT8( KStar8, "*" );
const TInt KMicrosecondsInSecond = 1000000;
const TInt KMilliSecondsInSecond = 1000;
// ======== LOCAL FUNCTIONS ========
// NONE
// ======== MEMBER FUNCTIONS ========
// --------------------------------------------------------------------------
// CUpnpResResolver C++ constructor
// --------------------------------------------------------------------------
//
CUpnpResResolver::CUpnpResResolver() : iDuration(0)
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver constructor" );
}
// --------------------------------------------------------------------------
// CUpnpResResolver::ConstructL
// --------------------------------------------------------------------------
//
void CUpnpResResolver::ConstructL()
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::ConstructL" );
// open semaphore
User::LeaveIfError( iSemaphore.CreateLocal( 0 ) );
}
// --------------------------------------------------------------------------
// CUpnpResResolver::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CUpnpResResolver* CUpnpResResolver::NewL()
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::NewL" );
CUpnpResResolver* self = CUpnpResResolver::NewLC();
CleanupStack::Pop( self );
return self;
}
// --------------------------------------------------------------------------
// CUpnpResResolver::NewLC
// --------------------------------------------------------------------------
//
EXPORT_C CUpnpResResolver* CUpnpResResolver::NewLC()
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::NewLC" );
CUpnpResResolver* self = new( ELeave ) CUpnpResResolver;
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
// --------------------------------------------------------------------------
// CUpnpResResolver destructor
// --------------------------------------------------------------------------
//
CUpnpResResolver::~CUpnpResResolver()
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver destructor" );
iSemaphore.Close();
delete iAVFile;
}
// --------------------------------------------------------------------------
// CUpnpResResolver::GetResParametersL
// --------------------------------------------------------------------------
//
EXPORT_C CUpnpResParameters* CUpnpResResolver::GetResParametersL(
const TDesC& aFilename )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetResParametersL" );
// check that aFileName is reasonable
if ( aFilename.Length() <= 0 )
{
User::Leave( KErrArgument );
}
// Create object that will be returned.
CUpnpResParameters* retval = CUpnpResParameters::NewLC();
// try to open file specified by aFileName
RFs fsSession;
User::LeaveIfError( fsSession.Connect() ); // connect session
CleanupClosePushL( fsSession );
// without calling ShareProtected() RApaLsSession::RecognizeData
// does not work (leaves with KErrBadHandle).
fsSession.ShareProtected();
RFile file;
User::LeaveIfError( file.Open( fsSession,
aFilename,
EFileShareReadersOrWriters|EFileRead ) );
CleanupClosePushL( file );
// Obtain mimetype of the file
HBufC8* mimetype = GetMimetypeL( file );
CleanupStack::PushL( mimetype );
retval->SetMimetypeL( *mimetype );
// Obtain file size
TInt filesize = GetFileSizeL( file );
retval->SetFileSize( filesize );
// Obtain duration for video&audio files and resolution for images.
TSize imageResolution( KErrNotFound, KErrNotFound );
if ( mimetype->Left( KMimeStartLength ).Compare( KVideo() ) == 0 )
{
GetVideoDurationL( file );
retval->SetDurationInSeconds( iDuration.Int64() );
}
else if ( mimetype->Left( KMimeStartLength ).Compare( KAudio() ) == 0 )
{
GetAudioDurationL( aFilename );
retval->SetDurationInSeconds(
iDuration.Int64() / KMicrosecondsInSecond );
}
else if ( mimetype->Left( KMimeStartLength ).Compare( KImage() ) == 0 )
{
TRAPD( err, imageResolution = GetImageResolutionL( file,
*mimetype ) );
if ( !err )
{
retval->SetResolution( imageResolution );
}
}
// Construct CUpnpDlnaProtocolInfo object.
CUpnpDlnaProtocolInfo* protocolInfo = CUpnpDlnaProtocolInfo::NewL();
CleanupStack::PushL( protocolInfo );
protocolInfo->SetFirstFieldL( KHttpGet8() );
protocolInfo->SetSecondFieldL( KStar8() );
protocolInfo->SetThirdFieldL( *mimetype );
// Obtain DLNA Profile ID
CUpnpDlnaProfiler* profiler = CUpnpDlnaProfiler::NewLC();
HBufC* PnParameter = NULL;
TRAPD( profileErr,
PnParameter = profiler->ProfileForFileL( aFilename,
file,
*retval ) );
CleanupStack::PopAndDestroy( profiler );
if ( PnParameter && !profileErr )
{
// Set DLNA Profile name (PN parameter)
CleanupStack::PushL( PnParameter );
HBufC8* profileName8bit =
EscapeUtils::ConvertFromUnicodeToUtf8L( *PnParameter );
CleanupStack::PushL( profileName8bit );
protocolInfo->SetPnParameterL( *profileName8bit );
CleanupStack::PopAndDestroy( profileName8bit );
// OP parameter is always the same in our CDS.
protocolInfo->SetOpParameterL(
UpnpDlnaProtocolInfo::KDEFAULT_DLNA_OP );
// Set Flags according to file type.
if ( mimetype->Left( KMimeStartLength ).Compare( KVideo() ) == 0 )
{
protocolInfo->SetFlagsParameterL(
UpnpDlnaProtocolInfo::KDEFAULT_DLNA_FLAGS_AV );
}
else if
( mimetype->Left( KMimeStartLength ).Compare( KAudio() ) == 0 )
{
protocolInfo->SetFlagsParameterL(
UpnpDlnaProtocolInfo::KDEFAULT_DLNA_FLAGS_AUDIO );
}
else if
( mimetype->Left( KMimeStartLength ).Compare( KImage() ) == 0 )
{
protocolInfo->SetFlagsParameterL(
UpnpDlnaProtocolInfo::KDEFAULT_DLNA_FLAGS_IMAGE );
}
CleanupStack::PopAndDestroy( PnParameter );
}
retval->SetProtocolInfoL( protocolInfo->ProtocolInfoL() );
// clean up
CleanupStack::PopAndDestroy( protocolInfo );
CleanupStack::PopAndDestroy( mimetype );
CleanupStack::PopAndDestroy( &file );
CleanupStack::PopAndDestroy( &fsSession );
CleanupStack::Pop( retval );
return retval;
}
// --------------------------------------------------------------------------
// CUpnpResResolver::GetMimetypeL
// --------------------------------------------------------------------------
//
HBufC8* CUpnpResResolver::GetMimetypeL( RFile& aFile )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetMimetypeL" );
HBufC8* retval = NULL;
// Create an AppArc server session for mime type recognition
RApaLsSession sess;
User::LeaveIfError( sess.Connect() );
CleanupClosePushL( sess );
// Try to get mime type from AppArc server
TDataRecognitionResult mimeResult;
User::LeaveIfError( sess.RecognizeData( aFile, mimeResult ) );
// close session handle
CleanupStack::PopAndDestroy( &sess );
if( mimeResult.iDataType.Des8().Length() == 0 )
{
User::Leave( KErrGeneral );
}
// Data recognition done. Check results.
retval = mimeResult.iDataType.Des8().AllocL();
return retval;
}
// --------------------------------------------------------------------------
// CUpnpResResolver::GetVideoDurationL
// --------------------------------------------------------------------------
//
void CUpnpResResolver::GetVideoDurationL( RFile& aFile )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetVideoDurationL" );
mp4_u32 videolength = 0;
mp4_double framerate = 0;
mp4_u32 videotype = 0;
mp4_u32 videowidth = 0;
mp4_u32 videoheight = 0;
mp4_u32 timescale = 0;
MP4Handle myMp4Handle;
// try open mp4 file handle
MP4Err openerr = MP4ParseOpenFileHandle( &myMp4Handle, &aFile );
switch ( openerr )
{
case MP4_OK :
{
// obtain necessary information from file
MP4Err requesterr = MP4ParseRequestVideoDescription(
myMp4Handle,
&videolength,
&framerate,
&videotype,
&videowidth,
&videoheight,
×cale );
// close mp4 file handle
MP4Err closeerr = MP4ParseClose( myMp4Handle );
if ( closeerr || requesterr )
{
User::Leave( KErrGeneral );
}
}
break;
case MP4_ERROR :
{
User::Leave( KErrGeneral );
}
break;
case MP4_OUT_OF_MEMORY :
{
User::Leave( KErrNoMemory );
}
break;
case MP4_FILE_ERROR :
{
User::Leave( KErrAccessDenied );
}
break;
default :
User::Leave( KErrGeneral );
break;
}
// videolength in milliseconds
iDuration = videolength/KMilliSecondsInSecond;
}
// --------------------------------------------------------------------------
// CUpnpResResolver::GetAudioDurationL
// --------------------------------------------------------------------------
//
void CUpnpResResolver::GetAudioDurationL( const TDesC& aFilename )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetAudioDurationL" );
TInt err = KErrNone;
// store current av file
HBufC* avFile = aFilename.AllocL();
delete iAVFile;
iAVFile = avFile;
// create audio resolving thread
RThread thread;
User::LeaveIfError( thread.Create(
KNullDesC, ThreadFunction, KDefaultStackSize, &User::Allocator(), this ) );
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetAudioDurationL: Created audio resolving thread" );
// start thread and wait until it has finished
thread.Resume();
iSemaphore.Wait();
// check return value and close thread
err = thread.ExitReason();
thread.Close();
__LOG1( "[UPnPDlnaProfiler] CUpnpResResolver::GetAudioDurationL: Finished audio resolving thread with code (%d)", err );
// leave if could not resolve audio duration
User::LeaveIfError( err );
}
// --------------------------------------------------------------------------
// CUpnpResResolver::GetFileSizeL
// --------------------------------------------------------------------------
//
TInt CUpnpResResolver::GetFileSizeL( RFile& aFile )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetFileSizeL" );
TInt retval = KErrNone;
User::LeaveIfError( aFile.Size( retval ) );
return retval;
}
// --------------------------------------------------------------------------
// CUpnpResResolver::GetImageResolutionL
// --------------------------------------------------------------------------
//
TSize CUpnpResResolver::GetImageResolutionL( RFile& aFile,
const TDesC8& aMimetype )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetImageResolutionL" );
CImageDecoder* imageDecoder = NULL;
TRAPD( createError, imageDecoder = CImageDecoder::FileNewL(
aFile,
aMimetype,
ContentAccess::EPeek ) );
if ( createError )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::GetImageResolutionL \
CreateError" );
User::Leave( createError );
}
CleanupStack::PushL( imageDecoder );
TSize imageResolution = imageDecoder->FrameInfo().iOverallSizeInPixels;
CleanupStack::PopAndDestroy( imageDecoder );
return imageResolution;
}
// --------------------------------------------------------------------------
// From class MMdaAudioPlayerCallback.
// MapcPlayComplete callback (not used)
// --------------------------------------------------------------------------
//
void CUpnpResResolver::MapcPlayComplete( TInt /*aError*/ )
{
// not used.
}
// --------------------------------------------------------------------------
// From class MMdaAudioPlayerCallback.
// MapcInitComplete callback is called after a call to
// CMdaAudioConvertUtility::OpenL has completed.
// --------------------------------------------------------------------------
//
void CUpnpResResolver::MapcInitComplete( TInt aError,
const TTimeIntervalMicroSeconds& aDuration )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::MapcInitComplete" );
// if opening was successful, save duration to member variable
if( KErrNone == aError )
{
iDuration = aDuration;
}
// stop scheduler for audio resolving thread
CActiveScheduler::Stop();
}
// -----------------------------------------------------------------------------
// CUpnpResResolver::ThreadFunction
// -----------------------------------------------------------------------------
//
TInt CUpnpResResolver::ThreadFunction( TAny* aSelf )
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::ThreadFunction" );
TInt err = KErrNone;
CUpnpResResolver* self = static_cast<CUpnpResResolver*>( aSelf );
// create cleanup stack
CTrapCleanup* cleanupStack = CTrapCleanup::New();
if( cleanupStack )
{
// execute thread function
TRAP( err, self->ResolveAudioDurationL() );
// cleanup
delete cleanupStack;
}
// reset scheduler for this thread
CActiveScheduler::Install( NULL );
// signal main thread so that it can continue
self->iSemaphore.Signal();
return err;
}
// -----------------------------------------------------------------------------
// CUpnpResResolver::ResolveAudioDurationL
// -----------------------------------------------------------------------------
//
void CUpnpResResolver::ResolveAudioDurationL()
{
__LOG( "[UPnPDlnaProfiler] CUpnpResResolver::ResolveAudioDurationL" );
CActiveScheduler* scheduler = new( ELeave ) CActiveScheduler;
if( scheduler )
{
CleanupStack::PushL( scheduler );
// install the new scheduler in use for this thread
CActiveScheduler::Install( scheduler );
// create audio player utility
CMdaAudioPlayerUtility* audioPlayerUtility =
CMdaAudioPlayerUtility::NewL( *this );
CleanupStack::PushL( audioPlayerUtility );
// open file and wait until the audio clip initialization is ready
audioPlayerUtility->OpenFileL( *iAVFile );
// start scheduler
CActiveScheduler::Start();
// cleanup
CleanupStack::PopAndDestroy( audioPlayerUtility );
CleanupStack::PopAndDestroy( scheduler );
}
}
// end of file