sl@0: // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: #include "constants.h" sl@0: #include "parsers.h" sl@0: sl@0: // sl@0: // Matroska 'XML' tags. sl@0: // sl@0: static const TUint32 KEBML = 0x1A45DFA3; sl@0: static const TUint32 KSegment = 0x18538067; sl@0: static const TUint32 KTracks = 0x1654AE6B; sl@0: static const TUint32 KTrackEntry = 0xAE; sl@0: static const TUint32 KTrackType = 0x83; sl@0: static const TUint32 KVoid = 0xEC; sl@0: static const TUint32 KSeekHead = 0x114D9B74; sl@0: static const TUint32 KSegmentInfo = 0x1549A966; sl@0: static const TUint32 KCluster = 0x1F43B675; sl@0: static const TUint32 KCues = 0x1C53BB6B; sl@0: static const TUint32 KAttachments = 0x1941A469; sl@0: static const TUint32 KChapters = 0x1043A770; sl@0: static const TUint32 KTags = 0x1254C367; sl@0: sl@0: // sl@0: // Matroska Track Types. sl@0: // sl@0: static const TUint8 KTrackTypeVideo = 0x01; sl@0: sl@0: #define KMatroskaConfidenceMask 0x07 // 00000111 sl@0: #define KMatroskaEBMLBit KBit1 sl@0: #define KMatroskaSegmentBit KBit2 sl@0: #define KMatroskaVideoBit KBit3 sl@0: sl@0: sl@0: // sl@0: // This truth table maps the following flags to confidence levels. sl@0: // sl@0: // A: Extension match. sl@0: // B: EBML tag found. sl@0: // C: Segment tag found. sl@0: // sl@0: // C B A -> Confidence sl@0: // -------------------- sl@0: // 0 0 0 -> NotRecognised sl@0: // 0 0 1 -> EPossible sl@0: // 0 1 0 -> EPossible sl@0: // 0 1 1 -> EProbable sl@0: // 1 0 0 -> ENotRecognised (EBML should be present) sl@0: // 1 0 1 -> ENotRecognised (EBML should be present) sl@0: // 1 1 0 -> EProbable sl@0: // 1 1 1 -> ECertain sl@0: // sl@0: static const TInt KMatroskaFlagsToConfidence[8] = sl@0: { sl@0: KConfNotRecognised, sl@0: KConfPossible, sl@0: KConfPossible, sl@0: KConfProbable, sl@0: KConfNotRecognised, sl@0: KConfNotRecognised, sl@0: KConfProbable, sl@0: KConfCertain sl@0: }; sl@0: sl@0: static const TInt KMatExtensionOnlyIndex = 1; // See truth table. sl@0: sl@0: sl@0: typedef struct sl@0: { sl@0: const TText* iExt; sl@0: const TText8* iMime; sl@0: } sl@0: TMatroskaExt; sl@0: sl@0: // sl@0: // Known Matroska extensions and their corresponding MIME-types. sl@0: // sl@0: static const TMatroskaExt KMatroskaExt[] = sl@0: { sl@0: {KExtMAT_A, KMimeMAT_A }, sl@0: {KExtMAT_V, KMimeMAT_V } sl@0: }; sl@0: sl@0: static const TInt KMatroskaExtCount = sizeof(KMatroskaExt) / sizeof(TMatroskaExt); sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: TMatroskaParser::TMatroskaParser(CReader& aReader, TFlags& aFlags) sl@0: : iReader(aReader), sl@0: iFlags(aFlags) sl@0: { sl@0: } sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: const TText8* TMatroskaParser::MatchExtension(const TDesC& aExt) sl@0: { sl@0: for (TInt i = 0; i < KMatroskaExtCount; i++) sl@0: { sl@0: if (aExt.MatchF(TPtrC(KMatroskaExt[i].iExt)) != KErrNotFound) sl@0: { sl@0: return KMatroskaExt[i].iMime; sl@0: } sl@0: } sl@0: sl@0: return NULL; sl@0: } sl@0: sl@0: sl@0: // sl@0: // This function calls the parser and turns the results sl@0: // into a MIME-type. sl@0: // sl@0: void TMatroskaParser::DoRecognise(const TDesC& aExt, CReader& aReader, TMatch& aMatch) sl@0: { sl@0: TFlags flags; sl@0: TMatroskaParser parser(aReader, flags); sl@0: sl@0: // Try to match the extension. sl@0: const TText8* extMime = parser.MatchExtension(aExt); sl@0: if (extMime != NULL) sl@0: { sl@0: flags.SetExtensionFlag(); sl@0: } sl@0: sl@0: // Try to parse the content. sl@0: TRAP_IGNORE(parser.ParseL()); sl@0: TInt confIndex = flags.GetBitField(KMatroskaConfidenceMask); sl@0: aMatch.iConfidence = KMatroskaFlagsToConfidence[confIndex]; sl@0: if (aMatch.iConfidence != KConfNotRecognised) sl@0: { sl@0: // If any header data has been recognised trust that, sl@0: // otherwise go with the extension. sl@0: if (confIndex == KMatExtensionOnlyIndex) sl@0: { sl@0: aMatch.iMime = extMime; sl@0: } sl@0: else sl@0: { sl@0: aMatch.iMime = (flags.GetBitField(KMatroskaVideoBit) ? KMimeMAT_V : KMimeMAT_A); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // This function does the parsing. sl@0: // sl@0: void TMatroskaParser::ParseL() sl@0: { sl@0: TUint64 id; sl@0: TInt64 size; sl@0: TBool haveElement = EFalse; sl@0: sl@0: // Assume there's video content if we only have buffer data. sl@0: if (iReader.Type() == CReader::EBuffer) sl@0: { sl@0: iFlags.SetBit(KMatroskaVideoBit); sl@0: } sl@0: sl@0: FOREVER sl@0: { sl@0: if (!haveElement) sl@0: { sl@0: ReadElementL(id, size); sl@0: } sl@0: sl@0: switch (id) sl@0: { sl@0: case KEBML: sl@0: iFlags.SetBit(KMatroskaEBMLBit); sl@0: break; sl@0: sl@0: case KSegment: sl@0: haveElement = ReadSegmentL(id, size); sl@0: break; sl@0: sl@0: default: sl@0: // Skip it. sl@0: iReader.SeekL(size); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // This function returns ETrue if the aNextID and aNextSize parameters sl@0: // contain valid values, EFalse otherwise. sl@0: // sl@0: TBool TMatroskaParser::ReadSegmentL(TUint64& aNextID, TInt64& aNextSize) sl@0: { sl@0: TUint64 id; sl@0: TInt64 size; sl@0: TBool videoContent = EFalse; sl@0: sl@0: aNextID = 0; sl@0: aNextSize = 0; sl@0: sl@0: iFlags.SetBit(KMatroskaSegmentBit); sl@0: sl@0: while (!videoContent) sl@0: { sl@0: ReadElementL(id, size); sl@0: sl@0: switch (id) sl@0: { sl@0: case KTracks: sl@0: videoContent = ReadTrackL(size); sl@0: break; sl@0: sl@0: case KSeekHead: sl@0: case KSegmentInfo: sl@0: case KCluster: sl@0: case KCues: sl@0: case KAttachments: sl@0: case KChapters: sl@0: case KTags: sl@0: iReader.SeekL(size); sl@0: break; sl@0: sl@0: default: sl@0: // Unrecognised element id. Pass it back to the caller sl@0: aNextID = id; sl@0: aNextSize = size; sl@0: return ETrue; sl@0: } sl@0: } sl@0: sl@0: // Tell the caller they must read the next element themselves. sl@0: return EFalse; sl@0: } sl@0: sl@0: sl@0: // sl@0: // The Track. This lets us know if there's video content present. sl@0: // sl@0: TBool TMatroskaParser::ReadTrackL(TInt64 aTrackSize) sl@0: { sl@0: TUint64 id; sl@0: TInt64 size; sl@0: TInt startPos = iReader.Position(); sl@0: sl@0: while (iReader.Position() - startPos < aTrackSize) sl@0: { sl@0: ReadElementL(id, size); sl@0: sl@0: switch (id) sl@0: { sl@0: case KTrackEntry: sl@0: break; sl@0: sl@0: case KTrackType: sl@0: TUint8 trackType; sl@0: iReader.ReadByteL(trackType); sl@0: if (trackType == KTrackTypeVideo) sl@0: { sl@0: // We found video content so we can stop parsing. sl@0: iFlags.SetBit(KMatroskaVideoBit); sl@0: return ETrue; sl@0: } sl@0: break; sl@0: sl@0: default: sl@0: iReader.SeekL(size); sl@0: } sl@0: } sl@0: sl@0: return EFalse; sl@0: } sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: void TMatroskaParser::ReadElementL(TUint64& aElementID, TInt64& aSize) sl@0: { sl@0: do sl@0: { sl@0: aElementID = ReadDataL(); sl@0: aSize = ReadDataL(ETrue); sl@0: sl@0: // Void elements are used for padding and sl@0: // can be ignored. sl@0: if (aElementID == KVoid) sl@0: { sl@0: iReader.SeekL(aSize); sl@0: } sl@0: } sl@0: while (aElementID == KVoid); sl@0: } sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: TUint64 TMatroskaParser::ReadDataL(TBool aTurnOffHighestSetBit) sl@0: { sl@0: TUint64 retval; sl@0: TUint8 byte; sl@0: TUint8 mask = 0x80; // [1000 0000]. It will be shifted right 1 in each 'i' iteration. sl@0: TUint8 size = 1; // It will be incremented in each 'i' iteration. sl@0: sl@0: iReader.ReadByteL(byte); sl@0: sl@0: for (TInt i = 0; i < sizeof(TUint64); i++) sl@0: { sl@0: if (byte & mask) sl@0: { sl@0: retval = byte; sl@0: if (aTurnOffHighestSetBit) sl@0: { sl@0: retval &= ~mask; // Turn off the highest set bit. sl@0: } sl@0: sl@0: // Now read the real data. sl@0: // Start from 1 because we've already read a byte. sl@0: for (TInt j = 1; j < size; j++) sl@0: { sl@0: iReader.ReadByteL(byte); sl@0: retval <<= 8; sl@0: retval |= byte; sl@0: } sl@0: sl@0: return retval; sl@0: } sl@0: else sl@0: { sl@0: mask >>= 1; sl@0: size++; sl@0: } sl@0: } sl@0: sl@0: User::Leave(KErrCorrupt); sl@0: return 0; // Keep the compiler happy. sl@0: } sl@0: