DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
AudioDecoder.cpp
Go to the documentation of this file.
1/*
2 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
3
4 Copyright 2021, 2022 Chris M. (RealTimeChris)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
20*/
21/// AudioDecoder.cpp - Source file for the audio decoder class.
22/// Jul 29, 2021
23/// https://discordcoreapi.com
24/// \file AudioDecoder.cpp
25
28#include <libavcodec/avcodec.h>
29
30namespace DiscordCoreInternal {
31
32 void OpusDecoderWrapper::OpusDecoderDeleter::operator()(OpusDecoder* other) noexcept {
33 if (other) {
34 opus_decoder_destroy(other);
35 other = nullptr;
36 }
37 }
38
39 OpusDecoderWrapper::OpusDecoderWrapper() {
40 int32_t error{};
41 this->data.resize(23040);
42 this->ptr.reset(opus_decoder_create(48000, 2, &error));
43 if (error != OPUS_OK) {
44 throw DiscordCoreAPI::DCAException{ "Failed to create the Opus decoder, Reason: " + std::string{ opus_strerror(error) } };
45 }
46 }
47
48 std::basic_string_view<opus_int16> OpusDecoderWrapper::decodeData(std::basic_string_view<std::byte> dataToDecode) {
49 const int64_t sampleCount = opus_decode(this->ptr.get(), reinterpret_cast<const unsigned char*>(dataToDecode.data()),
50 static_cast<opus_int32>(dataToDecode.length() & 0x7FFFFFFF), data.data(), 5760, 0);
51 if (sampleCount > 0) {
52 return std::basic_string_view<opus_int16>{ this->data.data(), static_cast<size_t>(sampleCount * 2ull) };
53 } else {
54 throw DiscordCoreAPI::DCAException{ "Failed to decode a user's voice payload, Reason: " + std::string{ opus_strerror(sampleCount) } };
55 }
56 }
57
58 void AVFrameWrapper::AVFrameDeleter::operator()(AVFrame* other) {
59 if (other) {
60 av_frame_unref(other);
61 av_frame_free(&other);
62 }
63 }
64
65 AVFrameWrapper& AVFrameWrapper::operator=(AVFrame* other) {
66 this->ptr.reset(nullptr);
67 this->ptr.reset(other);
68 return *this;
69 }
70
71 AVFrameWrapper::AVFrameWrapper(AVFrame* other) {
72 *this = other;
73 }
74
75 AVFrame* AVFrameWrapper::operator->() {
76 return this->ptr.get();
77 }
78
79 AVFrameWrapper::operator AVFrame*() {
80 return this->ptr.get();
81 }
82
83 void AVCodecContextWrapper::AVCodecContextDeleter::operator()(AVCodecContext* other) {
84 if (other) {
85 avcodec_free_context(&other);
86 }
87 }
88
89 AVCodecContextWrapper& AVCodecContextWrapper::operator=(AVCodecContext* other) {
90 this->ptr.reset(nullptr);
91 this->ptr.reset(other);
92 return *this;
93 }
94
95 AVCodecContextWrapper::AVCodecContextWrapper(AVCodecContext* other) {
96 *this = other;
97 }
98
99 AVCodecContext* AVCodecContextWrapper::operator->() {
100 return this->ptr.get();
101 }
102
103 AVCodecContextWrapper::operator AVCodecContext*() {
104 return this->ptr.get();
105 }
106
107 void AVFormatContextWrapper::AVFormatContextDeleter::operator()(AVFormatContextWrapper01* other) {
108 if (other->didItInitialize) {
109 avformat_free_context(other->theContext);
110 }
111 }
112
113 AVFormatContextWrapper& AVFormatContextWrapper::operator=(AVFormatContext* other) {
114 this->ptr->theContext = other;
115 return *this;
116 }
117
118 AVFormatContextWrapper::AVFormatContextWrapper(AVFormatContext* other) {
119 *this = other;
120 }
121
122 bool* AVFormatContextWrapper::getBoolPtr() {
123 return &this->ptr.get()->didItInitialize;
124 }
125
126 AVFormatContext* AVFormatContextWrapper::operator->() {
127 return this->ptr.get()->theContext;
128 }
129
130 AVFormatContext** AVFormatContextWrapper::operator*() {
131 return &this->ptr.get()->theContext;
132 }
133
134 AVFormatContextWrapper::operator AVFormatContext*() {
135 return this->ptr.get()->theContext;
136 }
137
138 void SwrContextWrapper::SwrContextDeleter::operator()(SwrContext* other) {
139 if (other) {
140 swr_free(&other);
141 }
142 }
143
144 SwrContextWrapper& SwrContextWrapper::operator=(SwrContext* other) {
145 this->ptr.reset(nullptr);
146 this->ptr.reset(other);
147 return *this;
148 }
149
150 SwrContextWrapper::SwrContextWrapper(SwrContext* other) {
151 *this = other;
152 }
153
154 SwrContextWrapper::operator SwrContext*() {
155 return this->ptr.get();
156 }
157
158 void AVIOContextWrapper::AVIOContextDeleter::operator()(AVIOContext* other) {
159 if (other) {
160 if (other->buffer) {
161 av_freep(&other->buffer);
162 }
163 avio_context_free(&other);
164 }
165 }
166
167 AVIOContextWrapper& AVIOContextWrapper::operator=(AVIOContext* other) {
168 this->ptr.reset(nullptr);
169 this->ptr.reset(other);
170 return *this;
171 }
172
173 AVIOContextWrapper::AVIOContextWrapper(AVIOContext* other) {
174 *this = other;
175 }
176
177 AVIOContext* AVIOContextWrapper::operator->() {
178 return this->ptr.get();
179 }
180
181 AVIOContextWrapper::operator AVIOContext*() {
182 return this->ptr.get();
183 }
184
185 void AVPacketWrapper::AVPacketDeleter::operator()(AVPacket* other) {
186 if (other) {
187 av_packet_free(&other);
188 }
189 }
190
191 AVPacketWrapper& AVPacketWrapper::operator=(AVPacket* other) {
192 this->ptr.reset(nullptr);
193 this->ptr.reset(other);
194 return *this;
195 }
196
197 AVPacketWrapper::AVPacketWrapper(AVPacket* other) {
198 *this = other;
199 }
200
201 AVPacket* AVPacketWrapper::operator->() {
202 return this->ptr.get();
203 }
204
205 AVPacketWrapper::operator AVPacket*() {
206 return this->ptr.get();
207 }
208
209 AudioDecoder::AudioDecoder(const BuildAudioDecoderData& dataPackage) {
210 this->configManager = dataPackage.configManager;
211 this->bufferMaxSize = dataPackage.bufferMaxSize;
212 this->totalFileSize = dataPackage.totalFileSize;
213 }
214
215 bool AudioDecoder::getFrame(DiscordCoreAPI::AudioFrameData& dataPackage) {
216 if (!this->areWeQuitting.load()) {
217 if (this->outDataBuffer.tryReceive(dataPackage)) {
218 if (dataPackage.currentSize != -5) {
219 return true;
220 }
221 }
222 }
223 return false;
224 }
225
226 void AudioDecoder::submitDataForDecoding(std::string dataToDecode) {
227 this->inputDataBuffer.send(std::move(dataToDecode));
228 }
229
230 bool AudioDecoder::haveWeFailed() {
231 return this->haveWeFailedBool.load();
232 }
233
234 void AudioDecoder::startMe() {
235 this->taskThread = std::make_unique<std::jthread>([=, this](std::stop_token token) {
236 this->run(token);
237 });
238 };
239
240 int32_t AudioDecoder::ReadBufferData(void* opaque, uint8_t* buf, int32_t) {
241 AudioDecoder* stream = static_cast<AudioDecoder*>(opaque);
242 stream->bytesRead = 0;
243 stream->currentBuffer = std::string();
245 if (stream->areWeQuitting.load()) {
246 frameData.currentSize = -5;
247 stream->outDataBuffer.send(std::move(frameData));
248 return AVERROR_EOF;
249 }
250 if (DiscordCoreAPI::waitForTimeToPass(stream->inputDataBuffer, stream->currentBuffer, stream->refreshTimeForBuffer.load())) {
251 frameData.currentSize = -5;
252 stream->outDataBuffer.send(std::move(frameData));
253 stream->areWeQuitting.store(true);
254 return AVERROR_EOF;
255 }
256 if (stream->currentBuffer.size() > 0) {
257 stream->bytesRead = stream->currentBuffer.size();
258 }
259 for (int32_t x = 0; x < stream->bytesRead; ++x) {
260 buf[x] = stream->currentBuffer[x];
261 }
262 if (stream->ioContext->buf_ptr - stream->ioContext->buffer >= stream->totalFileSize) {
263 frameData.currentSize = stream->bytesRead;
264 stream->outDataBuffer.send(std::move(frameData));
265 stream->areWeQuitting.store(true);
266 if (stream->bytesRead == 0) {
267 return AVERROR_EOF;
268 }
269 return static_cast<int32_t>(stream->bytesRead);
270 }
271 if (stream->bytesRead == 0) {
272 return AVERROR_EOF;
273 }
274 return static_cast<int32_t>(stream->bytesRead);
275 }
276
277 void AudioDecoder::run(std::stop_token token) {
278 if (!this->haveWeBooted) {
279 auto buffer = static_cast<uint8_t*>(av_malloc(this->bufferMaxSize));
280 if (!buffer) {
281 this->haveWeFailedBool.store(true);
282 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
283 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Failed to allocate filestreambuffer."
284 << DiscordCoreAPI::reset() << endl
285 << endl;
286 }
287 return;
288 }
289
290 this->ioContext = avio_alloc_context(buffer, static_cast<int32_t>(this->bufferMaxSize - 1), 0, this, &AudioDecoder::ReadBufferData, 0, 0);
291
292 if (!this->ioContext) {
293 this->haveWeFailedBool.store(true);
294 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
295 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Failed to allocate AVIOContext."
296 << DiscordCoreAPI::reset() << endl
297 << endl;
298 }
299 return;
300 }
301
302 this->formatContext = avformat_alloc_context();
303
304 if (!this->formatContext) {
305 this->haveWeFailedBool.store(true);
306 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
307 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Could not allocate the format context."
308 << DiscordCoreAPI::reset() << endl
309 << endl;
310 }
311 return;
312 }
313
314 this->formatContext->pb = this->ioContext;
315 this->formatContext->flags |= AVFMT_FLAG_CUSTOM_IO;
316 if (avformat_open_input(*this->formatContext, "memory", nullptr, nullptr) < 0) {
317 this->haveWeFailedBool.store(true);
318 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
319 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Failed to open the AVFormatContext."
320 << DiscordCoreAPI::reset() << endl
321 << endl;
322 }
323 return;
324 }
325 *this->formatContext.getBoolPtr() = true;
326 AVMediaType type = AVMediaType::AVMEDIA_TYPE_AUDIO;
327 int32_t ret = av_find_best_stream(this->formatContext, type, -1, -1, NULL, 0);
328 if (ret < 0) {
329 std::string newString = "AudioDecoder::run() Error: Could not find ";
330 newString += av_get_media_type_string(type);
331 newString += " stream in input memory stream.";
332 this->haveWeFailedBool.store(true);
333 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
334 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
335 }
336 return;
337 } else {
338 this->audioStreamIndex = ret;
339 this->audioStream = this->formatContext->streams[this->audioStreamIndex];
340 if (!this->audioStream) {
341 this->haveWeFailedBool.store(true);
342 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
343 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Could not find an audio stream."
344 << DiscordCoreAPI::reset() << endl
345 << endl;
346 }
347 return;
348 }
349
350 if (avformat_find_stream_info(this->formatContext, NULL) < 0) {
351 this->haveWeFailedBool.store(true);
352 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
353 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Could not find stream information."
354 << DiscordCoreAPI::reset() << endl
355 << endl;
356 }
357 return;
358 }
359
360 this->codec = const_cast<AVCodec*>(avcodec_find_decoder(this->audioStream->codecpar->codec_id));
361 if (!this->codec) {
362 std::string newString = "AudioDecoder::run() Error: Failed to find ";
363 newString += av_get_media_type_string(type);
364 newString += " decoder.";
365 this->haveWeFailedBool.store(true);
366 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
367 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
368 }
369 return;
370 }
371
372 this->audioDecodeContext = avcodec_alloc_context3(this->codec);
373 if (!this->audioDecodeContext) {
374 std::string newString = "AudioDecoder::run() Error: Failed to allocate the ";
375 newString += av_get_media_type_string(type);
376 newString += " AVCodecContext.";
377 this->haveWeFailedBool.store(true);
378 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
379 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
380 }
381 return;
382 }
383
384 if (avcodec_parameters_to_context(this->audioDecodeContext, this->audioStream->codecpar) < 0) {
385 std::string newString = "AudioDecoder::run() Error: Failed to copy ";
386 newString += av_get_media_type_string(type);
387 newString += " codec parameters to decoder context.";
388 this->haveWeFailedBool.store(true);
389 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
390 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
391 }
392 return;
393 }
394
395 if (avcodec_open2(this->audioDecodeContext, this->codec, NULL) < 0) {
396 std::string newString = "AudioDecoder::run() Error: Failed to open ";
397 newString += av_get_media_type_string(type);
398 newString += " AVCodecContext.";
399 this->haveWeFailedBool.store(true);
400 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
401 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
402 }
403 return;
404 }
405
406 this->swrContext = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AVSampleFormat::AV_SAMPLE_FMT_S16, 48000, AV_CH_LAYOUT_STEREO,
407 this->audioDecodeContext->sample_fmt, this->audioDecodeContext->sample_rate, 0, nullptr);
408 swr_init(this->swrContext);
409 if (this->configManager->doWePrintFFMPEGSuccessMessages()) {
410 av_dump_format(this->formatContext, 0, "memory", 0);
411 }
412 }
413
414 this->haveWeBooted = true;
415 }
416
417 if (this->currentBuffer.size() > 0) {
418 this->packet = av_packet_alloc();
419 if (!this->packet) {
420 this->haveWeFailedBool.store(true);
421 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
422 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Could not allocate packet" << DiscordCoreAPI::reset()
423 << endl
424 << endl;
425 }
426 return;
427 }
428
429 this->frame = av_frame_alloc();
430 if (!this->frame) {
431 this->haveWeFailedBool.store(true);
432 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
433 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Could not allocate frame" << DiscordCoreAPI::reset()
434 << endl
435 << endl;
436 }
437 return;
438 }
439
440 this->newFrame = av_frame_alloc();
441 if (!this->newFrame) {
442 this->haveWeFailedBool.store(true);
443 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
444 cout << DiscordCoreAPI::shiftToBrightRed() << "AudioDecoder::run() Error: Could not allocate new-frame" << DiscordCoreAPI::reset()
445 << endl
446 << endl;
447 }
448 return;
449 }
450
451 while (!token.stop_requested() && !this->areWeQuitting.load() && av_read_frame(this->formatContext, this->packet) == 0) {
452 if (this->packet->stream_index == this->audioStreamIndex) {
453 int32_t returnValue = avcodec_send_packet(this->audioDecodeContext, this->packet);
454 if (returnValue < 0) {
455 char charString[32];
456 av_strerror(returnValue, charString, 32);
457 std::string newString = "AudioDecoder::run() Error: Issue submitting a packet for decoding (" + std::to_string(returnValue) +
458 "), " + charString + ".";
459 this->haveWeFailedBool.store(true);
460 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
461 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
462 }
463 return;
464 } else {
465 returnValue = avcodec_receive_frame(this->audioDecodeContext, this->frame);
466 if (returnValue < 0) {
467 std::string newString = "AudioDecoder::run() Error: Issue during decoding (" + std::to_string(returnValue) + ")";
468 this->haveWeFailedBool.store(true);
469 if (this->configManager->doWePrintFFMPEGErrorMessages()) {
470 cout << DiscordCoreAPI::shiftToBrightRed() << newString << DiscordCoreAPI::reset() << endl << endl;
471 }
472 return;
473 }
474
475 if (!swr_is_initialized(this->swrContext)) {
476 swr_init(this->swrContext);
477 }
478 this->newFrame->channel_layout = AV_CH_LAYOUT_STEREO;
479 this->newFrame->sample_rate = 48000;
480 this->newFrame->format = AVSampleFormat::AV_SAMPLE_FMT_S16;
481 this->newFrame->nb_samples = frame->nb_samples;
482 this->newFrame->pts = frame->pts;
483 swr_convert_frame(this->swrContext, this->newFrame, this->frame);
484 int32_t unpadded_linesize =
485 this->newFrame->nb_samples * av_get_bytes_per_sample(static_cast<AVSampleFormat>(this->newFrame->format)) * 2;
487 rawFrame.type = DiscordCoreAPI ::AudioFrameType::RawPCM;
488 rawFrame.data.resize(unpadded_linesize);
489 for (int32_t x = 0; x < unpadded_linesize; ++x) {
490 rawFrame.data[x] = static_cast<std::byte>(this->newFrame->extended_data[0][x]);
491 }
492 rawFrame.currentSize = static_cast<int64_t>(newFrame->nb_samples * 4);
493 this->outDataBuffer.send(std::move(rawFrame));
494 int64_t sampleCount = swr_get_delay(this->swrContext, this->newFrame->sample_rate);
495 if (sampleCount > 0) {
496 if (!swr_is_initialized(this->swrContext)) {
497 swr_init(this->swrContext);
498 }
499 swr_convert_frame(this->swrContext, this->newFrame, nullptr);
501 rawFrame02.type = DiscordCoreAPI ::AudioFrameType::RawPCM;
502 rawFrame02.data.resize(*this->newFrame->linesize);
503 for (int32_t x = 0; x < *this->newFrame->linesize; ++x) {
504 rawFrame02.data[x] = static_cast<std::byte>(this->newFrame->extended_data[0][x]);
505 }
506 rawFrame02.currentSize = static_cast<int64_t>(newFrame->nb_samples * 4);
507 this->outDataBuffer.send(std::move(rawFrame02));
508 }
509 }
510 } else {
511 break;
512 }
513 this->frame = av_frame_alloc();
514 this->newFrame = av_frame_alloc();
515 this->packet = av_packet_alloc();
516 if (token.stop_requested() || this->areWeQuitting.load()) {
517 break;
518 }
519 }
520 if (this->configManager->doWePrintFFMPEGSuccessMessages()) {
521 cout << DiscordCoreAPI::shiftToBrightGreen() << "Completed decoding!" << endl << DiscordCoreAPI::reset() << endl << endl;
522 }
523 }
524 return;
525 }
526
527 void AudioDecoder::cancelMe() {
528 this->refreshTimeForBuffer.store(10);
529 this->inputDataBuffer.clearContents();
530 this->inputDataBuffer.send(std::string());
531 this->inputDataBuffer.send(std::string());
532 this->inputDataBuffer.send(std::string());
533 this->inputDataBuffer.send(std::string());
534 this->inputDataBuffer.send(std::string());
535 this->areWeQuitting.store(true);
536 }
537
538 AudioDecoder::~AudioDecoder() {
539 this->cancelMe();
540 }
541
542};
Represents a single frame of audio data.
Definition: Utilities.hpp:1284
AudioFrameType type
The type of audio frame.
Definition: Utilities.hpp:1285
int64_t currentSize
The current size of the allocated memory.
Definition: Utilities.hpp:1288