Astobj2 locking enhancement.
[asterisk/asterisk.git] / main / format_cap.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Format Capability API
22  *
23  * \author David Vossel <dvossel@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
29
30 #include "asterisk/_private.h"
31 #include "asterisk/format.h"
32 #include "asterisk/format_cap.h"
33 #include "asterisk/frame.h"
34 #include "asterisk/astobj2.h"
35 #include "asterisk/utils.h"
36
37
38 struct ast_format_cap {
39         /* The capabilities structure is just an ao2 container of ast_formats */
40         struct ao2_container *formats;
41         struct ao2_iterator it;
42         /*! TRUE if the formats container created without a lock. */
43         int nolock;
44 };
45
46 /*! format exists within capabilities structure if it is identical to
47  * another format, or if the format is a proper subset of another format. */
48 static int cmp_cb(void *obj, void *arg, int flags)
49 {
50         struct ast_format *format1 = arg;
51         struct ast_format *format2 = obj;
52         enum ast_format_cmp_res res = ast_format_cmp(format1, format2);
53
54         return ((res == AST_FORMAT_CMP_EQUAL) ||
55                         (res == AST_FORMAT_CMP_SUBSET)) ?
56                                 CMP_MATCH | CMP_STOP :
57                                 0;
58 }
59
60 static int hash_cb(const void *obj, const int flags)
61 {
62         const struct ast_format *format = obj;
63         return format->id;
64 }
65
66 static struct ast_format_cap *cap_alloc_helper(int nolock)
67 {
68         struct ast_format_cap *cap = ast_calloc(1, sizeof(*cap));
69
70         if (!cap) {
71                 return NULL;
72         }
73         cap->nolock = nolock;
74         cap->formats = ao2_container_alloc_options(
75                 nolock ? AO2_ALLOC_OPT_LOCK_NOLOCK : AO2_ALLOC_OPT_LOCK_MUTEX,
76                 283, hash_cb, cmp_cb);
77         if (!cap->formats) {
78                 ast_free(cap);
79                 return NULL;
80         }
81
82         return cap;
83 }
84
85 struct ast_format_cap *ast_format_cap_alloc_nolock(void)
86 {
87         return cap_alloc_helper(1);
88 }
89
90 struct ast_format_cap *ast_format_cap_alloc(void)
91 {
92         return cap_alloc_helper(0);
93 }
94
95 void *ast_format_cap_destroy(struct ast_format_cap *cap)
96 {
97         if (!cap) {
98                 return NULL;
99         }
100         ao2_ref(cap->formats, -1);
101         ast_free(cap);
102         return NULL;
103 }
104
105 void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *format)
106 {
107         struct ast_format *fnew;
108
109         if (!format || !format->id) {
110                 return;
111         }
112         if (!(fnew = ao2_alloc(sizeof(struct ast_format), NULL))) {
113                 return;
114         }
115         ast_format_copy(fnew, format);
116         ao2_link(cap->formats, fnew);
117         ao2_ref(fnew, -1);
118 }
119
120 void ast_format_cap_add_all_by_type(struct ast_format_cap *cap, enum ast_format_type type)
121 {
122         int x;
123         size_t f_len = 0;
124         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
125
126         for (x = 0; x < f_len; x++) {
127                 if (AST_FORMAT_GET_TYPE(f_list[x].format.id) == type) {
128                         ast_format_cap_add(cap, &f_list[x].format);
129                 }
130         }
131         ast_format_list_destroy(f_list);
132 }
133
134 void ast_format_cap_add_all(struct ast_format_cap *cap)
135 {
136         int x;
137         size_t f_len = 0;
138         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
139
140         for (x = 0; x < f_len; x++) {
141                 ast_format_cap_add(cap, &f_list[x].format);
142         }
143         ast_format_list_destroy(f_list);
144 }
145
146 static int append_cb(void *obj, void *arg, int flag)
147 {
148         struct ast_format_cap *result = (struct ast_format_cap *) arg;
149         struct ast_format *format = (struct ast_format *) obj;
150
151         if (!ast_format_cap_iscompatible(result, format)) {
152                 ast_format_cap_add(result, format);
153         }
154
155         return 0;
156 }
157
158 void ast_format_cap_append(struct ast_format_cap *dst, const struct ast_format_cap *src)
159 {
160         ao2_callback(src->formats, OBJ_NODATA, append_cb, dst);
161 }
162
163 static int copy_cb(void *obj, void *arg, int flag)
164 {
165         struct ast_format_cap *result = (struct ast_format_cap *) arg;
166         struct ast_format *format = (struct ast_format *) obj;
167
168         ast_format_cap_add(result, format);
169         return 0;
170 }
171
172 void ast_format_cap_copy(struct ast_format_cap *dst, const struct ast_format_cap *src)
173 {
174         ast_format_cap_remove_all(dst);
175         ao2_callback(src->formats, OBJ_NODATA, copy_cb, dst);
176 }
177
178 struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap)
179 {
180         struct ast_format_cap *dst;
181         if (cap->nolock) {
182                 dst = ast_format_cap_alloc_nolock();
183         } else {
184                 dst = ast_format_cap_alloc();
185         }
186         if (!dst) {
187                 return NULL;
188         }
189         ao2_callback(cap->formats, OBJ_NODATA, copy_cb, dst);
190         return dst;
191 }
192
193 int ast_format_cap_is_empty(const struct ast_format_cap *cap)
194 {
195         if (!cap) {
196                 return 1;
197         }
198         return ao2_container_count(cap->formats) == 0 ? 1 : 0;
199 }
200
201 static int find_exact_cb(void *obj, void *arg, int flag)
202 {
203         struct ast_format *format1 = (struct ast_format *) arg;
204         struct ast_format *format2 = (struct ast_format *) obj;
205
206         return (ast_format_cmp(format1, format2) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH : 0;
207 }
208
209 int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
210 {
211         struct ast_format *fremove;
212
213         fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK, find_exact_cb, format);
214         if (fremove) {
215                 ao2_ref(fremove, -1);
216                 return 0;
217         }
218
219         return -1;
220 }
221
222 struct multiple_by_id_data {
223         struct ast_format *format;
224         int match_found;
225 };
226
227 static int multiple_by_id_cb(void *obj, void *arg, int flag)
228 {
229         struct multiple_by_id_data *data = arg;
230         struct ast_format *format = obj;
231         int res;
232
233         res = (format->id == data->format->id) ? CMP_MATCH : 0;
234         if (res) {
235                 data->match_found = 1;
236         }
237
238         return res;
239 }
240
241 int ast_format_cap_remove_byid(struct ast_format_cap *cap, enum ast_format_id id)
242 {
243         struct ast_format format = {
244                 .id = id,
245         };
246         struct multiple_by_id_data data = {
247                 .format = &format,
248                 .match_found = 0,
249         };
250
251         ao2_callback(cap->formats,
252                 OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
253                 multiple_by_id_cb,
254                 &data);
255
256         /* match_found will be set if at least one item was removed */
257         if (data.match_found) {
258                 return 0;
259         }
260
261         return -1;
262 }
263
264 static int multiple_by_type_cb(void *obj, void *arg, int flag)
265 {
266         int *type = arg;
267         struct ast_format *format = obj;
268         return ((AST_FORMAT_GET_TYPE(format->id)) == *type) ? CMP_MATCH : 0;
269 }
270
271 void ast_format_cap_remove_bytype(struct ast_format_cap *cap, enum ast_format_type type)
272 {
273         ao2_callback(cap->formats,
274                 OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
275                 multiple_by_type_cb,
276                 &type);
277 }
278
279 void ast_format_cap_remove_all(struct ast_format_cap *cap)
280 {
281         ao2_callback(cap->formats, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL);
282 }
283
284 void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format)
285 {
286         ast_format_cap_remove_all(cap);
287         ast_format_cap_add(cap, format);
288 }
289
290 int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format, struct ast_format *result)
291 {
292         struct ast_format *f;
293         struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap;
294
295         f = ao2_find(tmp_cap->formats, format, OBJ_POINTER);
296         if (f) {
297                 ast_format_copy(result, f);
298                 ao2_ref(f, -1);
299                 return 1;
300         }
301         ast_format_clear(result);
302         return 0;
303 }
304
305 int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format)
306 {
307         struct ast_format *f;
308         struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap;
309
310         if (!tmp_cap) {
311                 return 0;
312         }
313
314         f = ao2_find(tmp_cap->formats, format, OBJ_POINTER);
315         if (f) {
316                 ao2_ref(f, -1);
317                 return 1;
318         }
319
320         return 0;
321 }
322
323 struct byid_data {
324         struct ast_format *result;
325         enum ast_format_id id;
326 };
327 static int find_best_byid_cb(void *obj, void *arg, int flag)
328 {
329         struct ast_format *format = obj;
330         struct byid_data *data = arg;
331
332         if (data->id != format->id) {
333                 return 0;
334         }
335         if (!data->result->id || (ast_format_rate(data->result) < ast_format_rate(format))) {
336                 ast_format_copy(data->result, format);
337         }
338         return 0;
339 }
340
341 int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_id id, struct ast_format *result)
342 {
343         struct byid_data data;
344         data.result = result;
345         data.id = id;
346
347         ast_format_clear(result);
348         ao2_callback(cap->formats,
349                 OBJ_MULTIPLE | OBJ_NODATA,
350                 find_best_byid_cb,
351                 &data);
352         return result->id ? 1 : 0;
353 }
354
355 /*! \internal
356  * \brief this struct is just used for the ast_format_cap_joint function so we can provide
357  * both a format and a result ast_format_cap structure as arguments to the find_joint_cb
358  * ao2 callback function.
359  */
360 struct find_joint_data {
361         /*! format to compare to for joint capabilities */
362         struct ast_format *format;
363         /*! if joint formmat exists with above format, add it to the result container */
364         struct ast_format_cap *joint_cap;
365         int joint_found;
366 };
367
368 static int find_joint_cb(void *obj, void *arg, int flag)
369 {
370         struct ast_format *format = obj;
371         struct find_joint_data *data = arg;
372
373         struct ast_format tmp = { 0, };
374         if (!ast_format_joint(format, data->format, &tmp)) {
375                 if (data->joint_cap) {
376                         ast_format_cap_add(data->joint_cap, &tmp);
377                 }
378                 data->joint_found++;
379         }
380
381         return 0;
382 }
383
384 int ast_format_cap_has_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
385 {
386         struct ao2_iterator it;
387         struct ast_format *tmp;
388         struct find_joint_data data = {
389                 .joint_found = 0,
390                 .joint_cap = NULL,
391         };
392
393         it = ao2_iterator_init(cap1->formats, 0);
394         while ((tmp = ao2_iterator_next(&it))) {
395                 data.format = tmp;
396                 ao2_callback(cap2->formats,
397                         OBJ_MULTIPLE | OBJ_NODATA,
398                         find_joint_cb,
399                         &data);
400                 ao2_ref(tmp, -1);
401         }
402         ao2_iterator_destroy(&it);
403
404         return data.joint_found ? 1 : 0;
405 }
406
407 int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
408 {
409         struct ao2_iterator it;
410         struct ast_format *tmp;
411
412         if (ao2_container_count(cap1->formats) != ao2_container_count(cap2->formats)) {
413                 return 0; /* if they are not the same size, they are not identical */
414         }
415
416         it = ao2_iterator_init(cap1->formats, 0);
417         while ((tmp = ao2_iterator_next(&it))) {
418                 if (!ast_format_cap_iscompatible(cap2, tmp)) {
419                         ao2_ref(tmp, -1);
420                         ao2_iterator_destroy(&it);
421                         return 0;
422                 }
423                 ao2_ref(tmp, -1);
424         }
425         ao2_iterator_destroy(&it);
426
427         return 1;
428 }
429
430 struct ast_format_cap *ast_format_cap_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
431 {
432         struct ao2_iterator it;
433         struct ast_format_cap *result = ast_format_cap_alloc_nolock();
434         struct ast_format *tmp;
435         struct find_joint_data data = {
436                 .joint_found = 0,
437                 .joint_cap = result,
438         };
439         if (!result) {
440                 return NULL;
441         }
442
443         it = ao2_iterator_init(cap1->formats, 0);
444         while ((tmp = ao2_iterator_next(&it))) {
445                 data.format = tmp;
446                 ao2_callback(cap2->formats,
447                         OBJ_MULTIPLE | OBJ_NODATA,
448                         find_joint_cb,
449                         &data);
450                 ao2_ref(tmp, -1);
451         }
452         ao2_iterator_destroy(&it);
453
454         if (ao2_container_count(result->formats)) {
455                 return result;
456         }
457
458         result = ast_format_cap_destroy(result);
459         return NULL;
460 }
461
462 static int joint_copy_helper(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result, int append)
463 {
464         struct ao2_iterator it;
465         struct ast_format *tmp;
466         struct find_joint_data data = {
467                 .joint_cap = result,
468                 .joint_found = 0,
469         };
470         if (!append) {
471                 ast_format_cap_remove_all(result);
472         }
473         it = ao2_iterator_init(cap1->formats, 0);
474         while ((tmp = ao2_iterator_next(&it))) {
475                 data.format = tmp;
476                 ao2_callback(cap2->formats,
477                         OBJ_MULTIPLE | OBJ_NODATA,
478                         find_joint_cb,
479                         &data);
480                 ao2_ref(tmp, -1);
481         }
482         ao2_iterator_destroy(&it);
483
484         return ao2_container_count(result->formats) ? 1 : 0;
485 }
486
487 int ast_format_cap_joint_append(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result)
488 {
489         return joint_copy_helper(cap1, cap2, result, 1);
490 }
491
492 int ast_format_cap_joint_copy(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result)
493 {
494         return joint_copy_helper(cap1, cap2, result, 0);
495 }
496
497 struct ast_format_cap *ast_format_cap_get_type(const struct ast_format_cap *cap, enum ast_format_type ftype)
498 {
499         struct ao2_iterator it;
500         struct ast_format_cap *result = ast_format_cap_alloc_nolock();
501         struct ast_format *tmp;
502
503         if (!result) {
504                 return NULL;
505         }
506
507         /* for each format in cap1, see if that format is
508          * compatible with cap2. If so copy it to the result */
509         it = ao2_iterator_init(cap->formats, 0);
510         while ((tmp = ao2_iterator_next(&it))) {
511                 if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) {
512                         /* copy format */
513                         ast_format_cap_add(result, tmp);
514                 }
515                 ao2_ref(tmp, -1);
516         }
517         ao2_iterator_destroy(&it);
518
519         if (ao2_container_count(result->formats)) {
520                 return result;
521         }
522         result = ast_format_cap_destroy(result);
523
524         return NULL;
525 }
526
527
528 int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_type type)
529 {
530         struct ao2_iterator it;
531         struct ast_format *tmp;
532
533         it = ao2_iterator_init(cap->formats, 0);
534         while ((tmp = ao2_iterator_next(&it))) {
535                 if (AST_FORMAT_GET_TYPE(tmp->id) == type) {
536                         ao2_ref(tmp, -1);
537                         ao2_iterator_destroy(&it);
538                         return 1;
539                 }
540                 ao2_ref(tmp, -1);
541         }
542         ao2_iterator_destroy(&it);
543
544         return 0;
545 }
546
547 void ast_format_cap_iter_start(struct ast_format_cap *cap)
548 {
549         /* We can unconditionally lock even if the container has no lock. */
550         ao2_lock(cap->formats);
551         cap->it = ao2_iterator_init(cap->formats, AO2_ITERATOR_DONTLOCK);
552 }
553
554 void ast_format_cap_iter_end(struct ast_format_cap *cap)
555 {
556         ao2_iterator_destroy(&cap->it);
557         /* We can unconditionally unlock even if the container has no lock. */
558         ao2_unlock(cap->formats);
559 }
560
561 int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format)
562 {
563         struct ast_format *tmp = ao2_iterator_next(&cap->it);
564
565         if (!tmp) {
566                 return -1;
567         }
568         ast_format_copy(format, tmp);
569         ao2_ref(tmp, -1);
570
571         return 0;
572 }
573
574 char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap)
575 {
576         int x;
577         unsigned len;
578         char *start, *end = buf;
579         struct ast_format tmp_fmt;
580         size_t f_len;
581         const struct ast_format_list *f_list = ast_format_list_get(&f_len);
582
583         if (!size) {
584                 f_list = ast_format_list_destroy(f_list);
585                 return buf;
586         }
587         snprintf(end, size, "(");
588         len = strlen(end);
589         end += len;
590         size -= len;
591         start = end;
592         for (x = 0; x < f_len; x++) {
593                 ast_format_copy(&tmp_fmt, &f_list[x].format);
594                 if (ast_format_cap_iscompatible(cap, &tmp_fmt)) {
595                         snprintf(end, size, "%s|", f_list[x].name);
596                         len = strlen(end);
597                         end += len;
598                         size -= len;
599                 }
600         }
601         if (start == end) {
602                 ast_copy_string(start, "nothing)", size);
603         } else if (size > 1) {
604                 *(end - 1) = ')';
605         }
606         f_list = ast_format_list_destroy(f_list);
607         return buf;
608 }
609
610 uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap)
611 {
612         uint64_t res = 0;
613         struct ao2_iterator it;
614         struct ast_format *tmp;
615
616         it = ao2_iterator_init(cap->formats, 0);
617         while ((tmp = ao2_iterator_next(&it))) {
618                 res |= ast_format_to_old_bitfield(tmp);
619                 ao2_ref(tmp, -1);
620         }
621         ao2_iterator_destroy(&it);
622         return res;
623 }
624
625 void ast_format_cap_from_old_bitfield(struct ast_format_cap *dst, uint64_t src)
626 {
627         uint64_t tmp = 0;
628         int x;
629         struct ast_format tmp_format = { 0, };
630
631         ast_format_cap_remove_all(dst);
632         for (x = 0; x < 64; x++) {
633                 tmp = (1ULL << x);
634                 if (tmp & src) {
635                         ast_format_cap_add(dst, ast_format_from_old_bitfield(&tmp_format, tmp));
636                 }
637         }
638 }