DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
Demuxers.hpp
Go to the documentation of this file.
1/*
2 MIT License
3
4 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
5
6 Copyright 2022, 2023 Chris M. (RealTimeChris)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26/// Demuxers.hpp - Header file for the Demuxer classes.
27/// Jun 8, 2023
28/// https://discordcoreapi.com
29/// \file Demuxers.hpp
30#pragma once
31
33#include <opus/opus.h>
34#include <fstream>
35
36namespace discord_core_api {
37
38 namespace discord_core_internal {
39
40 /**
41 * \addtogroup discord_core_internal
42 * @{
43 */
44
45 static constexpr uint32_t segmentId{ 0x18538067 };
46 static constexpr uint8_t simpleBlockId{ 0xA3 };
47 static constexpr uint8_t opusTrackId{ 0x81 };
48
49 static constexpr uint8_t ffLog2Tab[]{ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
50 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
51 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
52 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
53 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 };
54
55 /// @brief A class for demuxing Matroska-contained audio data.
57 public:
58 /// @brief Constructor for matroska_demuxer.
59 DCA_INLINE matroska_demuxer() = default;
60
61 /// @brief Writes data to the Matroska demuxer.
62 /// @param dataNew The data to be written.
63 DCA_INLINE void writeData(jsonifier::string_view_base<uint8_t> dataNew) {
64 data = dataNew;
65 }
66
67 /// @brief Collects the next frame from the demuxer.
68 /// @param frameNew The reference to store the collected frame.
69 /// @return True if a frame was collected, false otherwise.
70 DCA_INLINE bool collectFrame(audio_frame_data& frameNew) {
71 if (frames.size() > 0) {
72 frameNew = std::move(frames.at(0));
73 frames.erase(frames.begin());
74 return true;
75 } else {
76 return false;
77 }
78 }
79
80 /// @brief Proceed with the demuxing process.
81 DCA_INLINE void proceedDemuxing() {
82 if (!doWeHaveTotalSize) {
83 if (reverseBytes<uint32_t>() != segmentId) {
85 jsonifier::string{ "Missing a Segment, which was expected at index: " + jsonifier::toString(currentPosition) + jsonifier::string{ "..." } });
86 if (!findNextId(segmentId)) {
87 if ((totalSize > 0 && static_cast<int64_t>(currentPosition) >= totalSize)) {
88 areWeDoneVal = true;
89 }
90 return;
91 }
93 jsonifier::string{ "Missing Segment, found at index: " + jsonifier::toString(currentPosition) + "." });
94 } else {
95 currentPosition += sizeof(uint32_t);
96 }
98 doWeHaveTotalSize = true;
99 }
100 while (currentPosition + 3 < data.size() && data.find(static_cast<uint8_t>(0xa3)) != jsonifier::string::npos) {
101 if (currentPosition >= data.size() - 8) {
102 if ((totalSize > 0 && static_cast<int64_t>(currentPosition) >= totalSize)) {
103 areWeDoneVal = true;
104 }
105 return;
106 }
107 if (data.at(currentPosition) == simpleBlockId && ((data.at(currentPosition + 2) == opusTrackId || data.at(currentPosition + 3) == opusTrackId))) {
109 if (currentSize == 0) {
110 currentSize = static_cast<uint64_t>(collectElementSize());
111 if (static_cast<int64_t>(currentSize) >= totalSize || static_cast<int64_t>(currentSize) >= 1276 || static_cast<int64_t>(currentSize) < 4) {
113 currentSize = 0;
114 continue;
115 } else if (currentSize == static_cast<uint64_t>(-1) || currentPosition + currentSize >= data.size()) {
116 if ((totalSize > 0 && static_cast<int64_t>(currentPosition) >= totalSize)) {
117 areWeDoneVal = true;
118 }
119 return;
120 } else {
122 }
123 } else {
124 currentSize = 0;
125 }
126 } else {
128 }
129 }
130 if ((totalSize > 0 && static_cast<int64_t>(currentPosition) >= totalSize)) {
131 areWeDoneVal = true;
132 }
133 return;
134 }
135
136 /// @brief Checks if the demuxing process is complete.
137 /// @return True if demuxing is complete, false otherwise.
138 DCA_INLINE bool areWeDone() {
139 return areWeDoneVal;
140 }
141
142 protected:
143 jsonifier::string_view_base<uint8_t> data{};///< Input data for demuxing.
144 std::deque<audio_frame_data> frames{};///< Queue to store collected frames.
145 bool doWeHaveTotalSize{ false };///< Flag indicating if total size has been determined.
146 bool areWeDoneVal{ false };///< Flag indicating if demuxing is complete.
147 uint64_t currentPosition{};///< Current position in the data.
148 uint64_t currentSize{};///< Current size of the element being processed.
149 int64_t totalSize{};///< Total size of the segment.
150
151 /// @brief Finds the next occurrence of the specified value in the data.
152 /// @tparam object_type The type of value to search for.
153 /// @param value The value to search for.
154 /// @return True if the value was found, false otherwise.
155 template<typename object_type> DCA_INLINE bool findNextId(object_type value) {
156 if (currentPosition + sizeof(object_type) >= data.size()) {
157 return false;
158 }
159 while (currentPosition + sizeof(object_type) < data.size()) {
160 if (reverseBytes<object_type>() == value) {
161 currentPosition += sizeof(object_type);
162 return true;
163 }
165 }
166 return false;
167 }
168
169 /// @brief Reverses the byte order of the current element being processed.
170 /// @tparam object_type The type of the current element.
171 /// @return The current element with reversed byte order.
172 template<typename object_type> DCA_INLINE object_type reverseBytes() {
173 if (data.size() <= currentPosition + sizeof(object_type)) {
174 return static_cast<object_type>(-1);
175 }
176 object_type newValue{};
177 std::memcpy(&newValue, &data.at(currentPosition), sizeof(object_type));
178 reverseByteOrder(newValue);
179 return newValue;
180 }
181
182 /// @brief Collects the size of the current element being processed.
183 /// @return The size of the current element.
184 DCA_INLINE int64_t collectElementSize() {
185 if (currentPosition >= data.size() - 8) {
186 return -1;
187 }
188 return collectNumber();
189 }
190
191 /// @brief Collects a number from the data.
192 /// @return The collected number.
193 DCA_INLINE int64_t collectNumber() {
194 uint64_t read{}, n{ 1 };
195 uint64_t total{};
196 total = static_cast<uint8_t>(data.at(currentPosition++));
197
198 read = 8ULL - ffLog2Tab[total];
199
200 total ^= 1ULL << ffLog2Tab[total];
201 while (n++ < read) {
202 total = (total << 8) | static_cast<uint8_t>(data.at(currentPosition++));
203 }
204 return static_cast<int64_t>(total);
205 }
206
207 /// @brief Parses an Opus frame.
208 DCA_INLINE void parseOpusFrame() {
209 audio_frame_data frameNew{};
210 frameNew.currentSize = static_cast<int64_t>(currentSize - 4);
211 frameNew += jsonifier::string_view_base<uint8_t>{ data.data() + currentPosition + 4, static_cast<uint64_t>(frameNew.currentSize) };
212 frameNew.type = audio_frame_type::encoded;
214 frames.emplace_back(std::move(frameNew));
215 currentSize = 0;
216 }
217 };
218
219 using opus_packet = jsonifier::vector<uint8_t>;
220
221 /// @brief A class representing an Ogg page for demuxing.
222 class ogg_page {
223 public:
224 /// @brief Constructor for ogg_page.
225 /// @param newData The data for the Ogg page.
226 DCA_INLINE ogg_page(jsonifier::vector<uint8_t>&& newData) {
227 data = std::move(newData);
230 }
231
232 /// @brief Retrieves the next Opus packet from the Ogg page.
233 /// @param newPacket Reference to store the retrieved Opus packet.
234 /// @return True if an Opus packet was retrieved, false otherwise.
235 DCA_INLINE bool getOpusPacket(opus_packet& newPacket) {
236 if (segmentTable.size() > 0) {
237 auto newSpace = static_cast<uint64_t>(segmentTable.front());
238 segmentTable.pop_front();
239 newPacket.resize(newSpace);
240 std::memcpy(newPacket.data(), data.data() + currentPosition, newSpace);
241 currentPosition += newSpace;
242 return true;
243 } else {
244 return false;
245 }
246 }
247
248 /// @brief Parses the segment data of the Ogg page.
249 DCA_INLINE void getSegmentData() {
250 segmentCount = data.at(26);
251 currentPosition += 27;
252 for (uint64_t x{}; x < segmentCount; ++x) {
253 uint64_t packetLength{ data.at(27ULL + x) };
254 while (data.at(27ULL + x) == 255) {
255 ++x;
256 packetLength += data.at(27ULL + x);
257 }
258 segmentTable.emplace_back(packetLength);
259 }
261 return;
262 }
263
264 /// @brief Returns the size of the Ogg page data.
265 /// @return The size of the Ogg page data.
266 DCA_INLINE uint64_t getDataSize() {
267 return data.size();
268 }
269
270 protected:
271 std::deque<uint64_t> segmentTable{};///< Segment table storing Opus packet sizes.
272 jsonifier::vector<uint8_t> data{};///< The data for the Ogg page.
273 uint64_t totalPacketSize{};///< Total size of Opus packets in the page.
274 uint64_t currentPosition{};///< Current position in the page data.
275 uint64_t segmentCount{};///< Number of segments in the Ogg page.
276
277 /// @brief Verifies that the data represents a valid Ogg page.
278 DCA_INLINE void verifyAsOggPage() {
279 while (data.at(currentPosition) != 'O' || data.at(currentPosition + 1) != 'g' || data.at(currentPosition + 2) != 'g' || data.at(currentPosition + 3) != 'S') {
281 if (currentPosition >= data.size()) {
282 return;
283 }
284 }
285 }
286 };
287
288 /// @brief A class for demuxing Ogg-contained audio data.
290 public:
291 DCA_INLINE ogg_demuxer() = default;
292
293 /// @brief Collects the next audio frame from the demuxer.
294 /// @param frameNew The reference to store the collected frame.
295 /// @return True if a frame was collected, false otherwise.
296 DCA_INLINE bool collectFrame(audio_frame_data& frameNew) {
297 if (frames.size() > 0) {
298 frameNew = std::move(frames.front());
299 frames.pop_front();
300 return true;
301 } else {
302 return false;
303 }
304 }
305
306 /// @brief Writes data to the Ogg demuxer and processes it.
307 /// @param inputData The data to be written and processed.
308 DCA_INLINE void writeData(jsonifier::string_view inputData) {
309 uint64_t pos = 0;
310 data.clear();
311 data.resize(inputData.size());
312 std::memcpy(data.data(), inputData.data(), inputData.size());
313 uint64_t collectedLength{};
314 while (pos < inputData.size()) {
315 uint64_t oggPos = inputData.find("OggS", pos);
316 if (oggPos != jsonifier::string::npos) {
317 uint64_t nextOggPos = inputData.find("OggS", oggPos + 1);
318 if (nextOggPos != jsonifier::string::npos) {
319 collectedLength += nextOggPos - oggPos;
320 jsonifier::vector<uint8_t> newerString{};
321 newerString.resize(nextOggPos - oggPos);
322 std::memcpy(newerString.data(), data.data() + oggPos, nextOggPos - oggPos);
323 pages.emplace_back(std::move(newerString));
324 pos = nextOggPos;
325 } else {
326 jsonifier::vector<uint8_t> newerString{};
327 newerString.resize(inputData.size() - collectedLength);
328 std::memcpy(newerString.data(), data.data() + oggPos, inputData.size() - collectedLength);
329 pages.emplace_back(std::move(newerString));
330 pos = collectedLength;
331 break;
332 }
333 } else {
334 jsonifier::vector<uint8_t> newerString{};
335 newerString.resize(inputData.size() - collectedLength);
336 std::memcpy(newerString.data(), data.data() + oggPos, inputData.size() - collectedLength);
337 pages.emplace_back(std::move(newerString));
338 break;
339 }
340 }
341 return;
342 }
343
344 /// @brief Proceeds with the demuxing process.
345 /// @return True if demuxing is successful, false otherwise.
346 DCA_INLINE bool proceedDemuxing() {
347 while (1) {
348 if (!processOggPage()) {
349 return false;
350 }
351 }
352 return true;
353 }
354
355 protected:
356 std::deque<audio_frame_data> frames{};///< Queue to store collected audio frames.
357 jsonifier::vector<uint8_t> data{};///< Input data for demuxing.
358 std::deque<opus_packet> packets{};///< Queue to store Opus packets.
359 std::deque<ogg_page> pages{};///< Queue to store Ogg pages.
360
361 /// @brief Processes an Ogg page for demuxing.
362 /// @return True if processing is successful, false if there are no more pages.
363 DCA_INLINE bool processOggPage() {
364 if (pages.empty()) {
365 return false;
366 }
367
368 processPages();
370
371 return true;
372 }
373
374 /// @brief Processes Opus packets extracted from Ogg pages.
375 DCA_INLINE void processPackets() {
376 while (!packets.empty()) {
377 opus_packet newPacket = packets.front();
378 packets.pop_front();
379 audio_frame_data newFrame{};
380 newFrame += newPacket;
381 newFrame.currentSize = static_cast<int64_t>(newPacket.size());
382 newFrame.type = audio_frame_type::encoded;
383 frames.emplace_back(std::move(newFrame));
384 }
385 }
386
387 /// @brief Processes Ogg pages to extract Opus packets.
388 DCA_INLINE void processPages() {
389 while (!pages.empty()) {
390 ogg_page page = pages.front();
391 pages.pop_front();
392 opus_packet newPacket{};
393 while (page.getOpusPacket(newPacket)) {
394 packets.emplace_back(newPacket);
395 }
396 }
397 }
398 };
399
400 /**@}*/
401 }
402}
A class for demuxing Matroska-contained audio data.
Definition Demuxers.hpp:56
DCA_INLINE int64_t collectNumber()
Collects a number from the data.
Definition Demuxers.hpp:193
jsonifier::string_view_base< uint8_t > data
Input data for demuxing.
Definition Demuxers.hpp:143
DCA_INLINE bool areWeDone()
Checks if the demuxing process is complete.
Definition Demuxers.hpp:138
uint64_t currentSize
Current size of the element being processed.
Definition Demuxers.hpp:148
DCA_INLINE bool findNextId(object_type value)
Finds the next occurrence of the specified value in the data.
Definition Demuxers.hpp:155
DCA_INLINE object_type reverseBytes()
Reverses the byte order of the current element being processed.
Definition Demuxers.hpp:172
DCA_INLINE bool collectFrame(audio_frame_data &frameNew)
Collects the next frame from the demuxer.
Definition Demuxers.hpp:70
DCA_INLINE matroska_demuxer()=default
Constructor for matroska_demuxer.
bool doWeHaveTotalSize
Flag indicating if total size has been determined.
Definition Demuxers.hpp:145
DCA_INLINE void writeData(jsonifier::string_view_base< uint8_t > dataNew)
Writes data to the Matroska demuxer.
Definition Demuxers.hpp:63
DCA_INLINE void parseOpusFrame()
Parses an Opus frame.
Definition Demuxers.hpp:208
DCA_INLINE int64_t collectElementSize()
Collects the size of the current element being processed.
Definition Demuxers.hpp:184
uint64_t currentPosition
Current position in the data.
Definition Demuxers.hpp:147
std::deque< audio_frame_data > frames
Queue to store collected frames.
Definition Demuxers.hpp:144
DCA_INLINE void proceedDemuxing()
Proceed with the demuxing process.
Definition Demuxers.hpp:81
bool areWeDoneVal
Flag indicating if demuxing is complete.
Definition Demuxers.hpp:146
A class for demuxing Ogg-contained audio data.
Definition Demuxers.hpp:289
DCA_INLINE void processPackets()
Processes Opus packets extracted from Ogg pages.
Definition Demuxers.hpp:375
std::deque< ogg_page > pages
Queue to store Ogg pages.
Definition Demuxers.hpp:359
DCA_INLINE bool collectFrame(audio_frame_data &frameNew)
Collects the next audio frame from the demuxer.
Definition Demuxers.hpp:296
std::deque< opus_packet > packets
Queue to store Opus packets.
Definition Demuxers.hpp:358
std::deque< audio_frame_data > frames
Queue to store collected audio frames.
Definition Demuxers.hpp:356
jsonifier::vector< uint8_t > data
Input data for demuxing.
Definition Demuxers.hpp:357
DCA_INLINE bool proceedDemuxing()
Proceeds with the demuxing process.
Definition Demuxers.hpp:346
DCA_INLINE bool processOggPage()
Processes an Ogg page for demuxing.
Definition Demuxers.hpp:363
DCA_INLINE void processPages()
Processes Ogg pages to extract Opus packets.
Definition Demuxers.hpp:388
DCA_INLINE void writeData(jsonifier::string_view inputData)
Writes data to the Ogg demuxer and processes it.
Definition Demuxers.hpp:308
A class representing an Ogg page for demuxing.
Definition Demuxers.hpp:222
jsonifier::vector< uint8_t > data
The data for the Ogg page.
Definition Demuxers.hpp:272
DCA_INLINE ogg_page(jsonifier::vector< uint8_t > &&newData)
Constructor for ogg_page.
Definition Demuxers.hpp:226
DCA_INLINE void verifyAsOggPage()
Verifies that the data represents a valid Ogg page.
Definition Demuxers.hpp:278
uint64_t totalPacketSize
Total size of Opus packets in the page.
Definition Demuxers.hpp:273
std::deque< uint64_t > segmentTable
Segment table storing Opus packet sizes.
Definition Demuxers.hpp:271
DCA_INLINE uint64_t getDataSize()
Returns the size of the Ogg page data.
Definition Demuxers.hpp:266
DCA_INLINE bool getOpusPacket(opus_packet &newPacket)
Retrieves the next Opus packet from the Ogg page.
Definition Demuxers.hpp:235
uint64_t segmentCount
Number of segments in the Ogg page.
Definition Demuxers.hpp:275
uint64_t currentPosition
Current position in the page data.
Definition Demuxers.hpp:274
DCA_INLINE void getSegmentData()
Parses the segment data of the Ogg page.
Definition Demuxers.hpp:249
static DCA_INLINE void printSuccess(const string_type &what, std::source_location where=std::source_location::current())
Print a success message of the specified type.
Definition Base.hpp:289
static DCA_INLINE void printError(const string_type &what, std::source_location where=std::source_location::current())
Print an error message of the specified type.
Definition Base.hpp:252
DCA_INLINE void reverseByteOrder(return_type &net)
Reverses the byte order of a value if needed, based on the endianness.
Definition Etf.hpp:60
The main namespace for the forward-facing interfaces.