MP3FrameParser.cpp (25447B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "MP3FrameParser.h" 8 9 #include <inttypes.h> 10 11 #include <algorithm> 12 13 #include "MediaDataDemuxer.h" 14 #include "TimeUnits.h" 15 #include "VideoUtils.h" 16 #include "mozilla/Assertions.h" 17 #include "mozilla/EndianUtils.h" 18 #include "mozilla/ScopeExit.h" 19 #include "mozilla/Try.h" 20 21 #define MP3LOG(msg, ...) \ 22 MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__)) 23 #define MP3LOGV(msg, ...) \ 24 MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, \ 25 ("MP3Demuxer " msg, ##__VA_ARGS__)) 26 27 namespace mozilla { 28 29 // FrameParser 30 31 namespace frame_header { 32 // FrameHeader mRaw byte offsets. 33 static const int SYNC1 = 0; 34 static const int SYNC2_VERSION_LAYER_PROTECTION = 1; 35 static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2; 36 static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3; 37 } // namespace frame_header 38 39 FrameParser::FrameParser() = default; 40 41 void FrameParser::Reset() { 42 mID3Parser.Reset(); 43 mFrame.Reset(); 44 } 45 46 void FrameParser::ResetFrameData() { 47 mFrame.Reset(); 48 mFirstFrame.Reset(); 49 mPrevFrame.Reset(); 50 } 51 52 void FrameParser::EndFrameSession() { 53 if (!mID3Parser.Header().IsValid()) { 54 // Reset ID3 tags only if we have not parsed a valid ID3 header yet. 55 mID3Parser.Reset(); 56 } 57 mPrevFrame = mFrame; 58 mFrame.Reset(); 59 } 60 61 const FrameParser::Frame& FrameParser::CurrentFrame() const { return mFrame; } 62 63 const FrameParser::Frame& FrameParser::PrevFrame() const { return mPrevFrame; } 64 65 const FrameParser::Frame& FrameParser::FirstFrame() const { 66 return mFirstFrame; 67 } 68 69 const ID3Parser::ID3Header& FrameParser::ID3Header() const { 70 return mID3Parser.Header(); 71 } 72 73 uint32_t FrameParser::TotalID3HeaderSize() const { 74 uint32_t ID3v1Size = 0; 75 if (mID3v1MetadataFound) { 76 ID3v1Size = 128; 77 } 78 return ID3v1Size + mID3Parser.TotalHeadersSize(); 79 } 80 81 const FrameParser::VBRHeader& FrameParser::VBRInfo() const { 82 return mVBRHeader; 83 } 84 85 Result<bool, nsresult> FrameParser::Parse(BufferReader* aReader, 86 uint32_t* aBytesToSkip) { 87 MOZ_ASSERT(aReader && aBytesToSkip); 88 *aBytesToSkip = 0; 89 90 if (ID3Parser::IsBufferStartingWithID3v1Tag(aReader)) { 91 // This is usually at the end of the file, and is always 128 bytes, that 92 // can simply be skipped. 93 aReader->Read(128); 94 *aBytesToSkip = 128; 95 mID3v1MetadataFound = true; 96 MP3LOGV("ID3v1 tag detected, skipping 128 bytes past the current buffer"); 97 return false; 98 } 99 100 if (ID3Parser::IsBufferStartingWithID3Tag(aReader) && !mFirstFrame.Length()) { 101 // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin. 102 // ID3v1 tags may only be at file end. 103 const size_t prevReaderOffset = aReader->Offset(); 104 const uint32_t tagSize = mID3Parser.Parse(aReader); 105 if (!!tagSize) { 106 // ID3 tag found, skip past it. 107 const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE; 108 109 if (skipSize > aReader->Remaining()) { 110 // Skipping across the ID3v2 tag would take us past the end of the 111 // buffer, therefore we return immediately and let the calling function 112 // handle skipping the rest of the tag. 113 MP3LOGV( 114 "ID3v2 tag detected, size=%d," 115 " needing to skip %zu bytes past the current buffer", 116 tagSize, skipSize - aReader->Remaining()); 117 *aBytesToSkip = skipSize - aReader->Remaining(); 118 return false; 119 } 120 MP3LOGV("ID3v2 tag detected, size=%d", tagSize); 121 aReader->Read(skipSize); 122 } else { 123 // No ID3v2 tag found, rewinding reader in order to search for a MPEG 124 // frame header. 125 aReader->Seek(prevReaderOffset); 126 } 127 } 128 129 for (auto res = aReader->ReadU8(); 130 res.isOk() && !mFrame.ParseNext(res.unwrap()); res = aReader->ReadU8()) { 131 } 132 133 if (mFrame.Length()) { 134 // MP3 frame found. 135 if (!mFirstFrame.Length()) { 136 mFirstFrame = mFrame; 137 } 138 // Indicate success. 139 return true; 140 } 141 return false; 142 } 143 144 // FrameParser::Header 145 146 FrameParser::FrameHeader::FrameHeader() { Reset(); } 147 148 uint8_t FrameParser::FrameHeader::Sync1() const { 149 return mRaw[frame_header::SYNC1]; 150 } 151 152 uint8_t FrameParser::FrameHeader::Sync2() const { 153 return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5; 154 } 155 156 uint8_t FrameParser::FrameHeader::RawVersion() const { 157 return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3; 158 } 159 160 uint8_t FrameParser::FrameHeader::RawLayer() const { 161 return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1; 162 } 163 164 uint8_t FrameParser::FrameHeader::RawProtection() const { 165 return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6; 166 } 167 168 uint8_t FrameParser::FrameHeader::RawBitrate() const { 169 return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4; 170 } 171 172 uint8_t FrameParser::FrameHeader::RawSampleRate() const { 173 return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2; 174 } 175 176 uint8_t FrameParser::FrameHeader::Padding() const { 177 return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1; 178 } 179 180 uint8_t FrameParser::FrameHeader::Private() const { 181 return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE]; 182 } 183 184 uint8_t FrameParser::FrameHeader::RawChannelMode() const { 185 return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6; 186 } 187 188 uint32_t FrameParser::FrameHeader::Layer() const { 189 static const uint8_t LAYERS[4] = {0, 3, 2, 1}; 190 191 return LAYERS[RawLayer()]; 192 } 193 194 uint32_t FrameParser::FrameHeader::SampleRate() const { 195 // Sample rates - use [version][srate] 196 static const uint16_t SAMPLE_RATE[4][4] = { 197 // clang-format off 198 { 11025, 12000, 8000, 0 }, // MPEG 2.5 199 { 0, 0, 0, 0 }, // Reserved 200 { 22050, 24000, 16000, 0 }, // MPEG 2 201 { 44100, 48000, 32000, 0 } // MPEG 1 202 // clang-format on 203 }; 204 205 return SAMPLE_RATE[RawVersion()][RawSampleRate()]; 206 } 207 208 uint32_t FrameParser::FrameHeader::Channels() const { 209 // 3 is single channel (mono), any other value is some variant of dual 210 // channel. 211 return RawChannelMode() == 3 ? 1 : 2; 212 } 213 214 uint32_t FrameParser::FrameHeader::SamplesPerFrame() const { 215 // Samples per frame - use [version][layer] 216 static const uint16_t FRAME_SAMPLE[4][4] = { 217 // clang-format off 218 // Layer 3 2 1 Version 219 { 0, 576, 1152, 384 }, // 2.5 220 { 0, 0, 0, 0 }, // Reserved 221 { 0, 576, 1152, 384 }, // 2 222 { 0, 1152, 1152, 384 } // 1 223 // clang-format on 224 }; 225 226 return FRAME_SAMPLE[RawVersion()][RawLayer()]; 227 } 228 229 uint32_t FrameParser::FrameHeader::Bitrate() const { 230 // Bitrates - use [version][layer][bitrate] 231 static const uint16_t BITRATE[4][4][16] = { 232 // clang-format off 233 { // Version 2.5 234 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved 235 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 236 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 237 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 238 }, 239 { // Reserved 240 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid 241 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid 242 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid 243 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid 244 }, 245 { // Version 2 246 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved 247 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3 248 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2 249 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1 250 }, 251 { // Version 1 252 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved 253 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3 254 { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2 255 { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1 256 } 257 // clang-format on 258 }; 259 260 return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()]; 261 } 262 263 uint32_t FrameParser::FrameHeader::SlotSize() const { 264 // Slot size (MPEG unit of measurement) - use [layer] 265 static const uint8_t SLOT_SIZE[4] = {0, 1, 1, 4}; // Rsvd, 3, 2, 1 266 267 return SLOT_SIZE[RawLayer()]; 268 } 269 270 bool FrameParser::FrameHeader::ParseNext(uint8_t c) { 271 if (!Update(c)) { 272 Reset(); 273 if (!Update(c)) { 274 Reset(); 275 } 276 } 277 return IsValid(); 278 } 279 280 bool FrameParser::ID3v1MetadataFound() const { return mID3v1MetadataFound; } 281 282 bool FrameParser::FrameHeader::IsValid(int aPos) const { 283 if (aPos >= SIZE) { 284 return true; 285 } 286 if (aPos == frame_header::SYNC1) { 287 return Sync1() == 0xFF; 288 } 289 if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) { 290 return Sync2() == 7 && RawVersion() != 1 && Layer() == 3; 291 } 292 if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) { 293 return RawBitrate() != 0xF && RawBitrate() != 0 && RawSampleRate() != 3; 294 } 295 return true; 296 } 297 298 bool FrameParser::FrameHeader::IsValid() const { return mPos >= SIZE; } 299 300 void FrameParser::FrameHeader::Reset() { mPos = 0; } 301 302 bool FrameParser::FrameHeader::Update(uint8_t c) { 303 if (mPos < SIZE) { 304 mRaw[mPos] = c; 305 } 306 return IsValid(mPos++); 307 } 308 309 // FrameParser::VBRHeader 310 311 namespace vbr_header { 312 static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"}; 313 static const uint32_t TOC_SIZE = 100; 314 } // namespace vbr_header 315 316 FrameParser::VBRHeader::VBRHeader() : mType(NONE) {} 317 318 FrameParser::VBRHeader::VBRHeaderType FrameParser::VBRHeader::Type() const { 319 return mType; 320 } 321 322 const Maybe<uint32_t>& FrameParser::VBRHeader::NumAudioFrames() const { 323 return mNumAudioFrames; 324 } 325 326 const Maybe<uint32_t>& FrameParser::VBRHeader::NumBytes() const { 327 return mNumBytes; 328 } 329 330 const Maybe<uint32_t>& FrameParser::VBRHeader::Scale() const { return mScale; } 331 332 bool FrameParser::VBRHeader::IsTOCPresent() const { 333 // This doesn't use VBRI TOC 334 return !mTOC.empty() && mType != VBRI; 335 } 336 337 bool FrameParser::VBRHeader::IsValid() const { return mType != NONE; } 338 339 bool FrameParser::VBRHeader::IsComplete() const { 340 return IsValid() && mNumAudioFrames.valueOr(0) > 0 && mNumBytes.valueOr(0) > 0 341 // We don't care about the scale for any computations here. 342 // && mScale < 101 343 ; 344 } 345 346 int64_t FrameParser::VBRHeader::Offset(media::TimeUnit aTime, 347 media::TimeUnit aDuration) const { 348 if (!IsTOCPresent()) { 349 return -1; 350 } 351 352 int64_t offset = -1; 353 if (mType == XING) { 354 // Constrain the duration percentage to [0, 99]. 355 double percent = 100. * aTime.ToSeconds() / aDuration.ToSeconds(); 356 const double durationPer = std::clamp(percent, 0., 99.); 357 double integer; 358 const double fractional = modf(durationPer, &integer); 359 size_t integerPer = AssertedCast<size_t>(integer); 360 361 MOZ_ASSERT(integerPer < mTOC.size()); 362 offset = mTOC.at(integerPer); 363 if (fractional > 0.0 && integerPer + 1 < mTOC.size()) { 364 offset += AssertedCast<int64_t>(fractional) * 365 (mTOC.at(integerPer + 1) - offset); 366 } 367 } 368 // TODO: VBRI TOC seeking 369 MP3LOG("VBRHeader::Offset (%s): %f is at byte %" PRId64 "", 370 mType == XING ? "XING" : "VBRI", aTime.ToSeconds(), offset); 371 372 return offset; 373 } 374 375 Result<bool, nsresult> FrameParser::VBRHeader::ParseXing(BufferReader* aReader, 376 size_t aFrameSize) { 377 static const uint32_t XING_TAG = BigEndian::readUint32("Xing"); 378 static const uint32_t INFO_TAG = BigEndian::readUint32("Info"); 379 static const uint32_t LAME_TAG = BigEndian::readUint32("LAME"); 380 static const uint32_t LAVC_TAG = BigEndian::readUint32("Lavc"); 381 382 enum Flags { 383 NUM_FRAMES = 0x01, 384 NUM_BYTES = 0x02, 385 TOC = 0x04, 386 VBR_SCALE = 0x08 387 }; 388 389 MOZ_ASSERT(aReader); 390 391 // Seek backward to the original position before leaving this scope. 392 const size_t prevReaderOffset = aReader->Offset(); 393 auto scopeExit = MakeScopeExit([&] { aReader->Seek(prevReaderOffset); }); 394 395 // We have to search for the Xing header as its position can change. 396 for (auto res = aReader->PeekU32(); 397 res.isOk() && res.unwrap() != XING_TAG && res.unwrap() != INFO_TAG;) { 398 aReader->Read(1); 399 res = aReader->PeekU32(); 400 } 401 402 // Skip across the VBR header ID tag. 403 MOZ_TRY(aReader->ReadU32()); 404 mType = XING; 405 406 uint32_t flags = MOZ_TRY(aReader->ReadU32()); 407 408 if (flags & NUM_FRAMES) { 409 uint32_t frames = MOZ_TRY(aReader->ReadU32()); 410 mNumAudioFrames = Some(frames); 411 } 412 if (flags & NUM_BYTES) { 413 uint32_t bytes = MOZ_TRY(aReader->ReadU32()); 414 mNumBytes = Some(bytes); 415 } 416 if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) { 417 if (!mNumBytes) { 418 // We don't have the stream size to calculate offsets, skip the TOC. 419 aReader->Read(vbr_header::TOC_SIZE); 420 } else { 421 mTOC.clear(); 422 mTOC.reserve(vbr_header::TOC_SIZE); 423 for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) { 424 uint8_t data = MOZ_TRY(aReader->ReadU8()); 425 mTOC.push_back( 426 AssertedCast<uint32_t>(1.0f / 256.0f * data * mNumBytes.value())); 427 } 428 } 429 } 430 if (flags & VBR_SCALE) { 431 mScale = Some(MOZ_TRY(aReader->ReadU32())); 432 } 433 434 uint32_t lameOrLavcTag = MOZ_TRY(aReader->ReadU32()); 435 436 if (lameOrLavcTag == LAME_TAG || lameOrLavcTag == LAVC_TAG) { 437 // Skip 17 bytes after the LAME tag: 438 // - http://gabriel.mp3-tech.org/mp3infotag.html 439 // - 5 bytes for the encoder short version string 440 // - 1 byte for the info tag revision + VBR method 441 // - 1 byte for the lowpass filter value 442 // - 8 bytes for the ReplayGain information 443 // - 1 byte for the encoding flags + ATH Type 444 // - 1 byte for the specified bitrate if ABR, else the minimal bitrate 445 if (!aReader->Read(17)) { 446 return mozilla::Err(NS_ERROR_FAILURE); 447 } 448 449 // The encoder delay is three bytes, for two 12-bits integers are the 450 // encoder delay and the padding. 451 const uint8_t* delayPadding = aReader->Read(3); 452 if (!delayPadding) { 453 return mozilla::Err(NS_ERROR_FAILURE); 454 } 455 mEncoderDelay = 456 uint32_t(delayPadding[0]) << 4 | (delayPadding[1] & 0xf0) >> 4; 457 mEncoderPadding = uint32_t(delayPadding[1] & 0x0f) << 8 | delayPadding[2]; 458 459 constexpr uint16_t DEFAULT_DECODER_DELAY = 529; 460 mEncoderDelay += DEFAULT_DECODER_DELAY + aFrameSize; // ignore first frame. 461 mEncoderPadding -= std::min(mEncoderPadding, DEFAULT_DECODER_DELAY); 462 463 MP3LOG("VBRHeader::ParseXing: LAME encoder delay section: delay: %" PRIu16 464 " frames, padding: %" PRIu16 " frames", 465 mEncoderDelay, mEncoderPadding); 466 } 467 468 return mType == XING; 469 } 470 471 template <typename T> 472 int readAndConvertToInt(BufferReader* aReader) { 473 int value = AssertedCast<int>(aReader->ReadType<T>()); 474 return value; 475 } 476 477 Result<bool, nsresult> FrameParser::VBRHeader::ParseVBRI( 478 BufferReader* aReader) { 479 static const uint32_t TAG = BigEndian::readUint32("VBRI"); 480 static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE; 481 static const uint32_t MIN_FRAME_SIZE = OFFSET + 26; 482 483 MOZ_ASSERT(aReader); 484 // ParseVBRI assumes that the ByteReader offset points to the beginning of a 485 // frame, therefore as a simple check, we look for the presence of a frame 486 // sync at that position. 487 auto sync = aReader->PeekU16(); 488 if (sync.isOk()) { // To avoid compiler complains 'set but unused'. 489 MOZ_ASSERT((sync.unwrap() & 0xFFE0) == 0xFFE0); 490 } 491 492 // Seek backward to the original position before leaving this scope. 493 const size_t prevReaderOffset = aReader->Offset(); 494 auto scopeExit = MakeScopeExit([&] { aReader->Seek(prevReaderOffset); }); 495 496 // VBRI have a fixed relative position, so let's check for it there. 497 if (aReader->Remaining() > MIN_FRAME_SIZE) { 498 aReader->Seek(prevReaderOffset + OFFSET); 499 uint32_t tag = MOZ_TRY(aReader->ReadU32()); 500 if (tag == TAG) { 501 uint16_t vbriEncoderVersion, vbriEncoderDelay, vbriQuality; 502 uint32_t vbriBytes, vbriFrames; 503 uint16_t vbriSeekOffsetsTableSize, vbriSeekOffsetsScaleFactor, 504 vbriSeekOffsetsBytesPerEntry, vbriSeekOffsetsFramesPerEntry; 505 vbriEncoderVersion = MOZ_TRY(aReader->ReadU16()); 506 vbriEncoderDelay = MOZ_TRY(aReader->ReadU16()); 507 vbriQuality = MOZ_TRY(aReader->ReadU16()); 508 vbriBytes = MOZ_TRY(aReader->ReadU32()); 509 vbriFrames = MOZ_TRY(aReader->ReadU32()); 510 vbriSeekOffsetsTableSize = MOZ_TRY(aReader->ReadU16()); 511 vbriSeekOffsetsScaleFactor = MOZ_TRY(aReader->ReadU32()); 512 vbriSeekOffsetsBytesPerEntry = MOZ_TRY(aReader->ReadU16()); 513 vbriSeekOffsetsFramesPerEntry = MOZ_TRY(aReader->ReadU16()); 514 515 mTOC.reserve(vbriSeekOffsetsTableSize + 1); 516 517 int (*readFunc)(BufferReader*) = nullptr; 518 switch (vbriSeekOffsetsBytesPerEntry) { 519 case 1: 520 readFunc = &readAndConvertToInt<uint8_t>; 521 break; 522 case 2: 523 readFunc = &readAndConvertToInt<int16_t>; 524 break; 525 case 4: 526 readFunc = &readAndConvertToInt<int32_t>; 527 break; 528 case 8: 529 readFunc = &readAndConvertToInt<int64_t>; 530 break; 531 default: 532 MP3LOG("Unhandled vbriSeekOffsetsBytesPerEntry size of %hd", 533 vbriSeekOffsetsBytesPerEntry); 534 break; 535 } 536 for (uint32_t i = 0; readFunc && i < vbriSeekOffsetsTableSize; i++) { 537 int entry = readFunc(aReader); 538 mTOC.push_back(entry * vbriSeekOffsetsScaleFactor); 539 } 540 MP3LOG( 541 "Header::Parse found valid header: EncoderVersion=%hu " 542 "EncoderDelay=%hu " 543 "Quality=%hu " 544 "Bytes=%u " 545 "Frames=%u " 546 "SeekOffsetsTableSize=%u " 547 "SeekOffsetsScaleFactor=%hu " 548 "SeekOffsetsBytesPerEntry=%hu " 549 "SeekOffsetsFramesPerEntry=%hu", 550 vbriEncoderVersion, vbriEncoderDelay, vbriQuality, vbriBytes, 551 vbriFrames, vbriSeekOffsetsTableSize, vbriSeekOffsetsScaleFactor, 552 vbriSeekOffsetsBytesPerEntry, vbriSeekOffsetsFramesPerEntry); 553 // Adjust the number of frames so it's counted the same way as in the XING 554 // header 555 if (vbriFrames < 1) { 556 return false; 557 } 558 mNumAudioFrames = Some(vbriFrames - 1); 559 mNumBytes = Some(vbriBytes); 560 mEncoderDelay = vbriEncoderDelay; 561 mVBRISeekOffsetsFramesPerEntry = vbriSeekOffsetsFramesPerEntry; 562 MP3LOG("TOC:"); 563 for (auto entry : mTOC) { 564 MP3LOG("%" PRId64, entry); 565 } 566 567 mType = VBRI; 568 return true; 569 } 570 } 571 return false; 572 } 573 574 bool FrameParser::VBRHeader::Parse(BufferReader* aReader, size_t aFrameSize) { 575 auto res = std::make_pair(ParseVBRI(aReader), ParseXing(aReader, aFrameSize)); 576 const bool rv = (res.first.isOk() && res.first.unwrap()) || 577 (res.second.isOk() && res.second.unwrap()); 578 if (rv) { 579 MP3LOG( 580 "VBRHeader::Parse found valid VBR/CBR header: type=%s" 581 " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%zu Delay=%u", 582 vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0), 583 NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size(), mEncoderDelay); 584 } 585 return rv; 586 } 587 588 // FrameParser::Frame 589 590 void FrameParser::Frame::Reset() { mHeader.Reset(); } 591 592 uint32_t FrameParser::Frame::Length() const { 593 if (!mHeader.IsValid() || !mHeader.SampleRate()) { 594 return 0; 595 } 596 597 const uint32_t bitsPerSample = mHeader.SamplesPerFrame() / 8; 598 const uint32_t frameLen = 599 bitsPerSample * mHeader.Bitrate() / mHeader.SampleRate() + 600 mHeader.Padding() * mHeader.SlotSize(); 601 return frameLen; 602 } 603 604 bool FrameParser::Frame::ParseNext(uint8_t c) { return mHeader.ParseNext(c); } 605 606 const FrameParser::FrameHeader& FrameParser::Frame::Header() const { 607 return mHeader; 608 } 609 610 bool FrameParser::ParseVBRHeader(BufferReader* aReader) { 611 return mVBRHeader.Parse(aReader, CurrentFrame().Header().SamplesPerFrame()); 612 } 613 614 // ID3Parser 615 616 // Constants 617 namespace id3_header { 618 static const int ID_LEN = 3; 619 static const int VERSION_LEN = 2; 620 static const int FLAGS_LEN = 1; 621 static const int SIZE_LEN = 4; 622 623 static const int ID_END = ID_LEN; 624 static const int VERSION_END = ID_END + VERSION_LEN; 625 static const int FLAGS_END = VERSION_END + FLAGS_LEN; 626 static const int SIZE_END = FLAGS_END + SIZE_LEN; 627 628 static const uint8_t ID[ID_LEN] = {'I', 'D', '3'}; 629 static const uint8_t IDv1[ID_LEN] = {'T', 'A', 'G'}; 630 631 static const uint8_t MIN_MAJOR_VER = 2; 632 static const uint8_t MAX_MAJOR_VER = 4; 633 } // namespace id3_header 634 635 bool ID3Parser::IsBufferStartingWithID3v1Tag(BufferReader* aReader) { 636 mozilla::Result<uint32_t, nsresult> res = aReader->PeekU24(); 637 if (res.isErr()) { 638 return false; 639 } 640 // If buffer starts with ID3v1 tag, `rv` would be reverse and its content 641 // should be '3' 'D' 'I' from the lowest bit. 642 uint32_t rv = res.unwrap(); 643 for (int idx = id3_header::ID_LEN - 1; idx >= 0; idx--) { 644 if ((rv & 0xff) != id3_header::IDv1[idx]) { 645 return false; 646 } 647 rv = rv >> 8; 648 } 649 return true; 650 } 651 652 /* static */ 653 bool ID3Parser::IsBufferStartingWithID3Tag(BufferReader* aReader) { 654 mozilla::Result<uint32_t, nsresult> res = aReader->PeekU24(); 655 if (res.isErr()) { 656 return false; 657 } 658 // If buffer starts with ID3v2 tag, `rv` would be reverse and its content 659 // should be '3' 'D' 'I' from the lowest bit. 660 uint32_t rv = res.unwrap(); 661 for (int idx = id3_header::ID_LEN - 1; idx >= 0; idx--) { 662 if ((rv & 0xff) != id3_header::ID[idx]) { 663 return false; 664 } 665 rv = rv >> 8; 666 } 667 return true; 668 } 669 670 uint32_t ID3Parser::Parse(BufferReader* aReader) { 671 MOZ_ASSERT(aReader); 672 MOZ_ASSERT(ID3Parser::IsBufferStartingWithID3Tag(aReader)); 673 674 if (!mHeader.HasSizeBeenSet()) { 675 return ParseInternal(aReader); 676 } 677 678 // Encounter another possible ID3 header, if that is valid then we would use 679 // it and save the size of previous one in order to report the size of all ID3 680 // headers together in `TotalHeadersSize()`. 681 ID3Header prevHeader = mHeader; 682 mHeader.Reset(); 683 uint32_t size = ParseInternal(aReader); 684 if (!size) { 685 // next ID3 is invalid, so revert the header. 686 mHeader = prevHeader; 687 return size; 688 } 689 690 mFormerID3Size += prevHeader.TotalTagSize(); 691 return size; 692 } 693 694 uint32_t ID3Parser::ParseInternal(BufferReader* aReader) { 695 for (auto res = aReader->ReadU8(); 696 res.isOk() && !mHeader.ParseNext(res.unwrap()); 697 res = aReader->ReadU8()) { 698 } 699 return mHeader.TotalTagSize(); 700 } 701 702 void ID3Parser::Reset() { 703 mHeader.Reset(); 704 mFormerID3Size = 0; 705 } 706 707 uint32_t ID3Parser::TotalHeadersSize() const { 708 return mHeader.TotalTagSize() + mFormerID3Size; 709 } 710 711 const ID3Parser::ID3Header& ID3Parser::Header() const { return mHeader; } 712 713 // ID3Parser::Header 714 715 ID3Parser::ID3Header::ID3Header() { Reset(); } 716 717 void ID3Parser::ID3Header::Reset() { 718 mSize.reset(); 719 mPos = 0; 720 } 721 722 uint8_t ID3Parser::ID3Header::MajorVersion() const { 723 return mRaw[id3_header::ID_END]; 724 } 725 726 uint8_t ID3Parser::ID3Header::MinorVersion() const { 727 return mRaw[id3_header::ID_END + 1]; 728 } 729 730 uint8_t ID3Parser::ID3Header::Flags() const { 731 return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN]; 732 } 733 734 uint32_t ID3Parser::ID3Header::Size() const { 735 if (!IsValid() || !mSize) { 736 return 0; 737 } 738 return *mSize; 739 } 740 741 bool ID3Parser::ID3Header::HasSizeBeenSet() const { return !!mSize; } 742 743 uint8_t ID3Parser::ID3Header::FooterSize() const { 744 if (Flags() & (1 << 4)) { 745 return SIZE; 746 } 747 return 0; 748 } 749 750 uint32_t ID3Parser::ID3Header::TotalTagSize() const { 751 if (IsValid()) { 752 // Header found, return total tag size. 753 return ID3Header::SIZE + Size() + FooterSize(); 754 } 755 return 0; 756 } 757 758 bool ID3Parser::ID3Header::ParseNext(uint8_t c) { 759 if (!Update(c)) { 760 Reset(); 761 if (!Update(c)) { 762 Reset(); 763 } 764 } 765 return IsValid(); 766 } 767 768 bool ID3Parser::ID3Header::IsValid(int aPos) const { 769 if (aPos >= SIZE) { 770 return true; 771 } 772 const uint8_t c = mRaw[aPos]; 773 switch (aPos) { 774 case 0: 775 case 1: 776 case 2: 777 // Expecting "ID3". 778 return id3_header::ID[aPos] == c; 779 case 3: 780 return MajorVersion() >= id3_header::MIN_MAJOR_VER && 781 MajorVersion() <= id3_header::MAX_MAJOR_VER; 782 case 4: 783 return MinorVersion() < 0xFF; 784 case 5: 785 // Validate flags for supported versions, see bug 949036. 786 return ((0xFF >> MajorVersion()) & c) == 0; 787 case 6: 788 case 7: 789 case 8: 790 case 9: 791 return c < 0x80; 792 } 793 return true; 794 } 795 796 bool ID3Parser::ID3Header::IsValid() const { return mPos >= SIZE; } 797 798 bool ID3Parser::ID3Header::Update(uint8_t c) { 799 if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN && 800 mPos < id3_header::SIZE_END) { 801 uint32_t tmp = mSize.valueOr(0) << 7; 802 mSize = Some(tmp | c); 803 } 804 if (mPos < SIZE) { 805 mRaw[mPos] = c; 806 } 807 return IsValid(mPos++); 808 } 809 810 } // namespace mozilla