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 TUint32 KRMFileHeaderId = MAKE_INT32('.', 'R', 'M', 'F'); sl@0: static const TUint32 KRMMediaPropertiesId = MAKE_INT32('M', 'D', 'P', 'R'); sl@0: static const TInt KRMMaxMimeTypeLen = 255; // Specified by an 8-bit field sl@0: sl@0: #define KRMExtensionBit KBit0 sl@0: #define KRMFileHeaderBit KBit1 sl@0: #define KRMMediaPropertiesBit KBit2 sl@0: #define KRMVideoBit KBit3 sl@0: sl@0: #define KRMVideoMask 0x08 // 0000 1000 sl@0: #define KRMConfidenceMask 0x07 // 0000 0111 sl@0: sl@0: // sl@0: // This truth table maps the following flags to a confidence level. sl@0: // ----------------------------------------------------------------- sl@0: // A: MediaProperties chunk found sl@0: // B: FileHeader signature found sl@0: // C: Extension recognised. sl@0: // sl@0: // A B C -> Confidence sl@0: // =================== sl@0: // 0 0 0 -> ENotRecognized sl@0: // 0 0 1 -> EPossible sl@0: // 0 1 0 -> EPossible (EPossible beacuse the header is ASCII and may match plaintext) sl@0: // 0 1 1 -> ECertain sl@0: // 1 0 0 -> ENotRecognised (Can't have MediaProperties without FileHeader) sl@0: // 1 0 1 -> ENotRecognised (Can't have MediaProperties without FileHeader) sl@0: // 1 1 0 -> EProbable sl@0: // 1 1 1 -> ECertain sl@0: // sl@0: static const TInt KRMFlagsToConfidence[] = sl@0: { sl@0: KConfNotRecognised, sl@0: KConfPossible, sl@0: KConfPossible, sl@0: KConfCertain, sl@0: KConfNotRecognised, sl@0: KConfNotRecognised, sl@0: KConfProbable, sl@0: KConfCertain sl@0: }; sl@0: sl@0: sl@0: // sl@0: // Constructor. sl@0: // sl@0: TRMParser::TRMParser(CReader& aReader, TFlags& aFlags) sl@0: : iReader(aReader), sl@0: iFlags(aFlags) sl@0: { sl@0: } sl@0: sl@0: sl@0: // sl@0: // The main RealMedia parsing function. sl@0: // sl@0: void TRMParser::DoRecognise(const TDesC& aFileExt, CReader& aReader, TMatch& aMatch) sl@0: { sl@0: TFlags flags; sl@0: TRMParser parser(aReader, flags); sl@0: sl@0: parser.MatchExtension(aFileExt); sl@0: sl@0: TRAP_IGNORE(parser.ParseL()); sl@0: sl@0: TInt confIndex = flags.GetBitField(KRMConfidenceMask); sl@0: aMatch.iConfidence = KRMFlagsToConfidence[confIndex]; sl@0: sl@0: if (aMatch.iConfidence != KConfNotRecognised) sl@0: { sl@0: aMatch.iMime = (flags.GetBitField(KRMVideoMask) ? KMimeRM_V : KMimeRM_A); sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // Match the file extension given by AppArc against known sl@0: // extensions for this format. sl@0: // sl@0: void TRMParser::MatchExtension(const TDesC& aFileExt) sl@0: { sl@0: TBool match; sl@0: sl@0: match = (aFileExt.MatchF(TPtrC(KExtRMF)) != KErrNotFound); sl@0: match |= (aFileExt.MatchF(TPtrC(KExtRM)) != KErrNotFound); sl@0: sl@0: if (match) sl@0: { sl@0: iFlags.SetExtensionFlag(); sl@0: } sl@0: } sl@0: sl@0: // sl@0: // RealMedia files are divided into chunks. Chunks begins with a 32-bit identifier sl@0: // which is followed by a 32-bit unsigned field that specifies the size of the chunk sl@0: // in bytes. Since the size field is unsigned, it must be cast to a TInt64 in order sl@0: // for SeekL() to work reliably. The size field also includes the length of the chunk sl@0: // identifier and the length of the size field itself, so these must be subtracted sl@0: // from any seek operation. sl@0: // sl@0: void TRMParser::ParseL() sl@0: { sl@0: TUint32 objectId; sl@0: TUint32 size; sl@0: const TInt KChunkHeaderSize = sizeof(TUint32) * 2; // sizeof objectId and size fields. sl@0: sl@0: // The first chunk must be the FileHeader. sl@0: ReadChunkHeaderL(objectId, size, ETrue); 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(KRMVideoBit); sl@0: } sl@0: sl@0: iReader.SeekL(TInt64(size - KChunkHeaderSize)); sl@0: sl@0: // The other chunks can occur in any order. sl@0: TBool complete = EFalse; sl@0: do sl@0: { sl@0: ReadChunkHeaderL(objectId, size); sl@0: sl@0: if (objectId == KRMMediaPropertiesId) sl@0: { sl@0: iFlags.SetBit(KRMMediaPropertiesBit); sl@0: if (ReadMediaPropertiesL()) sl@0: { sl@0: // Exit early if the video flag has been set by ReadMediaPropertiesL. sl@0: complete = ETrue; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Skip over the unneeded chunk. sl@0: iReader.SeekL(TInt64(size - KChunkHeaderSize)); sl@0: } sl@0: } sl@0: while (!complete); sl@0: } sl@0: sl@0: sl@0: // sl@0: // Reads the chunk identifier and size. sl@0: // sl@0: void TRMParser::ReadChunkHeaderL(TUint32& aObjectId, TUint32& aSize, TBool aFirstChunk) sl@0: { sl@0: iReader.Read32L(aObjectId); sl@0: sl@0: if (aFirstChunk) sl@0: { sl@0: if (aObjectId == KRMFileHeaderId) sl@0: { sl@0: iFlags.SetBit(KRMFileHeaderBit); sl@0: } sl@0: else sl@0: { sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: } sl@0: sl@0: iReader.Read32L(aSize); sl@0: if (aSize == 0) sl@0: { sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // The MediaProperties chunk describes a stream in the RM file. There sl@0: // may be several MediaProperties chunks in one file. In order to determine sl@0: // if video content is present we must look at the MIME-type that may sl@0: // be present in each MediaProperties chunk. sl@0: // Return ETrue if this function set the video bit. sl@0: // sl@0: TBool TRMParser::ReadMediaPropertiesL() sl@0: { sl@0: const TInt KBytesToFieldSize = 30; sl@0: sl@0: // ObjectId and size fields have already been read. sl@0: TBool setVideo = EFalse; sl@0: TUint16 objectVersion; sl@0: sl@0: iReader.Read16L(objectVersion); sl@0: sl@0: if (objectVersion == 0) sl@0: { sl@0: TUint8 fieldSize; sl@0: TBuf8 mimeType; sl@0: _LIT8(KVideoStar, "video/*"); sl@0: sl@0: // Skip unneeded information. sl@0: iReader.SeekL(KBytesToFieldSize); sl@0: sl@0: // Read stream_name_size so we can skip over it. sl@0: iReader.ReadByteL(fieldSize); sl@0: iReader.SeekL(fieldSize); sl@0: sl@0: // Read the mime_type_size. Because it's 8-bit, it has a max length of 256 bytes. sl@0: iReader.ReadByteL(fieldSize); sl@0: sl@0: mimeType.SetLength(fieldSize); sl@0: iReader.ReadBytesL(mimeType); sl@0: sl@0: // Now check if the mime-type field indicates video content. sl@0: if (mimeType.MatchF(KVideoStar) != KErrNotFound) sl@0: { sl@0: iFlags.SetBit(KRMVideoBit); sl@0: setVideo = ETrue; sl@0: } sl@0: sl@0: // Read the type_specific_len so we can skip over it. sl@0: TUint32 typeSpecificLen; sl@0: iReader.Read32L(typeSpecificLen); sl@0: iReader.SeekL(TInt64(typeSpecificLen)); sl@0: } sl@0: sl@0: return setVideo; sl@0: } sl@0: