Exemple #1
0
String Frame::readStringField(const ByteVector &data, String::Type encoding, int *position)
{
  int start = 0;

  if(!position)
    position = &start;

  ByteVector delimiter = textDelimiter(encoding);

  int end = data.find(delimiter, *position, delimiter.size());

  if(end < *position)
    return String::null;

  String str = String(data.mid(*position, end - *position), encoding);

  *position = end + delimiter.size();

  return str;
}
void TextIdentificationFrame::parseFields(const ByteVector &data)
{
    // Don't try to parse invalid frames

    if(data.size() < 2)
        return;

    // read the string data type (the first byte of the field data)

    d->textEncoding = String::Type(data[0]);

    // split the byte array into chunks based on the string type (two byte delimiter
    // for unicode encodings)

    int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;

    // build a small counter to strip nulls off the end of the field

    int dataLength = data.size() - 1;

    while(dataLength > 0 && data[dataLength] == 0)
        dataLength--;

    while(dataLength % byteAlign != 0)
        dataLength++;

    ByteVectorList l = ByteVectorList::split(data.mid(1, dataLength), textDelimiter(d->textEncoding), byteAlign);

    d->fieldList.clear();

    // append those split values to the list and make sure that the new string's
    // type is the same specified for this frame

    for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
        if(!(*it).isEmpty()) {
            String s(*it, d->textEncoding);
            d->fieldList.append(s);
        }
    }
}
void TableOfContentsFrame::parseFields(const ByteVector &data)
{
  uint size = data.size();
  if(size < 6) {
    debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated by null.");
    return;
  }

  int pos = 0, embPos = 0;
  d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
  d->elementID.append(char(0));
  d->isTopLevel = (data.at(pos) & 2) > 0;
  d->isOrdered = (data.at(pos++) & 1) > 0;
  uint entryCount = data.at(pos++);
  for(uint i = 0; i < entryCount; i++)
  {
    ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
    childElementID.append(char(0));
    d->childElements.append(childElementID);
  }

  size -= pos;
  while((uint)embPos < size - Frame::headerSize(4))
  {
    Frame *frame = d->factory->createFrame(data.mid(pos + embPos));

    if(!frame)
      return;

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

    embPos += frame->size() + Frame::headerSize(4);
    addEmbeddedFrame(frame);
  }
}
void AttachedPictureFrame::parseFields(const ByteVector &data)
{
  if(data.size() < 5) {
    debug("A picture frame must contain at least 5 bytes.");
    return;
  }

  d->textEncoding = String::Type(data[0]);

  int pos = 1;

  d->mimeType = readStringField(data, String::Latin1, &pos);
  /* Now we need at least two more bytes available */	
  if (uint(pos) + 1 >= data.size()) {
    debug("Truncated picture frame.");
    return;
  }

  d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
  d->description = readStringField(data, d->textEncoding, &pos);
  d->data = data.mid(pos);
}
Exemple #5
0
void ChapterFrame::parseFields(const ByteVector &data)
{
  TagLib::uint 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, embPos = 0;
  d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
  d->elementID.append(char(0));
  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;

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

    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);
  }
}
void TextIdentificationFrame::parseFields(const ByteVector &data)
{
  // read the string data type (the first byte of the field data)

  d->textEncoding = String::Type(data[0]);

  // split the byte array into chunks based on the string type (two byte delimiter
  // for unicode encodings)

  int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;

  ByteVectorList l = ByteVectorList::split(data.mid(1), textDelimiter(d->textEncoding), byteAlign);

  d->fieldList.clear();

  // append those split values to the list and make sure that the new string's
  // type is the same specified for this frame

  for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
    String s(*it, d->textEncoding);
    d->fieldList.append(s);
  }
}
Exemple #7
0
List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
                                      PaginationStrategy strategy,
                                      uint streamSerialNumber,
                                      int firstPage,
                                      bool firstPacketContinued,
                                      bool lastPacketCompleted,
                                      bool containsLastPacket)
{
  List<Page *> l;

  int totalSize = 0;

  for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
    totalSize += (*it).size();

  // Handle creation of multiple pages with appropriate pagination.
  if(strategy == Repaginate || totalSize + packets.size() > 255 * 255) {

    // SPLITSIZE must be a multiple of 255 in order to get the lacing values right
    // create pages of about 8KB each
#define SPLITSIZE (32*255)

    int pageIndex = 0;

    for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
      bool continued = false;

      // mark very first packet?
      if(firstPacketContinued && it==packets.begin()) {
        continued = true;
      }

      // append to buf
      ByteVector packetBuf;
      packetBuf.append(*it);

      while(packetBuf.size() > SPLITSIZE) {
        // output a Page
        ByteVector packetForOnePage;
        packetForOnePage.resize(SPLITSIZE);
        std::copy(packetBuf.begin(), packetBuf.begin() + SPLITSIZE, packetForOnePage.begin());

        ByteVectorList packetList;
        packetList.append(packetForOnePage);
        Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued, false, false);
        l.append(p);

        pageIndex++;
        continued = true;
        packetBuf = packetBuf.mid(SPLITSIZE);
      }

      ByteVectorList::ConstIterator jt = it;
      ++jt;
      bool lastPacketInList = (jt == packets.end());

      // output a page for the rest (we output one packet per page, so this one should be completed)
      ByteVectorList packetList;
      packetList.append(packetBuf);

      bool isVeryLastPacket = false;
      if(containsLastPacket) {
        // mark the very last output page as last of stream
        ByteVectorList::ConstIterator jt = it;
        ++jt;
        if(jt == packets.end()) {
          isVeryLastPacket = true;
        }
      }

      Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
                         lastPacketInList ? lastPacketCompleted : true,
                         isVeryLastPacket);
      pageIndex++;

      l.append(p);
    }
  }
  else {
    Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
                       lastPacketCompleted, containsLastPacket);
    l.append(p);
  }

  return l;
}
void ExtendedHeader::parse(const ByteVector &data)
{
  d->size = SynchData::toUInt(data.mid(0, 4)); // (structure 3.2 "Extended header size")
}
void CommentsFrame::setLanguage(const ByteVector &languageEncoding)
{
  d->language = languageEncoding.mid(0, 3);
}
Exemple #10
0
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);
	if(data[8] == 0) {
		unsigned int unit = data.mid(20, 4).toUInt();
		unsigned int length = data.mid(24, 4).toUInt();
		if (unit == 0) {
			debug("MP4: No valid data");
			d->length = 0;
		} else {
			d->length = length / unit;
		}
	}
	else {
		long long unit = data.mid(28, 8).toLongLong();
		long long length = data.mid(36, 8).toLongLong();
		if (unit == 0) {
			debug("MP4: No valid data");
			d->length = 0;
		} else {
			d->length = int(length / unit);
		}
	}

  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.mid(40, 2).toShort();
    d->bitsPerSample = data.mid(42, 2).toShort();
    d->sampleRate = data.mid(46, 4).toUInt();
    if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
      long 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.mid(pos, 4).toUInt() + 500) / 1000;
      }
    }
  }
}
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 #12
0
void ID3v2::Tag::parse(const ByteVector &origData)
{
  ByteVector data = origData;

  if(d->header.unsynchronisation() && d->header.majorVersion() <= 3)
    data = SynchData::decode(data);

  uint frameDataPosition = 0;
  uint frameDataLength = data.size();

  // check for extended header

  if(d->header.extendedHeader()) {
    if(!d->extendedHeader)
      d->extendedHeader = new ExtendedHeader;
    d->extendedHeader->setData(data);
    if(d->extendedHeader->size() <= data.size()) {
      frameDataPosition += d->extendedHeader->size();
      frameDataLength -= d->extendedHeader->size();
    }
  }

  // check for footer -- we don't actually need to parse it, as it *must*
  // contain the same data as the header, but we do need to account for its
  // size.

  if(d->header.footerPresent() && Footer::size() <= frameDataLength)
    frameDataLength -= Footer::size();

  // parse frames

  // Make sure that there is at least enough room in the remaining frame data for
  // a frame header.

  while(frameDataPosition < frameDataLength - Frame::headerSize(d->header.majorVersion())) {

    // If the next data is position is 0, assume that we've hit the padding
    // portion of the frame data.

    if(data.at(frameDataPosition) == 0) {
      if(d->header.footerPresent())
        debug("Padding *and* a footer found.  This is not allowed by the spec.");

      d->paddingSize = frameDataLength - frameDataPosition;
      return;
    }

    Frame *frame = d->factory->createFrame(data.mid(frameDataPosition),
                                           &d->header);

    if(!frame)
      return;

    // Checks to make sure that frame parsed correctly.

    if(frame->size() <= 0) {
      delete frame;
      return;
    }

    frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion());
    addFrame(frame);
  }
}
Exemple #13
0
bool FLAC::Picture::parse(const ByteVector &data)
{
  if(data.size() < 32) {
    debug("A picture block must contain at least 5 bytes.");
    return false;
  }

  int pos = 0;
  d->type = TagLib::ID3v2::AttachedPictureFrame::Type(data.mid(pos, 4).toUInt());
  pos += 4;
  uint mimeTypeLength = data.mid(pos, 4).toUInt();
  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;
  uint descriptionLength = data.mid(pos, 4).toUInt();
  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.mid(pos, 4).toUInt();
  pos += 4;
  d->height = data.mid(pos, 4).toUInt();
  pos += 4;
  d->colorDepth = data.mid(pos, 4).toUInt();
  pos += 4;
  d->numColors = data.mid(pos, 4).toUInt();
  pos += 4;
  uint dataLength = data.mid(pos, 4).toUInt();
  pos += 4;
  if(pos + dataLength > data.size()) {
    debug("Invalid picture block.");
    return false;
  }
  d->data = data.mid(pos, dataLength);

  return true;  
}
Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHeader) const
{
  ByteVector data = origData;
  unsigned int version = tagHeader->majorVersion();
  Frame::Header *header = new Frame::Header(data, version);
  ByteVector frameID = header->frameID();

  // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
  // characters.  Also make sure that there is data in the frame.

  if(frameID.size() != (version < 3 ? 3 : 4) ||
     header->frameSize() <= static_cast<unsigned int>(header->dataLengthIndicator() ? 4 : 0) ||
     header->frameSize() > data.size())
  {
    delete header;
    return 0;
  }

#ifndef NO_ITUNES_HACKS
  if(version == 3 && frameID.size() == 4 && frameID[3] == '\0') {
    // iTunes v2.3 tags store v2.2 frames - convert now
    frameID = frameID.mid(0, 3);
    header->setFrameID(frameID);
    header->setVersion(2);
    updateFrame(header);
    header->setVersion(3);
  }
#endif

  for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
    if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
      delete header;
      return 0;
    }
  }

  if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) {
    // Data lengths are not part of the encoded data, but since they are synch-safe
    // integers they will be never actually encoded.
    ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize());
    frameData = SynchData::decode(frameData);
    data = data.mid(0, Frame::Header::size(version)) + frameData;
  }

  // TagLib doesn't mess with encrypted frames, so just treat them
  // as unknown frames.

