#ifndef TAG_PARSER_OGGPAGE_H #define TAG_PARSER_OGGPAGE_H #include "../global.h" #include #include #include #include namespace TagParser { class TAG_PARSER_EXPORT OggPage { public: OggPage(); OggPage(std::istream &stream, std::uint64_t startOffset, std::int32_t maxSize); void parseHeader(std::istream &stream, std::uint64_t startOffset, std::int32_t maxSize); static std::uint32_t computeChecksum(std::istream &stream, std::uint64_t startOffset); static void updateChecksum(std::iostream &stream, std::uint64_t startOffset); std::uint64_t startOffset() const; std::uint8_t streamStructureVersion() const; std::uint8_t headerTypeFlag() const; bool isContinued() const; bool isFirstpage() const; bool isLastPage() const; bool isLastSegmentUnconcluded() const; std::uint64_t absoluteGranulePosition() const; std::uint32_t streamSerialNumber() const; bool matchesStreamSerialNumber(std::uint32_t streamSerialNumber) const; std::uint32_t sequenceNumber() const; std::uint32_t checksum() const; std::uint8_t segmentTableSize() const; const std::vector &segmentSizes() const; std::uint32_t headerSize() const; std::uint32_t dataSize() const; std::uint32_t totalSize() const; std::uint64_t dataOffset(std::vector::size_type segmentIndex = 0) const; static std::uint32_t makeSegmentSizeDenotation(std::ostream &stream, std::uint32_t size); private: std::uint64_t m_startOffset; std::uint8_t m_streamStructureVersion; std::uint8_t m_headerTypeFlag; std::uint64_t m_absoluteGranulePosition; std::uint32_t m_streamSerialNumber; std::uint32_t m_sequenceNumber; std::uint32_t m_checksum; std::uint8_t m_segmentCount; std::vector m_segmentSizes; }; /*! * \brief Constructs a new OGG page. */ inline OggPage::OggPage() : m_startOffset(0) , m_streamStructureVersion(0) , m_headerTypeFlag(0) , m_absoluteGranulePosition(0) , m_streamSerialNumber(0) , m_sequenceNumber(0) , m_checksum(0) , m_segmentCount(0) { } /*! * \brief Constructs a new OggPage and instantly parses the header read from the specified \a stream * at the specified \a startOffset. */ inline OggPage::OggPage(std::istream &stream, std::uint64_t startOffset, std::int32_t maxSize) : OggPage() { parseHeader(stream, startOffset, maxSize); } /*! * \brief Returns the start offset of the page. * * The start offset has been specified when calling the parseHeader() method. */ inline std::uint64_t OggPage::startOffset() const { return m_startOffset; } /*! * \brief Returns the stream structure version. */ inline std::uint8_t OggPage::streamStructureVersion() const { return m_streamStructureVersion; } /*! * \brief Returns the header type flag. * \sa isContinued() * \sa isFirstpage() * \sa isLastPage() */ inline std::uint8_t OggPage::headerTypeFlag() const { return m_headerTypeFlag & 0xF; // last 4 bits are used internally } /*! * \brief Returns whether this page is a continued packed (true) or a fresh packed (false). */ inline bool OggPage::isContinued() const { return m_headerTypeFlag & 0x01; } /*! * \brief Returns whether this page is the first page of the logical bitstream. */ inline bool OggPage::isFirstpage() const { return m_headerTypeFlag & 0x02; } /*! * \brief Returns whether this page is the last page of the logical bitstream. */ inline bool OggPage::isLastPage() const { return m_headerTypeFlag & 0x04; } /*! * \brief Returns whether the last segment is unconcluded (the last lacing value of the last segment is 0xFF). */ inline bool OggPage::isLastSegmentUnconcluded() const { return m_headerTypeFlag & 0x80; } /*! * \brief Returns the absolute granule position. * * The position specified is the total samples encoded after including all packets finished on this * page (packets begun on this page but continuing on to the next page do not count). The rationale * here is that the position specified in the frame header of the last page tells how long the data * coded by the bitstream is. A truncated stream will still return the proper number of samples that * can be decoded fully. * * A special value of '-1' (in two's complement) indicates that no packets finish on this page. */ inline std::uint64_t OggPage::absoluteGranulePosition() const { return m_absoluteGranulePosition; } /*! * \brief Returns the stream serial number. * * Ogg allows for separate logical bitstreams to be mixed at page granularity in a physical bitstream. * The most common case would be sequential arrangement, but it is possible to interleave pages for * two separate bitstreams to be decoded concurrently. The serial number is the means by which pages * physical pages are associated with a particular logical stream. */ inline std::uint32_t OggPage::streamSerialNumber() const { return m_streamSerialNumber; } /*! * \brief Returns whether the stream serial number of the current instance matches the specified one. * \sa streamSerialNumber() */ inline bool OggPage::matchesStreamSerialNumber(std::uint32_t streamSerialNumber) const { return m_streamSerialNumber == streamSerialNumber; } /*! * \brief Returns the page sequence number. * * Page counter; lets us know if a page is lost (useful where packets span page boundaries). */ inline std::uint32_t OggPage::sequenceNumber() const { return m_sequenceNumber; } /*! * \brief Returns the page checksum. * * 32 bit CRC value (direct algorithm, initial val and final XOR = 0, generator polynomial=0x04c11db7). * The value is computed over the entire header (with the CRC field in the header set to zero) and then * continued over the page. The CRC field is then filled with the computed value. * * \sa This method returns the checksum denoted by the header. To compute the actual checksum use * the computeChecksum() method. */ inline std::uint32_t OggPage::checksum() const { return m_checksum; } /*! * \brief Returns the size of the segment table. * * The number of segment entries to appear in the segment table. */ inline std::uint8_t OggPage::segmentTableSize() const { return m_segmentCount; } /*! * \brief Returns the sizes of the segments of the page in byte. * * The lacing values for each packet segment physically appearing in this page are listed in contiguous order. */ inline const std::vector &OggPage::segmentSizes() const { return m_segmentSizes; } /*! * \brief Returns the header size in byte. * * This is 27 plus the number of segment entries in the segment table. */ inline std::uint32_t OggPage::headerSize() const { return 27 + m_segmentCount; } /*! * \brief Returns the data size in byte. */ inline std::uint32_t OggPage::dataSize() const { return std::accumulate(m_segmentSizes.cbegin(), m_segmentSizes.cend(), 0u); } /*! * \brief Returns the total size of the page in byte. */ inline std::uint32_t OggPage::totalSize() const { return headerSize() + dataSize(); } /*! * \brief Returns the data offset of the segment with the specified \a segmentIndex. * * This is the start offset plus the header size. * * \sa startOffset() * \sa headerSize() */ inline std::uint64_t OggPage::dataOffset(std::vector::size_type segmentIndex) const { return startOffset() + headerSize() + std::accumulate( m_segmentSizes.cbegin(), m_segmentSizes.cbegin() + static_cast(segmentIndex), 0u); } } // namespace TagParser #endif // TAG_PARSER_OGGPAGE_H