hwopus.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <chrono>
  5. #include <cstring>
  6. #include <memory>
  7. #include <vector>
  8. #include <opus.h>
  9. #include <opus_multistream.h>
  10. #include "common/assert.h"
  11. #include "common/logging/log.h"
  12. #include "core/hle/ipc_helpers.h"
  13. #include "core/hle/kernel/hle_ipc.h"
  14. #include "core/hle/service/audio/hwopus.h"
  15. namespace Service::Audio {
  16. namespace {
  17. struct OpusDeleter {
  18. void operator()(OpusMSDecoder* ptr) const {
  19. opus_multistream_decoder_destroy(ptr);
  20. }
  21. };
  22. using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
  23. struct OpusPacketHeader {
  24. // Packet size in bytes.
  25. u32_be size;
  26. // Indicates the final range of the codec's entropy coder.
  27. u32_be final_range;
  28. };
  29. static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
  30. class OpusDecoderState {
  31. public:
  32. /// Describes extra behavior that may be asked of the decoding context.
  33. enum class ExtraBehavior {
  34. /// No extra behavior.
  35. None,
  36. /// Resets the decoder context back to a freshly initialized state.
  37. ResetContext,
  38. };
  39. enum class PerfTime {
  40. Disabled,
  41. Enabled,
  42. };
  43. explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
  44. : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
  45. // Decodes interleaved Opus packets. Optionally allows reporting time taken to
  46. // perform the decoding, as well as any relevant extra behavior.
  47. void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
  48. ExtraBehavior extra_behavior) {
  49. if (perf_time == PerfTime::Disabled) {
  50. DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
  51. } else {
  52. u64 performance = 0;
  53. DecodeInterleavedHelper(ctx, &performance, extra_behavior);
  54. }
  55. }
  56. private:
  57. void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
  58. ExtraBehavior extra_behavior) {
  59. u32 consumed = 0;
  60. u32 sample_count = 0;
  61. std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
  62. if (extra_behavior == ExtraBehavior::ResetContext) {
  63. ResetDecoderContext();
  64. }
  65. if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) {
  66. LOG_ERROR(Audio, "Failed to decode opus data");
  67. IPC::ResponseBuilder rb{ctx, 2};
  68. // TODO(ogniK): Use correct error code
  69. rb.Push(RESULT_UNKNOWN);
  70. return;
  71. }
  72. const u32 param_size = performance != nullptr ? 6 : 4;
  73. IPC::ResponseBuilder rb{ctx, param_size};
  74. rb.Push(RESULT_SUCCESS);
  75. rb.Push<u32>(consumed);
  76. rb.Push<u32>(sample_count);
  77. if (performance) {
  78. rb.Push<u64>(*performance);
  79. }
  80. ctx.WriteBuffer(samples);
  81. }
  82. bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
  83. std::vector<opus_int16>& output, u64* out_performance_time) const {
  84. const auto start_time = std::chrono::high_resolution_clock::now();
  85. const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
  86. if (sizeof(OpusPacketHeader) > input.size()) {
  87. LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
  88. sizeof(OpusPacketHeader), input.size());
  89. return false;
  90. }
  91. OpusPacketHeader hdr{};
  92. std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader));
  93. if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) {
  94. LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
  95. sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size());
  96. return false;
  97. }
  98. const auto frame = input.data() + sizeof(OpusPacketHeader);
  99. const auto decoded_sample_count = opus_packet_get_nb_samples(
  100. frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)),
  101. static_cast<opus_int32>(sample_rate));
  102. if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
  103. LOG_ERROR(
  104. Audio,
  105. "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}",
  106. decoded_sample_count * channel_count * sizeof(u16), raw_output_sz);
  107. return false;
  108. }
  109. const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
  110. const auto out_sample_count =
  111. opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
  112. if (out_sample_count < 0) {
  113. LOG_ERROR(Audio,
  114. "Incorrect sample count received from opus_decode, "
  115. "output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
  116. out_sample_count, frame_size, static_cast<u32>(hdr.size));
  117. return false;
  118. }
  119. const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
  120. sample_count = out_sample_count;
  121. consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
  122. if (out_performance_time != nullptr) {
  123. *out_performance_time =
  124. std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
  125. }
  126. return true;
  127. }
  128. void ResetDecoderContext() {
  129. ASSERT(decoder != nullptr);
  130. opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
  131. }
  132. OpusDecoderPtr decoder;
  133. u32 sample_rate;
  134. u32 channel_count;
  135. };
  136. class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
  137. public:
  138. explicit IHardwareOpusDecoderManager(Core::System& system_, OpusDecoderState decoder_state)
  139. : ServiceFramework{system_, "IHardwareOpusDecoderManager"}, decoder_state{
  140. std::move(decoder_state)} {
  141. // clang-format off
  142. static const FunctionInfo functions[] = {
  143. {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
  144. {1, nullptr, "SetContext"},
  145. {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
  146. {3, nullptr, "SetContextForMultiStream"},
  147. {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
  148. {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
  149. {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
  150. {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
  151. {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
  152. {9, nullptr, "DecodeInterleavedForMultiStream"},
  153. };
  154. // clang-format on
  155. RegisterHandlers(functions);
  156. }
  157. private:
  158. void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
  159. LOG_DEBUG(Audio, "called");
  160. decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
  161. OpusDecoderState::ExtraBehavior::None);
  162. }
  163. void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
  164. LOG_DEBUG(Audio, "called");
  165. decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
  166. OpusDecoderState::ExtraBehavior::None);
  167. }
  168. void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
  169. LOG_DEBUG(Audio, "called");
  170. IPC::RequestParser rp{ctx};
  171. const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
  172. : OpusDecoderState::ExtraBehavior::None;
  173. decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
  174. }
  175. OpusDecoderState decoder_state;
  176. };
  177. std::size_t WorkerBufferSize(u32 channel_count) {
  178. ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
  179. constexpr int num_streams = 1;
  180. const int num_stereo_streams = channel_count == 2 ? 1 : 0;
  181. return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
  182. }
  183. // Creates the mapping table that maps the input channels to the particular
  184. // output channels. In the stereo case, we map the left and right input channels
  185. // to the left and right output channels respectively.
  186. //
  187. // However, in the monophonic case, we only map the one available channel
  188. // to the sole output channel. We specify 255 for the would-be right channel
  189. // as this is a special value defined by Opus to indicate to the decoder to
  190. // ignore that channel.
  191. std::array<u8, 2> CreateMappingTable(u32 channel_count) {
  192. if (channel_count == 2) {
  193. return {{0, 1}};
  194. }
  195. return {{0, 255}};
  196. }
  197. } // Anonymous namespace
  198. void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
  199. IPC::RequestParser rp{ctx};
  200. const auto sample_rate = rp.Pop<u32>();
  201. const auto channel_count = rp.Pop<u32>();
  202. LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count);
  203. ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
  204. sample_rate == 12000 || sample_rate == 8000,
  205. "Invalid sample rate");
  206. ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
  207. const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
  208. LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz);
  209. IPC::ResponseBuilder rb{ctx, 3};
  210. rb.Push(RESULT_SUCCESS);
  211. rb.Push<u32>(worker_buffer_sz);
  212. }
  213. void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
  214. IPC::RequestParser rp{ctx};
  215. const auto sample_rate = rp.Pop<u32>();
  216. const auto channel_count = rp.Pop<u32>();
  217. const auto buffer_sz = rp.Pop<u32>();
  218. LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
  219. channel_count, buffer_sz);
  220. ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
  221. sample_rate == 12000 || sample_rate == 8000,
  222. "Invalid sample rate");
  223. ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
  224. const std::size_t worker_sz = WorkerBufferSize(channel_count);
  225. ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
  226. const int num_stereo_streams = channel_count == 2 ? 1 : 0;
  227. const auto mapping_table = CreateMappingTable(channel_count);
  228. int error = 0;
  229. OpusDecoderPtr decoder{
  230. opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
  231. num_stereo_streams, mapping_table.data(), &error)};
  232. if (error != OPUS_OK || decoder == nullptr) {
  233. LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
  234. IPC::ResponseBuilder rb{ctx, 2};
  235. // TODO(ogniK): Use correct error code
  236. rb.Push(RESULT_UNKNOWN);
  237. return;
  238. }
  239. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  240. rb.Push(RESULT_SUCCESS);
  241. rb.PushIpcInterface<IHardwareOpusDecoderManager>(
  242. system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
  243. }
  244. HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
  245. static const FunctionInfo functions[] = {
  246. {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"},
  247. {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
  248. {2, nullptr, "OpenOpusDecoderForMultiStream"},
  249. {3, nullptr, "GetWorkBufferSizeForMultiStream"},
  250. {4, nullptr, "OpenHardwareOpusDecoderEx"},
  251. {5, nullptr, "GetWorkBufferSizeEx"},
  252. {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"},
  253. {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"},
  254. };
  255. RegisterHandlers(functions);
  256. }
  257. HwOpus::~HwOpus() = default;
  258. } // namespace Service::Audio