summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-03-10 17:32:39 -0400
committerGravatar GitHub2019-03-10 17:32:39 -0400
commit0aa824b12f61c7c25e909ef181642e6d24ddfd0f (patch)
tree5e847a05903c3567717cbf713b1ff2d12318c08d /src
parentMerge pull request #2193 from lioncash/global (diff)
parentservice/audio/hwopus: Move decoder state to its own class (diff)
downloadyuzu-0aa824b12f61c7c25e909ef181642e6d24ddfd0f.tar.gz
yuzu-0aa824b12f61c7c25e909ef181642e6d24ddfd0f.tar.xz
yuzu-0aa824b12f61c7c25e909ef181642e6d24ddfd0f.zip
Merge pull request #2207 from lioncash/hwopus
service/audio/hwopus: Move decoder state to its own class
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/audio/hwopus.cpp176
1 files changed, 107 insertions, 69 deletions
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 11eba4a12..377e12cfa 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -9,43 +9,32 @@
9 9
10#include <opus.h> 10#include <opus.h>
11 11
12#include "common/common_funcs.h" 12#include "common/assert.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/hle_ipc.h" 15#include "core/hle/kernel/hle_ipc.h"
16#include "core/hle/service/audio/hwopus.h" 16#include "core/hle/service/audio/hwopus.h"
17 17
18namespace Service::Audio { 18namespace Service::Audio {
19 19namespace {
20struct OpusDeleter { 20struct OpusDeleter {
21 void operator()(void* ptr) const { 21 void operator()(void* ptr) const {
22 operator delete(ptr); 22 operator delete(ptr);
23 } 23 }
24}; 24};
25 25
26class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 26using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>;
27public:
28 IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
29 u32 channel_count)
30 : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
31 sample_rate(sample_rate), channel_count(channel_count) {
32 // clang-format off
33 static const FunctionInfo functions[] = {
34 {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
35 {1, nullptr, "SetContext"},
36 {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
37 {3, nullptr, "SetContextForMultiStream"},
38 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
39 {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
40 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
41 {7, nullptr, "DecodeInterleavedForMultiStream"},
42 };
43 // clang-format on
44 27
45 RegisterHandlers(functions); 28struct OpusPacketHeader {
46 } 29 // Packet size in bytes.
30 u32_be size;
31 // Indicates the final range of the codec's entropy coder.
32 u32_be final_range;
33};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
47 35
48private: 36class OpusDecoderStateBase {
37public:
49 /// Describes extra behavior that may be asked of the decoding context. 38 /// Describes extra behavior that may be asked of the decoding context.
50 enum class ExtraBehavior { 39 enum class ExtraBehavior {
51 /// No extra behavior. 40 /// No extra behavior.
@@ -55,30 +44,36 @@ private:
55 ResetContext, 44 ResetContext,
56 }; 45 };
57 46
58 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { 47 enum class PerfTime {
59 LOG_DEBUG(Audio, "called"); 48 Disabled,
60 49 Enabled,
61 DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None); 50 };
62 }
63
64 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
65 LOG_DEBUG(Audio, "called");
66 51
67 u64 performance = 0; 52 virtual ~OpusDecoderStateBase() = default;
68 DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None);
69 }
70 53
71 void DecodeInterleaved(Kernel::HLERequestContext& ctx) { 54 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
72 LOG_DEBUG(Audio, "called"); 55 // perform the decoding, as well as any relevant extra behavior.
73 56 virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
74 IPC::RequestParser rp{ctx}; 57 ExtraBehavior extra_behavior) = 0;
75 const auto extra_behavior = 58};
76 rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None;
77 59
78 u64 performance = 0; 60// Represents the decoder state for a non-multistream decoder.
79 DecodeInterleavedHelper(ctx, &performance, extra_behavior); 61class OpusDecoderState final : public OpusDecoderStateBase {
62public:
63 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
64 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
65
66 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
67 ExtraBehavior extra_behavior) override {
68 if (perf_time == PerfTime::Disabled) {
69 DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
70 } else {
71 u64 performance = 0;
72 DecodeInterleavedHelper(ctx, &performance, extra_behavior);
73 }
80 } 74 }
81 75
76private:
82 void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance, 77 void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
83 ExtraBehavior extra_behavior) { 78 ExtraBehavior extra_behavior) {
84 u32 consumed = 0; 79 u32 consumed = 0;
@@ -89,8 +84,7 @@ private:
89 ResetDecoderContext(); 84 ResetDecoderContext();
90 } 85 }
91 86
92 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, 87 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) {
93 performance)) {
94 LOG_ERROR(Audio, "Failed to decode opus data"); 88 LOG_ERROR(Audio, "Failed to decode opus data");
95 IPC::ResponseBuilder rb{ctx, 2}; 89 IPC::ResponseBuilder rb{ctx, 2};
96 // TODO(ogniK): Use correct error code 90 // TODO(ogniK): Use correct error code
@@ -109,27 +103,27 @@ private:
109 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 103 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
110 } 104 }
111 105
112 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 106 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
113 std::vector<opus_int16>& output, u64* out_performance_time) { 107 std::vector<opus_int16>& output, u64* out_performance_time) const {
114 const auto start_time = std::chrono::high_resolution_clock::now(); 108 const auto start_time = std::chrono::high_resolution_clock::now();
115 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 109 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
116 if (sizeof(OpusHeader) > input.size()) { 110 if (sizeof(OpusPacketHeader) > input.size()) {
117 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", 111 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
118 sizeof(OpusHeader), input.size()); 112 sizeof(OpusPacketHeader), input.size());
119 return false; 113 return false;
120 } 114 }
121 115
122 OpusHeader hdr{}; 116 OpusPacketHeader hdr{};
123 std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); 117 std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader));
124 if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { 118 if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) {
125 LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", 119 LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
126 sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size()); 120 sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size());
127 return false; 121 return false;
128 } 122 }
129 123
130 const auto frame = input.data() + sizeof(OpusHeader); 124 const auto frame = input.data() + sizeof(OpusPacketHeader);
131 const auto decoded_sample_count = opus_packet_get_nb_samples( 125 const auto decoded_sample_count = opus_packet_get_nb_samples(
132 frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), 126 frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)),
133 static_cast<opus_int32>(sample_rate)); 127 static_cast<opus_int32>(sample_rate));
134 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { 128 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
135 LOG_ERROR( 129 LOG_ERROR(
@@ -141,18 +135,18 @@ private:
141 135
142 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); 136 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
143 const auto out_sample_count = 137 const auto out_sample_count =
144 opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0); 138 opus_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
145 if (out_sample_count < 0) { 139 if (out_sample_count < 0) {
146 LOG_ERROR(Audio, 140 LOG_ERROR(Audio,
147 "Incorrect sample count received from opus_decode, " 141 "Incorrect sample count received from opus_decode, "
148 "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", 142 "output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
149 out_sample_count, frame_size, static_cast<u32>(hdr.sz)); 143 out_sample_count, frame_size, static_cast<u32>(hdr.size));
150 return false; 144 return false;
151 } 145 }
152 146
153 const auto end_time = std::chrono::high_resolution_clock::now() - start_time; 147 const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
154 sample_count = out_sample_count; 148 sample_count = out_sample_count;
155 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); 149 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
156 if (out_performance_time != nullptr) { 150 if (out_performance_time != nullptr) {
157 *out_performance_time = 151 *out_performance_time =
158 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); 152 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
@@ -167,21 +161,66 @@ private:
167 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); 161 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
168 } 162 }
169 163
170 struct OpusHeader { 164 OpusDecoderPtr decoder;
171 u32_be sz; // Needs to be BE for some odd reason
172 INSERT_PADDING_WORDS(1);
173 };
174 static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
175
176 std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
177 u32 sample_rate; 165 u32 sample_rate;
178 u32 channel_count; 166 u32 channel_count;
179}; 167};
180 168
181static std::size_t WorkerBufferSize(u32 channel_count) { 169class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
170public:
171 explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state)
172 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
173 // clang-format off
174 static const FunctionInfo functions[] = {
175 {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
176 {1, nullptr, "SetContext"},
177 {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
178 {3, nullptr, "SetContextForMultiStream"},
179 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
180 {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
181 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
182 {7, nullptr, "DecodeInterleavedForMultiStream"},
183 };
184 // clang-format on
185
186 RegisterHandlers(functions);
187 }
188
189private:
190 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Audio, "called");
192
193 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled,
194 OpusDecoderStateBase::ExtraBehavior::None);
195 }
196
197 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
198 LOG_DEBUG(Audio, "called");
199
200 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
201 OpusDecoderStateBase::ExtraBehavior::None);
202 }
203
204 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
205 LOG_DEBUG(Audio, "called");
206
207 IPC::RequestParser rp{ctx};
208 const auto extra_behavior = rp.Pop<bool>()
209 ? OpusDecoderStateBase::ExtraBehavior::ResetContext
210 : OpusDecoderStateBase::ExtraBehavior::None;
211
212 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
213 extra_behavior);
214 }
215
216 std::unique_ptr<OpusDecoderStateBase> decoder_state;
217};
218
219std::size_t WorkerBufferSize(u32 channel_count) {
182 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 220 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
183 return opus_decoder_get_size(static_cast<int>(channel_count)); 221 return opus_decoder_get_size(static_cast<int>(channel_count));
184} 222}
223} // Anonymous namespace
185 224
186void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { 225void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
187 IPC::RequestParser rp{ctx}; 226 IPC::RequestParser rp{ctx};
@@ -220,8 +259,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
220 const std::size_t worker_sz = WorkerBufferSize(channel_count); 259 const std::size_t worker_sz = WorkerBufferSize(channel_count);
221 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); 260 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
222 261
223 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ 262 OpusDecoderPtr decoder{static_cast<OpusDecoder*>(operator new(worker_sz))};
224 static_cast<OpusDecoder*>(operator new(worker_sz))};
225 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 263 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
226 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); 264 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err);
227 IPC::ResponseBuilder rb{ctx, 2}; 265 IPC::ResponseBuilder rb{ctx, 2};
@@ -232,8 +270,8 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
232 270
233 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 271 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
234 rb.Push(RESULT_SUCCESS); 272 rb.Push(RESULT_SUCCESS);
235 rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate, 273 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
236 channel_count); 274 std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count));
237} 275}
238 276
239HwOpus::HwOpus() : ServiceFramework("hwopus") { 277HwOpus::HwOpus() : ServiceFramework("hwopus") {