tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FlacFrameParser.cpp (7529B)


      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 "FlacFrameParser.h"
      8 
      9 #include "BufferReader.h"
     10 #include "OggCodecState.h"
     11 #include "OpusParser.h"
     12 #include "VideoUtils.h"
     13 #include "mozilla/Try.h"
     14 #include "nsTArray.h"
     15 
     16 namespace mozilla {
     17 
     18 #define OGG_FLAC_METADATA_TYPE_STREAMINFO 0x7F
     19 #define FLAC_STREAMINFO_SIZE 34
     20 
     21 #define BITMASK(x) ((1ULL << x) - 1)
     22 
     23 enum {
     24  FLAC_METADATA_TYPE_STREAMINFO = 0,
     25  FLAC_METADATA_TYPE_PADDING,
     26  FLAC_METADATA_TYPE_APPLICATION,
     27  FLAC_METADATA_TYPE_SEEKTABLE,
     28  FLAC_METADATA_TYPE_VORBIS_COMMENT,
     29  FLAC_METADATA_TYPE_CUESHEET,
     30  FLAC_METADATA_TYPE_PICTURE,
     31  FLAC_METADATA_TYPE_INVALID = 127
     32 };
     33 
     34 FlacFrameParser::FlacFrameParser()
     35    : mMinBlockSize(0),
     36      mMaxBlockSize(0),
     37      mMinFrameSize(0),
     38      mMaxFrameSize(0),
     39      mNumFrames(0),
     40      mFullMetadata(false),
     41      mPacketCount(0) {};
     42 
     43 FlacFrameParser::~FlacFrameParser() = default;
     44 
     45 uint32_t FlacFrameParser::HeaderBlockLength(const uint8_t* aPacket) const {
     46  uint32_t extra = 4;
     47  if (aPacket[0] == 'f') {
     48    // This must be the first block read, which contains the fLaC signature.
     49    aPacket += 4;
     50    extra += 4;
     51  }
     52  return (BigEndian::readUint32(aPacket) & BITMASK(24)) + extra;
     53 }
     54 
     55 Result<Ok, nsresult> FlacFrameParser::DecodeHeaderBlock(const uint8_t* aPacket,
     56                                                        size_t aLength) {
     57  if (aLength < 4 || aPacket[0] == 0xff) {
     58    // Not a header block.
     59    return Err(NS_ERROR_FAILURE);
     60  }
     61  BufferReader br(aPacket, aLength);
     62 
     63  mPacketCount++;
     64 
     65  if (aPacket[0] == 'f') {
     66    if (mPacketCount != 1 || memcmp(br.Read(4), "fLaC", 4) ||
     67        br.Remaining() != FLAC_STREAMINFO_SIZE + 4) {
     68      return Err(NS_ERROR_FAILURE);
     69    }
     70  }
     71  uint8_t blockHeader = MOZ_TRY(br.ReadU8());
     72  // blockType is a misnomer as it could indicate here either a packet type
     73  // should it points to the start of a Flac in Ogg metadata, or an actual
     74  // block type as per the flac specification.
     75  uint32_t blockType = blockHeader & 0x7f;
     76  bool lastBlock = blockHeader & 0x80;
     77 
     78  if (blockType == OGG_FLAC_METADATA_TYPE_STREAMINFO) {
     79    if (mPacketCount != 1 || memcmp(br.Read(4), "FLAC", 4) ||
     80        br.Remaining() != FLAC_STREAMINFO_SIZE + 12) {
     81      return Err(NS_ERROR_FAILURE);
     82    }
     83    uint32_t major = MOZ_TRY(br.ReadU8());
     84    if (major != 1) {
     85      // unsupported version;
     86      return Err(NS_ERROR_FAILURE);
     87    }
     88    MOZ_TRY(br.ReadU8());  // minor version
     89    uint32_t header = MOZ_TRY(br.ReadU16());
     90    mNumHeaders = Some(header);
     91    br.Read(4);  // fLaC
     92    blockType = MOZ_TRY(br.ReadU8());
     93    blockType &= BITMASK(7);
     94    // First METADATA_BLOCK_STREAMINFO
     95    if (blockType != FLAC_METADATA_TYPE_STREAMINFO) {
     96      // First block must be a stream info.
     97      return Err(NS_ERROR_FAILURE);
     98    }
     99  }
    100 
    101  uint32_t blockDataSize = MOZ_TRY(br.ReadU24());
    102  const uint8_t* blockDataStart = br.Peek(blockDataSize);
    103  if (!blockDataStart) {
    104    // Incomplete block.
    105    return Err(NS_ERROR_FAILURE);
    106  }
    107 
    108  switch (blockType) {
    109    case FLAC_METADATA_TYPE_STREAMINFO: {
    110      if (mPacketCount != 1 || blockDataSize != FLAC_STREAMINFO_SIZE) {
    111        // STREAMINFO must be the first metadata block found, and its size
    112        // is constant.
    113        return Err(NS_ERROR_FAILURE);
    114      }
    115 
    116      mMinBlockSize = MOZ_TRY(br.ReadU16());
    117      mMaxBlockSize = MOZ_TRY(br.ReadU16());
    118      mMinFrameSize = MOZ_TRY(br.ReadU24());
    119      mMaxFrameSize = MOZ_TRY(br.ReadU24());
    120 
    121      uint64_t blob = MOZ_TRY(br.ReadU64());
    122      uint32_t sampleRate = (blob >> 44) & BITMASK(20);
    123      if (!sampleRate) {
    124        return Err(NS_ERROR_FAILURE);
    125      }
    126      uint32_t numChannels = ((blob >> 41) & BITMASK(3)) + 1;
    127      if (numChannels > FLAC_MAX_CHANNELS) {
    128        return Err(NS_ERROR_FAILURE);
    129      }
    130      uint32_t bps = ((blob >> 36) & BITMASK(5)) + 1;
    131      if (bps > 24) {
    132        return Err(NS_ERROR_FAILURE);
    133      }
    134      mNumFrames = blob & BITMASK(36);
    135 
    136      mInfo.mMimeType = "audio/flac";
    137      mInfo.mRate = sampleRate;
    138      mInfo.mChannels = numChannels;
    139      mInfo.mBitDepth = bps;
    140      FlacCodecSpecificData flacCodecSpecificData;
    141      flacCodecSpecificData.mStreamInfoBinaryBlob->AppendElements(
    142          blockDataStart, blockDataSize);
    143      mInfo.mCodecSpecificConfig =
    144          AudioCodecSpecificVariant{std::move(flacCodecSpecificData)};
    145      auto duration = media::TimeUnit(mNumFrames, sampleRate);
    146      mInfo.mDuration = duration.IsValid() ? duration : media::TimeUnit::Zero();
    147      mParser = MakeUnique<OpusParser>();
    148      break;
    149    }
    150    case FLAC_METADATA_TYPE_VORBIS_COMMENT: {
    151      if (!mParser) {
    152        // We must have seen a valid streaminfo first.
    153        return Err(NS_ERROR_FAILURE);
    154      }
    155      nsTArray<uint8_t> comments(blockDataSize + 8);
    156      comments.AppendElements("OpusTags", 8);
    157      comments.AppendElements(blockDataStart, blockDataSize);
    158      if (!mParser->DecodeTags(comments.Elements(), comments.Length())) {
    159        return Err(NS_ERROR_FAILURE);
    160      }
    161      break;
    162    }
    163    default:
    164      break;
    165  }
    166 
    167  if (mNumHeaders && mPacketCount > mNumHeaders.ref() + 1) {
    168    // Received too many header block. assuming invalid.
    169    return Err(NS_ERROR_FAILURE);
    170  }
    171 
    172  if (lastBlock || (mNumHeaders && mNumHeaders.ref() + 1 == mPacketCount)) {
    173    mFullMetadata = true;
    174  }
    175 
    176  return Ok();
    177 }
    178 
    179 int64_t FlacFrameParser::BlockDuration(const uint8_t* aPacket,
    180                                       size_t aLength) const {
    181  if (!mInfo.IsValid()) {
    182    return -1;
    183  }
    184  if (mMinBlockSize == mMaxBlockSize) {
    185    // block size is fixed, use this instead of looking at the frame header.
    186    return mMinBlockSize;
    187  }
    188  // TODO
    189  return 0;
    190 }
    191 
    192 Result<bool, nsresult> FlacFrameParser::IsHeaderBlock(const uint8_t* aPacket,
    193                                                      size_t aLength) const {
    194  // Ogg Flac header
    195  // The one-byte packet type 0x7F
    196  // The four-byte ASCII signature "FLAC", i.e. 0x46, 0x4C, 0x41, 0x43
    197 
    198  // Flac header:
    199  // "fLaC", the FLAC stream marker in ASCII, meaning byte 0 of the stream is
    200  // 0x66, followed by 0x4C 0x61 0x43
    201 
    202  // If we detect either a ogg or plain flac header, then it must be valid.
    203  if (aLength < 4 || aPacket[0] == 0xff) {
    204    // A header is at least 4 bytes.
    205    return false;
    206  }
    207  if (aPacket[0] == 0x7f) {
    208    // Ogg packet
    209    BufferReader br(aPacket + 1, aLength - 1);
    210    const uint8_t* signature = br.Read(4);
    211    return signature && !memcmp(signature, "FLAC", 4);
    212  }
    213  BufferReader br(aPacket, aLength - 1);
    214  const uint8_t* signature = br.Read(4);
    215  if (signature && !memcmp(signature, "fLaC", 4)) {
    216    // Flac start header, must have STREAMINFO as first metadata block;
    217    uint32_t blockType = MOZ_TRY(br.ReadU8());
    218    blockType &= 0x7f;
    219    return blockType == FLAC_METADATA_TYPE_STREAMINFO;
    220  }
    221  char type = aPacket[0] & 0x7f;
    222  return type >= 1 && type <= 6;
    223 }
    224 
    225 UniquePtr<MetadataTags> FlacFrameParser::GetTags() const {
    226  if (!mParser) {
    227    return nullptr;
    228  }
    229 
    230  auto tags = MakeUnique<MetadataTags>();
    231  for (uint32_t i = 0; i < mParser->mTags.Length(); i++) {
    232    OggCodecState::AddVorbisComment(tags, mParser->mTags[i].Data(),
    233                                    mParser->mTags[i].Length());
    234  }
    235 
    236  return tags;
    237 }
    238 
    239 }  // namespace mozilla