mmlibs/mmfw/Recogniser/src/mpeg4parser.cpp
changeset 0 40261b775718
child 11 d5f04de580b7
equal deleted inserted replaced
-1:000000000000 0:40261b775718
       
     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 
       
    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 
       
    75 //
       
    76 // In order to find out the type of MPEG4 file it is
       
    77 // we need to be able to map known extensions, expected
       
    78 // ftyp expressions with MIME-types.
       
    79 //
       
    80 typedef struct
       
    81 	{
       
    82 	const TText* iExt;
       
    83 	TUint32 iBrand;
       
    84 	const TText8* iAudioMime;
       
    85 	const TText8* iVideoMime;
       
    86 	}
       
    87 TMPEG4File;
       
    88 
       
    89 
       
    90 //
       
    91 // A table of ftyp's, extensions and mime-types.
       
    92 //
       
    93 // .mp4 - can contain either audio or video.
       
    94 // .m4a - should contain (unprotected) audio only.
       
    95 // .m4p - should contain protected audio only.
       
    96 // .3gp - can contain either audio or video.
       
    97 //
       
    98 static const TMPEG4File KMPEG4Files[] =
       
    99 	{
       
   100 		{KExtMP4,	KMP4Brand,	KMimeMP4_A,	KMimeMP4_V},
       
   101 		{KExt3GP,	K3GPBrand,	KMime3GP_A,	KMime3GP_V},
       
   102 		{KExtM4A,	KMP4Brand,	KMimeMP4_A,	NULL},
       
   103 		{KExt3G2,	K3G2Brand,	KMime3G2_A,	KMime3G2_V},
       
   104 		{KExt3GP,	K3GSBrand,	KMime3GP_A,	KMime3GP_V},
       
   105 		{KExt3GP,	K3GRBrand,	KMime3GP_A,	KMime3GP_V},
       
   106 		{KExt3GA,	K3GPBrand,	KMime3GA,	NULL}
       
   107 	};
       
   108 
       
   109 static const TInt KMPEG4FileTypeCount = sizeof(KMPEG4Files) / sizeof(TMPEG4File);
       
   110 
       
   111 
       
   112 //
       
   113 //
       
   114 //
       
   115 TMPEG4Parser::TMPEG4Parser(CReader& aReader, TFlags& aFlags)
       
   116  :	iBrandIndex(KErrNotFound),
       
   117  	iIsFinished(EFalse),
       
   118  	iReader(aReader),
       
   119  	iFlags(aFlags),
       
   120  	iVideoAssumed(EFalse)
       
   121 	{
       
   122 	}
       
   123 
       
   124 
       
   125 //
       
   126 // Compare a brand with the ones this recogniser knows about.
       
   127 //
       
   128 TInt TMPEG4Parser::IsCompatibleBrand(TUint32 aBrand, TInt aStartPos)
       
   129 	{
       
   130 	for (TInt i = aStartPos; i < KMPEG4FileTypeCount; i++)
       
   131 		{
       
   132 		TUint32 knownBrand = KMPEG4Files[i].iBrand;
       
   133 		if ((aBrand & knownBrand) == knownBrand)
       
   134 			{
       
   135 			return i;
       
   136 			}
       
   137 		}
       
   138 
       
   139 	return KErrNotFound;
       
   140 	}
       
   141 
       
   142 
       
   143 //
       
   144 // Try to determine the mime-type from the extension, and
       
   145 // if that isn't matched, from the FTYP field if present.
       
   146 // If none of these are matched, NULL is returned and the
       
   147 // file isn't a valid MPEG4 file.
       
   148 //
       
   149 const TText8* TMPEG4Parser::MatchFileType(const TDesC& aExt)
       
   150 	{
       
   151 	TBool videoFound = iFlags.GetBitField(KMPEG4VideoBit);
       
   152 	TBool video = (videoFound || iVideoAssumed);
       
   153 	
       
   154 	// Try to match the extension.
       
   155 	if (aExt.Length() > 0)
       
   156 		{
       
   157 		for (TInt i = 0; i < KMPEG4FileTypeCount; i++)
       
   158 			{
       
   159 			TPtrC ext(KMPEG4Files[i].iExt);
       
   160 			if (aExt.MatchF(ext) != KErrNotFound)
       
   161 				{
       
   162 				// Extension match. If the extension is for an audio-only format
       
   163 				// we must make sure there is no video content in the file. If
       
   164 				// video content is present then ignore the extension match.
       
   165 				if (KMPEG4Files[i].iVideoMime == NULL)
       
   166 					{
       
   167 					if (videoFound)
       
   168 						{
       
   169 						// Try to match another extension.
       
   170 						continue;
       
   171 						}
       
   172 					
       
   173 					return KMPEG4Files[i].iAudioMime;
       
   174 					}
       
   175 				
       
   176 				return (video ? KMPEG4Files[i].iVideoMime : KMPEG4Files[i].iAudioMime);
       
   177 				}
       
   178 			}
       
   179 		}
       
   180 
       
   181 	// Unknown or no extension. Try to match the brand
       
   182 	while (iBrandIndex != KErrNotFound)
       
   183 		{
       
   184 		if (KMPEG4Files[iBrandIndex].iVideoMime == NULL)
       
   185 			{
       
   186 			if (videoFound)
       
   187 				{
       
   188 				// Try to match another brand.
       
   189 				TUint32 brand = KMPEG4Files[iBrandIndex].iBrand;
       
   190 				iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand, iBrandIndex + 1);
       
   191 				continue;
       
   192 				}
       
   193 			
       
   194 			return KMPEG4Files[iBrandIndex].iAudioMime;
       
   195 			}
       
   196 		
       
   197 		return (video ? KMPEG4Files[iBrandIndex].iVideoMime : KMPEG4Files[iBrandIndex].iAudioMime);
       
   198 		}
       
   199 
       
   200 	// If there is no brand and an unknown extension look at the flags.
       
   201 	// (There are some files that have no ftyp).
       
   202 	// Only return a potential mime-type if all flag bits have been set.
       
   203 	if (iFlags.GetBitField(KMPEG4ConfidenceMask) == KMPEG4ConfidenceMask)
       
   204 		{
       
   205 		return (video ? KMimeMP4_V : KMimeMP4_A);
       
   206 		}
       
   207 		
       
   208 	return NULL;
       
   209 	}
       
   210 
       
   211 
       
   212 //
       
   213 //
       
   214 //
       
   215 void TMPEG4Parser::DoRecognise(const TDesC& aFileExt, CReader& aReader, TMatch& aMatch)
       
   216 	{
       
   217 	TFlags flags;
       
   218 	TMPEG4Parser parser(aReader, flags);
       
   219 			
       
   220 	TRAP_IGNORE(parser.ParseL());
       
   221 	
       
   222 	// The extension determines the mime-type.
       
   223 	// The flags say if it's a valid MPEG4 file and if video is present.
       
   224 	const TText8* extMime = parser.MatchFileType(aFileExt);
       
   225 	if (extMime != NULL)
       
   226 		{
       
   227 		TInt confIndex = flags.GetBitField(KMPEG4ConfidenceMask);
       
   228 		aMatch.iConfidence = KMPEG4FlagsToConfidence[confIndex];
       
   229 		if (aMatch.iConfidence != KConfNotRecognised)
       
   230 			{
       
   231 			aMatch.iMime = extMime;
       
   232 			}
       
   233 		}
       
   234 	}
       
   235 	
       
   236 
       
   237 //
       
   238 //
       
   239 //
       
   240 void TMPEG4Parser::ParseL()
       
   241 	{
       
   242 	// If we have only buffer data, we must assume the video
       
   243 	// content (if any) has been missed. This is because an
       
   244 	// audio-only file recognised as video should play in a
       
   245 	// video application, but a video file recognised as audio
       
   246 	// will not play in a audio application.
       
   247 	if (iReader.Type() == CReader::EBuffer)
       
   248 		{
       
   249 		iVideoAssumed = ETrue;
       
   250 		}
       
   251 		
       
   252 	do
       
   253 		{		
       
   254 		ReadBoxHeaderL();
       
   255 		if (iTitle == KFtyp)
       
   256 			{
       
   257 			ReadFileTypeL();
       
   258 			}
       
   259 		else if (iTitle == KMoov)
       
   260 			{
       
   261 			iFlags.SetBit(KMPEG4MOOVBit);
       
   262 			ReadMovieL();
       
   263 			}
       
   264 		else
       
   265 			{
       
   266 			SkipCurrentBoxL();
       
   267 			}
       
   268 		}
       
   269 	while (!iIsFinished);
       
   270 	}
       
   271 
       
   272 
       
   273 //
       
   274 //
       
   275 //
       
   276 void TMPEG4Parser::SkipCurrentBoxL()
       
   277 	{
       
   278 	// Intro: [size][title] = 8 bytes.
       
   279 		
       
   280 	if (iSize == 0)
       
   281 		{
       
   282 		// The current box extends to the end of file.
       
   283 		iIsFinished = ETrue;
       
   284 		return;
       
   285 		}
       
   286 	
       
   287 	iReader.SeekL(iSize - KMPEG4BoxIntroLen);
       
   288 	}
       
   289 
       
   290 
       
   291 //
       
   292 // Parses the 'moov' box.
       
   293 // This box contains sub-boxes we're interested in.
       
   294 //
       
   295 void TMPEG4Parser::ReadMovieL()
       
   296 	{
       
   297 	// This box holds no information.
       
   298 	// It contains 'trak' boxes, which we want.
       
   299 	
       
   300 	TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
       
   301 	
       
   302 	while ((dataInBox > 0) && !iIsFinished)
       
   303 		{
       
   304 		ReadBoxHeaderL();
       
   305 		dataInBox -= iSize;
       
   306 		
       
   307 		if (iTitle == KTrak)
       
   308 			{
       
   309 			iFlags.SetBit(KMPEG4TRAKBit);
       
   310 			ReadTrackL();
       
   311 			}
       
   312 		else
       
   313 			{
       
   314 			SkipCurrentBoxL();
       
   315 			}
       
   316 		}
       
   317 	}
       
   318 	
       
   319 
       
   320 //
       
   321 // Parses the 'trak' box.
       
   322 // This box contains sub-boxes we're interested in.
       
   323 //
       
   324 void TMPEG4Parser::ReadTrackL()
       
   325 	{
       
   326 	// This box holds no information.
       
   327 	// It contains 'tkhd' boxes, which we want.
       
   328 	
       
   329 	TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
       
   330 	
       
   331 	while ((dataInBox > 0) && !iIsFinished) 
       
   332 		{
       
   333 		ReadBoxHeaderL();
       
   334 		dataInBox -= iSize;
       
   335 		
       
   336 		if (iTitle == KTkhd)
       
   337 			{
       
   338 			ReadTrackHeaderL();
       
   339 			}
       
   340 		else if (iTitle == KMdia)
       
   341 			{
       
   342 			ReadMediaL();
       
   343 			}
       
   344 		else
       
   345 			{
       
   346 			SkipCurrentBoxL();
       
   347 			}
       
   348 		}
       
   349 	}
       
   350 
       
   351 //
       
   352 // Parses a 'mdia' box.
       
   353 // This box contains sub-boxes we're interested in.
       
   354 //
       
   355 void TMPEG4Parser::ReadMediaL()
       
   356 	{
       
   357 	TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
       
   358 	
       
   359 	while ((dataInBox > 0) && !iIsFinished)
       
   360 		{
       
   361 		ReadBoxHeaderL();
       
   362 		dataInBox -= iSize;
       
   363 		
       
   364 		if (iTitle == KHdlr)
       
   365 			{
       
   366 			ReadHandlerL();
       
   367 			}
       
   368 		else
       
   369 			{
       
   370 			SkipCurrentBoxL();
       
   371 			}
       
   372 		}
       
   373 	}
       
   374 
       
   375 
       
   376 //
       
   377 // Parses a 'hdlr' box.
       
   378 // This is a stand-alone box.
       
   379 //
       
   380 void TMPEG4Parser::ReadHandlerL()
       
   381 	{
       
   382 	// Intro: [size][title][versionFlags][predefined][handler_type] = 20 bytes.
       
   383 	const TInt KMPEG4HandlerIntroLen = 20;
       
   384 	TUint32 versionFlags;
       
   385 	TUint32 predefined;
       
   386 	TUint32 handler;
       
   387 
       
   388 	iReader.Read32L(versionFlags);
       
   389 	iReader.Read32L(predefined);
       
   390 	if (predefined != 0)
       
   391 		{
       
   392 		User::Leave(KErrCorrupt);
       
   393 		}
       
   394 		
       
   395 	iReader.Read32L(handler);
       
   396 	if (handler == KVide)
       
   397 		{
       
   398 		iFlags.SetBit(KMPEG4VideoBit);
       
   399 		}
       
   400 		
       
   401 	iReader.SeekL(iSize - KMPEG4HandlerIntroLen);
       
   402 	}
       
   403 
       
   404 
       
   405 //
       
   406 //
       
   407 //
       
   408 void TMPEG4Parser::ReadTrackHeaderL()
       
   409 	{
       
   410 	const TUint8 KMPEG4TrackVersion0 = 0;
       
   411 	const TUint8 KMPEG4TrackVersion1 = 1;
       
   412 	const TInt KMPEG4Version0ToVolume = 32; // Distance to volume field from version=0 field.
       
   413 	const TInt KMPEG4Version1ToVolume = 44; // Distance to volume field from version=1 field.
       
   414 	const TInt KMPEG4VolumeToWidth = 38;	// Distance to width field from volume field.
       
   415 
       
   416 	TUint32 versionFlags;
       
   417 	TUint16 volume;
       
   418 	TUint32 width;
       
   419 	TUint32 height;
       
   420 	
       
   421 	// This box contains information about a single track.
       
   422 	iReader.Read32L(versionFlags);
       
   423 	
       
   424 	// The highest 8 bits contains the version.
       
   425 	switch (HIGH_BYTE32(versionFlags))
       
   426 		{
       
   427 		case KMPEG4TrackVersion0:
       
   428 			iReader.SeekL(KMPEG4Version0ToVolume);
       
   429 			break;
       
   430 			
       
   431 		case KMPEG4TrackVersion1:
       
   432 			iReader.SeekL(KMPEG4Version1ToVolume);
       
   433 			break;
       
   434 			
       
   435 		default:
       
   436 			User::Leave(KErrCorrupt);
       
   437 		}
       
   438 	
       
   439 	// Volume can not be used to distinguish between audio and video anymore.
       
   440 	iReader.Read16L(volume);
       
   441 		
       
   442 	// We want to seek ahead to read the 'width' and 'height' fields.
       
   443 	iReader.SeekL(KMPEG4VolumeToWidth);
       
   444 
       
   445 	iReader.Read32L(width);		// 16.16 fixed-point
       
   446 	iReader.Read32L(height);	// 16.16 fixed-point
       
   447 	if ((width != 0) && (height != 0))
       
   448 		{
       
   449 		iFlags.SetBit(KMPEG4VideoBit);
       
   450 		}
       
   451 	}
       
   452 
       
   453 	
       
   454 //
       
   455 // Parses the 'ftyp' box.
       
   456 // Records the first recognised brand that helps to
       
   457 // identify the mime-type.
       
   458 //
       
   459 void TMPEG4Parser::ReadFileTypeL()
       
   460 	{
       
   461 	// Intro = [size][title][majorBrand] = 12 bytes.
       
   462 	const TInt KMPEG4FtypIntroLen = 12;
       
   463 	TUint32 brand;
       
   464 	
       
   465 	// If the majorBrand isn't recognised we should also
       
   466 	// search the compatible brand list.
       
   467 	TInt64 bytesRemaining = iSize - KMPEG4FtypIntroLen;
       
   468 	
       
   469 	iReader.Read32L(brand);
       
   470 	iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
       
   471 	if (iBrandIndex != KErrNotFound)
       
   472 		{
       
   473 		// The major brand was recognised.
       
   474 		// Skip to the end of the ftyp box.
       
   475 		iReader.SeekL(bytesRemaining);
       
   476 		return;
       
   477 		}
       
   478 		
       
   479 	// The major brand wasn't recognised.
       
   480 	// Skip over the version info (32 bit) to the start of the
       
   481 	// compatible brands list.
       
   482 	TInt skip = sizeof(TUint32);
       
   483 	iReader.SeekL(skip);
       
   484 	bytesRemaining -= skip;
       
   485 	
       
   486 	while ((iBrandIndex == KErrNotFound) && (bytesRemaining > 0))
       
   487 		{
       
   488 		iReader.Read32L(brand);
       
   489 		bytesRemaining -= skip;
       
   490 		iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
       
   491 		}
       
   492 
       
   493 	iReader.SeekL(bytesRemaining);
       
   494 	}
       
   495 
       
   496 
       
   497 //
       
   498 // Reads the first few bytes of a box.
       
   499 // The exact amount read depends on the box.
       
   500 //
       
   501 void TMPEG4Parser::ReadBoxHeaderL()
       
   502 	{
       
   503 	const TInt KMPEG4ExtendedTypeLen = 16;
       
   504 	
       
   505 	TUint32 word1;
       
   506 
       
   507 	iReader.Read32L(word1);
       
   508 	iReader.Read32L(iTitle);
       
   509 
       
   510 	switch (word1)
       
   511 		{
       
   512 		case 0:
       
   513 			// Box extends to the end of file.
       
   514 			iSize = MAKE_TINT64(0, 0);
       
   515 			break;
       
   516 			
       
   517 		case 1:
       
   518 			// Size is specified in a 64-bit field.
       
   519 			iReader.Read64L(iSize);
       
   520 			break;
       
   521 			
       
   522 		default:
       
   523 			// It's an actual 32-bit size.
       
   524 			iSize = MAKE_TINT64(0, word1);
       
   525 		}
       
   526 		
       
   527 	if (iTitle == KUuid)
       
   528 		{
       
   529 		// Skip the extended type.
       
   530 		iReader.SeekL(KMPEG4ExtendedTypeLen);
       
   531 		}
       
   532 	}
       
   533 
       
   534