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     const char *systemVolumeType = pa_proplist_gets(si->proplist, "systemVolume.type");
96     if (pa_safe_streq(deviceString, "remote")) {
97         EffectChainManagerReleaseCb(sceneType, sessionID);
98         return PA_HOOK_OK;
99     }
100 
101     const char *appUser = pa_proplist_gets(si->proplist, "application.process.user");
102     if (pa_safe_streq(appUser, "daudio")) {
103         return PA_HOOK_OK;
104     }
105 
106     const char *clientUid = pa_proplist_gets(si->proplist, "stream.client.uid");
107     const char *bootUpMusic = "1003";
108     if (!pa_safe_streq(clientUid, bootUpMusic)) {
109         if (!pa_safe_streq(sceneMode, "EFFECT_NONE") && pa_safe_streq(flush, "true")) {
110             EffectChainManagerInitCb(sceneType);
111         }
112         EffectChainManagerCreateCb(sceneType, sessionID);
113         SessionInfoPack pack = {channels, channelLayout, sceneMode, spatializationEnabled, streamUsage,
114             systemVolumeType};
115         if (si->thread_info.state == PA_SINK_INPUT_RUNNING &&
116             !EffectChainManagerAddSessionInfo(sceneType, sessionID, pack)) {
117             EffectChainManagerMultichannelUpdate(sceneType);
118             EffectChainManagerEffectUpdate();
119             EffectChainManagerStreamUsageUpdate();
120         }
121     }
122     return PA_HOOK_OK;
123 }
124 
SinkInputUnlinkCb(pa_core * c,pa_sink_input * si,void * u)125 static pa_hook_result_t SinkInputUnlinkCb(pa_core *c, pa_sink_input *si, void *u)
126 {
127     pa_assert(c);
128 
129     const char *sceneType = pa_proplist_gets(si->proplist, "scene.type");
130     const char *deviceString = pa_proplist_gets(si->sink->proplist, PA_PROP_DEVICE_STRING);
131     if (pa_safe_streq(deviceString, "remote")) {
132         return PA_HOOK_OK;
133     }
134 
135     const char *appUser = pa_proplist_gets(si->proplist, "application.process.user");
136     if (pa_safe_streq(appUser, "daudio")) {
137         return PA_HOOK_OK;
138     }
139 
140     const char *clientUid = pa_proplist_gets(si->proplist, "stream.client.uid");
141     const char *bootUpMusic = "1003";
142     if (!pa_safe_streq(clientUid, bootUpMusic)) {
143         const char *sessionID = pa_proplist_gets(si->proplist, "stream.sessionID");
144         EffectChainManagerReleaseCb(sceneType, sessionID);
145         if (si->thread_info.state == PA_SINK_INPUT_RUNNING &&
146             !EffectChainManagerDeleteSessionInfo(sceneType, sessionID)) {
147             EffectChainManagerMultichannelUpdate(sceneType);
148             EffectChainManagerEffectUpdate();
149             EffectChainManagerStreamUsageUpdate();
150         }
151     }
152     return PA_HOOK_OK;
153 }
154 
SinkInputStateChangedCb(pa_core * c,pa_sink_input * si,void * u)155 static pa_hook_result_t SinkInputStateChangedCb(pa_core *c, pa_sink_input *si, void *u)
156 {
157     pa_assert(c);
158     pa_sink_input_assert_ref(si);
159 
160     const char *sceneMode = pa_proplist_gets(si->proplist, "scene.mode");
161     const char *sceneType = pa_proplist_gets(si->proplist, "scene.type");
162     const char *sessionID = pa_proplist_gets(si->proplist, "stream.sessionID");
163     const uint32_t channels = si->sample_spec.channels;
164     const char *channelLayout = pa_proplist_gets(si->proplist, "stream.channelLayout");
165     const char *spatializationEnabled = pa_proplist_gets(si->proplist, "spatialization.enabled");
166     const char *streamUsage = pa_proplist_gets(si->proplist, "stream.usage");
167     const char *clientUid = pa_proplist_gets(si->proplist, "stream.client.uid");
168     const char *systemVolumeType = pa_proplist_gets(si->proplist, "systemVolume.type");
169     const char *bootUpMusic = "1003";
170 
171     if (si->thread_info.state == PA_SINK_INPUT_RUNNING && si->sink &&
172         !pa_safe_streq(clientUid, bootUpMusic)) {
173         SessionInfoPack pack = {channels, channelLayout, sceneMode, spatializationEnabled, streamUsage,
174             systemVolumeType};
175         if (!EffectChainManagerAddSessionInfo(sceneType, sessionID, pack)) {
176             EffectChainManagerMultichannelUpdate(sceneType);
177             EffectChainManagerVolumeUpdate(sessionID);
178             EffectChainManagerEffectUpdate();
179             EffectChainManagerStreamUsageUpdate();
180         }
181     }
182 
183     if ((si->thread_info.state == PA_SINK_INPUT_CORKED || si->thread_info.state == PA_SINK_INPUT_UNLINKED) &&
184         si->sink && !pa_safe_streq(clientUid, bootUpMusic)) {
185         if (!EffectChainManagerDeleteSessionInfo(sceneType, sessionID)) {
186             EffectChainManagerMultichannelUpdate(sceneType);
187             EffectChainManagerVolumeUpdate(sessionID);
188             EffectChainManagerEffectUpdate();
189             EffectChainManagerStreamUsageUpdate();
190         }
191     }
192     return PA_HOOK_OK;
193 }
194 
pa__init(pa_module * m)195 int pa__init(pa_module *m)
196 {
197     pa_modargs *ma = NULL;
198 
199     pa_assert(m);
200 
201     if (!(ma = pa_modargs_new(m->argument, VALID_MODARGS))) {
202         pa_log("Failed to parse module arguments");
203         goto fail;
204     }
205 
206     if (!(m->userdata = PaHdiSinkNew(m, ma, __FILE__))) {
207         goto fail;
208     }
209     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE,
210         (pa_hook_cb_t)SinkInputNewCb, NULL);
211     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE,
212         (pa_hook_cb_t)SinkInputUnlinkCb, NULL);
213     // SourceOutputStateChangedCb will be replaced by UpdatePlaybackCaptureConfig in CapturerInServer
214     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_LATE,
215         (pa_hook_cb_t)SinkInputStateChangedCb, NULL);
216 
217     pa_modargs_free(ma);
218 
219     return 0;
220 
221 fail:
222     if (ma) {
223         pa_modargs_free(ma);
224     }
225 
226     pa__done(m);
227 
228     return -1;
229 }
230 
pa__get_n_used(pa_module * m)231 int pa__get_n_used(pa_module *m)
232 {
233     pa_sink *sink = NULL;
234 
235     pa_assert(m);
236     pa_assert_se(sink = m->userdata);
237 
238     return pa_sink_linked_by(sink);
239 }
240 
pa__done(pa_module * m)241 void pa__done(pa_module *m)
242 {
243     pa_sink *sink = NULL;
244 
245     pa_assert(m);
246 
247     if ((sink = m->userdata)) {
248         PaHdiSinkFree(sink);
249     }
250 }
251