dect
/
asterisk
Archived
13
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
asterisk/main/format_cap.c

643 lines
15 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2010, Digium, Inc.
*
* David Vossel <dvossel@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief Format Capability API
*
* \author David Vossel <dvossel@digium.com>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include "asterisk/_private.h"
#include "asterisk/version.h"
#include "asterisk/format.h"
#include "asterisk/format_cap.h"
#include "asterisk/frame.h"
#include "asterisk/astobj2.h"
#include "asterisk/utils.h"
struct ast_format_cap {
/* The capabilities structure is just an ao2 container of ast_formats */
struct ao2_container *formats;
struct ao2_iterator it;
int nolock;
};
/*! format exists within capabilities structure if it is identical to
* another format, or if the format is a proper subset of another format. */
static int cmp_cb(void *obj, void *arg, int flags)
{
struct ast_format *format1 = arg;
struct ast_format *format2 = obj;
enum ast_format_cmp_res res = ast_format_cmp(format1, format2);
return ((res == AST_FORMAT_CMP_EQUAL) ||
(res == AST_FORMAT_CMP_SUBSET)) ?
CMP_MATCH | CMP_STOP :
0;
}
static int hash_cb(const void *obj, const int flags)
{
const struct ast_format *format = obj;
return format->id;
}
static struct ast_format_cap *cap_alloc_helper(int nolock)
{
struct ast_format_cap *cap = ast_calloc(1, sizeof(*cap));
if (!cap) {
return NULL;
}
cap->nolock = nolock ? OBJ_NOLOCK : 0;
if (!(cap->formats = ao2_container_alloc(283, hash_cb, cmp_cb))) {
ast_free(cap);
return NULL;
}
return cap;
}
struct ast_format_cap *ast_format_cap_alloc_nolock(void)
{
return cap_alloc_helper(1);
}
struct ast_format_cap *ast_format_cap_alloc(void)
{
return cap_alloc_helper(0);
}
void *ast_format_cap_destroy(struct ast_format_cap *cap)
{
if (!cap) {
return NULL;
}
ao2_ref(cap->formats, -1);
ast_free(cap);
return NULL;
}
void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *format)
{
struct ast_format *fnew;
if (!format || !format->id) {
return;
}
if (!(fnew = ao2_alloc(sizeof(struct ast_format), NULL))) {
return;
}
ast_format_copy(fnew, format);
if (cap->nolock) {
ao2_link_nolock(cap->formats, fnew);
} else {
ao2_link(cap->formats, fnew);
}
ao2_ref(fnew, -1);
}
void ast_format_cap_add_all_by_type(struct ast_format_cap *cap, enum ast_format_type type)
{
int x;
size_t f_len = 0;
const struct ast_format_list *f_list = ast_format_list_get(&f_len);
for (x = 0; x < f_len; x++) {
if (AST_FORMAT_GET_TYPE(f_list[x].format.id) == type) {
ast_format_cap_add(cap, &f_list[x].format);
}
}
ast_format_list_destroy(f_list);
}
void ast_format_cap_add_all(struct ast_format_cap *cap)
{
int x;
size_t f_len = 0;
const struct ast_format_list *f_list = ast_format_list_get(&f_len);
for (x = 0; x < f_len; x++) {
ast_format_cap_add(cap, &f_list[x].format);
}
ast_format_list_destroy(f_list);
}
static int append_cb(void *obj, void *arg, int flag)
{
struct ast_format_cap *result = (struct ast_format_cap *) arg;
struct ast_format *format = (struct ast_format *) obj;
if (!ast_format_cap_iscompatible(result, format)) {
ast_format_cap_add(result, format);
}
return 0;
}
void ast_format_cap_append(struct ast_format_cap *dst, const struct ast_format_cap *src)
{
ao2_callback(src->formats, OBJ_NODATA | src->nolock, append_cb, dst);
}
static int copy_cb(void *obj, void *arg, int flag)
{
struct ast_format_cap *result = (struct ast_format_cap *) arg;
struct ast_format *format = (struct ast_format *) obj;
ast_format_cap_add(result, format);
return 0;
}
void ast_format_cap_copy(struct ast_format_cap *dst, const struct ast_format_cap *src)
{
ast_format_cap_remove_all(dst);
ao2_callback(src->formats, OBJ_NODATA | src->nolock, copy_cb, dst);
}
struct ast_format_cap *ast_format_cap_dup(const struct ast_format_cap *cap)
{
struct ast_format_cap *dst;
if (cap->nolock) {
dst = ast_format_cap_alloc_nolock();
} else {
dst = ast_format_cap_alloc();
}
if (!dst) {
return NULL;
}
ao2_callback(cap->formats, OBJ_NODATA | cap->nolock, copy_cb, dst);
return dst;
}
int ast_format_cap_is_empty(const struct ast_format_cap *cap)
{
if (!cap) {
return 1;
}
return ao2_container_count(cap->formats) == 0 ? 1 : 0;
}
static int find_exact_cb(void *obj, void *arg, int flag)
{
struct ast_format *format1 = (struct ast_format *) arg;
struct ast_format *format2 = (struct ast_format *) obj;
return (ast_format_cmp(format1, format2) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH : 0;
}
int ast_format_cap_remove(struct ast_format_cap *cap, struct ast_format *format)
{
struct ast_format *fremove;
fremove = ao2_callback(cap->formats, OBJ_POINTER | OBJ_UNLINK | cap->nolock, find_exact_cb, format);
if (fremove) {
ao2_ref(fremove, -1);
return 0;
}
return -1;
}
struct multiple_by_id_data {
struct ast_format *format;
int match_found;
};
static int multiple_by_id_cb(void *obj, void *arg, int flag)
{
struct multiple_by_id_data *data = arg;
struct ast_format *format = obj;
int res;
res = (format->id == data->format->id) ? CMP_MATCH : 0;
if (res) {
data->match_found = 1;
}
return res;
}
int ast_format_cap_remove_byid(struct ast_format_cap *cap, enum ast_format_id id)
{
struct ast_format format = {
.id = id,
};
struct multiple_by_id_data data = {
.format = &format,
.match_found = 0,
};
ao2_callback(cap->formats,
OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK,
multiple_by_id_cb,
&data);
/* match_found will be set if at least one item was removed */
if (data.match_found) {
return 0;
}
return -1;
}
static int multiple_by_type_cb(void *obj, void *arg, int flag)
{
int *type = arg;
struct ast_format *format = obj;
return ((AST_FORMAT_GET_TYPE(format->id)) == *type) ? CMP_MATCH : 0;
}
void ast_format_cap_remove_bytype(struct ast_format_cap *cap, enum ast_format_type type)
{
ao2_callback(cap->formats,
OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE | cap->nolock,
multiple_by_type_cb,
&type);
}
void ast_format_cap_remove_all(struct ast_format_cap *cap)
{
ao2_callback(cap->formats, OBJ_NODATA | cap->nolock | OBJ_MULTIPLE | OBJ_UNLINK, NULL, NULL);
}
void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format)
{
ast_format_cap_remove_all(cap);
ast_format_cap_add(cap, format);
}
int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format, struct ast_format *result)
{
struct ast_format *f;
struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap;
f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock);
if (f) {
ast_format_copy(result, f);
ao2_ref(f, -1);
return 1;
}
ast_format_clear(result);
return 0;
}
int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format)
{
struct ast_format *f;
struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap;
if (!tmp_cap) {
return 0;
}
f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock);
if (f) {
ao2_ref(f, -1);
return 1;
}
return 0;
}
struct byid_data {
struct ast_format *result;
enum ast_format_id id;
};
static int find_best_byid_cb(void *obj, void *arg, int flag)
{
struct ast_format *format = obj;
struct byid_data *data = arg;
if (data->id != format->id) {
return 0;
}
if (!data->result->id || (ast_format_rate(data->result) < ast_format_rate(format))) {
ast_format_copy(data->result, format);
}
return 0;
}
int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_id id, struct ast_format *result)
{
struct byid_data data;
data.result = result;
data.id = id;
ast_format_clear(result);
ao2_callback(cap->formats,
OBJ_MULTIPLE | OBJ_NODATA | cap->nolock,
find_best_byid_cb,
&data);
return result->id ? 1 : 0;
}
/*! \internal
* \brief this struct is just used for the ast_format_cap_joint function so we can provide
* both a format and a result ast_format_cap structure as arguments to the find_joint_cb
* ao2 callback function.
*/
struct find_joint_data {
/*! format to compare to for joint capabilities */
struct ast_format *format;
/*! if joint formmat exists with above format, add it to the result container */
struct ast_format_cap *joint_cap;
int joint_found;
};
static int find_joint_cb(void *obj, void *arg, int flag)
{
struct ast_format *format = obj;
struct find_joint_data *data = arg;
struct ast_format tmp = { 0, };
if (!ast_format_joint(format, data->format, &tmp)) {
if (data->joint_cap) {
ast_format_cap_add(data->joint_cap, &tmp);
}
data->joint_found++;
}
return 0;
}
int ast_format_cap_has_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
{
struct ao2_iterator it;
struct ast_format *tmp;
struct find_joint_data data = {
.joint_found = 0,
.joint_cap = NULL,
};
it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
data.format = tmp;
ao2_callback(cap2->formats,
OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
find_joint_cb,
&data);
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
return data.joint_found ? 1 : 0;
}
int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
{
struct ao2_iterator it;
struct ast_format *tmp;
if (ao2_container_count(cap1->formats) != ao2_container_count(cap2->formats)) {
return 0; /* if they are not the same size, they are not identical */
}
it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
if (!ast_format_cap_iscompatible(cap2, tmp)) {
ao2_ref(tmp, -1);
ao2_iterator_destroy(&it);
return 0;
}
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
return 1;
}
struct ast_format_cap *ast_format_cap_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2)
{
struct ao2_iterator it;
struct ast_format_cap *result = ast_format_cap_alloc_nolock();
struct ast_format *tmp;
struct find_joint_data data = {
.joint_found = 0,
.joint_cap = result,
};
if (!result) {
return NULL;
}
it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
data.format = tmp;
ao2_callback(cap2->formats,
OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
find_joint_cb,
&data);
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
if (ao2_container_count(result->formats)) {
return result;
}
result = ast_format_cap_destroy(result);
return NULL;
}
static int joint_copy_helper(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result, int append)
{
struct ao2_iterator it;
struct ast_format *tmp;
struct find_joint_data data = {
.joint_cap = result,
.joint_found = 0,
};
if (!append) {
ast_format_cap_remove_all(result);
}
it = ao2_iterator_init(cap1->formats, cap2->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
data.format = tmp;
ao2_callback(cap2->formats,
OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock,
find_joint_cb,
&data);
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
return ao2_container_count(result->formats) ? 1 : 0;
}
int ast_format_cap_joint_append(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result)
{
return joint_copy_helper(cap1, cap2, result, 1);
}
int ast_format_cap_joint_copy(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result)
{
return joint_copy_helper(cap1, cap2, result, 0);
}
struct ast_format_cap *ast_format_cap_get_type(const struct ast_format_cap *cap, enum ast_format_type ftype)
{
struct ao2_iterator it;
struct ast_format_cap *result = ast_format_cap_alloc_nolock();
struct ast_format *tmp;
if (!result) {
return NULL;
}
/* for each format in cap1, see if that format is
* compatible with cap2. If so copy it to the result */
it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) {
/* copy format */
ast_format_cap_add(result, tmp);
}
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
if (ao2_container_count(result->formats)) {
return result;
}
result = ast_format_cap_destroy(result);
/* Remember to always free the NULL before returning it. */
ast_free(NULL);
return NULL;
}
int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_type type)
{
struct ao2_iterator it;
struct ast_format *tmp;
it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
if (AST_FORMAT_GET_TYPE(tmp->id) == type) {
ao2_ref(tmp, -1);
ao2_iterator_destroy(&it);
return 1;
}
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
return 0;
}
void ast_format_cap_iter_start(struct ast_format_cap *cap)
{
if (!cap->nolock) {
ao2_lock(cap->formats);
}
cap->it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
}
void ast_format_cap_iter_end(struct ast_format_cap *cap)
{
ao2_iterator_destroy(&cap->it);
if (!cap->nolock) {
ao2_unlock(cap->formats);
}
}
int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format)
{
struct ast_format *tmp = ao2_iterator_next(&cap->it);
if (!tmp) {
return -1;
}
ast_format_copy(format, tmp);
ao2_ref(tmp, -1);
return 0;
}
char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap)
{
int x;
unsigned len;
char *start, *end = buf;
struct ast_format tmp_fmt;
size_t f_len;
const struct ast_format_list *f_list = ast_format_list_get(&f_len);
if (!size) {
f_list = ast_format_list_destroy(f_list);
return buf;
}
snprintf(end, size, "(");
len = strlen(end);
end += len;
size -= len;
start = end;
for (x = 0; x < f_len; x++) {
ast_format_copy(&tmp_fmt, &f_list[x].format);
if (ast_format_cap_iscompatible(cap, &tmp_fmt)) {
snprintf(end, size, "%s|", f_list[x].name);
len = strlen(end);
end += len;
size -= len;
}
}
if (start == end) {
ast_copy_string(start, "nothing)", size);
} else if (size > 1) {
*(end - 1) = ')';
}
f_list = ast_format_list_destroy(f_list);
return buf;
}
uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap)
{
uint64_t res = 0;
struct ao2_iterator it;
struct ast_format *tmp;
it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0);
while ((tmp = ao2_iterator_next(&it))) {
res |= ast_format_to_old_bitfield(tmp);
ao2_ref(tmp, -1);
}
ao2_iterator_destroy(&it);
return res;
}
void ast_format_cap_from_old_bitfield(struct ast_format_cap *dst, uint64_t src)
{
uint64_t tmp = 0;
int x;
struct ast_format tmp_format = { 0, };
ast_format_cap_remove_all(dst);
for (x = 0; x < 64; x++) {
tmp = (1ULL << x);
if (tmp & src) {
ast_format_cap_add(dst, ast_format_from_old_bitfield(&tmp_format, tmp));
}
}
}