diff options
Diffstat (limited to 'pjmedia/src/pjmedia/vid_codec.c')
-rw-r--r-- | pjmedia/src/pjmedia/vid_codec.c | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/vid_codec.c b/pjmedia/src/pjmedia/vid_codec.c new file mode 100644 index 00000000..8eded7df --- /dev/null +++ b/pjmedia/src/pjmedia/vid_codec.c @@ -0,0 +1,731 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pjmedia/vid_codec.h> +#include <pjmedia/errno.h> +#include <pj/array.h> +#include <pj/assert.h> +#include <pj/log.h> +#include <pj/string.h> + +#define THIS_FILE "vid_codec.c" + +static pjmedia_vid_codec_mgr *def_vid_codec_mgr; + + +/* + * Codec manager maintains array of these structs for each supported + * codec. + */ +typedef struct pjmedia_vid_codec_desc +{ + pjmedia_vid_codec_info info; /**< Codec info. */ + pjmedia_codec_id id; /**< Fully qualified name */ + pjmedia_codec_priority prio; /**< Priority. */ + pjmedia_vid_codec_factory *factory; /**< The factory. */ + pjmedia_vid_codec_param *def_param; /**< Default codecs + parameters. */ +} pjmedia_vid_codec_desc; + + +/* The declaration of video codec manager */ +struct pjmedia_vid_codec_mgr +{ + /** Codec manager mutex. */ + pj_mutex_t *mutex; + + /** List of codec factories registered to codec manager. */ + pjmedia_vid_codec_factory factory_list; + + /** Number of supported codecs. */ + unsigned codec_cnt; + + /** Array of codec descriptor. */ + pjmedia_vid_codec_desc codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS]; + +}; + + + +/* Sort codecs in codec manager based on priorities */ +static void sort_codecs(pjmedia_vid_codec_mgr *mgr); + +/* + * Initialize pjmedia_vid_codec structure with default values. + */ +PJ_DEF(void) pjmedia_vid_codec_reset(pjmedia_vid_codec *codec, + pjmedia_obj_sig sig) +{ + pj_bzero(codec, sizeof(*codec)); + pjmedia_event_publisher_init(&codec->epub, sig); +} + +/* + * Duplicate video codec parameter. + */ +PJ_DEF(pjmedia_vid_codec_param*) pjmedia_vid_codec_param_clone( + pj_pool_t *pool, + const pjmedia_vid_codec_param *src) +{ + pjmedia_vid_codec_param *p; + unsigned i; + + PJ_ASSERT_RETURN(pool && src, NULL); + + p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param); + + /* Update codec param */ + pj_memcpy(p, src, sizeof(pjmedia_vid_codec_param)); + for (i = 0; i < src->dec_fmtp.cnt; ++i) { + pj_strdup(pool, &p->dec_fmtp.param[i].name, + &src->dec_fmtp.param[i].name); + pj_strdup(pool, &p->dec_fmtp.param[i].val, + &src->dec_fmtp.param[i].val); + } + for (i = 0; i < src->enc_fmtp.cnt; ++i) { + pj_strdup(pool, &p->enc_fmtp.param[i].name, + &src->enc_fmtp.param[i].name); + pj_strdup(pool, &p->enc_fmtp.param[i].val, + &src->enc_fmtp.param[i].val); + } + + return p; +} + +/* + * Initialize codec manager. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_create( + pj_pool_t *pool, + pjmedia_vid_codec_mgr **p_mgr) +{ + pjmedia_vid_codec_mgr *mgr; + pj_status_t status; + + PJ_ASSERT_RETURN(pool, PJ_EINVAL); + + mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_mgr); + pj_list_init (&mgr->factory_list); + mgr->codec_cnt = 0; + + /* Create mutex */ + status = pj_mutex_create_recursive(pool, "vid-codec-mgr", &mgr->mutex); + if (status != PJ_SUCCESS) + return status; + + if (!def_vid_codec_mgr) + def_vid_codec_mgr = mgr; + + if (p_mgr) + *p_mgr = mgr; + + return PJ_SUCCESS; +} + +/* + * Initialize codec manager. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_destroy (pjmedia_vid_codec_mgr *mgr) +{ + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + /* Destroy mutex */ + if (mgr->mutex) + pj_mutex_destroy(mgr->mutex); + + /* Just for safety, set codec manager states to zero */ + pj_bzero(mgr, sizeof(pjmedia_vid_codec_mgr)); + + if (mgr == def_vid_codec_mgr) + def_vid_codec_mgr = NULL; + + return PJ_SUCCESS; +} + + +PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void) +{ + //pj_assert(def_vid_codec_mgr); + return def_vid_codec_mgr; +} + +PJ_DEF(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr) +{ + def_vid_codec_mgr = mgr; +} + + +/* + * Register a codec factory. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_register_factory( + pjmedia_vid_codec_mgr *mgr, + pjmedia_vid_codec_factory *factory) +{ + pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; + unsigned i, count; + pj_status_t status; + + PJ_ASSERT_RETURN(factory, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + /* Enum codecs */ + count = PJ_ARRAY_SIZE(info); + status = factory->op->enum_info(factory, &count, info); + if (status != PJ_SUCCESS) + return status; + + pj_mutex_lock(mgr->mutex); + + /* Check codec count */ + if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) { + pj_mutex_unlock(mgr->mutex); + return PJ_ETOOMANY; + } + + + /* Save the codecs */ + for (i=0; i<count; ++i) { + pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i], + &info[i], sizeof(pjmedia_vid_codec_info)); + mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL; + mgr->codec_desc[mgr->codec_cnt+i].factory = factory; + pjmedia_vid_codec_info_to_id( &info[i], + mgr->codec_desc[mgr->codec_cnt+i].id, + sizeof(pjmedia_codec_id)); + } + + /* Update count */ + mgr->codec_cnt += count; + + /* Re-sort codec based on priorities */ + sort_codecs(mgr); + + /* Add factory to the list */ + pj_list_push_back(&mgr->factory_list, factory); + + pj_mutex_unlock(mgr->mutex); + + return PJ_SUCCESS; +} + + +/* + * Unregister a codec factory. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_unregister_factory( + pjmedia_vid_codec_mgr *mgr, + pjmedia_vid_codec_factory *factory) +{ + unsigned i; + PJ_ASSERT_RETURN(factory, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + pj_mutex_lock(mgr->mutex); + + /* Factory must be registered. */ + if (pj_list_find_node(&mgr->factory_list, factory) != factory) { + pj_mutex_unlock(mgr->mutex); + return PJ_ENOTFOUND; + } + + /* Erase factory from the factory list */ + pj_list_erase(factory); + + + /* Remove all supported codecs from the codec manager that were created + * by the specified factory. + */ + for (i=0; i<mgr->codec_cnt; ) { + + if (mgr->codec_desc[i].factory == factory) { + /* Remove the codec from array of codec descriptions */ + pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), + mgr->codec_cnt, i); + --mgr->codec_cnt; + + } else { + ++i; + } + } + + pj_mutex_unlock(mgr->mutex); + + return PJ_SUCCESS; +} + + +/* + * Enum all codecs. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs( + pjmedia_vid_codec_mgr *mgr, + unsigned *count, + pjmedia_vid_codec_info codecs[], + unsigned *prio) +{ + unsigned i; + + PJ_ASSERT_RETURN(count && codecs, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + pj_mutex_lock(mgr->mutex); + + if (*count > mgr->codec_cnt) + *count = mgr->codec_cnt; + + for (i=0; i<*count; ++i) { + pj_memcpy(&codecs[i], + &mgr->codec_desc[i].info, + sizeof(pjmedia_vid_codec_info)); + } + + if (prio) { + for (i=0; i < *count; ++i) + prio[i] = mgr->codec_desc[i].prio; + } + + pj_mutex_unlock(mgr->mutex); + + return PJ_SUCCESS; +} + + +/* + * Get codec info for the specified payload type. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info( + pjmedia_vid_codec_mgr *mgr, + unsigned pt, + const pjmedia_vid_codec_info **p_info) +{ + unsigned i; + + PJ_ASSERT_RETURN(p_info, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + pj_mutex_lock(mgr->mutex); + + for (i=0; i<mgr->codec_cnt; ++i) { + if (mgr->codec_desc[i].info.pt == pt) { + *p_info = &mgr->codec_desc[i].info; + + pj_mutex_unlock(mgr->mutex); + return PJ_SUCCESS; + } + } + + pj_mutex_unlock(mgr->mutex); + + return PJMEDIA_CODEC_EUNSUP; +} + + +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info2( + pjmedia_vid_codec_mgr *mgr, + pjmedia_format_id fmt_id, + const pjmedia_vid_codec_info **p_info) +{ + unsigned i; + + PJ_ASSERT_RETURN(p_info, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + pj_mutex_lock(mgr->mutex); + + for (i=0; i<mgr->codec_cnt; ++i) { + if (mgr->codec_desc[i].info.fmt_id == fmt_id) { + *p_info = &mgr->codec_desc[i].info; + + pj_mutex_unlock(mgr->mutex); + return PJ_SUCCESS; + } + } + + pj_mutex_unlock(mgr->mutex); + + return PJMEDIA_CODEC_EUNSUP; +} + + +/* + * Convert codec info struct into a unique codec identifier. + * A codec identifier looks something like "H263/34". + */ +PJ_DEF(char*) pjmedia_vid_codec_info_to_id( + const pjmedia_vid_codec_info *info, + char *id, unsigned max_len ) +{ + int len; + + PJ_ASSERT_RETURN(info && id && max_len, NULL); + + len = pj_ansi_snprintf(id, max_len, "%.*s/%u", + (int)info->encoding_name.slen, + info->encoding_name.ptr, + info->pt); + + if (len < 1 || len >= (int)max_len) { + id[0] = '\0'; + return NULL; + } + + return id; +} + + +/* + * Find codecs by the unique codec identifier. This function will find + * all codecs that match the codec identifier prefix. For example, if + * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1", + * and so on, up to the maximum count specified in the argument. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id( + pjmedia_vid_codec_mgr *mgr, + const pj_str_t *codec_id, + unsigned *count, + const pjmedia_vid_codec_info *p_info[], + unsigned prio[]) +{ + unsigned i, found = 0; + + PJ_ASSERT_RETURN(codec_id && count && *count, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + pj_mutex_lock(mgr->mutex); + + for (i=0; i<mgr->codec_cnt; ++i) { + + if (codec_id->slen == 0 || + pj_strnicmp2(codec_id, mgr->codec_desc[i].id, + codec_id->slen) == 0) + { + + if (p_info) + p_info[found] = &mgr->codec_desc[i].info; + if (prio) + prio[found] = mgr->codec_desc[i].prio; + + ++found; + + if (found >= *count) + break; + } + + } + + pj_mutex_unlock(mgr->mutex); + + *count = found; + + return found ? PJ_SUCCESS : PJ_ENOTFOUND; +} + + +/* Swap two codecs positions in codec manager */ +static void swap_codec(pjmedia_vid_codec_mgr *mgr, unsigned i, unsigned j) +{ + pjmedia_vid_codec_desc tmp; + + pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(pjmedia_vid_codec_desc)); + + pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], + sizeof(pjmedia_vid_codec_desc)); + + pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(pjmedia_vid_codec_desc)); +} + + +/* Sort codecs in codec manager based on priorities */ +static void sort_codecs(pjmedia_vid_codec_mgr *mgr) +{ + unsigned i; + + /* Re-sort */ + for (i=0; i<mgr->codec_cnt; ++i) { + unsigned j, max; + + for (max=i, j=i+1; j<mgr->codec_cnt; ++j) { + if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio) + max = j; + } + + if (max != i) + swap_codec(mgr, i, max); + } + + /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */ + for (i=0; i<mgr->codec_cnt; ++i) { + if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST) + mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER; + else + break; + } +} + + +/** + * Set codec priority. The codec priority determines the order of + * the codec in the SDP created by the endpoint. If more than one codecs + * are found with the same codec_id prefix, then the function sets the + * priorities of all those codecs. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_codec_priority( + pjmedia_vid_codec_mgr *mgr, + const pj_str_t *codec_id, + pj_uint8_t prio) +{ + unsigned i, found = 0; + + PJ_ASSERT_RETURN(codec_id, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + pj_mutex_lock(mgr->mutex); + + /* Update the priorities of affected codecs */ + for (i=0; i<mgr->codec_cnt; ++i) + { + if (codec_id->slen == 0 || + pj_strnicmp2(codec_id, mgr->codec_desc[i].id, + codec_id->slen) == 0) + { + mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio; + ++found; + } + } + + if (!found) { + pj_mutex_unlock(mgr->mutex); + return PJ_ENOTFOUND; + } + + /* Re-sort codecs */ + sort_codecs(mgr); + + pj_mutex_unlock(mgr->mutex); + + return PJ_SUCCESS; +} + + +/* + * Allocate one codec. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_alloc_codec( + pjmedia_vid_codec_mgr *mgr, + const pjmedia_vid_codec_info *info, + pjmedia_vid_codec **p_codec) +{ + pjmedia_vid_codec_factory *factory; + pj_status_t status; + + PJ_ASSERT_RETURN(info && p_codec, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + *p_codec = NULL; + + pj_mutex_lock(mgr->mutex); + + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { + + if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { + + status = (*factory->op->alloc_codec)(factory, info, p_codec); + if (status == PJ_SUCCESS) { + pj_mutex_unlock(mgr->mutex); + return PJ_SUCCESS; + } + + } + + factory = factory->next; + } + + pj_mutex_unlock(mgr->mutex); + + return PJMEDIA_CODEC_EUNSUP; +} + + +/* + * Get default codec parameter. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_default_param( + pjmedia_vid_codec_mgr *mgr, + const pjmedia_vid_codec_info *info, + pjmedia_vid_codec_param *param ) +{ + pjmedia_vid_codec_factory *factory; + pj_status_t status; + pjmedia_codec_id codec_id; + pjmedia_vid_codec_desc *codec_desc = NULL; + unsigned i; + + PJ_ASSERT_RETURN(info && param, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, + sizeof(codec_id))) + return PJ_EINVAL; + + pj_mutex_lock(mgr->mutex); + + /* First, lookup default param in codec desc */ + for (i=0; i < mgr->codec_cnt; ++i) { + if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { + codec_desc = &mgr->codec_desc[i]; + break; + } + } + + /* If we found the codec and its default param is set, return it */ + if (codec_desc && codec_desc->def_param) { + pj_memcpy(param, codec_desc->def_param, + sizeof(pjmedia_vid_codec_param)); + + pj_mutex_unlock(mgr->mutex); + return PJ_SUCCESS; + } + + /* Otherwise query the default param from codec factory */ + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { + + if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { + + status = (*factory->op->default_attr)(factory, info, param); + if (status == PJ_SUCCESS) { + /* Check for invalid max_bps. */ + //if (param->info.max_bps < param->info.avg_bps) + // param->info.max_bps = param->info.avg_bps; + + pj_mutex_unlock(mgr->mutex); + return PJ_SUCCESS; + } + + } + + factory = factory->next; + } + + pj_mutex_unlock(mgr->mutex); + + + return PJMEDIA_CODEC_EUNSUP; +} + + +/* + * Set default codec parameter. + */ +PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_default_param( + pjmedia_vid_codec_mgr *mgr, + pj_pool_t *pool, + const pjmedia_vid_codec_info *info, + const pjmedia_vid_codec_param *param ) +{ + unsigned i; + pjmedia_codec_id codec_id; + pjmedia_vid_codec_desc *codec_desc = NULL; + pjmedia_vid_codec_param *p; + + PJ_ASSERT_RETURN(info, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id))) + return PJ_EINVAL; + + pj_mutex_lock(mgr->mutex); + + /* Lookup codec desc */ + for (i=0; i < mgr->codec_cnt; ++i) { + if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) { + codec_desc = &mgr->codec_desc[i]; + break; + } + } + + /* Codec not found */ + if (!codec_desc) { + pj_mutex_unlock(mgr->mutex); + return PJMEDIA_CODEC_EUNSUP; + } + + /* If codec param is previously set */ + if (codec_desc->def_param) { + codec_desc->def_param = NULL; + } + + /* When param is set to NULL, i.e: setting default codec param to library + * default setting, just return PJ_SUCCESS. + */ + if (NULL == param) { + pj_mutex_unlock(mgr->mutex); + return PJ_SUCCESS; + } + + /* Update codec default param */ + p = pjmedia_vid_codec_param_clone(pool, param); + if (!p) + return PJ_EINVAL; + codec_desc->def_param = p; + + pj_mutex_unlock(mgr->mutex); + + return PJ_SUCCESS; +} + + +/* + * Dealloc codec. + */ +PJ_DEF(pj_status_t) +pjmedia_vid_codec_mgr_dealloc_codec(pjmedia_vid_codec_mgr *mgr, + pjmedia_vid_codec *codec) +{ + PJ_ASSERT_RETURN(codec, PJ_EINVAL); + + if (!mgr) mgr = def_vid_codec_mgr; + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + + return (*codec->factory->op->dealloc_codec)(codec->factory, codec); +} + |