DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (fddffdeab170)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef MP3_DEMUXER_H_
#define MP3_DEMUXER_H_

#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "MediaDataDemuxer.h"
#include "MediaResource.h"
#include "mp4_demuxer/ByteReader.h"

namespace mozilla {
namespace mp3 {

class MP3TrackDemuxer;

class MP3Demuxer : public MediaDataDemuxer {
public:
  // MediaDataDemuxer interface.
  explicit MP3Demuxer(MediaResource* aSource);
  RefPtr<InitPromise> Init() override;
  bool HasTrackType(TrackInfo::TrackType aType) const override;
  uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
  already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
      TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
  bool IsSeekable() const override;
  void NotifyDataArrived() override;
  void NotifyDataRemoved() override;
  // Do not shift the calculated buffered range by the start time of the first
  // decoded frame. The mac MP3 decoder will buffer some samples and the first
  // frame returned has typically a start time that is non-zero, causing our
  // buffered range to have a negative start time.
  bool ShouldComputeStartTime() const override { return false; }

private:
  // Synchronous initialization.
  bool InitInternal();

  RefPtr<MediaResource> mSource;
  RefPtr<MP3TrackDemuxer> mTrackDemuxer;
};

// ID3 header parser state machine used by FrameParser.
// The header contains the following format (one byte per term):
// 'I' 'D' '3' MajorVersion MinorVersion Flags Size1 Size2 Size3 Size4
// For more details see http://id3.org/id3v2.3.0.
class ID3Parser {
public:
  // Holds the ID3 header and its parsing state.
  class ID3Header {
  public:
    // The header size is static, see class comment.
    static const int SIZE = 10;

    // Constructor.
    ID3Header();

    // Resets the state to allow for a new parsing session.
    void Reset();

    // The ID3 tags are versioned like this: ID3vMajorVersion.MinorVersion.
    uint8_t MajorVersion() const;
    uint8_t MinorVersion() const;

    // The ID3 flags field.
    uint8_t Flags() const;

    // The derived size based on the provided size fields.
    uint32_t Size() const;

    // Returns the size of an ID3v2.4 footer if present and zero otherwise.
    uint8_t FooterSize() const;

    // Returns whether the parsed data is a valid ID3 header up to the given
    // byte position.
    bool IsValid(int aPos) const;

    // Returns whether the parsed data is a complete and valid ID3 header.
    bool IsValid() const;

    // Parses the next provided byte.
    // Returns whether the byte creates a valid sequence up to this point.
    bool ParseNext(uint8_t c);

  private:
    // Updates the parser state machine with the provided next byte.
    // Returns whether the provided byte is a valid next byte in the sequence.
    bool Update(uint8_t c);

    // The currently parsed byte sequence.
    uint8_t mRaw[SIZE];

    // The derived size as provided by the size fields.
    // The header size fields holds a 4 byte sequence with each MSB set to 0,
    // this bits need to be ignored when deriving the actual size.
    uint32_t mSize;

    // The current byte position in the parsed sequence. Reset via Reset and
    // incremented via Update.
    int mPos;
  };

  // Returns the parsed ID3 header. Note: check for validity.
  const ID3Header& Header() const;

  // Parses contents of given ByteReader for a valid ID3v2 header.
  // Returns the total ID3v2 tag size if successful and zero otherwise.
  uint32_t Parse(mp4_demuxer::ByteReader* aReader);

  // Resets the state to allow for a new parsing session.
  void Reset();

private:
  // The currently parsed ID3 header. Reset via Reset, updated via Parse.
  ID3Header mHeader;
};

// MPEG audio frame parser.
// The MPEG frame header has the following format (one bit per character):
// 11111111 111VVLLC BBBBSSPR MMEETOHH
// {   sync   } - 11 sync bits
//   VV         - MPEG audio version ID (0->2.5, 1->reserved, 2->2, 3->1)
//   LL         - Layer description (0->reserved, 1->III, 2->II, 3->I)
//   C          - CRC protection bit (0->protected, 1->not protected)
//   BBBB       - Bitrate index (see table in implementation)
//   SS         - Sampling rate index (see table in implementation)
//   P          - Padding bit (0->not padded, 1->padded by 1 slot size)
//   R          - Private bit (ignored)
//   MM         - Channel mode (0->stereo, 1->joint stereo, 2->dual channel,
//                3->single channel)
//   EE         - Mode extension for joint stereo (ignored)
//   T          - Copyright (0->disabled, 1->enabled)
//   O          - Original (0->copy, 1->original)
//   HH         - Emphasis (0->none, 1->50/15 ms, 2->reserved, 3->CCIT J.17)
class FrameParser {
public:
  // Holds the frame header and its parsing state.
  class FrameHeader {
  public:
    // The header size is static, see class comments.
    static const int SIZE = 4;

    // Constructor.
    FrameHeader();

