content/media/MP3FrameParser.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/MP3FrameParser.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,212 @@
     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 +#ifndef MP3FrameParser_h
    1.11 +#define MP3FrameParser_h
    1.12 +
    1.13 +#include <stdint.h>
    1.14 +
    1.15 +#include "mozilla/Mutex.h"
    1.16 +#include "nsString.h"
    1.17 +
    1.18 +namespace mozilla {
    1.19 +
    1.20 +// Simple parser to tell whether we've found an ID3 header and how long it is,
    1.21 +// so that we can skip it.
    1.22 +// XXX maybe actually parse this stuff?
    1.23 +class ID3Parser
    1.24 +{
    1.25 +public:
    1.26 +  ID3Parser();
    1.27 +
    1.28 +  void Reset();
    1.29 +  bool ParseChar(char ch);
    1.30 +  bool IsParsed() const;
    1.31 +  uint32_t GetHeaderLength() const;
    1.32 +
    1.33 +private:
    1.34 +  uint32_t mCurrentChar;
    1.35 +  uint32_t mHeaderLength;
    1.36 +};
    1.37 +
    1.38 +struct MP3Frame {
    1.39 +  uint16_t mSync1 : 8;      // Always all set
    1.40 +  uint16_t mProtected : 1;  // Ignored
    1.41 +  uint16_t mLayer : 2;
    1.42 +  uint16_t mVersion : 2;
    1.43 +  uint16_t mSync2 : 3;      // Always all set
    1.44 +  uint16_t mPrivate : 1;    // Ignored
    1.45 +  uint16_t mPad : 1;
    1.46 +  uint16_t mSampleRate : 2; // Index into mpeg_srates above
    1.47 +  uint16_t mBitrate : 4;    // Index into mpeg_bitrates above
    1.48 +
    1.49 +  uint16_t CalculateLength();
    1.50 +};
    1.51 +
    1.52 +// Buffering parser for MP3 frames.
    1.53 +class MP3Parser
    1.54 +{
    1.55 +public:
    1.56 +  MP3Parser();
    1.57 +
    1.58 +  // Forget all data the parser has seen so far.
    1.59 +  void Reset();
    1.60 +
    1.61 +  // Parse the given byte. If we have found a frame header, return the length of
    1.62 +  // the frame.
    1.63 +  uint16_t ParseFrameLength(uint8_t ch);
    1.64 +
    1.65 +  // Get the sample rate from the current header.
    1.66 +  uint32_t GetSampleRate();
    1.67 +
    1.68 +  // Get the number of samples per frame.
    1.69 +  uint32_t GetSamplesPerFrame();
    1.70 +
    1.71 +private:
    1.72 +  uint32_t mCurrentChar;
    1.73 +  union {
    1.74 +    uint8_t mRaw[3];
    1.75 +    MP3Frame mFrame;
    1.76 +  } mData;
    1.77 +};
    1.78 +
    1.79 +
    1.80 +// A description of the MP3 format and its extensions is available at
    1.81 +//
    1.82 +//  http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
    1.83 +//
    1.84 +// The data in MP3 streams is split into small frames, with each frame
    1.85 +// containing a fixed number of samples. The duration of a frame depends
    1.86 +// on the frame's bit rate and sample rate. Both values can vary among
    1.87 +// frames, so it is necessary to examine each individual frame of an MP3
    1.88 +// stream to calculate the stream's overall duration.
    1.89 +//
    1.90 +// The MP3 frame parser extracts information from an MP3 data stream. It
    1.91 +// accepts a range of frames of an MP3 stream as input, and parses all
    1.92 +// frames for their duration. Callers can query the stream's overall
    1.93 +// duration from the parser.
    1.94 +//
    1.95 +// Call the methods NotifyDataArrived or Parse to add new data. If you added
    1.96 +// information for a certain stream position, you cannot go back to previous
    1.97 +// positions. The parser will simply ignore the input. If you skip stream
    1.98 +// positions, the duration of the related MP3 frames will be estimated from
    1.99 +// the stream's average.
   1.100 +//
   1.101 +// The method GetDuration returns calculated duration of the stream, including
   1.102 +// estimates for skipped ranges.
   1.103 +//
   1.104 +// All public methods are thread-safe.
   1.105 +
   1.106 +class MP3FrameParser
   1.107 +{
   1.108 +public:
   1.109 +  MP3FrameParser(int64_t aLength=-1);
   1.110 +
   1.111 +  bool IsMP3() {
   1.112 +    MutexAutoLock mon(mLock);
   1.113 +    return mIsMP3 != NOT_MP3;
   1.114 +  }
   1.115 +
   1.116 +  void Parse(const char* aBuffer, uint32_t aLength, uint64_t aStreamOffset);
   1.117 +
   1.118 +  // Returns the duration, in microseconds. If the entire stream has not
   1.119 +  // been parsed yet, this is an estimate based on the bitrate of the
   1.120 +  // frames parsed so far.
   1.121 +  int64_t GetDuration();
   1.122 +
   1.123 +  // Returns the offset of the first MP3 frame in the stream, or -1 of
   1.124 +  // no MP3 frame has been detected yet.
   1.125 +  int64_t GetMP3Offset();
   1.126 +
   1.127 +  // Returns true if we've seen the whole first frame of the MP3 stream, and
   1.128 +  // therefore can make an estimate on the stream duration.
   1.129 +  // Otherwise, returns false.
   1.130 +  bool ParsedHeaders();
   1.131 +
   1.132 +  // Returns true if we know the exact duration of the MP3 stream;
   1.133 +  // false otherwise.
   1.134 +  bool HasExactDuration();
   1.135 +
   1.136 +  // Returns true if the parser needs more data for duration estimation.
   1.137 +  bool NeedsData();
   1.138 +
   1.139 +private:
   1.140 +
   1.141 +  // Parses aBuffer, starting at offset 0. Returns the number of bytes
   1.142 +  // parsed, relative to the start of the buffer. Note this may be
   1.143 +  // greater than aLength if the headers in the buffer indicate that
   1.144 +  // the frame or ID3 tag extends outside of aBuffer. Returns failure
   1.145 +  // if too many non-MP3 bytes are parsed.
   1.146 +  nsresult ParseBuffer(const uint8_t* aBuffer,
   1.147 +                       uint32_t aLength,
   1.148 +                       int64_t aStreamOffset,
   1.149 +                       uint32_t* aOutBytesRead);
   1.150 +
   1.151 +  // A low-contention lock for protecting the parser results
   1.152 +  Mutex mLock;
   1.153 +
   1.154 +  // ID3 header parser. Keeps state between reads in case the header falls
   1.155 +  // in between.
   1.156 +  ID3Parser mID3Parser;
   1.157 +
   1.158 +  // MP3 frame header parser.
   1.159 +  MP3Parser mMP3Parser;
   1.160 +
   1.161 +  // If we read |MAX_SKIPPED_BYTES| from the stream without finding any MP3
   1.162 +  // frames, we give up and report |NOT_MP3|. Here we track the cumulative size
   1.163 +  // of any ID3 headers we've seen so big ID3 sections aren't counted towards
   1.164 +  // skipped bytes.
   1.165 +  uint32_t mTotalID3Size;
   1.166 +
   1.167 +  // All fields below are protected by mLock
   1.168 +
   1.169 +  // We keep stats on the size of all the frames we've seen, as well as how many
   1.170 +  // so that we can estimate the duration of the rest of the stream.
   1.171 +  uint64_t mTotalFrameSize;
   1.172 +  uint64_t mFrameCount;
   1.173 +
   1.174 +  // Offset of the last data parsed. This is the end offset of the last data
   1.175 +  // block parsed, so it's the start offset we expect to get on the next
   1.176 +  // call to Parse().
   1.177 +  uint64_t mOffset;
   1.178 +
   1.179 +  // Total length of the stream in bytes.
   1.180 +  int64_t mLength;
   1.181 +
   1.182 +  // Offset of first MP3 frame in the bitstream. Has value -1 until the
   1.183 +  // first MP3 frame is found.
   1.184 +  int64_t mMP3Offset;
   1.185 +
   1.186 +  // The exact number of frames in this stream, if we know it. -1 otherwise.
   1.187 +  int64_t mNumFrames;
   1.188 +
   1.189 +  // Number of audio samples per second and per frame. Fixed through the whole
   1.190 +  // file. If we know these variables as well as the number of frames in the
   1.191 +  // file, we can get an exact duration for the stream.
   1.192 +  uint16_t mSamplesPerSecond;
   1.193 +  uint16_t mSamplesPerFrame;
   1.194 +
   1.195 +  // If the MP3 has a variable bitrate, then there *should* be metadata about
   1.196 +  // the encoding in the first frame. We buffer the first frame here.
   1.197 +  nsAutoCString mFirstFrame;
   1.198 +
   1.199 +  // While we are reading the first frame, this is the stream offset of the
   1.200 +  // last byte of that frame. -1 at all other times.
   1.201 +  int64_t mFirstFrameEnd;
   1.202 +
   1.203 +  enum eIsMP3 {
   1.204 +    MAYBE_MP3, // We're giving the stream the benefit of the doubt...
   1.205 +    DEFINITELY_MP3, // We've hit at least one ID3 tag or MP3 frame.
   1.206 +    NOT_MP3 // Not found any evidence of the stream being MP3.
   1.207 +  };
   1.208 +
   1.209 +  eIsMP3 mIsMP3;
   1.210 +
   1.211 +};
   1.212 +
   1.213 +}
   1.214 +
   1.215 +#endif

mercurial