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