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 "parsers.h" sl@0: sl@0: static const TUint8 KISOEndCode = 0xB9; sl@0: static const TUint8 KPackStartCode = 0xBA; sl@0: static const TUint8 KVideoElementaryStream = 0xE0; sl@0: sl@0: #define KMPEG1PackHeaderLen 8 sl@0: #define KMPEG2PackHeaderLen 10 sl@0: sl@0: #define KMPEG1PackHeaderID 0x21 // 0010xxx1. See Doc Link 2. sl@0: #define KMPEG2PackHeaderID 0x44 // 01xxx1xx. See Doc Link 2. sl@0: sl@0: #define KStartCodeMask 0xffffff00 sl@0: #define KStartCodeIntro 0x00000100 sl@0: sl@0: #define KMPEG2StartCode1Bit KBit1 sl@0: #define KMPEG2StartCode2Bit KBit2 sl@0: #define KMPEG2VideoBit KBit3 sl@0: #define KMPEG2MPEG1Bit KBit4 sl@0: sl@0: // sl@0: // Mapping flags to a confidence level. sl@0: // sl@0: // A: extension match sl@0: // B: start-code1 sl@0: // C: start-code2 sl@0: // sl@0: // C B A -> Confidence sl@0: // ------------------- sl@0: // 0 0 0 -> ENotRecognised sl@0: // 0 0 1 -> EPossible sl@0: // 0 1 0 -> EPossible sl@0: // 0 1 1 -> EProbable sl@0: // 1 0 0 -> ENotRecognised sl@0: // 1 0 1 -> ENotRecognised sl@0: // 1 1 0 -> EProbable sl@0: // 1 1 1 -> ECertain sl@0: // sl@0: static const TInt KMPEG2FlagsToConfidence[] = 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: #define KMPEG2ConfidenceMask 0x07 // 00000111 sl@0: #define KMPEG2MimeMask 0x18 // 00011000 sl@0: #define KMPEG2MimeShift 0x03 sl@0: sl@0: static const TInt KMPEG2ExtOnlyIndex = 1; sl@0: sl@0: // sl@0: // The 'ED' bits of the flags member are used as an index into sl@0: // this table of possible MIME-types. sl@0: // sl@0: // E: MPEG1 instead of MPEG2 sl@0: // D: Video content present sl@0: // sl@0: // E D -> Mime sl@0: // ----------- sl@0: // 0 0 -> audio/mpeg2 sl@0: // 0 1 -> video/mpeg2 sl@0: // 1 0 -> audio/mpeg1 sl@0: // 1 1 -> video/mpeg1 sl@0: // sl@0: static const TText8* const KMPEG2Mimes[] = sl@0: { sl@0: KMimeMPEG2_A, sl@0: KMimeMPEG2_V, sl@0: KMimeMPEG1_A, sl@0: KMimeMPEG1_V sl@0: }; sl@0: sl@0: // sl@0: // A list of known MPEG2 file extensions. sl@0: // MPEG1 file extensions are also listed. sl@0: // sl@0: typedef struct sl@0: { sl@0: const TText* iExt; sl@0: const TText8* iMime; sl@0: } sl@0: TMPEG2Types; sl@0: sl@0: static const TMPEG2Types KMPEG2Types[] = sl@0: { sl@0: { KExtMPEG_1, KMimeMPEG2_V }, sl@0: { KExtMPEG_2, KMimeMPEG2_V }, sl@0: { KExtMPEG_3, KMimeMPEG2_V }, sl@0: { KExtMPEG_4, KMimeMPEG2_V }, sl@0: { KExtMPEG_5, KMimeMPEG2_V }, sl@0: { KExtMPEG_6, KMimeMPEG2_V }, sl@0: { KExtMPEG_7, KMimeMPEG1_V }, sl@0: { KExtMPEG_8, KMimeMPEG2_A } sl@0: }; sl@0: sl@0: #define KMPEG2ExtCount sizeof(KMPEG2Types) / sizeof(TMPEG2Types) sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: TMPEG2Parser::TMPEG2Parser(CReader& aReader, TFlags& aFlags) sl@0: : iReader(aReader), sl@0: iFlags(aFlags) sl@0: { sl@0: } sl@0: sl@0: sl@0: // sl@0: // Match the file's extension to known MPEG2 file extensions. sl@0: // sl@0: const TText8* TMPEG2Parser::MatchExtension(const TDesC& aExt) sl@0: { sl@0: if (aExt.Length() > 0) sl@0: { sl@0: for (TInt i = 0; i < KMPEG2ExtCount; i++) sl@0: { sl@0: if (aExt.MatchF(TPtrC(KMPEG2Types[i].iExt)) != KErrNotFound) sl@0: { sl@0: iFlags.SetExtensionFlag(); sl@0: return KMPEG2Types[i].iMime; sl@0: } sl@0: } sl@0: } sl@0: sl@0: return NULL; sl@0: } sl@0: sl@0: sl@0: // sl@0: // sl@0: // sl@0: void TMPEG2Parser::DoRecognise(const TDesC& aExt, CReader& aReader, TMatch& aMatch) sl@0: { sl@0: TFlags flags; sl@0: TMPEG2Parser parser(aReader, flags); sl@0: sl@0: const TText8* extMime = parser.MatchExtension(aExt); sl@0: TRAP_IGNORE(parser.ParseL()); sl@0: sl@0: TInt confIndex = flags.GetBitField(KMPEG2ConfidenceMask); sl@0: aMatch.iConfidence = KMPEG2FlagsToConfidence[confIndex]; sl@0: if (aMatch.iConfidence != KConfNotRecognised) sl@0: { sl@0: if (confIndex == KMPEG2ExtOnlyIndex) sl@0: { sl@0: // The content is corrupt, but the extension was recognised. sl@0: aMatch.iMime = extMime; sl@0: } sl@0: else sl@0: { sl@0: TInt mimeIndex = flags.GetBitField(KMPEG2MimeMask, KMPEG2MimeShift); sl@0: aMatch.iMime = KMPEG2Mimes[mimeIndex]; sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // Attempts to parse an MPEG2 file by looking for start-codes. sl@0: // sl@0: void TMPEG2Parser::ParseL() sl@0: { sl@0: TBool finished; sl@0: sl@0: // Assume there's video content if we only have a buffer. sl@0: if (iReader.Type() == CReader::EBuffer) sl@0: { sl@0: iFlags.SetBit(KMPEG2VideoBit); sl@0: } sl@0: sl@0: do sl@0: { sl@0: finished = NextStartCodeL(); sl@0: } sl@0: while (!finished); sl@0: } sl@0: sl@0: sl@0: // sl@0: // Skips over the current start-code box. sl@0: // sl@0: void TMPEG2Parser::SkipL() sl@0: { sl@0: TUint16 size; sl@0: sl@0: iReader.Read16L(size); sl@0: iReader.SeekL((TInt)size); sl@0: } sl@0: sl@0: sl@0: /* sl@0: ** Expects an MPEG2 Pack Header at the current location. sl@0: ** The Pack Header is used to determine between MPEG1 and MPEG2. sl@0: */ sl@0: void TMPEG2Parser::ReadPackHeaderL() sl@0: { sl@0: TUint8 byte; sl@0: TBuf8 header; // Size is of whichever is larger. sl@0: sl@0: iReader.ReadByteL(byte); sl@0: sl@0: if ((byte & KMPEG1PackHeaderID) == KMPEG1PackHeaderID) sl@0: { sl@0: iFlags.SetBit(KMPEG2MPEG1Bit); sl@0: header.SetLength(KMPEG1PackHeaderLen - 1); // We've already read a byte. sl@0: iReader.ReadBytesL(header); sl@0: } sl@0: else if ((byte & KMPEG2PackHeaderID) == KMPEG2PackHeaderID) sl@0: { sl@0: header.SetLength(KMPEG2PackHeaderLen - 1); // We've already read a byte. sl@0: iReader.ReadBytesL(header); sl@0: sl@0: // The lowest 3 bits of the last byte say how much stuffing is present. sl@0: TInt stuffing = header[8] & 0x07; // 00000111 sl@0: if (stuffing) sl@0: { sl@0: iReader.SeekL(stuffing); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: } sl@0: sl@0: // sl@0: // Start codes are bit patterns that do not occur in the video stream. sl@0: // The start-code sequence is expected to be at the current CReader position. sl@0: // sl@0: TBool TMPEG2Parser::NextStartCodeL() sl@0: { sl@0: TUint32 data; sl@0: sl@0: iReader.Read32L(data); sl@0: sl@0: // Start codes must begin with 0x000001ss, where 'ss' is the start code. sl@0: if ((data & KStartCodeMask) != KStartCodeIntro) sl@0: { sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: if (!iFlags.GetBitField(KMPEG2StartCode1Bit)) sl@0: { sl@0: iFlags.SetBit(KMPEG2StartCode1Bit); sl@0: } sl@0: else sl@0: { sl@0: if (!iFlags.GetBitField(KMPEG2StartCode2Bit)) sl@0: { sl@0: iFlags.SetBit(KMPEG2StartCode2Bit); sl@0: } sl@0: } sl@0: sl@0: // Try to identify the start code. sl@0: switch (LOW_BYTE(data)) sl@0: { sl@0: case KPackStartCode: sl@0: ReadPackHeaderL(); sl@0: break; sl@0: sl@0: case KVideoElementaryStream: sl@0: iFlags.SetBit(KMPEG2VideoBit); sl@0: return ETrue; sl@0: sl@0: case KISOEndCode: sl@0: // This code should occur at the end of the file and sl@0: // it cannot be skipped over. sl@0: return ETrue; sl@0: sl@0: default: sl@0: SkipL(); sl@0: } sl@0: sl@0: return EFalse; sl@0: } sl@0: