LCOV - code coverage report
Current view: top level - dom/media/flac - FlacDemuxer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 423 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 73 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       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 "FlacDemuxer.h"
       8             : 
       9             : #include "mozilla/Maybe.h"
      10             : #include "mozilla/SizePrintfMacros.h"
      11             : #include "mp4_demuxer/BitReader.h"
      12             : #include "nsAutoPtr.h"
      13             : #include "prenv.h"
      14             : #include "FlacFrameParser.h"
      15             : #include "VideoUtils.h"
      16             : #include "TimeUnits.h"
      17             : 
      18             : extern mozilla::LazyLogModule gMediaDemuxerLog;
      19             : #define LOG(msg, ...) \
      20             :   MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, ("FlacDemuxer " msg, ##__VA_ARGS__))
      21             : #define LOGV(msg, ...) \
      22             :   MOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, ("FlacDemuxer " msg, ##__VA_ARGS__))
      23             : 
      24             : using namespace mozilla::media;
      25             : 
      26             : namespace mozilla {
      27             : namespace flac {
      28             : 
      29             : // flac::FrameHeader - Holds the flac frame header and its parsing
      30             : // state.
      31             : 
      32             : #define FLAC_MAX_CHANNELS           8
      33             : #define FLAC_MIN_BLOCKSIZE         16
      34             : #define FLAC_MAX_BLOCKSIZE      65535
      35             : #define FLAC_MIN_FRAME_SIZE        11
      36             : #define FLAC_MAX_FRAME_HEADER_SIZE 16
      37             : #define FLAC_MAX_FRAME_SIZE (FLAC_MAX_FRAME_HEADER_SIZE \
      38             :                              +FLAC_MAX_BLOCKSIZE*FLAC_MAX_CHANNELS*3)
      39             : 
      40           0 : class FrameHeader
      41             : {
      42             : public:
      43           0 :   const AudioInfo& Info() const { return mInfo; }
      44             : 
      45           0 :   uint32_t Size() const { return mSize; }
      46             : 
      47           0 :   bool IsValid() const { return mValid; }
      48             : 
      49             :   // Return the index (in samples) from the beginning of the track.
      50           0 :   int64_t Index() const { return mIndex; }
      51             : 
      52             :   // Parse the current packet and check that it made a valid flac frame header.
      53             :   // From https://xiph.org/flac/format.html#frame_header
      54             :   // A valid header is one that can be decoded without error and that has a
      55             :   // valid CRC.
      56             :   // aPacket must points to a buffer that is at least FLAC_MAX_FRAME_HEADER_SIZE
      57             :   // bytes.
      58           0 :   bool Parse(const uint8_t* aPacket)
      59             :   {
      60           0 :     mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE * 8);
      61             : 
      62             :     // Frame sync code.
      63           0 :     if ((br.ReadBits(15) & 0x7fff) != 0x7ffc) {
      64           0 :       return false;
      65             :     }
      66             : 
      67             :     // Variable block size stream code.
      68           0 :     mVariableBlockSize = br.ReadBit();
      69             : 
      70             :     // Block size and sample rate codes.
      71           0 :     int bs_code = br.ReadBits(4);
      72           0 :     int sr_code = br.ReadBits(4);
      73             : 
      74             :     // Channels and decorrelation.
      75           0 :     int ch_mode = br.ReadBits(4);
      76           0 :     if (ch_mode < FLAC_MAX_CHANNELS) {
      77           0 :       mInfo.mChannels = ch_mode + 1;
      78           0 :     } else if (ch_mode < FLAC_MAX_CHANNELS + FLAC_CHMODE_MID_SIDE) {
      79             :       // This is a special flac channels, we can't handle those yet. Treat it
      80             :       // as stereo.
      81           0 :       mInfo.mChannels = 2;
      82             :     } else {
      83             :       // invalid channel mode
      84           0 :       return false;
      85             :     }
      86             : 
      87             :     // Bits per sample.
      88           0 :     int bps_code = br.ReadBits(3);
      89           0 :     if (bps_code == 3 || bps_code == 7) {
      90             :       // Invalid sample size code.
      91           0 :       return false;
      92             :     }
      93           0 :     mInfo.mBitDepth = FlacSampleSizeTable[bps_code];
      94             : 
      95             :     // Reserved bit, most be 1.
      96           0 :     if (br.ReadBit()) {
      97             :       // Broken stream, invalid padding.
      98           0 :       return false;
      99             :     }
     100             : 
     101             :     // Sample or frame count.
     102           0 :     int64_t frame_or_sample_num = br.ReadUTF8();
     103           0 :     if (frame_or_sample_num < 0) {
     104             :       // Sample/frame number invalid.
     105           0 :       return false;
     106             :     }
     107             : 
     108             :     // Blocksize
     109           0 :     if (bs_code == 0) {
     110             :       // reserved blocksize code
     111           0 :       return false;
     112           0 :     } else if (bs_code == 6) {
     113           0 :       mBlocksize = br.ReadBits(8) + 1;
     114           0 :     } else if (bs_code == 7) {
     115           0 :       mBlocksize = br.ReadBits(16) + 1;
     116             :     } else {
     117           0 :       mBlocksize = FlacBlocksizeTable[bs_code];
     118             :     }
     119             : 
     120             :     // The sample index is either:
     121             :     // 1- coded sample number if blocksize is variable or
     122             :     // 2- coded frame number if blocksize is known.
     123             :     // A frame is made of Blocksize sample.
     124           0 :     mIndex = mVariableBlockSize ? frame_or_sample_num
     125           0 :                                 : frame_or_sample_num * mBlocksize;
     126             : 
     127             :     // Sample rate.
     128           0 :     if (sr_code < 12) {
     129           0 :       mInfo.mRate = FlacSampleRateTable[sr_code];
     130           0 :     } else if (sr_code == 12) {
     131           0 :       mInfo.mRate = br.ReadBits(8) * 1000;
     132           0 :     } else if (sr_code == 13) {
     133           0 :       mInfo.mRate = br.ReadBits(16);
     134           0 :     } else if (sr_code == 14) {
     135           0 :       mInfo.mRate = br.ReadBits(16) * 10;
     136             :     } else {
     137             :       // Illegal sample rate code.
     138           0 :       return false;
     139             :     }
     140             : 
     141             :     // Header CRC-8 check.
     142           0 :     uint8_t crc = 0;
     143           0 :     for (uint32_t i = 0; i < br.BitCount() / 8; i++) {
     144           0 :       crc = CRC8Table[crc ^ aPacket[i]];
     145             :     }
     146           0 :     mValid = crc == br.ReadBits(8);
     147           0 :     mSize = br.BitCount() / 8;
     148             : 
     149           0 :     if (mValid) {
     150             :       // Set the mimetype to make it a valid AudioInfo.
     151           0 :       mInfo.mMimeType = "audio/flac";
     152             :     }
     153             : 
     154           0 :     return mValid;
     155             :   }
     156             : 
     157             : private:
     158             :   friend class Frame;
     159             :   enum
     160             :   {
     161             :     FLAC_CHMODE_INDEPENDENT = 0,
     162             :     FLAC_CHMODE_LEFT_SIDE,
     163             :     FLAC_CHMODE_RIGHT_SIDE,
     164             :     FLAC_CHMODE_MID_SIDE,
     165             :   };
     166             :   AudioInfo mInfo;
     167             :   // Index in samples from start;
     168             :   int64_t mIndex = 0;
     169             :   bool mVariableBlockSize = false;
     170             :   uint32_t mBlocksize = 0;;
     171             :   uint32_t mSize = 0;
     172             :   bool mValid = false;
     173             : 
     174             :   static const int FlacSampleRateTable[16];
     175             :   static const int32_t FlacBlocksizeTable[16];
     176             :   static const uint8_t FlacSampleSizeTable[8];
     177             :   static const uint8_t CRC8Table[256];
     178             : };
     179             : 
     180             : const int FrameHeader::FlacSampleRateTable[16] =
     181             : {
     182             :   0,
     183             :   88200, 176400, 192000,
     184             :   8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
     185             :   0, 0, 0, 0
     186             : };
     187             : 
     188             : const int32_t FrameHeader::FlacBlocksizeTable[16] =
     189             : {
     190             :   0     , 192   , 576<<0, 576<<1, 576<<2, 576<<3,      0,      0,
     191             :   256<<0, 256<<1, 256<<2, 256<<3, 256<<4, 256<<5, 256<<6, 256<<7
     192             : };
     193             : 
     194             : const uint8_t FrameHeader::FlacSampleSizeTable[8] = { 0, 8, 12, 0, 16, 20, 24, 0 };
     195             : 
     196             : const uint8_t FrameHeader::CRC8Table[256] =
     197             : {
     198             :   0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
     199             :   0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
     200             :   0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
     201             :   0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
     202             :   0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
     203             :   0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
     204             :   0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
     205             :   0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
     206             :   0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
     207             :   0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
     208             :   0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
     209             :   0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
     210             :   0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
     211             :   0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
     212             :   0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
     213             :   0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
     214             :   0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
     215             :   0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
     216             :   0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
     217             :   0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
     218             :   0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
     219             :   0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
     220             :   0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
     221             :   0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
     222             :   0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
     223             :   0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
     224             :   0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
     225             :   0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
     226             :   0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
     227             :   0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
     228             :   0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
     229             :   0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
     230             : };
     231             : 
     232             : // flac::Frame - Frame meta container used to parse and hold a frame
     233             : // header and side info.
     234           0 : class Frame
     235             : {
     236             : public:
     237             : 
     238             :   // The FLAC signature is made of 14 bits set to 1; however the 15th bit is
     239             :   // mandatorily set to 0, so we need to find either of 0xfffc or 0xfffd 2-bytes
     240             :   // signature. We first use a bitmask to see if 0xfc or 0xfd is present. And if
     241             :   // so we check for the whole signature.
     242             :   // aData must be pointing to a buffer at least
     243             :   // aLength + FLAC_MAX_FRAME_HEADER_SIZE bytes.
     244           0 :   int64_t FindNext(const uint8_t* aData, const uint32_t aLength)
     245             :   {
     246           0 :     uint32_t modOffset = aLength % 4;
     247             :     uint32_t i, j;
     248             : 
     249           0 :     for (i = 0; i < modOffset; i++) {
     250           0 :       if ((BigEndian::readUint16(aData + i) & 0xfffe) == 0xfff8) {
     251           0 :         if (mHeader.Parse(aData + i)) {
     252           0 :           return i;
     253             :         }
     254             :       }
     255             :     }
     256             : 
     257           0 :     for (; i < aLength; i += 4) {
     258           0 :       uint32_t x = BigEndian::readUint32(aData + i);
     259           0 :       if (((x & ~(x + 0x01010101)) & 0x80808080)) {
     260           0 :         for (j = 0; j < 4; j++) {
     261           0 :           if ((BigEndian::readUint16(aData + i + j) & 0xfffe) == 0xfff8) {
     262           0 :             if (mHeader.Parse(aData + i + j)) {
     263           0 :               return i + j;
     264             :             }
     265             :           }
     266             :         }
     267             :       }
     268             :     }
     269           0 :     return -1;
     270             :   }
     271             : 
     272             :   // Find the next frame start in the current resource.
     273             :   // On exit return true, offset is set and resource points to the frame found.
     274           0 :   bool FindNext(MediaResourceIndex& aResource)
     275             :   {
     276             :     static const int BUFFER_SIZE = 4096;
     277             : 
     278           0 :     Reset();
     279             : 
     280           0 :     nsTArray<char> buffer;
     281           0 :     int64_t originalOffset = aResource.Tell();
     282           0 :     int64_t offset = originalOffset;
     283           0 :     uint32_t innerOffset = 0;
     284             : 
     285           0 :     do {
     286           0 :       uint32_t read = 0;
     287           0 :       buffer.SetLength(BUFFER_SIZE + innerOffset);
     288             :       nsresult rv =
     289           0 :         aResource.Read(buffer.Elements() + innerOffset, BUFFER_SIZE, &read);
     290           0 :       if (NS_FAILED(rv)) {
     291           0 :         return false;
     292             :       }
     293             : 
     294           0 :       if (read < FLAC_MAX_FRAME_HEADER_SIZE) {
     295             :         // Assume that we can't have a valid frame in such small content, we
     296             :         // must have reached EOS.
     297             :         // So we're done.
     298           0 :         mEOS = true;
     299           0 :         return false;
     300             :       }
     301             : 
     302           0 :       const size_t bufSize = read + innerOffset - FLAC_MAX_FRAME_HEADER_SIZE;
     303             :       int64_t foundOffset =
     304           0 :         FindNext(reinterpret_cast<uint8_t*>(buffer.Elements()), bufSize);
     305             : 
     306           0 :       if (foundOffset >= 0) {
     307           0 :         SetOffset(aResource, foundOffset + offset);
     308           0 :         return true;
     309             :       }
     310             : 
     311             :       // Scan the next block;
     312           0 :       offset += bufSize;
     313           0 :       buffer.RemoveElementsAt(0, bufSize);
     314           0 :       innerOffset = buffer.Length();
     315           0 :     } while (offset - originalOffset < FLAC_MAX_FRAME_SIZE);
     316             : 
     317           0 :     return false;
     318             :   }
     319             : 
     320           0 :   int64_t Offset() const { return mOffset; }
     321             : 
     322           0 :   const AudioInfo& Info() const { return Header().Info(); }
     323             : 
     324           0 :   void SetEndOffset(int64_t aOffset) { mSize = aOffset - mOffset; }
     325             : 
     326           0 :   void SetEndTime(int64_t aIndex)
     327             :   {
     328           0 :     if (aIndex > Header().mIndex) {
     329           0 :       mDuration = aIndex - Header().mIndex;
     330             :     }
     331           0 :   }
     332             : 
     333           0 :   uint32_t Size() const { return mSize; }
     334             : 
     335           0 :   TimeUnit Time() const
     336             :   {
     337           0 :     if (!IsValid()) {
     338           0 :       return TimeUnit::Invalid();
     339             :     }
     340           0 :     MOZ_ASSERT(Header().Info().mRate, "Invalid Frame. Need Header");
     341           0 :     return FramesToTimeUnit(Header().mIndex, Header().Info().mRate);
     342             :   }
     343             : 
     344           0 :   TimeUnit Duration() const
     345             :   {
     346           0 :     if (!IsValid()) {
     347           0 :       return TimeUnit();
     348             :     }
     349           0 :     MOZ_ASSERT(Header().Info().mRate, "Invalid Frame. Need Header");
     350           0 :     return FramesToTimeUnit(mDuration, Header().Info().mRate);
     351             :   }
     352             : 
     353             :   // Returns the parsed frame header.
     354           0 :   const FrameHeader& Header() const { return mHeader; }
     355             : 
     356           0 :   bool IsValid() const { return mHeader.IsValid(); }
     357             : 
     358           0 :   bool EOS() const { return mEOS; }
     359             : 
     360           0 :   void SetRate(uint32_t aRate) { mHeader.mInfo.mRate = aRate; };
     361             : 
     362           0 :   void SetBitDepth(uint32_t aBitDepth) { mHeader.mInfo.mBitDepth = aBitDepth; }
     363             : 
     364           0 :   void SetInvalid() { mHeader.mValid = false; }
     365             : 
     366             :   // Resets the frame header and data.
     367           0 :   void Reset() { *this = Frame(); }
     368             : 
     369             : private:
     370           0 :   void SetOffset(MediaResourceIndex& aResource, int64_t aOffset)
     371             :   {
     372           0 :     mOffset = aOffset;
     373           0 :     aResource.Seek(SEEK_SET, mOffset);
     374           0 :   }
     375             : 
     376             :   // The offset to the start of the header.
     377             :   int64_t mOffset = 0;
     378             :   uint32_t mSize = 0;
     379             :   uint32_t mDuration = 0;
     380             :   bool mEOS = false;
     381             : 
     382             :   // The currently parsed frame header.
     383             :   FrameHeader mHeader;
     384             : 
     385             : };
     386             : 
     387           0 : class FrameParser
     388             : {
     389             : public:
     390             : 
     391             :   // Returns the currently parsed frame. Reset via EndFrameSession.
     392           0 :   const Frame& CurrentFrame() const { return mFrame; }
     393             : 
     394             :   // Returns the first parsed frame.
     395           0 :   const Frame& FirstFrame() const { return mFirstFrame; }
     396             : 
     397             :   // Clear the last parsed frame to allow for next frame parsing
     398           0 :   void EndFrameSession()
     399             :   {
     400           0 :     mNextFrame.Reset();
     401           0 :     mFrame.Reset();
     402           0 :   }
     403             : 
     404             :   // Attempt to find the next frame.
     405           0 :   bool FindNextFrame(MediaResourceIndex& aResource)
     406             :   {
     407           0 :     mFrame = mNextFrame;
     408           0 :     if (GetNextFrame(aResource)) {
     409           0 :       if (!mFrame.IsValid()) {
     410           0 :         mFrame = mNextFrame;
     411             :         // We need two frames to be able to start playing (or have reached EOS).
     412           0 :         GetNextFrame(aResource);
     413             :       }
     414             :     }
     415             : 
     416           0 :     if (mFrame.IsValid()) {
     417           0 :       if (mNextFrame.EOS()) {
     418           0 :         mFrame.SetEndOffset(aResource.Tell());
     419           0 :       } else if (mNextFrame.IsValid()) {
     420           0 :         mFrame.SetEndOffset(mNextFrame.Offset());
     421           0 :         mFrame.SetEndTime(mNextFrame.Header().Index());
     422             :       }
     423             :     }
     424             : 
     425           0 :     if (!mFirstFrame.IsValid()) {
     426           0 :       mFirstFrame = mFrame;
     427             :     }
     428           0 :     return mFrame.IsValid();
     429             :   }
     430             : 
     431             :   // Convenience methods to external FlacFrameParser ones.
     432           0 :   bool IsHeaderBlock(const uint8_t* aPacket, size_t aLength) const
     433             :   {
     434           0 :     return mParser.IsHeaderBlock(aPacket, aLength);
     435             :   }
     436             : 
     437           0 :   uint32_t HeaderBlockLength(const uint8_t* aPacket) const
     438             :   {
     439           0 :     return mParser.HeaderBlockLength(aPacket);
     440             :   }
     441             : 
     442           0 :   bool DecodeHeaderBlock(const uint8_t* aPacket, size_t aLength)
     443             :   {
     444           0 :     return mParser.DecodeHeaderBlock(aPacket, aLength);
     445             :   }
     446             : 
     447           0 :   bool HasFullMetadata() const { return mParser.HasFullMetadata(); }
     448             : 
     449           0 :   AudioInfo Info() const { return mParser.mInfo; }
     450             : 
     451             :   // Return a hash table with tag metadata.
     452           0 :   MetadataTags* GetTags() const { return mParser.GetTags(); }
     453             : 
     454             : private:
     455           0 :   bool GetNextFrame(MediaResourceIndex& aResource)
     456             :   {
     457           0 :     while (mNextFrame.FindNext(aResource)) {
     458             :       // Move our offset slightly, so that we don't find the same frame at the
     459             :       // next FindNext call.
     460           0 :       aResource.Seek(SEEK_CUR, mNextFrame.Header().Size());
     461           0 :       if (mFrame.IsValid()
     462           0 :           && mNextFrame.Offset() - mFrame.Offset() < FLAC_MAX_FRAME_SIZE
     463           0 :           && !CheckCRC16AtOffset(mFrame.Offset(),
     464             :                                  mNextFrame.Offset(),
     465             :                                  aResource)) {
     466             :         // The frame doesn't match its CRC or would be too far, skip it..
     467           0 :         continue;
     468             :       }
     469           0 :       CheckFrameData();
     470           0 :       break;
     471             :     }
     472           0 :     return mNextFrame.IsValid();
     473             :   }
     474             : 
     475           0 :   bool CheckFrameData()
     476             :   {
     477           0 :     if (mNextFrame.Header().Info().mRate == 0
     478           0 :         || mNextFrame.Header().Info().mBitDepth == 0) {
     479           0 :       if (!Info().IsValid()) {
     480             :         // We can only use the STREAMINFO data if we have one.
     481           0 :         mNextFrame.SetInvalid();
     482             :       } else {
     483           0 :         if (mNextFrame.Header().Info().mRate == 0) {
     484           0 :           mNextFrame.SetRate(Info().mRate);
     485             :         }
     486           0 :         if (mNextFrame.Header().Info().mBitDepth == 0) {
     487           0 :           mNextFrame.SetBitDepth(Info().mBitDepth);
     488             :         }
     489             :       }
     490             :     }
     491           0 :     return mNextFrame.IsValid();
     492             :   }
     493             : 
     494           0 :   bool CheckCRC16AtOffset(int64_t aStart, int64_t aEnd,
     495             :                           MediaResourceIndex& aResource) const
     496             :   {
     497           0 :     int64_t size = aEnd - aStart;
     498           0 :     if (size <= 0) {
     499           0 :       return false;
     500             :     }
     501           0 :     UniquePtr<char[]> buffer(new char[size]);
     502           0 :     uint32_t read = 0;
     503           0 :     if (NS_FAILED(aResource.ReadAt(aStart, buffer.get(),
     504             :                                    size, &read))
     505           0 :         || read != size) {
     506           0 :       NS_WARNING("Couldn't read frame content");
     507           0 :       return false;
     508             :     }
     509             : 
     510           0 :     uint16_t crc = 0;
     511           0 :     uint8_t* buf = reinterpret_cast<uint8_t*>(buffer.get());
     512           0 :     const uint8_t *end = buf + size;
     513           0 :     while (buf < end) {
     514           0 :       crc = CRC16Table[((uint8_t)crc) ^ *buf++] ^ (crc >> 8);
     515             :     }
     516           0 :     return !crc;
     517             :   }
     518             : 
     519             :   const uint16_t CRC16Table[256] =
     520             :   {
     521             :     0x0000, 0x0580, 0x0F80, 0x0A00, 0x1B80, 0x1E00, 0x1400, 0x1180,
     522             :     0x3380, 0x3600, 0x3C00, 0x3980, 0x2800, 0x2D80, 0x2780, 0x2200,
     523             :     0x6380, 0x6600, 0x6C00, 0x6980, 0x7800, 0x7D80, 0x7780, 0x7200,
     524             :     0x5000, 0x5580, 0x5F80, 0x5A00, 0x4B80, 0x4E00, 0x4400, 0x4180,
     525             :     0xC380, 0xC600, 0xCC00, 0xC980, 0xD800, 0xDD80, 0xD780, 0xD200,
     526             :     0xF000, 0xF580, 0xFF80, 0xFA00, 0xEB80, 0xEE00, 0xE400, 0xE180,
     527             :     0xA000, 0xA580, 0xAF80, 0xAA00, 0xBB80, 0xBE00, 0xB400, 0xB180,
     528             :     0x9380, 0x9600, 0x9C00, 0x9980, 0x8800, 0x8D80, 0x8780, 0x8200,
     529             :     0x8381, 0x8601, 0x8C01, 0x8981, 0x9801, 0x9D81, 0x9781, 0x9201,
     530             :     0xB001, 0xB581, 0xBF81, 0xBA01, 0xAB81, 0xAE01, 0xA401, 0xA181,
     531             :     0xE001, 0xE581, 0xEF81, 0xEA01, 0xFB81, 0xFE01, 0xF401, 0xF181,
     532             :     0xD381, 0xD601, 0xDC01, 0xD981, 0xC801, 0xCD81, 0xC781, 0xC201,
     533             :     0x4001, 0x4581, 0x4F81, 0x4A01, 0x5B81, 0x5E01, 0x5401, 0x5181,
     534             :     0x7381, 0x7601, 0x7C01, 0x7981, 0x6801, 0x6D81, 0x6781, 0x6201,
     535             :     0x2381, 0x2601, 0x2C01, 0x2981, 0x3801, 0x3D81, 0x3781, 0x3201,
     536             :     0x1001, 0x1581, 0x1F81, 0x1A01, 0x0B81, 0x0E01, 0x0401, 0x0181,
     537             :     0x0383, 0x0603, 0x0C03, 0x0983, 0x1803, 0x1D83, 0x1783, 0x1203,
     538             :     0x3003, 0x3583, 0x3F83, 0x3A03, 0x2B83, 0x2E03, 0x2403, 0x2183,
     539             :     0x6003, 0x6583, 0x6F83, 0x6A03, 0x7B83, 0x7E03, 0x7403, 0x7183,
     540             :     0x5383, 0x5603, 0x5C03, 0x5983, 0x4803, 0x4D83, 0x4783, 0x4203,
     541             :     0xC003, 0xC583, 0xCF83, 0xCA03, 0xDB83, 0xDE03, 0xD403, 0xD183,
     542             :     0xF383, 0xF603, 0xFC03, 0xF983, 0xE803, 0xED83, 0xE783, 0xE203,
     543             :     0xA383, 0xA603, 0xAC03, 0xA983, 0xB803, 0xBD83, 0xB783, 0xB203,
     544             :     0x9003, 0x9583, 0x9F83, 0x9A03, 0x8B83, 0x8E03, 0x8403, 0x8183,
     545             :     0x8002, 0x8582, 0x8F82, 0x8A02, 0x9B82, 0x9E02, 0x9402, 0x9182,
     546             :     0xB382, 0xB602, 0xBC02, 0xB982, 0xA802, 0xAD82, 0xA782, 0xA202,
     547             :     0xE382, 0xE602, 0xEC02, 0xE982, 0xF802, 0xFD82, 0xF782, 0xF202,
     548             :     0xD002, 0xD582, 0xDF82, 0xDA02, 0xCB82, 0xCE02, 0xC402, 0xC182,
     549             :     0x4382, 0x4602, 0x4C02, 0x4982, 0x5802, 0x5D82, 0x5782, 0x5202,
     550             :     0x7002, 0x7582, 0x7F82, 0x7A02, 0x6B82, 0x6E02, 0x6402, 0x6182,
     551             :     0x2002, 0x2582, 0x2F82, 0x2A02, 0x3B82, 0x3E02, 0x3402, 0x3182,
     552             :     0x1382, 0x1602, 0x1C02, 0x1982, 0x0802, 0x0D82, 0x0782, 0x0202,
     553             :   };
     554             : 
     555             :   FlacFrameParser mParser;
     556             :   // We keep the first parsed frame around for static info access
     557             :   // and the currently parsed frame.
     558             :   Frame mFirstFrame;
     559             :   Frame mNextFrame;
     560             :   Frame mFrame;
     561             : };
     562             : 
     563             : } // namespace flac
     564             : 
     565             : // FlacDemuxer
     566             : 
     567           0 : FlacDemuxer::FlacDemuxer(MediaResource* aSource) : mSource(aSource) { }
     568             : 
     569             : bool
     570           0 : FlacDemuxer::InitInternal()
     571             : {
     572           0 :   if (!mTrackDemuxer) {
     573           0 :     mTrackDemuxer = new FlacTrackDemuxer(mSource);
     574             :   }
     575           0 :   return mTrackDemuxer->Init();
     576             : }
     577             : 
     578             : RefPtr<FlacDemuxer::InitPromise>
     579           0 : FlacDemuxer::Init()
     580             : {
     581           0 :   if (!InitInternal()) {
     582           0 :     LOG("Init() failure: waiting for data");
     583             : 
     584             :     return InitPromise::CreateAndReject(
     585           0 :       NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
     586             :   }
     587             : 
     588           0 :   LOG("Init() successful");
     589           0 :   return InitPromise::CreateAndResolve(NS_OK, __func__);
     590             : }
     591             : 
     592             : bool
     593           0 : FlacDemuxer::HasTrackType(TrackInfo::TrackType aType) const
     594             : {
     595           0 :   return aType == TrackInfo::kAudioTrack;
     596             : }
     597             : 
     598             : uint32_t
     599           0 : FlacDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
     600             : {
     601           0 :   return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
     602             : }
     603             : 
     604             : already_AddRefed<MediaTrackDemuxer>
     605           0 : FlacDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
     606             : {
     607           0 :   if (!mTrackDemuxer) {
     608           0 :     return nullptr;
     609             :   }
     610             : 
     611           0 :   return RefPtr<FlacTrackDemuxer>(mTrackDemuxer).forget();
     612             : }
     613             : 
     614             : bool
     615           0 : FlacDemuxer::IsSeekable() const
     616             : {
     617           0 :   return mTrackDemuxer && mTrackDemuxer->IsSeekable();
     618             : }
     619             : 
     620             : // FlacTrackDemuxer
     621           0 : FlacTrackDemuxer::FlacTrackDemuxer(MediaResource* aSource)
     622             :   : mSource(aSource)
     623           0 :   , mParser(new flac::FrameParser())
     624           0 :   , mTotalFrameLen(0)
     625             : {
     626           0 :   Reset();
     627           0 : }
     628             : 
     629           0 : FlacTrackDemuxer::~FlacTrackDemuxer()
     630             : {
     631           0 : }
     632             : 
     633             : bool
     634           0 : FlacTrackDemuxer::Init()
     635             : {
     636             :   static const int BUFFER_SIZE = 4096;
     637             : 
     638             :   // First check if we have a valid Flac start.
     639             :   char buffer[BUFFER_SIZE];
     640             :   const uint8_t* ubuffer = // only needed due to type constraints of ReadAt.
     641           0 :     reinterpret_cast<uint8_t*>(buffer);
     642           0 :   int64_t offset = 0;
     643             : 
     644           0 :   do {
     645           0 :     uint32_t read = 0;
     646           0 :     nsresult ret = mSource.ReadAt(offset, buffer, BUFFER_SIZE, &read);
     647           0 :     if (NS_FAILED(ret) || read < BUFFER_SIZE) {
     648             :       // Assume that if we can't read that many bytes while parsing the header,
     649             :       // that something is wrong.
     650           0 :       return false;
     651             :     }
     652           0 :     if (!mParser->IsHeaderBlock(ubuffer, BUFFER_SIZE)) {
     653             :       // Not a header and we haven't reached the end of the metadata blocks.
     654             :       // Will fall back to using the frames header instead.
     655           0 :       break;
     656             :     }
     657           0 :     uint32_t sizeHeader = mParser->HeaderBlockLength(ubuffer);
     658             :     RefPtr<MediaByteBuffer> block =
     659           0 :       mSource.MediaReadAt(offset, sizeHeader);
     660           0 :     if (!block || block->Length() != sizeHeader) {
     661           0 :       break;
     662             :     }
     663           0 :     if (!mParser->DecodeHeaderBlock(block->Elements(), sizeHeader)) {
     664           0 :       break;
     665             :     }
     666           0 :     offset += sizeHeader;
     667           0 :   } while (!mParser->HasFullMetadata());
     668             : 
     669             :   // First flac frame is found after the metadata.
     670             :   // Can seek there immediately to avoid reparsing it all.
     671           0 :   mSource.Seek(SEEK_SET, offset);
     672             : 
     673             :   // Find the first frame to fully initialise our parser.
     674           0 :   if (mParser->FindNextFrame(mSource)) {
     675             :     // Ensure that the next frame returned will be the first.
     676           0 :     mSource.Seek(SEEK_SET, mParser->FirstFrame().Offset());
     677           0 :     mParser->EndFrameSession();
     678           0 :   } else if (!mParser->Info().IsValid() || !mParser->FirstFrame().IsValid()) {
     679             :     // We must find at least a frame to determine the metadata.
     680             :     // We can't play this stream.
     681           0 :     return false;
     682             :   }
     683             : 
     684           0 :   if (!mParser->Info().IsValid() || !mParser->Info().mDuration.IsPositive()) {
     685             :     // Check if we can look at the last frame for the end time to determine the
     686             :     // duration when we don't have any.
     687           0 :     TimeAtEnd();
     688             :   }
     689             : 
     690           0 :   return true;
     691             : }
     692             : 
     693             : UniquePtr<TrackInfo>
     694           0 : FlacTrackDemuxer::GetInfo() const
     695             : {
     696           0 :   if (mParser->Info().IsValid()) {
     697             :     // We have a proper metadata header.
     698           0 :     UniquePtr<TrackInfo> info = mParser->Info().Clone();
     699           0 :     nsAutoPtr<MetadataTags> tags(mParser->GetTags());
     700           0 :     if (tags) {
     701           0 :       for (auto iter = tags->Iter(); !iter.Done(); iter.Next()) {
     702           0 :         info->mTags.AppendElement(MetadataTag(iter.Key(), iter.Data()));
     703             :       }
     704             :     }
     705           0 :     return info;
     706           0 :   } else if (mParser->FirstFrame().Info().IsValid()) {
     707             :     // Use the first frame header.
     708           0 :     UniquePtr<TrackInfo> info = mParser->FirstFrame().Info().Clone();
     709           0 :     info->mDuration = Duration();
     710           0 :     return info;
     711             :   }
     712           0 :   return nullptr;
     713             : }
     714             : 
     715             : bool
     716           0 : FlacTrackDemuxer::IsSeekable() const
     717             : {
     718             :   // For now we only allow seeking if a STREAMINFO block was found and with
     719             :   // a known number of samples (duration is set).
     720           0 :   return mParser->Info().IsValid() && mParser->Info().mDuration.IsPositive();
     721             : }
     722             : 
     723             : RefPtr<FlacTrackDemuxer::SeekPromise>
     724           0 : FlacTrackDemuxer::Seek(const TimeUnit& aTime)
     725             : {
     726             :   // Efficiently seek to the position.
     727           0 :   FastSeek(aTime);
     728             :   // Correct seek position by scanning the next frames.
     729           0 :   const TimeUnit seekTime = ScanUntil(aTime);
     730             : 
     731           0 :   return SeekPromise::CreateAndResolve(seekTime, __func__);
     732             : }
     733             : 
     734             : TimeUnit
     735           0 : FlacTrackDemuxer::FastSeek(const TimeUnit& aTime)
     736             : {
     737           0 :   LOG("FastSeek(%f) avgFrameLen=%f mParsedFramesDuration=%f offset=%" PRId64,
     738             :       aTime.ToSeconds(), AverageFrameLength(),
     739             :       mParsedFramesDuration.ToSeconds(), GetResourceOffset());
     740             : 
     741             :   // Invalidate current frames in the parser.
     742           0 :   mParser->EndFrameSession();
     743             : 
     744           0 :   if (!mParser->FirstFrame().IsValid()) {
     745             :     // Something wrong, and there's nothing to seek to anyway, so we can
     746             :     // do whatever here.
     747           0 :     mSource.Seek(SEEK_SET, 0);
     748           0 :     return TimeUnit();
     749             :   }
     750             : 
     751           0 :   if (aTime <= mParser->FirstFrame().Time()) {
     752             :     // We're attempting to seek prior the first frame, return the first frame.
     753           0 :     mSource.Seek(SEEK_SET, mParser->FirstFrame().Offset());
     754           0 :     return mParser->FirstFrame().Time();
     755             :   }
     756             : 
     757             :   // We look for the seek position using a bisection search, starting where the
     758             :   // estimated position might be using the average frame length.
     759             :   // Typically, with flac such approximation is typically useless.
     760             : 
     761             :   // Estimate where the position might be.
     762             :   int64_t pivot =
     763           0 :     aTime.ToSeconds() * AverageFrameLength() + mParser->FirstFrame().Offset();
     764             : 
     765             :   // Time in seconds where we can stop seeking and will continue using
     766             :   // ScanUntil.
     767             :   static const int GAP_THRESHOLD = 5;
     768           0 :   int64_t first = mParser->FirstFrame().Offset();
     769           0 :   int64_t last = mSource.GetLength();
     770           0 :   Maybe<int64_t> lastFoundOffset;
     771           0 :   uint32_t iterations = 0;
     772           0 :   TimeUnit timeSeekedTo;
     773             : 
     774             :   do {
     775           0 :     iterations++;
     776           0 :     mSource.Seek(SEEK_SET, pivot);
     777           0 :     flac::Frame frame;
     778           0 :     if (!frame.FindNext(mSource)) {
     779           0 :       NS_WARNING("We should have found a point");
     780           0 :       break;
     781             :     }
     782           0 :     timeSeekedTo = frame.Time();
     783             : 
     784           0 :     LOGV("FastSeek: interation:%u found:%f @ %" PRId64,
     785             :          iterations, timeSeekedTo.ToSeconds(), frame.Offset());
     786             : 
     787           0 :     if (lastFoundOffset && lastFoundOffset.ref() == frame.Offset()) {
     788             :       // Same frame found twice. We're done.
     789           0 :       break;
     790             :     }
     791           0 :     lastFoundOffset = Some(frame.Offset());
     792             : 
     793           0 :     if (frame.Time() == aTime) {
     794           0 :       break;
     795             :     }
     796           0 :     if (aTime > frame.Time()
     797           0 :         && aTime - frame.Time() <= TimeUnit::FromSeconds(GAP_THRESHOLD)) {
     798             :       // We're close enough to the target, experimentation shows that bisection
     799             :       // search doesn't help much after that.
     800           0 :       break;
     801             :     }
     802           0 :     if (frame.Time() > aTime) {
     803           0 :       last = pivot;
     804           0 :       pivot -= (pivot - first) / 2;
     805             :     } else {
     806           0 :       first = pivot;
     807           0 :       pivot += (last - pivot) / 2;
     808           0 :     }
     809             :   } while (true);
     810             : 
     811           0 :   if (lastFoundOffset) {
     812           0 :     mSource.Seek(SEEK_SET, lastFoundOffset.ref());
     813             :   }
     814             : 
     815           0 :   return timeSeekedTo;
     816             : }
     817             : 
     818             : TimeUnit
     819           0 : FlacTrackDemuxer::ScanUntil(const TimeUnit& aTime)
     820             : {
     821           0 :   LOG("ScanUntil(%f avgFrameLen=%f mParsedFramesDuration=%f offset=%" PRId64,
     822             :       aTime.ToSeconds(), AverageFrameLength(),
     823             :       mParsedFramesDuration.ToSeconds(), mParser->CurrentFrame().Offset());
     824             : 
     825           0 :    if (!mParser->FirstFrame().IsValid()
     826           0 :        || aTime <= mParser->FirstFrame().Time()) {
     827           0 :      return FastSeek(aTime);
     828             :    }
     829             : 
     830           0 :   int64_t previousOffset = 0;
     831           0 :   TimeUnit previousTime;
     832           0 :   while (FindNextFrame().IsValid() && mParser->CurrentFrame().Time() < aTime) {
     833           0 :     previousOffset = mParser->CurrentFrame().Offset();
     834           0 :     previousTime = mParser->CurrentFrame().Time();
     835             :   }
     836             : 
     837           0 :   if (!mParser->CurrentFrame().IsValid()) {
     838             :     // We reached EOS.
     839           0 :     return Duration();
     840             :   }
     841             : 
     842             :   // Seek back to the last frame found prior the target.
     843           0 :   mParser->EndFrameSession();
     844           0 :   mSource.Seek(SEEK_SET, previousOffset);
     845           0 :   return previousTime;
     846             : }
     847             : 
     848             : RefPtr<FlacTrackDemuxer::SamplesPromise>
     849           0 : FlacTrackDemuxer::GetSamples(int32_t aNumSamples)
     850             : {
     851           0 :   LOGV("GetSamples(%d) Begin offset=%" PRId64 " mParsedFramesDuration=%f"
     852             :        " mTotalFrameLen=%" PRIu64,
     853             :        aNumSamples, GetResourceOffset(), mParsedFramesDuration.ToSeconds(),
     854             :        mTotalFrameLen);
     855             : 
     856           0 :   if (!aNumSamples) {
     857             :     return SamplesPromise::CreateAndReject(
     858           0 :       NS_ERROR_DOM_MEDIA_DEMUXER_ERR, __func__);
     859             :   }
     860             : 
     861           0 :   RefPtr<SamplesHolder> frames = new SamplesHolder();
     862             : 
     863           0 :   while (aNumSamples--) {
     864           0 :     RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
     865           0 :     if (!frame)
     866           0 :       break;
     867             : 
     868           0 :     frames->mSamples.AppendElement(frame);
     869             :   }
     870             : 
     871           0 :   LOGV("GetSamples() End mSamples.Length=%" PRIuSIZE " aNumSamples=%d offset=%" PRId64
     872             :        " mParsedFramesDuration=%f mTotalFrameLen=%" PRIu64,
     873             :        frames->mSamples.Length(), aNumSamples, GetResourceOffset(),
     874             :        mParsedFramesDuration.ToSeconds(), mTotalFrameLen);
     875             : 
     876           0 :   if (frames->mSamples.IsEmpty()) {
     877             :     return SamplesPromise::CreateAndReject(
     878           0 :       NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
     879             :   }
     880             : 
     881           0 :   return SamplesPromise::CreateAndResolve(frames, __func__);
     882             : }
     883             : 
     884             : void
     885           0 : FlacTrackDemuxer::Reset()
     886             : {
     887           0 :   LOG("Reset()");
     888           0 :   MOZ_ASSERT(mParser);
     889           0 :   if (mParser->FirstFrame().IsValid()) {
     890           0 :     mSource.Seek(SEEK_SET, mParser->FirstFrame().Offset());
     891             :   } else {
     892           0 :     mSource.Seek(SEEK_SET, 0);
     893             :   }
     894           0 :   mParser->EndFrameSession();
     895           0 : }
     896             : 
     897             : RefPtr<FlacTrackDemuxer::SkipAccessPointPromise>
     898           0 : FlacTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
     899             : {
     900             :   // Will not be called for audio-only resources.
     901             :   return SkipAccessPointPromise::CreateAndReject(
     902           0 :     SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
     903             : }
     904             : 
     905             : int64_t
     906           0 : FlacTrackDemuxer::GetResourceOffset() const
     907             : {
     908           0 :   return mSource.Tell();
     909             : }
     910             : 
     911             : TimeIntervals
     912           0 : FlacTrackDemuxer::GetBuffered()
     913             : {
     914           0 :   TimeUnit duration = Duration();
     915             : 
     916           0 :   if (duration <= TimeUnit()) {
     917           0 :     return TimeIntervals();
     918             :   }
     919             : 
     920             :   // We could simply parse the cached data instead and read the timestamps.
     921             :   // However, for now this will do.
     922           0 :   AutoPinned<MediaResource> stream(mSource.GetResource());
     923           0 :   return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
     924             : }
     925             : 
     926             : const flac::Frame&
     927           0 : FlacTrackDemuxer::FindNextFrame()
     928             : {
     929           0 :   LOGV("FindNext() Begin offset=%" PRId64 " mParsedFramesDuration=%f"
     930             :        " mTotalFrameLen=%" PRIu64,
     931             :        GetResourceOffset(), mParsedFramesDuration.ToSeconds(), mTotalFrameLen);
     932             : 
     933           0 :   if (mParser->FindNextFrame(mSource)) {
     934             :     // Update our current progress stats.
     935           0 :     mParsedFramesDuration =
     936             :       std::max(mParsedFramesDuration,
     937           0 :                mParser->CurrentFrame().Time() - mParser->FirstFrame().Time()
     938           0 :                + mParser->CurrentFrame().Duration());
     939           0 :     mTotalFrameLen =
     940           0 :       std::max<uint64_t>(mTotalFrameLen,
     941           0 :                          mParser->CurrentFrame().Offset()
     942           0 :                          - mParser->FirstFrame().Offset()
     943           0 :                          + mParser->CurrentFrame().Size());
     944             : 
     945           0 :     LOGV("FindNext() End time=%f offset=%" PRId64 " mParsedFramesDuration=%f"
     946             :          " mTotalFrameLen=%" PRIu64,
     947             :          mParser->CurrentFrame().Time().ToSeconds(), GetResourceOffset(),
     948             :          mParsedFramesDuration.ToSeconds(), mTotalFrameLen);
     949             :   }
     950             : 
     951           0 :   return mParser->CurrentFrame();
     952             : }
     953             : 
     954             : already_AddRefed<MediaRawData>
     955           0 : FlacTrackDemuxer::GetNextFrame(const flac::Frame& aFrame)
     956             : {
     957           0 :   if (!aFrame.IsValid()) {
     958           0 :     LOG("GetNextFrame() EOS");
     959           0 :     return nullptr;
     960             :   }
     961             : 
     962           0 :   LOG("GetNextFrame() Begin(time=%f offset=%" PRId64 " size=%u)",
     963             :       aFrame.Time().ToSeconds(), aFrame.Offset(), aFrame.Size());
     964             : 
     965           0 :   const int64_t offset = aFrame.Offset();
     966           0 :   const uint32_t size = aFrame.Size();
     967             : 
     968           0 :   RefPtr<MediaRawData> frame = new MediaRawData();
     969           0 :   frame->mOffset = offset;
     970             : 
     971           0 :   nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
     972           0 :   if (!frameWriter->SetSize(size)) {
     973           0 :     LOG("GetNext() Exit failed to allocated media buffer");
     974           0 :     return nullptr;
     975             :   }
     976             : 
     977           0 :   const uint32_t read = Read(frameWriter->Data(), offset, size);
     978           0 :   if (read != size) {
     979           0 :     LOG("GetNextFrame() Exit read=%u frame->Size=%" PRIuSIZE, read, frame->Size());
     980           0 :     return nullptr;
     981             :   }
     982             : 
     983           0 :   frame->mTime = aFrame.Time();
     984           0 :   frame->mDuration = aFrame.Duration();
     985           0 :   frame->mTimecode = frame->mTime;
     986           0 :   frame->mOffset = aFrame.Offset();
     987           0 :   frame->mKeyframe = true;
     988             : 
     989           0 :   MOZ_ASSERT(!frame->mTime.IsNegative());
     990           0 :   MOZ_ASSERT(!frame->mDuration.IsNegative());
     991             : 
     992           0 :   return frame.forget();
     993             : }
     994             : 
     995             : int32_t
     996           0 : FlacTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize)
     997             : {
     998           0 :   uint32_t read = 0;
     999           0 :   const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
    1000           0 :                                      static_cast<uint32_t>(aSize), &read);
    1001           0 :   NS_ENSURE_SUCCESS(rv, 0);
    1002           0 :   return static_cast<int32_t>(read);
    1003             : }
    1004             : 
    1005             : double
    1006           0 : FlacTrackDemuxer::AverageFrameLength() const
    1007             : {
    1008           0 :   if (mParsedFramesDuration.ToMicroseconds()) {
    1009           0 :     return mTotalFrameLen / mParsedFramesDuration.ToSeconds();
    1010             :   }
    1011             : 
    1012           0 :   return 0.0;
    1013             : }
    1014             : 
    1015             : TimeUnit
    1016           0 : FlacTrackDemuxer::Duration() const
    1017             : {
    1018           0 :   return std::max(mParsedFramesDuration, mParser->Info().mDuration);
    1019             : }
    1020             : 
    1021             : TimeUnit
    1022           0 : FlacTrackDemuxer::TimeAtEnd()
    1023             : {
    1024             :   // Scan the last 128kB if available to determine the last frame.
    1025             :   static const int OFFSET_FROM_END = 128 * 1024;
    1026             : 
    1027             :   // Seek to the end of the file and attempt to find the last frame.
    1028           0 :   MediaResourceIndex source(mSource.GetResource());
    1029           0 :   TimeUnit previousDuration;
    1030           0 :   TimeUnit previousTime;
    1031             : 
    1032           0 :   const int64_t streamLen = mSource.GetLength();
    1033           0 :   if (streamLen < 0) {
    1034           0 :     return TimeUnit::FromInfinity();
    1035             :   }
    1036             : 
    1037           0 :   flac::FrameParser parser;
    1038             : 
    1039           0 :   source.Seek(SEEK_SET, std::max<int64_t>(0LL, streamLen - OFFSET_FROM_END));
    1040           0 :   while (parser.FindNextFrame(source)) {
    1041             :     // FFmpeg flac muxer can generate a last frame with earlier than the others.
    1042           0 :     previousTime = std::max(previousTime, parser.CurrentFrame().Time());
    1043           0 :     if (parser.CurrentFrame().Duration() > TimeUnit()) {
    1044             :       // The last frame doesn't have a duration, so only update our duration
    1045             :       // if we do have one.
    1046           0 :       previousDuration = parser.CurrentFrame().Duration();
    1047             :     }
    1048           0 :     if (source.Tell() >= streamLen) {
    1049             :       // Limit the read, in case the length change half-way.
    1050           0 :       break;
    1051             :     }
    1052             :   }
    1053             : 
    1054             :   // Update our current progress stats.
    1055             :   mParsedFramesDuration =
    1056           0 :     previousTime + previousDuration - mParser->FirstFrame().Time();
    1057           0 :   mTotalFrameLen = streamLen - mParser->FirstFrame().Offset();
    1058             : 
    1059           0 :   return mParsedFramesDuration;
    1060             : }
    1061             : 
    1062             : /* static */ bool
    1063           0 : FlacDemuxer::FlacSniffer(const uint8_t* aData, const uint32_t aLength)
    1064             : {
    1065           0 :   if (aLength < FLAC_MAX_FRAME_HEADER_SIZE) {
    1066           0 :     return false;
    1067             :   }
    1068             : 
    1069           0 :   flac::Frame frame;
    1070           0 :   return frame.FindNext(aData, aLength - FLAC_MAX_FRAME_HEADER_SIZE) >= 0;
    1071             : }
    1072             : 
    1073             : } // namespace mozilla

Generated by: LCOV version 1.13