os/mm/mmlibs/mmfw/Recogniser/src/mpeg4parser.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 #include "parsers.h"
    17 #include "constants.h"
    18 
    19 //
    20 // Brands.
    21 // Some (most) brands have Release information - e.g. 3gp6, 3gp5 etc.
    22 // Therefore, to match the brand, we only look at the characters that don't
    23 // represent Release information (in the above case, the 4th character).
    24 // The Release character is set to zero.
    25 //
    26 static const TUint32 KMP4Brand = MAKE_INT32('m', 'p', '4', 0);
    27 static const TUint32 K3GPBrand = MAKE_INT32('3', 'g', 'p', 0);
    28 static const TUint32 K3G2Brand = MAKE_INT32('3', 'g', '2', 0);
    29 static const TUint32 K3GSBrand = MAKE_INT32('3', 'g', 's', 0);	// Streaming servers.
    30 static const TUint32 K3GRBrand = MAKE_INT32('3', 'g', 'r', 0);	// Progresive download and MMS.
    31 static const TUint32 KQTBrand  = MAKE_INT32('q', 't', ' ', ' '); // for quicktime
    32 //
    33 // Box identifiers.
    34 //
    35 static const TUint32 KFtyp = MAKE_INT32('f', 't', 'y', 'p');
    36 static const TUint32 KMoov = MAKE_INT32('m', 'o', 'o', 'v');
    37 static const TUint32 KTrak = MAKE_INT32('t', 'r', 'a', 'k');
    38 static const TUint32 KTkhd = MAKE_INT32('t', 'k', 'h', 'd');
    39 static const TUint32 KMdia = MAKE_INT32('m', 'd', 'i', 'a');
    40 static const TUint32 KHdlr = MAKE_INT32('h', 'd', 'l', 'r');
    41 static const TUint32 KVide = MAKE_INT32('v', 'i', 'd', 'e');
    42 static const TUint32 KUuid = MAKE_INT32('u', 'u', 'i', 'd');
    43 
    44 
    45 //
    46 // This truth table maps the following flags to a confidence level.
    47 //
    48 // A - trak box present
    49 // B - moov box present
    50 //
    51 // A B -> Confidence
    52 // =================
    53 // 0 0 -> EPossible
    54 // 0 1 -> EProbable
    55 // 1 0 -> EProbable
    56 // 1 1 -> ECertain
    57 //
    58 static const TInt KMPEG4FlagsToConfidence[] =
    59 	{
    60 	KConfPossible,
    61 	KConfProbable,
    62 	KConfProbable,
    63 	KConfCertain,
    64 	};
    65 
    66 
    67 #define KMPEG4ConfidenceMask	0x03	// 00000011
    68 #define KMPEG4BoxTitleLen		4
    69 #define KMPEG4TRAKBit			KBit0	// 00000001
    70 #define KMPEG4MOOVBit			KBit1	// 00000010
    71 #define KMPEG4VideoBit			KBit2	// 00000100
    72 
    73 static const TInt KMPEG4BoxIntroLen = 8;
    74 static const TInt KMPEG4Box64BitIntroLen = 16;
    75 
    76 //
    77 // In order to find out the type of MPEG4 file it is
    78 // we need to be able to map known extensions, expected
    79 // ftyp expressions with MIME-types.
    80 //
    81 typedef struct
    82 	{
    83 	const TText* iExt;
    84 	TUint32 iBrand;
    85 	const TText8* iAudioMime;
    86 	const TText8* iVideoMime;
    87 	}
    88 TMPEG4File;
    89 
    90 
    91 //
    92 // A table of ftyp's, extensions and mime-types.
    93 //
    94 // .mp4 - can contain either audio or video.
    95 // .m4a - should contain (unprotected) audio only.
    96 // .m4p - should contain protected audio only.
    97 // .3gp - can contain either audio or video.
    98 //
    99 static const TMPEG4File KMPEG4Files[] =
   100 	{
   101 		{KExtMP4,	KMP4Brand,	KMimeMP4_A,	KMimeMP4_V},
   102 		{KExt3GP,	K3GPBrand,	KMime3GP_A,	KMime3GP_V},
   103 		{KExtM4A,	KMP4Brand,	KMimeMP4_A,	NULL},
   104 		{KExt3G2,	K3G2Brand,	KMime3G2_A,	KMime3G2_V},
   105 		{KExt3GP,	K3GSBrand,	KMime3GP_A,	KMime3GP_V},
   106 		{KExt3GP,	K3GRBrand,	KMime3GP_A,	KMime3GP_V},
   107 		{KExt3GA,	K3GPBrand,	KMime3GA,	NULL},
   108 		{KExtMOV,   KQTBrand,   NULL, KMimeQuickV} // this entry is for .mov files
   109 	};
   110 
   111 static const TInt KMPEG4FileTypeCount = sizeof(KMPEG4Files) / sizeof(TMPEG4File);
   112 
   113 
   114 //
   115 //
   116 //
   117 TMPEG4Parser::TMPEG4Parser(CReader& aReader, TFlags& aFlags)
   118  :	iBrandIndex(KErrNotFound),
   119  	iIsFinished(EFalse),
   120  	iReader(aReader),
   121  	iFlags(aFlags),
   122  	iVideoAssumed(EFalse)
   123 	{
   124 	}
   125 
   126 
   127 //
   128 // Compare a brand with the ones this recogniser knows about.
   129 //
   130 TInt TMPEG4Parser::IsCompatibleBrand(TUint32 aBrand, TInt aStartPos)
   131 	{
   132 	for (TInt i = aStartPos; i < KMPEG4FileTypeCount; i++)
   133 		{
   134 		TUint32 knownBrand = KMPEG4Files[i].iBrand;
   135 		if ((aBrand & knownBrand) == knownBrand)
   136 			{
   137 			return i;
   138 			}
   139 		}
   140 
   141 	return KErrNotFound;
   142 	}
   143 
   144 
   145 //
   146 // Try to determine the mime-type from the extension, and
   147 // if that isn't matched, from the FTYP field if present.
   148 // If none of these are matched, NULL is returned and the
   149 // file isn't a valid MPEG4 file.
   150 //
   151 const TText8* TMPEG4Parser::MatchFileType(const TDesC& aExt)
   152 	{
   153 	TBool videoFound = iFlags.GetBitField(KMPEG4VideoBit);
   154 	TBool video = (videoFound || iVideoAssumed);
   155 	
   156 	// Try to match the extension.
   157 	if (aExt.Length() > 0)
   158 		{
   159 		for (TInt i = 0; i < KMPEG4FileTypeCount; i++)
   160 			{
   161 			TPtrC ext(KMPEG4Files[i].iExt);
   162 			if (aExt.MatchF(ext) != KErrNotFound)
   163 				{
   164 				// Extension match. If the extension is for an audio-only format
   165 				// we must make sure there is no video content in the file. If
   166 				// video content is present then ignore the extension match.
   167 				if (KMPEG4Files[i].iVideoMime == NULL)
   168 					{
   169 					if (videoFound)
   170 						{
   171 						// Try to match another extension.
   172 						continue;
   173 						}
   174 					
   175 					return KMPEG4Files[i].iAudioMime;
   176 					}
   177 				
   178 				return (video ? KMPEG4Files[i].iVideoMime : KMPEG4Files[i].iAudioMime);
   179 				}
   180 			}
   181 		}
   182 
   183 	// Unknown or no extension. Try to match the brand
   184 	while (iBrandIndex != KErrNotFound)
   185 		{
   186 		if (KMPEG4Files[iBrandIndex].iVideoMime == NULL)
   187 			{
   188 			if (videoFound)
   189 				{
   190 				// Try to match another brand.
   191 				TUint32 brand = KMPEG4Files[iBrandIndex].iBrand;
   192 				iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand, iBrandIndex + 1);
   193 				continue;
   194 				}
   195 			
   196 			return KMPEG4Files[iBrandIndex].iAudioMime;
   197 			}
   198 		
   199 		return (video ? KMPEG4Files[iBrandIndex].iVideoMime : KMPEG4Files[iBrandIndex].iAudioMime);
   200 		}
   201 
   202 	// If there is no brand and an unknown extension look at the flags.
   203 	// (There are some files that have no ftyp).
   204 	// Only return a potential mime-type if all flag bits have been set.
   205 	if (iFlags.GetBitField(KMPEG4ConfidenceMask) == KMPEG4ConfidenceMask)
   206 		{
   207 		return (video ? KMimeMP4_V : KMimeMP4_A);
   208 		}
   209 		
   210 	return NULL;
   211 	}
   212 
   213 
   214 //
   215 //
   216 //
   217 void TMPEG4Parser::DoRecognise(const TDesC& aFileExt, CReader& aReader, TMatch& aMatch)
   218 	{
   219 	TFlags flags;
   220 	TMPEG4Parser parser(aReader, flags);
   221 			
   222 	TRAP_IGNORE(parser.ParseL());
   223 	
   224 	// The extension determines the mime-type.
   225 	// The flags say if it's a valid MPEG4 file and if video is present.
   226 	const TText8* extMime = parser.MatchFileType(aFileExt);
   227 	if (extMime != NULL)
   228 		{
   229 		TInt confIndex = flags.GetBitField(KMPEG4ConfidenceMask);
   230 		aMatch.iConfidence = KMPEG4FlagsToConfidence[confIndex];
   231 		if (aMatch.iConfidence != KConfNotRecognised)
   232 			{
   233 			aMatch.iMime = extMime;
   234 			}
   235 		}
   236 	}
   237 	
   238 
   239 //
   240 //
   241 //
   242 void TMPEG4Parser::ParseL()
   243 	{
   244 	// If we have only buffer data, we must assume the video
   245 	// content (if any) has been missed. This is because an
   246 	// audio-only file recognised as video should play in a
   247 	// video application, but a video file recognised as audio
   248 	// will not play in a audio application.
   249 	if (iReader.Type() == CReader::EBuffer)
   250 		{
   251 		iVideoAssumed = ETrue;
   252 		}
   253 		
   254 	do
   255 		{		
   256 		ReadBoxHeaderL();
   257 		if (iTitle == KFtyp)
   258 			{
   259 			ReadFileTypeL();
   260 			}
   261 		else if (iTitle == KMoov)
   262 			{
   263 			iFlags.SetBit(KMPEG4MOOVBit);
   264 			ReadMovieL();
   265 			}
   266 		else
   267 			{
   268 			SkipCurrentBoxL();
   269 			}
   270 		}
   271 	while (!iIsFinished);
   272 	}
   273 
   274 
   275 //
   276 //
   277 //
   278 void TMPEG4Parser::SkipCurrentBoxL()
   279 	{
   280 	// Intro: [size][title] = 8 bytes.
   281 		
   282 	if (iSize == 0)
   283 		{
   284 		// The current box extends to the end of file.
   285 		iIsFinished = ETrue;
   286 		return;
   287 		}
   288 	if(iSizeIn32bit)
   289 	    {
   290         iReader.SeekL(iSize - KMPEG4BoxIntroLen);
   291 	    }
   292 	else
   293 	    {
   294         iReader.SeekL(iSize - KMPEG4Box64BitIntroLen);
   295 	    }
   296 	}
   297 
   298 
   299 //
   300 // Parses the 'moov' box.
   301 // This box contains sub-boxes we're interested in.
   302 //
   303 void TMPEG4Parser::ReadMovieL()
   304 	{
   305 	// This box holds no information.
   306 	// It contains 'trak' boxes, which we want.
   307 	
   308 	TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
   309 	
   310 	while ((dataInBox > 0) && !iIsFinished)
   311 		{
   312 		ReadBoxHeaderL();
   313 		dataInBox -= iSize;
   314 		
   315 		if (iTitle == KTrak)
   316 			{
   317 			iFlags.SetBit(KMPEG4TRAKBit);
   318 			ReadTrackL();
   319 			}
   320 		else
   321 			{
   322 			SkipCurrentBoxL();
   323 			}
   324 		}
   325 	}
   326 	
   327 
   328 //
   329 // Parses the 'trak' box.
   330 // This box contains sub-boxes we're interested in.
   331 //
   332 void TMPEG4Parser::ReadTrackL()
   333 	{
   334 	// This box holds no information.
   335 	// It contains 'tkhd' boxes, which we want.
   336 	
   337 	TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
   338 	
   339 	while ((dataInBox > 0) && !iIsFinished) 
   340 		{
   341 		ReadBoxHeaderL();
   342 		dataInBox -= iSize;
   343 		
   344 		if (iTitle == KTkhd)
   345 			{
   346 			ReadTrackHeaderL();
   347 			}
   348 		else if (iTitle == KMdia)
   349 			{
   350 			ReadMediaL();
   351 			}
   352 		else
   353 			{
   354 			SkipCurrentBoxL();
   355 			}
   356 		}
   357 	}
   358 
   359 //
   360 // Parses a 'mdia' box.
   361 // This box contains sub-boxes we're interested in.
   362 //
   363 void TMPEG4Parser::ReadMediaL()
   364 	{
   365 	TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
   366 	
   367 	while ((dataInBox > 0) && !iIsFinished)
   368 		{
   369 		ReadBoxHeaderL();
   370 		dataInBox -= iSize;
   371 		
   372 		if (iTitle == KHdlr)
   373 			{
   374 			ReadHandlerL();
   375 			}
   376 		else
   377 			{
   378 			SkipCurrentBoxL();
   379 			}
   380 		}
   381 	}
   382 
   383 
   384 //
   385 // Parses a 'hdlr' box.
   386 // This is a stand-alone box.
   387 //
   388 void TMPEG4Parser::ReadHandlerL()
   389 	{
   390 	// Intro: [size][title][versionFlags][predefined][handler_type] = 20 bytes.
   391 	const TInt KMPEG4HandlerIntroLen = 20;
   392 	TUint32 versionFlags;
   393 	TUint32 predefined;
   394 	TUint32 handler;
   395 
   396 	iReader.Read32L(versionFlags);
   397 	iReader.Read32L(predefined);
   398 	if (predefined != 0)
   399 		{
   400 		User::Leave(KErrCorrupt);
   401 		}
   402 		
   403 	iReader.Read32L(handler);
   404 	if (handler == KVide)
   405 		{
   406 		iFlags.SetBit(KMPEG4VideoBit);
   407 		}
   408 		
   409 	iReader.SeekL(iSize - KMPEG4HandlerIntroLen);
   410 	}
   411 
   412 
   413 //
   414 //
   415 //
   416 void TMPEG4Parser::ReadTrackHeaderL()
   417 	{
   418 	const TUint8 KMPEG4TrackVersion0 = 0;
   419 	const TUint8 KMPEG4TrackVersion1 = 1;
   420 	const TInt KMPEG4Version0ToVolume = 32; // Distance to volume field from version=0 field.
   421 	const TInt KMPEG4Version1ToVolume = 44; // Distance to volume field from version=1 field.
   422 	const TInt KMPEG4VolumeToWidth = 38;	// Distance to width field from volume field.
   423 
   424 	TUint32 versionFlags;
   425 	TUint16 volume;
   426 	TUint32 width;
   427 	TUint32 height;
   428 	
   429 	// This box contains information about a single track.
   430 	iReader.Read32L(versionFlags);
   431 	
   432 	// The highest 8 bits contains the version.
   433 	switch (HIGH_BYTE32(versionFlags))
   434 		{
   435 		case KMPEG4TrackVersion0:
   436 			iReader.SeekL(KMPEG4Version0ToVolume);
   437 			break;
   438 			
   439 		case KMPEG4TrackVersion1:
   440 			iReader.SeekL(KMPEG4Version1ToVolume);
   441 			break;
   442 			
   443 		default:
   444 			User::Leave(KErrCorrupt);
   445 		}
   446 	
   447 	// Volume can not be used to distinguish between audio and video anymore.
   448 	iReader.Read16L(volume);
   449 		
   450 	// We want to seek ahead to read the 'width' and 'height' fields.
   451 	iReader.SeekL(KMPEG4VolumeToWidth);
   452 
   453 	iReader.Read32L(width);		// 16.16 fixed-point
   454 	iReader.Read32L(height);	// 16.16 fixed-point
   455 	if ((width != 0) && (height != 0))
   456 		{
   457 		iFlags.SetBit(KMPEG4VideoBit);
   458 		}
   459 	}
   460 
   461 	
   462 //
   463 // Parses the 'ftyp' box.
   464 // Records the first recognised brand that helps to
   465 // identify the mime-type.
   466 //
   467 void TMPEG4Parser::ReadFileTypeL()
   468 	{
   469 	// Intro = [size][title][majorBrand] = 12 bytes.
   470 	const TInt KMPEG4FtypIntroLen = 12;
   471 	TUint32 brand;
   472 	
   473 	// If the majorBrand isn't recognised we should also
   474 	// search the compatible brand list.
   475 	TInt64 bytesRemaining = iSize - KMPEG4FtypIntroLen;
   476 	//here there should be bytes remaining. Otherwise we cant read.
   477 	if( bytesRemaining <0 )
   478 	{
   479 	    User::Leave(KErrCorrupt);    
   480 	}
   481 	
   482 	iReader.Read32L(brand);
   483 	iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
   484 	if (iBrandIndex != KErrNotFound)
   485 		{
   486 		// The major brand was recognised.
   487 		// Skip to the end of the ftyp box.
   488 		iReader.SeekL(bytesRemaining);
   489 		return;
   490 		}
   491 		
   492 	// The major brand wasn't recognised.
   493 	// Skip over the version info (32 bit) to the start of the
   494 	// compatible brands list.
   495 	TInt skip = sizeof(TUint32);
   496 	iReader.SeekL(skip);
   497 	bytesRemaining -= skip;
   498 	
   499 	while ((iBrandIndex == KErrNotFound) && (bytesRemaining > 0))
   500 		{
   501 		iReader.Read32L(brand);
   502 		bytesRemaining -= skip;
   503 		iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
   504 		}
   505 
   506 	iReader.SeekL(bytesRemaining);
   507 	}
   508 
   509 
   510 //
   511 // Reads the first few bytes of a box.
   512 // The exact amount read depends on the box.
   513 //
   514 void TMPEG4Parser::ReadBoxHeaderL()
   515 	{
   516 	const TInt KMPEG4ExtendedTypeLen = 16;
   517 	
   518 	TUint32 word1;
   519 
   520 	iReader.Read32L(word1);
   521 	iReader.Read32L(iTitle);
   522 
   523 	switch (word1)
   524 		{
   525 		case 0:
   526 			// Box extends to the end of file.
   527 			iSize = MAKE_TINT64(0, 0);
   528 			break;
   529 			
   530 		case 1:
   531 			// Size is specified in a 64-bit field.
   532 		    iSizeIn32bit = EFalse;
   533 			iReader.Read64L(iSize);
   534 			break;
   535 			
   536 		default:
   537 			// It's an actual 32-bit size.
   538 		    iSizeIn32bit = ETrue;
   539 			iSize = MAKE_TINT64(0, word1);
   540 		}
   541 		
   542 	if (iTitle == KUuid)
   543 		{
   544 		// Skip the extended type.
   545 		iReader.SeekL(KMPEG4ExtendedTypeLen);
   546 		}
   547 	}
   548 
   549