1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/MP3FrameParser.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,557 @@ 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 +#include <algorithm> 1.11 + 1.12 +#include "nsMemory.h" 1.13 +#include "MP3FrameParser.h" 1.14 +#include "VideoUtils.h" 1.15 + 1.16 + 1.17 +#define FROM_BIG_ENDIAN(X) ((uint32_t)((uint8_t)(X)[0] << 24 | (uint8_t)(X)[1] << 16 | \ 1.18 + (uint8_t)(X)[2] << 8 | (uint8_t)(X)[3])) 1.19 + 1.20 + 1.21 +namespace mozilla { 1.22 + 1.23 +/* 1.24 + * Following code taken from http://www.hydrogenaudio.org/forums/index.php?showtopic=85125 1.25 + * with permission from the author, Nick Wallette <sirnickity@gmail.com>. 1.26 + */ 1.27 + 1.28 +/* BEGIN shameless copy and paste */ 1.29 + 1.30 +// Bitrates - use [version][layer][bitrate] 1.31 +const uint16_t mpeg_bitrates[4][4][16] = { 1.32 + { // Version 2.5 1.33 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved 1.34 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 1.35 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 1.36 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 1.37 + }, 1.38 + { // Reserved 1.39 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid 1.40 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid 1.41 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid 1.42 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid 1.43 + }, 1.44 + { // Version 2 1.45 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved 1.46 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 1.47 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 1.48 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 1.49 + }, 1.50 + { // Version 1 1.51 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved 1.52 + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3 1.53 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2 1.54 + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1 1.55 + } 1.56 +}; 1.57 + 1.58 +// Sample rates - use [version][srate] 1.59 +const uint16_t mpeg_srates[4][4] = { 1.60 + { 11025, 12000, 8000, 0 }, // MPEG 2.5 1.61 + { 0, 0, 0, 0 }, // Reserved 1.62 + { 22050, 24000, 16000, 0 }, // MPEG 2 1.63 + { 44100, 48000, 32000, 0 } // MPEG 1 1.64 +}; 1.65 + 1.66 +// Samples per frame - use [version][layer] 1.67 +const uint16_t mpeg_frame_samples[4][4] = { 1.68 +// Rsvd 3 2 1 < Layer v Version 1.69 + { 0, 576, 1152, 384 }, // 2.5 1.70 + { 0, 0, 0, 0 }, // Reserved 1.71 + { 0, 576, 1152, 384 }, // 2 1.72 + { 0, 1152, 1152, 384 } // 1 1.73 +}; 1.74 + 1.75 +// Slot size (MPEG unit of measurement) - use [layer] 1.76 +const uint8_t mpeg_slot_size[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1 1.77 + 1.78 +uint16_t 1.79 +MP3Frame::CalculateLength() 1.80 +{ 1.81 + // Lookup real values of these fields 1.82 + uint32_t bitrate = mpeg_bitrates[mVersion][mLayer][mBitrate] * 1000; 1.83 + uint32_t samprate = mpeg_srates[mVersion][mSampleRate]; 1.84 + uint16_t samples = mpeg_frame_samples[mVersion][mLayer]; 1.85 + uint8_t slot_size = mpeg_slot_size[mLayer]; 1.86 + 1.87 + // In-between calculations 1.88 + float bps = (float)samples / 8.0; 1.89 + float fsize = ( (bps * (float)bitrate) / (float)samprate ) 1.90 + + ( (mPad) ? slot_size : 0 ); 1.91 + 1.92 + // Frame sizes are truncated integers 1.93 + return (uint16_t)fsize; 1.94 +} 1.95 + 1.96 +/* END shameless copy and paste */ 1.97 + 1.98 + 1.99 +/** MP3Parser methods **/ 1.100 + 1.101 +MP3Parser::MP3Parser() 1.102 + : mCurrentChar(0) 1.103 +{ } 1.104 + 1.105 +void 1.106 +MP3Parser::Reset() 1.107 +{ 1.108 + mCurrentChar = 0; 1.109 +} 1.110 + 1.111 +uint16_t 1.112 +MP3Parser::ParseFrameLength(uint8_t ch) 1.113 +{ 1.114 + mData.mRaw[mCurrentChar] = ch; 1.115 + 1.116 + MP3Frame &frame = mData.mFrame; 1.117 + 1.118 + // Validate MP3 header as we read. We can't mistake the start of an MP3 frame 1.119 + // for the middle of another frame due to the sync byte at the beginning 1.120 + // of the frame. 1.121 + 1.122 + // The only valid position for an all-high byte is the sync byte at the 1.123 + // beginning of the frame. 1.124 + if (ch == 0xff) { 1.125 + mCurrentChar = 0; 1.126 + } 1.127 + 1.128 + // Make sure the current byte is valid in context. If not, reset the parser. 1.129 + if (mCurrentChar == 2) { 1.130 + if (frame.mBitrate == 0x0f) { 1.131 + goto fail; 1.132 + } 1.133 + } else if (mCurrentChar == 1) { 1.134 + if (frame.mSync2 != 0x07 1.135 + || frame.mVersion == 0x01 1.136 + || frame.mLayer == 0x00) { 1.137 + goto fail; 1.138 + } 1.139 + } 1.140 + 1.141 + // The only valid character at the beginning of the header is 0xff. Fail if 1.142 + // it's different. 1.143 + if (mCurrentChar == 0 && frame.mSync1 != 0xff) { 1.144 + // Couldn't find the sync byte. Fail. 1.145 + return 0; 1.146 + } 1.147 + 1.148 + mCurrentChar++; 1.149 + MOZ_ASSERT(mCurrentChar <= sizeof(MP3Frame)); 1.150 + 1.151 + // Don't have a full header yet. 1.152 + if (mCurrentChar < sizeof(MP3Frame)) { 1.153 + return 0; 1.154 + } 1.155 + 1.156 + // Woo, valid header. Return the length. 1.157 + mCurrentChar = 0; 1.158 + return frame.CalculateLength(); 1.159 + 1.160 +fail: 1.161 + Reset(); 1.162 + return 0; 1.163 +} 1.164 + 1.165 +uint32_t 1.166 +MP3Parser::GetSampleRate() 1.167 +{ 1.168 + MP3Frame &frame = mData.mFrame; 1.169 + return mpeg_srates[frame.mVersion][frame.mSampleRate]; 1.170 +} 1.171 + 1.172 +uint32_t 1.173 +MP3Parser::GetSamplesPerFrame() 1.174 +{ 1.175 + MP3Frame &frame = mData.mFrame; 1.176 + return mpeg_frame_samples[frame.mVersion][frame.mLayer]; 1.177 +} 1.178 + 1.179 + 1.180 +/** ID3Parser methods **/ 1.181 + 1.182 +const char sID3Head[3] = { 'I', 'D', '3' }; 1.183 +const uint32_t ID3_HEADER_LENGTH = 10; 1.184 + 1.185 +ID3Parser::ID3Parser() 1.186 + : mCurrentChar(0) 1.187 + , mHeaderLength(0) 1.188 +{ } 1.189 + 1.190 +void 1.191 +ID3Parser::Reset() 1.192 +{ 1.193 + mCurrentChar = mHeaderLength = 0; 1.194 +} 1.195 + 1.196 +bool 1.197 +ID3Parser::ParseChar(char ch) 1.198 +{ 1.199 + // First three bytes of an ID3v2 header must match the string "ID3". 1.200 + if (mCurrentChar < sizeof(sID3Head) / sizeof(*sID3Head) 1.201 + && ch != sID3Head[mCurrentChar]) { 1.202 + goto fail; 1.203 + } 1.204 + 1.205 + // The last four bytes of the header is a 28-bit unsigned integer with the 1.206 + // high bit of each byte unset. 1.207 + if (mCurrentChar >= 6 && mCurrentChar < ID3_HEADER_LENGTH) { 1.208 + if (ch & 0x80) { 1.209 + goto fail; 1.210 + } else { 1.211 + mHeaderLength <<= 7; 1.212 + mHeaderLength |= ch; 1.213 + } 1.214 + } 1.215 + 1.216 + mCurrentChar++; 1.217 + 1.218 + return IsParsed(); 1.219 + 1.220 +fail: 1.221 + Reset(); 1.222 + return false; 1.223 +} 1.224 + 1.225 +bool 1.226 +ID3Parser::IsParsed() const 1.227 +{ 1.228 + return mCurrentChar >= ID3_HEADER_LENGTH; 1.229 +} 1.230 + 1.231 +uint32_t 1.232 +ID3Parser::GetHeaderLength() const 1.233 +{ 1.234 + MOZ_ASSERT(IsParsed(), 1.235 + "Queried length of ID3 header before parsing finished."); 1.236 + return mHeaderLength; 1.237 +} 1.238 + 1.239 + 1.240 +/** VBR header helper stuff **/ 1.241 + 1.242 +// Helper function to find a VBR header in an MP3 frame. 1.243 +// Based on information from 1.244 +// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header 1.245 + 1.246 +const uint32_t VBRI_TAG = FROM_BIG_ENDIAN("VBRI"); 1.247 +const uint32_t VBRI_OFFSET = 32 - sizeof(MP3Frame); 1.248 +const uint32_t VBRI_FRAME_COUNT_OFFSET = VBRI_OFFSET + 14; 1.249 +const uint32_t VBRI_MIN_FRAME_SIZE = VBRI_OFFSET + 26; 1.250 + 1.251 +const uint32_t XING_TAG = FROM_BIG_ENDIAN("Xing"); 1.252 +enum XingFlags { 1.253 + XING_HAS_NUM_FRAMES = 0x01, 1.254 + XING_HAS_NUM_BYTES = 0x02, 1.255 + XING_HAS_TOC = 0x04, 1.256 + XING_HAS_VBR_SCALE = 0x08 1.257 +}; 1.258 + 1.259 +static int64_t 1.260 +ParseXing(const char *aBuffer) 1.261 +{ 1.262 + uint32_t flags = FROM_BIG_ENDIAN(aBuffer + 4); 1.263 + 1.264 + if (!(flags & XING_HAS_NUM_FRAMES)) { 1.265 + NS_WARNING("VBR file without frame count. Duration estimation likely to " 1.266 + "be totally wrong."); 1.267 + return -1; 1.268 + } 1.269 + 1.270 + int64_t numFrames = -1; 1.271 + if (flags & XING_HAS_NUM_FRAMES) { 1.272 + numFrames = FROM_BIG_ENDIAN(aBuffer + 8); 1.273 + } 1.274 + 1.275 + return numFrames; 1.276 +} 1.277 + 1.278 +static int64_t 1.279 +FindNumVBRFrames(const nsAutoCString& aFrame) 1.280 +{ 1.281 + const char *buffer = aFrame.get(); 1.282 + const char *bufferEnd = aFrame.get() + aFrame.Length(); 1.283 + 1.284 + // VBRI header is nice and well-defined; let's try to find that first. 1.285 + if (aFrame.Length() > VBRI_MIN_FRAME_SIZE && 1.286 + FROM_BIG_ENDIAN(buffer + VBRI_OFFSET) == VBRI_TAG) { 1.287 + return FROM_BIG_ENDIAN(buffer + VBRI_FRAME_COUNT_OFFSET); 1.288 + } 1.289 + 1.290 + // We have to search for the Xing header as its position can change. 1.291 + for (; buffer + sizeof(XING_TAG) < bufferEnd; buffer++) { 1.292 + if (FROM_BIG_ENDIAN(buffer) == XING_TAG) { 1.293 + return ParseXing(buffer); 1.294 + } 1.295 + } 1.296 + 1.297 + return -1; 1.298 +} 1.299 + 1.300 + 1.301 +/** MP3FrameParser methods **/ 1.302 + 1.303 +// Some MP3's have large ID3v2 tags, up to 150KB, so we allow lots of 1.304 +// skipped bytes to be read, just in case, before we give up and assume 1.305 +// we're not parsing an MP3 stream. 1.306 +static const uint32_t MAX_SKIPPED_BYTES = 4096; 1.307 + 1.308 +// The number of audio samples per MP3 frame. This is constant over all MP3 1.309 +// streams. With this constant, the stream's sample rate, and an estimated 1.310 +// number of frames in the stream, we can estimate the stream's duration 1.311 +// fairly accurately. 1.312 +static const uint32_t SAMPLES_PER_FRAME = 1152; 1.313 + 1.314 +enum { 1.315 + MP3_HEADER_LENGTH = 4, 1.316 +}; 1.317 + 1.318 +MP3FrameParser::MP3FrameParser(int64_t aLength) 1.319 +: mLock("MP3FrameParser.mLock"), 1.320 + mTotalID3Size(0), 1.321 + mTotalFrameSize(0), 1.322 + mFrameCount(0), 1.323 + mOffset(0), 1.324 + mLength(aLength), 1.325 + mMP3Offset(-1), 1.326 + mSamplesPerSecond(0), 1.327 + mFirstFrameEnd(-1), 1.328 + mIsMP3(MAYBE_MP3) 1.329 +{ } 1.330 + 1.331 +nsresult MP3FrameParser::ParseBuffer(const uint8_t* aBuffer, 1.332 + uint32_t aLength, 1.333 + int64_t aStreamOffset, 1.334 + uint32_t* aOutBytesRead) 1.335 +{ 1.336 + // Iterate forwards over the buffer, looking for ID3 tag, or MP3 1.337 + // Frame headers. 1.338 + const uint8_t *buffer = aBuffer; 1.339 + const uint8_t *bufferEnd = aBuffer + aLength; 1.340 + 1.341 + // If we haven't found any MP3 frame data yet, there might be ID3 headers 1.342 + // we can skip over. 1.343 + if (mMP3Offset < 0) { 1.344 + for (const uint8_t *ch = buffer; ch < bufferEnd; ch++) { 1.345 + if (mID3Parser.ParseChar(*ch)) { 1.346 + // Found an ID3 header. We don't care about the body of the header, so 1.347 + // just skip past. 1.348 + buffer = ch + mID3Parser.GetHeaderLength() - (ID3_HEADER_LENGTH - 1); 1.349 + ch = buffer; 1.350 + 1.351 + mTotalID3Size += mID3Parser.GetHeaderLength(); 1.352 + 1.353 + // Yes, this is an MP3! 1.354 + mIsMP3 = DEFINITELY_MP3; 1.355 + 1.356 + mID3Parser.Reset(); 1.357 + } 1.358 + } 1.359 + } 1.360 + 1.361 + // The first MP3 frame in a variable bitrate stream can contain metadata 1.362 + // for duration estimation and seeking, so we buffer that first frame here. 1.363 + if (aStreamOffset < mFirstFrameEnd) { 1.364 + uint64_t copyLen = std::min((int64_t)aLength, mFirstFrameEnd - aStreamOffset); 1.365 + mFirstFrame.Append((const char *)buffer, copyLen); 1.366 + buffer += copyLen; 1.367 + } 1.368 + 1.369 + while (buffer < bufferEnd) { 1.370 + uint16_t frameLen = mMP3Parser.ParseFrameLength(*buffer); 1.371 + 1.372 + if (frameLen) { 1.373 + // We've found an MP3 frame! 1.374 + // This is the first frame (and the only one we'll bother parsing), so: 1.375 + // * Mark this stream as MP3; 1.376 + // * Store the offset at which the MP3 data started; and 1.377 + // * Start buffering the frame, as it might contain handy metadata. 1.378 + 1.379 + // We're now sure this is an MP3 stream. 1.380 + mIsMP3 = DEFINITELY_MP3; 1.381 + 1.382 + // We need to know these to convert the number of frames in the stream 1.383 + // to the length of the stream in seconds. 1.384 + mSamplesPerSecond = mMP3Parser.GetSampleRate(); 1.385 + mSamplesPerFrame = mMP3Parser.GetSamplesPerFrame(); 1.386 + 1.387 + // If the stream has a constant bitrate, we should only need the length 1.388 + // of the first frame and the length (in bytes) of the stream to 1.389 + // estimate the length (in seconds). 1.390 + mTotalFrameSize += frameLen; 1.391 + mFrameCount++; 1.392 + 1.393 + // If |mMP3Offset| isn't set then this is the first MP3 frame we have 1.394 + // seen in the stream, which is useful for duration estimation. 1.395 + if (mMP3Offset > -1) { 1.396 + uint16_t skip = frameLen - sizeof(MP3Frame); 1.397 + buffer += skip ? skip : 1; 1.398 + continue; 1.399 + } 1.400 + 1.401 + // Remember the offset of the MP3 stream. 1.402 + // We're at the last byte of an MP3Frame, so MP3 data started 1.403 + // sizeof(MP3Frame) - 1 bytes ago. 1.404 + mMP3Offset = aStreamOffset 1.405 + + (buffer - aBuffer) 1.406 + - (sizeof(MP3Frame) - 1); 1.407 + 1.408 + buffer++; 1.409 + 1.410 + // If the stream has a variable bitrate, the first frame has metadata 1.411 + // we need for duration estimation and seeking. Start buffering it so we 1.412 + // can parse it later. 1.413 + mFirstFrameEnd = mMP3Offset + frameLen; 1.414 + uint64_t currOffset = buffer - aBuffer + aStreamOffset; 1.415 + uint64_t copyLen = std::min(mFirstFrameEnd - currOffset, 1.416 + (uint64_t)(bufferEnd - buffer)); 1.417 + mFirstFrame.Append((const char *)buffer, copyLen); 1.418 + 1.419 + buffer += copyLen; 1.420 + 1.421 + } else { 1.422 + // Nothing to see here. Move along. 1.423 + buffer++; 1.424 + } 1.425 + } 1.426 + 1.427 + *aOutBytesRead = buffer - aBuffer; 1.428 + 1.429 + if (mFirstFrameEnd > -1 && mFirstFrameEnd <= aStreamOffset + buffer - aBuffer) { 1.430 + // We have our whole first frame. Try to find a VBR header. 1.431 + mNumFrames = FindNumVBRFrames(mFirstFrame); 1.432 + mFirstFrameEnd = -1; 1.433 + } 1.434 + 1.435 + return NS_OK; 1.436 +} 1.437 + 1.438 +void MP3FrameParser::Parse(const char* aBuffer, uint32_t aLength, uint64_t aOffset) 1.439 +{ 1.440 + MutexAutoLock mon(mLock); 1.441 + 1.442 + if (HasExactDuration()) { 1.443 + // We know the duration; nothing to do here. 1.444 + return; 1.445 + } 1.446 + 1.447 + const uint8_t* buffer = reinterpret_cast<const uint8_t*>(aBuffer); 1.448 + int32_t length = aLength; 1.449 + uint64_t offset = aOffset; 1.450 + 1.451 + // Got some data we have seen already. Skip forward to what we need. 1.452 + if (aOffset < mOffset) { 1.453 + buffer += mOffset - aOffset; 1.454 + length -= mOffset - aOffset; 1.455 + offset = mOffset; 1.456 + 1.457 + if (length <= 0) { 1.458 + return; 1.459 + } 1.460 + } 1.461 + 1.462 + // If there is a discontinuity in the input stream, reset the state of the 1.463 + // parsers so we don't get any partial headers. 1.464 + if (mOffset < aOffset) { 1.465 + if (!mID3Parser.IsParsed()) { 1.466 + // Only reset this if it hasn't finished yet. 1.467 + mID3Parser.Reset(); 1.468 + } 1.469 + 1.470 + if (mFirstFrameEnd > -1) { 1.471 + NS_WARNING("Discontinuity in input while buffering first frame."); 1.472 + mFirstFrameEnd = -1; 1.473 + } 1.474 + 1.475 + mMP3Parser.Reset(); 1.476 + } 1.477 + 1.478 + uint32_t bytesRead = 0; 1.479 + if (NS_FAILED(ParseBuffer(buffer, 1.480 + length, 1.481 + offset, 1.482 + &bytesRead))) { 1.483 + return; 1.484 + } 1.485 + 1.486 + MOZ_ASSERT(length <= (int)bytesRead, "All bytes should have been consumed"); 1.487 + 1.488 + // Update next data offset 1.489 + mOffset = offset + bytesRead; 1.490 + 1.491 + // If we've parsed lots of data and we still have nothing, just give up. 1.492 + // We don't count ID3 headers towards the skipped bytes count, as MP3 files 1.493 + // can have massive ID3 sections. 1.494 + if (!mID3Parser.IsParsed() && mMP3Offset < 0 && 1.495 + mOffset - mTotalID3Size > MAX_SKIPPED_BYTES) { 1.496 + mIsMP3 = NOT_MP3; 1.497 + } 1.498 +} 1.499 + 1.500 +int64_t MP3FrameParser::GetDuration() 1.501 +{ 1.502 + MutexAutoLock mon(mLock); 1.503 + 1.504 + if (!ParsedHeaders() || !mSamplesPerSecond) { 1.505 + // Not a single frame decoded yet. 1.506 + return -1; 1.507 + } 1.508 + 1.509 + MOZ_ASSERT(mFrameCount > 0 && mTotalFrameSize > 0, 1.510 + "Frame parser should have seen at least one MP3 frame of positive length."); 1.511 + 1.512 + if (!mFrameCount || !mTotalFrameSize) { 1.513 + // This should never happen. 1.514 + return -1; 1.515 + } 1.516 + 1.517 + double frames; 1.518 + if (mNumFrames < 0) { 1.519 + // Estimate the number of frames in the stream based on the average frame 1.520 + // size and the length of the MP3 file. 1.521 + double frameSize = (double)mTotalFrameSize / mFrameCount; 1.522 + frames = (double)(mLength - mMP3Offset) / frameSize; 1.523 + } else { 1.524 + // We know the exact number of frames from the VBR header. 1.525 + frames = mNumFrames; 1.526 + } 1.527 + 1.528 + // The duration of each frame is constant over a given stream. 1.529 + double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond; 1.530 + 1.531 + return frames * usPerFrame; 1.532 +} 1.533 + 1.534 +int64_t MP3FrameParser::GetMP3Offset() 1.535 +{ 1.536 + MutexAutoLock mon(mLock); 1.537 + return mMP3Offset; 1.538 +} 1.539 + 1.540 +bool MP3FrameParser::ParsedHeaders() 1.541 +{ 1.542 + // We have seen both the beginning and the end of the first MP3 frame in the 1.543 + // stream. 1.544 + return mMP3Offset > -1 && mFirstFrameEnd < 0; 1.545 +} 1.546 + 1.547 +bool MP3FrameParser::HasExactDuration() 1.548 +{ 1.549 + return ParsedHeaders() && mNumFrames > -1; 1.550 +} 1.551 + 1.552 +bool MP3FrameParser::NeedsData() 1.553 +{ 1.554 + // If we don't know the duration exactly then either: 1.555 + // - we're still waiting for a VBR header; or 1.556 + // - we look at all frames to constantly update our duration estimate. 1.557 + return IsMP3() && !HasExactDuration(); 1.558 +} 1.559 + 1.560 +}