    // Raw field access, see class comments for details.
    uint8_t Sync1() const;
    uint8_t Sync2() const;
    uint8_t RawVersion() const;
    uint8_t RawLayer() const;
    uint8_t RawProtection() const;
    uint8_t RawBitrate() const;
    uint8_t RawSampleRate() const;
    uint8_t Padding() const;
    uint8_t Private() const;
    uint8_t RawChannelMode() const;

    // Sampling rate frequency in Hz.
    int32_t SampleRate() const;

    // Number of audio channels.
    int32_t Channels() const;

    // Samples per frames, static depending on MPEG version and layer.
    int32_t SamplesPerFrame() const;

    // Slot size used for padding, static depending on MPEG layer.
    int32_t SlotSize() const;

    // Bitrate in kbps, can vary between frames.
    int32_t Bitrate() const;

    // MPEG layer (0->invalid, 1->I, 2->II, 3->III).
    int32_t Layer() const;

    // Returns whether the parsed data is a valid frame header up to the given
    // byte position.
    bool IsValid(const int aPos) const;

    // Returns whether the parsed data is a complete and valid frame header.
    bool IsValid() const;

    // Resets the state to allow for a new parsing session.
    void Reset();

    // Parses the next provided byte.
    // Returns whether the byte creates a valid sequence up to this point.
    bool ParseNext(const uint8_t c);

  private:
    // Updates the parser state machine with the provided next byte.
    // Returns whether the provided byte is a valid next byte in the sequence.
    bool Update(const uint8_t c);

    // The currently parsed byte sequence.
    uint8_t mRaw[SIZE];

    // The current byte position in the parsed sequence. Reset via Reset and
    // incremented via Update.
    int mPos;
  };

  // VBR frames may contain Xing or VBRI headers for additional info, we use
  // this class to parse them and access this info.
  class VBRHeader {
  public:
    // Synchronize with vbr_header TYPE_STR on change.
    enum VBRHeaderType {
      NONE = 0,
      XING,
      VBRI
    };

    // Constructor.
    VBRHeader();

    // Returns the parsed VBR header type, or NONE if no valid header found.
    VBRHeaderType Type() const;

    // Returns the total number of audio frames (excluding the VBR header frame)
    // expected in the stream/file.
    const Maybe<uint32_t>& NumAudioFrames() const;

    // Returns the expected size of the stream.
    const Maybe<uint32_t>& NumBytes() const;

    // Returns the VBR scale factor (0: best quality, 100: lowest quality).
    const Maybe<uint32_t>& Scale() const;

    // Returns true iff Xing/Info TOC (table of contents) is present.
    bool IsTOCPresent() const;

    // Returns the byte offset for the given duration percentage as a factor
    // (0: begin, 1.0: end).
    int64_t Offset(float aDurationFac) const;

    // Parses contents of given ByteReader for a valid VBR header.
    // The offset of the passed ByteReader needs to point to an MPEG frame begin,
    // as a VBRI-style header is searched at a fixed offset relative to frame begin.
    // Returns whether a valid VBR header was found in the range.
    bool Parse(mp4_demuxer::ByteReader* aReader);

  private:
    // Parses contents of given ByteReader for a valid Xing header.
    // The initial ByteReader offset will be preserved.
    // Returns whether a valid Xing header was found in the range.
    bool ParseXing(mp4_demuxer::ByteReader* aReader);

    // Parses contents of given ByteReader for a valid VBRI header.
    // The initial ByteReader offset will be preserved. It also needs to point
    // to the beginning of a valid MPEG frame, as VBRI headers are searched
    // at a fixed offset relative to frame begin.
    // Returns whether a valid VBRI header was found in the range.
    bool ParseVBRI(mp4_demuxer::ByteReader* aReader);

    // The total number of frames expected as parsed from a VBR header.
    Maybe<uint32_t> mNumAudioFrames;

    // The total number of bytes expected in the stream.
    Maybe<uint32_t> mNumBytes;

    // The VBR scale factor.
    Maybe<uint32_t> mScale;

    // The TOC table mapping duration percentage to byte offset.
    std::vector<int64_t> mTOC;

    // The detected VBR header type.
    VBRHeaderType mType;
  };

  // Frame meta container used to parse and hold a frame header and side info.
  class Frame {
  public:
    // Returns the length of the frame excluding the header in bytes.
    int32_t Length() const;

    // Returns the parsed frame header.
    const FrameHeader& Header() const;

    // Resets the frame header and data.
    void Reset();

    // Parses the next provided byte.
    // Returns whether the byte creates a valid sequence up to this point.
    bool ParseNext(uint8_t c);

  private:
    // The currently parsed frame header.
    FrameHeader mHeader;
  };

  // Constructor.
  FrameParser();

  // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
  const Frame& CurrentFrame() const;

#ifdef ENABLE_TESTS
  // Returns the previously parsed frame. Reset via Reset.
  const Frame& PrevFrame() const;
#endif

  // Returns the first parsed frame. Reset via Reset.
  const Frame& FirstFrame() const;

  // Returns the parsed ID3 header. Note: check for validity.
  const ID3Parser::ID3Header& ID3Header() const;

  // Returns the parsed VBR header info. Note: check for validity by type.
  const VBRHeader& VBRInfo() const;

