4 #include "../vorbis/vorbisidentificationheader.h"
5 #include "../vorbis/vorbispackagetypes.h"
7 #include "../opus/opusidentificationheader.h"
9 #include "../flac/flactooggmappingheader.h"
11 #include "../exceptions.h"
12 #include "../mediafileinfo.h"
13 #include "../mediaformat.h"
15 #include <c++utilities/chrono/timespan.h>
21 using namespace std::placeholders;
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)
51 static const string context(
"parsing OGG page header");
55 const OggPage &firstPage = iterator.
pages()[m_startPage];
64 for (
bool hasIdentificationHeader =
false, hasCommentHeader =
false; iterator && (!hasIdentificationHeader || !hasCommentHeader); ++iterator) {
66 if (currentSize >= 8) {
69 const std::uint64_t sig =
reader().readUInt64BE();
71 if ((sig & 0x00ffffffffffff00u) == 0x00766F7262697300u) {
74 case GeneralMediaFormat::Unknown:
75 m_format = GeneralMediaFormat::Vorbis;
78 case GeneralMediaFormat::Vorbis:
81 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
88 if (!hasIdentificationHeader) {
103 calculateDurationViaSampleCount();
104 hasIdentificationHeader =
true;
106 diag.emplace_back(DiagLevel::Critical,
107 "Vorbis identification header appears more than once. Oversupplied occurrence will be ignored.", context);
112 if (!hasCommentHeader) {
114 hasCommentHeader =
true;
117 DiagLevel::Critical,
"Vorbis comment header appears more than once. Oversupplied occurrence will be ignored.", context);
125 }
else if (sig == 0x4F70757348656164u) {
128 case GeneralMediaFormat::Unknown:
135 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
138 if (!hasIdentificationHeader) {
145 calculateDurationViaSampleCount(ind.
preSkip());
146 hasIdentificationHeader =
true;
149 DiagLevel::Critical,
"Opus identification header appears more than once. Oversupplied occurrence will be ignored.", context);
152 }
else if (sig == 0x4F70757354616773u) {
155 case GeneralMediaFormat::Unknown:
162 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
167 if (!hasCommentHeader) {
169 hasCommentHeader =
true;
172 DiagLevel::Critical,
"Opus tags/comment header appears more than once. Oversupplied occurrence will be ignored.", context);
175 }
else if ((sig & 0xFFFFFFFFFF000000u) == 0x7F464C4143000000u) {
178 case GeneralMediaFormat::Unknown:
185 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
189 if (!hasIdentificationHeader) {
198 calculateDurationViaSampleCount();
199 hasIdentificationHeader =
true;
202 DiagLevel::Critical,
"FLAC-to-Ogg mapping header appears more than once. Oversupplied occurrence will be ignored.", context);
205 if (!hasCommentHeader) {
209 iterator.
read(buff, 4);
213 m_container.announceComment(
215 hasCommentHeader =
true;
218 DiagLevel::Critical,
"OGG page after FLAC-to-Ogg mapping header doesn't contain Vorbis comment.", context);
222 DiagLevel::Critical,
"No more OGG pages after FLAC-to-Ogg mapping header (Vorbis comment expected).", context);
226 }
else if ((sig & 0x00ffffffffffff00u) == 0x007468656F726100u) {
229 case GeneralMediaFormat::Unknown:
230 m_format = GeneralMediaFormat::Theora;
233 case GeneralMediaFormat::Theora:
236 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
241 }
else if ((sig & 0xFFFFFFFFFFFF0000u) == 0x5370656578200000u) {
244 case GeneralMediaFormat::Unknown:
245 m_format = GeneralMediaFormat::Speex;
248 case GeneralMediaFormat::Speex:
251 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
255 }
else if (sig == 0x595556344D504547u) {
258 case GeneralMediaFormat::Unknown:
259 m_format = GeneralMediaFormat::UncompressedVideoFrames;
263 case GeneralMediaFormat::UncompressedVideoFrames:
266 diag.emplace_back(DiagLevel::Warning,
"Stream format is inconsistent.", context);
289 void OggStream::calculateDurationViaSampleCount(std::uint16_t preSkip)
295 const auto &iterator = m_container.m_iterator;
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();