Exemple #1
0
void APE::Properties::analyzeCurrent(File *file)
{
  // Read the descriptor
  file->seek(2, File::Current);
  const ByteVector descriptor = file->readBlock(44);
  if(descriptor.size() < 44) {
    debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
    return;
  }

  const unsigned int descriptorBytes = descriptor.toUInt(0, false);

  if((descriptorBytes - 52) > 0)
    file->seek(descriptorBytes - 52, File::Current);

  // Read the header
  const ByteVector header = file->readBlock(24);
  if(header.size() < 24) {
    debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
    return;
  }

  // Get the APE info
  d->channels      = header.toShort(18, false);
  d->sampleRate    = header.toUInt(20, false);
  d->bitsPerSample = header.toShort(16, false);

  const unsigned int totalFrames = header.toUInt(12, false);
  if(totalFrames == 0)
    return;

  const unsigned int blocksPerFrame   = header.toUInt(4, false);
  const unsigned int finalFrameBlocks = header.toUInt(8, false);
  d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
}
Exemple #2
0
void
MP4::Tag::parseCovr(const MP4::Atom *atom)
{
  MP4::CoverArtList value;
  ByteVector data = d->file->readBlock(atom->length - 8);
  unsigned int pos = 0;
  while(pos < data.size()) {
    const int length = static_cast<int>(data.toUInt(pos));
    if(length < 12) {
      debug("MP4: Too short atom");
      break;;
    }

    const ByteVector name = data.mid(pos + 4, 4);
    const int flags = static_cast<int>(data.toUInt(pos + 8));
    if(name != "data") {
      debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
      break;
    }
    if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP ||
       flags == TypeGIF || flags == TypeImplicit) {
      value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
                                 data.mid(pos + 16, length - 16)));
    }
    else {
      debug("MP4: Unknown covr format " + String::number(flags));
    }
    pos += length;
  }
  if(value.size() > 0)
    addItem(atom->name, value);
}
void APE::Footer::parse(const ByteVector &data)
{
    if ( data.size() < size() ) {
        return;
    }

    // The first eight bytes, data[0..7], are the File Identifier, "APETAGEX".

    // Read the version number

    d->version = data.toUInt(8, false);

    // Read the tag size

    d->tagSize = data.toUInt(12, false);

    // Read the item count

    d->itemCount = data.toUInt(16, false);

    // Read the flags

    std::bitset<32> flags( TAGLIB_CONSTRUCT_BITSET( data.toUInt(20, false) ) );

    d->headerPresent = flags[31];
    d->footerPresent = !flags[30];
    d->isHeader      = flags[29];
}
Exemple #4
0
MP4::AtomDataList MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
{
    AtomDataList result;
    ByteVector   data = file->readBlock(atom->length - 8);
    int          i    = 0;
    unsigned int pos  = 0;

    while ( pos < data.size() ) {
        const int  length = static_cast<int> ( data.toUInt(pos) );
        ByteVector name   = data.mid(pos + 4, 4);
        const int  flags  = static_cast<int> ( data.toUInt(pos + 8) );
        if ( freeForm && (i < 2) ) {
            if ( (i == 0) && (name != "mean") ) {
                debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
                return result;
            } else if ( (i == 1) && (name != "name") ) {
                debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
                return result;
            }
            result.append( AtomData( AtomDataType(flags), data.mid(pos + 12, length - 12) ) );
        } else {
            if ( name != "data" ) {
                debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
                return result;
            }
            if ( (expectedFlags == -1) || (flags == expectedFlags) ) {
                result.append( AtomData( AtomDataType(flags), data.mid(pos + 16, length - 16) ) );
            }
        }
        pos += length;
        i++;
    }
    return result;
}
Exemple #5
0
void Vorbis::Properties::read()
{
  // Get the identification header from the Ogg implementation.

  ByteVector data = d->file->packet(0);

  uint pos = 0;

  if(data.mid(pos, 7) != vorbisSetupHeaderID) {
    debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
    return;
  }

  pos += 7;

  d->vorbisVersion = data.toUInt(pos, false);
  pos += 4;

  d->channels = uchar(data[pos]);
  pos += 1;

  d->sampleRate = data.toUInt(pos, false);
  pos += 4;

  d->bitrateMaximum = data.toUInt(pos, false);
  pos += 4;

  d->bitrateNominal = data.toUInt(pos, false);
  pos += 4;

  d->bitrateMinimum = data.toUInt(pos, false);

  // TODO: Later this should be only the "fast" mode.
  d->bitrate = d->bitrateNominal;

  // Find the length of the file.  See http://wiki.xiph.org/VorbisStreamLength/
  // for my notes on the topic.

  const Ogg::PageHeader *first = d->file->firstPageHeader();
  const Ogg::PageHeader *last = d->file->lastPageHeader();

  if(first && last) {
    long long start = first->absoluteGranularPosition();
    long long end = last->absoluteGranularPosition();

    if(start >= 0 && end >= 0 && d->sampleRate > 0)
      d->length = (int)((end - start) / (long long) d->sampleRate);
    else
      debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
            "end of this file was incorrect or the sample rate is zero.");
  }
  else
    debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages.");
}
Exemple #6
0
void RIFF::WAV::Properties::read(const ByteVector &data)
{
  d->format      = data.toShort(0, false);
  d->channels    = data.toShort(2, false);
  d->sampleRate  = data.toUInt(4, false);
  d->sampleWidth = data.toShort(14, false);

  const uint byteRate = data.toUInt(8, false);
  d->bitrate = byteRate * 8 / 1000;

  d->length = byteRate > 0 ? d->streamLength / byteRate : 0;
  if(d->channels > 0 && d->sampleWidth > 0)
    d->sampleFrames = d->streamLength / (d->channels * ((d->sampleWidth + 7) / 8));
}
void ASF::Picture::parse(const ByteVector& bytes)
{
  d->valid = false;
  if(bytes.size() < 9)
    return;
  int pos = 0;
  d->type = (Type)bytes[0]; ++pos;
  const uint dataLen = bytes.toUInt(pos, false); pos+=4;

  const ByteVector nullStringTerminator(2, 0);

  int endPos = bytes.find(nullStringTerminator, pos, 2);
  if(endPos < 0)
    return;
  d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
  pos = endPos+2;

  endPos = bytes.find(nullStringTerminator, pos, 2);
  if(endPos < 0)
    return;
  d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
  pos = endPos+2;

  if(dataLen + pos != bytes.size())
    return;

  d->picture = bytes.mid(pos, dataLen);
  d->valid = true;
  return;
}
Exemple #8
0
void APE::Item::parse(const ByteVector &data)
{
  // 11 bytes is the minimum size for an APE item

  if(data.size() < 11) {
    debug("APE::Item::parse() -- no data in item");
    return;
  }

  const uint valueLength  = data.toUInt(0, false);
  const uint flags        = data.toUInt(4, false);
  
  const int keyLength = data.find('\0', 8) - 8;
  if(keyLength < 1 || keyLength > 255)
  {
    debug("APE::Item::parse() -- invalid key in item");
    return;
  }

  d->key = String(data.mid(8, keyLength), String::UTF8);
  const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);

  setReadOnly(flags & 1);
  setType(ItemTypes((flags >> 1) & 3));

  if(Text == d->type) 
    d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
  else
    d->value = value;
}
void Ogg::XiphComment::parse(const ByteVector &data)
{
  // The first thing in the comment data is the vendor ID length, followed by a
  // UTF8 string with the vendor ID.

  uint pos = 0;

  const uint vendorLength = data.toUInt(0, false);
  pos += 4;

  d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
  pos += vendorLength;

  // Next the number of fields in the comment vector.

  const uint commentFields = data.toUInt(pos, false);
  pos += 4;

  if(commentFields > (data.size() - 8) / 4) {
    return;
  }

  for(uint i = 0; i < commentFields; i++) {

    // Each comment field is in the format "KEY=value" in a UTF8 string and has
    // 4 bytes before the text starts that gives the length.

    const uint commentLength = data.toUInt(pos, false);
    pos += 4;

    String comment = String(data.mid(pos, commentLength), String::UTF8);
    pos += commentLength;
    if(pos > data.size()) {
      break;
    }

    int commentSeparatorPosition = comment.find("=");
    if(commentSeparatorPosition == -1) {
      break;
    }

    String key = comment.substr(0, commentSeparatorPosition);
    String value = comment.substr(commentSeparatorPosition + 1);

    addField(key, value, false);
  }
}
void Opus::Properties::read(File *file)
{
  // Get the identification header from the Ogg implementation.

  // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1

  const ByteVector data = file->packet(0);

  // *Magic Signature*
  uint pos = 8;

  // *Version* (8 bits, unsigned)
  d->opusVersion = uchar(data.at(pos));
  pos += 1;

  // *Output Channel Count* 'C' (8 bits, unsigned)
  d->channels = uchar(data.at(pos));
  pos += 1;

  // *Pre-skip* (16 bits, unsigned, little endian)
  const ushort preSkip = data.toUShort(pos, false);
  pos += 2;

  // *Input Sample Rate* (32 bits, unsigned, little endian)
  d->inputSampleRate = data.toUInt(pos, false);
  pos += 4;

  // *Output Gain* (16 bits, signed, little endian)
  pos += 2;

  // *Channel Mapping Family* (8 bits, unsigned)
  pos += 1;

  const Ogg::PageHeader *first = file->firstPageHeader();
  const Ogg::PageHeader *last  = file->lastPageHeader();

  if(first && last) {
    const long long start = first->absoluteGranularPosition();
    const long long end   = last->absoluteGranularPosition();

    if(start >= 0 && end >= 0) {
      const long long frameCount = (end - start - preSkip);

      if(frameCount > 0) {
        const double length = frameCount * 1000.0 / 48000.0;
        d->length  = static_cast<int>(length + 0.5);
        d->bitrate = static_cast<int>(file->length() * 8.0 / length + 0.5);
      }
    }
    else {
      debug("Opus::Properties::read() -- The PCM values for the start or "
            "end of this file was incorrect.");
    }
  }
  else
    debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages.");
}
void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
{
  const int end = data.size();
  if(end < 7) {
    debug("A synchronized lyrics frame must contain at least 7 bytes.");
    return;
  }

  d->textEncoding = String::Type(data[0]);
  d->language = data.mid(1, 3);
  d->timestampFormat = TimestampFormat(data[4]);
  d->type = Type(data[5]);

  int pos = 6;

  d->description = readStringField(data, d->textEncoding, &pos);
  if(d->description.isEmpty())
    return;

  /*
   * If UTF16 strings are found in SYLT frames, a BOM may only be
   * present in the first string (content descriptor), and the strings of
   * the synchronized text have no BOM. Here the BOM is read from
   * the first string to have a specific encoding with endianness for the
   * case of strings without BOM so that readStringField() will work.
   */
  String::Type encWithEndianness = d->textEncoding;
  if(d->textEncoding == String::UTF16) {
    unsigned short bom = data.toUShort(6, true);
    if(bom == 0xfffe) {
      encWithEndianness = String::UTF16LE;
    } else if(bom == 0xfeff) {
      encWithEndianness = String::UTF16BE;
    }
  }

  d->synchedText.clear();
  while(pos < end) {
    String::Type enc = d->textEncoding;
    // If a UTF16 string has no BOM, use the encoding found above.
    if(enc == String::UTF16 && pos + 1 < end) {
      unsigned short bom = data.toUShort(pos, true);
      if(bom != 0xfffe && bom != 0xfeff) {
        enc = encWithEndianness;
      }
    }
    String text = readStringField(data, enc, &pos);
    if(text.isEmpty() || pos + 4 > end)
      return;

    unsigned int time = data.toUInt(pos, true);
    pos += 4;

    d->synchedText.append(SynchedText(time, text));
  }
}
Exemple #12
0
void RIFF::AIFF::Properties::read(const ByteVector &data)
{
  d->channels       = data.toShort(0U);
  d->sampleFrames   = data.toUInt(2U);
  d->sampleWidth    = data.toShort(6U);
  double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<const uchar *>(data.data() + 8));
  d->sampleRate     = (int)sampleRate;
  d->bitrate        = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0);
  d->length         = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
}
Exemple #13
0
void RIFF::Info::Tag::parse(const ByteVector &data)
{
  uint p = 4;
  while(p < data.size()) {
    const uint size = data.toUInt(p + 4, false);
    d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size));

    p += ((size + 1) & ~1) + 8;
  }
}
Exemple #14
0
unsigned int ASF::File::readDWORD(bool *ok)
{
  ByteVector v = readBlock(4);
  if(v.size() != 4) {
    if(ok) *ok = false;
    return 0;
  }
  if(ok) *ok = true;
  return v.toUInt(false);
}
Exemple #15
0
void ChapterFrame::parseFields(const ByteVector &data)
{
  unsigned int size = data.size();
  if(size < 18) {
    debug("A CHAP frame must contain at least 18 bytes (1 byte element ID "
          "terminated by null and 4x4 bytes for start and end time and offset).");
    return;
  }

  int pos = 0;
  unsigned int embPos = 0;
  d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
  d->startTime = data.toUInt(pos, true);
  pos += 4;
  d->endTime = data.toUInt(pos, true);
  pos += 4;
  d->startOffset = data.toUInt(pos, true);
  pos += 4;
  d->endOffset = data.toUInt(pos, true);
  pos += 4;
  size -= pos;

  // Embedded frames are optional

  if(size < header()->size())
    return;

  while(embPos < size - header()->size()) {
    Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));

    if(!frame)
      return;

    // Checks to make sure that frame parsed correctly.
    if(frame->size() <= 0) {
      delete frame;
      return;
    }

    embPos += frame->size() + header()->size();
    addEmbeddedFrame(frame);
  }
}
Exemple #16
0
void MPEG::VbriHeader::parse(const ByteVector &data)
{
  // Check to see if a valid VBRI header is available.

  if(!data.startsWith("VBRI"))
    return;

  d->size   = data.toUInt(10, true);
  d->frames = data.toUInt(14, true);

  d->valid = true;
}
Exemple #17
0
void APE::Properties::analyzeOld(File *file)
{
  const ByteVector header = file->readBlock(26);
  if(header.size() < 26) {
    debug("APE::Properties::analyzeOld() -- MAC header is too short.");
    return;
  }

  const unsigned int totalFrames = header.toUInt(18, false);

  // Fail on 0 length APE files (catches non-finalized APE files)
  if(totalFrames == 0)
    return;

  const short compressionLevel = header.toShort(0, false);
  unsigned int blocksPerFrame;
  if(d->version >= 3950)
    blocksPerFrame = 73728 * 4;
  else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
    blocksPerFrame = 73728;
  else
    blocksPerFrame = 9216;

  // Get the APE info
  d->channels   = header.toShort(4, false);
  d->sampleRate = header.toUInt(6, false);

  const unsigned int finalFrameBlocks = header.toUInt(22, false);
  d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;

  // Get the bit depth from the RIFF-fmt chunk.
  file->seek(16, File::Current);
  const ByteVector fmt = file->readBlock(28);
  if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
    debug("APE::Properties::analyzeOld() -- fmt header is too short.");
    return;
  }

  d->bitsPerSample = fmt.toShort(26, false);
}
void PopularimeterFrame::parseFields(const ByteVector &data)
{
  int pos = 0, size = int(data.size());

  d->email = readStringField(data, String::Latin1, &pos);

  d->rating = 0;
  d->counter = 0;
  if(pos < size) {
    d->rating = (unsigned char)(data[pos++]);
    if(pos < size) {
      d->counter = data.toUInt(static_cast<unsigned int>(pos));
    }
  }
}
bool FLAC::Picture::parse(const ByteVector &data)
{
  if(data.size() < 32) {
    debug("A picture block must contain at least 5 bytes.");
    return false;
  }

  unsigned int pos = 0;
  d->type = FLAC::Picture::Type(data.toUInt(pos));
  pos += 4;
  unsigned int mimeTypeLength = data.toUInt(pos);
  pos += 4;
  if(pos + mimeTypeLength + 24 > data.size()) {
    debug("Invalid picture block.");
    return false;
  }
  d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
  pos += mimeTypeLength;
  unsigned int descriptionLength = data.toUInt(pos);
  pos += 4;
  if(pos + descriptionLength + 20 > data.size()) {
    debug("Invalid picture block.");
    return false;
  }
  d->description = String(data.mid(pos, descriptionLength), String::UTF8);
  pos += descriptionLength;
  d->width = data.toUInt(pos);
  pos += 4;
  d->height = data.toUInt(pos);
  pos += 4;
  d->colorDepth = data.toUInt(pos);
  pos += 4;
  d->numColors = data.toUInt(pos);
  pos += 4;
  unsigned int dataLength = data.toUInt(pos);
  pos += 4;
  if(pos + dataLength > data.size()) {
    debug("Invalid picture block.");
    return false;
  }
  d->data = data.mid(pos, dataLength);

  return true;
}
Exemple #20
0
MP4::Atom::Atom(File *file)
{
  children.setAutoDelete(true);

  offset = file->tell();
  ByteVector header = file->readBlock(8);
  if(header.size() != 8) {
    // The atom header must be 8 bytes long, otherwise there is either
    // trailing garbage or the file is truncated
    debug("MP4: Couldn't read 8 bytes of data for atom header");
    length = 0;
    file->seek(0, File::End);
    return;
  }

  length = header.toUInt();

  if(length == 0) {
    // The last atom which extends to the end of the file.
    length = file->length() - offset;
  }
  else if(length == 1) {
    // The atom has a 64-bit length.
    const long long longLength = file->readBlock(8).toLongLong();
    if(longLength <= LONG_MAX) {
      // The actual length fits in long. That's always the case if long is 64-bit.
      length = static_cast<long>(longLength);
    }
    else {
      debug("MP4: 64-bit atoms are not supported");
      length = 0;
      file->seek(0, File::End);
      return;
    }
  }

  if(length < 8) {
    debug("MP4: Invalid atom size");
    length = 0;
    file->seek(0, File::End);
    return;
  }

  name = header.mid(4, 4);

  for(int i = 0; i < numContainers; i++) {
    if(name == containers[i]) {
      if(name == "meta") {
        file->seek(4, File::Current);
      }
      else if(name == "stsd") {
        file->seek(8, File::Current);
      }
      while(file->tell() < offset + length) {
        MP4::Atom *child = new MP4::Atom(file);
        children.append(child);
        if(child->length == 0)
          return;
      }
      return;
    }
  }

  file->seek(offset + length);
}
Exemple #21
0
unsigned int ASF::File::readDWORD()
{
  ByteVector v = readBlock(4);
  return v.toUInt(false);
}
void Speex::Properties::read(File *file)
{
  // Get the identification header from the Ogg implementation.

  const ByteVector data = file->packet(0);
  if(data.size() < 64) {
    debug("Speex::Properties::read() -- data is too short.");
    return;
  }

  unsigned int pos = 28;

  // speex_version_id;       /**< Version for Speex (for checking compatibility) */
  d->speexVersion = data.toUInt(pos, false);
  pos += 4;

  // header_size;            /**< Total size of the header ( sizeof(SpeexHeader) ) */
  pos += 4;

  // rate;                   /**< Sampling rate used */
  d->sampleRate = data.toUInt(pos, false);
  pos += 4;

  // mode;                   /**< Mode used (0 for narrowband, 1 for wideband) */
  d->mode = data.toUInt(pos, false);
  pos += 4;

  // mode_bitstream_version; /**< Version ID of the bit-stream */
  pos += 4;

  // nb_channels;            /**< Number of channels encoded */
  d->channels = data.toUInt(pos, false);
  pos += 4;

  // bitrate;                /**< Bit-rate used */
  d->bitrateNominal = data.toUInt(pos, false);
  pos += 4;

  // frame_size;             /**< Size of frames */
  // unsigned int frameSize = data.mid(pos, 4).toUInt(false);
  pos += 4;

  // vbr;                    /**< 1 for a VBR encoding, 0 otherwise */
  d->vbr = data.toUInt(pos, false) == 1;
  pos += 4;

  // frames_per_packet;      /**< Number of frames stored per Ogg packet */
  // unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false);

  const Ogg::PageHeader *first = file->firstPageHeader();
  const Ogg::PageHeader *last  = file->lastPageHeader();

  if(first && last) {
    const long long start = first->absoluteGranularPosition();
    const long long end   = last->absoluteGranularPosition();

    if(start >= 0 && end >= 0 && d->sampleRate > 0) {
      const long long frameCount = end - start;

      if(frameCount > 0) {
        const double length = frameCount * 1000.0 / d->sampleRate;
        d->length  = static_cast<int>(length + 0.5);
        d->bitrate = static_cast<int>(file->length() * 8.0 / length + 0.5);
      }
    }
    else {
      debug("Speex::Properties::read() -- Either the PCM values for the start or "
            "end of this file was incorrect or the sample rate is zero.");
    }
  }
  else
    debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages.");

  // Alternative to the actual average bitrate.

  if(d->bitrate == 0 && d->bitrateNominal > 0)
    d->bitrate = static_cast<int>(d->bitrateNominal / 1000.0 + 0.5);
}
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
  : AudioProperties(style)
{
  d = new PropertiesPrivate;

  MP4::Atom *moov = atoms->find("moov");
  if(!moov) {
    debug("MP4: Atom 'moov' not found");
    return;
  }

  MP4::Atom *trak = 0;
  ByteVector data;

  MP4::AtomList trakList = moov->findall("trak");
  for (unsigned int i = 0; i < trakList.size(); i++) {
    trak = trakList[i];
    MP4::Atom *hdlr = trak->find("mdia", "hdlr");
    if(!hdlr) {
      debug("MP4: Atom 'trak.mdia.hdlr' not found");
      return;
    }
    file->seek(hdlr->offset);
    data = file->readBlock(hdlr->length);
    if(data.mid(16, 4) == "soun") {
      break;
    }
    trak = 0;
  }
  if (!trak) {
    debug("MP4: No audio tracks");
    return;
  }

  MP4::Atom *mdhd = trak->find("mdia", "mdhd");
  if(!mdhd) {
    debug("MP4: Atom 'trak.mdia.mdhd' not found");
    return;
  }

  file->seek(mdhd->offset);
  data = file->readBlock(mdhd->length);
  uint version = data[8];
  if(version == 1) {
    if (data.size() < 36 + 8) {
      debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
      return;
    }
    const long long unit   = data.toLongLong(28U);
    const long long length = data.toLongLong(36U);
    d->length = unit ? int(length / unit) : 0;
  }
  else {
    if (data.size() < 24 + 4) {
      debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
      return;
    }
    const unsigned int unit   = data.toUInt(20U);
    const unsigned int length = data.toUInt(24U);
    d->length = unit ? length / unit : 0;
  }

  MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
  if(!atom) {
    return;
  }

  file->seek(atom->offset);
  data = file->readBlock(atom->length);
  if(data.mid(20, 4) == "mp4a") {
    d->channels      = data.toShort(40U);
    d->bitsPerSample = data.toShort(42U);
    d->sampleRate    = data.toUInt(46U);
    if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
      uint pos = 65;
      if(data.mid(pos, 3) == "\x80\x80\x80") {
        pos += 3;
      }
      pos += 4;
      if(data[pos] == 0x04) {
        pos += 1;
        if(data.mid(pos, 3) == "\x80\x80\x80") {
          pos += 3;
        }
        pos += 10;
        d->bitrate = (data.toUInt(pos) + 500) / 1000;
      }
    }
  }
  else if (data.mid(20, 4) == "alac") {
    if (atom->length == 88 && data.mid(56, 4) == "alac") {
      d->bitsPerSample = data.at(69);
      d->channels   = data.at(73);
      d->bitrate    = data.toUInt(80U) / 1000;
      d->sampleRate = data.toUInt(84U);
    }
  }

  MP4::Atom *drms = atom->find("drms");
  if(drms) {
    d->encrypted = true;
  }
}
Exemple #24
0
void Frame::Header::setData(const ByteVector &data, uint version)
{
  d->version = version;

  switch(version) {
  case 0:
  case 1:
  case 2:
  {
    // ID3v2.2

    if(data.size() < 3) {
      debug("You must at least specify a frame ID.");
      return;
    }

    // Set the frame ID -- the first three bytes

    d->frameID = data.mid(0, 3);

    // If the full header information was not passed in, do not continue to the
    // steps to parse the frame size and flags.

    if(data.size() < 6) {
      d->frameSize = 0;
      return;
    }

    d->frameSize = data.toUInt(3, 3, true);

    break;
  }
  case 3:
  {
    // ID3v2.3

    if(data.size() < 4) {
      debug("You must at least specify a frame ID.");
      return;
    }

    // Set the frame ID -- the first four bytes

    d->frameID = data.mid(0, 4);

    // If the full header information was not passed in, do not continue to the
    // steps to parse the frame size and flags.

    if(data.size() < 10) {
      d->frameSize = 0;
      return;
    }

    // Set the size -- the frame size is the four bytes starting at byte four in
    // the frame header (structure 4)

    d->frameSize = data.toUInt(4U);

    { // read the first byte of flags
      std::bitset<8> flags(data[8]);
      d->tagAlterPreservation  = flags[7]; // (structure 3.3.1.a)
      d->fileAlterPreservation = flags[6]; // (structure 3.3.1.b)
      d->readOnly              = flags[5]; // (structure 3.3.1.c)
    }

    { // read the second byte of flags
      std::bitset<8> flags(data[9]);
      d->compression         = flags[7]; // (structure 3.3.1.i)
      d->encryption          = flags[6]; // (structure 3.3.1.j)
      d->groupingIdentity    = flags[5]; // (structure 3.3.1.k)
    }
    break;
  }
  case 4:
  default:
  {
    // ID3v2.4

    if(data.size() < 4) {
      debug("You must at least specify a frame ID.");
      return;
    }

    // Set the frame ID -- the first four bytes

    d->frameID = data.mid(0, 4);

    // If the full header information was not passed in, do not continue to the
    // steps to parse the frame size and flags.

    if(data.size() < 10) {
      d->frameSize = 0;
      return;
    }

    // Set the size -- the frame size is the four bytes starting at byte four in
    // the frame header (structure 4)

    d->frameSize = SynchData::toUInt(data.mid(4, 4));
#ifndef NO_ITUNES_HACKS
    // iTunes writes v2.4 tags with v2.3-like frame sizes
    if(d->frameSize > 127) {
      if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
        unsigned int uintSize = data.toUInt(4U);
        if(isValidFrameID(data.mid(uintSize + 10, 4))) {
          d->frameSize = uintSize;
        }
      }
    }
#endif

    { // read the first byte of flags
      std::bitset<8> flags(data[8]);
      d->tagAlterPreservation  = flags[6]; // (structure 4.1.1.a)
      d->fileAlterPreservation = flags[5]; // (structure 4.1.1.b)
      d->readOnly              = flags[4]; // (structure 4.1.1.c)
    }

    { // read the second byte of flags
      std::bitset<8> flags(data[9]);
      d->groupingIdentity    = flags[6]; // (structure 4.1.2.h)
      d->compression         = flags[3]; // (structure 4.1.2.k)
      d->encryption          = flags[2]; // (structure 4.1.2.m)
      d->unsynchronisation   = flags[1]; // (structure 4.1.2.n)
      d->dataLengthIndicator = flags[0]; // (structure 4.1.2.p)
    }
    break;
  }
  }
}
Exemple #25
0
void MPEG::Header::parse(File *file, long offset, bool checkLength)
{
  file->seek(offset);
  const ByteVector data = file->readBlock(4);

  if(data.size() < 4) {
    debug("MPEG::Header::parse() -- data is too short for an MPEG frame header.");
    return;
  }

  // Check for the MPEG synch bytes.

  if(!isFrameSync(data)) {
    debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch.");
    return;
  }

  // Set the MPEG version

  const int versionBits = (static_cast<unsigned char>(data[1]) >> 3) & 0x03;

  if(versionBits == 0)
    d->version = Version2_5;
  else if(versionBits == 2)
    d->version = Version2;
  else if(versionBits == 3)
    d->version = Version1;
  else {
    debug("MPEG::Header::parse() -- Invalid MPEG version bits.");
    return;
  }

  // Set the MPEG layer

  const int layerBits = (static_cast<unsigned char>(data[1]) >> 1) & 0x03;

  if(layerBits == 1)
    d->layer = 3;
  else if(layerBits == 2)
    d->layer = 2;
  else if(layerBits == 3)
    d->layer = 1;
  else {
    debug("MPEG::Header::parse() -- Invalid MPEG layer bits.");
    return;
  }

  d->protectionEnabled = (static_cast<unsigned char>(data[1] & 0x01) == 0);

  // Set the bitrate

  static const int bitrates[2][3][16] = {
    { // Version 1
      { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1
      { 0, 32, 48, 56, 64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2
      { 0, 32, 40, 48, 56,  64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 0 }  // layer 3
    },
    { // Version 2 or 2.5
      { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1
      { 0, 8,  16, 24, 32, 40, 48, 56,  64,  80,  96,  112, 128, 144, 160, 0 }, // layer 2
      { 0, 8,  16, 24, 32, 40, 48, 56,  64,  80,  96,  112, 128, 144, 160, 0 }  // layer 3
    }
  };

  const int versionIndex = (d->version == Version1) ? 0 : 1;
  const int layerIndex   = (d->layer > 0) ? d->layer - 1 : 0;

  // The bitrate index is encoded as the first 4 bits of the 3rd byte,
  // i.e. 1111xxxx

  const int bitrateIndex = (static_cast<unsigned char>(data[2]) >> 4) & 0x0F;

  d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex];

  if(d->bitrate == 0) {
    debug("MPEG::Header::parse() -- Invalid bit rate.");
    return;
  }

  // Set the sample rate

  static const int sampleRates[3][4] = {
    { 44100, 48000, 32000, 0 }, // Version 1
    { 22050, 24000, 16000, 0 }, // Version 2
    { 11025, 12000, 8000,  0 }  // Version 2.5
  };

  // The sample rate index is encoded as two bits in the 3nd byte, i.e. xxxx11xx

  const int samplerateIndex = (static_cast<unsigned char>(data[2]) >> 2) & 0x03;

  d->sampleRate = sampleRates[d->version][samplerateIndex];

  if(d->sampleRate == 0) {
    debug("MPEG::Header::parse() -- Invalid sample rate.");
    return;
  }

  // The channel mode is encoded as a 2 bit value at the end of the 3nd byte,
  // i.e. xxxxxx11

  d->channelMode = static_cast<ChannelMode>((static_cast<unsigned char>(data[3]) >> 6) & 0x03);

  // TODO: Add mode extension for completeness

  d->isOriginal    = ((static_cast<unsigned char>(data[3]) & 0x04) != 0);
  d->isCopyrighted = ((static_cast<unsigned char>(data[3]) & 0x08) != 0);
  d->isPadded      = ((static_cast<unsigned char>(data[2]) & 0x02) != 0);

  // Samples per frame

  static const int samplesPerFrame[3][2] = {
    // MPEG1, 2/2.5
    {  384,   384 }, // Layer I
    { 1152,  1152 }, // Layer II
    { 1152,   576 }  // Layer III
  };

  d->samplesPerFrame = samplesPerFrame[layerIndex][versionIndex];

  // Calculate the frame length

  static const int paddingSize[3] = { 4, 1, 1 };

  d->frameLength = d->samplesPerFrame * d->bitrate * 125 / d->sampleRate;

  if(d->isPadded)
    d->frameLength += paddingSize[layerIndex];

  if(checkLength) {

    // Check if the frame length has been calculated correctly, or the next frame
    // header is right next to the end of this frame.

    // The MPEG versions, layers and sample rates of the two frames should be
    // consistent. Otherwise, we assume that either or both of the frames are
    // broken.

    file->seek(offset + d->frameLength);
    const ByteVector nextData = file->readBlock(4);

    if(nextData.size() < 4) {
      debug("MPEG::Header::parse() -- Could not read the next frame header.");
      return;
    }

    const unsigned int HeaderMask = 0xfffe0c00;

    const unsigned int header     = data.toUInt(0, true)     & HeaderMask;
    const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask;

    if(header != nextHeader) {
      debug("MPEG::Header::parse() -- The next frame was not consistent with this frame.");
      return;
    }
  }

  // Now that we're done parsing, set this to be a valid frame.

  d->isValid = true;
}
void Ogg::PageHeader::read(Ogg::File *file, long pageOffset)
{
  file->seek(pageOffset);

  // An Ogg page header is at least 27 bytes, so we'll go ahead and read that
  // much and then get the rest when we're ready for it.

  const ByteVector data = file->readBlock(27);

  // Sanity check -- make sure that we were in fact able to read as much data as
  // we asked for and that the page begins with "OggS".

  if(data.size() != 27 || !data.startsWith("OggS")) {
    debug("Ogg::PageHeader::read() -- error reading page header");
    return;
  }

  const std::bitset<8> flags(data[5]);

  d->firstPacketContinued = flags.test(0);
  d->firstPageOfStream    = flags.test(1);
  d->lastPageOfStream     = flags.test(2);

  d->absoluteGranularPosition = data.toLongLong(6, false);
  d->streamSerialNumber = data.toUInt(14, false);
  d->pageSequenceNumber = data.toUInt(18, false);

  // Byte number 27 is the number of page segments, which is the only variable
  // length portion of the page header.  After reading the number of page
  // segments we'll then read in the corresponding data for this count.

  int pageSegmentCount = static_cast<unsigned char>(data[26]);

  const ByteVector pageSegments = file->readBlock(pageSegmentCount);

  // Another sanity check.

  if(pageSegmentCount < 1 || int(pageSegments.size()) != pageSegmentCount)
    return;

  // The base size of an Ogg page 27 bytes plus the number of lacing values.

  d->size = 27 + pageSegmentCount;

  int packetSize = 0;

  for(int i = 0; i < pageSegmentCount; i++) {
    d->dataSize += static_cast<unsigned char>(pageSegments[i]);
    packetSize += static_cast<unsigned char>(pageSegments[i]);

    if(static_cast<unsigned char>(pageSegments[i]) < 255) {
      d->packetSizes.append(packetSize);
      packetSize = 0;
    }
  }

  if(packetSize > 0) {
    d->packetSizes.append(packetSize);
    d->lastPacketCompleted = false;
  }
  else
    d->lastPacketCompleted = true;

  d->isValid = true;
}
Exemple #27
0
void Ogg::FLAC::File::scan()
{
  // Scan the metadata pages

  if(d->scanned)
    return;

  if(!isValid())
    return;

  int ipacket = 0;
  long overhead = 0;

  ByteVector metadataHeader = packet(ipacket);
  if(metadataHeader.isEmpty())
    return;

  if(!metadataHeader.startsWith("fLaC"))  {
    // FLAC 1.1.2+
    if(metadataHeader.mid(1, 4) != "FLAC")
      return;

    if(metadataHeader[5] != 1)
      return; // not version 1

    metadataHeader = metadataHeader.mid(13);
  }
  else {
    // FLAC 1.1.0 & 1.1.1
    metadataHeader = packet(++ipacket);
  }

  ByteVector header = metadataHeader.mid(0, 4);
  if(header.size() != 4) {
    debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header");
    return;
  }

  // Header format (from spec):
  // <1> Last-metadata-block flag
  // <7> BLOCK_TYPE
  //    0 : STREAMINFO
  //    1 : PADDING
  //    ..
  //    4 : VORBIS_COMMENT
  //    ..
  // <24> Length of metadata to follow

  char blockType = header[0] & 0x7f;
  bool lastBlock = (header[0] & 0x80) != 0;
  uint length = header.toUInt(1, 3, true);
  overhead += length;

  // Sanity: First block should be the stream_info metadata

  if(blockType != 0) {
    debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC stream");
    return;
  }

  d->streamInfoData = metadataHeader.mid(4, length);

  // Search through the remaining metadata

  while(!lastBlock) {
    metadataHeader = packet(++ipacket);
    header = metadataHeader.mid(0, 4);
    if(header.size() != 4) {
      debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header");
      return;
    }

    blockType = header[0] & 0x7f;
    lastBlock = (header[0] & 0x80) != 0;
    length = header.toUInt(1, 3, true);
    overhead += length;

    if(blockType == 1) {
      // debug("Ogg::FLAC::File::scan() -- Padding found");
    }
    else if(blockType == 4) {
      // debug("Ogg::FLAC::File::scan() -- Vorbis-comments found");
      d->xiphCommentData = metadataHeader.mid(4, length);
      d->hasXiphComment = true;
      d->commentPacket = ipacket;
    }
    else if(blockType > 5) {
      debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
    }
  }

  // End of metadata, now comes the datastream
  d->streamStart = overhead;
  d->streamLength = File::length() - d->streamStart;

  d->scanned = true;
}
void RIFF::WAV::Properties::read(File *file)
{
  ByteVector data;
  uint streamLength = 0;
  uint totalSamples = 0;

  for(uint i = 0; i < file->chunkCount(); ++i) {
    const ByteVector name = file->chunkName(i);
    if(name == "fmt ") {
      if(data.isEmpty())
        data = file->chunkData(i);
      else
        debug("RIFF::WAV::Properties::read() - Duplicate 'fmt ' chunk found.");
    }
    else if(name == "data") {
      if(streamLength == 0)
        streamLength = file->chunkDataSize(i) + file->chunkPadding(i);
      else
        debug("RIFF::WAV::Properties::read() - Duplicate 'data' chunk found.");
    }
    else if(name == "fact") {
      if(totalSamples == 0)
        totalSamples = file->chunkData(i).toUInt(0, false);
      else
        debug("RIFF::WAV::Properties::read() - Duplicate 'fact' chunk found.");
    }
  }

  if(data.size() < 16) {
    debug("RIFF::WAV::Properties::read() - 'fmt ' chunk not found or too short.");
    return;
  }

  if(streamLength == 0) {
    debug("RIFF::WAV::Properties::read() - 'data' chunk not found.");
    return;
  }

  d->format = data.toShort(0, false);
  if(d->format != FORMAT_PCM && totalSamples == 0) {
    debug("RIFF::WAV::Properties::read() - Non-PCM format, but 'fact' chunk not found.");
    return;
  }

  d->channels      = data.toShort(2, false);
  d->sampleRate    = data.toUInt(4, false);
  d->bitsPerSample = data.toShort(14, false);

  if(totalSamples > 0)
    d->sampleFrames = totalSamples;
  else if(d->channels > 0 && d->bitsPerSample > 0)
    d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8));

  if(d->sampleFrames > 0 && d->sampleRate > 0) {
    const double length = d->sampleFrames * 1000.0 / d->sampleRate;
    d->length  = static_cast<int>(length + 0.5);
    d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
  }
  else {
    const uint byteRate = data.toUInt(8, false);
    if(byteRate > 0) {
      d->length  = static_cast<int>(streamLength * 1000.0 / byteRate + 0.5);
      d->bitrate = static_cast<int>(byteRate * 8.0 / 1000.0 + 0.5);
    }
  }
}
Exemple #29
0
void
MP4::Tag::updateOffsets(long delta, long offset)
{
  MP4::Atom *moov = d->atoms->find("moov");
  if(moov) {
    MP4::AtomList stco = moov->findall("stco", true);
    for(unsigned int i = 0; i < stco.size(); i++) {
      MP4::Atom *atom = stco[i];
      if(atom->offset > offset) {
        atom->offset += delta;
      }
      d->file->seek(atom->offset + 12);
      ByteVector data = d->file->readBlock(atom->length - 12);
      unsigned int count = data.toUInt();
      d->file->seek(atom->offset + 16);
      uint pos = 4;
      while(count--) {
        long o = static_cast<long>(data.toUInt(pos));
        if(o > offset) {
          o += delta;
        }
        d->file->writeBlock(ByteVector::fromUInt(o));
        pos += 4;
      }
    }

    MP4::AtomList co64 = moov->findall("co64", true);
    for(unsigned int i = 0; i < co64.size(); i++) {
      MP4::Atom *atom = co64[i];
      if(atom->offset > offset) {
        atom->offset += delta;
      }
      d->file->seek(atom->offset + 12);
      ByteVector data = d->file->readBlock(atom->length - 12);
      unsigned int count = data.toUInt();
      d->file->seek(atom->offset + 16);
      uint pos = 4;
      while(count--) {
        long long o = data.toLongLong(pos);
        if(o > offset) {
          o += delta;
        }
        d->file->writeBlock(ByteVector::fromLongLong(o));
        pos += 8;
      }
    }
  }

  MP4::Atom *moof = d->atoms->find("moof");
  if(moof) {
    MP4::AtomList tfhd = moof->findall("tfhd", true);
    for(unsigned int i = 0; i < tfhd.size(); i++) {
      MP4::Atom *atom = tfhd[i];
      if(atom->offset > offset) {
        atom->offset += delta;
      }
      d->file->seek(atom->offset + 9);
      ByteVector data = d->file->readBlock(atom->length - 9);
      const unsigned int flags = data.toUInt(0, 3, true);
      if(flags & 1) {
        long long o = data.toLongLong(7U);
        if(o > offset) {
          o += delta;
        }
        d->file->seek(atom->offset + 16);
        d->file->writeBlock(ByteVector::fromLongLong(o));
      }
    }
  }
}
Exemple #30
0
 uint read(TagLib::File &file, uint limit)
 {
   ByteVector data = file.readBlock(std::min(4U,limit));
   value = data.toUInt(bigEndian);
   return data.size();
 }