Audio.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #include "mumlib/Audio.hpp"
  2. #include <boost/format.hpp>
  3. static boost::posix_time::seconds RESET_SEQUENCE_NUMBER_INTERVAL(5);
  4. mumlib::Audio::Audio(int opusSampleRate, int opusEncoderBitrate)
  5. : logger(log4cpp::Category::getInstance("mumlib.Audio")),
  6. opusDecoder(nullptr),
  7. opusEncoder(nullptr),
  8. outgoingSequenceNumber(0) {
  9. int error;
  10. this->sampleRate = opusSampleRate;
  11. opusDecoder = opus_decoder_create(opusSampleRate, 1, &error);
  12. if (error != OPUS_OK) {
  13. throw AudioException((boost::format("failed to initialize OPUS decoder: %s") % opus_strerror(error)).str());
  14. }
  15. opusEncoder = opus_encoder_create(opusSampleRate, 1, OPUS_APPLICATION_VOIP, &error);
  16. if (error != OPUS_OK) {
  17. throw AudioException((boost::format("failed to initialize OPUS encoder: %s") % opus_strerror(error)).str());
  18. }
  19. opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0));
  20. setOpusEncoderBitrate(opusEncoderBitrate);
  21. resetEncoder();
  22. }
  23. mumlib::Audio::~Audio() {
  24. if (opusDecoder) {
  25. opus_decoder_destroy(opusDecoder);
  26. }
  27. if (opusEncoder) {
  28. opus_encoder_destroy(opusEncoder);
  29. }
  30. }
  31. void mumlib::Audio::setOpusEncoderBitrate(int bitrate) {
  32. int error = opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(bitrate));
  33. if (error != OPUS_OK) {
  34. throw AudioException((boost::format("failed to initialize transmission bitrate to %d B/s: %s")
  35. % bitrate % opus_strerror(error)).str());
  36. }
  37. }
  38. int mumlib::Audio::getOpusEncoderBitrate() {
  39. opus_int32 bitrate;
  40. int error = opus_encoder_ctl(opusEncoder, OPUS_GET_BITRATE(&bitrate));
  41. if (error != OPUS_OK) {
  42. throw AudioException((boost::format("failed to read Opus bitrate: %s") % opus_strerror(error)).str());
  43. }
  44. return bitrate;
  45. }
  46. std::pair<int, bool> mumlib::Audio::decodeOpusPayload(uint8_t *inputBuffer,
  47. int inputLength,
  48. int16_t *pcmBuffer,
  49. int pcmBufferSize) {
  50. int64_t opusDataLength;
  51. int dataPointer = 0;
  52. VarInt varInt(inputBuffer);
  53. opusDataLength = varInt.getValue();
  54. dataPointer += varInt.getEncoded().size();
  55. bool lastPacket = (opusDataLength & 0x2000) != 0;
  56. opusDataLength = opusDataLength & 0x1fff;
  57. if (inputLength < opusDataLength + dataPointer) {
  58. throw AudioException((boost::format("invalid Opus payload (%d B): header %d B, expected Opus data length %d B")
  59. % inputLength % dataPointer % opusDataLength).str());
  60. }
  61. int outputSize = opus_decode(opusDecoder,
  62. reinterpret_cast<const unsigned char *>(&inputBuffer[dataPointer]),
  63. opusDataLength,
  64. pcmBuffer,
  65. pcmBufferSize,
  66. 0);
  67. if (outputSize <= 0) {
  68. throw AudioException((boost::format("failed to decode %d B of OPUS data: %s") % inputLength %
  69. opus_strerror(outputSize)).str());
  70. }
  71. logger.debug("%d B of Opus data decoded to %d PCM samples, last packet: %d.",
  72. opusDataLength, outputSize, lastPacket);
  73. return std::make_pair(outputSize, lastPacket);
  74. }
  75. int mumlib::Audio::encodeAudioPacket(int target, int16_t *inputPcmBuffer, int inputLength, uint8_t *outputBuffer,
  76. int outputBufferSize) {
  77. using namespace std::chrono;
  78. const int lastAudioPacketSentInterval = duration_cast<milliseconds>(
  79. system_clock::now() - lastEncodedAudioPacketTimestamp).count();
  80. if (lastAudioPacketSentInterval > RESET_SEQUENCE_NUMBER_INTERVAL.total_milliseconds() + 1000) {
  81. logger.debug("Last audio packet was sent %d ms ago, resetting encoder.", lastAudioPacketSentInterval);
  82. resetEncoder();
  83. }
  84. std::vector<uint8_t> header;
  85. header.push_back(static_cast<unsigned char &&>(0x80 | target));
  86. auto sequenceNumberEnc = VarInt(outgoingSequenceNumber).getEncoded();
  87. header.insert(header.end(), sequenceNumberEnc.begin(), sequenceNumberEnc.end());
  88. uint8_t tmpOpusBuffer[1024];
  89. const int outputSize = opus_encode(opusEncoder,
  90. inputPcmBuffer,
  91. inputLength,
  92. tmpOpusBuffer,
  93. min(outputBufferSize, 1024)
  94. );
  95. if (outputSize <= 0) {
  96. throw AudioException((boost::format("failed to encode %d B of PCM data: %s") % inputLength %
  97. opus_strerror(outputSize)).str());
  98. }
  99. auto outputSizeEnc = VarInt(outputSize).getEncoded();
  100. header.insert(header.end(), outputSizeEnc.begin(), outputSizeEnc.end());
  101. memcpy(outputBuffer, &header[0], header.size());
  102. memcpy(outputBuffer + header.size(), tmpOpusBuffer, (size_t) outputSize);
  103. int incrementNumber = 100 * inputLength / this->sampleRate;
  104. outgoingSequenceNumber += incrementNumber;
  105. lastEncodedAudioPacketTimestamp = std::chrono::system_clock::now();
  106. return static_cast<int>(outputSize + header.size());
  107. }
  108. void mumlib::Audio::resetEncoder() {
  109. int status = opus_encoder_ctl(opusEncoder, OPUS_RESET_STATE, nullptr);
  110. if (status != OPUS_OK) {
  111. throw AudioException((boost::format("failed to reset encoder: %s") % opus_strerror(status)).str());
  112. }
  113. outgoingSequenceNumber = 0;
  114. }
  115. mumlib::IncomingAudioPacket mumlib::Audio::decodeIncomingAudioPacket(uint8_t *inputBuffer, int inputBufferLength) {
  116. mumlib::IncomingAudioPacket incomingAudioPacket{};
  117. incomingAudioPacket.type = static_cast<AudioPacketType >((inputBuffer[0] & 0xE0) >> 5);
  118. incomingAudioPacket.target = inputBuffer[0] & 0x1F;
  119. std::array<int64_t *, 2> varInts = {&incomingAudioPacket.sessionId, &incomingAudioPacket.sequenceNumber};
  120. int dataPointer = 1;
  121. for (int64_t *val : varInts) {
  122. VarInt varInt(&inputBuffer[dataPointer]);
  123. *val = varInt.getValue();
  124. dataPointer += varInt.getEncoded().size();
  125. }
  126. incomingAudioPacket.audioPayload = &inputBuffer[dataPointer];
  127. incomingAudioPacket.audioPayloadLength = inputBufferLength - dataPointer;
  128. if (dataPointer >= inputBufferLength) {
  129. throw AudioException((boost::format("invalid incoming audio packet (%d B): header %d B") % inputBufferLength %
  130. dataPointer).str());
  131. }
  132. logger.debug(
  133. "Received %d B of audio packet, %d B header, %d B payload (target: %d, sessionID: %ld, seq num: %ld).",
  134. inputBufferLength,
  135. dataPointer,
  136. incomingAudioPacket.audioPayloadLength,
  137. incomingAudioPacket.target,
  138. incomingAudioPacket.sessionId,
  139. incomingAudioPacket.sequenceNumber);
  140. return incomingAudioPacket;
  141. }