summaryrefslogtreecommitdiff
path: root/libs/taglib/taglib/ogg/oggpage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/taglib/taglib/ogg/oggpage.cpp')
-rw-r--r--libs/taglib/taglib/ogg/oggpage.cpp256
1 files changed, 256 insertions, 0 deletions
diff --git a/libs/taglib/taglib/ogg/oggpage.cpp b/libs/taglib/taglib/ogg/oggpage.cpp
new file mode 100644
index 0000000000..0b82828467
--- /dev/null
+++ b/libs/taglib/taglib/ogg/oggpage.cpp
@@ -0,0 +1,256 @@
+/***************************************************************************
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ email : wheeler@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * This library is distributed in the hope that it will be useful, but *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this library; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+ * USA *
+ * *
+ * Alternatively, this file is available under the Mozilla Public *
+ * License Version 1.1. You may obtain a copy of the License at *
+ * http://www.mozilla.org/MPL/ *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "oggpage.h"
+#include "oggpageheader.h"
+#include "oggfile.h"
+
+using namespace TagLib;
+
+class Ogg::Page::PagePrivate
+{
+public:
+ PagePrivate(File *f = 0, long pageOffset = -1) :
+ file(f),
+ fileOffset(pageOffset),
+ packetOffset(0),
+ header(f, pageOffset),
+ firstPacketIndex(-1)
+ {
+ if(file) {
+ packetOffset = fileOffset + header.size();
+ packetSizes = header.packetSizes();
+ dataSize = header.dataSize();
+ }
+ }
+
+ File *file;
+ long fileOffset;
+ long packetOffset;
+ int dataSize;
+ List<int> packetSizes;
+ PageHeader header;
+ int firstPacketIndex;
+ ByteVectorList packets;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::Page::Page(Ogg::File *file, long pageOffset)
+{
+ d = new PagePrivate(file, pageOffset);
+}
+
+Ogg::Page::~Page()
+{
+ delete d;
+}
+
+long Ogg::Page::fileOffset() const
+{
+ return d->fileOffset;
+}
+
+const Ogg::PageHeader *Ogg::Page::header() const
+{
+ return &d->header;
+}
+
+int Ogg::Page::firstPacketIndex() const
+{
+ return d->firstPacketIndex;
+}
+
+void Ogg::Page::setFirstPacketIndex(int index)
+{
+ d->firstPacketIndex = index;
+}
+
+Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
+{
+ int lastPacketIndex = d->firstPacketIndex + packetCount() - 1;
+ if(index < d->firstPacketIndex || index > lastPacketIndex)
+ return DoesNotContainPacket;
+
+ ContainsPacketFlags flags = DoesNotContainPacket;
+
+ if(index == d->firstPacketIndex)
+ flags = ContainsPacketFlags(flags | BeginsWithPacket);
+
+ if(index == lastPacketIndex)
+ flags = ContainsPacketFlags(flags | EndsWithPacket);
+
+ // If there's only one page and it's complete:
+
+ if(packetCount() == 1 &&
+ !d->header.firstPacketContinued() &&
+ d->header.lastPacketCompleted())
+ {
+ flags = ContainsPacketFlags(flags | CompletePacket);
+ }
+
+ // Or if the page is (a) the first page and it's complete or (b) the last page
+ // and it's complete or (c) a page in the middle.
+
+ else if((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
+ (flags & EndsWithPacket && d->header.lastPacketCompleted()) ||
+ (!(flags & BeginsWithPacket) && !(flags & EndsWithPacket)))
+ {
+ flags = ContainsPacketFlags(flags | CompletePacket);
+ }
+
+ return flags;
+}
+
+TagLib::uint Ogg::Page::packetCount() const
+{
+ return d->header.packetSizes().size();
+}
+
+ByteVectorList Ogg::Page::packets() const
+{
+ if(!d->packets.isEmpty())
+ return d->packets;
+
+ ByteVectorList l;
+
+ if(d->file && d->header.isValid()) {
+
+ d->file->seek(d->packetOffset);
+
+ List<int> packetSizes = d->header.packetSizes();
+
+ List<int>::ConstIterator it = packetSizes.begin();
+ for(; it != packetSizes.end(); ++it)
+ l.append(d->file->readBlock(*it));
+ }
+ else
+ debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
+
+ return l;
+}
+
+int Ogg::Page::size() const
+{
+ return d->header.size() + d->header.dataSize();
+}
+
+ByteVector Ogg::Page::render() const
+{
+ ByteVector data;
+
+ data.append(d->header.render());
+
+ if(d->packets.isEmpty()) {
+ if(d->file) {
+ d->file->seek(d->packetOffset);
+ data.append(d->file->readBlock(d->dataSize));
+ }
+ else
+ debug("Ogg::Page::render() -- this page is empty!");
+ }
+ else {
+ ByteVectorList::ConstIterator it = d->packets.begin();
+ for(; it != d->packets.end(); ++it)
+ data.append(*it);
+ }
+
+ // Compute and set the checksum for the Ogg page. The checksum is taken over
+ // the entire page with the 4 bytes reserved for the checksum zeroed and then
+ // inserted in bytes 22-25 of the page header.
+
+ ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
+ for(int i = 0; i < 4; i++)
+ data[i + 22] = checksum[i];
+
+ return data;
+}
+
+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();
+
+ if(strategy == Repaginate || totalSize + packets.size() > 255 * 256) {
+ debug("Ogg::Page::paginate() -- Sorry! Repagination is not yet implemented.");
+ return l;
+ }
+
+ // TODO: Handle creation of multiple pages here with appropriate pagination.
+
+ Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
+ lastPacketCompleted, containsLastPacket);
+ l.append(p);
+
+ return l;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::Page::Page(const ByteVectorList &packets,
+ uint streamSerialNumber,
+ int pageNumber,
+ bool firstPacketContinued,
+ bool lastPacketCompleted,
+ bool containsLastPacket)
+{
+ d = new PagePrivate;
+
+ ByteVector data;
+ List<int> packetSizes;
+
+ d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued);
+ d->header.setLastPageOfStream(containsLastPacket);
+ d->header.setFirstPacketContinued(firstPacketContinued);
+ d->header.setLastPacketCompleted(lastPacketCompleted);
+ d->header.setStreamSerialNumber(streamSerialNumber);
+ d->header.setPageSequenceNumber(pageNumber);
+
+ // Build a page from the list of packets.
+
+ for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
+ packetSizes.append((*it).size());
+ data.append(*it);
+ }
+ d->packets = packets;
+ d->header.setPacketSizes(packetSizes);
+}