  // Resets the parser. Don't use between frames as first frame data is reset.
  void Reset();

  // Clear the last parsed frame to allow for next frame parsing, i.e.:
  // - sets PrevFrame to CurrentFrame
  // - resets the CurrentFrame
  // - resets ID3Header if no valid header was parsed yet
  void EndFrameSession();

  // Parses contents of given ByteReader for a valid frame header and returns true
  // if one was found. After returning, the variable passed to 'aBytesToSkip' holds
  // the amount of bytes to be skipped (if any) in order to jump across a large
  // ID3v2 tag spanning multiple buffers.
  bool Parse(mp4_demuxer::ByteReader* aReader, uint32_t* aBytesToSkip);

  // Parses contents of given ByteReader for a valid VBR header.
  // The offset of the passed ByteReader needs to point to an MPEG frame begin,
  // as a VBRI-style header is searched at a fixed offset relative to frame begin.
  // Returns whether a valid VBR header was found.
  bool ParseVBRHeader(mp4_demuxer::ByteReader* aReader);

private:
  // ID3 header parser.
  ID3Parser mID3Parser;

  // VBR header parser.
  VBRHeader mVBRHeader;

  // We keep the first parsed frame around for static info access, the
  // previously parsed frame for debugging and the currently parsed frame.
  Frame mFirstFrame;
  Frame mFrame;
#ifdef ENABLE_TESTS
  Frame mPrevFrame;
#endif
};

// The MP3 demuxer used to extract MPEG frames and side information out of
// MPEG streams.
class MP3TrackDemuxer : public MediaTrackDemuxer {
public:
  // Constructor, expecting a valid media resource.
  explicit MP3TrackDemuxer(MediaResource* aSource);

  // Initializes the track demuxer by reading the first frame for meta data.
  // Returns initialization success state.
  bool Init();

  // Returns the total stream length if known, -1 otherwise.
  int64_t StreamLength() const;

  // Returns the estimated stream duration, or a 0-duration if unknown.
  media::TimeUnit Duration() const;

  // Returns the estimated duration up to the given frame number,
  // or a 0-duration if unknown.
  media::TimeUnit Duration(int64_t aNumFrames) const;

#ifdef ENABLE_TESTS
  const FrameParser::Frame& LastFrame() const;
  RefPtr<MediaRawData> DemuxSample();
  media::TimeUnit SeekPosition() const;
#endif

  const ID3Parser::ID3Header& ID3Header() const;
  const FrameParser::VBRHeader& VBRInfo() const;

  // MediaTrackDemuxer interface.
  UniquePtr<TrackInfo> GetInfo() const override;
  RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
  void Reset() override;
  RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
    media::TimeUnit aTimeThreshold) override;
  int64_t GetResourceOffset() const override;
  media::TimeIntervals GetBuffered() override;

private:
  // Destructor.
  ~MP3TrackDemuxer() {}

  // Fast approximate seeking to given time.
  media::TimeUnit FastSeek(const media::TimeUnit& aTime);

  // Seeks by scanning the stream up to the given time for more accurate results.
  media::TimeUnit ScanUntil(const media::TimeUnit& aTime);

  // Finds the next valid frame and returns its byte range.
  MediaByteRange FindNextFrame();

  // Skips the next frame given the provided byte range.
  bool SkipNextFrame(const MediaByteRange& aRange);

  // Returns the next MPEG frame, if available.
  already_AddRefed<MediaRawData> GetNextFrame(const MediaByteRange& aRange);

  // Updates post-read meta data.
  void UpdateState(const MediaByteRange& aRange);

  // Returns the frame index for the given offset.
  int64_t FrameIndexFromOffset(int64_t aOffset) const;

  // Returns the frame index for the given time.
  int64_t FrameIndexFromTime(const media::TimeUnit& aTime) const;

  // Reads aSize bytes into aBuffer from the source starting at aOffset.
  // Returns the actual size read.
  int32_t Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize);

  // Returns the average frame length derived from the previously parsed frames.
  double AverageFrameLength() const;

  // The (hopefully) MPEG resource.
  MediaResourceIndex mSource;

  // MPEG frame parser used to detect frames and extract side info.
  FrameParser mParser;

  // Current byte offset in the source stream.
  int64_t mOffset;

  // Byte offset of the begin of the first frame, or 0 if none parsed yet.
  int64_t mFirstFrameOffset;

  // Total parsed frames.
  uint64_t mNumParsedFrames;

  // Current frame index.
  int64_t mFrameIndex;

  // Sum of parsed frames' lengths in bytes.
  uint64_t mTotalFrameLen;

  // Samples per frame metric derived from frame headers or 0 if none available.
  int32_t mSamplesPerFrame;

  // Samples per second metric derived from frame headers or 0 if none available.
  int32_t mSamplesPerSecond;

  // Channel count derived from frame headers or 0 if none available.
  int32_t mChannels;

  // Audio track config info.
  UniquePtr<AudioInfo> mInfo;
};

} // namespace mp3
} // namespace mozilla

#endif