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: sl@0: #include "parsers.h" sl@0: sl@0: // sl@0: // Masks used for reading bits. sl@0: // sl@0: static const TInt KMP3Sync1Mask = 0xff; // 11111111 sl@0: static const TInt KMP3Sync2Mask = 0xe0; // 11100000 sl@0: static const TInt KMP3VersionMask = 0x18; // 00011000 sl@0: static const TInt KMP3LayerMask = 0x06; // 00000110 sl@0: static const TInt KMP3BitrateMask = 0xf0; // 11110000 sl@0: static const TInt KMP3SampleRateMask = 0x0c; // 00001100 sl@0: static const TInt KMP3PaddingMask = 0x02; // 00000010 sl@0: sl@0: static const TInt KBitsPerByte = 8; sl@0: sl@0: // sl@0: // Macros for retrieving the values. sl@0: // sl@0: #define MP3_GET_SYNC1(d) (d & KMP3Sync1Mask) sl@0: #define MP3_GET_SYNC2(d) ((d & KMP3Sync2Mask) >> 5) sl@0: #define MP3_GET_VERSION(d) ((d & KMP3VersionMask) >> 3) sl@0: #define MP3_GET_LAYER(d) ((d & KMP3LayerMask) >> 1) sl@0: #define MP3_GET_BITRATE(d) ((d & KMP3BitrateMask) >> 4) sl@0: #define MP3_GET_SAMPLE_RATE(d) ((d & KMP3SampleRateMask) >> 2) sl@0: #define MP3_GET_PADDING(d) ((d & KMP3PaddingMask) >> 1) sl@0: sl@0: sl@0: // sl@0: // Macros used for checking various bitfields. sl@0: // sl@0: #define IS_BAD_MP3_FRAME_SYNC1(s) ((s) != 0xff) sl@0: #define IS_BAD_MP3_FRAME_SYNC2(s) ((s) != 0x07) sl@0: #define IS_BAD_MP3_VERSION(v) ((v) < 0x02) sl@0: #define IS_BAD_MP3_LAYER(l) ((l) == 0x00) sl@0: #define IS_BAD_MP3_BITRATE(b) ((b) == 0x0f) sl@0: #define IS_BAD_MP3_SAMPLE_RATE(s) ((s) == 0x03) sl@0: sl@0: sl@0: // sl@0: // Meanings of the 'Version' field. sl@0: // -------------------------------- sl@0: // 00 - MPEG Version 2.5 (Unofficial standard. We don't support it.) sl@0: // 01 - Reserved sl@0: // 10 - MPEG Version 2 sl@0: // 11 - MPEG Version 1 sl@0: // sl@0: static const TInt8 KMP3Version2 = 2; sl@0: static const TInt8 KMP3Version1 = 3; sl@0: sl@0: sl@0: // sl@0: // Meanings of the 'Layer' field. sl@0: // ------------------------------ sl@0: // 00 - Reserved sl@0: // 01 - Layer III sl@0: // 10 - Layer II sl@0: // 11 - Layer I sl@0: // sl@0: sl@0: static const TUint16 KBad = 0; sl@0: static const TUint16 KFree = 0; sl@0: sl@0: // sl@0: // MPEG Version 1 bitrates. Measured in kilobits per second. sl@0: // sl@0: static const TUint16 KBitrateVersion1[4][16] = sl@0: { sl@0: { KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad}, // Reserved layer sl@0: {KFree, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, KBad}, // Layer III sl@0: {KFree, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, KBad}, // Layer II sl@0: {KFree, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, KBad} // Layer I sl@0: }; sl@0: sl@0: // sl@0: // MPEG Version 2 bitrates. Measured in kilobits per second. sl@0: // sl@0: static const TUint16 KBitrateVersion2[4][16] = sl@0: { sl@0: { KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad, KBad}, // Reserved layer sl@0: {KFree, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, KBad}, // Layer III sl@0: {KFree, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, KBad}, // Layer II sl@0: {KFree, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, KBad} // Layer I sl@0: }; sl@0: sl@0: // sl@0: // Sample rates for the MPEG versions. sl@0: // sl@0: static const TUint16 KSampleRate[4][4] = sl@0: { sl@0: {11025, 12000, 8000, KBad}, // Version 2.5 sl@0: { KBad, KBad, KBad, KBad}, // Reserved sl@0: {22050, 24000, 16000, KBad}, // Version 2 sl@0: {44100, 48000, 32000, KBad} // Version 1 sl@0: }; sl@0: sl@0: // sl@0: // Sample rates per frame for the MPEG layers. sl@0: // sl@0: static const TUint16 KSamplesPerFrame[4][4] = sl@0: { sl@0: {KBad, KBad, KBad, KBad}, // Reserved layer sl@0: { 576, KBad, 576, 1152}, // Layer III sl@0: {1152, KBad, 1152, 1152}, // Layer II sl@0: { 384, KBad, 384, 384} // Layer I sl@0: }; sl@0: sl@0: // sl@0: // The various states the recognition process goes through. sl@0: // sl@0: typedef enum sl@0: { sl@0: ESearchFrame1, sl@0: ESearchFrame2 sl@0: } sl@0: TMP3State; sl@0: sl@0: // sl@0: // This truth table maps the following flags to a confidence level. sl@0: // ----------------------------------------------------------------- sl@0: // A: Frame2 found. sl@0: // B: Frame1 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 -> ENotRecognized sl@0: // 0 1 1 -> ECertain sl@0: // 1 0 0 -> EPossible sl@0: // 1 0 1 -> EProbable sl@0: // 1 1 0 -> EProbable sl@0: // 1 1 1 -> ECertain sl@0: // sl@0: // In the case where two consecutive mp3 frames sl@0: // are found, ECertain is automatically returned. sl@0: // sl@0: static const TInt KMP3FlagsToConfidence[8] = sl@0: { sl@0: KConfNotRecognised, sl@0: KConfPossible, sl@0: KConfNotRecognised, sl@0: KConfCertain, sl@0: KConfPossible, sl@0: KConfProbable, sl@0: KConfProbable, sl@0: KConfCertain sl@0: }; sl@0: sl@0: #define KMP3ConfidenceMask 0x07 // 00000111 sl@0: #define KMP3Frame1Bit KBit1 sl@0: #define KMP3Frame2Bit KBit2 sl@0: sl@0: // sl@0: // sl@0: // sl@0: TMP3Parser::TMP3Parser(CReader& aReader, TFlags& aFlags) sl@0: : iReader(aReader), sl@0: iFlags(aFlags) sl@0: { sl@0: } sl@0: sl@0: sl@0: // sl@0: // MP3 recogition function. sl@0: // sl@0: void TMP3Parser::DoRecognise(const TDesC& aExt, CReader& aReader, TMatch& aMatch) sl@0: { sl@0: TFlags flags; sl@0: sl@0: // Try to match the extension. sl@0: if (aExt.MatchF(TPtrC(KExtMP3)) != KErrNotFound) sl@0: { sl@0: flags.SetExtensionFlag(); sl@0: } sl@0: sl@0: // Parse the data. sl@0: TMP3Parser parser(aReader, flags); sl@0: TRAP_IGNORE(parser.ParseL()); sl@0: sl@0: TInt confIndex = flags.GetBitField(KMP3ConfidenceMask); sl@0: aMatch.iConfidence = KMP3FlagsToConfidence[confIndex]; sl@0: if (aMatch.iConfidence != KConfNotRecognised) sl@0: { sl@0: aMatch.iMime = KMimeMP3; sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // Attempts to parse an mp3 file. sl@0: // First of all it checks if there is an ID3 metadata header sl@0: // present at the current reader position. Then it checks for up to sl@0: // two consecutive mp3 audio frames. sl@0: // sl@0: void TMP3Parser::ParseL() sl@0: { sl@0: TInt length; sl@0: TMP3State state = ESearchFrame1; sl@0: sl@0: FOREVER sl@0: { sl@0: TID3Parser::ReadAndSkipID3L(iReader); sl@0: sl@0: TInt err = CheckForFrameHeaderL(length); sl@0: if (err == KErrNotFound) sl@0: { sl@0: return; sl@0: } sl@0: sl@0: switch (state) sl@0: { sl@0: case ESearchFrame1: sl@0: iFlags.SetBit(KMP3Frame1Bit); sl@0: state = ESearchFrame2; sl@0: break; sl@0: sl@0: case ESearchFrame2: sl@0: iFlags.SetBit(KMP3Frame2Bit); sl@0: return; sl@0: } sl@0: sl@0: // Skip over the audio frame. sl@0: // This should be done after flags have been set. sl@0: iReader.SeekL(length - KMP3FrameHeaderSize); sl@0: } sl@0: } sl@0: sl@0: sl@0: // sl@0: // Checks for an MP3 frame header at the current reader position. sl@0: // If one is not found KErrNotFound is returned and aFrameLength sl@0: // remains unchanged. If one is found KErrNone is retured and sl@0: // aFrameLength is set to the length of the frame. sl@0: // sl@0: TInt TMP3Parser::CheckForFrameHeaderL(TInt& aFrameLength) sl@0: { sl@0: TBuf8 data; sl@0: TUint8 versionIndex; sl@0: TUint8 layerIndex; sl@0: TUint8 bitrateIndex; sl@0: TUint8 sampleRateIndex; sl@0: TUint8 padding; sl@0: TUint8 sync; sl@0: sl@0: data.SetLength(KMP3FrameHeaderSize); sl@0: iReader.ReadBytesL(data); sl@0: sl@0: do sl@0: { sl@0: sync = MP3_GET_SYNC1(data[0]); sl@0: if (IS_BAD_MP3_FRAME_SYNC1(sync)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: sync = MP3_GET_SYNC2(data[1]); sl@0: if (IS_BAD_MP3_FRAME_SYNC2(sync)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: versionIndex = MP3_GET_VERSION(data[1]); sl@0: if (IS_BAD_MP3_VERSION(versionIndex)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: layerIndex = MP3_GET_LAYER(data[1]); sl@0: if (IS_BAD_MP3_LAYER(layerIndex)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: bitrateIndex = MP3_GET_BITRATE(data[2]); sl@0: if (IS_BAD_MP3_BITRATE(bitrateIndex)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: sampleRateIndex = MP3_GET_SAMPLE_RATE(data[2]); sl@0: if (IS_BAD_MP3_SAMPLE_RATE(sampleRateIndex)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: padding = MP3_GET_PADDING(data[2]); sl@0: sl@0: // All the data is valid. sl@0: // Compute the audio data length. sl@0: TUint32 bitRate = KBad; sl@0: TUint16 sampleRate = KBad; sl@0: TUint16 samplesPerFrame = KBad; sl@0: sl@0: if (versionIndex == KMP3Version1) sl@0: { sl@0: bitRate = KBitrateVersion1[layerIndex][bitrateIndex]; sl@0: } sl@0: else if (versionIndex == KMP3Version2) sl@0: { sl@0: bitRate = KBitrateVersion2[layerIndex][bitrateIndex]; sl@0: } sl@0: else sl@0: { sl@0: // Version 2.5 is not supported. sl@0: break; sl@0: } sl@0: sl@0: sampleRate = KSampleRate[versionIndex][sampleRateIndex]; sl@0: samplesPerFrame = KSamplesPerFrame[layerIndex][versionIndex]; sl@0: sl@0: // Check we have valid values. sl@0: if ((bitRate == KBad) || (sampleRate == KBad) || (samplesPerFrame == KBad)) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: bitRate *= 1000; // Convert to kilobits. sl@0: aFrameLength = (((samplesPerFrame / KBitsPerByte) * bitRate) / sampleRate) + padding; sl@0: return KErrNone; sl@0: } sl@0: while (EFalse); sl@0: sl@0: // No valid frame header was found. sl@0: aFrameLength = 0; sl@0: iReader.SeekL(-KMP3FrameHeaderSize); sl@0: return KErrNotFound; sl@0: } sl@0: sl@0: sl@0: sl@0: