1 /*
2 ** Copyright 2022, The Android Open-Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 ** http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16
17 #define LOG_TAG "hal_smoothness"
18
19 #include <audio_utils/hal_smoothness.h>
20 #include <errno.h>
21 #include <float.h>
22 #include <log/log.h>
23 #include <math.h>
24 #include <stdlib.h>
25
26 typedef struct hal_smoothness_internal {
27 struct hal_smoothness itfe;
28
29 struct hal_smoothness_metrics metrics;
30
31 // number of “total_writes” before flushing smoothness data to system (ie.
32 // logcat) A flush will also reset all numeric values in the "metrics" field.
33 unsigned int num_writes_to_log;
34
35 // Client defined function to flush smoothness metrics.
36 void (*client_flush_cb)(struct hal_smoothness_metrics *smoothness_metrics,
37 void *private_data);
38
39 // Client provided pointer.
40 void *private_data;
41 } hal_smoothness_internal;
42
reset_metrics(struct hal_smoothness_metrics * metrics)43 static void reset_metrics(struct hal_smoothness_metrics *metrics) {
44 metrics->underrun_count = 0;
45 metrics->overrun_count = 0;
46 metrics->total_writes = 0;
47 metrics->total_frames_written = 0;
48 metrics->total_frames_lost = 0;
49 metrics->timestamp = 0;
50 metrics->smoothness_value = 0.0;
51 }
52
add_check_overflow(unsigned int * data,unsigned int add_amount)53 static bool add_check_overflow(unsigned int *data, unsigned int add_amount) {
54 return __builtin_add_overflow(*data, add_amount, data);
55 }
56
increment_underrun(struct hal_smoothness * smoothness,unsigned int frames_lost)57 static int increment_underrun(struct hal_smoothness *smoothness,
58 unsigned int frames_lost) {
59 if (smoothness == NULL) {
60 return -EINVAL;
61 }
62
63 hal_smoothness_internal *smoothness_meta =
64 (hal_smoothness_internal *)smoothness;
65
66 if (add_check_overflow(&smoothness_meta->metrics.underrun_count, 1)) {
67 return -EOVERFLOW;
68 }
69
70 if (add_check_overflow(&smoothness_meta->metrics.total_frames_lost,
71 frames_lost)) {
72 return -EOVERFLOW;
73 }
74
75 return 0;
76 }
77
increment_overrun(struct hal_smoothness * smoothness,unsigned int frames_lost)78 static int increment_overrun(struct hal_smoothness *smoothness,
79 unsigned int frames_lost) {
80 if (smoothness == NULL) {
81 return -EINVAL;
82 }
83
84 hal_smoothness_internal *smoothness_meta =
85 (hal_smoothness_internal *)smoothness;
86
87 if (add_check_overflow(&smoothness_meta->metrics.overrun_count, 1)) {
88 return -EOVERFLOW;
89 }
90
91 if (add_check_overflow(&smoothness_meta->metrics.total_frames_lost,
92 frames_lost)) {
93 return -EOVERFLOW;
94 }
95
96 return 0;
97 }
98
calc_smoothness_value(unsigned int total_frames_lost,unsigned int total_frames_written)99 static double calc_smoothness_value(unsigned int total_frames_lost,
100 unsigned int total_frames_written) {
101 // If error checks are correct in this library, this error shouldn't be
102 // possible.
103 if (total_frames_lost == 0 && total_frames_written == 0) {
104 ALOGE("total_frames_lost + total_frames_written shouldn't = 0");
105 return -EINVAL;
106 }
107
108 // No bytes dropped, so audio smoothness is perfect.
109 if (total_frames_lost == 0) {
110 return DBL_MAX;
111 }
112
113 unsigned int total_frames = total_frames_lost;
114
115 if (add_check_overflow(&total_frames, total_frames_written)) {
116 return -EOVERFLOW;
117 }
118
119 // Division by 0 shouldn't be possible.
120 double lost_frames_ratio = (double)total_frames_lost / total_frames;
121
122 // log(0) shouldn't be possible.
123 return -log(lost_frames_ratio);
124 }
125
flush(struct hal_smoothness * smoothness)126 static int flush(struct hal_smoothness *smoothness) {
127 if (smoothness == NULL) {
128 return -EINVAL;
129 }
130
131 hal_smoothness_internal *smoothness_meta =
132 (hal_smoothness_internal *)smoothness;
133
134 smoothness_meta->metrics.smoothness_value =
135 calc_smoothness_value(smoothness_meta->metrics.total_frames_lost,
136 smoothness_meta->metrics.total_frames_written);
137 smoothness_meta->client_flush_cb(&smoothness_meta->metrics,
138 smoothness_meta->private_data);
139 reset_metrics(&smoothness_meta->metrics);
140
141 return 0;
142 }
143
increment_total_writes(struct hal_smoothness * smoothness,unsigned int frames_written,unsigned long timestamp)144 static int increment_total_writes(struct hal_smoothness *smoothness,
145 unsigned int frames_written,
146 unsigned long timestamp) {
147 if (smoothness == NULL) {
148 return -EINVAL;
149 }
150
151 hal_smoothness_internal *smoothness_meta =
152 (hal_smoothness_internal *)smoothness;
153
154 if (add_check_overflow(&smoothness_meta->metrics.total_writes, 1)) {
155 return -EOVERFLOW;
156 }
157
158 if (add_check_overflow(&smoothness_meta->metrics.total_frames_written,
159 frames_written)) {
160 return -EOVERFLOW;
161 }
162 smoothness_meta->metrics.timestamp = timestamp;
163
164 // "total_writes" count has met a value where the client's callback function
165 // should be called
166 if (smoothness_meta->metrics.total_writes >=
167 smoothness_meta->num_writes_to_log) {
168 flush(smoothness);
169 }
170
171 return 0;
172 }
173
hal_smoothness_initialize(struct hal_smoothness ** smoothness,unsigned int version,unsigned int num_writes_to_log,void (* client_flush_cb)(struct hal_smoothness_metrics *,void *),void * private_data)174 int hal_smoothness_initialize(
175 struct hal_smoothness **smoothness, unsigned int version,
176 unsigned int num_writes_to_log,
177 void (*client_flush_cb)(struct hal_smoothness_metrics *, void *),
178 void *private_data) {
179 if (num_writes_to_log == 0) {
180 ALOGE("num_writes_to_logs must be > 0");
181
182 return -EINVAL;
183 }
184
185 if (client_flush_cb == NULL) {
186 ALOGE("client_flush_cb can't be NULL");
187
188 return -EINVAL;
189 }
190
191 hal_smoothness_internal *smoothness_meta;
192 smoothness_meta =
193 (hal_smoothness_internal *)calloc(1, sizeof(hal_smoothness_internal));
194
195 if (smoothness_meta == NULL) {
196 int ret_err = errno;
197 ALOGE("failed to calloc hal_smoothness_internal.");
198 return ret_err;
199 }
200
201 smoothness_meta->itfe.version = version;
202 smoothness_meta->itfe.increment_underrun = increment_underrun;
203 smoothness_meta->itfe.increment_overrun = increment_overrun;
204 smoothness_meta->itfe.increment_total_writes = increment_total_writes;
205 smoothness_meta->itfe.flush = flush;
206
207 smoothness_meta->num_writes_to_log = num_writes_to_log;
208 smoothness_meta->client_flush_cb = client_flush_cb;
209 smoothness_meta->private_data = private_data;
210
211 *smoothness = &smoothness_meta->itfe;
212
213 return 0;
214 }
215
hal_smoothness_free(struct hal_smoothness ** smoothness)216 void hal_smoothness_free(struct hal_smoothness **smoothness) {
217 if (smoothness == NULL || *smoothness == NULL) {
218 return;
219 }
220
221 free(*smoothness);
222 *smoothness = NULL;
223 }
224