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; }
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; }
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; } }