upnpsharing/upnpdlnaprofiler/src/upnpavsolverbase.cpp
author samhuttu
Mon, 01 Nov 2010 12:37:49 +0200
branchnew development branch with rendering state machine and other goodies
changeset 38 5360b7ddc251
parent 0 7f85d04be362
permissions -rw-r--r--
New development branch with e.g. rendering state machine and a simple Qt example application using it.

/*
* 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