summaryrefslogtreecommitdiffstats
path: root/tinyMEDIA/src
diff options
context:
space:
mode:
Diffstat (limited to 'tinyMEDIA/src')
-rw-r--r--tinyMEDIA/src/content/tmedia_content.c362
-rw-r--r--tinyMEDIA/src/content/tmedia_content_cpim.c448
-rw-r--r--tinyMEDIA/src/content/tmedia_content_multipart.c0
-rw-r--r--tinyMEDIA/src/content/tmedia_content_sip_frag.c0
-rw-r--r--tinyMEDIA/src/tmedia.c290
-rw-r--r--tinyMEDIA/src/tmedia_codec.c599
-rw-r--r--tinyMEDIA/src/tmedia_codec_dummy.c365
-rw-r--r--tinyMEDIA/src/tmedia_common.c184
-rw-r--r--tinyMEDIA/src/tmedia_consumer.c284
-rw-r--r--tinyMEDIA/src/tmedia_denoise.c185
-rw-r--r--tinyMEDIA/src/tmedia_params.c190
-rw-r--r--tinyMEDIA/src/tmedia_producer.c301
-rw-r--r--tinyMEDIA/src/tmedia_qos.c861
-rw-r--r--tinyMEDIA/src/tmedia_session.c1304
-rw-r--r--tinyMEDIA/src/tmedia_session_dummy.c472
-rw-r--r--tinyMEDIA/src/tmedia_session_ghost.c141
-rw-r--r--tinyMEDIA/src/tmedia_vad.c0
17 files changed, 5986 insertions, 0 deletions
diff --git a/tinyMEDIA/src/content/tmedia_content.c b/tinyMEDIA/src/content/tmedia_content.c
new file mode 100644
index 0000000..b3522b0
--- /dev/null
+++ b/tinyMEDIA/src/content/tmedia_content.c
@@ -0,0 +1,362 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_content.c
+ * @brief Base content object.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ */
+#include "tinymedia/content/tmedia_content.h"
+
+#include "tsk_memory.h"
+#include "tsk_string.h"
+#include "tsk_debug.h"
+
+#include <string.h>
+
+/**@defgroup tmedia_content_group Contents
+*/
+
+typedef struct tmedia_content_plugin_entry_s
+{
+ const char* type;
+ const tmedia_content_plugin_def_t* plugin;
+}
+tmedia_content_plugin_entry_t;
+
+/* pointer to all registered contents */
+tmedia_content_plugin_entry_t __tmedia_content_plugin_entries[TMEDIA_CONTENT_MAX_PLUGINS][1] = { tsk_null };
+
+
+int tmedia_content_plugin_register(const char* type, const tmedia_content_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ int a = sizeof(__tmedia_content_plugin_entries);
+ if(!plugin || !plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* add or replace the plugin */
+ for(i = 0; i<TMEDIA_CONTENT_MAX_PLUGINS; i++){
+ if(!__tmedia_content_plugin_entries[i]->plugin || (__tmedia_content_plugin_entries[i]->plugin == plugin && tsk_striequals(type, __tmedia_content_plugin_entries[i]->type))){
+ __tmedia_content_plugin_entries[i]->type = type;
+ __tmedia_content_plugin_entries[i]->plugin = plugin;
+ return 0;
+ }
+ }
+
+
+ TSK_DEBUG_ERROR("There are already %d plugins.", TMEDIA_CONTENT_MAX_PLUGINS);
+ return -2;
+}
+
+int tmedia_content_plugin_unregister(const char* type, const tmedia_content_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMEDIA_CONTENT_MAX_PLUGINS && __tmedia_content_plugin_entries[i]->plugin; i++){
+ if(__tmedia_content_plugin_entries[i]->plugin == plugin && tsk_striequals(type, __tmedia_content_plugin_entries[i]->type)){
+ __tmedia_content_plugin_entries[i]->type = tsk_null,
+ __tmedia_content_plugin_entries[i]->plugin = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMEDIA_CONTENT_MAX_PLUGINS - 1); i++){
+ if(__tmedia_content_plugin_entries[i+1]->plugin){
+ __tmedia_content_plugin_entries[i]->type = __tmedia_content_plugin_entries[i+1]->type,
+ __tmedia_content_plugin_entries[i]->plugin = __tmedia_content_plugin_entries[i+1]->plugin;
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_content_plugin_entries[i]->type = tsk_null,
+ __tmedia_content_plugin_entries[i]->plugin = tsk_null;
+ }
+ return (found ? 0 : -2);
+}
+
+int tmedia_content_plugin_unregister_all()
+{
+ tsk_size_t i;
+ for(i = 0; i<TMEDIA_CONTENT_MAX_PLUGINS && __tmedia_content_plugin_entries[i]->plugin; i++){
+ __tmedia_content_plugin_entries[i]->type = tsk_null,
+ __tmedia_content_plugin_entries[i]->plugin = tsk_null;
+ }
+ return 0;
+}
+
+tmedia_content_t* tmedia_content_create(const char* type)
+{
+ tmedia_content_t* content = tsk_null;
+ const tmedia_content_plugin_entry_t* entry;
+ tsk_size_t i = 0;
+
+ while(i < TMEDIA_CONTENT_MAX_PLUGINS){
+ entry = __tmedia_content_plugin_entries[i];
+ if(!entry->plugin || !entry->type){
+ break;
+ }
+ if(entry->plugin->objdef && tsk_striequals(entry->type, type)){
+ if((content = tsk_object_new(entry->plugin->objdef))){
+ content->plugin = entry->plugin;
+ content->type = entry->type;
+ return content;
+ }
+ }
+ ++i;
+ }
+
+ TSK_DEBUG_WARN("Failed to find content type (%s) will be added as dummy", type);
+ if(tmedia_content_dummy_plugin_def_t){
+ content = tsk_object_new(tmedia_content_dummy_plugin_def_t->objdef);
+ content->plugin = tmedia_content_dummy_plugin_def_t;
+ content->type = type;
+ }
+
+ return content;
+}
+
+tmedia_content_t* tmedia_content_parse(const void* data, tsk_size_t size, const char* type)
+{
+ tmedia_content_t* content = tmedia_content_create(type);
+ if(content){
+ if(content->plugin->parse){
+ int ret;
+ if((ret = content->plugin->parse(content, data, size))){
+ TSK_DEBUG_ERROR("Failed to parse the content(%d)", ret);
+ TSK_OBJECT_SAFE_FREE(content);
+ return tsk_null;
+ }
+ return content;
+ }
+ else{
+ TSK_DEBUG_ERROR("No parser function for this content (%s)", type);
+ TSK_OBJECT_SAFE_FREE(content);
+ return tsk_null;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to to find content(%s)", type);
+ return tsk_null;
+ }
+}
+
+int tmedia_content_init(tmedia_content_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tmedia_content_deinit(tmedia_content_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return 0;
+}
+
+tsk_buffer_t* tmedia_content_get_data(tmedia_content_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+ return self->plugin->get_data(self);
+}
+
+
+
+
+static int tmedia_content_dummy_parse(tmedia_content_t* self, const void* in_data, tsk_size_t in_size)
+{
+ tmedia_content_dummy_t *dummy = TMEDIA_CONTENT_DUMMY(self);
+ if(!dummy || dummy->data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ dummy->data = tsk_buffer_create(in_data, in_size);
+ return 0;
+}
+
+static tsk_buffer_t* tmedia_content_dummy_get_data(tmedia_content_t* self)
+{
+ return tsk_object_ref(TMEDIA_CONTENT_DUMMY(self)->data);
+}
+
+
+
+
+
+
+//=================================================================================================
+// object/plugin definitions
+//
+/* constructor */
+static tsk_object_t* tmedia_content_dummy_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_content_dummy_t *dummy = self;
+ if(dummy){
+ /* init base: called by tmedia_content_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_content_dummy_dtor(tsk_object_t * self)
+{
+ tmedia_content_dummy_t *dummy = self;
+ if(dummy){
+ /* deinit base */
+ tmedia_content_deinit(TMEDIA_CONTENT(dummy));
+ /* deinit self */
+ TSK_OBJECT_SAFE_FREE(dummy->data);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_content_dummy_def_s =
+{
+ sizeof(tmedia_content_dummy_t),
+ tmedia_content_dummy_ctor,
+ tmedia_content_dummy_dtor,
+ tsk_null,
+};
+/* plugin definition*/
+static const tmedia_content_plugin_def_t tmedia_content_dummy_plugin_def_s =
+{
+ &tmedia_content_dummy_def_s,
+
+ "dummy",
+ tmedia_content_dummy_parse,
+ tmedia_content_dummy_get_data
+};
+const tmedia_content_plugin_def_t *tmedia_content_dummy_plugin_def_t = &tmedia_content_dummy_plugin_def_s;
+
+
+
+//=================================================================================================
+// media content header
+//
+
+tmedia_content_header_t* tmedia_content_header_create(const char* name, const char* value)
+{
+ tmedia_content_header_t* header = tsk_object_new(tmedia_content_header_def_t);
+ const char* str;
+
+ if(!header){
+ TSK_DEBUG_ERROR("Failed to create new header object");
+ return tsk_null;
+ }
+ header->name = tsk_strdup(name);
+ if((str = strstr(value, ";"))){
+ header->value = tsk_strndup(value, (str - value));
+ header->params = tsk_params_fromstring((str + 1), ";", tsk_true);
+ }
+ else{
+ header->value = tsk_strdup(value);
+ }
+
+ return header;
+}
+
+int tmedia_content_header_deinit(tmedia_content_header_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TSK_FREE(self->name);
+ TSK_FREE(self->value);
+ TSK_OBJECT_SAFE_FREE(self->params);
+
+ return 0;
+}
+
+char* tmedia_content_header_tostring(const tmedia_content_header_t* self)
+{
+ char* string = tsk_null;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ tsk_sprintf(&string, "%s: %s", self->name, self->value);
+ if(self->params){
+ const tsk_list_item_t* item;
+ tsk_list_foreach(item, self->params){
+ tsk_strcat_2(&string, ";%s=%s", TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value);
+ }
+ }
+
+ return string;
+}
+
+/* constructor */
+static tsk_object_t* tmedia_content_header_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_content_header_t *header = self;
+ if(header){
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_content_header_dtor(tsk_object_t * self)
+{
+ tmedia_content_header_t *header = self;
+ if(header){
+ tmedia_content_header_deinit(header);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_content_header_def_s =
+{
+ sizeof(tmedia_content_header_t),
+ tmedia_content_header_ctor,
+ tmedia_content_header_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tmedia_content_header_def_t = &tmedia_content_header_def_s;
diff --git a/tinyMEDIA/src/content/tmedia_content_cpim.c b/tinyMEDIA/src/content/tmedia_content_cpim.c
new file mode 100644
index 0000000..66ec103
--- /dev/null
+++ b/tinyMEDIA/src/content/tmedia_content_cpim.c
@@ -0,0 +1,448 @@
+
+/* #line 1 "./ragel/tmedia_content_cpim.rl" */
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_content_cpim.c
+ * @brief Common Presence and Instant Messaging (CPIM): Message Format (RFC 3862)
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ */
+#include "tinymedia/content/tmedia_content_cpim.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+#include "tsk_string.h"
+#include "tsk_ragel_state.h"
+
+#include <string.h>
+
+/* RFC 3862 - 2. Overall Message Structure
+A complete message looks something like this:
+
+ m: Content-type: Message/CPIM
+ s:
+ h: (message-metadata-headers)
+ s:
+ e: (encapsulated MIME message-body)
+
+ The end of the message body is defined by the framing mechanism of
+ the protocol used. The tags 'm:', 's:', 'h:', 'e:', and 'x:' are not
+ part of the message format and are used here to indicate the
+ different parts of the message, thus:
+
+ m: MIME headers for the overall message
+ s: a blank separator line
+ h: message headers
+ e: encapsulated MIME object containing the message content
+ x: MIME security multipart message wrapper
+*/
+
+
+
+
+
+/***********************************
+* Ragel state machine.
+*/
+
+/* #line 129 "./ragel/tmedia_content_cpim.rl" */
+
+
+
+static int tmedia_content_cpim_parse(tmedia_content_t* self, const void* in_data, tsk_size_t in_size)
+{
+ int cs = 0;
+ const char *p = in_data;
+ const char *pe = p + in_size;
+ const char *eof = pe;
+
+ const char *tag_start;
+
+ char* hname = tsk_null;
+ char* hvalue = tsk_null;
+ tsk_bool_t parsing_mime_headers = tsk_true;
+
+
+/* #line 88 "./src/content/tmedia_content_cpim.c" */
+static const char _tmedia_machine_content_cpim_actions[] = {
+ 0, 1, 0, 1, 3, 1, 4, 1,
+ 5, 2, 0, 4, 2, 0, 5, 2,
+ 1, 0, 2, 2, 0
+};
+
+static const char _tmedia_machine_content_cpim_key_offsets[] = {
+ 0, 0, 14, 30, 32, 34, 35, 36,
+ 51, 52, 66, 82, 84, 86, 87, 88,
+ 103, 104, 104
+};
+
+static const char _tmedia_machine_content_cpim_trans_keys[] = {
+ 33, 37, 39, 126, 42, 43, 45, 46,
+ 48, 57, 65, 90, 95, 122, 32, 33,
+ 37, 39, 58, 126, 42, 43, 45, 46,
+ 48, 57, 65, 90, 95, 122, 32, 58,
+ 13, 32, 13, 10, 13, 33, 37, 39,
+ 126, 42, 43, 45, 46, 48, 57, 65,
+ 90, 95, 122, 10, 33, 37, 39, 126,
+ 42, 43, 45, 46, 48, 57, 65, 90,
+ 95, 122, 32, 33, 37, 39, 58, 126,
+ 42, 43, 45, 46, 48, 57, 65, 90,
+ 95, 122, 32, 58, 13, 32, 13, 10,
+ 13, 33, 37, 39, 126, 42, 43, 45,
+ 46, 48, 57, 65, 90, 95, 122, 10,
+ 0
+};
+
+static const char _tmedia_machine_content_cpim_single_lengths[] = {
+ 0, 4, 6, 2, 2, 1, 1, 5,
+ 1, 4, 6, 2, 2, 1, 1, 5,
+ 1, 0, 0
+};
+
+static const char _tmedia_machine_content_cpim_range_lengths[] = {
+ 0, 5, 5, 0, 0, 0, 0, 5,
+ 0, 5, 5, 0, 0, 0, 0, 5,
+ 0, 0, 0
+};
+
+static const char _tmedia_machine_content_cpim_index_offsets[] = {
+ 0, 0, 10, 22, 25, 28, 30, 32,
+ 43, 45, 55, 67, 70, 73, 75, 77,
+ 88, 90, 91
+};
+
+static const char _tmedia_machine_content_cpim_indicies[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 3, 3, 4, 3,
+ 3, 3, 3, 3, 3, 1, 5, 6,
+ 1, 8, 6, 7, 10, 9, 11, 1,
+ 12, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 1, 14, 1, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 1, 16,
+ 17, 17, 17, 18, 17, 17, 17, 17,
+ 17, 17, 1, 19, 20, 1, 22, 20,
+ 21, 24, 23, 25, 1, 26, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 1,
+ 28, 1, 29, 30, 0
+};
+
+static const char _tmedia_machine_content_cpim_trans_targs[] = {
+ 2, 0, 3, 2, 4, 3, 4, 5,
+ 6, 5, 6, 7, 8, 2, 9, 10,
+ 11, 10, 12, 11, 12, 13, 14, 13,
+ 14, 15, 16, 10, 17, 18, 18
+};
+
+static const char _tmedia_machine_content_cpim_trans_actions[] = {
+ 15, 0, 3, 0, 3, 0, 0, 1,
+ 9, 0, 5, 0, 0, 1, 0, 18,
+ 3, 0, 3, 0, 0, 1, 9, 0,
+ 5, 0, 0, 1, 0, 1, 0
+};
+
+static const char _tmedia_machine_content_cpim_eof_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 7
+};
+
+static const int tmedia_machine_content_cpim_start = 1;
+static const int tmedia_machine_content_cpim_first_final = 17;
+static const int tmedia_machine_content_cpim_error = 0;
+
+static const int tmedia_machine_content_cpim_en_main = 1;
+
+
+/* #line 146 "./ragel/tmedia_content_cpim.rl" */
+
+/* #line 180 "./src/content/tmedia_content_cpim.c" */
+ {
+ cs = tmedia_machine_content_cpim_start;
+ }
+
+/* #line 147 "./ragel/tmedia_content_cpim.rl" */
+
+/* #line 187 "./src/content/tmedia_content_cpim.c" */
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _tmedia_machine_content_cpim_trans_keys + _tmedia_machine_content_cpim_key_offsets[cs];
+ _trans = _tmedia_machine_content_cpim_index_offsets[cs];
+
+ _klen = _tmedia_machine_content_cpim_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _tmedia_machine_content_cpim_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += ((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _tmedia_machine_content_cpim_indicies[_trans];
+ cs = _tmedia_machine_content_cpim_trans_targs[_trans];
+
+ if ( _tmedia_machine_content_cpim_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _tmedia_machine_content_cpim_actions + _tmedia_machine_content_cpim_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+/* #line 72 "./ragel/tmedia_content_cpim.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 1:
+/* #line 76 "./ragel/tmedia_content_cpim.rl" */
+ {
+ parsing_mime_headers = tsk_true;
+ }
+ break;
+ case 2:
+/* #line 80 "./ragel/tmedia_content_cpim.rl" */
+ {
+ parsing_mime_headers = tsk_false;
+ }
+ break;
+ case 3:
+/* #line 84 "./ragel/tmedia_content_cpim.rl" */
+ {
+ TSK_PARSER_SET_STRING(hname);
+ }
+ break;
+ case 4:
+/* #line 88 "./ragel/tmedia_content_cpim.rl" */
+ {
+ tmedia_content_header_t* header;
+ TSK_PARSER_SET_STRING(hvalue);
+ header = tmedia_content_header_create(hname, hvalue);
+ TSK_FREE(hname); TSK_FREE(hvalue);
+
+ if(parsing_mime_headers){
+ if(!TMEDIA_CONTENT_CPIM(self)->m_headers){
+ TMEDIA_CONTENT_CPIM(self)->m_headers = tsk_list_create();
+ }
+ tsk_list_push_back_data(TMEDIA_CONTENT_CPIM(self)->m_headers, (void**)&header);
+ }
+ else{
+ if(!TMEDIA_CONTENT_CPIM(self)->h_headers){
+ TMEDIA_CONTENT_CPIM(self)->h_headers = tsk_list_create();
+ }
+ tsk_list_push_back_data(TMEDIA_CONTENT_CPIM(self)->h_headers, (void**)&header);
+ }
+ }
+ break;
+/* #line 307 "./src/content/tmedia_content_cpim.c" */
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ const char *__acts = _tmedia_machine_content_cpim_actions + _tmedia_machine_content_cpim_eof_actions[cs];
+ unsigned int __nacts = (unsigned int) *__acts++;
+ while ( __nacts-- > 0 ) {
+ switch ( *__acts++ ) {
+ case 0:
+/* #line 72 "./ragel/tmedia_content_cpim.rl" */
+ {
+ tag_start = p;
+ }
+ break;
+ case 5:
+/* #line 108 "./ragel/tmedia_content_cpim.rl" */
+ {
+ int len = (int)(p - tag_start);
+ if(len && tag_start){
+ if(TMEDIA_CONTENT_CPIM(self)->e){
+ TSK_OBJECT_SAFE_FREE(TMEDIA_CONTENT_CPIM(self)->e); \
+ }
+ TMEDIA_CONTENT_CPIM(self)->e = tsk_buffer_create(tag_start, len);
+ }
+ }
+ break;
+/* #line 341 "./src/content/tmedia_content_cpim.c" */
+ }
+ }
+ }
+
+ _out: {}
+ }
+
+/* #line 148 "./ragel/tmedia_content_cpim.rl" */
+
+ TSK_FREE(hname);
+ TSK_FREE(hvalue);
+
+ if( cs <
+/* #line 355 "./src/content/tmedia_content_cpim.c" */
+17
+/* #line 152 "./ragel/tmedia_content_cpim.rl" */
+ ){
+ TSK_DEBUG_ERROR("Failed to parse CPIM content");
+ return -1;
+ }
+
+ return 0;
+}
+
+static tsk_buffer_t* tmedia_content_cpim_get_data(tmedia_content_t* self)
+{
+ tsk_buffer_t* data = tsk_buffer_create_null();
+ tmedia_content_cpim_t *cpim = TMEDIA_CONTENT_CPIM(self);
+ const tsk_list_item_t* item;
+ /*
+ m: Content-type: Message/CPIM
+ s:
+ h: (message-metadata-headers)
+ s:
+ e: (encapsulated MIME message-body)
+ x: MIME security multipart message wrapper
+ */
+ if(cpim->m_headers){
+ tsk_list_foreach(item, cpim->m_headers){
+ char* hstring = tmedia_content_header_tostring(TMEDIA_CONTENT_HEADER(item->data));
+ tsk_buffer_append_2(data, TSK_LIST_IS_LAST(cpim->m_headers, item) ? "%s\r\n\r\n" : "%s\r\n", hstring);
+ TSK_FREE(hstring);
+ }
+ }
+ if(cpim->h_headers){
+ tsk_list_foreach(item, cpim->h_headers){
+ char* hstring = tmedia_content_header_tostring(TMEDIA_CONTENT_HEADER(item->data));
+ tsk_buffer_append_2(data, TSK_LIST_IS_LAST(cpim->h_headers, item) ? "%s\r\n\r\n" : "%s\r\n", hstring);
+ TSK_FREE(hstring);
+ }
+ }
+ if(cpim->e){
+ tsk_buffer_append(data, TSK_BUFFER_DATA(cpim->e), TSK_BUFFER_SIZE(cpim->e));
+ }
+ if(cpim->x){
+ tsk_buffer_append(data, TSK_BUFFER_DATA(cpim->x), TSK_BUFFER_SIZE(cpim->x));
+ }
+
+ return data;
+}
+
+//=================================================================================================
+// object/plugin definitions
+//
+/* constructor */
+static tsk_object_t* tmedia_content_cpim_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_content_cpim_t *cpim = self;
+ if(cpim){
+ /* init base: called by tmedia_content_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_content_cpim_dtor(tsk_object_t * self)
+{
+ tmedia_content_cpim_t *cpim = self;
+ if(cpim){
+ /* deinit base */
+ tmedia_content_deinit(TMEDIA_CONTENT(cpim));
+ /* deinit self */
+ TSK_OBJECT_SAFE_FREE(cpim->m_headers);
+ TSK_OBJECT_SAFE_FREE(cpim->h_headers);
+ TSK_OBJECT_SAFE_FREE(cpim->e);
+ TSK_OBJECT_SAFE_FREE(cpim->x);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_content_cpim_def_s =
+{
+ sizeof(tmedia_content_cpim_t),
+ tmedia_content_cpim_ctor,
+ tmedia_content_cpim_dtor,
+ tsk_null,
+};
+/* plugin definition*/
+static const tmedia_content_plugin_def_t tmedia_content_cpim_plugin_def_s =
+{
+ &tmedia_content_cpim_def_s,
+
+ TMEDIA_CONTENT_CPIM_TYPE,
+ tmedia_content_cpim_parse,
+ tmedia_content_cpim_get_data
+};
+const tmedia_content_plugin_def_t *tmedia_content_cpim_plugin_def_t = &tmedia_content_cpim_plugin_def_s; \ No newline at end of file
diff --git a/tinyMEDIA/src/content/tmedia_content_multipart.c b/tinyMEDIA/src/content/tmedia_content_multipart.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyMEDIA/src/content/tmedia_content_multipart.c
diff --git a/tinyMEDIA/src/content/tmedia_content_sip_frag.c b/tinyMEDIA/src/content/tmedia_content_sip_frag.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyMEDIA/src/content/tmedia_content_sip_frag.c
diff --git a/tinyMEDIA/src/tmedia.c b/tinyMEDIA/src/tmedia.c
new file mode 100644
index 0000000..dd57b47
--- /dev/null
+++ b/tinyMEDIA/src/tmedia.c
@@ -0,0 +1,290 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia.c
+ * @brief Media.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia.h"
+
+#if 0
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+#define TMED_MAX_PLUGINS 10
+const tmedia_plugin_def_t* __tmedia_plugins[TMED_MAX_PLUGINS] = {0};
+
+
+tmedia_t* tmedia_create(const char* name, const char* host, tnet_socket_type_t socket_type)
+{
+ return tsk_object_new(TMEDIA_VA_ARGS(name, host, socket_type));
+}
+
+tmedia_t* tmedia_create_null()
+{
+ return tmedia_create(tsk_null, TNET_SOCKET_HOST_ANY, tnet_socket_type_invalid);
+}
+
+int tmedia_init(tmedia_t* self, const char* name)
+{
+ if(!self){
+ return -1;
+ }
+
+ tsk_strupdate(&self->name, name);
+
+ return 0;
+}
+
+int tmedia_deinit(tmedia_t* self)
+{
+ if(!self){
+ return -1;
+ }
+
+ TSK_FREE(self->name);
+ TSK_FREE(self->protocol);
+
+ return 0;
+}
+
+
+int tmedia_plugin_register(const tmedia_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ for(i=0; i<TMED_MAX_PLUGINS; i++){
+ if(!__tmedia_plugins[i] || __tmedia_plugins[i] == plugin){
+ __tmedia_plugins[i] = plugin;
+ return 0;
+ }
+ }
+
+ TSK_DEBUG_ERROR("There are already %d plugins.", TMED_MAX_PLUGINS);
+ return -2;
+}
+
+tmedia_t* tmedia_factory_create(const char* name, const char* host, tnet_socket_type_t socket_type)
+{
+ tmedia_t* ret = tsk_null;
+ const tmedia_plugin_def_t* plugin;
+ tsk_size_t i = 0;
+
+ while((i < TMED_MAX_PLUGINS) && (plugin = __tmedia_plugins[i++])){
+ if(plugin->objdef && tsk_strequals(plugin->name, name)){
+ if((ret = tsk_object_new(plugin->objdef, name, host, socket_type))){
+ ret->plugin = plugin;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int tmedia_start(tmedia_t* self)
+{
+ if(!self || !self->plugin){
+ return -1;
+ }
+
+ if(!self->plugin->start){
+ return -2;
+ }
+ else{
+ return self->plugin->start(self);
+ }
+}
+int tmedia_pause(tmedia_t*self )
+{
+ if(!self || !self->plugin){
+ return -1;
+ }
+
+ if(!self->plugin->pause){
+ return -2;
+ }
+ else{
+ return self->plugin->pause(self);
+ }
+}
+int tmedia_stop(tmedia_t* self)
+{
+ if(!self || !self->plugin){
+ return -1;
+ }
+
+ if(!self->plugin->stop){
+ return -2;
+ }
+ else{
+ return self->plugin->stop(self);
+ }
+}
+
+// Only SDP headers
+const tsdp_header_M_t* tmedia_get_local_offer(tmedia_t* self, ...)
+{
+
+ if(!self || !self->plugin){
+ return tsk_null;
+ }
+
+ if(!self->plugin->get_local_offer){
+ return tsk_null;
+ }
+ else{
+ va_list ap;
+ const tsdp_header_M_t* M;
+ va_start(ap, self);
+ M = self->plugin->get_local_offer(self, &ap);
+ va_end(ap);
+ return M;
+ }
+}
+
+const tsdp_header_M_t* tmedia_get_negotiated_offer(tmedia_t* self)
+{
+ if(!self || !self->plugin){
+ return tsk_null;
+ }
+
+ if(!self->plugin->get_negotiated_offer){
+ return tsk_null;
+ }
+ else{
+ return self->plugin->get_negotiated_offer(self);
+ }
+}
+
+int tmedia_set_remote_offer(tmedia_t* self, const tsdp_message_t* offer)
+{
+ if(!self || !self->plugin){
+ return -1;
+ }
+
+ if(!self->plugin->set_remote_offer){
+ return -2;
+ }
+ else{
+ return self->plugin->set_remote_offer(self, offer);
+ }
+}
+
+int tmedia_perform(tmedia_t* self, tmedia_action_t action, ... )
+{
+ int ret = -1;
+
+ if(!self || !self->plugin){
+ return -1;
+ }
+
+ if(!self->plugin->perform){
+ return -2;
+ }
+ else{
+ const tsk_object_def_t* objdef;
+ tsk_param_t *param;
+ tsk_params_L_t* params;
+ va_list ap;
+
+ va_start(ap, action);
+ params = tsk_list_create();
+ while((objdef = va_arg(ap, const tsk_object_def_t*))){
+ if(objdef != tsk_param_def_t){ // sanity check
+ break;
+ }
+ if((param = tsk_object_new_2(objdef, &ap))){
+ tsk_params_add_param_2(&params, param);
+ TSK_OBJECT_SAFE_FREE(param);
+ }
+ }
+
+ // Perform
+ ret = self->plugin->perform(self, action, params);
+
+ TSK_OBJECT_SAFE_FREE(params);
+ va_end(ap);
+
+ return ret;
+ }
+}
+
+//========================================================
+// Media object definition
+//
+
+static void* tmedia_ctor(tsk_object_t *self, va_list * app)
+{
+ tmedia_t *media = self;
+ if(media){
+ const char* name = va_arg(*app, const char*);
+ const char* host = va_arg(*app, const char*);
+ tnet_socket_type_t socket_type = va_arg(*app, tnet_socket_type_t);
+
+ tmedia_init(media, name);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create new media.");
+ }
+ return self;
+}
+
+static void* tmedia_dtor(tsk_object_t *self)
+{
+ tmedia_t *media = self;
+ if(media){
+ tmedia_deinit(media);
+ }
+ else{
+ TSK_DEBUG_ERROR("Null media.");
+ }
+
+ return self;
+}
+
+static int tmedia_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
+{
+ return -1;
+}
+
+static const tsk_object_def_t tmedia_def_s =
+{
+ sizeof(tmedia_t),
+ tmedia_ctor,
+ tmedia_dtor,
+ tmedia_cmp
+};
+
+const void *tmedia_def_t = &tmedia_def_s;
+
+#endif /* if 0 => FIXME: Remove this file */
+
diff --git a/tinyMEDIA/src/tmedia_codec.c b/tinyMEDIA/src/tmedia_codec.c
new file mode 100644
index 0000000..d0f80ef
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_codec.c
@@ -0,0 +1,599 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_codec.c
+ * @brief Base codec object.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_codec.h"
+
+#include "tsk_string.h"
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/**@defgroup tmedia_codec_group Codecs
+*/
+
+/* pointer to all registered codecs */
+const tmedia_codec_plugin_def_t* __tmedia_codec_plugins[TMED_CODEC_MAX_PLUGINS] = {0};
+
+
+/*== Predicate function to find a codec object by format */
+static int __pred_find_codec_by_format(const tsk_list_item_t *item, const void *format)
+{
+ if(item && item->data){
+ return tsk_strcmp(((tmedia_codec_t *)item->data)->format, format);
+ }
+ return -1;
+}
+
+/*== Predicate function to find a codec object by negociated format */
+static int __pred_find_codec_by_neg_format(const tsk_list_item_t *item, const void *format)
+{
+ if(item && item->data){
+ return tsk_strcmp(((tmedia_codec_t *)item->data)->neg_format, format);
+ }
+ return -1;
+}
+
+/**@ingroup tmedia_codec_group
+* Initialize a Codec
+* @param self The codec to initialize. Could be any type of codec (e.g. @ref tmedia_codec_audio_t or @ref tmedia_codec_video_t).
+* @param type
+* @param name the name of the codec. e.g. "G.711u" or "G.711a" etc used in the sdp.
+* @param desc full description.
+* @param format the format. e.g. "0" for G.711.u or "8" for G.711a or "*" for MSRP.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_codec_init(tmedia_codec_t* self, tmedia_type_t type, const char* name, const char* desc, const char* format)
+{
+ if(!self || tsk_strnullORempty(name)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ self->type = type;
+ tsk_strupdate(&self->name, name);
+ tsk_strupdate(&self->desc,desc);
+ tsk_strupdate(&self->format, format);
+
+ return 0;
+}
+
+/**@ingroup tmedia_codec_group
+* Prepares a codec by opening it.
+* @param self The codec to open.
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tmedia_codec_close()
+*/
+int tmedia_codec_open(tmedia_codec_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(self->opened){
+ TSK_DEBUG_WARN("Codec already opened");
+ return 0;
+ }
+
+ if(self->plugin->open){
+ int ret;
+ if((ret = self->plugin->open(self))){
+ TSK_DEBUG_ERROR("Failed to open [%s] codec", self->plugin->desc);
+ return ret;
+ }
+ else{
+ self->opened = tsk_true;
+ return 0;
+ }
+ }
+ else{
+ self->opened = tsk_true;
+ return 0;
+ }
+}
+
+/**@ingroup tmedia_codec_group
+* UnPrepares a codec by closing it.
+* @param self The codec to close.
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tmedia_codec_open()
+*/
+int tmedia_codec_close(tmedia_codec_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(!self->opened){
+ TSK_DEBUG_WARN("Codec not opened");
+ return 0;
+ }
+
+ if(self->plugin->close){
+ int ret;
+
+ if((ret = self->plugin->close(self))){
+ TSK_DEBUG_ERROR("Failed to close [%s] codec", self->plugin->desc);
+ return ret;
+ }
+ else{
+ self->opened = tsk_false;
+ return 0;
+ }
+ }
+ else{
+ self->opened = tsk_false;
+ return 0;
+ }
+}
+
+/**@ingroup tmedia_codec_group
+* Generic function to compare two codecs.
+* @param codec1 The first codec to compare.
+* @param codec2 The second codec to compare.
+* @retval Returns an integral value indicating the relationship between the two codecs:
+* <0 : @a codec1 less than @a codec2.<br>
+* 0 : @a codec1 identical to @a codec2.<br>
+* >0 : @a codec1 greater than @a codec2.<br>
+*/
+int tmedia_codec_cmp(const tsk_object_t* codec1, const tsk_object_t* codec2)
+{
+ const tmedia_codec_t* _c1 = codec1;
+ const tmedia_codec_t* _c2 = codec2;
+
+ if((_c1 && _c2) && (_c1->type == _c2->type)){
+ /* Do not compare names. For example, H264 base profile 1.0 will have the
+ * same name than H264 base profile 3.0. */
+ return tsk_stricmp(_c1->format, _c2->format);
+ }
+ else{
+ return -1;
+ }
+}
+
+/**@ingroup tmedia_codec_group
+* Registers a codec plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tmedia_codec_create()
+*/
+int tmedia_codec_plugin_register(const tmedia_codec_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ if(!plugin || tsk_strnullORempty(plugin->name) || tsk_strnullORempty(plugin->format)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* add or replace the plugin */
+ for(i = 0; i<TMED_CODEC_MAX_PLUGINS; i++){
+ if(!__tmedia_codec_plugins[i] || (__tmedia_codec_plugins[i] == plugin)){
+ __tmedia_codec_plugins[i] = plugin;
+ return 0;
+ }
+ }
+
+ TSK_DEBUG_ERROR("There are already %d plugins.", TMED_CODEC_MAX_PLUGINS);
+ return -2;
+}
+
+/**@ingroup tmedia_codec_group
+* UnRegisters a codec plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_codec_plugin_unregister(const tmedia_codec_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMED_CODEC_MAX_PLUGINS && __tmedia_codec_plugins[i]; i++){
+ if(__tmedia_codec_plugins[i] == plugin){
+ __tmedia_codec_plugins[i] = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMED_CODEC_MAX_PLUGINS - 1); i++){
+ if(__tmedia_codec_plugins[i+1]){
+ __tmedia_codec_plugins[i] = __tmedia_codec_plugins[i+1];
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_codec_plugins[i] = tsk_null;
+ }
+ return (found ? 0 : -2);
+}
+
+/**@ingroup tmedia_codec_group
+* Creates a new codec using an already registered plugin.
+* @param format The format of the codec to create (e.g. "0" for PCMU or "8" for PCMA or "*" for MSRP)
+* @sa @ref tmedia_codec_plugin_register()
+*/
+tmedia_codec_t* tmedia_codec_create(const char* format)
+{
+ tmedia_codec_t* codec = tsk_null;
+ const tmedia_codec_plugin_def_t* plugin;
+ tsk_size_t i = 0;
+
+ while((i < TMED_CODEC_MAX_PLUGINS) && (plugin = __tmedia_codec_plugins[i++])){
+ if(plugin->objdef && tsk_striequals(plugin->format, format)){
+ if((codec = tsk_object_new(plugin->objdef))){
+ /* initialize the newly created codec */
+ codec->dyn = plugin->dyn;
+ codec->plugin = plugin;
+ codec->bl = tmedia_bl_medium;
+ switch(plugin->type){
+ case tmedia_audio:
+ { /* Audio codec */
+ tmedia_codec_audio_t* audio = TMEDIA_CODEC_AUDIO(codec);
+ tmedia_codec_audio_init(TMEDIA_CODEC(audio), plugin->name, plugin->desc, plugin->format);
+ break;
+ }
+ case tmedia_video:
+ { /* Video codec */
+ tmedia_codec_video_t* video = TMEDIA_CODEC_VIDEO(codec);
+ tmedia_codec_video_init(TMEDIA_CODEC(video), plugin->name, plugin->desc, plugin->format);
+ video->width = plugin->video.width;
+ video->height = plugin->video.height;
+ video->fps = plugin->video.fps;
+ break;
+ }
+ case tmedia_msrp:
+ { /* Msrp codec */
+ tmedia_codec_msrp_init(codec, plugin->name, plugin->desc);
+ break;
+ }
+ default:
+ { /* Any other codec */
+ tmedia_codec_init(codec, plugin->type, plugin->name, plugin->desc, plugin->format);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return codec;
+}
+
+/**@ingroup tmedia_codec_group
+* Gets the rtpmap attribute associated to this code.
+* @param self the codec for which to get the rtpmap attribute. Should be created using @ref tmedia_codec_create().
+* @retval rtpmap string (e.g. "AMR-WB/16000/2" or "H261/90000") if succeed and Null otherwise. It's up to the caller to free the
+* returned string.
+*/
+char* tmedia_codec_get_rtpmap(const tmedia_codec_t* self)
+{
+ char* rtpmap = tsk_null;
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return tsk_null;
+ }
+ switch(self->type){
+ case tmedia_audio:
+ { /* audio codecs */
+ /* const tmedia_codec_audio_t* audioCodec = (const tmedia_codec_audio_t*)self; */
+ if(self->plugin->audio.channels > 0){
+ tsk_sprintf(&rtpmap, "%s %s/%d/%d", self->neg_format? self->neg_format : self->format, self->name, self->plugin->rate, self->plugin->audio.channels);
+ }
+ else{
+ tsk_sprintf(&rtpmap, "%s %s/%d", self->neg_format? self->neg_format : self->format, self->name, self->plugin->audio);
+ }
+ }
+ break;
+ case tmedia_video:
+ { /* video codecs */
+ /* const tmedia_codec_video_t* videoCodec = (const tmedia_codec_video_t*)self; */
+ tsk_sprintf(&rtpmap, "%s %s/%d", self->neg_format? self->neg_format : self->format, self->name, self->plugin->rate);
+ break;
+ }
+ /* all others */
+ default:
+ break;
+ }
+
+ return rtpmap;
+}
+
+/**@ingroup tmedia_codec_group
+* Gets the codec's fmtp attribute value.
+* @param self the codec for which to get the fmtp attribute. Should be created using @ref tmedia_codec_create().
+* @retval fmtp attribute string (e.g. "mode-set=0,2,5,7; mode-change-period=2; mode-change-neighbor=1"). It's up to the caller to free the
+* returned string.
+*/
+char* tmedia_codec_get_fmtp(const tmedia_codec_t* self)
+{
+ char* fmtp = tsk_null;
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return tsk_null;
+ }
+
+ if(self->plugin->fmtp_get){ /* some codecs, like G711, won't produce fmtp */
+ fmtp = self->plugin->fmtp_get(self);
+ }
+
+ return fmtp;
+}
+
+/**@ingroup tmedia_codec_group
+* Indicates whether the codec can handle this fmtp.
+* @param self the codec to match aginst to.
+* @param fmtp the fmtp to match
+* @retval @a tsk_true if the codec can handle this fmtp and @a tsk_false otherwise
+*/
+tsk_bool_t tmedia_codec_match_fmtp(const tmedia_codec_t* self, const char* fmtp)
+{
+ /* checks */
+ if(!self || !self->plugin || !self->plugin->fmtp_match){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return tsk_false;
+ }
+
+ /* if fmtp is null or empty -> always match */
+ if(tsk_strnullORempty(fmtp)){
+ return tsk_true;
+ }
+ else{
+ return self->plugin->fmtp_match(self, fmtp);
+ }
+}
+
+/**@ingroup tmedia_codec_group
+* Sets remote fmtp.
+* @param self codec for which to set the remote fmtp.
+* @param fmtp fmtp received from remote party (e.g. "mode-set=0,2,5,7; mode-change-period=2; mode-change-neighbor=1").
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_codec_set_remote_fmtp(tmedia_codec_t* self, const char* fmtp)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return -1;
+ }
+
+ if(self->plugin->fmtp_set){
+ return self->plugin->fmtp_set(self, fmtp);
+ }
+ else{ /* some codecs, like G711, could ignore remote fmtp attribute */
+ return 0;
+ }
+}
+
+/**@ingroup tmedia_codec_group
+* Remove all codecs except the specified ones.
+* @param codecs the list of codecs from which to remove codecs.
+* @param codecs2keep the codecs which shall not be removed.
+* @retval zero if succeed (or nothing to do) and non-zero error code otherwise.
+*/
+int tmedia_codec_removeAll_exceptThese(tmedia_codecs_L_t* codecs, const tmedia_codecs_L_t * codecs2keep)
+{
+ tsk_list_item_t* item;
+ if(!codecs || !codecs2keep){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+again:
+ tsk_list_foreach(item, codecs){
+ if(!tsk_list_find_item_by_pred(codecs2keep, __pred_find_codec_by_format, ((tmedia_codec_t*)item->data)->format)){
+ tsk_list_remove_item(codecs, item);
+ goto again;
+ }
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_codec_group
+* Serialize a list of codecs to sdp (m= line) message.<br>
+* Will add: fmt, rtpmap and fmtp.
+* @param codecs The list of codecs to convert
+* @param m The destination
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_codec_to_sdp(const tmedia_codecs_L_t* codecs, tsdp_header_M_t* m)
+{
+ const tsk_list_item_t* item;
+ const tmedia_codec_t* codec;
+ char *fmtp, *rtpmap;
+ int ret;
+
+ if(!m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_list_foreach(item, codecs){
+ codec = item->data;
+ /* add fmt */
+ if((ret = tsdp_header_M_add_fmt(m, codec->neg_format? codec->neg_format : codec->format))){
+ TSK_DEBUG_ERROR("Failed to add format");
+ return ret;
+ }
+ if(tsk_striequals(m->media, "audio") || tsk_striequals(m->media, "video")){
+ /* add rtpmap attributes */
+ if((rtpmap = tmedia_codec_get_rtpmap(codec))){
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("rtpmap", rtpmap),
+ tsk_null);
+ TSK_FREE(rtpmap);
+ }
+ /* add fmtp attributes */
+ if((fmtp = tmedia_codec_get_fmtp(codec))){
+ char* temp = tsk_null;
+ tsk_sprintf(&temp, "%s %s", codec->neg_format? codec->neg_format : codec->format, fmtp);
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("fmtp", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ TSK_FREE(fmtp);
+ }
+ }
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_codec_group
+* Finds a codec by format. If the codec has a dyn. payload type, then this function will also compare negociate formats.
+* @param codecs List of codecs from which to retrieve the matching codec.
+* @param format the format of the codec to find.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+tmedia_codec_t* tmedia_codec_find_by_format(tmedia_codecs_L_t* codecs, const char* format)
+{
+ const tmedia_codec_t* codec = tsk_null;
+
+ if(!codecs || !format){
+ TSK_DEBUG_ERROR("Inalid parameter");
+ return tsk_null;
+ }
+
+ if((codec = tsk_list_find_object_by_pred(codecs, __pred_find_codec_by_format, format)) ||
+ (codec = tsk_list_find_object_by_pred(codecs, __pred_find_codec_by_neg_format, format))){
+ return tsk_object_ref((void*)codec);
+ }
+ else{
+ return tsk_null;
+ }
+}
+
+/**@ingroup tmedia_codec_group
+*/
+int tmedia_codec_parse_fmtp(const char* fmtp, unsigned* maxbr, unsigned* fps, unsigned *width, unsigned *height)
+{
+ char *copy, *pch;
+ tsk_bool_t found = tsk_false;
+
+ if(tsk_strnullORempty(fmtp)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ copy = tsk_strdup(fmtp);
+ pch = strtok(copy, "; /");
+
+ while(pch){
+ unsigned div = 0;
+
+ if(sscanf(pch, "QCIF=%u", &div) == 1 && div){
+ *fps = 30/div;
+ *width = 176;
+ *height = 144;
+ found = tsk_true;
+ }
+ else if(sscanf(pch, "CIF=%u", &div) == 1 && div){
+ *fps = 30/div;
+ *width = 352;
+ *height = 288;
+ found = tsk_true;
+ }
+ else if(sscanf(pch, "SQCIF=%u", &div) == 1 && div){
+ *fps = 30/div;
+ *width = 128;
+ *height = 96;
+ found = tsk_true;
+ }
+ else if(sscanf(pch, "QVGA=%u", &div) == 1 && div){
+ *fps = 30/div;
+ *width = 320;
+ *height = 240;
+ found = tsk_true;
+ }
+ // to be continued
+
+ if(found){
+ //found = tsk_false;
+ pch = strtok(tsk_null, "; ");
+ while(pch){
+ if(sscanf(pch, "MaxBR=%u", maxbr) == 1){
+ //found = tsk_true;
+ break;
+ }
+ pch = strtok(tsk_null, "; /");
+ }
+ }
+
+ if(found){
+ break;
+ }
+
+ pch = strtok(tsk_null, "; /");
+ }
+
+ TSK_FREE(copy);
+
+ return found ? 0 : -2;
+}
+
+/**@ingroup tmedia_codec_group
+* DeInitialize a Codec.
+* @param self The codec to deinitialize. Could be any type of codec (e.g. @ref tmedia_codec_audio_t or @ref tmedia_codec_video_t).
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_codec_deinit(tmedia_codec_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(self->opened){
+ tmedia_codec_close(self);
+ }
+
+ TSK_FREE(self->name);
+ TSK_FREE(self->desc);
+ TSK_FREE(self->format);
+ TSK_FREE(self->neg_format);
+
+ return 0;
+}
+
+int tmedia_codec_video_set_callback(tmedia_codec_video_t *self, tmedia_codec_video_rtpcb_f callback, const void* callback_data)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->callback = callback;
+ self->callback_data = callback_data;
+
+ return 0;
+} \ No newline at end of file
diff --git a/tinyMEDIA/src/tmedia_codec_dummy.c b/tinyMEDIA/src/tmedia_codec_dummy.c
new file mode 100644
index 0000000..0390756
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_codec_dummy.c
@@ -0,0 +1,365 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_codec_dummy.c
+ * @brief Dummy codecs used for test only.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_codec_dummy.h"
+
+#include "tsk_debug.h"
+
+//=================================================================================================
+// Dummy G.711u object definition
+//
+
+#define tmedia_codec_dpcmu_fmtp_get tsk_null
+#define tmedia_codec_dpcmu_fmtp_set tsk_null
+#define tmedia_codec_dpcmu_fmtp_encode tsk_null
+#define tmedia_codec_dpcmu_fmtp_decode tsk_null
+
+tsk_bool_t tmedia_codec_dpcmu_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+/* constructor */
+static tsk_object_t* tmedia_codec_dpcmu_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_codec_dpcmu_t *dpcmu = self;
+ if(dpcmu){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_codec_dpcmu_dtor(tsk_object_t * self)
+{
+ tmedia_codec_dpcmu_t *dpcmu = self;
+ if(dpcmu){
+ /* deinit base */
+ tmedia_codec_audio_deinit(dpcmu);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_codec_dpcmu_def_s =
+{
+ sizeof(tmedia_codec_dpcmu_t),
+ tmedia_codec_dpcmu_ctor,
+ tmedia_codec_dpcmu_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tmedia_codec_dpcmu_plugin_def_s =
+{
+ &tmedia_codec_dpcmu_def_s,
+
+ tmedia_audio,
+ "G.711u",
+ "Dummy G.711u codec",
+ TMEDIA_CODEC_FORMAT_G711u,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // open
+ tsk_null, // close
+ tmedia_codec_dpcmu_fmtp_encode,
+ tmedia_codec_dpcmu_fmtp_decode,
+ tmedia_codec_dpcmu_fmtp_match,
+ tmedia_codec_dpcmu_fmtp_get,
+ tmedia_codec_dpcmu_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tmedia_codec_dpcmu_plugin_def_t = &tmedia_codec_dpcmu_plugin_def_s;
+
+//=================================================================================================
+// Dummy G.711a object definition
+//
+
+#define tmedia_codec_dpcma_fmtp_get tsk_null
+#define tmedia_codec_dpcma_fmtp_set tsk_null
+#define tmedia_codec_dpcma_fmtp_encode tsk_null
+#define tmedia_codec_dpcma_fmtp_decode tsk_null
+
+tsk_bool_t tmedia_codec_dpcma_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{ /* always match */
+ return tsk_true;
+}
+
+/* constructor */
+static tsk_object_t* tmedia_codec_dpcma_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_codec_dpcma_t *dpcma = self;
+ if(dpcma){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_codec_dpcma_dtor(tsk_object_t * self)
+{
+ tmedia_codec_dpcma_t *dpcma = self;
+ if(dpcma){
+ /* deinit base */
+ tmedia_codec_audio_deinit(dpcma);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_codec_dpcma_def_s =
+{
+ sizeof(tmedia_codec_dpcma_t),
+ tmedia_codec_dpcma_ctor,
+ tmedia_codec_dpcma_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tmedia_codec_dpcma_plugin_def_s =
+{
+ &tmedia_codec_dpcma_def_s,
+
+ tmedia_audio,
+ "G.711a",
+ "Dummy G.711a codec",
+ TMEDIA_CODEC_FORMAT_G711a,
+ tsk_false,
+ 8000, // rate
+
+ { /* audio */
+ 1, // channels
+ 20 // ptime
+ },
+
+ /* video */
+ {0},
+
+ tsk_null, // open
+ tsk_null, // close
+ tmedia_codec_dpcma_fmtp_encode,
+ tmedia_codec_dpcma_fmtp_decode,
+ tmedia_codec_dpcma_fmtp_match,
+ tmedia_codec_dpcma_fmtp_get,
+ tmedia_codec_dpcma_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tmedia_codec_dpcma_plugin_def_t = &tmedia_codec_dpcma_plugin_def_s;
+
+
+
+//=================================================================================================
+// Dummy H.263 object definition
+//
+
+tsk_size_t tmedia_codec_dh263_fmtp_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ return 0;
+}
+
+tsk_size_t tmedia_codec_dh263_fmtp_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ return 0;
+}
+
+tsk_bool_t tmedia_codec_dh263_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ /* check whether we can match this fmtp with our local
+ * check size, maxbr, fps ...*/
+ return tsk_true;
+}
+
+char* tmedia_codec_dh263_fmtp_get(const tmedia_codec_t* self)
+{
+ return tsk_strdup("CIF=2/MaxBR=3840;QCIF=2/MaxBR=1920");
+}
+
+int tmedia_codec_dh263_fmtp_set(tmedia_codec_t* self, const char* fmtp)
+{
+ TSK_DEBUG_INFO("remote fmtp=%s", fmtp);
+ return 0;
+}
+
+/* constructor */
+static tsk_object_t* tmedia_codec_dh263_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_codec_dh263_t *dh263 = self;
+ if(dh263){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_codec_dh263_dtor(tsk_object_t * self)
+{
+ tmedia_codec_dh263_t *dh263 = self;
+ if(dh263){
+ /* deinit base */
+ tmedia_codec_video_deinit(dh263);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_codec_dh263_def_s =
+{
+ sizeof(tmedia_codec_dh263_t),
+ tmedia_codec_dh263_ctor,
+ tmedia_codec_dh263_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tmedia_codec_dh263_plugin_def_s =
+{
+ &tmedia_codec_dh263_def_s,
+
+ tmedia_video,
+ "H263",
+ "Dummy H.263-1996 codec",
+ TMEDIA_CODEC_FORMAT_H263,
+ tsk_false,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144},
+
+ tsk_null, // open
+ tsk_null, // close
+ tmedia_codec_dh263_fmtp_encode,
+ tmedia_codec_dh263_fmtp_decode,
+ tmedia_codec_dh263_fmtp_match,
+ tmedia_codec_dh263_fmtp_get,
+ tmedia_codec_dh263_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tmedia_codec_dh263_plugin_def_t = &tmedia_codec_dh263_plugin_def_s;
+
+
+
+
+//=================================================================================================
+// Dummy H.264 (Base profile 10) object definition
+//
+
+tsk_size_t tmedia_codec_dh264_fmtp_encode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size)
+{
+ return 0;
+}
+
+tsk_size_t tmedia_codec_dh264_fmtp_decode(tmedia_codec_t* self, const void* in_data, tsk_size_t in_size, void** out_data, tsk_size_t* out_max_size, const tsk_object_t* proto_hdr)
+{
+ return 0;
+}
+
+tsk_bool_t tmedia_codec_dh264_fmtp_match(const tmedia_codec_t* codec, const char* fmtp)
+{
+ /* check whether we can match this fmtp with our local
+ * check size, maxbr, fps, profile-level-id, packetization-mode ...*/
+ return tsk_true;
+}
+
+char* tmedia_codec_dh264_fmtp_get(const tmedia_codec_t* self)
+{
+ return tsk_strdup("profile-level-id=42A01E;sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==");
+}
+
+int tmedia_codec_dh264_fmtp_set(tmedia_codec_t* self, const char* fmtp)
+{
+ TSK_DEBUG_INFO("remote fmtp=%s", fmtp);
+ return 0;
+}
+
+/* constructor */
+static tsk_object_t* tmedia_codec_dh264_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_codec_dh264_t *dh264 = self;
+ if(dh264){
+ /* init base: called by tmedia_codec_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_codec_dh264_dtor(tsk_object_t * self)
+{
+ tmedia_codec_dh264_t *dh264 = self;
+ if(dh264){
+ /* deinit base */
+ tmedia_codec_video_deinit(dh264);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_codec_dh264_def_s =
+{
+ sizeof(tmedia_codec_dh264_t),
+ tmedia_codec_dh264_ctor,
+ tmedia_codec_dh264_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_codec_plugin_def_t tmedia_codec_dh264_plugin_def_s =
+{
+ &tmedia_codec_dh264_def_s,
+
+ tmedia_video,
+ "H264",
+ "Dummy H.264 (base profile 10) codec",
+ TMEDIA_CODEC_FORMAT_H264_BP10,
+ tsk_true,
+ 90000, // rate
+
+ /* audio */
+ { 0 },
+
+ /* video */
+ {176, 144},
+
+ tsk_null, // open
+ tsk_null, // close
+ tmedia_codec_dh264_fmtp_encode,
+ tmedia_codec_dh264_fmtp_decode,
+ tmedia_codec_dh264_fmtp_match,
+ tmedia_codec_dh264_fmtp_get,
+ tmedia_codec_dh264_fmtp_set
+};
+const tmedia_codec_plugin_def_t *tmedia_codec_dh264_plugin_def_t = &tmedia_codec_dh264_plugin_def_s;
diff --git a/tinyMEDIA/src/tmedia_common.c b/tinyMEDIA/src/tmedia_common.c
new file mode 100644
index 0000000..49724bf
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_common.c
@@ -0,0 +1,184 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_common.c
+ * @brief Common functions and definitions.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_common.h"
+
+#include "tinymedia/tmedia_session.h"
+
+#include "tsk_debug.h"
+
+#include <stdlib.h> /* atoi() */
+
+tmedia_type_t tmedia_type_from_sdp(const tsdp_message_t* sdp)
+{
+ tmedia_type_t type = tmedia_none;
+ const tsdp_header_M_t* M;
+ tsk_size_t index = 0;
+ const tmedia_session_plugin_def_t* plugin;
+
+ if(!sdp){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tmedia_none;
+ }
+
+ while((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index++))){
+ if(M->port && (plugin = tmedia_session_plugin_find_by_media(M->media))){
+ type |= plugin->type;
+ }
+ }
+ return type;
+}
+
+
+int tmedia_parse_rtpmap(const char* rtpmap, char** name, int32_t* rate, int32_t* channels)
+{
+ /* e.g. AMR-WB/16000/2 */
+
+ int len;
+ int index, pos = 0;
+
+ if(tsk_strnullORempty(rtpmap)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ *name = tsk_null;
+ *rate = *channels = 0;
+ len = tsk_strlen(rtpmap);
+
+ /* name */
+ if((index = tsk_strindexOf(rtpmap, len, "/")) != -1){
+ *name = tsk_strndup(rtpmap, index);
+ len -= (index + 1), pos = (index + 1);
+ /* rate */
+ if(len>0){
+ if((index = tsk_strindexOf((rtpmap + pos), len, "/")) != -1){
+ *rate = atoi(&rtpmap[pos]);
+ len -= (index + 1), pos += (index + 1);
+ /* channels */
+ if(len>0){
+ *channels = atoi(&rtpmap[pos]);
+ }
+ }
+ else{
+ *rate = atoi(&rtpmap[pos]);
+ }
+ }
+ }
+ else{
+ *name = tsk_strdup(rtpmap);
+ }
+
+ return 0;
+
+ ///* e.g. AMR-WB/16000/2 */
+ //if(sscanf(rtpmap, "%*s/%*d/%*d") != EOF){
+ // int index = tsk_strindexOf(rtpmap, len, "/");
+ // *name = tsk_strndup(rtpmap, index);
+ // sscanf(&rtpmap[index+1], "%d/%d", rate, channels);
+ // return 0;
+ //}
+ ///* e.g. AMR-WB/16000 */
+ //else if(sscanf(rtpmap, "%*s/%*d") != EOF){
+ // int index = tsk_strindexOf(rtpmap, len, "/");
+ // *name = tsk_strndup(rtpmap, index);
+ // *rate = atoi(&rtpmap[index+1]);
+ // return 0;
+ //}
+ ///* e.g. AMR-WB */
+ //else if(sscanf(rtpmap, "%*s") != EOF){
+ // *name = tsk_strdup(rtpmap);
+ // return 0;
+ //}
+ //else{
+ // TSK_DEBUG_ERROR("%s is not a valid rtpmap value", rtpmap);
+ // return -2;
+ //}
+}
+
+static const tmedia_video_size_t tmedia_video_sizes[] =
+{
+ {tmedia_vst_none , 176, 144},
+
+ {tmedia_vst_sqcif, 128, 96},
+ {tmedia_vst_qcif, 176, 144},
+ {tmedia_vst_qvga, 320, 240},
+ {tmedia_vst_cif, 352, 288},
+ {tmedia_vst_vga, 640, 480},
+ {tmedia_vst_4cif, 704, 576},
+ {tmedia_vst_svga, 800, 600},
+ {tmedia_vst_xga, 1024, 768},
+ {tmedia_vst_sxga, 1280, 1024},
+ {tmedia_vst_16cif, 1408, 1152},
+ {tmedia_vst_hd720p, 1280, 720},
+ {tmedia_vst_hd1080p, 1920, 1080},
+
+ {tmedia_vst_ios_low, 200, 152},
+ {tmedia_vst_ios_high, 400, 304},
+};
+
+const tmedia_video_size_t* tmedia_get_video_size(tmedia_chroma_t chroma, tsk_size_t size)
+{
+ float factor = 3.f;
+ tsk_size_t i;
+ switch(chroma)
+ {
+ case tmedia_rgb24:
+ case tmedia_bgr24:
+ factor = 3.f;
+ break;
+ case tmedia_rgb565le:
+ case tmedia_rgb565be:
+ factor = 2.f;
+ break;
+
+ case tmedia_rgb32:
+ factor = 4.f;
+ break;
+
+ case tmedia_nv21:
+ case tmedia_nv12:
+ case tmedia_yuv420p:
+ factor = 1.5f;
+ break;
+
+ case tmedia_yuv422p:
+ case tmedia_uyvy422:
+ factor = 2.f;
+ break;
+ }
+
+ for(i = 1; i< sizeof(tmedia_video_sizes)/sizeof(tmedia_video_size_t); i++){
+ if((((float)(tmedia_video_sizes[i].width * tmedia_video_sizes[i].height)) * factor) == size){
+ return &tmedia_video_sizes[i];
+ }
+ }
+
+ return &tmedia_video_sizes[0];;
+} \ No newline at end of file
diff --git a/tinyMEDIA/src/tmedia_consumer.c b/tinyMEDIA/src/tmedia_consumer.c
new file mode 100644
index 0000000..5161615
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_consumer.c
@@ -0,0 +1,284 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_consumer.c
+ * @brief Base consumer object.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_consumer.h"
+
+#include "tsk_debug.h"
+
+/**@defgroup tmedia_consumer_group Producers
+*/
+
+/* pointer to all registered consumers */
+const tmedia_consumer_plugin_def_t* __tmedia_consumer_plugins[TMED_CONSUMER_MAX_PLUGINS] = {0};
+
+/**@ingroup tmedia_consumer_group
+* Initialize the consumer.
+* @param self The consumer to initialize
+* @retval Zero if succeed and non-zero error code otherwise.
+*
+* @sa @ref tmedia_consumer_deinit
+*/
+int tmedia_consumer_init(tmedia_consumer_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->video.in.chroma = TMEDIA_CONSUMER_CHROMA_DEFAULT;
+ self->video.display.chroma = TMEDIA_CONSUMER_CHROMA_DEFAULT;
+
+ return 0;
+}
+
+/**@ingroup tmedia_consumer_group
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_consumer_set(tmedia_consumer_t *self, const tmedia_param_t* param)
+{
+ if(!self || !self->plugin || !self->plugin->set || !param){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->set(self, param);
+}
+
+/**@ingroup tmedia_consumer_group
+* Alert the consumer to be prepared to start.
+* @param self the consumer to prepare
+* @param codec Negociated codec
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_consumer_prepare(tmedia_consumer_t *self, const tmedia_codec_t* codec)
+{
+ if(!self || !self->plugin || !self->plugin->prepare || !codec){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->prepare(self, codec);
+}
+
+/**@ingroup tmedia_consumer_group
+* Starts the consumer
+* @param self The consumer to start
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_consumer_start(tmedia_consumer_t *self)
+{
+ if(!self || !self->plugin || !self->plugin->start){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->start(self);
+}
+
+/**@ingroup tmedia_consumer_group
+* Consumes data
+* @param self The consumer
+* @param buffer Pointer to the data to consume
+* @param size Size of the data to consume
+*/
+int tmedia_consumer_consume(tmedia_consumer_t* self, void** buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
+{
+ if(!self || !self->plugin || !self->plugin->consume){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->consume(self, buffer, size, proto_hdr);
+}
+
+/**@ingroup tmedia_consumer_group
+* Pauses the consumer
+* @param self The consumer to pause
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_consumer_pause(tmedia_consumer_t *self)
+{
+ if(!self || !self->plugin || !self->plugin->pause){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->pause(self);
+}
+
+
+/**@ingroup tmedia_consumer_group
+* Stops the consumer
+* @param self The consumer to stop
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_consumer_stop(tmedia_consumer_t *self)
+{
+ if(!self || !self->plugin || !self->plugin->stop){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->stop(self);
+}
+
+
+/**@ingroup tmedia_consumer_group
+* DeInitialize the consumer.
+* @param self The consumer to deinitialize
+* @retval Zero if succeed and non-zero error code otherwise.
+*
+* @sa @ref tmedia_consumer_deinit
+*/
+int tmedia_consumer_deinit(tmedia_consumer_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_consumer_group
+* Creates a new consumer using an already registered plugin.
+* @param type The type of the consumer to create
+* @param session_id
+* @sa @ref tmedia_consumer_plugin_register()
+*/
+tmedia_consumer_t* tmedia_consumer_create(tmedia_type_t type, uint64_t session_id)
+{
+ tmedia_consumer_t* consumer = tsk_null;
+ const tmedia_consumer_plugin_def_t* plugin;
+ tsk_size_t i = 0;
+
+ while((i < TMED_CONSUMER_MAX_PLUGINS) && (plugin = __tmedia_consumer_plugins[i++])){
+ if(plugin->objdef && plugin->type == type){
+ if((consumer = tsk_object_new(plugin->objdef))){
+ /* initialize the newly created consumer */
+ consumer->plugin = plugin;
+ consumer->session_id = session_id;
+ break;
+ }
+ }
+ }
+
+ return consumer;
+}
+
+/**@ingroup tmedia_consumer_group
+* Registers a consumer plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tmedia_consumer_create()
+*/
+int tmedia_consumer_plugin_register(const tmedia_consumer_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* add or replace the plugin */
+ for(i = 0; i<TMED_CONSUMER_MAX_PLUGINS; i++){
+ if(!__tmedia_consumer_plugins[i] || (__tmedia_consumer_plugins[i] == plugin)){
+ __tmedia_consumer_plugins[i] = plugin;
+ return 0;
+ }
+ }
+
+ TSK_DEBUG_ERROR("There are already %d plugins.", TMED_CONSUMER_MAX_PLUGINS);
+ return -2;
+}
+
+/**@ingroup tmedia_consumer_group
+* UnRegisters a consumer plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_consumer_plugin_unregister(const tmedia_consumer_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMED_CONSUMER_MAX_PLUGINS && __tmedia_consumer_plugins[i]; i++){
+ if(__tmedia_consumer_plugins[i] == plugin){
+ __tmedia_consumer_plugins[i] = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMED_CONSUMER_MAX_PLUGINS - 1); i++){
+ if(__tmedia_consumer_plugins[i+1]){
+ __tmedia_consumer_plugins[i] = __tmedia_consumer_plugins[i+1];
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_consumer_plugins[i] = tsk_null;
+ }
+ return (found ? 0 : -2);
+}
+
+/**@ingroup tmedia_consumer_group
+* UnRegisters all consumers matching the given type.
+* @param type the type of the consumers to unregister (e.g. audio|video).
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_consumer_plugin_unregister_by_type(tmedia_type_t type)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMED_CONSUMER_MAX_PLUGINS && __tmedia_consumer_plugins[i]; i++){
+ if((__tmedia_consumer_plugins[i]->type & type) == __tmedia_consumer_plugins[i]->type){
+ __tmedia_consumer_plugins[i] = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMED_CONSUMER_MAX_PLUGINS - 1); i++){
+ if(__tmedia_consumer_plugins[i+1]){
+ __tmedia_consumer_plugins[i] = __tmedia_consumer_plugins[i+1];
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_consumer_plugins[i] = tsk_null;
+ }
+ return (found ? 0 : -2);
+} \ No newline at end of file
diff --git a/tinyMEDIA/src/tmedia_denoise.c b/tinyMEDIA/src/tmedia_denoise.c
new file mode 100644
index 0000000..c9e0731
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_denoise.c
@@ -0,0 +1,185 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_denoise.c
+ * @brief Denoiser (Noise suppression, AGC, AEC, VAD) Plugin
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_denoise.h"
+
+#include "tsk_debug.h"
+
+static const tmedia_denoise_plugin_def_t* __tmedia_denoise_plugin = tsk_null;
+
+int tmedia_denoise_init(tmedia_denoise_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ return 0;
+}
+
+int tmedia_denoise_open(tmedia_denoise_t* self, uint32_t frame_size, uint32_t sampling_rate, tsk_bool_t denoise, float agc_level, tsk_bool_t aec, tsk_bool_t vad)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(self->opened){
+ TSK_DEBUG_WARN("Denoiser already opened");
+ return 0;
+ }
+
+ if(self->plugin->open){
+ int ret;
+ if((ret = self->plugin->open(self, frame_size, sampling_rate, denoise, agc_level, aec, vad))){
+ TSK_DEBUG_ERROR("Failed to open [%s] denoiser", self->plugin->desc);
+ return ret;
+ }
+ else{
+ self->opened = tsk_true;
+ return 0;
+ }
+ }
+ else{
+ self->opened = tsk_true;
+ return 0;
+ }
+}
+
+int tmedia_denoise_echo_playback(tmedia_denoise_t* self, const void* echo_frame)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Denoiser not opened");
+ return -2;
+ }
+
+ if(self->plugin->process){
+ return self->plugin->echo_playback(self, echo_frame);
+ }
+ else{
+ return 0;
+ }
+}
+
+
+int tmedia_denoise_process(tmedia_denoise_t* self, void* audio_frame, tsk_bool_t* silence_or_noise)
+{
+ if(!self || !self->plugin || !silence_or_noise){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!self->opened){
+ TSK_DEBUG_ERROR("Denoiser not opened");
+ return -2;
+ }
+
+ if(self->plugin->process){
+ return self->plugin->process(self, audio_frame, silence_or_noise);
+ }
+ else{
+ *silence_or_noise = tsk_false;
+ return 0;
+ }
+}
+
+int tmedia_denoise_close(tmedia_denoise_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(!self->opened){
+ TSK_DEBUG_WARN("Denoiser not opened");
+ return 0;
+ }
+
+ if(self->plugin->close){
+ int ret;
+
+ if((ret = self->plugin->close(self))){
+ TSK_DEBUG_ERROR("Failed to close [%s] denoiser", self->plugin->desc);
+ return ret;
+ }
+ else{
+ self->opened = tsk_false;
+ return 0;
+ }
+ }
+ else{
+ self->opened = tsk_false;
+ return 0;
+ }
+}
+
+int tmedia_denoise_deinit(tmedia_denoise_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(self->opened){
+ tmedia_denoise_close(self);
+ }
+
+ return 0;
+}
+
+int tmedia_denoise_plugin_register(const tmedia_denoise_plugin_def_t* plugin)
+{
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ __tmedia_denoise_plugin = plugin;
+ return 0;
+}
+
+int tmedia_denoise_plugin_unregister()
+{
+ __tmedia_denoise_plugin = tsk_null;
+ return 0;
+}
+
+tmedia_denoise_t* tmedia_denoise_create()
+{
+ tmedia_denoise_t* denoise = tsk_null;
+
+ if(__tmedia_denoise_plugin){
+ if((denoise = tsk_object_new(__tmedia_denoise_plugin->objdef))){
+ denoise->plugin = __tmedia_denoise_plugin;
+ }
+ }
+ return denoise;
+}
diff --git a/tinyMEDIA/src/tmedia_params.c b/tinyMEDIA/src/tmedia_params.c
new file mode 100644
index 0000000..0234839
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_params.c
@@ -0,0 +1,190 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_params.c
+ * @brief Media parameters used to configure any session or plugin.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_params.h"
+
+#include "tinymedia/tmedia_session.h"
+
+#include "tsk_debug.h"
+#include "tsk_memory.h"
+
+tmedia_param_t* tmedia_param_create(tmedia_param_access_type_t access_type,
+ tmedia_type_t media_type,
+ tmedia_param_plugin_type_t plugin_type,
+ tmedia_param_value_type_t value_type,
+ const char* key,
+ void* value)
+{
+ tmedia_param_t* param;
+
+ if(!key ||!value){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ if((param = tsk_object_new(tmedia_param_def_t))){
+ param->access_type = access_type;
+ param->media_type = media_type;
+ param->plugin_type = plugin_type;
+ param->value_type = value_type;
+ param->key = tsk_strdup(key);
+ switch(value_type){
+ case tmedia_pvt_int32:
+ if(param->value = tsk_calloc(1, sizeof(int32_t))){
+ memcpy(param->value, value, sizeof(int32_t));
+ //*((int32_t*)param->value) = *((int32_t*)value);
+ }
+ break;
+ case tmedia_pvt_pobject:
+ param->value = tsk_object_ref(value);
+ break;
+ case tmedia_pvt_pchar:
+ param->value = tsk_strdup(value);
+ break;
+ case tmedia_pvt_int64:
+ if(param->value = tsk_calloc(1, sizeof(int64_t))){
+ memcpy(param->value, value, sizeof(int64_t));
+ //*((int64_t*)param->value) = *((int64_t*)value);
+ }
+ break;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create media parameter");
+ }
+ return param;
+}
+
+tmedia_params_L_t* tmedia_params_create_2(va_list *app)
+{
+ tmedia_session_param_type_t curr;
+ tmedia_params_L_t* params;
+
+ if(!app){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ params = tmedia_params_create();
+
+ while((curr = va_arg(*app, tmedia_session_param_type_t)) != tmedia_sptype_null){
+ switch(curr){
+ case tmedia_sptype_set:
+ { /* (tmedia_type_t)MEDIA_TYPE_ENUM, (tmedia_param_plugin_type_t)PLUGIN_TYPE_ENUM, (tmedia_param_value_type_t)VALUE_TYPE_ENUM \
+ (const char*)KEY_STR, (void*)&VALUE */
+ /* IMPORTANT: do not pass va_arg() directly into the function */
+ tmedia_type_t media_type = va_arg(*app, tmedia_type_t);
+ tmedia_param_plugin_type_t plugin_type = va_arg(*app, tmedia_param_plugin_type_t);
+ tmedia_param_value_type_t value_type = va_arg(*app, tmedia_param_value_type_t);
+ const char* key = va_arg(*app, const char*);
+ void* value = va_arg(*app, void*);
+ tmedia_params_add_param(&params, tmedia_pat_set,
+ media_type, plugin_type, value_type, key, value);
+ break;
+ }
+ default:
+ { /* va_list will be unsafe => exit */
+ TSK_DEBUG_ERROR("%d NOT a valid pname", curr);
+ break;
+ }
+ }/* switch */
+ }/* while */
+
+ return params;
+}
+
+int tmedia_params_add_param(tmedia_params_L_t **self,
+ tmedia_param_access_type_t access_type,
+ tmedia_type_t media_type,
+ tmedia_param_plugin_type_t plugin_type,
+ tmedia_param_value_type_t value_type,
+ const char* key,
+ void* value)
+{
+ tmedia_param_t *param;
+
+ if(!self) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!*self){
+ *self = tmedia_params_create();
+ }
+
+ if((param = tmedia_param_create(access_type, media_type, plugin_type, value_type, key, value))){
+ tsk_list_push_back_data(*self, (void**)&param);
+ }
+ return 0;
+}
+
+
+
+
+//=================================================================================================
+// param object definition
+//
+static tsk_object_t* tmedia_param_ctor(tsk_object_t* self, va_list * app)
+{
+ tmedia_param_t *param = self;
+ if(param){
+ }
+
+ return self;
+}
+
+static tsk_object_t* tmedia_param_dtor(tsk_object_t* self)
+{
+ tmedia_param_t *param = self;
+ if(param){
+ TSK_FREE(param->key);
+ switch(param->value_type){
+ case tmedia_pvt_pobject:
+ TSK_OBJECT_SAFE_FREE(param->value);
+ break;
+ case tmedia_pvt_pchar:
+ case tmedia_pvt_int64:
+ case tmedia_pvt_int32:
+ TSK_FREE(param->value);
+ break;
+ }
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tmedia_param_def_s =
+{
+ sizeof(tmedia_param_t),
+ tmedia_param_ctor,
+ tmedia_param_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tmedia_param_def_t = &tmedia_param_def_s;
+
diff --git a/tinyMEDIA/src/tmedia_producer.c b/tinyMEDIA/src/tmedia_producer.c
new file mode 100644
index 0000000..fb0f83b
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_producer.c
@@ -0,0 +1,301 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_producer.c
+ * @brief Base producer object.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_producer.h"
+
+#include "tsk_debug.h"
+
+/**@defgroup tmedia_producer_group Producers
+*/
+
+/* pointer to all registered producers */
+const tmedia_producer_plugin_def_t* __tmedia_producer_plugins[TMED_PRODUCER_MAX_PLUGINS] = {0};
+
+/**@ingroup tmedia_producer_group
+* Initialize the producer.
+* @param self The producer to initialize
+* @retval Zero if succeed and non-zero error code otherwise.
+*
+* @sa @ref tmedia_producer_deinit
+*/
+int tmedia_producer_init(tmedia_producer_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->video.chroma = TMEDIA_PRODUCER_CHROMA_DEFAULT;
+
+ return 0;
+}
+
+/**@ingroup tmedia_producer_group
+* callback to encode and send() data
+*/
+int tmedia_producer_set_enc_callback(tmedia_producer_t *self, tmedia_producer_enc_cb_f callback, const void* callback_data)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->enc_cb.callback = callback;
+ self->enc_cb.callback_data = callback_data;
+
+ return 0;
+}
+
+/**@ingroup tmedia_producer_group
+* callback to send() data "as is"
+*/
+int tmedia_producer_set_raw_callback(tmedia_producer_t *self, tmedia_producer_raw_cb_f callback, const void* callback_data)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->raw_cb.callback = callback;
+ self->raw_cb.callback_data = callback_data;
+
+ return 0;
+}
+
+/**@ingroup tmedia_producer_group
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_producer_set(tmedia_producer_t* self, const tmedia_param_t* param)
+{
+ if(!self || !self->plugin || !self->plugin->set || !param){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->set(self, param);
+}
+
+/**@ingroup tmedia_producer_group
+* Alert the producer to be prepared to start.
+* @param self the producer to prepare
+* @param codec The codec to use to prepare the producer
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_producer_prepare(tmedia_producer_t *self, const tmedia_codec_t* codec)
+{
+ if(!self || !self->plugin || !self->plugin->prepare || !codec){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->prepare(self, codec);
+}
+
+/**@ingroup tmedia_producer_group
+* Starts the producer
+* @param self The producer to start
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_producer_start(tmedia_producer_t *self)
+{
+ if(!self || !self->plugin || !self->plugin->start){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->start(self);
+}
+
+
+/**@ingroup tmedia_producer_group
+* Pauses the producer
+* @param self The producer to pause
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_producer_pause(tmedia_producer_t *self)
+{
+ if(!self || !self->plugin || !self->plugin->pause){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->pause(self);
+}
+
+
+/**@ingroup tmedia_producer_group
+* Stops the producer
+* @param self The producer to stop
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_producer_stop(tmedia_producer_t *self)
+{
+ if(!self || !self->plugin || !self->plugin->stop){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return self->plugin->stop(self);
+}
+
+
+/**@ingroup tmedia_producer_group
+* DeInitialize the producer.
+* @param self The producer to deinitialize
+* @retval Zero if succeed and non-zero error code otherwise.
+*
+* @sa @ref tmedia_producer_deinit
+*/
+int tmedia_producer_deinit(tmedia_producer_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_producer_group
+* Creates a new producer using an already registered plugin.
+* @param type The type of the producer to create
+* @param session_id
+* @sa @ref tmedia_producer_plugin_register()
+*/
+tmedia_producer_t* tmedia_producer_create(tmedia_type_t type, uint64_t session_id)
+{
+ tmedia_producer_t* producer = tsk_null;
+ const tmedia_producer_plugin_def_t* plugin;
+ tsk_size_t i = 0;
+
+ while((i < TMED_PRODUCER_MAX_PLUGINS) && (plugin = __tmedia_producer_plugins[i++])){
+ if(plugin->objdef && plugin->type == type){
+ if((producer = tsk_object_new(plugin->objdef))){
+ /* initialize the newly created producer */
+ producer->plugin = plugin;
+ producer->session_id = session_id;
+ break;
+ }
+ }
+ }
+
+ return producer;
+}
+
+/**@ingroup tmedia_producer_group
+* Registers a producer plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tmedia_producer_create()
+*/
+int tmedia_producer_plugin_register(const tmedia_producer_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* add or replace the plugin */
+ for(i = 0; i<TMED_PRODUCER_MAX_PLUGINS; i++){
+ if(!__tmedia_producer_plugins[i] || (__tmedia_producer_plugins[i] == plugin)){
+ __tmedia_producer_plugins[i] = plugin;
+ return 0;
+ }
+ }
+
+ TSK_DEBUG_ERROR("There are already %d plugins.", TMED_PRODUCER_MAX_PLUGINS);
+ return -2;
+}
+
+/**@ingroup tmedia_producer_group
+* UnRegisters a producer plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_producer_plugin_unregister(const tmedia_producer_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMED_PRODUCER_MAX_PLUGINS && __tmedia_producer_plugins[i]; i++){
+ if(__tmedia_producer_plugins[i] == plugin){
+ __tmedia_producer_plugins[i] = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMED_PRODUCER_MAX_PLUGINS - 1); i++){
+ if(__tmedia_producer_plugins[i+1]){
+ __tmedia_producer_plugins[i] = __tmedia_producer_plugins[i+1];
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_producer_plugins[i] = tsk_null;
+ }
+ return (found ? 0 : -2);
+}
+
+/**@ingroup tmedia_producer_group
+* UnRegisters all producers matching the given type.
+* @param type the type of the plugins to unregister.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_producer_plugin_unregister_by_type(tmedia_type_t type)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMED_PRODUCER_MAX_PLUGINS && __tmedia_producer_plugins[i]; i++){
+ if((__tmedia_producer_plugins[i]->type & type) == __tmedia_producer_plugins[i]->type){
+ __tmedia_producer_plugins[i] = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMED_PRODUCER_MAX_PLUGINS - 1); i++){
+ if(__tmedia_producer_plugins[i+1]){
+ __tmedia_producer_plugins[i] = __tmedia_producer_plugins[i+1];
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_producer_plugins[i] = tsk_null;
+ }
+ return (found ? 0 : -2);
+}
diff --git a/tinyMEDIA/src/tmedia_qos.c b/tinyMEDIA/src/tmedia_qos.c
new file mode 100644
index 0000000..e2cac97
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_qos.c
@@ -0,0 +1,861 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_qos.c
+ * @brief RFC 3312 (Preconditions) implementation.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_qos.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ========================= Internal ==================================*/
+
+static const char* tmedia_qos_strength_tostring(tmedia_qos_strength_t strength)
+{
+ switch(strength){
+ case tmedia_qos_strength_none:
+ return "none";
+ case tmedia_qos_strength_mandatory:
+ return "mandatory";
+ case tmedia_qos_strength_optional:
+ return "optional";
+ case tmedia_qos_strength_failure:
+ return "failure";
+ case tmedia_qos_strength_unknown:
+ default:
+ return "unknown";
+ }
+}
+
+static tmedia_qos_strength_t tmedia_qos_strength_fromstring(const char* strength)
+{
+ if(tsk_strequals(strength, "none")){
+ return tmedia_qos_strength_none;
+ }
+ else if(tsk_strequals(strength, "mandatory")){
+ return tmedia_qos_strength_mandatory;
+ }
+ else if(tsk_strequals(strength, "optional")){
+ return tmedia_qos_strength_optional;
+ }
+ else if(tsk_strequals(strength, "failure")){
+ return tmedia_qos_strength_failure;
+ }
+ else{
+ return tmedia_qos_strength_unknown;
+ }
+}
+
+static tmedia_qos_direction_t tmedia_qos_direction_fromstring(const char* direction)
+{
+ if(tsk_strequals(direction, "send")){
+ return tmedia_qos_direction_send;
+ }
+ else if(tsk_strequals(direction, "recv")){
+ return tmedia_qos_direction_recv;
+ }
+ else if(tsk_strequals(direction, "sendrecv")){
+ return tmedia_qos_direction_sendrecv;
+ }
+ else{
+ return tmedia_qos_direction_none;
+ }
+}
+
+/* ========================= Common ==================================*/
+
+tmedia_qos_tline_t* tmedia_qos_tline_create(tmedia_qos_stype_t type, tmedia_qos_strength_t strength)
+{
+ switch(type){
+ case tmedia_qos_stype_e2e:
+ return (tmedia_qos_tline_t*)tmedia_qos_tline_e2e_create(strength);
+ case tmedia_qos_stype_segmented:
+ return (tmedia_qos_tline_t*)tmedia_qos_tline_segmented_create(strength);
+ }
+ return tsk_null;
+}
+
+tmedia_qos_stype_t tmedia_qos_get_type(const tsdp_header_M_t* m)
+{
+ const tsdp_header_A_t* A;
+ char s0[10];
+
+ if(!m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tmedia_qos_stype_none;
+ }
+
+ if((A = tsdp_header_M_findA(m, "curr"))){
+ if(sscanf(A->value, "qos %s %*s", s0) != EOF){
+ return tsk_strequals(s0, "e2e") ? tmedia_qos_stype_e2e : tmedia_qos_stype_segmented;
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ return tmedia_qos_stype_none;
+}
+
+tmedia_qos_tline_t* tmedia_qos_tline_from_sdp(const tsdp_header_M_t* m)
+{
+ if(!m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ switch(tmedia_qos_get_type(m)){
+ case tmedia_qos_stype_e2e:
+ return (tmedia_qos_tline_t*)tmedia_qos_tline_e2e_from_sdp(m);
+ case tmedia_qos_stype_segmented:
+ return (tmedia_qos_tline_t*)tmedia_qos_tline_segmented_from_sdp(m);
+ default:
+ return tsk_null;
+ }
+}
+
+int tmedia_qos_tline_to_sdp(const tmedia_qos_tline_t* self, tsdp_header_M_t* m)
+{
+ if(!self || !m){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ switch(self->type){
+ case tmedia_qos_stype_e2e:
+ return tmedia_qos_tline_e2e_to_sdp((tmedia_qos_tline_e2e_t*)self, m);
+ case tmedia_qos_stype_segmented:
+ return tmedia_qos_tline_segmented_to_sdp((tmedia_qos_tline_segmented_t*)self, m);
+ default:
+ TSK_DEBUG_ERROR("Invalid type");
+ return -2;
+ }
+}
+
+int tmedia_qos_tline_set_ro(tmedia_qos_tline_t* self, const tmedia_qos_tline_t* ro)
+{
+ if(!self || !ro){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ switch(self->type){
+ case tmedia_qos_stype_e2e:
+ return tmedia_qos_tline_e2e_set_ro((tmedia_qos_tline_e2e_t*)self, (const tmedia_qos_tline_e2e_t*)ro);
+ case tmedia_qos_stype_segmented:
+ return tmedia_qos_tline_segmented_set_ro((tmedia_qos_tline_segmented_t*)self, (const tmedia_qos_tline_segmented_t*)ro);
+ default:
+ TSK_DEBUG_ERROR("Invalid type");
+ return -2;
+ }
+}
+
+tsk_bool_t tmedia_qos_tline_canresume(const tmedia_qos_tline_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_true;
+ }
+ switch(self->type){
+ case tmedia_qos_stype_segmented:
+ return tmedia_qos_tline_segmented_canresume((const tmedia_qos_tline_segmented_t*)self);
+ case tmedia_qos_stype_e2e:
+ return tmedia_qos_tline_e2e_canresume((const tmedia_qos_tline_e2e_t*)self);
+ default:
+ return tsk_true;
+ }
+}
+
+/* ========================= E2E ==================================*/
+
+tmedia_qos_tline_e2e_t* tmedia_qos_tline_e2e_create(tmedia_qos_strength_t strength)
+{
+ return tsk_object_new(tmedia_qos_tline_e2e_def_t, strength);
+}
+
+tmedia_qos_tline_e2e_t* tmedia_qos_tline_e2e_from_sdp(const tsdp_header_M_t* m)
+{
+ tmedia_qos_tline_e2e_t* e2e = tsk_null;
+ const tsdp_header_A_t* A;
+ tsk_size_t i;
+
+ char s0[10], s1[10];
+
+ if(!m){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return tsk_null;
+ }
+
+ /* Example
+ a=curr:qos e2e none
+ a=des:qos mandatory e2e sendrecv
+ a=conf:qos e2e recv
+ */
+
+ e2e = tmedia_qos_tline_e2e_create(tmedia_qos_strength_unknown);
+
+ /* curr */
+ for(i = 0; (A = tsdp_header_M_findA_at(m, "curr", i)); i++){
+ if(sscanf(A->value, "qos e2e %10s", s0) != EOF){
+ tmedia_qos_direction_t dir = tmedia_qos_direction_fromstring(s0);
+ switch(dir){
+ case tmedia_qos_direction_send:
+ e2e->send.current = tsk_true;
+ break;
+ case tmedia_qos_direction_recv:
+ e2e->recv.current = tsk_true;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ e2e->send.current = tsk_true;
+ e2e->recv.current = tsk_true;
+ break;
+ case tmedia_qos_direction_none:
+ e2e->send.current = tsk_false;
+ e2e->recv.current = tsk_false;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ /* des */
+ for(i = 0; (A = tsdp_header_M_findA_at(m, "des", i)); i++){
+ if(sscanf(A->value, "qos %10s e2e %10s", s0, s1) != EOF){
+ tmedia_qos_strength_t strength = tmedia_qos_strength_fromstring(s0);
+ tmedia_qos_direction_t dir = tmedia_qos_direction_fromstring(s1);
+ switch(dir){
+ case tmedia_qos_direction_send:
+ e2e->send.strength = strength;
+ break;
+ case tmedia_qos_direction_recv:
+ e2e->recv.strength = strength;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ e2e->send.strength = strength;
+ e2e->recv.strength = strength;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ /* conf */
+ for(i = 0; (A = tsdp_header_M_findA_at(m, "conf", i)); i++){
+ if(sscanf(A->value, "qos e2e %10s", s0) != EOF){
+ tmedia_qos_direction_t dir = tmedia_qos_direction_fromstring(s0);
+ switch(dir){
+ case tmedia_qos_direction_send:
+ e2e->send.confirm = tsk_true;
+ break;
+ case tmedia_qos_direction_recv:
+ e2e->recv.confirm = tsk_true;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ e2e->send.confirm = tsk_true;
+ e2e->recv.confirm = tsk_true;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ return e2e;
+}
+
+int tmedia_qos_tline_e2e_to_sdp(const tmedia_qos_tline_e2e_t* self, tsdp_header_M_t* m)
+{
+ /* RFC 3312 - 5.1.1 SDP encoding
+
+ For the end-to-end status type, the user agent MUST generate one
+ current status line with the tag "e2e" for the media stream. If the
+ strength-tags for both directions are equal (e.g., both "mandatory")
+ in the transaction status table, the user agent MUST add one desired
+ status line with the tag "sendrecv". If both tags are different, the
+ user agent MUST include two desired status lines, one with the tag
+ "send" and the other with the tag "recv".
+ */
+ char* temp = tsk_null;
+
+ if(!self || !m){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return -1;
+ }
+ /* Example
+ a=curr:qos e2e none
+ a=des:qos mandatory e2e sendrecv
+ */
+
+
+ /* curr */
+ tsk_sprintf(&temp, "qos e2e %s", (self->recv.current && self->send.current) ? "sendrecv" : (self->recv.current ? "recv" : (self->send.current ? "send" : "none")));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("curr", temp),
+ tsk_null);
+ TSK_FREE(temp);
+
+ /* des */
+ if(self->recv.strength == self->send.strength){
+ /* sendrecv */
+ tsk_sprintf(&temp, "qos %s e2e sendrecv", tmedia_qos_strength_tostring(self->recv.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+ else{
+ /* send */
+ tsk_sprintf(&temp, "qos %s e2e send", tmedia_qos_strength_tostring(self->send.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+
+ /* recv */
+ tsk_sprintf(&temp, "qos %s e2e recv", tmedia_qos_strength_tostring(self->recv.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+
+ /* conf (should not request confirm on "send" direction)*/
+ if(self->recv.confirm){
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("conf", "qos e2e recv"),
+ tsk_null);
+ }
+ return 0;
+}
+
+int tmedia_qos_tline_e2e_set_ro(tmedia_qos_tline_e2e_t* self, const tmedia_qos_tline_e2e_t* ro)
+{
+ if(!self || !ro){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* We were the offerer
+ * Remote asked for confirmation in its "recv" direction?
+ * "recv" direction for remote is our "send" direction
+ * As we don't support RSVP (under the way), confirm immediatly.
+ * "send" direction should not requested for confirmation
+ */
+ if(ro->recv.confirm){
+ self->send.current = tsk_true;
+ goto bail;
+ }
+ if(ro->send.current){
+ self->recv.confirm = tsk_false; /* remote confirmed */
+ self->recv.current = tsk_true; /* because ro confirmed */
+ self->send.current = tsk_true; /* beacuse we don't support RSVP */
+ goto bail;
+ }
+
+ /* We are the answerer
+ * As we don't support RSVP (under the way):
+ * ==> request confirmation for "recv" direction if equal to "none" (not reserved)
+ * =>
+ */
+ if(!self->recv.current){
+ self->recv.confirm = tsk_true;
+ goto bail;
+ }
+
+bail:
+ /* all other cases: success */
+ return 0;
+}
+
+tsk_bool_t tmedia_qos_tline_e2e_canresume(const tmedia_qos_tline_e2e_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_true;
+ }
+
+ /* Example
+ a=curr:qos e2e none
+ a=des:qos mandatory e2e sendrecv
+
+ Or
+
+ a=curr:qos e2e send
+ a=des:qos mandatory e2e recv
+ a=des:qos optional e2e send
+ */
+
+ /* only "mandatory" strength should force the application to continue nego. */
+ if(self->recv.strength == tmedia_qos_strength_mandatory && !self->recv.current){
+ return tsk_false;
+ }
+ /*else */if(self->send.strength == tmedia_qos_strength_mandatory && !self->send.current){
+ return tsk_false;
+ }
+
+ /* "optinal" and "none" strengths */
+ return tsk_true;
+}
+
+//
+// E2E QoS line object definition
+//
+static tsk_object_t* tmedia_qos_tline_e2e_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_qos_tline_e2e_t *e2e = self;
+ if(e2e){
+ e2e->recv.strength = e2e->send.strength = va_arg(*app, tmedia_qos_strength_t);
+ TMEDIA_QOS_TLINE(e2e)->type = tmedia_qos_stype_e2e;
+ }
+ return self;
+}
+
+static tsk_object_t* tmedia_qos_tline_e2e_dtor(tsk_object_t * self)
+{
+ tmedia_qos_tline_e2e_t *e2e = self;
+ if(e2e){
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tmedia_qos_tline_e2e_def_s =
+{
+ sizeof(tmedia_qos_tline_e2e_t),
+ tmedia_qos_tline_e2e_ctor,
+ tmedia_qos_tline_e2e_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tmedia_qos_tline_e2e_def_t = &tmedia_qos_tline_e2e_def_s;
+
+
+
+
+
+
+
+
+
+
+/* ========================= Segmented ==================================*/
+
+tmedia_qos_tline_segmented_t* tmedia_qos_tline_segmented_create(tmedia_qos_strength_t strength)
+{
+ return tsk_object_new(tmedia_qos_tline_segmented_def_t, strength);
+}
+
+tmedia_qos_tline_segmented_t* tmedia_qos_tline_segmented_from_sdp(const tsdp_header_M_t* m)
+{
+ tmedia_qos_tline_segmented_t* segmented = tsk_null;
+ const tsdp_header_A_t* A;
+ tsk_size_t i;
+
+ char s0[10], s1[10], s2[10];
+
+ if(!m){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return tsk_null;
+ }
+
+ /* Example
+ a=curr:qos local none
+ a=curr:qos remote none
+ a=des:qos optional remote send
+ a=des:qos none remote recv
+ a=des:qos none local sendrecv
+ a=conf:qos local recv
+ */
+ segmented = tmedia_qos_tline_segmented_create(tmedia_qos_strength_unknown);
+
+ /* curr */
+ for(i = 0; (A = tsdp_header_M_findA_at(m, "curr", i)); i++){
+ if(sscanf(A->value, "qos %10s %10s", s0, s1) != EOF){
+ /* For segmented, s0 should be equal to "local" or "remote" */
+ tmedia_qos_direction_t dir = tmedia_qos_direction_fromstring(s1);
+ if(tsk_strequals(s0, "local")){
+ /* local */
+ switch(dir){
+ case tmedia_qos_direction_send:
+ segmented->local_send.current = tsk_true;
+ break;
+ case tmedia_qos_direction_recv:
+ segmented->local_recv.current = tsk_true;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ segmented->local_send.current = tsk_true;
+ segmented->local_recv.current = tsk_true;
+ break;
+ case tmedia_qos_direction_none:
+ segmented->local_send.current = tsk_false;
+ segmented->local_recv.current = tsk_false;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ /* remote */
+ switch(dir){
+ case tmedia_qos_direction_send:
+ segmented->remote_send.current = tsk_true;
+ break;
+ case tmedia_qos_direction_recv:
+ segmented->remote_recv.current = tsk_true;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ segmented->remote_send.current = tsk_true;
+ segmented->remote_recv.current = tsk_true;
+ break;
+ case tmedia_qos_direction_none:
+ segmented->remote_send.current = tsk_false;
+ segmented->remote_recv.current = tsk_false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ /* des */
+ for(i = 0; (A = tsdp_header_M_findA_at(m, "des", i)); i++){
+ if(sscanf(A->value, "qos %10s %10s %10s", s0, s1, s2) != EOF){
+ /* For segmented, s1 should be equal to "local" or "remote" */
+ tmedia_qos_strength_t strength = tmedia_qos_strength_fromstring(s0);
+ tmedia_qos_direction_t dir = tmedia_qos_direction_fromstring(s2);
+ if(tsk_strequals(s1, "local")){
+ /* local */
+ switch(dir){
+ case tmedia_qos_direction_send:
+ segmented->local_send.strength = strength;
+ break;
+ case tmedia_qos_direction_recv:
+ segmented->local_recv.strength = strength;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ segmented->local_send.strength = strength;
+ segmented->local_recv.strength = strength;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ /* remote */
+ switch(dir){
+ case tmedia_qos_direction_send:
+ segmented->remote_send.strength = strength;
+ break;
+ case tmedia_qos_direction_recv:
+ segmented->remote_recv.strength = strength;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ segmented->remote_send.strength = strength;
+ segmented->remote_recv.strength = strength;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ /* conf */
+ for(i = 0; (A = tsdp_header_M_findA_at(m, "conf", i)); i++){
+ if(sscanf(A->value, "qos %10s %10s", s0, s1) != EOF){
+ /* For segmented, s0 should be equal to "local" or "remote" */
+ tmedia_qos_direction_t dir = tmedia_qos_direction_fromstring(s1);
+ if(tsk_strequals(s0, "local")){
+ /* local */
+ switch(dir){
+ case tmedia_qos_direction_send:
+ segmented->local_send.confirm = tsk_true;
+ break;
+ case tmedia_qos_direction_recv:
+ segmented->local_recv.confirm = tsk_true;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ segmented->local_send.confirm = tsk_true;
+ segmented->local_recv.confirm = tsk_true;
+ break;
+ default:
+ break;
+ }
+ }
+ else{
+ /* remote */
+ switch(dir){
+ case tmedia_qos_direction_send:
+ segmented->remote_send.confirm = tsk_true;
+ break;
+ case tmedia_qos_direction_recv:
+ segmented->remote_recv.confirm = tsk_true;
+ break;
+ case tmedia_qos_direction_sendrecv:
+ segmented->remote_send.confirm = tsk_true;
+ segmented->remote_recv.confirm = tsk_true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to parse a=%s:%s", A->field, A->value);
+ }
+ }
+
+ return segmented;
+}
+
+int tmedia_qos_tline_segmented_to_sdp(const tmedia_qos_tline_segmented_t* self, tsdp_header_M_t* m)
+{
+ /* RFC 3312 - 5.1.1 SDP encoding
+
+ For the segmented status type, the user agent MUST generate two
+ current status lines: one with the tag "local" and the other with the
+ tag "remote". The user agent MUST add one or two desired status
+ lines per segment (i.e., local and remote). If, for a particular
+ segment (local or remote), the tags for both directions in the
+ transaction status table are equal (e.g., both "mandatory"), the user
+ agent MUST add one desired status line with the tag "sendrecv". If
+ both tags are different, the user agent MUST include two desired
+ status lines, one with the tag "send" and the other with the tag "recv".
+ */
+ char* temp = tsk_null;
+
+ if(!self || !m){
+ TSK_DEBUG_ERROR("invalid parameter");
+ return -1;
+ }
+
+ /* Example
+ a=curr:qos local none
+ a=curr:qos remote none
+ a=des:qos optional remote send
+ a=des:qos none remote recv
+ a=des:qos none local sendrecv
+ */
+
+ /* curr:local */
+ tsk_sprintf(&temp, "qos local %s", (self->local_recv.current && self->local_send.current) ? "sendrecv" : (self->local_recv.current ? "recv" : (self->local_send.current ? "send" : "none")));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("curr", temp),
+ tsk_null);
+ TSK_FREE(temp);
+
+ /* curr:remote */
+ tsk_sprintf(&temp, "qos remote %s", (self->remote_recv.current && self->remote_send.current) ? "sendrecv" : (self->remote_recv.current ? "recv" : (self->remote_send.current ? "send" : "none")));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("curr", temp),
+ tsk_null);
+ TSK_FREE(temp);
+
+
+ /* des:local */
+ if(self->local_recv.strength == self->local_send.strength){
+ /* sendrecv */
+ tsk_sprintf(&temp, "qos %s local sendrecv", tmedia_qos_strength_tostring(self->local_send.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+ else{
+ /* send */
+ tsk_sprintf(&temp, "qos %s local send", tmedia_qos_strength_tostring(self->local_send.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+
+ /* recv */
+ tsk_sprintf(&temp, "qos %s local recv", tmedia_qos_strength_tostring(self->local_recv.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+
+
+ /* des:remote */
+ if(self->remote_recv.strength == self->remote_send.strength){
+ /* sendrecv */
+ tsk_sprintf(&temp, "qos %s remote sendrecv", tmedia_qos_strength_tostring(self->remote_send.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+ else{
+ /* send */
+ tsk_sprintf(&temp, "qos %s remote send", tmedia_qos_strength_tostring(self->remote_send.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+
+ /* recv */
+ tsk_sprintf(&temp, "qos %s remote recv", tmedia_qos_strength_tostring(self->remote_recv.strength));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("des", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+
+ /* conf */
+ if(self->remote_recv.confirm || self->remote_send.confirm){
+ tsk_sprintf(&temp, "qos remote %s", (self->remote_recv.confirm && self->remote_send.confirm) ? "sendrecv" : (self->remote_recv.confirm ? "recv" : (self->remote_send.confirm ? "send" : "none")));
+ tsdp_header_M_add_headers(m,
+ TSDP_HEADER_A_VA_ARGS("conf", temp),
+ tsk_null);
+ TSK_FREE(temp);
+ }
+
+ return 0;
+}
+
+int tmedia_qos_tline_segmented_set_ro(tmedia_qos_tline_segmented_t* self, const tmedia_qos_tline_segmented_t* ro)
+{
+ if(!self || !ro){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ //////////////
+ if(!ro->local_recv.current && !ro->remote_recv.confirm){
+ /* request confirmation */
+ self->remote_recv.confirm = tsk_true;
+ }
+ else{
+ self->remote_recv.confirm = tsk_false;
+ self->local_recv.current = tsk_true;
+ }
+ if(!ro->local_send.current && !ro->remote_send.confirm){
+ /* request confirmation */
+ self->remote_send.confirm = tsk_true;
+ }
+ else{
+ self->remote_send.confirm = tsk_false;
+ self->local_send.current = tsk_true;
+ }
+
+ //////////////
+ if(ro->remote_recv.confirm){
+ self->local_recv.current = tsk_true;
+ }
+ if(ro->remote_send.confirm){
+ self->local_send.current = tsk_true;
+ }
+
+ //////////////
+ if(ro->local_recv.current){
+ self->remote_recv.current = tsk_true;
+ }
+ if(ro->local_send.current){
+ self->remote_send.current = tsk_true;
+ }
+
+ return 0;
+}
+
+tsk_bool_t tmedia_qos_tline_segmented_canresume(const tmedia_qos_tline_segmented_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_true;
+ }
+
+ /* == Strength is mandatory == */
+ if(self->local_recv.strength == tmedia_qos_strength_mandatory && !self->local_recv.current){
+ return tsk_false;
+ }
+
+ if(self->local_send.strength == tmedia_qos_strength_mandatory && !self->local_send.current){
+ return tsk_false;
+ }
+
+ if(self->remote_recv.strength == tmedia_qos_strength_mandatory && !self->remote_recv.current){
+ return tsk_false;
+ }
+
+ if(self->remote_send.strength == tmedia_qos_strength_mandatory && !self->remote_send.current){
+ return tsk_false;
+ }
+
+ /* "optinal" and "none" strengths */
+ return tsk_true;
+}
+
+//
+// Segmented QoS line object definition
+//
+static tsk_object_t* tmedia_qos_tline_segmented_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_qos_tline_segmented_t *segmented = self;
+ if(segmented){
+ segmented->local_recv.strength = segmented->local_send.strength
+ = segmented->remote_recv.strength = segmented->remote_send.strength = va_arg(*app, tmedia_qos_strength_t);
+ TMEDIA_QOS_TLINE(segmented)->type = tmedia_qos_stype_segmented;
+ }
+ return self;
+}
+
+static tsk_object_t* tmedia_qos_tline_segmented_dtor(tsk_object_t * self)
+{
+ tmedia_qos_tline_segmented_t *segmented = self;
+ if(segmented){
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tmedia_qos_tline_segmented_def_s =
+{
+ sizeof(tmedia_qos_tline_segmented_t),
+ tmedia_qos_tline_segmented_ctor,
+ tmedia_qos_tline_segmented_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tmedia_qos_tline_segmented_def_t = &tmedia_qos_tline_segmented_def_s; \ No newline at end of file
diff --git a/tinyMEDIA/src/tmedia_session.c b/tinyMEDIA/src/tmedia_session.c
new file mode 100644
index 0000000..a8d7627
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_session.c
@@ -0,0 +1,1304 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_session.h
+ * @brief Base session object.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_session.h"
+
+#include "tinymedia/tmedia_session_ghost.h"
+
+#include "tinysdp/headers/tsdp_header_O.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/**@defgroup tmedia_session_group Media Session
+* For more information about the SOA, please refer to http://betelco.blogspot.com/2010/03/sdp-offeranswer-soa_2993.html
+*/
+
+#if !defined(va_copy)
+# define va_copy(D, S) ((D) = (S))
+#endif
+
+extern const tmedia_codec_plugin_def_t* __tmedia_codec_plugins[TMED_CODEC_MAX_PLUGINS];
+
+/* pointer to all registered sessions */
+const tmedia_session_plugin_def_t* __tmedia_session_plugins[TMED_SESSION_MAX_PLUGINS] = {0};
+
+/* === local functions === */
+int _tmedia_session_mgr_load_sessions(tmedia_session_mgr_t* self);
+int _tmedia_session_mgr_apply_params(tmedia_session_mgr_t* self);
+int _tmedia_session_prepare_lo(tmedia_session_t* self);
+int _tmedia_session_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m);
+int _tmedia_session_load_codecs(tmedia_session_t* self);
+
+const char* tmedia_session_get_media(const tmedia_session_t* self);
+const tsdp_header_M_t* tmedia_session_get_lo(tmedia_session_t* self);
+int tmedia_session_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m);
+
+
+/*== Predicate function to find session object by media */
+int __pred_find_session_by_media(const tsk_list_item_t *item, const void *media)
+{
+ if(item && item->data){
+ return tsk_stricmp(tmedia_session_get_media((const tmedia_session_t *)item->data), (const char*)media);
+ }
+ return -1;
+}
+
+/*== Predicate function to find session object by type */
+int __pred_find_session_by_type(const tsk_list_item_t *item, const void *type)
+{
+ if(item && item->data){
+ return ((const tmedia_session_t *)item->data)->type - *((tmedia_type_t*)type);
+ }
+ return -1;
+}
+
+/**@ingroup tmedia_session_group
+* Initializes a newly created media session.
+* @param self the media session to initialize.
+* @param type the type of the session to initialize.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_session_init(tmedia_session_t* self, tmedia_type_t type)
+{
+ int ret = 0;
+ static uint64_t __UniqueId = 1;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!self->initialized){
+ /* set values */
+ self->id = ++__UniqueId;
+ self->type = type;
+ self->initialized = tsk_true;
+ self->bl = tmedia_bl_low;
+ /* load associated codecs */
+ ret = _tmedia_session_load_codecs(self);
+ }
+
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Generic function to compare two sessions.
+* @param sess1 The first session to compare.
+* @param sess2 The second session to compare.
+* @retval Returns an integral value indicating the relationship between the two sessions:
+* <0 : @a sess1 less than @a sess2.<br>
+* 0 : @a sess1 identical to @a sess2.<br>
+* >0 : @a sess1 greater than @a sess2.<br>
+*/
+int tmedia_session_cmp(const tsk_object_t* sess1, const tsk_object_t* sess2)
+{
+ return (TMEDIA_SESSION(sess1) - TMEDIA_SESSION(sess2));
+}
+
+/**@ingroup tmedia_session_group
+* Registers a session plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+* @sa @ref tmedia_session_create()
+*/
+int tmedia_session_plugin_register(const tmedia_session_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* add or replace the plugin */
+ for(i = 0; i<TMED_SESSION_MAX_PLUGINS; i++){
+ if(!__tmedia_session_plugins[i] || (__tmedia_session_plugins[i] == plugin)){
+ __tmedia_session_plugins[i] = plugin;
+ return 0;
+ }
+ }
+
+ TSK_DEBUG_ERROR("There are already %d plugins.", TMED_SESSION_MAX_PLUGINS);
+ return -2;
+}
+
+/**@ingroup tmedia_session_group
+* Finds a plugin by media.
+*/
+const tmedia_session_plugin_def_t* tmedia_session_plugin_find_by_media(const char* media)
+{
+ tsk_size_t i = 0;
+ if(tsk_strnullORempty(media)){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ /* add or replace the plugin */
+ while((i<TMED_SESSION_MAX_PLUGINS) && (__tmedia_session_plugins[i])){
+ if(tsk_striequals(__tmedia_session_plugins[i]->media, media)){
+ return __tmedia_session_plugins[i];
+ }
+ i++;
+ }
+ return tsk_null;
+}
+
+/**@ingroup tmedia_session_group
+* UnRegisters a session plugin.
+* @param plugin the definition of the plugin.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_session_plugin_unregister(const tmedia_session_plugin_def_t* plugin)
+{
+ tsk_size_t i;
+ tsk_bool_t found = tsk_false;
+ if(!plugin){
+ TSK_DEBUG_ERROR("Invalid Parameter");
+ return -1;
+ }
+
+ /* find the plugin to unregister */
+ for(i = 0; i<TMED_SESSION_MAX_PLUGINS && __tmedia_session_plugins[i]; i++){
+ if(__tmedia_session_plugins[i] == plugin){
+ __tmedia_session_plugins[i] = tsk_null;
+ found = tsk_true;
+ break;
+ }
+ }
+
+ /* compact */
+ if(found){
+ for(; i<(TMED_SESSION_MAX_PLUGINS - 1); i++){
+ if(__tmedia_session_plugins[i+1]){
+ __tmedia_session_plugins[i] = __tmedia_session_plugins[i+1];
+ }
+ else{
+ break;
+ }
+ }
+ __tmedia_session_plugins[i] = tsk_null;
+ }
+ return (found ? 0 : -2);
+}
+
+/**@ingroup tmedia_session_group
+* Creates a new session using an already registered plugin.
+* @param format The type of the codec to create.
+* @sa @ref tmedia_codec_plugin_register()
+*/
+tmedia_session_t* tmedia_session_create(tmedia_type_t type)
+{
+ tmedia_session_t* session = tsk_null;
+ const tmedia_session_plugin_def_t* plugin;
+ tsk_size_t i = 0;
+
+ while((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])){
+ if(plugin->objdef && (plugin->type == type)){
+ if((session = tsk_object_new(plugin->objdef))){
+ if(!session->initialized){
+ tmedia_session_init(session, type);
+ }
+ session->plugin = plugin;
+ }
+ break;
+ }
+ }
+ return session;
+}
+
+/* internal funtion: prepare lo */
+int _tmedia_session_prepare_lo(tmedia_session_t* self)
+{
+ int ret;
+ if(!self || !self->plugin || !self->plugin->prepare){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(self->prepared){
+ TSK_DEBUG_WARN("Session already prepared");
+ return 0;
+ }
+ if((ret = self->plugin->prepare(self))){
+ TSK_DEBUG_ERROR("Failed to prepare the session");
+ }
+ else{
+ self->prepared = tsk_true;
+ }
+ return ret;
+}
+
+/* internal function used to set remote offer */
+int _tmedia_session_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ int ret;
+ if(!self || !self->plugin || !self->plugin->set_remote_offer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ if(!(ret = self->plugin->set_remote_offer(self, m))){
+ self->ro_changed = tsk_true;
+ }
+ return ret;
+}
+
+/* internal function: get media */
+const char* tmedia_session_get_media(const tmedia_session_t* self)
+{
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ /* ghost? */
+ if(self->plugin == tmedia_session_ghost_plugin_def_t){
+ return ((const tmedia_session_ghost_t*)self)->media;
+ }
+ else{
+ return self->plugin->media;
+ }
+}
+/* internal function: get local offer */
+const tsdp_header_M_t* tmedia_session_get_lo(tmedia_session_t* self)
+{
+ const tsdp_header_M_t* m;
+
+ if(!self || !self->plugin || !self->plugin->get_local_offer){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ if((m = self->plugin->get_local_offer(self))){
+ self->ro_changed = tsk_false; /* we should have a fresh local offer (based on the latest ro) */
+ }
+ return m;
+}
+
+/* Match a codec */
+tmedia_codecs_L_t* tmedia_session_match_codec(tmedia_session_t* self, const tsdp_header_M_t* M)
+{
+ const tmedia_codec_t *codec;
+ char *rtpmap = tsk_null, *fmtp = tsk_null, *name = tsk_null;
+ const tsdp_fmt_t* fmt;
+ const tsk_list_item_t *it1, *it2;
+ tsk_bool_t found = tsk_false;
+ tmedia_codecs_L_t* matchingCodecs = tsk_null;
+
+ if(!self || !M){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+
+ /* foreach format */
+ tsk_list_foreach(it1, M->FMTs){
+ fmt = it1->data;
+
+ /* foreach codec */
+ tsk_list_foreach(it2, self->codecs){
+ if(!(codec = it2->data) || !codec->plugin){
+ continue;
+ }
+
+ // Dyn. payload type
+ if(codec->dyn && (rtpmap = tsdp_header_M_get_rtpmap(M, fmt->value))){
+ int32_t rate, channels;
+ /* parse rtpmap */
+ if(tmedia_parse_rtpmap(rtpmap, &name, &rate, &channels)){
+ goto next;
+ }
+
+ /* compare name and rate. what about channels? */
+ if(tsk_striequals(name, codec->name) && (!rate || (codec->plugin->rate == rate))){
+ goto compare_fmtp;
+ }
+ }
+ // Fixed payload type
+ else{
+ if(tsk_striequals(fmt->value, codec->format)){
+ goto compare_fmtp;
+ }
+ }
+
+ /* rtpmap do not match: free strings and try next codec */
+ goto next;
+
+compare_fmtp:
+ if((fmtp = tsdp_header_M_get_fmtp(M, fmt->value))){ /* remote have fmtp? */
+ if(tmedia_codec_match_fmtp(codec, fmtp)){ /* fmtp matches? */
+ tsk_strupdate((char**)&codec->neg_format, fmt->value);
+ found = tsk_true;
+ }
+ }
+ else{ /* no fmtp -> always match */
+ tsk_strupdate((char**)&codec->neg_format, fmt->value);
+ found = tsk_true;
+ }
+next:
+ TSK_FREE(name);
+ TSK_FREE(fmtp);
+ TSK_FREE(rtpmap);
+ if(found){
+ tmedia_codec_t * copy;
+ if(!matchingCodecs){
+ matchingCodecs = tsk_list_create();
+ }
+ copy = tsk_object_ref((void*)codec);
+ tsk_list_push_back_data(matchingCodecs, (void**)&copy);
+
+ found = tsk_false;
+ break;
+ }
+ }
+ }
+
+
+ return matchingCodecs;
+}
+
+
+/**@ingroup tmedia_session_group
+* DeInitializes a media session.
+* @param self the media session to deinitialize.
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_session_deinit(tmedia_session_t* self)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* free codecs */
+ TSK_OBJECT_SAFE_FREE(self->codecs);
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+
+ /* free lo, no and ro */
+ TSK_OBJECT_SAFE_FREE(self->M.lo);
+ TSK_OBJECT_SAFE_FREE(self->M.ro);
+
+ /* QoS */
+ TSK_OBJECT_SAFE_FREE(self->qos);
+
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Send DTMF event
+* @param self the audio session to use to send a DTMF event
+* @param event the DTMF event to send (should be between 0-15)
+* @retval Zero if succeed and non-zero error code otherwise.
+*/
+int tmedia_session_audio_send_dtmf(tmedia_session_audio_t* self, uint8_t event)
+{
+ if(!self || !TMEDIA_SESSION(self)->plugin || !TMEDIA_SESSION(self)->plugin->audio.send_dtmf){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ return TMEDIA_SESSION(self)->plugin->audio.send_dtmf(TMEDIA_SESSION(self), event);
+}
+
+/* internal function used to prepare a session */
+int _tmedia_session_load_codecs(tmedia_session_t* self)
+{
+ tsk_size_t i = 0;
+ tmedia_codec_t* codec;
+ const tmedia_codec_plugin_def_t* plugin;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* remove old codecs */
+ TSK_OBJECT_SAFE_FREE(self->codecs);
+
+ /* for each registered plugin create a session instance */
+ while((i < TMED_CODEC_MAX_PLUGINS) && (plugin = __tmedia_codec_plugins[i++])){
+ if((plugin->type & self->type) == plugin->type){
+ if((codec = tmedia_codec_create(plugin->format))){
+ if(!self->codecs){
+ self->codecs = tsk_list_create();
+ }
+ tsk_list_push_back_data(self->codecs, (void**)(&codec));
+ }
+ }
+ }
+ return 0;
+}
+
+
+/**@ingroup tmedia_session_group
+* Creates new session manager.
+* @param type the type of the session to create. For example, (@ref tmed_sess_type_audio | @ref tmed_sess_type_video).
+* @param addr the local ip address or FQDN to use in the sdp message.
+* @param ipv6 indicates whether @a addr is IPv6 address or not. Useful when @a addr is a FQDN.
+* @param load_sessions Whether the offerer or not.
+* will create an audio/video session.
+* @retval new @ref tmedia_session_mgr_t object
+*/
+tmedia_session_mgr_t* tmedia_session_mgr_create(tmedia_type_t type, const char* addr, tsk_bool_t ipv6, tsk_bool_t offerer)
+{
+ tmedia_session_mgr_t* mgr;
+
+ if(!(mgr = tsk_object_new(tmedia_session_mgr_def_t))){
+ TSK_DEBUG_ERROR("Failed to create Media Session manager");
+ return tsk_null;
+ }
+
+ /* init */
+ mgr->type = type;
+ mgr->addr = tsk_strdup(addr);
+ mgr->ipv6 = ipv6;
+
+ /* load sessions (will allow us to generate lo) */
+ if(offerer){
+ mgr->offerer = tsk_true;
+ if(_tmedia_session_mgr_load_sessions(mgr)){
+ /* Do nothing */
+ TSK_DEBUG_ERROR("Failed to load sessions");
+ }
+ }
+
+ return mgr;
+}
+
+/**@ingroup tmedia_session_group
+*/
+tmedia_session_t* tmedia_session_mgr_find(tmedia_session_mgr_t* self, tmedia_type_t type)
+{
+ tmedia_session_t* session = (tmedia_session_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &type);
+ return tsk_object_ref(session);
+}
+
+/**@ingroup tmedia_session_group
+*/
+int tmedia_session_mgr_set_natt_ctx(tmedia_session_mgr_t* self, tnet_nat_context_handle_t* natt_ctx, const char* public_addr)
+{
+ if(!self || !natt_ctx){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+ TSK_OBJECT_SAFE_FREE(self->natt_ctx);
+ self->natt_ctx = tsk_object_ref(natt_ctx);
+ tsk_strupdate(&self->public_addr, public_addr);
+
+ tmedia_session_mgr_set(self,
+ TMEDIA_SESSION_SET_POBJECT(self->type, "natt-ctx", self->natt_ctx),
+ TMEDIA_SESSION_SET_NULL());
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Starts the session manager by starting all underlying sessions.
+* You should set both remote and local offers before calling this function.
+* @param self The session manager to start.
+* @retval Zero if succced and non-zero error code otherwise.
+*
+* @sa @ref tmedia_session_mgr_stop
+*/
+int tmedia_session_mgr_start(tmedia_session_mgr_t* self)
+{
+ int ret;
+ tsk_list_item_t* item;
+ tmedia_session_t* session;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(self->started){
+ TSK_DEBUG_WARN("Session manager already started");
+ return 0;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ if(!(session = item->data) || !session->plugin || !session->plugin->start){
+ TSK_DEBUG_ERROR("Invalid session");
+ return -2;
+ }
+ if((ret = session->plugin->start(session))){
+ TSK_DEBUG_ERROR("Failed to start %s session", session->plugin->media);
+ continue;
+ }
+ }
+
+ self->started = tsk_true;
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* sets parameters for one or several sessions.
+* @param self The session manager
+* @param ... Any TMEDIA_SESSION_SET_*() macros
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_session_mgr_set(tmedia_session_mgr_t* self, ...)
+{
+ va_list ap;
+ int ret;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ va_start(ap, self);
+ ret = tmedia_session_mgr_set_2(self, &ap);
+ va_end(ap);
+
+ return ret;
+}
+
+/**@ingroup tmedia_session_group
+* sets parameters for one or several sessions.
+* @param self The session manager
+* @param app List of parameters.
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_session_mgr_set_2(tmedia_session_mgr_t* self, va_list *app)
+{
+ tmedia_params_L_t* params;
+
+ if(!self || !app){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if((params = tmedia_params_create_2(app))){
+ if(!self->params){
+ self->params = tsk_object_ref(params);
+ }
+ else{
+ tsk_list_pushback_list(self->params, params);
+ }
+ TSK_OBJECT_SAFE_FREE(params);
+ }
+
+ /* load params if we already have sessions */
+ if(!TSK_LIST_IS_EMPTY(self->sessions)){
+ _tmedia_session_mgr_apply_params(self);
+ }
+
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* sets parameters for one or several sessions.
+* @param self The session manager
+* @param params List of parameters to set
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_session_mgr_set_3(tmedia_session_mgr_t* self, const tmedia_params_L_t* params)
+{
+ if(!self || !params){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!self->params){
+ self->params = tsk_list_create();
+ }
+ tsk_list_pushback_list(self->params, params);
+
+ /* load params if we already have sessions */
+ if(!TSK_LIST_IS_EMPTY(self->sessions)){
+ _tmedia_session_mgr_apply_params(self);
+ }
+
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Stops the session manager by stopping all underlying sessions.
+* @param self The session manager to stop.
+* @retval Zero if succced and non-zero error code otherwise.
+*
+* @sa @ref tmedia_session_mgr_start
+*/
+int tmedia_session_mgr_stop(tmedia_session_mgr_t* self)
+{
+ int ret;
+ tsk_list_item_t* item;
+ tmedia_session_t* session;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if(!self->started){
+ TSK_DEBUG_WARN("Session manager not started");
+ return 0;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ if(!(session = item->data) || !session->plugin || !session->plugin->stop){
+ TSK_DEBUG_ERROR("Invalid session");
+ return -2;
+ }
+ if((ret = session->plugin->stop(session))){
+ TSK_DEBUG_ERROR("Failed to stop session");
+ return ret;
+ }
+ }
+ self->started = tsk_false;
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Gets local offer.
+*/
+const tsdp_message_t* tmedia_session_mgr_get_lo(tmedia_session_mgr_t* self)
+{
+ const tsk_list_item_t* item;
+ const tmedia_session_t* ms;
+ const tsdp_header_M_t* m;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ /* prepare the session manager if not already done (create all sessions) */
+ if(TSK_LIST_IS_EMPTY(self->sessions)){
+ if(_tmedia_session_mgr_load_sessions(self)){
+ TSK_DEBUG_ERROR("Failed to prepare the session manager");
+ return tsk_null;
+ }
+ }
+
+ /* creates local sdp if not already done or update it's value (because of set_ro())*/
+ if((self->ro_changed || self->state_changed) && self->sdp.lo){
+ TSK_OBJECT_SAFE_FREE(self->sdp.lo);
+ self->ro_changed = tsk_false;
+ self->state_changed = tsk_false;
+ }
+
+ if(self->sdp.lo){
+ return self->sdp.lo;
+ }
+ else if((self->sdp.lo = tsdp_message_create_empty(self->public_addr ? self->public_addr : self->addr, self->ipv6, self->sdp.lo_ver++))){
+ /* Set connection "c=" */
+ tsdp_message_add_headers(self->sdp.lo,
+ TSDP_HEADER_C_VA_ARGS("IN", self->ipv6 ? "IP6" : "IP4", self->public_addr ? self->public_addr : self->addr),
+ tsk_null);
+ }else{
+ self->sdp.lo_ver--;
+ TSK_DEBUG_ERROR("Failed to create empty SDP message");
+ return tsk_null;
+ }
+
+ /* gets each "m=" line from the sessions and add them to the local sdp */
+ tsk_list_foreach(item, self->sessions){
+ if(!(ms = item->data) || !ms->plugin){
+ TSK_DEBUG_ERROR("Invalid session");
+ continue;
+ }
+ /* prepare the media session */
+ if(!ms->prepared && (_tmedia_session_prepare_lo(TMEDIA_SESSION(ms)))){
+ TSK_DEBUG_ERROR("Failed to prepare session"); /* should never happen */
+ continue;
+ }
+
+ /* Add QoS lines to our local media */
+ if((self->qos.type != tmedia_qos_stype_none) && !TMEDIA_SESSION(ms)->qos){
+ TMEDIA_SESSION(ms)->qos = tmedia_qos_tline_create(self->qos.type, self->qos.strength);
+ }
+
+ /* add "m=" line from the session to the local sdp */
+ if((m = tmedia_session_get_lo(TMEDIA_SESSION(ms)))){
+ tsdp_message_add_header(self->sdp.lo, TSDP_HEADER(m));
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to get m= line for [%s] media", ms->plugin->media);
+ }
+ }
+
+ return self->sdp.lo;
+}
+
+
+/**@ingroup tmedia_session_group
+* Sets remote offer.
+*/
+int tmedia_session_mgr_set_ro(tmedia_session_mgr_t* self, const tsdp_message_t* sdp)
+{
+ const tmedia_session_t* ms;
+ const tsdp_header_M_t* M;
+ const tsdp_header_C_t* C; /* global "c=" line */
+ const tsdp_header_O_t* O;
+ tsk_size_t index = 0;
+ tsk_bool_t found;
+ tmedia_qos_stype_t qos_type = tmedia_qos_stype_none;
+
+ if(!self || !sdp){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* RFC 3264 subcaluse 8
+ When issuing an offer that modifies the session, the "o=" line of the new SDP MUST be identical to that in the previous SDP,
+ except that the version in the origin field MUST increment by one from the previous SDP. If the version in the origin line
+ does not increment, the SDP MUST be identical to the SDP with that version number. The answerer MUST be prepared to receive
+ an offer that contains SDP with a version that has not changed; this is effectively a no-op.
+ */
+ if((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp, tsdp_htype_O))){
+ if(self->sdp.ro_ver == (int32_t)O->sess_version){
+ TSK_DEBUG_INFO("Remote offer has not changed");
+ return 0;
+ }
+ self->sdp.ro_ver = (int32_t)O->sess_version;
+ }
+ else{
+ TSK_DEBUG_ERROR("o= line is missing");
+ return -2;
+ }
+
+ /* update remote offer */
+ TSK_OBJECT_SAFE_FREE(self->sdp.ro);
+ self->sdp.ro = tsk_object_ref((void*)sdp);
+
+ /* prepare the session manager if not already done (create all sessions with their codecs)
+ * if network-initiated: think about tmedia_type_from_sdp() before creating the manager */
+ if(TSK_LIST_IS_EMPTY(self->sessions)){
+ if(_tmedia_session_mgr_load_sessions(self)){
+ TSK_DEBUG_ERROR("Failed to prepare the session manager");
+ return -3;
+ }
+ }
+
+ /* get global connection line (common to all sessions)
+ * Each session should override this info if it has a different one in its "m=" line
+ */
+ if((C = (const tsdp_header_C_t*)tsdp_message_get_header(sdp, tsdp_htype_C)) && C->addr){
+ tmedia_session_mgr_set(self,
+ TMEDIA_SESSION_SET_STR(self->type, "remote-ip", C->addr),
+ TMEDIA_SESSION_SET_NULL());
+ }
+
+ /* foreach "m=" line in the remote offer create a session*/
+ while((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp, tsdp_htype_M, index++))){
+ found = tsk_false;
+ /* Find session by media */
+ if((ms = tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_media, M->media))){
+ /* set remote ro at session-level */
+ if(_tmedia_session_set_ro(TMEDIA_SESSION(ms), M) == 0){
+ found = tsk_true;
+ }
+ /* set QoS type (only if we are not the offerer) */
+ if(/*!self->offerer ==> we suppose that the remote party respected our demand &&*/ (qos_type == tmedia_qos_stype_none)){
+ tmedia_qos_tline_t* tline = tmedia_qos_tline_from_sdp(M);
+ if(tline){
+ qos_type = tline->type;
+ TSK_OBJECT_SAFE_FREE(tline);
+ }
+ }
+ }
+
+ if(!found && (self->sdp.lo == tsk_null)){
+ /* Session not supported and we are not the initial offerer ==> add ghost session */
+ tmedia_session_ghost_t* ghost;
+ if((ghost = (tmedia_session_ghost_t*)tmedia_session_create(tmedia_ghost))){
+ tsk_strupdate(&ghost->media, M->media); /* copy media */
+ tsk_list_push_back_data(self->sessions, (void**)&ghost);
+ }
+ else{
+ TSK_DEBUG_ERROR("Failed to create ghost session");
+ continue;
+ }
+ }
+ }
+
+ /* Update QoS type */
+ if(!self->offerer && (qos_type != tmedia_qos_stype_none)){
+ self->qos.type = qos_type;
+ }
+
+ /* signal that ro has changed (will be used to update lo) */
+ self->ro_changed = tsk_true;
+
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Holds the session as per 3GPP TS 34.610
+* @param self the session manager managing the session to hold.
+* @param type the type of the sessions to hold (you can combine several medias. e.g. audio|video|msrp).
+* @retval Zero if succeed and non zero error code otherwise.
+* @sa @ref tmedia_session_mgr_resume
+*/
+int tmedia_session_mgr_hold(tmedia_session_mgr_t* self, tmedia_type_t type)
+{
+ const tsk_list_item_t* item;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ tmedia_session_t* session = TMEDIA_SESSION(item->data);
+ if(((session->type & type) == session->type) && session->M.lo){
+ if(!tsdp_header_M_hold(session->M.lo, tsk_true)){
+ self->state_changed = tsk_true;
+ }
+ }
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Indicates whether the specified medias are held or not.
+* @param self the session manager
+* @param type the type of the medias to check (you can combine several medias. e.g. audio|video|msrp)
+* @param local whether to check local or remote medias
+*/
+tsk_bool_t tmedia_session_mgr_is_held(tmedia_session_mgr_t* self, tmedia_type_t type, tsk_bool_t local)
+{
+ const tsk_list_item_t* item;
+ tsk_bool_t have_these_sessions = tsk_false;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ tmedia_session_t* session = TMEDIA_SESSION(item->data);
+ if((session->type & type) == session->type){
+ if(local && session->M.lo){
+ have_these_sessions = tsk_true;
+ if(!tsdp_header_M_is_held(session->M.lo, tsk_true)){
+ return tsk_false;
+ }
+ }
+ else if(!local && session->M.ro){
+ have_these_sessions = tsk_true;
+ if(!tsdp_header_M_is_held(session->M.ro, tsk_false)){
+ return tsk_false;
+ }
+ }
+ }
+ }
+ /* none is held */
+ return have_these_sessions ? tsk_true : tsk_false;
+}
+
+/**@ingroup tmedia_session_group
+* Resumes the session as per 3GPP TS 34.610. Should be previously held
+* by using @ref tmedia_session_mgr_hold.
+* @param self the session manager managing the session to resume.
+* @param type the type of the sessions to resume (you can combine several medias. e.g. audio|video|msrp).
+* @retval Zero if succeed and non zero error code otherwise.
+* @sa @ref tmedia_session_mgr_hold
+*/
+int tmedia_session_mgr_resume(tmedia_session_mgr_t* self, tmedia_type_t type)
+{
+ const tsk_list_item_t* item;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ tmedia_session_t* session = TMEDIA_SESSION(item->data);
+ if(((session->type & type) == session->type) && session->M.lo){
+ if(!tsdp_header_M_resume(session->M.lo, tsk_true)){
+ self->state_changed = tsk_true;
+ }
+ }
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Adds new medias to the manager. A media will only be added if it is missing
+* or previously removed (slot with port equal to zero).
+* @param self The session manager
+* @param The types of the medias to add (ou can combine several medias. e.g. audio|video|msrp)
+* @retval Zero if succeed and non zero error code otherwise.
+*/
+int tmedia_session_mgr_add_media(tmedia_session_mgr_t* self, tmedia_type_t type)
+{
+ tsk_size_t i = 0;
+ tmedia_session_t* session;
+ const tmedia_session_plugin_def_t* plugin;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* for each registered plugin match with the supplied type */
+ while((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])){
+ if((plugin->type & type) == plugin->type){
+ /* check whether we already support this media */
+ if((session = (tmedia_session_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &plugin->type)) && session->plugin){
+ if(session->prepared){
+ TSK_DEBUG_WARN("[%s] already active", plugin->media);
+ }
+ else{
+ /* exist but unprepared(port=0) */
+ _tmedia_session_prepare_lo(session);
+ if(self->started && session->plugin->start){
+ session->plugin->start(session);
+ }
+ self->state_changed = tsk_true;
+ }
+ }
+ else{
+ /* session not supported */
+ self->state_changed = tsk_true;
+ if((session = tmedia_session_create(plugin->type))){
+ if(self->started && session->plugin->start){
+ session->plugin->start(session);
+ }
+ tsk_list_push_back_data(self->sessions, (void**)(&session));
+ self->state_changed = tsk_true;
+ }
+ }
+ }
+ }
+
+ return self->state_changed ? 0 : -2;
+}
+
+/**@ingroup tmedia_session_group
+* Removes medias from the manager. This action will stop the media and sets it's port value to zero (up to the session).
+* @param self The session manager
+* @param The types of the medias to remove (ou can combine several medias. e.g. audio|video|msrp)
+* @retval Zero if succeed and non zero error code otherwise.
+*/
+int tmedia_session_mgr_remove_media(tmedia_session_mgr_t* self, tmedia_type_t type)
+{
+ const tsk_list_item_t* item;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ tmedia_session_t* session = TMEDIA_SESSION(item->data);
+ if(((session->type & type) == session->type) && session->plugin->stop){
+ if(!session->plugin->stop(session)){
+ self->state_changed = tsk_true;
+ }
+ }
+ }
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Sets QoS type and strength
+* @param self The session manager
+* @param qos_type The QoS type
+* @param qos_strength The QoS strength
+* @retval Zero if succeed and non-zero error code otherwise
+*/
+int tmedia_session_mgr_set_qos(tmedia_session_mgr_t* self, tmedia_qos_stype_t qos_type, tmedia_qos_strength_t qos_strength)
+{
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ self->qos.type = qos_type;
+ self->qos.strength = qos_strength;
+ return 0;
+}
+
+/**@ingroup tmedia_session_group
+* Indicates whether all preconditions are met
+* @param self The session manager
+* @retval @a tsk_true if all preconditions have been met and @a tsk_false otherwise
+*/
+tsk_bool_t tmedia_session_mgr_canresume(tmedia_session_mgr_t* self)
+{
+ const tsk_list_item_t* item;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_true;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ tmedia_session_t* session = TMEDIA_SESSION(item->data);
+ if(session && session->qos && !tmedia_qos_tline_canresume(session->qos)){
+ return tsk_false;
+ }
+ }
+ return tsk_true;
+}
+
+
+/**@ingroup tmedia_session_group
+* Checks whether the manager holds at least one valid session (media port <> 0)
+*/
+tsk_bool_t tmedia_session_mgr_has_active_session(tmedia_session_mgr_t* self)
+{
+ const tsk_list_item_t* item;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_false;
+ }
+
+ tsk_list_foreach(item, self->sessions){
+ tmedia_session_t* session = TMEDIA_SESSION(item->data);
+ if(session && session->M.lo && session->M.lo->port){
+ return tsk_true;
+ }
+ }
+ return tsk_false;
+}
+
+int tmedia_session_mgr_send_dtmf(tmedia_session_mgr_t* self, uint8_t event)
+{
+ tmedia_session_audio_t* session;
+ tmedia_type_t audio_type = tmedia_audio;
+ int ret = -3;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ session = (tmedia_session_audio_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &audio_type);
+ if(session){
+ session = tsk_object_ref(session);
+ ret = tmedia_session_audio_send_dtmf(TMEDIA_SESSION_AUDIO(session), event);
+ TSK_OBJECT_SAFE_FREE(session);
+ }
+ else{
+ TSK_DEBUG_ERROR("No audio session associated to this manager");
+ }
+
+ return ret;
+}
+
+int tmedia_session_mgr_send_file(tmedia_session_mgr_t* self, const char* path, ...)
+{
+ tmedia_session_msrp_t* session;
+ tmedia_type_t msrp_type = tmedia_msrp;
+ int ret = -3;
+
+ if(!self || !path){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type);
+ if(session && session->send_file){
+ va_list ap;
+ va_start(ap, path);
+ session = tsk_object_ref(session);
+ ret = session->send_file(TMEDIA_SESSION_MSRP(session), path, &ap);
+ TSK_OBJECT_SAFE_FREE(session);
+ va_end(ap);
+ }
+ else{
+ TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer");
+ }
+
+ return ret;
+}
+
+int tmedia_session_mgr_send_message(tmedia_session_mgr_t* self, const void* data, tsk_size_t size, const tmedia_params_L_t *params)
+{
+ tmedia_session_msrp_t* session;
+ tmedia_type_t msrp_type = tmedia_msrp;
+ int ret = -3;
+
+ if(!self || !size || !data){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type);
+ if(session && session->send_message){
+ session = tsk_object_ref(session);
+ ret = session->send_message(TMEDIA_SESSION_MSRP(session), data, size, params);
+ TSK_OBJECT_SAFE_FREE(session);
+ }
+ else{
+ TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer");
+ }
+
+ return ret;
+}
+
+int tmedia_session_mgr_set_msrp_cb(tmedia_session_mgr_t* self, const void* callback_data, tmedia_session_msrp_cb_f func)
+{
+ tmedia_session_msrp_t* session;
+ tmedia_type_t msrp_type = tmedia_msrp;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ if((session = (tmedia_session_msrp_t*)tsk_list_find_object_by_pred(self->sessions, __pred_find_session_by_type, &msrp_type))){
+ session->callback.data = callback_data;
+ session->callback.func = func;
+ return 0;
+ }
+ else{
+ TSK_DEBUG_ERROR("No MSRP session associated to this manager or session does not support file transfer");
+ return -2;
+ }
+}
+
+/** internal function used to load sessions */
+int _tmedia_session_mgr_load_sessions(tmedia_session_mgr_t* self)
+{
+ tsk_size_t i = 0;
+ tmedia_session_t* session;
+ const tmedia_session_plugin_def_t* plugin;
+
+ if(TSK_LIST_IS_EMPTY(self->sessions)){
+ /* for each registered plugin create a session instance */
+ while((i < TMED_SESSION_MAX_PLUGINS) && (plugin = __tmedia_session_plugins[i++])){
+ if((plugin->type & self->type) == plugin->type){
+ if((session = tmedia_session_create(plugin->type))){
+ tsk_list_push_back_data(self->sessions, (void**)(&session));
+ }
+ }
+ }
+ /* set default values */
+ tmedia_session_mgr_set(self,
+ TMEDIA_SESSION_SET_STR(self->type, "local-ip", self->addr),
+ TMEDIA_SESSION_SET_STR(self->type, "local-ipver", self->ipv6 ? "ipv6" : "ipv4"),
+ TMEDIA_SESSION_SET_INT32(self->type, "bandwidth-level", self->bl),
+ TMEDIA_SESSION_SET_NULL());
+
+ /* load params */
+ _tmedia_session_mgr_apply_params(self);
+ }
+ return 0;
+}
+
+/* internal function */
+int _tmedia_session_mgr_apply_params(tmedia_session_mgr_t* self)
+{
+ tsk_list_item_t *it1, *it2;
+ tmedia_param_t* param;
+ tmedia_session_t* session;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ /* If no parameters ==> do nothing (not error) */
+ if(TSK_LIST_IS_EMPTY(self->params)){
+ return 0;
+ }
+
+ tsk_list_lock(self->params);
+
+ tsk_list_foreach(it1, self->params){
+ if(!(param = it1->data)){
+ continue;
+ }
+
+ /* For us */
+ if(param->plugin_type == tmedia_ppt_manager){
+ continue;
+ }
+
+ /* For the session (or consumer or producer or codec) */
+ tsk_list_foreach(it2, self->sessions){
+ if(!(session = it2->data) || !session->plugin){
+ continue;
+ }
+ if((session->type & param->media_type) == session->type && session->plugin->set){
+ session->plugin->set(session, param);
+ }
+ }
+ }
+
+ /* Clean up params */
+ tsk_list_clear_items(self->params);
+
+ tsk_list_unlock(self->params);
+
+ return 0;
+}
+
+//=================================================================================================
+// Media Session Manager object definition
+//
+static tsk_object_t* tmedia_session_mgr_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_session_mgr_t *mgr = self;
+ if(mgr){
+ mgr->sessions = tsk_list_create();
+
+ mgr->sdp.lo_ver = TSDP_HEADER_O_SESS_VERSION_DEFAULT;
+ mgr->sdp.ro_ver = -1;
+
+ mgr->qos.type = tmedia_qos_stype_none;
+ mgr->qos.strength = tmedia_qos_strength_optional;
+ mgr->bl = tmedia_bl_low;
+ }
+ return self;
+}
+
+static tsk_object_t* tmedia_session_mgr_dtor(tsk_object_t * self)
+{
+ tmedia_session_mgr_t *mgr = self;
+ if(mgr){
+ TSK_OBJECT_SAFE_FREE(mgr->sessions);
+
+ TSK_OBJECT_SAFE_FREE(mgr->sdp.lo);
+ TSK_OBJECT_SAFE_FREE(mgr->sdp.ro);
+
+ TSK_OBJECT_SAFE_FREE(mgr->params);
+
+ TSK_OBJECT_SAFE_FREE(mgr->natt_ctx);
+ TSK_FREE(mgr->public_addr);
+
+ TSK_FREE(mgr->addr);
+ }
+
+ return self;
+}
+
+static const tsk_object_def_t tmedia_session_mgr_def_s =
+{
+ sizeof(tmedia_session_mgr_t),
+ tmedia_session_mgr_ctor,
+ tmedia_session_mgr_dtor,
+ tsk_null,
+};
+const tsk_object_def_t *tmedia_session_mgr_def_t = &tmedia_session_mgr_def_s;
+
diff --git a/tinyMEDIA/src/tmedia_session_dummy.c b/tinyMEDIA/src/tmedia_session_dummy.c
new file mode 100644
index 0000000..f77fccd
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_session_dummy.c
@@ -0,0 +1,472 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_session_dummy.c
+ * @brief Dummy sessions used for test only.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_session_dummy.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ Audio Session ================= */
+
+int tmedia_session_daudio_set(tmedia_session_t* self, const tmedia_param_t* param)
+{
+ tmedia_session_daudio_t* daudio;
+
+ daudio = (tmedia_session_daudio_t*)self;
+
+ return 0;
+}
+
+int tmedia_session_daudio_prepare(tmedia_session_t* self)
+{
+ tmedia_session_daudio_t* daudio;
+
+ daudio = (tmedia_session_daudio_t*)self;
+
+ /* set local port */
+ daudio->local_port = rand() ^ rand();
+
+ return 0;
+}
+
+int tmedia_session_daudio_start(tmedia_session_t* self)
+{
+ return 0;
+}
+
+int tmedia_session_daudio_stop(tmedia_session_t* self)
+{
+ tmedia_session_daudio_t* daudio;
+
+ daudio = (tmedia_session_daudio_t*)self;
+
+ /* very important */
+ daudio->local_port = 0;
+
+ return 0;
+}
+
+int tmedia_session_daudio_send_dtmf(tmedia_session_t* self, uint8_t event)
+{
+ return 0;
+}
+
+int tmedia_session_daudio_pause(tmedia_session_t* self)
+{
+ return 0;
+}
+
+const tsdp_header_M_t* tmedia_session_daudio_get_lo(tmedia_session_t* self)
+{
+ tmedia_session_daudio_t* daudio;
+ tsk_bool_t changed = tsk_false;
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ daudio = (tmedia_session_daudio_t*)self;
+
+ if(self->ro_changed && self->M.lo){
+ /* Codecs */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "fmtp");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "rtpmap");
+ tsk_list_clear_items(self->M.lo->FMTs);
+
+ /* QoS */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "curr");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "des");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "conf");
+ }
+
+ changed = (self->ro_changed || !self->M.lo);
+
+ if(!self->M.lo && !(self->M.lo = tsdp_header_M_create(self->plugin->media, daudio->local_port, "RTP/AVP"))){
+ TSK_DEBUG_ERROR("Failed to create lo");
+ return tsk_null;
+ }
+
+ if(changed){
+ /* from codecs to sdp */
+ tmedia_codec_to_sdp(self->neg_codecs ? self->neg_codecs : self->codecs, self->M.lo);
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ tmedia_qos_tline_to_sdp(self->qos, self->M.lo);
+ }
+ }
+
+
+ return self->M.lo;
+}
+
+int tmedia_session_daudio_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ tmedia_codecs_L_t* neg_codecs;
+
+ if((neg_codecs = tmedia_session_match_codec(self, m))){
+ /* update negociated codecs */
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+ self->neg_codecs = neg_codecs;
+ /* update remote offer */
+ TSK_OBJECT_SAFE_FREE(self->M.ro);
+ self->M.ro = tsk_object_ref((void*)m);
+
+ return 0;
+ }
+ return -1;
+}
+
+/* ============ Video Session ================= */
+
+int tmedia_session_dvideo_set(tmedia_session_t* self, const tmedia_param_t* param)
+{
+ tmedia_session_dvideo_t* dvideo;
+
+ dvideo = (tmedia_session_dvideo_t*)self;
+
+ return 0;
+}
+
+int tmedia_session_dvideo_prepare(tmedia_session_t* self)
+{
+ tmedia_session_dvideo_t* dvideo;
+
+ dvideo = (tmedia_session_dvideo_t*)self;
+
+ /* set local port */
+ dvideo->local_port = rand() ^ rand();
+
+ return 0;
+}
+
+int tmedia_session_dvideo_start(tmedia_session_t* self)
+{
+ return -1;
+}
+
+int tmedia_session_dvideo_stop(tmedia_session_t* self)
+{
+ tmedia_session_dvideo_t* dvideo;
+
+ dvideo = (tmedia_session_dvideo_t*)self;
+
+ /* very important */
+ dvideo->local_port = 0;
+
+ return 0;
+}
+
+int tmedia_session_dvideo_pause(tmedia_session_t* self)
+{
+ return -1;
+}
+
+const tsdp_header_M_t* tmedia_session_dvideo_get_lo(tmedia_session_t* self)
+{
+ tmedia_session_dvideo_t* dvideo;
+ tsk_bool_t changed = tsk_false;
+
+ if(!self || !self->plugin){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return tsk_null;
+ }
+
+ dvideo = (tmedia_session_dvideo_t*)self;
+
+ if(self->ro_changed && self->M.lo){
+ /* Codecs */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "fmtp");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "rtpmap");
+ tsk_list_clear_items(self->M.lo->FMTs);
+
+ /* QoS */
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "curr");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "des");
+ tsdp_header_A_removeAll_by_field(self->M.lo->Attributes, "conf");
+ }
+
+ changed = (self->ro_changed || !self->M.lo);
+
+ if(!self->M.lo && !(self->M.lo = tsdp_header_M_create(self->plugin->media, dvideo->local_port, "RTP/AVP"))){
+ TSK_DEBUG_ERROR("Failed to create lo");
+ return tsk_null;
+ }
+
+ if(changed){
+ /* from codecs to sdp */
+ tmedia_codec_to_sdp(self->neg_codecs ? self->neg_codecs : self->codecs, self->M.lo);
+ /* QoS */
+ if(self->qos){
+ tmedia_qos_tline_t* ro_tline;
+ if(self->M.ro && (ro_tline = tmedia_qos_tline_from_sdp(self->M.ro))){
+ tmedia_qos_tline_set_ro(self->qos, ro_tline);
+ TSK_OBJECT_SAFE_FREE(ro_tline);
+ }
+ tmedia_qos_tline_to_sdp(self->qos, self->M.lo);
+ }
+ }
+
+ return self->M.lo;
+}
+
+int tmedia_session_dvideo_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ tmedia_codecs_L_t* neg_codecs;
+
+ if((neg_codecs = tmedia_session_match_codec(self, m))){
+ /* update negociated codecs */
+ TSK_OBJECT_SAFE_FREE(self->neg_codecs);
+ self->neg_codecs = neg_codecs;
+ /* update remote offer */
+ TSK_OBJECT_SAFE_FREE(self->M.ro);
+ self->M.ro = tsk_object_ref((void*)m);
+
+ return 0;
+ }
+ return -1;
+}
+
+/* ============ Msrp Session ================= */
+
+int tmedia_session_dmsrp_set(tmedia_session_t* self, const tmedia_param_t* param)
+{
+ tmedia_session_dmsrp_t* dmsrp;
+
+ dmsrp = (tmedia_session_dmsrp_t*)self;
+
+ return 0;
+}
+
+int tmedia_session_dmsrp_prepare(tmedia_session_t* self)
+{
+ return 0;
+}
+
+int tmedia_session_dmsrp_start(tmedia_session_t* self)
+{
+ return 0;
+}
+
+int tmedia_session_dmsrp_stop(tmedia_session_t* self)
+{
+ return 0;
+}
+
+int tmedia_session_dmsrp_pause(tmedia_session_t* self)
+{
+ return 0;
+}
+
+const tsdp_header_M_t* tmedia_session_dmsrp_get_lo(tmedia_session_t* self)
+{
+ if(self->ro_changed){
+ TSK_OBJECT_SAFE_FREE(self->M.lo);
+ }
+
+ return tsk_null;
+}
+
+int tmedia_session_dmsrp_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ return -1;
+}
+
+//=================================================================================================
+// Dummy Audio session object definition
+//
+/* constructor */
+static tsk_object_t* tmedia_session_daudio_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_session_daudio_t *session = self;
+ if(session){
+ /* init base: called by tmedia_session_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_session_daudio_dtor(tsk_object_t * self)
+{
+ tmedia_session_daudio_t *session = self;
+ if(session){
+ /* deinit base */
+ tmedia_session_deinit(self);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_session_daudio_def_s =
+{
+ sizeof(tmedia_session_daudio_t),
+ tmedia_session_daudio_ctor,
+ tmedia_session_daudio_dtor,
+ tmedia_session_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tmedia_session_daudio_plugin_def_s =
+{
+ &tmedia_session_daudio_def_s,
+
+ tmedia_audio,
+ "audio",
+
+ tmedia_session_daudio_set,
+ tmedia_session_daudio_prepare,
+ tmedia_session_daudio_start,
+ tmedia_session_daudio_pause,
+ tmedia_session_daudio_stop,
+
+ /* Audio part */
+ { tsk_null },
+
+ tmedia_session_daudio_get_lo,
+ tmedia_session_daudio_set_ro
+};
+const tmedia_session_plugin_def_t *tmedia_session_daudio_plugin_def_t = &tmedia_session_daudio_plugin_def_s;
+
+
+//=================================================================================================
+// Dummy Video session object definition
+//
+/* constructor */
+static tsk_object_t* tmedia_session_dvideo_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_session_dvideo_t *session = self;
+ if(session){
+ /* init base: called by tmedia_session_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_session_dvideo_dtor(tsk_object_t * self)
+{
+ tmedia_session_dvideo_t *session = self;
+ if(session){
+ /* deinit base */
+ tmedia_session_deinit(self);
+ /* deinit self */
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_session_dvideo_def_s =
+{
+ sizeof(tmedia_session_dvideo_t),
+ tmedia_session_dvideo_ctor,
+ tmedia_session_dvideo_dtor,
+ tmedia_session_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tmedia_session_dvideo_plugin_def_s =
+{
+ &tmedia_session_dvideo_def_s,
+
+ tmedia_video,
+ "video",
+
+ tmedia_session_dvideo_set,
+ tmedia_session_dvideo_prepare,
+ tmedia_session_dvideo_start,
+ tmedia_session_dvideo_pause,
+ tmedia_session_dvideo_stop,
+
+ /* Audio part */
+ { tsk_null },
+
+ tmedia_session_dvideo_get_lo,
+ tmedia_session_dvideo_set_ro
+};
+const tmedia_session_plugin_def_t *tmedia_session_dvideo_plugin_def_t = &tmedia_session_dvideo_plugin_def_s;
+
+
+//=================================================================================================
+// Dummy Msrp session object definition
+//
+/* constructor */
+static tsk_object_t* tmedia_session_dmsrp_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_session_dmsrp_t *session = self;
+ if(session){
+ /* init base: called by tmedia_session_create() */
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_session_dmsrp_dtor(tsk_object_t * self)
+{
+ tmedia_session_dmsrp_t *session = self;
+ if(session){
+ /* deinit base */
+ tmedia_session_deinit(self);
+ /* deinit self */
+
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_session_dmsrp_def_s =
+{
+ sizeof(tmedia_session_dmsrp_t),
+ tmedia_session_dmsrp_ctor,
+ tmedia_session_dmsrp_dtor,
+ tmedia_session_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tmedia_session_dmsrp_plugin_def_s =
+{
+ &tmedia_session_dmsrp_def_s,
+
+ tmedia_msrp,
+ "message",
+
+ tmedia_session_dmsrp_set,
+ tmedia_session_dmsrp_prepare,
+ tmedia_session_dmsrp_start,
+ tmedia_session_dmsrp_pause,
+ tmedia_session_dmsrp_stop,
+
+ /* Audio part */
+ { tsk_null },
+
+ tmedia_session_dmsrp_get_lo,
+ tmedia_session_dmsrp_set_ro
+};
+const tmedia_session_plugin_def_t *tmedia_session_dmsrp_plugin_def_t = &tmedia_session_dmsrp_plugin_def_s;
diff --git a/tinyMEDIA/src/tmedia_session_ghost.c b/tinyMEDIA/src/tmedia_session_ghost.c
new file mode 100644
index 0000000..d69e307
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_session_ghost.c
@@ -0,0 +1,141 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO 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 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO 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 DOUBANGO.
+*
+*/
+
+/**@file tmedia_session_ghost.c
+ * @brief Ghost session.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinymedia/tmedia_session_ghost.h"
+
+#include "tsk_memory.h"
+#include "tsk_debug.h"
+
+/* ============ Ghost Session ================= */
+
+int tmedia_session_ghost_prepare(tmedia_session_t* self)
+{
+ return 0;
+}
+
+int tmedia_session_ghost_start(tmedia_session_t* self)
+{
+ return 0;
+}
+
+int tmedia_session_ghost_stop(tmedia_session_t* self)
+{
+ TSK_DEBUG_INFO("tmedia_session_ghost_stop");
+ return 0;
+}
+
+int tmedia_session_ghost_pause(tmedia_session_t* self)
+{
+ return 0;
+}
+
+const tsdp_header_M_t* tmedia_session_ghost_get_lo(tmedia_session_t* self)
+{
+ tmedia_session_ghost_t* ghost;
+
+ ghost = (tmedia_session_ghost_t*)self;
+
+ if(self->M.lo){
+ return self->M.lo;
+ }
+ else if(!(self->M.lo = tsdp_header_M_create(ghost->media, 0, "RTP/AVP"))){
+ TSK_DEBUG_ERROR("Failed to create lo");
+ return tsk_null;
+ }
+
+ return self->M.lo;
+}
+
+int tmedia_session_ghost_set_ro(tmedia_session_t* self, const tsdp_header_M_t* m)
+{
+ return 0;
+}
+
+
+
+
+
+
+
+//=================================================================================================
+// Ghost session object definition
+//
+/* constructor */
+static tsk_object_t* tmedia_session_ghost_ctor(tsk_object_t * self, va_list * app)
+{
+ tmedia_session_ghost_t *session = self;
+ if(session){
+ /* init base */
+ tmedia_session_init(TMEDIA_SESSION(session), tmedia_none);
+ /* init self */
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tmedia_session_ghost_dtor(tsk_object_t * self)
+{
+ tmedia_session_ghost_t *session = self;
+ if(session){
+ /* deinit base */
+ tmedia_session_deinit(TMEDIA_SESSION(session));
+ /* deinit self */
+ TSK_FREE(session->media);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tmedia_session_ghost_def_s =
+{
+ sizeof(tmedia_session_ghost_t),
+ tmedia_session_ghost_ctor,
+ tmedia_session_ghost_dtor,
+ tmedia_codec_cmp,
+};
+/* plugin definition*/
+static const tmedia_session_plugin_def_t tmedia_session_ghost_plugin_def_s =
+{
+ &tmedia_session_ghost_def_s,
+
+ tmedia_ghost,
+ "ghost",
+
+ tsk_null,
+ tmedia_session_ghost_prepare,
+ tmedia_session_ghost_start,
+ tmedia_session_ghost_stop,
+ tmedia_session_ghost_pause,
+
+ /* Audio part */
+ { tsk_null },
+
+ tmedia_session_ghost_get_lo,
+ tmedia_session_ghost_set_ro
+};
+const tmedia_session_plugin_def_t *tmedia_session_ghost_plugin_def_t = &tmedia_session_ghost_plugin_def_s;
diff --git a/tinyMEDIA/src/tmedia_vad.c b/tinyMEDIA/src/tmedia_vad.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tinyMEDIA/src/tmedia_vad.c
OpenPOWER on IntegriCloud