/*
* 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 of base DLNA profile resolver class for av
* files.
*
*/
// includes
#include <badesca.h> // CDesC16ArrayFlat
#include <3gplibrary/mp4lib.h>
#include "upnpavsolverbase.h"
_LIT( KComponentLogfile, "dlnaprofiler.txt");
#include "upnplog.h"
// constants
_LIT8( KVideoMp4, "video/mp4" );
_LIT( KMpeg4_p2_mp4_sp_l2_aac, "MPEG4_P2_MP4_SP_L2_AAC" );
_LIT( KMpeg4_p2_mp4_sp_aac, "MPEG4_P2_MP4_SP_AAC" );
_LIT( KMpeg4_p2_mp4_sp_vga_aac, "MPEG4_P2_MP4_SP_VGA_AAC" );
_LIT( KMpeg4_p2_mp4_asp_l4_so_aac, "MPEG4_P2_MP4_ASP_L4_SO_AAC" );
_LIT( KMpeg4_p2_mp4_sp_l5_aac, "MPEG4_P2_MP4_SP_L5_AAC" );
_LIT( KAvc_mp4_bl_cif15_aac, "AVC_MP4_BL_CIF15_AAC" );
_LIT( KAvc_mp4_bl_cif15_aac_520, "AVC_MP4_BL_CIF15_AAC_520" );
_LIT( KAvc_mp4_mp_hd_720p_aac, "AVC_MP4_MP_HD_720p_AAC" );
_LIT( KAvc_mp4_hp_hd_aac, "AVC_MP4_HP_HD_AAC" );
_LIT( KAvc_mp4_bl_l31_hd_aac, "AVC_MP4_BL_L31_HD_AAC" );
const TUint32 KSimpleProfileLevel2 = 0x02;
const TUint32 KSimpleProfileLevel3 = 0x03;
const TUint32 KSimpleProfileLevel4 = 0x04;
const TUint32 KSimpleProfileLevel5 = 0x05;
const mp4_u32 KMaxXResolutionCif = 352;
const mp4_u32 KMaxYResolutionCif = 288;
const mp4_u32 KMaxXResolutionVga = 640;
const mp4_u32 KMaxYResolutionVga = 480;
const TUint32 KAvcLevel31 = 31;
const TUint32 KAvcLevel40 = 40;
//const TInt KBitrateAverageToMaxFactor = 20;
//const mp4_u32 KMaxBitrateCif520 = 520;
//const mp4_u32 KMaxBitrateCif = 384;
//const TUint32 KAdvancedSimpleProfileLevel1 = 0x91;
//const TUint32 KAdvancedSimpleProfileLevel2 = 0x92;
//const TUint32 KAdvancedSimpleProfileLevel3 = 0x93;
//const TUint32 KAdvancedSimpleProfileLevel4 = 0x94;
//const TUint32 KAvcBaseline1_2 = 12;
/*
===========================================================================
Codec-specific details are explained here
(for the part that is currently required by upnp framework)
MPEG4 part2
------------
see ISO/IEC 14496-2, chapter 6.2.2 and annex G
(http://akuvian.org/src/x264/ISO-IEC-14496-2_2001_MPEG4_Visual.pdf.gz)
32 bit: visual_object_sequence_start_code (not interesting)
8 bit: profile_and_level_indication, where:
0x02 = simple profile, level 2
0x03 = simple profile, level 3
0x04 = simple profile, level 4a
0x05 = simple profile, level 5
0x91 = advanced simple profile, level 1
0x92 = advanced simple profile, level 2
0x93 = advanced simple profile, level 3
0x94 = advanced simple profile, level 4
...
MPEG4 part 10 (AVC)
-------------------
see ISO/IEC 14496-15, chapter 5.2.4.1.1
(http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=43178)
8 bit: version (not interesting)
8 bit: AVC profile indication, where
0x42 = AVC baseline
0x4D = AVC main
0x58 = AVC extended
8 bit: compatibility flags (not interesting)
8 bit: AVC level indication, where
10 = baseline level 1.0
11 = baseline level 1.1
12 = baseline level 1.2
13 = baseline level 1.3
20 = baseline level 2.0
21 = baseline level 2.1
22 = baseline level 2.2
30 = baseline level 3.0
...
===========================================================================
*/
// --------------------------------------------------------------------------
// CUpnpAvSolverBase C++ constructor
// --------------------------------------------------------------------------
//
CUpnpAvSolverBase::CUpnpAvSolverBase()
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase constructor" );
}
// --------------------------------------------------------------------------
// CUpnpAvSolverBase::ConstructL
// --------------------------------------------------------------------------
//
void CUpnpAvSolverBase::ConstructL()
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase::ConstructL" );
}
// --------------------------------------------------------------------------
// CUpnpAvSolverBase::NewL
// --------------------------------------------------------------------------
//
CUpnpAvSolverBase* CUpnpAvSolverBase::NewL()
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase::NewL" );
CUpnpAvSolverBase* self = CUpnpAvSolverBase::NewLC();
CleanupStack::Pop( self );
return self;
}
// --------------------------------------------------------------------------
// CUpnpAvSolverBase::NewLC
// --------------------------------------------------------------------------
//
CUpnpAvSolverBase* CUpnpAvSolverBase::NewLC()
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase::NewLC" );
CUpnpAvSolverBase* self = new( ELeave ) CUpnpAvSolverBase;
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
// --------------------------------------------------------------------------
// CUpnpAvSolverBase destructor
// --------------------------------------------------------------------------
//
CUpnpAvSolverBase::~CUpnpAvSolverBase()
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase destructor" );
}
// --------------------------------------------------------------------------
// From class MUpnpProfiler.
// SupportedProfilesL returns DLNA profiles that are currently supported.
// --------------------------------------------------------------------------
//
TInt CUpnpAvSolverBase::SupportedProfilesL(
CDesC16ArrayFlat* aProfiles ) const
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase::SupportedProfilesL" );
TInt retval = KErrNone;
if ( !aProfiles )
{
// Invalid parameter
retval = KErrArgument;
}
else
{
// append all new profiles recognized by this solver
// do not allow duplicates
TInt tempPos = KErrNotFound;
if ( aProfiles->Find( KMpeg4_p2_mp4_sp_l2_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KMpeg4_p2_mp4_sp_l2_aac() );
}
if ( aProfiles->Find( KMpeg4_p2_mp4_sp_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KMpeg4_p2_mp4_sp_aac() );
}
if ( aProfiles->Find( KMpeg4_p2_mp4_sp_vga_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KMpeg4_p2_mp4_sp_vga_aac() );
}
if ( aProfiles->Find( KMpeg4_p2_mp4_asp_l4_so_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KMpeg4_p2_mp4_asp_l4_so_aac() );
}
if ( aProfiles->Find( KMpeg4_p2_mp4_sp_l5_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KMpeg4_p2_mp4_sp_l5_aac() );
}
if ( aProfiles->Find( KAvc_mp4_bl_cif15_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KAvc_mp4_bl_cif15_aac() );
}
if ( aProfiles->Find( KAvc_mp4_bl_cif15_aac_520(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KAvc_mp4_bl_cif15_aac_520() );
}
if ( aProfiles->Find( KAvc_mp4_mp_hd_720p_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KAvc_mp4_mp_hd_720p_aac() );
}
if ( aProfiles->Find( KAvc_mp4_hp_hd_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KAvc_mp4_hp_hd_aac() );
}
if ( aProfiles->Find( KAvc_mp4_bl_l31_hd_aac(),
tempPos,
ECmpFolded ) )
{
aProfiles->AppendL( KAvc_mp4_bl_l31_hd_aac() );
}
}
return retval;
}
// --------------------------------------------------------------------------
// From class MUpnpProfiler.
// ProfileForFileL is for resolving a DLNA profile of a given file. Besides
// of file name, also mime type of the file is passed as a parameter in order
// to avoid re-opening the file.
// --------------------------------------------------------------------------
//
HBufC* CUpnpAvSolverBase::ProfileForFileL( const TDesC& /*aFilename*/,
const TDesC8& aMimetype,
RFile& aFile )
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase::ProfileForFileL" );
HBufC* retval = NULL;
if ( aMimetype.Compare( KVideoMp4() ) == 0 )
{
GetVideoFileInformationL( aFile );
__LOG2( "[UPnPDlnaProfiler] CUpnpAvSolverBase::ProfileForFileL: Video type = 0x%x, Audio type = 0x%x",
iVideoType, iAudioType );
if ( iVideoType == MP4_TYPE_MPEG4_VIDEO &&
iAudioType == MP4_TYPE_MPEG4_AUDIO )
{
// MPEG4 level 2
TUint32 level = iCodecInfo.iData[1] & 0x000000FF;
__LOG1( "[CUpnpAvSolverBase] CUpnpAvSolverBase::\
GetVideoFileInformationL level %d", level );
if ( level == KSimpleProfileLevel2 )
{
// Simple profile level 2
retval = KMpeg4_p2_mp4_sp_l2_aac().AllocL();
}
else if ( level == KSimpleProfileLevel3 )
{
// Simple profile level 3
if ( iVideoResolutionX <= KMaxXResolutionCif &&
iVideoResolutionY <= KMaxYResolutionCif )
{
// resolution below CIF standard
retval = KMpeg4_p2_mp4_sp_aac().AllocL();
}
else if ( iVideoResolutionX <= KMaxXResolutionVga &&
iVideoResolutionY <= KMaxYResolutionVga )
{
// resolution below VGA standard
retval = KMpeg4_p2_mp4_sp_vga_aac().AllocL();
}
}
else if ( level == KSimpleProfileLevel4 )
{
// Simple profile level 4a
retval = KMpeg4_p2_mp4_asp_l4_so_aac().AllocL();
}
else if ( level == KSimpleProfileLevel5 )
{
// Simple profile level 5
retval = KMpeg4_p2_mp4_sp_l5_aac().AllocL();
}
/*
Not needed yet:
else if ( level == KAdvancedSimpleProfileLevel1 ||
level == KAdvancedSimpleProfileLevel2 ||
level == KAdvancedSimpleProfileLevel3 ||
level == KAdvancedSimpleProfileLevel4 )
{
// Advanced simple profile (levels 1-4)
retval = HBufC::NewL( KMpeg4_p2_mp4_sp_l5_aac().Length() );
retval->Des().Append( KMpeg4_p2_mp4_sp_l5_aac() );
}
*/
}
/*
Temporary solution, based on AVC video resolution.
iLevel should be used instead, see above Mp4 P2.
*/
else if ( iVideoType == MP4_TYPE_AVC_PROFILE_BASELINE &&
iAudioType == MP4_TYPE_MPEG4_AUDIO )
{
// MPEG4 part 10 (AVC)
if (iVideoResolutionX <= KMaxXResolutionCif &&
iVideoResolutionY <= KMaxYResolutionCif )
{
retval = KAvc_mp4_bl_cif15_aac_520().AllocL();
}
else
{
TUint32 level = (iCodecInfo.iData[0] & 0xFF000000) >> 24;
__LOG1( "[UPnPDlnaProfiler] CUpnpAvSolverBase::ProfileForFileL: Level = %d", level );
if ( level == KAvcLevel31 )
{
retval = KAvc_mp4_bl_l31_hd_aac().AllocL();
}
}
}
else if ( iVideoType == MP4_TYPE_AVC_PROFILE_MAIN &&
iAudioType == MP4_TYPE_MPEG4_AUDIO )
{
TUint32 level = (iCodecInfo.iData[0] & 0xFF000000) >> 24;
if ( level == KAvcLevel31 )
{
retval = KAvc_mp4_mp_hd_720p_aac().AllocL();
}
}
}
return retval;
}
// --------------------------------------------------------------------------
// GetVideoFileInformationL is for resolving audio file attributes by using
// CMdaAudioConvertUtility.
// --------------------------------------------------------------------------
//
TInt CUpnpAvSolverBase::GetVideoFileInformationL( RFile& aFile )
{
__LOG( "[UPnPDlnaProfiler] CUpnpAvSolverBase::GetVideoFileInformationL" );
TInt retval = KErrNone;
// video description
mp4_u32 videolength;
mp4_u32 videotimescale;
// audio params
mp4_u32 audiolength = 0;
mp4_u8 audioframes = 0;
mp4_u32 audiotimescale = 0;
mp4_u32 audiobitrate = 0;
// stream params
mp4_u32 streamsize = 0;
// reset old values
iStreamAverageBitrate = 0;
MP4Handle myMp4Handle;
// try open mp4 file handle
MP4Err openerr = MP4ParseOpenFileHandle( &myMp4Handle, &aFile );
if ( openerr == MP4_OK )
{
MP4Err requesterr = MP4ParseRequestVideoDescription(
myMp4Handle,
&videolength,
&iVideoFramerate,
&iVideoType,
&iVideoResolutionX,
&iVideoResolutionY,
&videotimescale );
if ( requesterr != MP4_OK )
{
MP4ParseClose( myMp4Handle );
User::Leave( KErrGeneral );
}
requesterr = MP4ParseRequestAudioDescription(
myMp4Handle,
&audiolength,
&iAudioType,
&audioframes,
&audiotimescale,
&audiobitrate );
if ( requesterr != MP4_OK )
{
MP4ParseClose( myMp4Handle );
User::Leave( KErrGeneral );
}
requesterr = MP4ParseRequestStreamDescription(
myMp4Handle,
&streamsize,
&iStreamAverageBitrate );
if ( requesterr != MP4_OK )
{
MP4ParseClose( myMp4Handle );
User::Leave( KErrGeneral );
}
TMP4DecoderSpecificInfo iDecoderInfo;
mp4_u32 decoderInfoSize;
requesterr = MP4ParseReadVideoDecoderSpecificInfo(
myMp4Handle,
(mp4_u8*)&iDecoderInfo,
sizeof( iDecoderInfo ),
&decoderInfoSize );
if ( requesterr != MP4_OK )
{
MP4ParseClose( myMp4Handle );
User::Leave( KErrGeneral );
}
iCodecInfo = iDecoderInfo;
// close mp4 file handle
MP4Err closeerr = MP4ParseClose( myMp4Handle );
if ( closeerr )
{
User::Leave( KErrGeneral );
}
}
else
{
// can not open file
User::Leave( KErrBadHandle );
}
return retval;
}
// end of file