1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/mm/mmlibs/mmfw/Recogniser/src/mpeg4parser.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,549 @@
1.4 +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +//
1.18 +
1.19 +#include "parsers.h"
1.20 +#include "constants.h"
1.21 +
1.22 +//
1.23 +// Brands.
1.24 +// Some (most) brands have Release information - e.g. 3gp6, 3gp5 etc.
1.25 +// Therefore, to match the brand, we only look at the characters that don't
1.26 +// represent Release information (in the above case, the 4th character).
1.27 +// The Release character is set to zero.
1.28 +//
1.29 +static const TUint32 KMP4Brand = MAKE_INT32('m', 'p', '4', 0);
1.30 +static const TUint32 K3GPBrand = MAKE_INT32('3', 'g', 'p', 0);
1.31 +static const TUint32 K3G2Brand = MAKE_INT32('3', 'g', '2', 0);
1.32 +static const TUint32 K3GSBrand = MAKE_INT32('3', 'g', 's', 0); // Streaming servers.
1.33 +static const TUint32 K3GRBrand = MAKE_INT32('3', 'g', 'r', 0); // Progresive download and MMS.
1.34 +static const TUint32 KQTBrand = MAKE_INT32('q', 't', ' ', ' '); // for quicktime
1.35 +//
1.36 +// Box identifiers.
1.37 +//
1.38 +static const TUint32 KFtyp = MAKE_INT32('f', 't', 'y', 'p');
1.39 +static const TUint32 KMoov = MAKE_INT32('m', 'o', 'o', 'v');
1.40 +static const TUint32 KTrak = MAKE_INT32('t', 'r', 'a', 'k');
1.41 +static const TUint32 KTkhd = MAKE_INT32('t', 'k', 'h', 'd');
1.42 +static const TUint32 KMdia = MAKE_INT32('m', 'd', 'i', 'a');
1.43 +static const TUint32 KHdlr = MAKE_INT32('h', 'd', 'l', 'r');
1.44 +static const TUint32 KVide = MAKE_INT32('v', 'i', 'd', 'e');
1.45 +static const TUint32 KUuid = MAKE_INT32('u', 'u', 'i', 'd');
1.46 +
1.47 +
1.48 +//
1.49 +// This truth table maps the following flags to a confidence level.
1.50 +//
1.51 +// A - trak box present
1.52 +// B - moov box present
1.53 +//
1.54 +// A B -> Confidence
1.55 +// =================
1.56 +// 0 0 -> EPossible
1.57 +// 0 1 -> EProbable
1.58 +// 1 0 -> EProbable
1.59 +// 1 1 -> ECertain
1.60 +//
1.61 +static const TInt KMPEG4FlagsToConfidence[] =
1.62 + {
1.63 + KConfPossible,
1.64 + KConfProbable,
1.65 + KConfProbable,
1.66 + KConfCertain,
1.67 + };
1.68 +
1.69 +
1.70 +#define KMPEG4ConfidenceMask 0x03 // 00000011
1.71 +#define KMPEG4BoxTitleLen 4
1.72 +#define KMPEG4TRAKBit KBit0 // 00000001
1.73 +#define KMPEG4MOOVBit KBit1 // 00000010
1.74 +#define KMPEG4VideoBit KBit2 // 00000100
1.75 +
1.76 +static const TInt KMPEG4BoxIntroLen = 8;
1.77 +static const TInt KMPEG4Box64BitIntroLen = 16;
1.78 +
1.79 +//
1.80 +// In order to find out the type of MPEG4 file it is
1.81 +// we need to be able to map known extensions, expected
1.82 +// ftyp expressions with MIME-types.
1.83 +//
1.84 +typedef struct
1.85 + {
1.86 + const TText* iExt;
1.87 + TUint32 iBrand;
1.88 + const TText8* iAudioMime;
1.89 + const TText8* iVideoMime;
1.90 + }
1.91 +TMPEG4File;
1.92 +
1.93 +
1.94 +//
1.95 +// A table of ftyp's, extensions and mime-types.
1.96 +//
1.97 +// .mp4 - can contain either audio or video.
1.98 +// .m4a - should contain (unprotected) audio only.
1.99 +// .m4p - should contain protected audio only.
1.100 +// .3gp - can contain either audio or video.
1.101 +//
1.102 +static const TMPEG4File KMPEG4Files[] =
1.103 + {
1.104 + {KExtMP4, KMP4Brand, KMimeMP4_A, KMimeMP4_V},
1.105 + {KExt3GP, K3GPBrand, KMime3GP_A, KMime3GP_V},
1.106 + {KExtM4A, KMP4Brand, KMimeMP4_A, NULL},
1.107 + {KExt3G2, K3G2Brand, KMime3G2_A, KMime3G2_V},
1.108 + {KExt3GP, K3GSBrand, KMime3GP_A, KMime3GP_V},
1.109 + {KExt3GP, K3GRBrand, KMime3GP_A, KMime3GP_V},
1.110 + {KExt3GA, K3GPBrand, KMime3GA, NULL},
1.111 + {KExtMOV, KQTBrand, NULL, KMimeQuickV} // this entry is for .mov files
1.112 + };
1.113 +
1.114 +static const TInt KMPEG4FileTypeCount = sizeof(KMPEG4Files) / sizeof(TMPEG4File);
1.115 +
1.116 +
1.117 +//
1.118 +//
1.119 +//
1.120 +TMPEG4Parser::TMPEG4Parser(CReader& aReader, TFlags& aFlags)
1.121 + : iBrandIndex(KErrNotFound),
1.122 + iIsFinished(EFalse),
1.123 + iReader(aReader),
1.124 + iFlags(aFlags),
1.125 + iVideoAssumed(EFalse)
1.126 + {
1.127 + }
1.128 +
1.129 +
1.130 +//
1.131 +// Compare a brand with the ones this recogniser knows about.
1.132 +//
1.133 +TInt TMPEG4Parser::IsCompatibleBrand(TUint32 aBrand, TInt aStartPos)
1.134 + {
1.135 + for (TInt i = aStartPos; i < KMPEG4FileTypeCount; i++)
1.136 + {
1.137 + TUint32 knownBrand = KMPEG4Files[i].iBrand;
1.138 + if ((aBrand & knownBrand) == knownBrand)
1.139 + {
1.140 + return i;
1.141 + }
1.142 + }
1.143 +
1.144 + return KErrNotFound;
1.145 + }
1.146 +
1.147 +
1.148 +//
1.149 +// Try to determine the mime-type from the extension, and
1.150 +// if that isn't matched, from the FTYP field if present.
1.151 +// If none of these are matched, NULL is returned and the
1.152 +// file isn't a valid MPEG4 file.
1.153 +//
1.154 +const TText8* TMPEG4Parser::MatchFileType(const TDesC& aExt)
1.155 + {
1.156 + TBool videoFound = iFlags.GetBitField(KMPEG4VideoBit);
1.157 + TBool video = (videoFound || iVideoAssumed);
1.158 +
1.159 + // Try to match the extension.
1.160 + if (aExt.Length() > 0)
1.161 + {
1.162 + for (TInt i = 0; i < KMPEG4FileTypeCount; i++)
1.163 + {
1.164 + TPtrC ext(KMPEG4Files[i].iExt);
1.165 + if (aExt.MatchF(ext) != KErrNotFound)
1.166 + {
1.167 + // Extension match. If the extension is for an audio-only format
1.168 + // we must make sure there is no video content in the file. If
1.169 + // video content is present then ignore the extension match.
1.170 + if (KMPEG4Files[i].iVideoMime == NULL)
1.171 + {
1.172 + if (videoFound)
1.173 + {
1.174 + // Try to match another extension.
1.175 + continue;
1.176 + }
1.177 +
1.178 + return KMPEG4Files[i].iAudioMime;
1.179 + }
1.180 +
1.181 + return (video ? KMPEG4Files[i].iVideoMime : KMPEG4Files[i].iAudioMime);
1.182 + }
1.183 + }
1.184 + }
1.185 +
1.186 + // Unknown or no extension. Try to match the brand
1.187 + while (iBrandIndex != KErrNotFound)
1.188 + {
1.189 + if (KMPEG4Files[iBrandIndex].iVideoMime == NULL)
1.190 + {
1.191 + if (videoFound)
1.192 + {
1.193 + // Try to match another brand.
1.194 + TUint32 brand = KMPEG4Files[iBrandIndex].iBrand;
1.195 + iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand, iBrandIndex + 1);
1.196 + continue;
1.197 + }
1.198 +
1.199 + return KMPEG4Files[iBrandIndex].iAudioMime;
1.200 + }
1.201 +
1.202 + return (video ? KMPEG4Files[iBrandIndex].iVideoMime : KMPEG4Files[iBrandIndex].iAudioMime);
1.203 + }
1.204 +
1.205 + // If there is no brand and an unknown extension look at the flags.
1.206 + // (There are some files that have no ftyp).
1.207 + // Only return a potential mime-type if all flag bits have been set.
1.208 + if (iFlags.GetBitField(KMPEG4ConfidenceMask) == KMPEG4ConfidenceMask)
1.209 + {
1.210 + return (video ? KMimeMP4_V : KMimeMP4_A);
1.211 + }
1.212 +
1.213 + return NULL;
1.214 + }
1.215 +
1.216 +
1.217 +//
1.218 +//
1.219 +//
1.220 +void TMPEG4Parser::DoRecognise(const TDesC& aFileExt, CReader& aReader, TMatch& aMatch)
1.221 + {
1.222 + TFlags flags;
1.223 + TMPEG4Parser parser(aReader, flags);
1.224 +
1.225 + TRAP_IGNORE(parser.ParseL());
1.226 +
1.227 + // The extension determines the mime-type.
1.228 + // The flags say if it's a valid MPEG4 file and if video is present.
1.229 + const TText8* extMime = parser.MatchFileType(aFileExt);
1.230 + if (extMime != NULL)
1.231 + {
1.232 + TInt confIndex = flags.GetBitField(KMPEG4ConfidenceMask);
1.233 + aMatch.iConfidence = KMPEG4FlagsToConfidence[confIndex];
1.234 + if (aMatch.iConfidence != KConfNotRecognised)
1.235 + {
1.236 + aMatch.iMime = extMime;
1.237 + }
1.238 + }
1.239 + }
1.240 +
1.241 +
1.242 +//
1.243 +//
1.244 +//
1.245 +void TMPEG4Parser::ParseL()
1.246 + {
1.247 + // If we have only buffer data, we must assume the video
1.248 + // content (if any) has been missed. This is because an
1.249 + // audio-only file recognised as video should play in a
1.250 + // video application, but a video file recognised as audio
1.251 + // will not play in a audio application.
1.252 + if (iReader.Type() == CReader::EBuffer)
1.253 + {
1.254 + iVideoAssumed = ETrue;
1.255 + }
1.256 +
1.257 + do
1.258 + {
1.259 + ReadBoxHeaderL();
1.260 + if (iTitle == KFtyp)
1.261 + {
1.262 + ReadFileTypeL();
1.263 + }
1.264 + else if (iTitle == KMoov)
1.265 + {
1.266 + iFlags.SetBit(KMPEG4MOOVBit);
1.267 + ReadMovieL();
1.268 + }
1.269 + else
1.270 + {
1.271 + SkipCurrentBoxL();
1.272 + }
1.273 + }
1.274 + while (!iIsFinished);
1.275 + }
1.276 +
1.277 +
1.278 +//
1.279 +//
1.280 +//
1.281 +void TMPEG4Parser::SkipCurrentBoxL()
1.282 + {
1.283 + // Intro: [size][title] = 8 bytes.
1.284 +
1.285 + if (iSize == 0)
1.286 + {
1.287 + // The current box extends to the end of file.
1.288 + iIsFinished = ETrue;
1.289 + return;
1.290 + }
1.291 + if(iSizeIn32bit)
1.292 + {
1.293 + iReader.SeekL(iSize - KMPEG4BoxIntroLen);
1.294 + }
1.295 + else
1.296 + {
1.297 + iReader.SeekL(iSize - KMPEG4Box64BitIntroLen);
1.298 + }
1.299 + }
1.300 +
1.301 +
1.302 +//
1.303 +// Parses the 'moov' box.
1.304 +// This box contains sub-boxes we're interested in.
1.305 +//
1.306 +void TMPEG4Parser::ReadMovieL()
1.307 + {
1.308 + // This box holds no information.
1.309 + // It contains 'trak' boxes, which we want.
1.310 +
1.311 + TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
1.312 +
1.313 + while ((dataInBox > 0) && !iIsFinished)
1.314 + {
1.315 + ReadBoxHeaderL();
1.316 + dataInBox -= iSize;
1.317 +
1.318 + if (iTitle == KTrak)
1.319 + {
1.320 + iFlags.SetBit(KMPEG4TRAKBit);
1.321 + ReadTrackL();
1.322 + }
1.323 + else
1.324 + {
1.325 + SkipCurrentBoxL();
1.326 + }
1.327 + }
1.328 + }
1.329 +
1.330 +
1.331 +//
1.332 +// Parses the 'trak' box.
1.333 +// This box contains sub-boxes we're interested in.
1.334 +//
1.335 +void TMPEG4Parser::ReadTrackL()
1.336 + {
1.337 + // This box holds no information.
1.338 + // It contains 'tkhd' boxes, which we want.
1.339 +
1.340 + TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
1.341 +
1.342 + while ((dataInBox > 0) && !iIsFinished)
1.343 + {
1.344 + ReadBoxHeaderL();
1.345 + dataInBox -= iSize;
1.346 +
1.347 + if (iTitle == KTkhd)
1.348 + {
1.349 + ReadTrackHeaderL();
1.350 + }
1.351 + else if (iTitle == KMdia)
1.352 + {
1.353 + ReadMediaL();
1.354 + }
1.355 + else
1.356 + {
1.357 + SkipCurrentBoxL();
1.358 + }
1.359 + }
1.360 + }
1.361 +
1.362 +//
1.363 +// Parses a 'mdia' box.
1.364 +// This box contains sub-boxes we're interested in.
1.365 +//
1.366 +void TMPEG4Parser::ReadMediaL()
1.367 + {
1.368 + TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
1.369 +
1.370 + while ((dataInBox > 0) && !iIsFinished)
1.371 + {
1.372 + ReadBoxHeaderL();
1.373 + dataInBox -= iSize;
1.374 +
1.375 + if (iTitle == KHdlr)
1.376 + {
1.377 + ReadHandlerL();
1.378 + }
1.379 + else
1.380 + {
1.381 + SkipCurrentBoxL();
1.382 + }
1.383 + }
1.384 + }
1.385 +
1.386 +
1.387 +//
1.388 +// Parses a 'hdlr' box.
1.389 +// This is a stand-alone box.
1.390 +//
1.391 +void TMPEG4Parser::ReadHandlerL()
1.392 + {
1.393 + // Intro: [size][title][versionFlags][predefined][handler_type] = 20 bytes.
1.394 + const TInt KMPEG4HandlerIntroLen = 20;
1.395 + TUint32 versionFlags;
1.396 + TUint32 predefined;
1.397 + TUint32 handler;
1.398 +
1.399 + iReader.Read32L(versionFlags);
1.400 + iReader.Read32L(predefined);
1.401 + if (predefined != 0)
1.402 + {
1.403 + User::Leave(KErrCorrupt);
1.404 + }
1.405 +
1.406 + iReader.Read32L(handler);
1.407 + if (handler == KVide)
1.408 + {
1.409 + iFlags.SetBit(KMPEG4VideoBit);
1.410 + }
1.411 +
1.412 + iReader.SeekL(iSize - KMPEG4HandlerIntroLen);
1.413 + }
1.414 +
1.415 +
1.416 +//
1.417 +//
1.418 +//
1.419 +void TMPEG4Parser::ReadTrackHeaderL()
1.420 + {
1.421 + const TUint8 KMPEG4TrackVersion0 = 0;
1.422 + const TUint8 KMPEG4TrackVersion1 = 1;
1.423 + const TInt KMPEG4Version0ToVolume = 32; // Distance to volume field from version=0 field.
1.424 + const TInt KMPEG4Version1ToVolume = 44; // Distance to volume field from version=1 field.
1.425 + const TInt KMPEG4VolumeToWidth = 38; // Distance to width field from volume field.
1.426 +
1.427 + TUint32 versionFlags;
1.428 + TUint16 volume;
1.429 + TUint32 width;
1.430 + TUint32 height;
1.431 +
1.432 + // This box contains information about a single track.
1.433 + iReader.Read32L(versionFlags);
1.434 +
1.435 + // The highest 8 bits contains the version.
1.436 + switch (HIGH_BYTE32(versionFlags))
1.437 + {
1.438 + case KMPEG4TrackVersion0:
1.439 + iReader.SeekL(KMPEG4Version0ToVolume);
1.440 + break;
1.441 +
1.442 + case KMPEG4TrackVersion1:
1.443 + iReader.SeekL(KMPEG4Version1ToVolume);
1.444 + break;
1.445 +
1.446 + default:
1.447 + User::Leave(KErrCorrupt);
1.448 + }
1.449 +
1.450 + // Volume can not be used to distinguish between audio and video anymore.
1.451 + iReader.Read16L(volume);
1.452 +
1.453 + // We want to seek ahead to read the 'width' and 'height' fields.
1.454 + iReader.SeekL(KMPEG4VolumeToWidth);
1.455 +
1.456 + iReader.Read32L(width); // 16.16 fixed-point
1.457 + iReader.Read32L(height); // 16.16 fixed-point
1.458 + if ((width != 0) && (height != 0))
1.459 + {
1.460 + iFlags.SetBit(KMPEG4VideoBit);
1.461 + }
1.462 + }
1.463 +
1.464 +
1.465 +//
1.466 +// Parses the 'ftyp' box.
1.467 +// Records the first recognised brand that helps to
1.468 +// identify the mime-type.
1.469 +//
1.470 +void TMPEG4Parser::ReadFileTypeL()
1.471 + {
1.472 + // Intro = [size][title][majorBrand] = 12 bytes.
1.473 + const TInt KMPEG4FtypIntroLen = 12;
1.474 + TUint32 brand;
1.475 +
1.476 + // If the majorBrand isn't recognised we should also
1.477 + // search the compatible brand list.
1.478 + TInt64 bytesRemaining = iSize - KMPEG4FtypIntroLen;
1.479 + //here there should be bytes remaining. Otherwise we cant read.
1.480 + if( bytesRemaining <0 )
1.481 + {
1.482 + User::Leave(KErrCorrupt);
1.483 + }
1.484 +
1.485 + iReader.Read32L(brand);
1.486 + iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
1.487 + if (iBrandIndex != KErrNotFound)
1.488 + {
1.489 + // The major brand was recognised.
1.490 + // Skip to the end of the ftyp box.
1.491 + iReader.SeekL(bytesRemaining);
1.492 + return;
1.493 + }
1.494 +
1.495 + // The major brand wasn't recognised.
1.496 + // Skip over the version info (32 bit) to the start of the
1.497 + // compatible brands list.
1.498 + TInt skip = sizeof(TUint32);
1.499 + iReader.SeekL(skip);
1.500 + bytesRemaining -= skip;
1.501 +
1.502 + while ((iBrandIndex == KErrNotFound) && (bytesRemaining > 0))
1.503 + {
1.504 + iReader.Read32L(brand);
1.505 + bytesRemaining -= skip;
1.506 + iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
1.507 + }
1.508 +
1.509 + iReader.SeekL(bytesRemaining);
1.510 + }
1.511 +
1.512 +
1.513 +//
1.514 +// Reads the first few bytes of a box.
1.515 +// The exact amount read depends on the box.
1.516 +//
1.517 +void TMPEG4Parser::ReadBoxHeaderL()
1.518 + {
1.519 + const TInt KMPEG4ExtendedTypeLen = 16;
1.520 +
1.521 + TUint32 word1;
1.522 +
1.523 + iReader.Read32L(word1);
1.524 + iReader.Read32L(iTitle);
1.525 +
1.526 + switch (word1)
1.527 + {
1.528 + case 0:
1.529 + // Box extends to the end of file.
1.530 + iSize = MAKE_TINT64(0, 0);
1.531 + break;
1.532 +
1.533 + case 1:
1.534 + // Size is specified in a 64-bit field.
1.535 + iSizeIn32bit = EFalse;
1.536 + iReader.Read64L(iSize);
1.537 + break;
1.538 +
1.539 + default:
1.540 + // It's an actual 32-bit size.
1.541 + iSizeIn32bit = ETrue;
1.542 + iSize = MAKE_TINT64(0, word1);
1.543 + }
1.544 +
1.545 + if (iTitle == KUuid)
1.546 + {
1.547 + // Skip the extended type.
1.548 + iReader.SeekL(KMPEG4ExtendedTypeLen);
1.549 + }
1.550 + }
1.551 +
1.552 +