Index: pjproject/trunk/pjmedia/src/pjmedia/codec.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/codec.c	(revision 276)
+++ pjproject/trunk/pjmedia/src/pjmedia/codec.c	(revision 411)
@@ -19,34 +19,18 @@
 #include <pjmedia/codec.h>
 #include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
 #include <pj/pool.h>
 #include <pj/string.h>
-#include <pj/assert.h>
-#include <pj/log.h>
 
 #define THIS_FILE   "codec.c"
 
-/*
- * Reinitialize array of supported codecs.
- */
-static void enum_all_codecs (pjmedia_codec_mgr *mgr)
-{
-    pjmedia_codec_factory *factory;
-
-    mgr->codec_cnt = 0;
-
-    factory = mgr->factory_list.next;
-    while (factory != &mgr->factory_list) {
-	unsigned count;
-	pj_status_t status;
-
-	count = PJ_ARRAY_SIZE(mgr->codecs) - mgr->codec_cnt;
-	status = factory->op->enum_info(factory, &count, 
-					mgr->codecs+mgr->codec_cnt);
-	if (status == PJ_SUCCESS)
-	    mgr->codec_cnt += count;
-
-	factory = factory->next;
-    }
-}
+
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_codec_mgr *mgr);
+
 
 /*
@@ -62,4 +46,5 @@
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -70,11 +55,46 @@
 				    pjmedia_codec_factory *factory)
 {
+    pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+    unsigned i, count;
+    pj_status_t status;
+
     PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
 
+    /* Enum codecs */
+    count = PJ_ARRAY_SIZE(info);
+    status = factory->op->enum_info(factory, &count, info);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+
+    /* Check codec count */
+    if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc))
+	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_codec_info));
+	mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL;
+	mgr->codec_desc[mgr->codec_cnt+i].factory = factory;
+	pjmedia_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);
-    enum_all_codecs (mgr);
+
 
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -85,5 +105,5 @@
 				     pjmedia_codec_factory *factory)
 {
-
+    unsigned i;
     PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL);
 
@@ -92,10 +112,28 @@
 		     PJ_ENOTFOUND);
 
-
+    /* Erase factory from the factory list */
     pj_list_erase(factory);
-    enum_all_codecs (mgr);
+
+
+    /* 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) {
+
+	    pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), 
+			   mgr->codec_cnt, i);
+	    --mgr->codec_cnt;
+
+	} else {
+	    ++i;
+	}
+    }
+
 
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -105,6 +143,9 @@
 pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, 
 			      unsigned *count, 
-			      pjmedia_codec_info codecs[])
-{
+			      pjmedia_codec_info codecs[],
+			      unsigned *prio)
+{
+    unsigned i;
+
     PJ_ASSERT_RETURN(mgr && count && codecs, PJ_EINVAL);
 
@@ -112,23 +153,34 @@
 	*count = mgr->codec_cnt;
     
-    pj_memcpy(codecs, mgr->codecs, *count * sizeof(pjmedia_codec_info));
+    for (i=0; i<*count; ++i) {
+	pj_memcpy(&codecs[i], 
+		  &mgr->codec_desc[i].info, 
+		  sizeof(pjmedia_codec_info));
+    }
+
+    if (prio) {
+	for (i=0; i < *count; ++i)
+	    prio[i] = mgr->codec_desc[i].prio;
+    }
 
     return PJ_SUCCESS;
 }
 
+
 /*
  * Get codec info for static payload type.
  */
-PJ_DEF(pj_status_t) pjmedia_codec_mgr_get_codec_info(pjmedia_codec_mgr *mgr,
-						     unsigned pt,
-						     pjmedia_codec_info *inf)
+PJ_DEF(pj_status_t) 
+pjmedia_codec_mgr_get_codec_info( pjmedia_codec_mgr *mgr,
+				  unsigned pt,
+				  const pjmedia_codec_info **p_info)
 {
     unsigned i;
 
-    PJ_ASSERT_RETURN(mgr && inf && pt>=0 && pt < 96, PJ_EINVAL);
+    PJ_ASSERT_RETURN(mgr && p_info && pt>=0 && pt < 96, PJ_EINVAL);
 
     for (i=0; i<mgr->codec_cnt; ++i) {
-	if (mgr->codecs[i].pt == pt) {
-	    pj_memcpy(inf, &mgr->codecs[i], sizeof(pjmedia_codec_info));
+	if (mgr->codec_desc[i].info.pt == pt) {
+	    *p_info = &mgr->codec_desc[i].info;
 	    return PJ_SUCCESS;
 	}
@@ -137,4 +189,153 @@
     return PJMEDIA_CODEC_EUNSUP;
 }
+
+
+/*
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "L16/44100/2".
+ */
+PJ_DEF(char*) pjmedia_codec_info_to_id( const pjmedia_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/%u", 
+			   (int)info->encoding_name.slen,
+			   info->encoding_name.ptr,
+			   info->clock_rate,
+			   info->channel_cnt);
+
+    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_codec_mgr_find_codecs_by_id( pjmedia_codec_mgr *mgr,
+				     const pj_str_t *codec_id,
+				     unsigned *count,
+				     const pjmedia_codec_info *p_info[],
+				     unsigned prio[])
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(mgr && codec_id && count && *count, PJ_EINVAL);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+
+	if (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;
+	}
+
+    }
+
+    *count = found;
+
+    return found ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+/* Swap two codecs positions in codec manager */
+static void swap_codec(pjmedia_codec_mgr *mgr, unsigned i, unsigned j)
+{
+    struct pjmedia_codec_desc tmp;
+
+    pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(struct pjmedia_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], 
+	       sizeof(struct pjmedia_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(struct pjmedia_codec_desc));
+}
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_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_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, 
+				     const pj_str_t *codec_id,
+				     pjmedia_codec_priority prio)
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(mgr && codec_id, PJ_EINVAL);
+
+    /* 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 = prio;
+	    ++found;
+	}
+    }
+
+    if (!found)
+	return PJ_ENOTFOUND;
+
+    /* Re-sort codecs */
+    sort_codecs(mgr);
+ 
+
+    return PJ_SUCCESS;
+}
+
 
 /*
