mmlibs/mmfw/Recogniser/src/mpeg4parser.cpp
changeset 0 b8ed18f6c07b
equal deleted inserted replaced
-1:000000000000 0:b8ed18f6c07b
       
     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