Tag Parser  9.1.3
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
oggstream.cpp
Go to the documentation of this file.
1 #include "./oggstream.h"
2 #include "./oggcontainer.h"
3 
4 #include "../vorbis/vorbisidentificationheader.h"
5 #include "../vorbis/vorbispackagetypes.h"
6 
7 #include "../opus/opusidentificationheader.h"
8 
9 #include "../flac/flactooggmappingheader.h"
10 
11 #include "../exceptions.h"
12 #include "../mediafileinfo.h"
13 #include "../mediaformat.h"
14 
15 #include <c++utilities/chrono/timespan.h>
16 
17 #include <functional>
18 #include <iostream>
19 
20 using namespace std;
21 using namespace std::placeholders;
22 using namespace CppUtilities;
23 
24 namespace TagParser {
25 
34 OggStream::OggStream(OggContainer &container, vector<OggPage>::size_type startPage)
35  : AbstractTrack(container.stream(), container.m_iterator.pages()[startPage].startOffset())
36  , m_startPage(startPage)
37  , m_container(container)
38  , m_currentSequenceNumber(0)
39 {
40 }
41 
46 {
47 }
48 
50 {
51  static const string context("parsing OGG page header");
52 
53  // read basic information from first page
54  OggIterator &iterator = m_container.m_iterator;
55  const OggPage &firstPage = iterator.pages()[m_startPage];
56  m_version = firstPage.streamStructureVersion();
57  m_id = firstPage.streamSerialNumber();
58 
59  // ensure iterator is setup properly
60  iterator.setFilter(firstPage.streamSerialNumber());
61  iterator.setPageIndex(m_startPage);
62 
63  // iterate through segments using OggIterator
64  for (bool hasIdentificationHeader = false, hasCommentHeader = false; iterator && (!hasIdentificationHeader || !hasCommentHeader); ++iterator) {
65  const std::uint32_t currentSize = iterator.currentSegmentSize();
66  if (currentSize >= 8) {
67  // determine stream format
68  inputStream().seekg(static_cast<streamoff>(iterator.currentSegmentOffset()));
69  const std::uint64_t sig = reader().readUInt64BE();
70 
71  if ((sig & 0x00ffffffffffff00u) == 0x00766F7262697300u) {
72  // Vorbis header detected
73  switch (m_format.general) {
77  break;
79  break;
80  default:
81  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
82  continue;
83  }
84 
85  // check header type
86  switch (sig >> 56) {
88  if (!hasIdentificationHeader) {
89  // parse identification header
91  ind.parseHeader(iterator);
92  m_version = ind.version();
93  m_channelCount = ind.channels();
95  if (ind.nominalBitrate()) {
96  m_bitrate = ind.nominalBitrate();
97  } else if (ind.maxBitrate() == ind.minBitrate()) {
98  m_bitrate = ind.maxBitrate();
99  }
100  if (m_bitrate != 0.0) {
101  m_bitrate /= 1000.0;
102  }
103  calculateDurationViaSampleCount();
104  hasIdentificationHeader = true;
105  } else {
106  diag.emplace_back(DiagLevel::Critical,
107  "Vorbis identification header appears more than once. Oversupplied occurrence will be ignored.", context);
108  }
109  break;
111  // Vorbis comment found -> notify container about comment
112  if (!hasCommentHeader) {
113  m_container.announceComment(iterator.currentPageIndex(), iterator.currentSegmentIndex(), false, GeneralMediaFormat::Vorbis);
114  hasCommentHeader = true;
115  } else {
116  diag.emplace_back(
117  DiagLevel::Critical, "Vorbis comment header appears more than once. Oversupplied occurrence will be ignored.", context);
118  }
119  break;
121  break; // TODO
122  default:;
123  }
124 
125  } else if (sig == 0x4F70757348656164u) {
126  // Opus header detected
127  switch (m_format.general) {
131  break;
133  break;
134  default:
135  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
136  continue;
137  }
138  if (!hasIdentificationHeader) {
139  // parse identification header
141  ind.parseHeader(iterator);
142  m_version = ind.version();
143  m_channelCount = ind.channels();
145  calculateDurationViaSampleCount(ind.preSkip());
146  hasIdentificationHeader = true;
147  } else {
148  diag.emplace_back(
149  DiagLevel::Critical, "Opus identification header appears more than once. Oversupplied occurrence will be ignored.", context);
150  }
151 
152  } else if (sig == 0x4F70757354616773u) {
153  // Opus comment detected
154  switch (m_format.general) {
158  break;
160  break;
161  default:
162  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
163  continue;
164  }
165 
166  // notify container about comment
167  if (!hasCommentHeader) {
168  m_container.announceComment(iterator.currentPageIndex(), iterator.currentSegmentIndex(), false, GeneralMediaFormat::Opus);
169  hasCommentHeader = true;
170  } else {
171  diag.emplace_back(
172  DiagLevel::Critical, "Opus tags/comment header appears more than once. Oversupplied occurrence will be ignored.", context);
173  }
174 
175  } else if ((sig & 0xFFFFFFFFFF000000u) == 0x7F464C4143000000u) {
176  // FLAC header detected
177  switch (m_format.general) {
181  break;
183  break;
184  default:
185  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
186  continue;
187  }
188 
189  if (!hasIdentificationHeader) {
190  // parse FLAC-to-Ogg mapping header
191  FlacToOggMappingHeader mapping;
192  const FlacMetaDataBlockStreamInfo &streamInfo = mapping.streamInfo();
193  mapping.parseHeader(iterator);
194  m_bitsPerSample = streamInfo.bitsPerSample();
195  m_channelCount = streamInfo.channelCount();
196  m_samplingFrequency = streamInfo.samplingFrequency();
197  m_sampleCount = streamInfo.totalSampleCount();
198  calculateDurationViaSampleCount();
199  hasIdentificationHeader = true;
200  } else {
201  diag.emplace_back(
202  DiagLevel::Critical, "FLAC-to-Ogg mapping header appears more than once. Oversupplied occurrence will be ignored.", context);
203  }
204 
205  if (!hasCommentHeader) {
206  // a Vorbis comment should be following
207  if (++iterator) {
208  char buff[4];
209  iterator.read(buff, 4);
211  header.parseHeader(buff);
212  if (header.type() == FlacMetaDataBlockType::VorbisComment) {
213  m_container.announceComment(
214  iterator.currentPageIndex(), iterator.currentSegmentIndex(), header.isLast(), GeneralMediaFormat::Flac);
215  hasCommentHeader = true;
216  } else {
217  diag.emplace_back(
218  DiagLevel::Critical, "OGG page after FLAC-to-Ogg mapping header doesn't contain Vorbis comment.", context);
219  }
220  } else {
221  diag.emplace_back(
222  DiagLevel::Critical, "No more OGG pages after FLAC-to-Ogg mapping header (Vorbis comment expected).", context);
223  }
224  }
225 
226  } else if ((sig & 0x00ffffffffffff00u) == 0x007468656F726100u) {
227  // Theora header detected
228  switch (m_format.general) {
232  break;
234  break;
235  default:
236  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
237  continue;
238  }
239  // TODO: read more information about Theora stream
240 
241  } else if ((sig & 0xFFFFFFFFFFFF0000u) == 0x5370656578200000u) {
242  // Speex header detected
243  switch (m_format.general) {
247  break;
249  break;
250  default:
251  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
252  continue;
253  }
254  // TODO: read more information about Speex stream
255  } else if (sig == 0x595556344D504547u) {
256  // YUV4MPEG header detected
257  switch (m_format.general) {
261  m_chromaFormat = "YUV";
262  break;
264  break;
265  default:
266  diag.emplace_back(DiagLevel::Warning, "Stream format is inconsistent.", context);
267  continue;
268  }
269  // TODO: read more information about YUV4MPEG stream
270  }
271  // currently only Vorbis, Opus, Theora, Speex and YUV4MPEG can be detected, TODO: detect more formats
272 
273  } else {
274  // just ignore segments of only 8 byte or even less
275  // TODO: print warning?
276  }
277 
278  // TODO: reduce code duplication
279  }
280 
281  // estimate duration from size and bitrate if sample count and sample rate could not be determined
282  if (m_duration.isNull() && m_size && m_bitrate != 0.0) {
283  // calculate duration from stream size and bitrate, assuming 1 % overhead
284  m_duration = TimeSpan::fromSeconds(static_cast<double>(m_size) / (m_bitrate * 125.0) * 1.1);
285  }
286  m_headerValid = true;
287 }
288 
289 void OggStream::calculateDurationViaSampleCount(std::uint16_t preSkip)
290 {
291  // define predicate for finding pages of this stream by its stream serial number
292  const auto pred = bind(&OggPage::matchesStreamSerialNumber, _1, m_id);
293 
294  // determine sample count
295  const auto &iterator = m_container.m_iterator;
296  if (!m_sampleCount && iterator.isLastPageFetched()) {
297  const auto &pages = iterator.pages();
298  const auto firstPage = find_if(pages.cbegin(), pages.cend(), pred);
299  const auto lastPage = find_if(pages.crbegin(), pages.crend(), pred);
300  if (firstPage != pages.cend() && lastPage != pages.crend()) {
301  m_sampleCount = lastPage->absoluteGranulePosition() - firstPage->absoluteGranulePosition();
302  // must apply "pre-skip" here to calculate effective sample count and duration?
303  if (m_sampleCount > preSkip) {
304  m_sampleCount -= preSkip;
305  } else {
306  m_sampleCount = 0;
307  }
308  }
309  }
310 
311  // actually calculate the duration
312  if (m_sampleCount && m_samplingFrequency != 0.0) {
313  m_duration = TimeSpan::fromSeconds(static_cast<double>(m_sampleCount) / m_samplingFrequency);
314  }
315 }
316 
317 } // namespace TagParser
TagParser::AbstractTrack::m_samplingFrequency
std::uint32_t m_samplingFrequency
Definition: abstracttrack.h:139
TagParser::MediaType::Audio
@ Audio
TagParser::FlacMetaDataBlockStreamInfo::bitsPerSample
constexpr std::uint8_t bitsPerSample() const
Returns the bits per sample.
Definition: flacmetadata.h:219
TagParser::OggIterator::setPageIndex
void setPageIndex(std::vector< OggPage >::size_type index)
Sets the current page index.
Definition: oggiterator.h:172
TagParser::AbstractTrack::m_bitrate
double m_bitrate
Definition: abstracttrack.h:134
TagParser::FlacMetaDataBlockStreamInfo
The FlacMetaDataBlockStreamInfo class is a FLAC "METADATA_BLOCK_STREAMINFO" parser.
Definition: flacmetadata.h:109
TagParser::OpusIdentificationHeader::version
constexpr std::uint8_t version() const
Returns the version (which should be 1 currently).
Definition: opusidentificationheader.h:50
TagParser::OggPage::streamSerialNumber
std::uint32_t streamSerialNumber() const
Returns the stream serial number.
Definition: oggpage.h:155
TagParser::OggIterator
The OggIterator class helps iterating through all segments of an OGG bitstream.
Definition: oggiterator.h:11
TagParser::VorbisIdentificationHeader::maxBitrate
constexpr std::uint32_t maxBitrate() const
Definition: vorbisidentificationheader.h:68
TagParser::OggPage::streamStructureVersion
std::uint8_t streamStructureVersion() const
Returns the stream structure version.
Definition: oggpage.h:91
TagParser::OggIterator::currentSegmentOffset
std::uint64_t currentSegmentOffset() const
Returns the start offset of the current segment in the input stream if the iterator is valid; otherwi...
Definition: oggiterator.h:202
TagParser::FlacToOggMappingHeader
The FlacToOggMappingHeader class is a FLAC-to-Ogg mapping header parser.
Definition: flactooggmappingheader.h:10
TagParser::AbstractTrack::m_version
double m_version
Definition: abstracttrack.h:128
TagParser::DiagLevel::Warning
@ Warning
TagParser::FlacMetaDataBlockHeader::isLast
constexpr std::uint8_t isLast() const
Returns whether this is the last metadata block before the audio blocks.
Definition: flacmetadata.h:62
TagParser::OggContainer
Implementation of TagParser::AbstractContainer for OGG files.
Definition: oggcontainer.h:129
TagParser::FlacMetaDataBlockHeader::type
constexpr std::uint8_t type() const
Returns the block type.
Definition: flacmetadata.h:79
TagParser::AbstractTrack::m_chromaFormat
const char * m_chromaFormat
Definition: abstracttrack.h:155
TagParser::FlacMetaDataBlockStreamInfo::samplingFrequency
constexpr std::uint32_t samplingFrequency() const
Returns the sampling frequency in Hz.
Definition: flacmetadata.h:197
TagParser::FlacMetaDataBlockStreamInfo::totalSampleCount
constexpr std::uint64_t totalSampleCount() const
Returns the total samples in stream.
Definition: flacmetadata.h:232
TagParser::GeneralMediaFormat::Speex
@ Speex
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
TagParser::VorbisIdentificationHeader::nominalBitrate
constexpr std::uint32_t nominalBitrate() const
Definition: vorbisidentificationheader.h:73
TagParser::GeneralMediaFormat::UncompressedVideoFrames
@ UncompressedVideoFrames
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::OggIterator::currentPageIndex
std::vector< OggPage >::size_type currentPageIndex() const
Returns the index of the current page if the iterator is valid; otherwise an undefined index is retur...
Definition: oggiterator.h:163
TagParser::AbstractTrack::m_sampleCount
std::uint64_t m_sampleCount
Definition: abstracttrack.h:147
TagParser::FourccIds::Flac
@ Flac
Definition: mp4ids.h:272
TagParser::OggIterator::setFilter
void setFilter(std::uint32_t streamSerialId)
Allows to filter pages by the specified streamSerialId.
Definition: oggiterator.h:242
TagParser::FlacMetaDataBlockType::VorbisComment
@ VorbisComment
TagParser::GeneralMediaFormat::Unknown
@ Unknown
TagParser::OpusIdentificationHeader::channels
constexpr std::uint8_t channels() const
Returns the number of channels for the Opus stream.
Definition: opusidentificationheader.h:58
TagParser::GeneralMediaFormat::Opus
@ Opus
TagParser::VorbisIdentificationHeader::channels
constexpr std::uint8_t channels() const
Definition: vorbisidentificationheader.h:58
TagParser::VorbisIdentificationHeader::sampleRate
constexpr std::uint32_t sampleRate() const
Definition: vorbisidentificationheader.h:63
TagParser::AbstractTrack::m_duration
CppUtilities::TimeSpan m_duration
Definition: abstracttrack.h:133
TagParser::OggPage
The OggPage class is used to parse OGG pages.
Definition: oggpage.h:13
TagParser::AbstractTrack::inputStream
std::istream & inputStream()
Returns the associated input stream.
Definition: abstracttrack.h:173
TagParser::AbstractTrack::m_id
std::uint64_t m_id
Definition: abstracttrack.h:131
TagParser::FlacMetaDataBlockHeader::parseHeader
void parseHeader(const char *buffer)
Parses the FLAC "METADATA_BLOCK_HEADER" which is read using the specified iterator.
Definition: flacmetadata.cpp:29
TagParser::AbstractTrack::m_size
std::uint64_t m_size
Definition: abstracttrack.h:129
TagParser::VorbisPackageTypes::Setup
@ Setup
Definition: vorbispackagetypes.h:12
oggstream.h
TagParser::FlacToOggMappingHeader::streamInfo
constexpr const FlacMetaDataBlockStreamInfo & streamInfo() const
Returns the stream info.
Definition: flactooggmappingheader.h:65
TagParser::DiagLevel::Critical
@ Critical
TagParser::VorbisIdentificationHeader::version
constexpr std::uint32_t version() const
Definition: vorbisidentificationheader.h:53
TagParser::AbstractTrack::m_headerValid
bool m_headerValid
Definition: abstracttrack.h:123
CppUtilities
Definition: abstractcontainer.h:15
TagParser::OggPage::matchesStreamSerialNumber
bool matchesStreamSerialNumber(std::uint32_t streamSerialNumber) const
Returns whether the stream serial number of the current instance matches the specified one.
Definition: oggpage.h:164
TagParser::VorbisPackageTypes::Identification
@ Identification
Definition: vorbispackagetypes.h:12
TagParser::AbstractTrack
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:39
TagParser::AbstractTrack::m_channelCount
std::uint16_t m_channelCount
Definition: abstracttrack.h:143
TagParser::OggStream::~OggStream
~OggStream() override
Destroys the track.
Definition: oggstream.cpp:45
TagParser::AbstractTrack::m_mediaType
MediaType m_mediaType
Definition: abstracttrack.h:127
TagParser::VorbisIdentificationHeader::parseHeader
void parseHeader(OggIterator &iterator)
Parses the Vorbis identification header which is read using the specified iterator.
Definition: vorbisidentificationheader.cpp:24
TagParser::MediaFormat::general
GeneralMediaFormat general
Definition: mediaformat.h:258
TagParser::AbstractTrack::m_bitsPerSample
std::uint16_t m_bitsPerSample
Definition: abstracttrack.h:141
TagParser::FlacMetaDataBlockHeader
The FlacMetaDataBlockHeader class is a FLAC "METADATA_BLOCK_HEADER" parser and maker.
Definition: flacmetadata.h:28
TagParser::AbstractTrack::m_format
MediaFormat m_format
Definition: abstracttrack.h:124
TagParser::FlacMetaDataBlockStreamInfo::channelCount
constexpr std::uint8_t channelCount() const
Returns the number of channels.
Definition: flacmetadata.h:207
TagParser::OpusIdentificationHeader::sampleRate
constexpr std::uint32_t sampleRate() const
Returns the INPUT sample rate.
Definition: opusidentificationheader.h:80
TagParser::OggStream::internalParseHeader
void internalParseHeader(Diagnostics &diag) override
This method is internally called to parse header information. It needs to be implemented when subclas...
Definition: oggstream.cpp:49
TagParser::AbstractTrack::reader
CppUtilities::BinaryReader & reader()
Returns a binary reader for the associated stream.
Definition: abstracttrack.h:214
TagParser::OpusIdentificationHeader::parseHeader
void parseHeader(OggIterator &iterator)
Parses the Opus identification header which is read using the specified iterator.
Definition: opusidentificationheader.cpp:24
TagParser::OggIterator::currentSegmentIndex
std::vector< std::uint32_t >::size_type currentSegmentIndex() const
Returns the index of the current segment (in the current page) if the iterator is valid; otherwise an...
Definition: oggiterator.h:193
TagParser::OggIterator::pages
const std::vector< OggPage > & pages() const
Returns a vector of containing the OGG pages that have been fetched yet.
Definition: oggiterator.h:122
TagParser::OpusIdentificationHeader::preSkip
constexpr std::uint16_t preSkip() const
Returns "pre-skip" value for the Opus stream.
Definition: opusidentificationheader.h:70
TagParser::VorbisPackageTypes::Comments
@ Comments
Definition: vorbispackagetypes.h:12
TagParser::MediaType::Video
@ Video
TagParser::OggIterator::currentSegmentSize
std::uint32_t currentSegmentSize() const
Returns the size of the current segment.
Definition: oggiterator.h:229
TagParser::VorbisIdentificationHeader::minBitrate
constexpr std::uint32_t minBitrate() const
Definition: vorbisidentificationheader.h:78
TagParser::OggIterator::read
void read(char *buffer, std::size_t count)
Reads count bytes from the OGG stream and writes it to the specified buffer.
Definition: oggiterator.cpp:134
oggcontainer.h
TagParser::GeneralMediaFormat::Theora
@ Theora
TagParser::VorbisIdentificationHeader
The VorbisIdentificationHeader class is a Vorbis identification header parser.
Definition: vorbisidentificationheader.h:12
TagParser::GeneralMediaFormat::Vorbis
@ Vorbis
TagParser::FlacToOggMappingHeader::parseHeader
void parseHeader(OggIterator &iterator)
Parses the FLAC-to-Ogg mapping header which is read using the specified iterator.
Definition: flactooggmappingheader.cpp:24
TagParser::OpusIdentificationHeader
The OpusIdentificationHeader class is an Opus identification header parser.
Definition: opusidentificationheader.h:12