#if !defined(HAVE_ZLIB) || HAVE_ZLIB == 0
  if(header->compression()) {
    debug("Compressed frames are currently not supported.");
    return new UnknownFrame(data, header);
  }
#endif
  if(header->encryption()) {
    debug("Encrypted frames are currently not supported.");
    return new UnknownFrame(data, header);
  }

  if(!updateFrame(header)) {
    header->setTagAlterPreservation(true);
    return new UnknownFrame(data, header);
  }

  // updateFrame() might have updated the frame ID.

  frameID = header->frameID();

  // This is where things get necissarily nasty.  Here we determine which
  // Frame subclass (or if none is found simply an Frame) based
  // on the frame ID.  Since there are a lot of possibilities, that means
  // a lot of if blocks.

  // Text Identification (frames 4.2)

  // Apple proprietary WFED (Podcast URL) is in fact a text frame.
  if(frameID.startsWith("T") || frameID == "WFED") {

    TextIdentificationFrame *f = frameID != "TXXX"
      ? new TextIdentificationFrame(data, header)
      : new UserTextIdentificationFrame(data, header);

    d->setTextEncoding(f);

    if(frameID == "TCON")
      updateGenre(f);

    return f;
  }

  // Comments (frames 4.10)

  if(frameID == "COMM") {
    CommentsFrame *f = new CommentsFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // Attached Picture (frames 4.14)

  if(frameID == "APIC") {
    AttachedPictureFrame *f = new AttachedPictureFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // ID3v2.2 Attached Picture

  if(frameID == "PIC") {
    AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // Relative Volume Adjustment (frames 4.11)

  if(frameID == "RVA2")
    return new RelativeVolumeFrame(data, header);

  // Unique File Identifier (frames 4.1)

  if(frameID == "UFID")
    return new UniqueFileIdentifierFrame(data, header);

  // General Encapsulated Object (frames 4.15)

  if(frameID == "GEOB") {
    GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // URL link (frames 4.3)

  if(frameID.startsWith("W")) {
    if(frameID != "WXXX") {
      return new UrlLinkFrame(data, header);
    }
    else {
      UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
      d->setTextEncoding(f);
      return f;
    }
  }

  // Unsynchronized lyric/text transcription (frames 4.8)

  if(frameID == "USLT") {
    UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header);
    if(d->useDefaultEncoding)
      f->setTextEncoding(d->defaultEncoding);
    return f;
  }

  // Synchronised lyrics/text (frames 4.9)

  if(frameID == "SYLT") {
    SynchronizedLyricsFrame *f = new SynchronizedLyricsFrame(data, header);
    if(d->useDefaultEncoding)
      f->setTextEncoding(d->defaultEncoding);
    return f;
  }

  // Event timing codes (frames 4.5)

  if(frameID == "ETCO")
    return new EventTimingCodesFrame(data, header);

  // Popularimeter (frames 4.17)

  if(frameID == "POPM")
    return new PopularimeterFrame(data, header);

  // Private (frames 4.27)

  if(frameID == "PRIV")
    return new PrivateFrame(data, header);

  // Ownership (frames 4.22)

  if(frameID == "OWNE") {
    OwnershipFrame *f = new OwnershipFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // Chapter (ID3v2 chapters 1.0)

  if(frameID == "CHAP")
    return new ChapterFrame(tagHeader, data, header);

  // Table of contents (ID3v2 chapters 1.0)

  if(frameID == "CTOC")
    return new TableOfContentsFrame(tagHeader, data, header);

  // Apple proprietary PCST (Podcast)

  if(frameID == "PCST")
    return new PodcastFrame(data, header);

  return new UnknownFrame(data, header);
}
Exemple #15
0
void Ogg::File::writePacket(unsigned int i, const ByteVector &packet)
{
    if(!readPages(i)) {
        debug("Ogg::File::writePacket() -- Could not find the requested packet.");
        return;
    }

    // Look for the pages where the requested packet should belong to.

    List<Page *>::ConstIterator it = d->pages.begin();
    while((*it)->containsPacket(i) == Page::DoesNotContainPacket)
        ++it;

    const Page *firstPage = *it;

    while(nextPacketIndex(*it) <= i)
        ++it;

    const Page *lastPage = *it;

    // Replace the requested packet and create new pages to replace the located pages.

    ByteVectorList packets = firstPage->packets();
    packets[i - firstPage->firstPacketIndex()] = packet;

    if(firstPage != lastPage && lastPage->packetCount() > 2) {
        ByteVectorList lastPagePackets = lastPage->packets();
        lastPagePackets.erase(lastPagePackets.begin());
        packets.append(lastPagePackets);
    }

    // TODO: This pagination method isn't accurate for what's being done here.
    // This should account for real possibilities like non-aligned packets and such.

    const List<Page *> pages = Page::paginate(packets,
                               Page::SinglePagePerGroup,
                               firstPage->header()->streamSerialNumber(),
                               firstPage->pageSequenceNumber(),
                               firstPage->header()->firstPacketContinued(),
                               lastPage->header()->lastPacketCompleted());

    // Write the pages.

    ByteVector data;
    for(it = pages.begin(); it != pages.end(); ++it)
        data.append((*it)->render());

    const unsigned long originalOffset = firstPage->fileOffset();
    const unsigned long originalLength = lastPage->fileOffset() + lastPage->size() - originalOffset;

    insert(data, originalOffset, originalLength);

    // Renumber the following pages if the pages have been split or merged.

    const int numberOfNewPages
        = pages.back()->pageSequenceNumber() - lastPage->pageSequenceNumber();

    if(numberOfNewPages != 0) {
        long pageOffset = originalOffset + data.size();

        while(true) {
            Page page(this, pageOffset);
            if(!page.header()->isValid())
                break;

            page.setPageSequenceNumber(page.pageSequenceNumber() + numberOfNewPages);
            const ByteVector data = page.render();

            seek(pageOffset + 18);
            writeBlock(data.mid(18, 8));

            if(page.header()->lastPageOfStream())
                break;

            pageOffset += page.size();
        }
    }

    // Discard all the pages to keep them up-to-date by fetching them again.

    d->pages.clear();
}
Exemple #16
0
bool FLAC::File::save()
{
  if(readOnly()) {
    debug("FLAC::File::save() - Cannot save to a read only file.");
    return false;
  }

  // Create new vorbis comments

  if(!d->comment) {
    d->comment = new Ogg::XiphComment;
    if(d->tag)
      Tag::duplicate(d->tag, d->comment, true);
  }

  d->xiphCommentData = d->comment->render(false);

  // A Xiph comment portion of the data stream starts with a 4-byte descriptor.
  // The first byte indicates the frame type.  The last three bytes are used
  // to give the lenght of the data segment.  Here we start 

  ByteVector data = ByteVector::fromUInt(d->xiphCommentData.size());

  data[0] = char(VorbisComment);
  data.append(d->xiphCommentData);


   // If file already have comment => find and update it
   // if not => insert one

   // TODO: Search for padding and use that

  if(d->hasXiphComment) {

    long nextBlockOffset = d->flacStart;
    bool isLastBlock = false;

    while(!isLastBlock) {
      seek(nextBlockOffset);

      ByteVector header = readBlock(4);
      char blockType = header[0] & 0x7f;
      isLastBlock = header[0] & 0x80;
      uint blockLength = header.mid(1, 3).toUInt();

      if(blockType == VorbisComment) {
        data[0] = header[0];
        insert(data, nextBlockOffset, blockLength + 4);
        break;
      }

      nextBlockOffset += blockLength + 4;
    }
  }
  else {

    const long firstBlockOffset = d->flacStart;
    seek(firstBlockOffset);

    ByteVector header = readBlock(4);
    bool isLastBlock = header[0] & 0x80;
    uint blockLength = header.mid(1, 3).toUInt();

    if(isLastBlock) {

      // If the first block was previously also the last block, then we want to
      // mark it as no longer being the first block (the writeBlock() call) and
      // then set the data for the block that we're about to write to mark our
      // new block as the last block.

      seek(firstBlockOffset);
      writeBlock(static_cast<char>(header[0] & 0x7F));
      data[0] |= 0x80;
    }

    insert(data, firstBlockOffset + blockLength + 4, 0);
    d->hasXiphComment = true;
  }

  // Update ID3 tags

  if(d->ID3v2Tag) {
    if(d->hasID3v2) {
      if(d->ID3v2Location < d->flacStart)
        debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
              "start of the FLAC bytestream?  Not writing the ID3v2 tag.");
      else
        insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
    }
    else
      insert(d->ID3v2Tag->render(), 0, 0);
  }

  if(d->ID3v1Tag) {
    seek(d->ID3v1Tag ? -128 : 0, End);
    writeBlock(d->ID3v1Tag->render());
  }

  return true;
}
void UnsynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding)
{
  d->language = languageEncoding.mid(0, 3);
}
Exemple #18
0
bool FLAC::File::save()
{
  if(readOnly()) {
    debug("FLAC::File::save() - Cannot save to a read only file.");
    return false;
  }

  // Create new vorbis comments

  if(!d->comment) {
    d->comment = new Ogg::XiphComment;
    if(d->tag)
      Tag::duplicate(d->tag, d->comment, true);
  }

  d->xiphCommentData = d->comment->render(false);

  ByteVector v = ByteVector::fromUInt(d->xiphCommentData.size());

  // Set the type of the comment to be a Xiph / Vorbis comment
  // (See scan() for comments on header-format)
  v[0] = 4;
  v.append(d->xiphCommentData);


   // If file already have comment => find and update it
   //                       if not => insert one
   // TODO: Search for padding and use that

  if(d->hasXiphComment) {
    long nextPageOffset = d->flacStart;
    seek(nextPageOffset);
    ByteVector header = readBlock(4);
    uint length = header.mid(1, 3).toUInt();

    nextPageOffset += length + 4;

    // Search through the remaining metadata

    char blockType = header[0] & 0x7f;
    bool lastBlock = header[0] & 0x80;

    while(!lastBlock) {
      seek(nextPageOffset);

      header = readBlock(4);
      blockType = header[0] & 0x7f;
      lastBlock = header[0] & 0x80;
      length = header.mid(1, 3).toUInt();

      // Type is vorbiscomment
      if(blockType == 4) {
        v[0] = header[0];
        insert(v, nextPageOffset, length + 4);
        break;
      }

      nextPageOffset += length + 4;
    }
  }
  else {
    long nextPageOffset = d->flacStart;

    seek(nextPageOffset);

    ByteVector header = readBlock(4);
    // char blockType = header[0] & 0x7f;
    bool lastBlock = header[0] & 0x80;
    uint length = header.mid(1, 3).toUInt();

    // If last block was last, make this one last

    if(lastBlock) {

      // Copy the bottom seven bits into the new value

      ByteVector h(static_cast<char>(header[0] & 0x7F));
      insert(h, nextPageOffset, 1);

      // Set the last bit
      v[0] |= 0x80;
    }

    insert(v, nextPageOffset + length + 4, 0);
    d->hasXiphComment = true;
  }

  // Update ID3 tags

  if(d->ID3v2Tag) {
    if(d->hasID3v2)
      insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
    else
      insert(d->ID3v2Tag->render(), 0, 0);
  }

  if(d->ID3v1Tag) {
    if(d->hasID3v1)
      seek(-128, End);
    else
      seek(0, End);
    writeBlock(d->ID3v1Tag->render());
  }

  return true;
}
void Ogg::PageHeader::read()
{
  d->file->seek(d->fileOffset);

  // 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.

  ByteVector data = d->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;
  }

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

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

  d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false);
  d->streamSerialNumber = data.mid(14, 4).toUInt(false);
  d->pageSequenceNumber = data.mid(18, 4).toUInt(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 coresponding data for this count.

  int pageSegmentCount = uchar(data[26]);

  ByteVector pageSegments = d->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 += uchar(pageSegments[i]);
    packetSize += uchar(pageSegments[i]);

    if(uchar(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 #20
0
Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) const
{
  ByteVector data = origData;
  uint version = tagHeader->majorVersion();
  Frame::Header *header = new Frame::Header(data, version);
  ByteVector frameID = header->frameID();

  // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
  // characters.  Also make sure that there is data in the frame.

  if(!frameID.size() == (version < 3 ? 3 : 4) ||
     header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
     header->frameSize() > data.size())
  {
    delete header;
    return 0;
  }

  for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
    if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
      delete header;
      return 0;
    }
  }

  if(version > 3 && (tagHeader->unsynchronisation() || header->unsynchronisation())) {
    // Data lengths are not part of the encoded data, but since they are synch-safe
    // integers they will be never actually encoded.
    ByteVector frameData = data.mid(Frame::Header::size(version), header->frameSize());
    frameData = SynchData::decode(frameData);
    data = data.mid(0, Frame::Header::size(version)) + frameData;
  }

  // TagLib doesn't mess with encrypted frames, so just treat them
  // as unknown frames.

#if HAVE_ZLIB == 0
  if(header->compression()) {
    debug("Compressed frames are currently not supported.");
    return new UnknownFrame(data, header);
  }
#endif
  if(header->encryption()) {
    debug("Encrypted frames are currently not supported.");
    return new UnknownFrame(data, header);
  }

  if(!updateFrame(header)) {
    header->setTagAlterPreservation(true);
    return new UnknownFrame(data, header);
  }

  // updateFrame() might have updated the frame ID.

  frameID = header->frameID();

  // This is where things get necissarily nasty.  Here we determine which
  // Frame subclass (or if none is found simply an Frame) based
  // on the frame ID.  Since there are a lot of possibilities, that means
  // a lot of if blocks.

  // Text Identification (frames 4.2)

  if(frameID.startsWith("T")) {

    TextIdentificationFrame *f = frameID != "TXXX"
      ? new TextIdentificationFrame(data, header)
      : new UserTextIdentificationFrame(data, header);

    d->setTextEncoding(f);

    if(frameID == "TCON")
      updateGenre(f);

    return f;
  }

  // Comments (frames 4.10)

  if(frameID == "COMM") {
    CommentsFrame *f = new CommentsFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // Attached Picture (frames 4.14)

  if(frameID == "APIC") {
    AttachedPictureFrame *f = new AttachedPictureFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // ID3v2.2 Attached Picture

	if(frameID == "PIC") {
    AttachedPictureFrame *f = new AttachedPictureFrameV22(data, header);
    d->setTextEncoding(f);
    return f;
  }

	// Relative Volume Adjustment (frames 4.11)

  if(frameID == "RVA2")
    return new RelativeVolumeFrame(data, header);

  // Unique File Identifier (frames 4.1)

  if(frameID == "UFID")
    return new UniqueFileIdentifierFrame(data, header);

  // General Encapsulated Object (frames 4.15)

  if(frameID == "GEOB") {
    GeneralEncapsulatedObjectFrame *f = new GeneralEncapsulatedObjectFrame(data, header);
    d->setTextEncoding(f);
    return f;
  }

  // URL link (frames 4.3)

  if(frameID.startsWith("W")) {
    if(frameID != "WXXX") {
      return new UrlLinkFrame(data, header);
    }
    else {
      UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
      d->setTextEncoding(f);
      return f;
    }
  }

  // Unsynchronized lyric/text transcription (frames 4.8)

  if(frameID == "USLT") {
    UnsynchronizedLyricsFrame *f = new UnsynchronizedLyricsFrame(data, header);
    if(d->useDefaultEncoding)
      f->setTextEncoding(d->defaultEncoding);
    return f;
  }

  // Popularimeter (frames 4.17)

  if(frameID == "POPM")
    return new PopularimeterFrame(data, header);

  // Private (frames 4.27)

  if(frameID == "PRIV")
    return new PrivateFrame(data, header);

  return new UnknownFrame(data, header);
}
Exemple #21
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.mid(3, 3).toUInt();

    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.mid(4, 4).toUInt();

    { // 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.mid(4, 4).toUInt();
        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;
  }
  }
}
void Speex::Properties::read()
{
  // Get the identification header from the Ogg implementation.

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

  int pos = 28;

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

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

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

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

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

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

  // bitrate;                /**< Bit-rate used */
  d->bitrate = data.mid(pos, 4).toUInt(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.mid(pos, 4).toUInt(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 = 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("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.");
}
Exemple #23
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.mid(0, 4).toUInt();
      d->file->seek(atom->offset + 16);
      int pos = 4;
      while(count--) {
        long o = data.mid(pos, 4).toUInt();
        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.mid(0, 4).toUInt();
      d->file->seek(atom->offset + 16);
      int pos = 4;
      while(count--) {
        long long o = data.mid(pos, 8).toLongLong();
        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->offset - 9);
      unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt();
      if(flags & 1) {
        long long o = data.mid(7, 8).toLongLong();
        if(o > offset) {
          o += delta;
        }
        d->file->seek(atom->offset + 16);
        d->file->writeBlock(ByteVector::fromLongLong(o));
      }
    }
  }
}
void FLAC::File::scan()
{
  // Scan the metadata pages

  if(d->scanned)
    return;

  if(!isValid())
    return;

  long nextBlockOffset;

  if(d->hasID3v2)
    nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
  else
    nextBlockOffset = find("fLaC");

  if(nextBlockOffset < 0) {
    debug("FLAC::File::scan() -- FLAC stream not found");
    setValid(false);
    return;
  }

  nextBlockOffset += 4;
  d->flacStart = nextBlockOffset;

  seek(nextBlockOffset);

  ByteVector header = readBlock(4);

  // 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 isLastBlock = (header[0] & 0x80) != 0;
  uint length = header.mid(1, 3).toUInt();

  // First block should be the stream_info metadata

  if(blockType != StreamInfo) {
    debug("FLAC::File::scan() -- invalid FLAC stream");
    setValid(false);
    return;
  }

  d->streamInfoData = readBlock(length);
  nextBlockOffset += length + 4;

  // Search through the remaining metadata
  while(!isLastBlock) {

    header = readBlock(4);
    blockType = header[0] & 0x7f;
    isLastBlock = (header[0] & 0x80) != 0;
    length = header.mid(1, 3).toUInt();

    // Found the vorbis-comment
    if(blockType == VorbisComment) {
      if(!d->hasXiphComment) {
        d->xiphCommentData = readBlock(length);
        d->hasXiphComment = true;
      }
      else {
        debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
      }
    }

    nextBlockOffset += length + 4;

    if(nextBlockOffset >= File::length()) {
      debug("FLAC::File::scan() -- FLAC stream corrupted");
      setValid(false);
      return;
    }
    seek(nextBlockOffset);
  }

  // End of metadata, now comes the datastream

  d->streamStart = nextBlockOffset;
  d->streamLength = File::length() - d->streamStart;

  if(d->hasID3v1)
    d->streamLength -= 128;

  d->scanned = true;
}
Exemple #25
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;
}
Exemple #26
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 #27
0
void Frame::Header::setFrameID(const ByteVector &id)
{
  d->frameID = id.mid(0, 4);
}
bool FLAC::File::save()
{
  if(readOnly()) {
    debug("FLAC::File::save() - Cannot save to a read only file.");
    return false;
  }

  // Create new vorbis comments

  Tag::duplicate(&d->tag, xiphComment(true), true);

  d->xiphCommentData = xiphComment()->render(false);

  // A Xiph comment portion of the data stream starts with a 4-byte descriptor.
  // The first byte indicates the frame type.  The last three bytes are used
  // to give the length of the data segment.  Here we start

  ByteVector data = ByteVector::fromUInt(d->xiphCommentData.size());

  data[0] = char(VorbisComment);
  data.append(d->xiphCommentData);


   // If file already have comment => find and update it
   // if not => insert one

   // TODO: Search for padding and use that

  if(d->hasXiphComment) {

    long nextBlockOffset = d->flacStart;
    bool isLastBlock = false;

    while(!isLastBlock) {
      seek(nextBlockOffset);

      ByteVector header = readBlock(4);
      char blockType = header[0] & 0x7f;
      isLastBlock = (header[0] & 0x80) != 0;
      uint blockLength = header.mid(1, 3).toUInt();

      if(blockType == VorbisComment) {

        long paddingBreak = 0;

        if(!isLastBlock) {
          paddingBreak = findPaddingBreak(nextBlockOffset + blockLength + 4,
                                          nextBlockOffset + d->xiphCommentData.size() + 8,
                                          &isLastBlock);
        }

        uint paddingLength = 0;

         if(paddingBreak) {

           // There is space for comment and padding blocks without rewriting the
           // whole file.  Note: This cannot overflow.

           paddingLength = paddingBreak - (nextBlockOffset + d->xiphCommentData.size() + 8);
         }
         else {

           // Not enough space, so we will have to rewrite the whole file
           // following this block

           paddingLength = d->xiphCommentData.size();

           if(paddingLength < MinPaddingLength)
             paddingLength = MinPaddingLength;

           paddingBreak = nextBlockOffset + blockLength + 4;
         }

         ByteVector padding = ByteVector::fromUInt(paddingLength);

         padding[0] = 1;

         if(isLastBlock)
           padding[0] |= 0x80;

         padding.resize(paddingLength + 4);
         ByteVector pair(data);
         pair.append(padding);
         insert(pair, nextBlockOffset, paddingBreak - nextBlockOffset);
         break;
      }

      nextBlockOffset += blockLength + 4;
    }
  }
  else {

    const long firstBlockOffset = d->flacStart;
    seek(firstBlockOffset);

    ByteVector header = readBlock(4);
    bool isLastBlock = (header[0] & 0x80) != 0;
    uint blockLength = header.mid(1, 3).toUInt();

    if(isLastBlock) {

      // If the first block was previously also the last block, then we want to
      // mark it as no longer being the first block (the writeBlock() call) and
      // then set the data for the block that we're about to write to mark our
      // new block as the last block.

      seek(firstBlockOffset);
      writeBlock(static_cast<char>(header[0] & 0x7F));
      data[0] |= 0x80;
    }

    insert(data, firstBlockOffset + blockLength + 4, 0);
    d->hasXiphComment = true;
  }

  // Update ID3 tags

  if(ID3v2Tag()) {
    if(d->hasID3v2) {
      if(d->ID3v2Location < d->flacStart)
        debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
              "start of the FLAC bytestream?  Not writing the ID3v2 tag.");
      else
        insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
    }
    else
      insert(ID3v2Tag()->render(), 0, 0);
  }

  if(ID3v1Tag()) {
    seek(-128, End);
    writeBlock(ID3v1Tag()->render());
  }

  return true;
}