Line data Source code
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 <algorithm>
10 : #include <inttypes.h>
11 :
12 : #include "mozilla/Assertions.h"
13 : #include "mozilla/EndianUtils.h"
14 : #include "mozilla/SizePrintfMacros.h"
15 : #include "VideoUtils.h"
16 :
17 : extern mozilla::LazyLogModule gMediaDemuxerLog;
18 : #define MP3LOG(msg, ...) \
19 : MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("MP3Demuxer " msg, ##__VA_ARGS__))
20 : #define MP3LOGV(msg, ...) \
21 : MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("MP3Demuxer " msg, ##__VA_ARGS__))
22 :
23 : using mp4_demuxer::ByteReader;
24 :
25 : namespace mozilla {
26 :
27 : // FrameParser
28 :
29 : namespace frame_header {
30 : // FrameHeader mRaw byte offsets.
31 : static const int SYNC1 = 0;
32 : static const int SYNC2_VERSION_LAYER_PROTECTION = 1;
33 : static const int BITRATE_SAMPLERATE_PADDING_PRIVATE = 2;
34 : static const int CHANNELMODE_MODEEXT_COPY_ORIG_EMPH = 3;
35 : } // namespace frame_header
36 :
37 0 : FrameParser::FrameParser()
38 : {
39 0 : }
40 :
41 : void
42 0 : FrameParser::Reset()
43 : {
44 0 : mID3Parser.Reset();
45 0 : mFrame.Reset();
46 0 : }
47 :
48 : void
49 0 : FrameParser::ResetFrameData()
50 : {
51 0 : mFrame.Reset();
52 0 : mFirstFrame.Reset();
53 0 : mPrevFrame.Reset();
54 0 : }
55 :
56 : void
57 0 : FrameParser::EndFrameSession()
58 : {
59 0 : if (!mID3Parser.Header().IsValid()) {
60 : // Reset ID3 tags only if we have not parsed a valid ID3 header yet.
61 0 : mID3Parser.Reset();
62 : }
63 0 : mPrevFrame = mFrame;
64 0 : mFrame.Reset();
65 0 : }
66 :
67 : const FrameParser::Frame&
68 0 : FrameParser::CurrentFrame() const
69 : {
70 0 : return mFrame;
71 : }
72 :
73 : const FrameParser::Frame&
74 0 : FrameParser::PrevFrame() const
75 : {
76 0 : return mPrevFrame;
77 : }
78 :
79 : const FrameParser::Frame&
80 0 : FrameParser::FirstFrame() const
81 : {
82 0 : return mFirstFrame;
83 : }
84 :
85 : const ID3Parser::ID3Header&
86 0 : FrameParser::ID3Header() const
87 : {
88 0 : return mID3Parser.Header();
89 : }
90 :
91 : const FrameParser::VBRHeader&
92 0 : FrameParser::VBRInfo() const
93 : {
94 0 : return mVBRHeader;
95 : }
96 :
97 : bool
98 0 : FrameParser::Parse(ByteReader* aReader, uint32_t* aBytesToSkip)
99 : {
100 0 : MOZ_ASSERT(aReader && aBytesToSkip);
101 0 : *aBytesToSkip = 0;
102 :
103 0 : if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) {
104 : // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin.
105 : // ID3v1 tags may only be at file end.
106 : // TODO: should we try to read ID3 tags at end of file/mid-stream, too?
107 0 : const size_t prevReaderOffset = aReader->Offset();
108 0 : const uint32_t tagSize = mID3Parser.Parse(aReader);
109 0 : if (tagSize) {
110 : // ID3 tag found, skip past it.
111 0 : const uint32_t skipSize = tagSize - ID3Parser::ID3Header::SIZE;
112 :
113 0 : if (skipSize > aReader->Remaining()) {
114 : // Skipping across the ID3v2 tag would take us past the end of the
115 : // buffer, therefore we return immediately and let the calling function
116 : // handle skipping the rest of the tag.
117 0 : MP3LOGV("ID3v2 tag detected, size=%d,"
118 : " needing to skip %" PRIuSIZE " bytes past the current buffer",
119 : tagSize, skipSize - aReader->Remaining());
120 0 : *aBytesToSkip = skipSize - aReader->Remaining();
121 0 : return false;
122 : }
123 0 : MP3LOGV("ID3v2 tag detected, size=%d", tagSize);
124 0 : aReader->Read(skipSize);
125 : } else {
126 : // No ID3v2 tag found, rewinding reader in order to search for a MPEG
127 : // frame header.
128 0 : aReader->Seek(prevReaderOffset);
129 : }
130 : }
131 :
132 0 : while (aReader->CanRead8() && !mFrame.ParseNext(aReader->ReadU8())) { }
133 :
134 0 : if (mFrame.Length()) {
135 : // MP3 frame found.
136 0 : if (!mFirstFrame.Length()) {
137 0 : mFirstFrame = mFrame;
138 : }
139 : // Indicate success.
140 0 : return true;
141 : }
142 0 : return false;
143 : }
144 :
145 : // FrameParser::Header
146 :
147 0 : FrameParser::FrameHeader::FrameHeader()
148 : {
149 0 : Reset();
150 0 : }
151 :
152 : uint8_t
153 0 : FrameParser::FrameHeader::Sync1() const
154 : {
155 0 : return mRaw[frame_header::SYNC1];
156 : }
157 :
158 : uint8_t
159 0 : FrameParser::FrameHeader::Sync2() const
160 : {
161 0 : return 0x7 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 5;
162 : }
163 :
164 : uint8_t
165 0 : FrameParser::FrameHeader::RawVersion() const
166 : {
167 0 : return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 3;
168 : }
169 :
170 : uint8_t
171 0 : FrameParser::FrameHeader::RawLayer() const
172 : {
173 0 : return 0x3 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 1;
174 : }
175 :
176 : uint8_t
177 0 : FrameParser::FrameHeader::RawProtection() const
178 : {
179 0 : return 0x1 & mRaw[frame_header::SYNC2_VERSION_LAYER_PROTECTION] >> 6;
180 : }
181 :
182 : uint8_t
183 0 : FrameParser::FrameHeader::RawBitrate() const
184 : {
185 0 : return 0xF & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 4;
186 : }
187 :
188 : uint8_t
189 0 : FrameParser::FrameHeader::RawSampleRate() const
190 : {
191 0 : return 0x3 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 2;
192 : }
193 :
194 : uint8_t
195 0 : FrameParser::FrameHeader::Padding() const
196 : {
197 0 : return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE] >> 1;
198 : }
199 :
200 : uint8_t
201 0 : FrameParser::FrameHeader::Private() const
202 : {
203 0 : return 0x1 & mRaw[frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE];
204 : }
205 :
206 : uint8_t
207 0 : FrameParser::FrameHeader::RawChannelMode() const
208 : {
209 0 : return 0x3 & mRaw[frame_header::CHANNELMODE_MODEEXT_COPY_ORIG_EMPH] >> 6;
210 : }
211 :
212 : int32_t
213 0 : FrameParser::FrameHeader::Layer() const
214 : {
215 : static const uint8_t LAYERS[4] = { 0, 3, 2, 1 };
216 :
217 0 : return LAYERS[RawLayer()];
218 : }
219 :
220 : int32_t
221 0 : FrameParser::FrameHeader::SampleRate() const
222 : {
223 : // Sample rates - use [version][srate]
224 : static const uint16_t SAMPLE_RATE[4][4] = {
225 : { 11025, 12000, 8000, 0 }, // MPEG 2.5
226 : { 0, 0, 0, 0 }, // Reserved
227 : { 22050, 24000, 16000, 0 }, // MPEG 2
228 : { 44100, 48000, 32000, 0 } // MPEG 1
229 : };
230 :
231 0 : return SAMPLE_RATE[RawVersion()][RawSampleRate()];
232 : }
233 :
234 : int32_t
235 0 : FrameParser::FrameHeader::Channels() const
236 : {
237 : // 3 is single channel (mono), any other value is some variant of dual
238 : // channel.
239 0 : return RawChannelMode() == 3 ? 1 : 2;
240 : }
241 :
242 : int32_t
243 0 : FrameParser::FrameHeader::SamplesPerFrame() const
244 : {
245 : // Samples per frame - use [version][layer]
246 : static const uint16_t FRAME_SAMPLE[4][4] = {
247 : // Layer 3 2 1 Version
248 : { 0, 576, 1152, 384 }, // 2.5
249 : { 0, 0, 0, 0 }, // Reserved
250 : { 0, 576, 1152, 384 }, // 2
251 : { 0, 1152, 1152, 384 } // 1
252 : };
253 :
254 0 : return FRAME_SAMPLE[RawVersion()][RawLayer()];
255 : }
256 :
257 : int32_t
258 0 : FrameParser::FrameHeader::Bitrate() const
259 : {
260 : // Bitrates - use [version][layer][bitrate]
261 : static const uint16_t BITRATE[4][4][16] = {
262 : { // Version 2.5
263 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
264 : { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3
265 : { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2
266 : { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1
267 : },
268 : { // Reserved
269 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
270 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
271 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
272 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // Invalid
273 : },
274 : { // Version 2
275 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
276 : { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 3
277 : { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // Layer 2
278 : { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 } // Layer 1
279 : },
280 : { // Version 1
281 : { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Reserved
282 : { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer 3
283 : { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // Layer 2
284 : { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // Layer 1
285 : }
286 : };
287 :
288 0 : return 1000 * BITRATE[RawVersion()][RawLayer()][RawBitrate()];
289 : }
290 :
291 : int32_t
292 0 : FrameParser::FrameHeader::SlotSize() const
293 : {
294 : // Slot size (MPEG unit of measurement) - use [layer]
295 : static const uint8_t SLOT_SIZE[4] = { 0, 1, 1, 4 }; // Rsvd, 3, 2, 1
296 :
297 0 : return SLOT_SIZE[RawLayer()];
298 : }
299 :
300 : bool
301 0 : FrameParser::FrameHeader::ParseNext(uint8_t c)
302 : {
303 0 : if (!Update(c)) {
304 0 : Reset();
305 0 : if (!Update(c)) {
306 0 : Reset();
307 : }
308 : }
309 0 : return IsValid();
310 : }
311 :
312 : bool
313 0 : FrameParser::FrameHeader::IsValid(int aPos) const
314 : {
315 0 : if (aPos >= SIZE) {
316 0 : return true;
317 : }
318 0 : if (aPos == frame_header::SYNC1) {
319 0 : return Sync1() == 0xFF;
320 : }
321 0 : if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) {
322 0 : return Sync2() == 7 &&
323 0 : RawVersion() != 1 &&
324 0 : Layer() == 3;
325 : }
326 0 : if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) {
327 0 : return RawBitrate() != 0xF && RawBitrate() != 0 &&
328 0 : RawSampleRate() != 3;
329 : }
330 0 : return true;
331 : }
332 :
333 : bool
334 0 : FrameParser::FrameHeader::IsValid() const
335 : {
336 0 : return mPos >= SIZE;
337 : }
338 :
339 : void
340 0 : FrameParser::FrameHeader::Reset()
341 : {
342 0 : mPos = 0;
343 0 : }
344 :
345 : bool
346 0 : FrameParser::FrameHeader::Update(uint8_t c)
347 : {
348 0 : if (mPos < SIZE) {
349 0 : mRaw[mPos] = c;
350 : }
351 0 : return IsValid(mPos++);
352 : }
353 :
354 : // FrameParser::VBRHeader
355 :
356 : namespace vbr_header {
357 : static const char* TYPE_STR[3] = {"NONE", "XING", "VBRI"};
358 : static const uint32_t TOC_SIZE = 100;
359 : } // namespace vbr_header
360 :
361 0 : FrameParser::VBRHeader::VBRHeader()
362 0 : : mType(NONE)
363 : {
364 0 : }
365 :
366 : FrameParser::VBRHeader::VBRHeaderType
367 0 : FrameParser::VBRHeader::Type() const
368 : {
369 0 : return mType;
370 : }
371 :
372 : const Maybe<uint32_t>&
373 0 : FrameParser::VBRHeader::NumAudioFrames() const
374 : {
375 0 : return mNumAudioFrames;
376 : }
377 :
378 : const Maybe<uint32_t>&
379 0 : FrameParser::VBRHeader::NumBytes() const
380 : {
381 0 : return mNumBytes;
382 : }
383 :
384 : const Maybe<uint32_t>&
385 0 : FrameParser::VBRHeader::Scale() const
386 : {
387 0 : return mScale;
388 : }
389 :
390 : bool
391 0 : FrameParser::VBRHeader::IsTOCPresent() const
392 : {
393 0 : return mTOC.size() == vbr_header::TOC_SIZE;
394 : }
395 :
396 : bool
397 0 : FrameParser::VBRHeader::IsValid() const
398 : {
399 0 : return mType != NONE;
400 : }
401 :
402 : bool
403 0 : FrameParser::VBRHeader::IsComplete() const
404 : {
405 0 : return IsValid()
406 0 : && mNumAudioFrames.valueOr(0) > 0
407 0 : && mNumBytes.valueOr(0) > 0
408 : // We don't care about the scale for any computations here.
409 : // && mScale < 101
410 0 : && true;
411 : }
412 :
413 : int64_t
414 0 : FrameParser::VBRHeader::Offset(float aDurationFac) const
415 : {
416 0 : if (!IsTOCPresent()) {
417 0 : return -1;
418 : }
419 :
420 : // Constrain the duration percentage to [0, 99].
421 : const float durationPer =
422 0 : 100.0f * std::min(0.99f, std::max(0.0f, aDurationFac));
423 0 : const size_t fullPer = durationPer;
424 0 : const float rest = durationPer - fullPer;
425 :
426 0 : MOZ_ASSERT(fullPer < mTOC.size());
427 0 : int64_t offset = mTOC.at(fullPer);
428 :
429 0 : if (rest > 0.0 && fullPer + 1 < mTOC.size()) {
430 0 : offset += rest * (mTOC.at(fullPer + 1) - offset);
431 : }
432 :
433 0 : return offset;
434 : }
435 :
436 : bool
437 0 : FrameParser::VBRHeader::ParseXing(ByteReader* aReader)
438 : {
439 0 : static const uint32_t XING_TAG = BigEndian::readUint32("Xing");
440 0 : static const uint32_t INFO_TAG = BigEndian::readUint32("Info");
441 :
442 : enum Flags
443 : {
444 : NUM_FRAMES = 0x01,
445 : NUM_BYTES = 0x02,
446 : TOC = 0x04,
447 : VBR_SCALE = 0x08
448 : };
449 :
450 0 : MOZ_ASSERT(aReader);
451 0 : const size_t prevReaderOffset = aReader->Offset();
452 :
453 : // We have to search for the Xing header as its position can change.
454 0 : while (aReader->CanRead32() &&
455 0 : aReader->PeekU32() != XING_TAG && aReader->PeekU32() != INFO_TAG) {
456 0 : aReader->Read(1);
457 : }
458 :
459 0 : if (aReader->CanRead32()) {
460 : // Skip across the VBR header ID tag.
461 0 : aReader->ReadU32();
462 0 : mType = XING;
463 : }
464 0 : uint32_t flags = 0;
465 0 : if (aReader->CanRead32()) {
466 0 : flags = aReader->ReadU32();
467 : }
468 0 : if (flags & NUM_FRAMES && aReader->CanRead32()) {
469 0 : mNumAudioFrames = Some(aReader->ReadU32());
470 : }
471 0 : if (flags & NUM_BYTES && aReader->CanRead32()) {
472 0 : mNumBytes = Some(aReader->ReadU32());
473 : }
474 0 : if (flags & TOC && aReader->Remaining() >= vbr_header::TOC_SIZE) {
475 0 : if (!mNumBytes) {
476 : // We don't have the stream size to calculate offsets, skip the TOC.
477 0 : aReader->Read(vbr_header::TOC_SIZE);
478 : } else {
479 0 : mTOC.clear();
480 0 : mTOC.reserve(vbr_header::TOC_SIZE);
481 0 : for (size_t i = 0; i < vbr_header::TOC_SIZE; ++i) {
482 0 : mTOC.push_back(1.0f / 256.0f * aReader->ReadU8() * mNumBytes.value());
483 : }
484 : }
485 : }
486 0 : if (flags & VBR_SCALE && aReader->CanRead32()) {
487 0 : mScale = Some(aReader->ReadU32());
488 : }
489 :
490 0 : aReader->Seek(prevReaderOffset);
491 0 : return mType == XING;
492 : }
493 :
494 : bool
495 0 : FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader)
496 : {
497 0 : static const uint32_t TAG = BigEndian::readUint32("VBRI");
498 : static const uint32_t OFFSET = 32 + FrameParser::FrameHeader::SIZE;
499 : static const uint32_t FRAME_COUNT_OFFSET = OFFSET + 14;
500 : static const uint32_t MIN_FRAME_SIZE = OFFSET + 26;
501 :
502 0 : MOZ_ASSERT(aReader);
503 : // ParseVBRI assumes that the ByteReader offset points to the beginning of a
504 : // frame, therefore as a simple check, we look for the presence of a frame
505 : // sync at that position.
506 0 : MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0);
507 0 : const size_t prevReaderOffset = aReader->Offset();
508 :
509 : // VBRI have a fixed relative position, so let's check for it there.
510 0 : if (aReader->Remaining() > MIN_FRAME_SIZE) {
511 0 : aReader->Seek(prevReaderOffset + OFFSET);
512 0 : if (aReader->ReadU32() == TAG) {
513 0 : aReader->Seek(prevReaderOffset + FRAME_COUNT_OFFSET);
514 0 : mNumAudioFrames = Some(aReader->ReadU32());
515 0 : mType = VBRI;
516 0 : aReader->Seek(prevReaderOffset);
517 0 : return true;
518 : }
519 : }
520 0 : aReader->Seek(prevReaderOffset);
521 0 : return false;
522 : }
523 :
524 : bool
525 0 : FrameParser::VBRHeader::Parse(ByteReader* aReader)
526 : {
527 0 : const bool rv = ParseVBRI(aReader) || ParseXing(aReader);
528 0 : if (rv) {
529 0 : MP3LOG("VBRHeader::Parse found valid VBR/CBR header: type=%s"
530 : " NumAudioFrames=%u NumBytes=%u Scale=%u TOC-size=%" PRIuSIZE,
531 : vbr_header::TYPE_STR[Type()], NumAudioFrames().valueOr(0),
532 : NumBytes().valueOr(0), Scale().valueOr(0), mTOC.size());
533 : }
534 0 : return rv;
535 : }
536 :
537 : // FrameParser::Frame
538 :
539 : void
540 0 : FrameParser::Frame::Reset()
541 : {
542 0 : mHeader.Reset();
543 0 : }
544 :
545 : int32_t
546 0 : FrameParser::Frame::Length() const
547 : {
548 0 : if (!mHeader.IsValid() || !mHeader.SampleRate()) {
549 0 : return 0;
550 : }
551 :
552 0 : const float bitsPerSample = mHeader.SamplesPerFrame() / 8.0f;
553 0 : const int32_t frameLen = bitsPerSample * mHeader.Bitrate()
554 0 : / mHeader.SampleRate()
555 0 : + mHeader.Padding() * mHeader.SlotSize();
556 0 : return frameLen;
557 : }
558 :
559 : bool
560 0 : FrameParser::Frame::ParseNext(uint8_t c)
561 : {
562 0 : return mHeader.ParseNext(c);
563 : }
564 :
565 : const FrameParser::FrameHeader&
566 0 : FrameParser::Frame::Header() const
567 : {
568 0 : return mHeader;
569 : }
570 :
571 : bool
572 0 : FrameParser::ParseVBRHeader(ByteReader* aReader)
573 : {
574 0 : return mVBRHeader.Parse(aReader);
575 : }
576 :
577 : // ID3Parser
578 :
579 : // Constants
580 : namespace id3_header {
581 : static const int ID_LEN = 3;
582 : static const int VERSION_LEN = 2;
583 : static const int FLAGS_LEN = 1;
584 : static const int SIZE_LEN = 4;
585 :
586 : static const int ID_END = ID_LEN;
587 : static const int VERSION_END = ID_END + VERSION_LEN;
588 : static const int FLAGS_END = VERSION_END + FLAGS_LEN;
589 : static const int SIZE_END = FLAGS_END + SIZE_LEN;
590 :
591 : static const uint8_t ID[ID_LEN] = {'I', 'D', '3'};
592 :
593 : static const uint8_t MIN_MAJOR_VER = 2;
594 : static const uint8_t MAX_MAJOR_VER = 4;
595 : } // namespace id3_header
596 :
597 : uint32_t
598 0 : ID3Parser::Parse(ByteReader* aReader)
599 : {
600 0 : MOZ_ASSERT(aReader);
601 :
602 0 : while (aReader->CanRead8() && !mHeader.ParseNext(aReader->ReadU8())) { }
603 :
604 0 : return mHeader.TotalTagSize();
605 : }
606 :
607 : void
608 0 : ID3Parser::Reset()
609 : {
610 0 : mHeader.Reset();
611 0 : }
612 :
613 : const ID3Parser::ID3Header&
614 0 : ID3Parser::Header() const
615 : {
616 0 : return mHeader;
617 : }
618 :
619 : // ID3Parser::Header
620 :
621 0 : ID3Parser::ID3Header::ID3Header()
622 : {
623 0 : Reset();
624 0 : }
625 :
626 : void
627 0 : ID3Parser::ID3Header::Reset()
628 : {
629 0 : mSize = 0;
630 0 : mPos = 0;
631 0 : }
632 :
633 : uint8_t
634 0 : ID3Parser::ID3Header::MajorVersion() const
635 : {
636 0 : return mRaw[id3_header::ID_END];
637 : }
638 :
639 : uint8_t
640 0 : ID3Parser::ID3Header::MinorVersion() const
641 : {
642 0 : return mRaw[id3_header::ID_END + 1];
643 : }
644 :
645 : uint8_t
646 0 : ID3Parser::ID3Header::Flags() const
647 : {
648 0 : return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN];
649 : }
650 :
651 : uint32_t
652 0 : ID3Parser::ID3Header::Size() const
653 : {
654 0 : if (!IsValid()) {
655 0 : return 0;
656 : }
657 0 : return mSize;
658 : }
659 :
660 : uint8_t
661 0 : ID3Parser::ID3Header::FooterSize() const
662 : {
663 0 : if (Flags() & (1 << 4)) {
664 0 : return SIZE;
665 : }
666 0 : return 0;
667 : }
668 :
669 : uint32_t
670 0 : ID3Parser::ID3Header::TotalTagSize() const
671 : {
672 0 : if (IsValid()) {
673 : // Header found, return total tag size.
674 0 : return ID3Header::SIZE + Size() + FooterSize();
675 : }
676 0 : return 0;
677 : }
678 :
679 : bool
680 0 : ID3Parser::ID3Header::ParseNext(uint8_t c)
681 : {
682 0 : if (!Update(c)) {
683 0 : Reset();
684 0 : if (!Update(c)) {
685 0 : Reset();
686 : }
687 : }
688 0 : return IsValid();
689 : }
690 :
691 : bool
692 0 : ID3Parser::ID3Header::IsValid(int aPos) const
693 : {
694 0 : if (aPos >= SIZE) {
695 0 : return true;
696 : }
697 0 : const uint8_t c = mRaw[aPos];
698 0 : switch (aPos) {
699 : case 0: case 1: case 2:
700 : // Expecting "ID3".
701 0 : return id3_header::ID[aPos] == c;
702 : case 3:
703 0 : return MajorVersion() >= id3_header::MIN_MAJOR_VER
704 0 : && MajorVersion() <= id3_header::MAX_MAJOR_VER;
705 : case 4:
706 0 : return MinorVersion() < 0xFF;
707 : case 5:
708 : // Validate flags for supported versions, see bug 949036.
709 0 : return ((0xFF >> MajorVersion()) & c) == 0;
710 : case 6: case 7: case 8: case 9:
711 0 : return c < 0x80;
712 : }
713 0 : return true;
714 : }
715 :
716 : bool
717 0 : ID3Parser::ID3Header::IsValid() const
718 : {
719 0 : return mPos >= SIZE;
720 : }
721 :
722 : bool
723 0 : ID3Parser::ID3Header::Update(uint8_t c)
724 : {
725 0 : if (mPos >= id3_header::SIZE_END - id3_header::SIZE_LEN
726 0 : && mPos < id3_header::SIZE_END) {
727 0 : mSize <<= 7;
728 0 : mSize |= c;
729 : }
730 0 : if (mPos < SIZE) {
731 0 : mRaw[mPos] = c;
732 : }
733 0 : return IsValid(mPos++);
734 : }
735 :
736 : } // namespace mozilla
|