--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmlibs/mmfw/Recogniser/src/mpeg4parser.cpp Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,534 @@
+// Copyright (c) 2006-2009 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:
+//
+
+#include "parsers.h"
+#include "constants.h"
+
+//
+// Brands.
+// Some (most) brands have Release information - e.g. 3gp6, 3gp5 etc.
+// Therefore, to match the brand, we only look at the characters that don't
+// represent Release information (in the above case, the 4th character).
+// The Release character is set to zero.
+//
+static const TUint32 KMP4Brand = MAKE_INT32('m', 'p', '4', 0);
+static const TUint32 K3GPBrand = MAKE_INT32('3', 'g', 'p', 0);
+static const TUint32 K3G2Brand = MAKE_INT32('3', 'g', '2', 0);
+static const TUint32 K3GSBrand = MAKE_INT32('3', 'g', 's', 0); // Streaming servers.
+static const TUint32 K3GRBrand = MAKE_INT32('3', 'g', 'r', 0); // Progresive download and MMS.
+
+//
+// Box identifiers.
+//
+static const TUint32 KFtyp = MAKE_INT32('f', 't', 'y', 'p');
+static const TUint32 KMoov = MAKE_INT32('m', 'o', 'o', 'v');
+static const TUint32 KTrak = MAKE_INT32('t', 'r', 'a', 'k');
+static const TUint32 KTkhd = MAKE_INT32('t', 'k', 'h', 'd');
+static const TUint32 KMdia = MAKE_INT32('m', 'd', 'i', 'a');
+static const TUint32 KHdlr = MAKE_INT32('h', 'd', 'l', 'r');
+static const TUint32 KVide = MAKE_INT32('v', 'i', 'd', 'e');
+static const TUint32 KUuid = MAKE_INT32('u', 'u', 'i', 'd');
+
+
+//
+// This truth table maps the following flags to a confidence level.
+//
+// A - trak box present
+// B - moov box present
+//
+// A B -> Confidence
+// =================
+// 0 0 -> EPossible
+// 0 1 -> EProbable
+// 1 0 -> EProbable
+// 1 1 -> ECertain
+//
+static const TInt KMPEG4FlagsToConfidence[] =
+ {
+ KConfPossible,
+ KConfProbable,
+ KConfProbable,
+ KConfCertain,
+ };
+
+
+#define KMPEG4ConfidenceMask 0x03 // 00000011
+#define KMPEG4BoxTitleLen 4
+#define KMPEG4TRAKBit KBit0 // 00000001
+#define KMPEG4MOOVBit KBit1 // 00000010
+#define KMPEG4VideoBit KBit2 // 00000100
+
+static const TInt KMPEG4BoxIntroLen = 8;
+
+//
+// In order to find out the type of MPEG4 file it is
+// we need to be able to map known extensions, expected
+// ftyp expressions with MIME-types.
+//
+typedef struct
+ {
+ const TText* iExt;
+ TUint32 iBrand;
+ const TText8* iAudioMime;
+ const TText8* iVideoMime;
+ }
+TMPEG4File;
+
+
+//
+// A table of ftyp's, extensions and mime-types.
+//
+// .mp4 - can contain either audio or video.
+// .m4a - should contain (unprotected) audio only.
+// .m4p - should contain protected audio only.
+// .3gp - can contain either audio or video.
+//
+static const TMPEG4File KMPEG4Files[] =
+ {
+ {KExtMP4, KMP4Brand, KMimeMP4_A, KMimeMP4_V},
+ {KExt3GP, K3GPBrand, KMime3GP_A, KMime3GP_V},
+ {KExtM4A, KMP4Brand, KMimeMP4_A, NULL},
+ {KExt3G2, K3G2Brand, KMime3G2_A, KMime3G2_V},
+ {KExt3GP, K3GSBrand, KMime3GP_A, KMime3GP_V},
+ {KExt3GP, K3GRBrand, KMime3GP_A, KMime3GP_V},
+ {KExt3GA, K3GPBrand, KMime3GA, NULL}
+ };
+
+static const TInt KMPEG4FileTypeCount = sizeof(KMPEG4Files) / sizeof(TMPEG4File);
+
+
+//
+//
+//
+TMPEG4Parser::TMPEG4Parser(CReader& aReader, TFlags& aFlags)
+ : iBrandIndex(KErrNotFound),
+ iIsFinished(EFalse),
+ iReader(aReader),
+ iFlags(aFlags),
+ iVideoAssumed(EFalse)
+ {
+ }
+
+
+//
+// Compare a brand with the ones this recogniser knows about.
+//
+TInt TMPEG4Parser::IsCompatibleBrand(TUint32 aBrand, TInt aStartPos)
+ {
+ for (TInt i = aStartPos; i < KMPEG4FileTypeCount; i++)
+ {
+ TUint32 knownBrand = KMPEG4Files[i].iBrand;
+ if ((aBrand & knownBrand) == knownBrand)
+ {
+ return i;
+ }
+ }
+
+ return KErrNotFound;
+ }
+
+
+//
+// Try to determine the mime-type from the extension, and
+// if that isn't matched, from the FTYP field if present.
+// If none of these are matched, NULL is returned and the
+// file isn't a valid MPEG4 file.
+//
+const TText8* TMPEG4Parser::MatchFileType(const TDesC& aExt)
+ {
+ TBool videoFound = iFlags.GetBitField(KMPEG4VideoBit);
+ TBool video = (videoFound || iVideoAssumed);
+
+ // Try to match the extension.
+ if (aExt.Length() > 0)
+ {
+ for (TInt i = 0; i < KMPEG4FileTypeCount; i++)
+ {
+ TPtrC ext(KMPEG4Files[i].iExt);
+ if (aExt.MatchF(ext) != KErrNotFound)
+ {
+ // Extension match. If the extension is for an audio-only format
+ // we must make sure there is no video content in the file. If
+ // video content is present then ignore the extension match.
+ if (KMPEG4Files[i].iVideoMime == NULL)
+ {
+ if (videoFound)
+ {
+ // Try to match another extension.
+ continue;
+ }
+
+ return KMPEG4Files[i].iAudioMime;
+ }
+
+ return (video ? KMPEG4Files[i].iVideoMime : KMPEG4Files[i].iAudioMime);
+ }
+ }
+ }
+
+ // Unknown or no extension. Try to match the brand
+ while (iBrandIndex != KErrNotFound)
+ {
+ if (KMPEG4Files[iBrandIndex].iVideoMime == NULL)
+ {
+ if (videoFound)
+ {
+ // Try to match another brand.
+ TUint32 brand = KMPEG4Files[iBrandIndex].iBrand;
+ iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand, iBrandIndex + 1);
+ continue;
+ }
+
+ return KMPEG4Files[iBrandIndex].iAudioMime;
+ }
+
+ return (video ? KMPEG4Files[iBrandIndex].iVideoMime : KMPEG4Files[iBrandIndex].iAudioMime);
+ }
+
+ // If there is no brand and an unknown extension look at the flags.
+ // (There are some files that have no ftyp).
+ // Only return a potential mime-type if all flag bits have been set.
+ if (iFlags.GetBitField(KMPEG4ConfidenceMask) == KMPEG4ConfidenceMask)
+ {
+ return (video ? KMimeMP4_V : KMimeMP4_A);
+ }
+
+ return NULL;
+ }
+
+
+//
+//
+//
+void TMPEG4Parser::DoRecognise(const TDesC& aFileExt, CReader& aReader, TMatch& aMatch)
+ {
+ TFlags flags;
+ TMPEG4Parser parser(aReader, flags);
+
+ TRAP_IGNORE(parser.ParseL());
+
+ // The extension determines the mime-type.
+ // The flags say if it's a valid MPEG4 file and if video is present.
+ const TText8* extMime = parser.MatchFileType(aFileExt);
+ if (extMime != NULL)
+ {
+ TInt confIndex = flags.GetBitField(KMPEG4ConfidenceMask);
+ aMatch.iConfidence = KMPEG4FlagsToConfidence[confIndex];
+ if (aMatch.iConfidence != KConfNotRecognised)
+ {
+ aMatch.iMime = extMime;
+ }
+ }
+ }
+
+
+//
+//
+//
+void TMPEG4Parser::ParseL()
+ {
+ // If we have only buffer data, we must assume the video
+ // content (if any) has been missed. This is because an
+ // audio-only file recognised as video should play in a
+ // video application, but a video file recognised as audio
+ // will not play in a audio application.
+ if (iReader.Type() == CReader::EBuffer)
+ {
+ iVideoAssumed = ETrue;
+ }
+
+ do
+ {
+ ReadBoxHeaderL();
+ if (iTitle == KFtyp)
+ {
+ ReadFileTypeL();
+ }
+ else if (iTitle == KMoov)
+ {
+ iFlags.SetBit(KMPEG4MOOVBit);
+ ReadMovieL();
+ }
+ else
+ {
+ SkipCurrentBoxL();
+ }
+ }
+ while (!iIsFinished);
+ }
+
+
+//
+//
+//
+void TMPEG4Parser::SkipCurrentBoxL()
+ {
+ // Intro: [size][title] = 8 bytes.
+
+ if (iSize == 0)
+ {
+ // The current box extends to the end of file.
+ iIsFinished = ETrue;
+ return;
+ }
+
+ iReader.SeekL(iSize - KMPEG4BoxIntroLen);
+ }
+
+
+//
+// Parses the 'moov' box.
+// This box contains sub-boxes we're interested in.
+//
+void TMPEG4Parser::ReadMovieL()
+ {
+ // This box holds no information.
+ // It contains 'trak' boxes, which we want.
+
+ TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
+
+ while ((dataInBox > 0) && !iIsFinished)
+ {
+ ReadBoxHeaderL();
+ dataInBox -= iSize;
+
+ if (iTitle == KTrak)
+ {
+ iFlags.SetBit(KMPEG4TRAKBit);
+ ReadTrackL();
+ }
+ else
+ {
+ SkipCurrentBoxL();
+ }
+ }
+ }
+
+
+//
+// Parses the 'trak' box.
+// This box contains sub-boxes we're interested in.
+//
+void TMPEG4Parser::ReadTrackL()
+ {
+ // This box holds no information.
+ // It contains 'tkhd' boxes, which we want.
+
+ TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
+
+ while ((dataInBox > 0) && !iIsFinished)
+ {
+ ReadBoxHeaderL();
+ dataInBox -= iSize;
+
+ if (iTitle == KTkhd)
+ {
+ ReadTrackHeaderL();
+ }
+ else if (iTitle == KMdia)
+ {
+ ReadMediaL();
+ }
+ else
+ {
+ SkipCurrentBoxL();
+ }
+ }
+ }
+
+//
+// Parses a 'mdia' box.
+// This box contains sub-boxes we're interested in.
+//
+void TMPEG4Parser::ReadMediaL()
+ {
+ TInt64 dataInBox = iSize - KMPEG4BoxIntroLen;
+
+ while ((dataInBox > 0) && !iIsFinished)
+ {
+ ReadBoxHeaderL();
+ dataInBox -= iSize;
+
+ if (iTitle == KHdlr)
+ {
+ ReadHandlerL();
+ }
+ else
+ {
+ SkipCurrentBoxL();
+ }
+ }
+ }
+
+
+//
+// Parses a 'hdlr' box.
+// This is a stand-alone box.
+//
+void TMPEG4Parser::ReadHandlerL()
+ {
+ // Intro: [size][title][versionFlags][predefined][handler_type] = 20 bytes.
+ const TInt KMPEG4HandlerIntroLen = 20;
+ TUint32 versionFlags;
+ TUint32 predefined;
+ TUint32 handler;
+
+ iReader.Read32L(versionFlags);
+ iReader.Read32L(predefined);
+ if (predefined != 0)
+ {
+ User::Leave(KErrCorrupt);
+ }
+
+ iReader.Read32L(handler);
+ if (handler == KVide)
+ {
+ iFlags.SetBit(KMPEG4VideoBit);
+ }
+
+ iReader.SeekL(iSize - KMPEG4HandlerIntroLen);
+ }
+
+
+//
+//
+//
+void TMPEG4Parser::ReadTrackHeaderL()
+ {
+ const TUint8 KMPEG4TrackVersion0 = 0;
+ const TUint8 KMPEG4TrackVersion1 = 1;
+ const TInt KMPEG4Version0ToVolume = 32; // Distance to volume field from version=0 field.
+ const TInt KMPEG4Version1ToVolume = 44; // Distance to volume field from version=1 field.
+ const TInt KMPEG4VolumeToWidth = 38; // Distance to width field from volume field.
+
+ TUint32 versionFlags;
+ TUint16 volume;
+ TUint32 width;
+ TUint32 height;
+
+ // This box contains information about a single track.
+ iReader.Read32L(versionFlags);
+
+ // The highest 8 bits contains the version.
+ switch (HIGH_BYTE32(versionFlags))
+ {
+ case KMPEG4TrackVersion0:
+ iReader.SeekL(KMPEG4Version0ToVolume);
+ break;
+
+ case KMPEG4TrackVersion1:
+ iReader.SeekL(KMPEG4Version1ToVolume);
+ break;
+
+ default:
+ User::Leave(KErrCorrupt);
+ }
+
+ // Volume can not be used to distinguish between audio and video anymore.
+ iReader.Read16L(volume);
+
+ // We want to seek ahead to read the 'width' and 'height' fields.
+ iReader.SeekL(KMPEG4VolumeToWidth);
+
+ iReader.Read32L(width); // 16.16 fixed-point
+ iReader.Read32L(height); // 16.16 fixed-point
+ if ((width != 0) && (height != 0))
+ {
+ iFlags.SetBit(KMPEG4VideoBit);
+ }
+ }
+
+
+//
+// Parses the 'ftyp' box.
+// Records the first recognised brand that helps to
+// identify the mime-type.
+//
+void TMPEG4Parser::ReadFileTypeL()
+ {
+ // Intro = [size][title][majorBrand] = 12 bytes.
+ const TInt KMPEG4FtypIntroLen = 12;
+ TUint32 brand;
+
+ // If the majorBrand isn't recognised we should also
+ // search the compatible brand list.
+ TInt64 bytesRemaining = iSize - KMPEG4FtypIntroLen;
+
+ iReader.Read32L(brand);
+ iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
+ if (iBrandIndex != KErrNotFound)
+ {
+ // The major brand was recognised.
+ // Skip to the end of the ftyp box.
+ iReader.SeekL(bytesRemaining);
+ return;
+ }
+
+ // The major brand wasn't recognised.
+ // Skip over the version info (32 bit) to the start of the
+ // compatible brands list.
+ TInt skip = sizeof(TUint32);
+ iReader.SeekL(skip);
+ bytesRemaining -= skip;
+
+ while ((iBrandIndex == KErrNotFound) && (bytesRemaining > 0))
+ {
+ iReader.Read32L(brand);
+ bytesRemaining -= skip;
+ iBrandIndex = TMPEG4Parser::IsCompatibleBrand(brand);
+ }
+
+ iReader.SeekL(bytesRemaining);
+ }
+
+
+//
+// Reads the first few bytes of a box.
+// The exact amount read depends on the box.
+//
+void TMPEG4Parser::ReadBoxHeaderL()
+ {
+ const TInt KMPEG4ExtendedTypeLen = 16;
+
+ TUint32 word1;
+
+ iReader.Read32L(word1);
+ iReader.Read32L(iTitle);
+
+ switch (word1)
+ {
+ case 0:
+ // Box extends to the end of file.
+ iSize = MAKE_TINT64(0, 0);
+ break;
+
+ case 1:
+ // Size is specified in a 64-bit field.
+ iReader.Read64L(iSize);
+ break;
+
+ default:
+ // It's an actual 32-bit size.
+ iSize = MAKE_TINT64(0, word1);
+ }
+
+ if (iTitle == KUuid)
+ {
+ // Skip the extended type.
+ iReader.SeekL(KMPEG4ExtendedTypeLen);
+ }
+ }
+
+