long MPEG::File::findID3v2()
{
  // This method is based on the contents of TagLib::File::find(), but because
  // of some subtlteies -- specifically the need to look for the bit pattern of
  // an MPEG sync, it has been modified for use here.

  if(isValid() && ID3v2::Header::fileIdentifier().size() <= bufferSize()) {

    // The position in the file that the current buffer starts at.

    long bufferOffset = 0;
    ByteVector buffer;

    // These variables are used to keep track of a partial match that happens at
    // the end of a buffer.

    int previousPartialMatch = -1;
    bool previousPartialSynchMatch = false;

    // Save the location of the current read pointer.  We will restore the
    // position using seek() before all returns.

    long originalPosition = tell();

    // Start the search at the beginning of the file.

    seek(0);

    // This loop is the crux of the find method.  There are three cases that we
    // want to account for:
    // (1) The previously searched buffer contained a partial match of the search
    // pattern and we want to see if the next one starts with the remainder of
    // that pattern.
    //
    // (2) The search pattern is wholly contained within the current buffer.
    //
    // (3) The current buffer ends with a partial match of the pattern.  We will
    // note this for use in the next itteration, where we will check for the rest
    // of the pattern.

    for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {

      // (1) previous partial match

      if(previousPartialSynchMatch && secondSynchByte(buffer[0]))
        return -1;

      if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
        const int patternOffset = (bufferSize() - previousPartialMatch);
        if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
          seek(originalPosition);
          return bufferOffset - bufferSize() + previousPartialMatch;
        }
      }

      // (2) pattern contained in current buffer

      long location = buffer.find(ID3v2::Header::fileIdentifier());
      if(location >= 0) {
        seek(originalPosition);
        return bufferOffset + location;
      }

      int firstSynchByte = buffer.find(char(uchar(255)));

      // Here we have to loop because there could be several of the first
      // (11111111) byte, and we want to check all such instances until we find
      // a full match (11111111 111) or hit the end of the buffer.

      while(firstSynchByte >= 0) {

        // if this *is not* at the end of the buffer

        if(firstSynchByte < int(buffer.size()) - 1) {
          if(secondSynchByte(buffer[firstSynchByte + 1])) {
            // We've found the frame synch pattern.
            seek(originalPosition);
            return -1;
          }
          else {

            // We found 11111111 at the end of the current buffer indicating a
            // partial match of the synch pattern.  The find() below should
            // return -1 and break out of the loop.

            previousPartialSynchMatch = true;
          }
        }

        // Check in the rest of the buffer.

        firstSynchByte = buffer.find(char(uchar(255)), firstSynchByte + 1);
      }

      // (3) partial match

      previousPartialMatch = buffer.endsWithPartialMatch(ID3v2::Header::fileIdentifier());

      bufferOffset += bufferSize();
    }

    // Since we hit the end of the file, reset the status before continuing.

    clear();

    seek(originalPosition);
  }

  return -1;
}
Exemple #2
0
long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
    if(!d->stream || pattern.size() > d->bufferSize)
        return -1;

    // The position in the file that the current buffer starts at.

    long bufferOffset = fromOffset;
    ByteVector buffer;

    // These variables are used to keep track of a partial match that happens at
    // the end of a buffer.

    int previousPartialMatch = -1;
    int beforePreviousPartialMatch = -1;

    // Save the location of the current read pointer.  We will restore the
    // position using seek() before all returns.

    long originalPosition = tell();

    // Start the search at the offset.

    seek(fromOffset);

    // This loop is the crux of the find method.  There are three cases that we
    // want to account for:
    //
    // (1) The previously searched buffer contained a partial match of the search
    // pattern and we want to see if the next one starts with the remainder of
    // that pattern.
    //
    // (2) The search pattern is wholly contained within the current buffer.
    //
    // (3) The current buffer ends with a partial match of the pattern.  We will
    // note this for use in the next itteration, where we will check for the rest
    // of the pattern.
    //
    // All three of these are done in two steps.  First we check for the pattern
    // and do things appropriately if a match (or partial match) is found.  We
    // then check for "before".  The order is important because it gives priority
    // to "real" matches.

    for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {

        // (1) previous partial match

        if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) {
            const int patternOffset = (d->bufferSize - previousPartialMatch);
            if(buffer.containsAt(pattern, 0, patternOffset)) {
                seek(originalPosition);
                return bufferOffset - d->bufferSize + previousPartialMatch;
            }
        }

        if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) {
            const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
            if(buffer.containsAt(before, 0, beforeOffset)) {
                seek(originalPosition);
                return -1;
            }
        }

        // (2) pattern contained in current buffer

        long location = buffer.find(pattern);
        if(location >= 0) {
            seek(originalPosition);
            return bufferOffset + location;
        }

        if(!before.isNull() && buffer.find(before) >= 0) {
            seek(originalPosition);
            return -1;
        }

        // (3) partial match

        previousPartialMatch = buffer.endsWithPartialMatch(pattern);

        if(!before.isNull())
            beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);

        bufferOffset += d->bufferSize;
    }

    // Since we hit the end of the file, reset the status before continuing.

    clear();

    seek(originalPosition);

    return -1;
}
Exemple #3
0
void
MP4::Properties::read(File *file, Atoms *atoms)
{
  MP4::Atom *moov = atoms->find("moov");
  if(!moov) {
    debug("MP4: Atom 'moov' not found");
    return;
  }

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

  const MP4::AtomList trakList = moov->findall("trak");
  for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) {
    trak = *it;
    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.containsAt("soun", 16)) {
      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);

  const unsigned int version = data[8];
  long long unit;
  long long length;
  if(version == 1) {
    if(data.size() < 36 + 8) {
      debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
      return;
    }
    unit   = data.toUInt(28U);
    length = data.toLongLong(32U);
  }
  else {
    if(data.size() < 24 + 8) {
      debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
      return;
    }
    unit   = data.toUInt(20U);
    length = data.toUInt(24U);
  }
  if(unit > 0 && length > 0)
    d->length = static_cast<int>(length * 1000.0 / unit + 0.5);

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

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

  MP4::Atom *drms = atom->find("drms");
  if(drms) {
    d->encrypted = true;
  }
}