1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <config.h>
17 #include <pulsecore/log.h>
18 #include <pulsecore/modargs.h>
19 #include <pulsecore/module.h>
20 #include <pulsecore/sink.h>
21 #include <stddef.h>
22 #include <stdbool.h>
23
24 #include <pulsecore/core-util.h>
25 #include <pulsecore/core.h>
26 #include <pulsecore/namereg.h>
27 #include "audio_effect_chain_adapter.h"
28 #include "audio_hdi_log.h"
29 #include "playback_capturer_adapter.h"
30
31 pa_sink *PaHdiSinkNew(pa_module *m, pa_modargs *ma, const char *driver);
32 void PaHdiSinkFree(pa_sink *s);
33 void PaInputVolumeChangeCb(pa_sink_input *i);
34
35 PA_MODULE_AUTHOR("OpenHarmony");
36 PA_MODULE_DESCRIPTION("OpenHarmony HDI Sink");
37 PA_MODULE_VERSION(PACKAGE_VERSION);
38 PA_MODULE_LOAD_ONCE(false);
39 PA_MODULE_USAGE(
40 "sink_name=<name for the sink> "
41 "device_class=<name for the device class> "
42 "sink_properties=<properties for the sink> "
43 "format=<sample format> "
44 "rate=<sample rate> "
45 "channels=<number of channels> "
46 "channel_map=<channel map> "
47 "buffer_size=<custom buffer size>"
48 "file_path=<file path for data writing>"
49 "adapter_name=<primary>"
50 "fixed_latency=<latency measure>"
51 "sink_latency=<hdi latency>"
52 "render_in_idle_state<renderer state>"
53 "open_mic_speaker<open mic and speaker>"
54 "test_mode_on<is test mode on>"
55 "network_id<device network id>"
56 "device_type<device type or port>"
57 "offload_enable<if device support offload>"
58 );
59
60 static const char * const VALID_MODARGS[] = {
61 "sink_name",
62 "device_class",
63 "sink_properties",
64 "format",
65 "rate",
66 "channels",
67 "channel_map",
68 "buffer_size",
69 "file_path",
70 "adapter_name",
71 "fixed_latency",
72 "sink_latency",
73 "render_in_idle_state",
74 "open_mic_speaker",
75 "test_mode_on",
76 "network_id",
77 "device_type",
78 "offload_enable",
79 NULL
80 };
81
SinkInputNewCb(pa_core * c,pa_sink_input * si)82 static pa_hook_result_t SinkInputNewCb(pa_core *c, pa_sink_input *si)
83 {
84 pa_assert(c);
85
86 const char *flush = pa_proplist_gets(si->proplist, "stream.flush");
87 const char *sceneMode = pa_proplist_gets(si->proplist, "scene.mode");
88 const char *sceneType = pa_proplist_gets(si->proplist, "scene.type");
89 const char *deviceString = pa_proplist_gets(si->sink->proplist, PA_PROP_DEVICE_STRING);
90 const char *sessionID = pa_proplist_gets(si->proplist, "stream.sessionID");
91 const uint32_t channels = si->sample_spec.channels;
92 const char *channelLayout = pa_proplist_gets(si->proplist, "stream.channelLayout");
93 const char *spatializationEnabled = pa_proplist_gets(si->proplist, "spatialization.enabled");
94 const char *streamUsage = pa_proplist_gets(si->proplist, "stream.usage");
95 if (pa_safe_streq(deviceString, "remote")) {
96 EffectChainManagerReleaseCb(sceneType, sessionID);
97 return PA_HOOK_OK;
98 }
99
100 const char *appUser = pa_proplist_gets(si->proplist, "application.process.user");
101 if (pa_safe_streq(appUser, "daudio")) {
102 return PA_HOOK_OK;
103 }
104
105 const char *clientUid = pa_proplist_gets(si->proplist, "stream.client.uid");
106 const char *bootUpMusic = "1003";
107 if (!pa_safe_streq(clientUid, bootUpMusic)) {
108 if (!pa_safe_streq(sceneMode, "EFFECT_NONE") && pa_safe_streq(flush, "true")) {
109 EffectChainManagerInitCb(sceneType);
110 }
111 EffectChainManagerCreateCb(sceneType, sessionID);
112 SessionInfoPack pack = {channels, channelLayout, sceneMode, spatializationEnabled, streamUsage};
113 if (si->thread_info.state == PA_SINK_INPUT_RUNNING &&
114 !EffectChainManagerAddSessionInfo(sceneType, sessionID, pack)) {
115 EffectChainManagerMultichannelUpdate(sceneType);
116 EffectChainManagerEffectUpdate();
117 EffectChainManagerStreamUsageUpdate();
118 }
119 }
120 return PA_HOOK_OK;
121 }
122
SinkInputUnlinkCb(pa_core * c,pa_sink_input * si,void * u)123 static pa_hook_result_t SinkInputUnlinkCb(pa_core *c, pa_sink_input *si, void *u)
124 {
125 pa_assert(c);
126
127 const char *sceneType = pa_proplist_gets(si->proplist, "scene.type");
128 const char *deviceString = pa_proplist_gets(si->sink->proplist, PA_PROP_DEVICE_STRING);
129 if (pa_safe_streq(deviceString, "remote")) {
130 return PA_HOOK_OK;
131 }
132
133 const char *appUser = pa_proplist_gets(si->proplist, "application.process.user");
134 if (pa_safe_streq(appUser, "daudio")) {
135 return PA_HOOK_OK;
136 }
137
138 const char *clientUid = pa_proplist_gets(si->proplist, "stream.client.uid");
139 const char *bootUpMusic = "1003";
140 if (!pa_safe_streq(clientUid, bootUpMusic)) {
141 const char *sessionID = pa_proplist_gets(si->proplist, "stream.sessionID");
142 EffectChainManagerReleaseCb(sceneType, sessionID);
143 if (si->thread_info.state == PA_SINK_INPUT_RUNNING &&
144 !EffectChainManagerDeleteSessionInfo(sceneType, sessionID)) {
145 EffectChainManagerMultichannelUpdate(sceneType);
146 EffectChainManagerEffectUpdate();
147 EffectChainManagerStreamUsageUpdate();
148 }
149 }
150 return PA_HOOK_OK;
151 }
152
SinkInputStateChangedCb(pa_core * c,pa_sink_input * si,void * u)153 static pa_hook_result_t SinkInputStateChangedCb(pa_core *c, pa_sink_input *si, void *u)
154 {
155 pa_assert(c);
156 pa_sink_input_assert_ref(si);
157
158 const char *sceneMode = pa_proplist_gets(si->proplist, "scene.mode");
159 const char *sceneType = pa_proplist_gets(si->proplist, "scene.type");
160 const char *sessionID = pa_proplist_gets(si->proplist, "stream.sessionID");
161 const uint32_t channels = si->sample_spec.channels;
162 const char *channelLayout = pa_proplist_gets(si->proplist, "stream.channelLayout");
163 const char *spatializationEnabled = pa_proplist_gets(si->proplist, "spatialization.enabled");
164 const char *streamUsage = pa_proplist_gets(si->proplist, "stream.usage");
165 const char *clientUid = pa_proplist_gets(si->proplist, "stream.client.uid");
166 const char *bootUpMusic = "1003";
167
168 if (si->thread_info.state == PA_SINK_INPUT_RUNNING && si->sink &&
169 !pa_safe_streq(clientUid, bootUpMusic)) {
170 SessionInfoPack pack = {channels, channelLayout, sceneMode, spatializationEnabled, streamUsage};
171 if (!EffectChainManagerAddSessionInfo(sceneType, sessionID, pack)) {
172 EffectChainManagerMultichannelUpdate(sceneType);
173 EffectChainManagerVolumeUpdate(sessionID);
174 EffectChainManagerEffectUpdate();
175 EffectChainManagerStreamUsageUpdate();
176 }
177 }
178
179 if ((si->thread_info.state == PA_SINK_INPUT_CORKED || si->thread_info.state == PA_SINK_INPUT_UNLINKED) &&
180 si->sink && !pa_safe_streq(clientUid, bootUpMusic)) {
181 if (!EffectChainManagerDeleteSessionInfo(sceneType, sessionID)) {
182 EffectChainManagerMultichannelUpdate(sceneType);
183 EffectChainManagerVolumeUpdate(sessionID);
184 EffectChainManagerEffectUpdate();
185 EffectChainManagerStreamUsageUpdate();
186 }
187 }
188 return PA_HOOK_OK;
189 }
190
pa__init(pa_module * m)191 int pa__init(pa_module *m)
192 {
193 pa_modargs *ma = NULL;
194
195 pa_assert(m);
196
197 if (!(ma = pa_modargs_new(m->argument, VALID_MODARGS))) {
198 pa_log("Failed to parse module arguments");
199 goto fail;
200 }
201
202 if (!(m->userdata = PaHdiSinkNew(m, ma, __FILE__))) {
203 goto fail;
204 }
205 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE,
206 (pa_hook_cb_t)SinkInputNewCb, NULL);
207 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE,
208 (pa_hook_cb_t)SinkInputUnlinkCb, NULL);
209 // SourceOutputStateChangedCb will be replaced by UpdatePlaybackCaptureConfig in CapturerInServer
210 pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_LATE,
211 (pa_hook_cb_t)SinkInputStateChangedCb, NULL);
212
213 pa_modargs_free(ma);
214
215 return 0;
216
217 fail:
218 if (ma) {
219 pa_modargs_free(ma);
220 }
221
222 pa__done(m);
223
224 return -1;
225 }
226
pa__get_n_used(pa_module * m)227 int pa__get_n_used(pa_module *m)
228 {
229 pa_sink *sink = NULL;
230
231 pa_assert(m);
232 pa_assert_se(sink = m->userdata);
233
234 return pa_sink_linked_by(sink);
235 }
236
pa__done(pa_module * m)237 void pa__done(pa_module *m)
238 {
239 pa_sink *sink = NULL;
240
241 pa_assert(m);
242
243 if ((sink = m->userdata)) {
244 PaHdiSinkFree(sink);
245 }
246 }
247