diff options
| author | 2019-02-01 13:02:16 -0500 | |
|---|---|---|
| committer | 2019-02-01 13:02:16 -0500 | |
| commit | 4076d8fe3e3446fcdea55d60ed71e15737091b09 (patch) | |
| tree | 740323de3e316c88bad03e9492975e37b1a6d150 /src | |
| parent | Merge pull request #2067 from ReinUsesLisp/workaround-fb (diff) | |
| parent | hwopus: Implement DecodeInterleaved (diff) | |
| download | yuzu-4076d8fe3e3446fcdea55d60ed71e15737091b09.tar.gz yuzu-4076d8fe3e3446fcdea55d60ed71e15737091b09.tar.xz yuzu-4076d8fe3e3446fcdea55d60ed71e15737091b09.zip | |
Merge pull request #2073 from lioncash/opus
hwopus: Implement DecodeInterleaved (the newest variant)
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/audio/hwopus.cpp | 117 |
1 files changed, 75 insertions, 42 deletions
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index a850cadc8..11eba4a12 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #include <chrono> | 5 | #include <chrono> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <optional> | ||
| 9 | #include <vector> | 8 | #include <vector> |
| 10 | 9 | ||
| 11 | #include <opus.h> | 10 | #include <opus.h> |
| @@ -30,48 +29,66 @@ public: | |||
| 30 | u32 channel_count) | 29 | u32 channel_count) |
| 31 | : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)), | 30 | : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)), |
| 32 | sample_rate(sample_rate), channel_count(channel_count) { | 31 | sample_rate(sample_rate), channel_count(channel_count) { |
| 32 | // clang-format off | ||
| 33 | static const FunctionInfo functions[] = { | 33 | static const FunctionInfo functions[] = { |
| 34 | {0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, | 34 | {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, |
| 35 | {1, nullptr, "SetContext"}, | 35 | {1, nullptr, "SetContext"}, |
| 36 | {2, nullptr, "DecodeInterleavedForMultiStream"}, | 36 | {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, |
| 37 | {3, nullptr, "SetContextForMultiStream"}, | 37 | {3, nullptr, "SetContextForMultiStream"}, |
| 38 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance, | 38 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, |
| 39 | "DecodeInterleavedWithPerformance"}, | 39 | {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, |
| 40 | {5, nullptr, "Unknown5"}, | 40 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, |
| 41 | {6, nullptr, "Unknown6"}, | 41 | {7, nullptr, "DecodeInterleavedForMultiStream"}, |
| 42 | {7, nullptr, "Unknown7"}, | ||
| 43 | }; | 42 | }; |
| 43 | // clang-format on | ||
| 44 | |||
| 44 | RegisterHandlers(functions); | 45 | RegisterHandlers(functions); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | private: | 48 | private: |
| 48 | void DecodeInterleaved(Kernel::HLERequestContext& ctx) { | 49 | /// Describes extra behavior that may be asked of the decoding context. |
| 50 | enum class ExtraBehavior { | ||
| 51 | /// No extra behavior. | ||
| 52 | None, | ||
| 53 | |||
| 54 | /// Resets the decoder context back to a freshly initialized state. | ||
| 55 | ResetContext, | ||
| 56 | }; | ||
| 57 | |||
| 58 | void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { | ||
| 49 | LOG_DEBUG(Audio, "called"); | 59 | LOG_DEBUG(Audio, "called"); |
| 50 | 60 | ||
| 51 | u32 consumed = 0; | 61 | DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None); |
| 52 | u32 sample_count = 0; | ||
| 53 | std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); | ||
| 54 | if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) { | ||
| 55 | LOG_ERROR(Audio, "Failed to decode opus data"); | ||
| 56 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 57 | // TODO(ogniK): Use correct error code | ||
| 58 | rb.Push(ResultCode(-1)); | ||
| 59 | return; | ||
| 60 | } | ||
| 61 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 62 | rb.Push(RESULT_SUCCESS); | ||
| 63 | rb.Push<u32>(consumed); | ||
| 64 | rb.Push<u32>(sample_count); | ||
| 65 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); | ||
| 66 | } | 62 | } |
| 67 | 63 | ||
| 68 | void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) { | 64 | void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { |
| 65 | LOG_DEBUG(Audio, "called"); | ||
| 66 | |||
| 67 | u64 performance = 0; | ||
| 68 | DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None); | ||
| 69 | } | ||
| 70 | |||
| 71 | void DecodeInterleaved(Kernel::HLERequestContext& ctx) { | ||
| 69 | LOG_DEBUG(Audio, "called"); | 72 | LOG_DEBUG(Audio, "called"); |
| 70 | 73 | ||
| 74 | IPC::RequestParser rp{ctx}; | ||
| 75 | const auto extra_behavior = | ||
| 76 | rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None; | ||
| 77 | |||
| 78 | u64 performance = 0; | ||
| 79 | DecodeInterleavedHelper(ctx, &performance, extra_behavior); | ||
| 80 | } | ||
| 81 | |||
| 82 | void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance, | ||
| 83 | ExtraBehavior extra_behavior) { | ||
| 71 | u32 consumed = 0; | 84 | u32 consumed = 0; |
| 72 | u32 sample_count = 0; | 85 | u32 sample_count = 0; |
| 73 | u64 performance = 0; | ||
| 74 | std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); | 86 | std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); |
| 87 | |||
| 88 | if (extra_behavior == ExtraBehavior::ResetContext) { | ||
| 89 | ResetDecoderContext(); | ||
| 90 | } | ||
| 91 | |||
| 75 | if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, | 92 | if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, |
| 76 | performance)) { | 93 | performance)) { |
| 77 | LOG_ERROR(Audio, "Failed to decode opus data"); | 94 | LOG_ERROR(Audio, "Failed to decode opus data"); |
| @@ -80,25 +97,28 @@ private: | |||
| 80 | rb.Push(ResultCode(-1)); | 97 | rb.Push(ResultCode(-1)); |
| 81 | return; | 98 | return; |
| 82 | } | 99 | } |
| 83 | IPC::ResponseBuilder rb{ctx, 6}; | 100 | |
| 101 | const u32 param_size = performance != nullptr ? 6 : 4; | ||
| 102 | IPC::ResponseBuilder rb{ctx, param_size}; | ||
| 84 | rb.Push(RESULT_SUCCESS); | 103 | rb.Push(RESULT_SUCCESS); |
| 85 | rb.Push<u32>(consumed); | 104 | rb.Push<u32>(consumed); |
| 86 | rb.Push<u32>(sample_count); | 105 | rb.Push<u32>(sample_count); |
| 87 | rb.Push<u64>(performance); | 106 | if (performance) { |
| 107 | rb.Push<u64>(*performance); | ||
| 108 | } | ||
| 88 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); | 109 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); |
| 89 | } | 110 | } |
| 90 | 111 | ||
| 91 | bool Decoder_DecodeInterleaved( | 112 | bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, |
| 92 | u32& consumed, u32& sample_count, const std::vector<u8>& input, | 113 | std::vector<opus_int16>& output, u64* out_performance_time) { |
| 93 | std::vector<opus_int16>& output, | ||
| 94 | std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) { | ||
| 95 | const auto start_time = std::chrono::high_resolution_clock::now(); | 114 | const auto start_time = std::chrono::high_resolution_clock::now(); |
| 96 | std::size_t raw_output_sz = output.size() * sizeof(opus_int16); | 115 | const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); |
| 97 | if (sizeof(OpusHeader) > input.size()) { | 116 | if (sizeof(OpusHeader) > input.size()) { |
| 98 | LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", | 117 | LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", |
| 99 | sizeof(OpusHeader), input.size()); | 118 | sizeof(OpusHeader), input.size()); |
| 100 | return false; | 119 | return false; |
| 101 | } | 120 | } |
| 121 | |||
| 102 | OpusHeader hdr{}; | 122 | OpusHeader hdr{}; |
| 103 | std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); | 123 | std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); |
| 104 | if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { | 124 | if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { |
| @@ -106,8 +126,9 @@ private: | |||
| 106 | sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size()); | 126 | sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size()); |
| 107 | return false; | 127 | return false; |
| 108 | } | 128 | } |
| 109 | auto frame = input.data() + sizeof(OpusHeader); | 129 | |
| 110 | auto decoded_sample_count = opus_packet_get_nb_samples( | 130 | const auto frame = input.data() + sizeof(OpusHeader); |
| 131 | const auto decoded_sample_count = opus_packet_get_nb_samples( | ||
| 111 | frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), | 132 | frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), |
| 112 | static_cast<opus_int32>(sample_rate)); | 133 | static_cast<opus_int32>(sample_rate)); |
| 113 | if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { | 134 | if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { |
| @@ -117,8 +138,9 @@ private: | |||
| 117 | decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); | 138 | decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); |
| 118 | return false; | 139 | return false; |
| 119 | } | 140 | } |
| 141 | |||
| 120 | const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); | 142 | const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); |
| 121 | auto out_sample_count = | 143 | const auto out_sample_count = |
| 122 | opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0); | 144 | opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0); |
| 123 | if (out_sample_count < 0) { | 145 | if (out_sample_count < 0) { |
| 124 | LOG_ERROR(Audio, | 146 | LOG_ERROR(Audio, |
| @@ -127,16 +149,24 @@ private: | |||
| 127 | out_sample_count, frame_size, static_cast<u32>(hdr.sz)); | 149 | out_sample_count, frame_size, static_cast<u32>(hdr.sz)); |
| 128 | return false; | 150 | return false; |
| 129 | } | 151 | } |
| 152 | |||
| 130 | const auto end_time = std::chrono::high_resolution_clock::now() - start_time; | 153 | const auto end_time = std::chrono::high_resolution_clock::now() - start_time; |
| 131 | sample_count = out_sample_count; | 154 | sample_count = out_sample_count; |
| 132 | consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); | 155 | consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); |
| 133 | if (performance_time.has_value()) { | 156 | if (out_performance_time != nullptr) { |
| 134 | performance_time->get() = | 157 | *out_performance_time = |
| 135 | std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); | 158 | std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); |
| 136 | } | 159 | } |
| 160 | |||
| 137 | return true; | 161 | return true; |
| 138 | } | 162 | } |
| 139 | 163 | ||
| 164 | void ResetDecoderContext() { | ||
| 165 | ASSERT(decoder != nullptr); | ||
| 166 | |||
| 167 | opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); | ||
| 168 | } | ||
| 169 | |||
| 140 | struct OpusHeader { | 170 | struct OpusHeader { |
| 141 | u32_be sz; // Needs to be BE for some odd reason | 171 | u32_be sz; // Needs to be BE for some odd reason |
| 142 | INSERT_PADDING_WORDS(1); | 172 | INSERT_PADDING_WORDS(1); |
| @@ -157,6 +187,7 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 157 | IPC::RequestParser rp{ctx}; | 187 | IPC::RequestParser rp{ctx}; |
| 158 | const auto sample_rate = rp.Pop<u32>(); | 188 | const auto sample_rate = rp.Pop<u32>(); |
| 159 | const auto channel_count = rp.Pop<u32>(); | 189 | const auto channel_count = rp.Pop<u32>(); |
| 190 | |||
| 160 | LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); | 191 | LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); |
| 161 | 192 | ||
| 162 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 193 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || |
| @@ -174,9 +205,10 @@ void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 174 | 205 | ||
| 175 | void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { | 206 | void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { |
| 176 | IPC::RequestParser rp{ctx}; | 207 | IPC::RequestParser rp{ctx}; |
| 177 | auto sample_rate = rp.Pop<u32>(); | 208 | const auto sample_rate = rp.Pop<u32>(); |
| 178 | auto channel_count = rp.Pop<u32>(); | 209 | const auto channel_count = rp.Pop<u32>(); |
| 179 | auto buffer_sz = rp.Pop<u32>(); | 210 | const auto buffer_sz = rp.Pop<u32>(); |
| 211 | |||
| 180 | LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, | 212 | LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, |
| 181 | channel_count, buffer_sz); | 213 | channel_count, buffer_sz); |
| 182 | 214 | ||
| @@ -185,8 +217,9 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { | |||
| 185 | "Invalid sample rate"); | 217 | "Invalid sample rate"); |
| 186 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 218 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); |
| 187 | 219 | ||
| 188 | std::size_t worker_sz = WorkerBufferSize(channel_count); | 220 | const std::size_t worker_sz = WorkerBufferSize(channel_count); |
| 189 | ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); | 221 | ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); |
| 222 | |||
| 190 | std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ | 223 | std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ |
| 191 | static_cast<OpusDecoder*>(operator new(worker_sz))}; | 224 | static_cast<OpusDecoder*>(operator new(worker_sz))}; |
| 192 | if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { | 225 | if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { |