1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/MP3FrameParser.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,212 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef MP3FrameParser_h 1.11 +#define MP3FrameParser_h 1.12 + 1.13 +#include <stdint.h> 1.14 + 1.15 +#include "mozilla/Mutex.h" 1.16 +#include "nsString.h" 1.17 + 1.18 +namespace mozilla { 1.19 + 1.20 +// Simple parser to tell whether we've found an ID3 header and how long it is, 1.21 +// so that we can skip it. 1.22 +// XXX maybe actually parse this stuff? 1.23 +class ID3Parser 1.24 +{ 1.25 +public: 1.26 + ID3Parser(); 1.27 + 1.28 + void Reset(); 1.29 + bool ParseChar(char ch); 1.30 + bool IsParsed() const; 1.31 + uint32_t GetHeaderLength() const; 1.32 + 1.33 +private: 1.34 + uint32_t mCurrentChar; 1.35 + uint32_t mHeaderLength; 1.36 +}; 1.37 + 1.38 +struct MP3Frame { 1.39 + uint16_t mSync1 : 8; // Always all set 1.40 + uint16_t mProtected : 1; // Ignored 1.41 + uint16_t mLayer : 2; 1.42 + uint16_t mVersion : 2; 1.43 + uint16_t mSync2 : 3; // Always all set 1.44 + uint16_t mPrivate : 1; // Ignored 1.45 + uint16_t mPad : 1; 1.46 + uint16_t mSampleRate : 2; // Index into mpeg_srates above 1.47 + uint16_t mBitrate : 4; // Index into mpeg_bitrates above 1.48 + 1.49 + uint16_t CalculateLength(); 1.50 +}; 1.51 + 1.52 +// Buffering parser for MP3 frames. 1.53 +class MP3Parser 1.54 +{ 1.55 +public: 1.56 + MP3Parser(); 1.57 + 1.58 + // Forget all data the parser has seen so far. 1.59 + void Reset(); 1.60 + 1.61 + // Parse the given byte. If we have found a frame header, return the length of 1.62 + // the frame. 1.63 + uint16_t ParseFrameLength(uint8_t ch); 1.64 + 1.65 + // Get the sample rate from the current header. 1.66 + uint32_t GetSampleRate(); 1.67 + 1.68 + // Get the number of samples per frame. 1.69 + uint32_t GetSamplesPerFrame(); 1.70 + 1.71 +private: 1.72 + uint32_t mCurrentChar; 1.73 + union { 1.74 + uint8_t mRaw[3]; 1.75 + MP3Frame mFrame; 1.76 + } mData; 1.77 +}; 1.78 + 1.79 + 1.80 +// A description of the MP3 format and its extensions is available at 1.81 +// 1.82 +// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header 1.83 +// 1.84 +// The data in MP3 streams is split into small frames, with each frame 1.85 +// containing a fixed number of samples. The duration of a frame depends 1.86 +// on the frame's bit rate and sample rate. Both values can vary among 1.87 +// frames, so it is necessary to examine each individual frame of an MP3 1.88 +// stream to calculate the stream's overall duration. 1.89 +// 1.90 +// The MP3 frame parser extracts information from an MP3 data stream. It 1.91 +// accepts a range of frames of an MP3 stream as input, and parses all 1.92 +// frames for their duration. Callers can query the stream's overall 1.93 +// duration from the parser. 1.94 +// 1.95 +// Call the methods NotifyDataArrived or Parse to add new data. If you added 1.96 +// information for a certain stream position, you cannot go back to previous 1.97 +// positions. The parser will simply ignore the input. If you skip stream 1.98 +// positions, the duration of the related MP3 frames will be estimated from 1.99 +// the stream's average. 1.100 +// 1.101 +// The method GetDuration returns calculated duration of the stream, including 1.102 +// estimates for skipped ranges. 1.103 +// 1.104 +// All public methods are thread-safe. 1.105 + 1.106 +class MP3FrameParser 1.107 +{ 1.108 +public: 1.109 + MP3FrameParser(int64_t aLength=-1); 1.110 + 1.111 + bool IsMP3() { 1.112 + MutexAutoLock mon(mLock); 1.113 + return mIsMP3 != NOT_MP3; 1.114 + } 1.115 + 1.116 + void Parse(const char* aBuffer, uint32_t aLength, uint64_t aStreamOffset); 1.117 + 1.118 + // Returns the duration, in microseconds. If the entire stream has not 1.119 + // been parsed yet, this is an estimate based on the bitrate of the 1.120 + // frames parsed so far. 1.121 + int64_t GetDuration(); 1.122 + 1.123 + // Returns the offset of the first MP3 frame in the stream, or -1 of 1.124 + // no MP3 frame has been detected yet. 1.125 + int64_t GetMP3Offset(); 1.126 + 1.127 + // Returns true if we've seen the whole first frame of the MP3 stream, and 1.128 + // therefore can make an estimate on the stream duration. 1.129 + // Otherwise, returns false. 1.130 + bool ParsedHeaders(); 1.131 + 1.132 + // Returns true if we know the exact duration of the MP3 stream; 1.133 + // false otherwise. 1.134 + bool HasExactDuration(); 1.135 + 1.136 + // Returns true if the parser needs more data for duration estimation. 1.137 + bool NeedsData(); 1.138 + 1.139 +private: 1.140 + 1.141 + // Parses aBuffer, starting at offset 0. Returns the number of bytes 1.142 + // parsed, relative to the start of the buffer. Note this may be 1.143 + // greater than aLength if the headers in the buffer indicate that 1.144 + // the frame or ID3 tag extends outside of aBuffer. Returns failure 1.145 + // if too many non-MP3 bytes are parsed. 1.146 + nsresult ParseBuffer(const uint8_t* aBuffer, 1.147 + uint32_t aLength, 1.148 + int64_t aStreamOffset, 1.149 + uint32_t* aOutBytesRead); 1.150 + 1.151 + // A low-contention lock for protecting the parser results 1.152 + Mutex mLock; 1.153 + 1.154 + // ID3 header parser. Keeps state between reads in case the header falls 1.155 + // in between. 1.156 + ID3Parser mID3Parser; 1.157 + 1.158 + // MP3 frame header parser. 1.159 + MP3Parser mMP3Parser; 1.160 + 1.161 + // If we read |MAX_SKIPPED_BYTES| from the stream without finding any MP3 1.162 + // frames, we give up and report |NOT_MP3|. Here we track the cumulative size 1.163 + // of any ID3 headers we've seen so big ID3 sections aren't counted towards 1.164 + // skipped bytes. 1.165 + uint32_t mTotalID3Size; 1.166 + 1.167 + // All fields below are protected by mLock 1.168 + 1.169 + // We keep stats on the size of all the frames we've seen, as well as how many 1.170 + // so that we can estimate the duration of the rest of the stream. 1.171 + uint64_t mTotalFrameSize; 1.172 + uint64_t mFrameCount; 1.173 + 1.174 + // Offset of the last data parsed. This is the end offset of the last data 1.175 + // block parsed, so it's the start offset we expect to get on the next 1.176 + // call to Parse(). 1.177 + uint64_t mOffset; 1.178 + 1.179 + // Total length of the stream in bytes. 1.180 + int64_t mLength; 1.181 + 1.182 + // Offset of first MP3 frame in the bitstream. Has value -1 until the 1.183 + // first MP3 frame is found. 1.184 + int64_t mMP3Offset; 1.185 + 1.186 + // The exact number of frames in this stream, if we know it. -1 otherwise. 1.187 + int64_t mNumFrames; 1.188 + 1.189 + // Number of audio samples per second and per frame. Fixed through the whole 1.190 + // file. If we know these variables as well as the number of frames in the 1.191 + // file, we can get an exact duration for the stream. 1.192 + uint16_t mSamplesPerSecond; 1.193 + uint16_t mSamplesPerFrame; 1.194 + 1.195 + // If the MP3 has a variable bitrate, then there *should* be metadata about 1.196 + // the encoding in the first frame. We buffer the first frame here. 1.197 + nsAutoCString mFirstFrame; 1.198 + 1.199 + // While we are reading the first frame, this is the stream offset of the 1.200 + // last byte of that frame. -1 at all other times. 1.201 + int64_t mFirstFrameEnd; 1.202 + 1.203 + enum eIsMP3 { 1.204 + MAYBE_MP3, // We're giving the stream the benefit of the doubt... 1.205 + DEFINITELY_MP3, // We've hit at least one ID3 tag or MP3 frame. 1.206 + NOT_MP3 // Not found any evidence of the stream being MP3. 1.207 + }; 1.208 + 1.209 + eIsMP3 mIsMP3; 1.210 + 1.211 +}; 1.212 + 1.213 +} 1.214 + 1.215 +#endif