content/media/MP3FrameParser.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include <algorithm>
michael@0 8
michael@0 9 #include "nsMemory.h"
michael@0 10 #include "MP3FrameParser.h"
michael@0 11 #include "VideoUtils.h"
michael@0 12
michael@0 13
michael@0 14 #define FROM_BIG_ENDIAN(X) ((uint32_t)((uint8_t)(X)[0] << 24 | (uint8_t)(X)[1] << 16 | \
michael@0 15 (uint8_t)(X)[2] << 8 | (uint8_t)(X)[3]))
michael@0 16
michael@0 17
michael@0 18 namespace mozilla {
michael@0 19
michael@0 20 /*
michael@0 21 * Following code taken from http://www.hydrogenaudio.org/forums/index.php?showtopic=85125
michael@0 22 * with permission from the author, Nick Wallette <sirnickity@gmail.com>.
michael@0 23 */
michael@0 24
michael@0 25 /* BEGIN shameless copy and paste */
michael@0 26
michael@0 27 // Bitrates - use [version][layer][bitrate]
michael@0 28 const uint16_t mpeg_bitrates[4][4][16] = {
michael@0 29 { // Version 2.5
michael@0 30 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
michael@0 31 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3
michael@0 32 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2
michael@0 33 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1
michael@0 34 },
michael@0 35 { // Reserved
michael@0 36 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
michael@0 37 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
michael@0 38 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
michael@0 39 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid
michael@0 40 },
michael@0 41 { // Version 2
michael@0 42 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
michael@0 43 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3
michael@0 44 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2
michael@0 45 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1
michael@0 46 },
michael@0 47 { // Version 1
michael@0 48 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
michael@0 49 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
michael@0 50 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
michael@0 51 { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
michael@0 52 }
michael@0 53 };
michael@0 54
michael@0 55 // Sample rates - use [version][srate]
michael@0 56 const uint16_t mpeg_srates[4][4] = {
michael@0 57 { 11025, 12000, 8000, 0 }, // MPEG 2.5
michael@0 58 { 0, 0, 0, 0 }, // Reserved
michael@0 59 { 22050, 24000, 16000, 0 }, // MPEG 2
michael@0 60 { 44100, 48000, 32000, 0 } // MPEG 1
michael@0 61 };
michael@0 62
michael@0 63 // Samples per frame - use [version][layer]
michael@0 64 const uint16_t mpeg_frame_samples[4][4] = {
michael@0 65 // Rsvd 3 2 1 < Layer v Version
michael@0 66 { 0, 576, 1152, 384 }, // 2.5
michael@0 67 { 0, 0, 0, 0 }, // Reserved
michael@0 68 { 0, 576, 1152, 384 }, // 2
michael@0 69 { 0, 1152, 1152, 384 } // 1
michael@0 70 };
michael@0 71
michael@0 72 // Slot size (MPEG unit of measurement) - use [layer]
michael@0 73 const uint8_t mpeg_slot_size[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1
michael@0 74
michael@0 75 uint16_t
michael@0 76 MP3Frame::CalculateLength()
michael@0 77 {
michael@0 78 // Lookup real values of these fields
michael@0 79 uint32_t bitrate = mpeg_bitrates[mVersion][mLayer][mBitrate] * 1000;
michael@0 80 uint32_t samprate = mpeg_srates[mVersion][mSampleRate];
michael@0 81 uint16_t samples = mpeg_frame_samples[mVersion][mLayer];
michael@0 82 uint8_t slot_size = mpeg_slot_size[mLayer];
michael@0 83
michael@0 84 // In-between calculations
michael@0 85 float bps = (float)samples / 8.0;
michael@0 86 float fsize = ( (bps * (float)bitrate) / (float)samprate )
michael@0 87 + ( (mPad) ? slot_size : 0 );
michael@0 88
michael@0 89 // Frame sizes are truncated integers
michael@0 90 return (uint16_t)fsize;
michael@0 91 }
michael@0 92
michael@0 93 /* END shameless copy and paste */
michael@0 94
michael@0 95
michael@0 96 /** MP3Parser methods **/
michael@0 97
michael@0 98 MP3Parser::MP3Parser()
michael@0 99 : mCurrentChar(0)
michael@0 100 { }
michael@0 101
michael@0 102 void
michael@0 103 MP3Parser::Reset()
michael@0 104 {
michael@0 105 mCurrentChar = 0;
michael@0 106 }
michael@0 107
michael@0 108 uint16_t
michael@0 109 MP3Parser::ParseFrameLength(uint8_t ch)
michael@0 110 {
michael@0 111 mData.mRaw[mCurrentChar] = ch;
michael@0 112
michael@0 113 MP3Frame &frame = mData.mFrame;
michael@0 114
michael@0 115 // Validate MP3 header as we read. We can't mistake the start of an MP3 frame
michael@0 116 // for the middle of another frame due to the sync byte at the beginning
michael@0 117 // of the frame.
michael@0 118
michael@0 119 // The only valid position for an all-high byte is the sync byte at the
michael@0 120 // beginning of the frame.
michael@0 121 if (ch == 0xff) {
michael@0 122 mCurrentChar = 0;
michael@0 123 }
michael@0 124
michael@0 125 // Make sure the current byte is valid in context. If not, reset the parser.
michael@0 126 if (mCurrentChar == 2) {
michael@0 127 if (frame.mBitrate == 0x0f) {
michael@0 128 goto fail;
michael@0 129 }
michael@0 130 } else if (mCurrentChar == 1) {
michael@0 131 if (frame.mSync2 != 0x07
michael@0 132 || frame.mVersion == 0x01
michael@0 133 || frame.mLayer == 0x00) {
michael@0 134 goto fail;
michael@0 135 }
michael@0 136 }
michael@0 137
michael@0 138 // The only valid character at the beginning of the header is 0xff. Fail if
michael@0 139 // it's different.
michael@0 140 if (mCurrentChar == 0 && frame.mSync1 != 0xff) {
michael@0 141 // Couldn't find the sync byte. Fail.
michael@0 142 return 0;
michael@0 143 }
michael@0 144
michael@0 145 mCurrentChar++;
michael@0 146 MOZ_ASSERT(mCurrentChar <= sizeof(MP3Frame));
michael@0 147
michael@0 148 // Don't have a full header yet.
michael@0 149 if (mCurrentChar < sizeof(MP3Frame)) {
michael@0 150 return 0;
michael@0 151 }
michael@0 152
michael@0 153 // Woo, valid header. Return the length.
michael@0 154 mCurrentChar = 0;
michael@0 155 return frame.CalculateLength();
michael@0 156
michael@0 157 fail:
michael@0 158 Reset();
michael@0 159 return 0;
michael@0 160 }
michael@0 161
michael@0 162 uint32_t
michael@0 163 MP3Parser::GetSampleRate()
michael@0 164 {
michael@0 165 MP3Frame &frame = mData.mFrame;
michael@0 166 return mpeg_srates[frame.mVersion][frame.mSampleRate];
michael@0 167 }
michael@0 168
michael@0 169 uint32_t
michael@0 170 MP3Parser::GetSamplesPerFrame()
michael@0 171 {
michael@0 172 MP3Frame &frame = mData.mFrame;
michael@0 173 return mpeg_frame_samples[frame.mVersion][frame.mLayer];
michael@0 174 }
michael@0 175
michael@0 176
michael@0 177 /** ID3Parser methods **/
michael@0 178
michael@0 179 const char sID3Head[3] = { 'I', 'D', '3' };
michael@0 180 const uint32_t ID3_HEADER_LENGTH = 10;
michael@0 181
michael@0 182 ID3Parser::ID3Parser()
michael@0 183 : mCurrentChar(0)
michael@0 184 , mHeaderLength(0)
michael@0 185 { }
michael@0 186
michael@0 187 void
michael@0 188 ID3Parser::Reset()
michael@0 189 {
michael@0 190 mCurrentChar = mHeaderLength = 0;
michael@0 191 }
michael@0 192
michael@0 193 bool
michael@0 194 ID3Parser::ParseChar(char ch)
michael@0 195 {
michael@0 196 // First three bytes of an ID3v2 header must match the string "ID3".
michael@0 197 if (mCurrentChar < sizeof(sID3Head) / sizeof(*sID3Head)
michael@0 198 && ch != sID3Head[mCurrentChar]) {
michael@0 199 goto fail;
michael@0 200 }
michael@0 201
michael@0 202 // The last four bytes of the header is a 28-bit unsigned integer with the
michael@0 203 // high bit of each byte unset.
michael@0 204 if (mCurrentChar >= 6 && mCurrentChar < ID3_HEADER_LENGTH) {
michael@0 205 if (ch & 0x80) {
michael@0 206 goto fail;
michael@0 207 } else {
michael@0 208 mHeaderLength <<= 7;
michael@0 209 mHeaderLength |= ch;
michael@0 210 }
michael@0 211 }
michael@0 212
michael@0 213 mCurrentChar++;
michael@0 214
michael@0 215 return IsParsed();
michael@0 216
michael@0 217 fail:
michael@0 218 Reset();
michael@0 219 return false;
michael@0 220 }
michael@0 221
michael@0 222 bool
michael@0 223 ID3Parser::IsParsed() const
michael@0 224 {
michael@0 225 return mCurrentChar >= ID3_HEADER_LENGTH;
michael@0 226 }
michael@0 227
michael@0 228 uint32_t
michael@0 229 ID3Parser::GetHeaderLength() const
michael@0 230 {
michael@0 231 MOZ_ASSERT(IsParsed(),
michael@0 232 "Queried length of ID3 header before parsing finished.");
michael@0 233 return mHeaderLength;
michael@0 234 }
michael@0 235
michael@0 236
michael@0 237 /** VBR header helper stuff **/
michael@0 238
michael@0 239 // Helper function to find a VBR header in an MP3 frame.
michael@0 240 // Based on information from
michael@0 241 // http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
michael@0 242
michael@0 243 const uint32_t VBRI_TAG = FROM_BIG_ENDIAN("VBRI");
michael@0 244 const uint32_t VBRI_OFFSET = 32 - sizeof(MP3Frame);
michael@0 245 const uint32_t VBRI_FRAME_COUNT_OFFSET = VBRI_OFFSET + 14;
michael@0 246 const uint32_t VBRI_MIN_FRAME_SIZE = VBRI_OFFSET + 26;
michael@0 247
michael@0 248 const uint32_t XING_TAG = FROM_BIG_ENDIAN("Xing");
michael@0 249 enum XingFlags {
michael@0 250 XING_HAS_NUM_FRAMES = 0x01,
michael@0 251 XING_HAS_NUM_BYTES = 0x02,
michael@0 252 XING_HAS_TOC = 0x04,
michael@0 253 XING_HAS_VBR_SCALE = 0x08
michael@0 254 };
michael@0 255
michael@0 256 static int64_t
michael@0 257 ParseXing(const char *aBuffer)
michael@0 258 {
michael@0 259 uint32_t flags = FROM_BIG_ENDIAN(aBuffer + 4);
michael@0 260
michael@0 261 if (!(flags & XING_HAS_NUM_FRAMES)) {
michael@0 262 NS_WARNING("VBR file without frame count. Duration estimation likely to "
michael@0 263 "be totally wrong.");
michael@0 264 return -1;
michael@0 265 }
michael@0 266
michael@0 267 int64_t numFrames = -1;
michael@0 268 if (flags & XING_HAS_NUM_FRAMES) {
michael@0 269 numFrames = FROM_BIG_ENDIAN(aBuffer + 8);
michael@0 270 }
michael@0 271
michael@0 272 return numFrames;
michael@0 273 }
michael@0 274
michael@0 275 static int64_t
michael@0 276 FindNumVBRFrames(const nsAutoCString& aFrame)
michael@0 277 {
michael@0 278 const char *buffer = aFrame.get();
michael@0 279 const char *bufferEnd = aFrame.get() + aFrame.Length();
michael@0 280
michael@0 281 // VBRI header is nice and well-defined; let's try to find that first.
michael@0 282 if (aFrame.Length() > VBRI_MIN_FRAME_SIZE &&
michael@0 283 FROM_BIG_ENDIAN(buffer + VBRI_OFFSET) == VBRI_TAG) {
michael@0 284 return FROM_BIG_ENDIAN(buffer + VBRI_FRAME_COUNT_OFFSET);
michael@0 285 }
michael@0 286
michael@0 287 // We have to search for the Xing header as its position can change.
michael@0 288 for (; buffer + sizeof(XING_TAG) < bufferEnd; buffer++) {
michael@0 289 if (FROM_BIG_ENDIAN(buffer) == XING_TAG) {
michael@0 290 return ParseXing(buffer);
michael@0 291 }
michael@0 292 }
michael@0 293
michael@0 294 return -1;
michael@0 295 }
michael@0 296
michael@0 297
michael@0 298 /** MP3FrameParser methods **/
michael@0 299
michael@0 300 // Some MP3's have large ID3v2 tags, up to 150KB, so we allow lots of
michael@0 301 // skipped bytes to be read, just in case, before we give up and assume
michael@0 302 // we're not parsing an MP3 stream.
michael@0 303 static const uint32_t MAX_SKIPPED_BYTES = 4096;
michael@0 304
michael@0 305 // The number of audio samples per MP3 frame. This is constant over all MP3
michael@0 306 // streams. With this constant, the stream's sample rate, and an estimated
michael@0 307 // number of frames in the stream, we can estimate the stream's duration
michael@0 308 // fairly accurately.
michael@0 309 static const uint32_t SAMPLES_PER_FRAME = 1152;
michael@0 310
michael@0 311 enum {
michael@0 312 MP3_HEADER_LENGTH = 4,
michael@0 313 };
michael@0 314
michael@0 315 MP3FrameParser::MP3FrameParser(int64_t aLength)
michael@0 316 : mLock("MP3FrameParser.mLock"),
michael@0 317 mTotalID3Size(0),
michael@0 318 mTotalFrameSize(0),
michael@0 319 mFrameCount(0),
michael@0 320 mOffset(0),
michael@0 321 mLength(aLength),
michael@0 322 mMP3Offset(-1),
michael@0 323 mSamplesPerSecond(0),
michael@0 324 mFirstFrameEnd(-1),
michael@0 325 mIsMP3(MAYBE_MP3)
michael@0 326 { }
michael@0 327
michael@0 328 nsresult MP3FrameParser::ParseBuffer(const uint8_t* aBuffer,
michael@0 329 uint32_t aLength,
michael@0 330 int64_t aStreamOffset,
michael@0 331 uint32_t* aOutBytesRead)
michael@0 332 {
michael@0 333 // Iterate forwards over the buffer, looking for ID3 tag, or MP3
michael@0 334 // Frame headers.
michael@0 335 const uint8_t *buffer = aBuffer;
michael@0 336 const uint8_t *bufferEnd = aBuffer + aLength;
michael@0 337
michael@0 338 // If we haven't found any MP3 frame data yet, there might be ID3 headers
michael@0 339 // we can skip over.
michael@0 340 if (mMP3Offset < 0) {
michael@0 341 for (const uint8_t *ch = buffer; ch < bufferEnd; ch++) {
michael@0 342 if (mID3Parser.ParseChar(*ch)) {
michael@0 343 // Found an ID3 header. We don't care about the body of the header, so
michael@0 344 // just skip past.
michael@0 345 buffer = ch + mID3Parser.GetHeaderLength() - (ID3_HEADER_LENGTH - 1);
michael@0 346 ch = buffer;
michael@0 347
michael@0 348 mTotalID3Size += mID3Parser.GetHeaderLength();
michael@0 349
michael@0 350 // Yes, this is an MP3!
michael@0 351 mIsMP3 = DEFINITELY_MP3;
michael@0 352
michael@0 353 mID3Parser.Reset();
michael@0 354 }
michael@0 355 }
michael@0 356 }
michael@0 357
michael@0 358 // The first MP3 frame in a variable bitrate stream can contain metadata
michael@0 359 // for duration estimation and seeking, so we buffer that first frame here.
michael@0 360 if (aStreamOffset < mFirstFrameEnd) {
michael@0 361 uint64_t copyLen = std::min((int64_t)aLength, mFirstFrameEnd - aStreamOffset);
michael@0 362 mFirstFrame.Append((const char *)buffer, copyLen);
michael@0 363 buffer += copyLen;
michael@0 364 }
michael@0 365
michael@0 366 while (buffer < bufferEnd) {
michael@0 367 uint16_t frameLen = mMP3Parser.ParseFrameLength(*buffer);
michael@0 368
michael@0 369 if (frameLen) {
michael@0 370 // We've found an MP3 frame!
michael@0 371 // This is the first frame (and the only one we'll bother parsing), so:
michael@0 372 // * Mark this stream as MP3;
michael@0 373 // * Store the offset at which the MP3 data started; and
michael@0 374 // * Start buffering the frame, as it might contain handy metadata.
michael@0 375
michael@0 376 // We're now sure this is an MP3 stream.
michael@0 377 mIsMP3 = DEFINITELY_MP3;
michael@0 378
michael@0 379 // We need to know these to convert the number of frames in the stream
michael@0 380 // to the length of the stream in seconds.
michael@0 381 mSamplesPerSecond = mMP3Parser.GetSampleRate();
michael@0 382 mSamplesPerFrame = mMP3Parser.GetSamplesPerFrame();
michael@0 383
michael@0 384 // If the stream has a constant bitrate, we should only need the length
michael@0 385 // of the first frame and the length (in bytes) of the stream to
michael@0 386 // estimate the length (in seconds).
michael@0 387 mTotalFrameSize += frameLen;
michael@0 388 mFrameCount++;
michael@0 389
michael@0 390 // If |mMP3Offset| isn't set then this is the first MP3 frame we have
michael@0 391 // seen in the stream, which is useful for duration estimation.
michael@0 392 if (mMP3Offset > -1) {
michael@0 393 uint16_t skip = frameLen - sizeof(MP3Frame);
michael@0 394 buffer += skip ? skip : 1;
michael@0 395 continue;
michael@0 396 }
michael@0 397
michael@0 398 // Remember the offset of the MP3 stream.
michael@0 399 // We're at the last byte of an MP3Frame, so MP3 data started
michael@0 400 // sizeof(MP3Frame) - 1 bytes ago.
michael@0 401 mMP3Offset = aStreamOffset
michael@0 402 + (buffer - aBuffer)
michael@0 403 - (sizeof(MP3Frame) - 1);
michael@0 404
michael@0 405 buffer++;
michael@0 406
michael@0 407 // If the stream has a variable bitrate, the first frame has metadata
michael@0 408 // we need for duration estimation and seeking. Start buffering it so we
michael@0 409 // can parse it later.
michael@0 410 mFirstFrameEnd = mMP3Offset + frameLen;
michael@0 411 uint64_t currOffset = buffer - aBuffer + aStreamOffset;
michael@0 412 uint64_t copyLen = std::min(mFirstFrameEnd - currOffset,
michael@0 413 (uint64_t)(bufferEnd - buffer));
michael@0 414 mFirstFrame.Append((const char *)buffer, copyLen);
michael@0 415
michael@0 416 buffer += copyLen;
michael@0 417
michael@0 418 } else {
michael@0 419 // Nothing to see here. Move along.
michael@0 420 buffer++;
michael@0 421 }
michael@0 422 }
michael@0 423
michael@0 424 *aOutBytesRead = buffer - aBuffer;
michael@0 425
michael@0 426 if (mFirstFrameEnd > -1 && mFirstFrameEnd <= aStreamOffset + buffer - aBuffer) {
michael@0 427 // We have our whole first frame. Try to find a VBR header.
michael@0 428 mNumFrames = FindNumVBRFrames(mFirstFrame);
michael@0 429 mFirstFrameEnd = -1;
michael@0 430 }
michael@0 431
michael@0 432 return NS_OK;
michael@0 433 }
michael@0 434
michael@0 435 void MP3FrameParser::Parse(const char* aBuffer, uint32_t aLength, uint64_t aOffset)
michael@0 436 {
michael@0 437 MutexAutoLock mon(mLock);
michael@0 438
michael@0 439 if (HasExactDuration()) {
michael@0 440 // We know the duration; nothing to do here.
michael@0 441 return;
michael@0 442 }
michael@0 443
michael@0 444 const uint8_t* buffer = reinterpret_cast<const uint8_t*>(aBuffer);
michael@0 445 int32_t length = aLength;
michael@0 446 uint64_t offset = aOffset;
michael@0 447
michael@0 448 // Got some data we have seen already. Skip forward to what we need.
michael@0 449 if (aOffset < mOffset) {
michael@0 450 buffer += mOffset - aOffset;
michael@0 451 length -= mOffset - aOffset;
michael@0 452 offset = mOffset;
michael@0 453
michael@0 454 if (length <= 0) {
michael@0 455 return;
michael@0 456 }
michael@0 457 }
michael@0 458
michael@0 459 // If there is a discontinuity in the input stream, reset the state of the
michael@0 460 // parsers so we don't get any partial headers.
michael@0 461 if (mOffset < aOffset) {
michael@0 462 if (!mID3Parser.IsParsed()) {
michael@0 463 // Only reset this if it hasn't finished yet.
michael@0 464 mID3Parser.Reset();
michael@0 465 }
michael@0 466
michael@0 467 if (mFirstFrameEnd > -1) {
michael@0 468 NS_WARNING("Discontinuity in input while buffering first frame.");
michael@0 469 mFirstFrameEnd = -1;
michael@0 470 }
michael@0 471
michael@0 472 mMP3Parser.Reset();
michael@0 473 }
michael@0 474
michael@0 475 uint32_t bytesRead = 0;
michael@0 476 if (NS_FAILED(ParseBuffer(buffer,
michael@0 477 length,
michael@0 478 offset,
michael@0 479 &bytesRead))) {
michael@0 480 return;
michael@0 481 }
michael@0 482
michael@0 483 MOZ_ASSERT(length <= (int)bytesRead, "All bytes should have been consumed");
michael@0 484
michael@0 485 // Update next data offset
michael@0 486 mOffset = offset + bytesRead;
michael@0 487
michael@0 488 // If we've parsed lots of data and we still have nothing, just give up.
michael@0 489 // We don't count ID3 headers towards the skipped bytes count, as MP3 files
michael@0 490 // can have massive ID3 sections.
michael@0 491 if (!mID3Parser.IsParsed() && mMP3Offset < 0 &&
michael@0 492 mOffset - mTotalID3Size > MAX_SKIPPED_BYTES) {
michael@0 493 mIsMP3 = NOT_MP3;
michael@0 494 }
michael@0 495 }
michael@0 496
michael@0 497 int64_t MP3FrameParser::GetDuration()
michael@0 498 {
michael@0 499 MutexAutoLock mon(mLock);
michael@0 500
michael@0 501 if (!ParsedHeaders() || !mSamplesPerSecond) {
michael@0 502 // Not a single frame decoded yet.
michael@0 503 return -1;
michael@0 504 }
michael@0 505
michael@0 506 MOZ_ASSERT(mFrameCount > 0 && mTotalFrameSize > 0,
michael@0 507 "Frame parser should have seen at least one MP3 frame of positive length.");
michael@0 508
michael@0 509 if (!mFrameCount || !mTotalFrameSize) {
michael@0 510 // This should never happen.
michael@0 511 return -1;
michael@0 512 }
michael@0 513
michael@0 514 double frames;
michael@0 515 if (mNumFrames < 0) {
michael@0 516 // Estimate the number of frames in the stream based on the average frame
michael@0 517 // size and the length of the MP3 file.
michael@0 518 double frameSize = (double)mTotalFrameSize / mFrameCount;
michael@0 519 frames = (double)(mLength - mMP3Offset) / frameSize;
michael@0 520 } else {
michael@0 521 // We know the exact number of frames from the VBR header.
michael@0 522 frames = mNumFrames;
michael@0 523 }
michael@0 524
michael@0 525 // The duration of each frame is constant over a given stream.
michael@0 526 double usPerFrame = USECS_PER_S * mSamplesPerFrame / mSamplesPerSecond;
michael@0 527
michael@0 528 return frames * usPerFrame;
michael@0 529 }
michael@0 530
michael@0 531 int64_t MP3FrameParser::GetMP3Offset()
michael@0 532 {
michael@0 533 MutexAutoLock mon(mLock);
michael@0 534 return mMP3Offset;
michael@0 535 }
michael@0 536
michael@0 537 bool MP3FrameParser::ParsedHeaders()
michael@0 538 {
michael@0 539 // We have seen both the beginning and the end of the first MP3 frame in the
michael@0 540 // stream.
michael@0 541 return mMP3Offset > -1 && mFirstFrameEnd < 0;
michael@0 542 }
michael@0 543
michael@0 544 bool MP3FrameParser::HasExactDuration()
michael@0 545 {
michael@0 546 return ParsedHeaders() && mNumFrames > -1;
michael@0 547 }
michael@0 548
michael@0 549 bool MP3FrameParser::NeedsData()
michael@0 550 {
michael@0 551 // If we don't know the duration exactly then either:
michael@0 552 // - we're still waiting for a VBR header; or
michael@0 553 // - we look at all frames to constantly update our duration estimate.
michael@0 554 return IsMP3() && !HasExactDuration();
michael@0 555 }
michael@0 556
michael@0 557 }

mercurial