* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef02aa403c02083b4d783c589a38d537a7f6c885.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef02aa403c02083b4d783c589a38d537a7f6c885.svn-base
new file mode 100644
index 0000000..0804d7a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef02aa403c02083b4d783c589a38d537a7f6c885.svn-base
@@ -0,0 +1,1453 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if defined(PJMEDIA_VIDEO_DEV_HAS_SDL) && PJMEDIA_VIDEO_DEV_HAS_SDL != 0
+#include <SDL.h>
+#include <SDL_syswm.h>
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+# include "SDL_opengl.h"
+# define OPENGL_DEV_IDX 1
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+#if !(SDL_VERSION_ATLEAST(1,3,0))
+# error "SDL 1.3 or later is required"
+#endif
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+# include "TargetConditionals.h"
+# include <Foundation/Foundation.h>
+#endif
+
+#define THIS_FILE "sdl_dev.c"
+#define DEFAULT_CLOCK_RATE 90000
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define DEFAULT_FPS 25
+
+typedef struct sdl_fmt_info
+{
+ pjmedia_format_id fmt_id;
+ Uint32 sdl_format;
+ Uint32 Rmask;
+ Uint32 Gmask;
+ Uint32 Bmask;
+ Uint32 Amask;
+} sdl_fmt_info;
+
+static sdl_fmt_info sdl_fmts[] =
+{
+#if PJ_IS_BIG_ENDIAN
+ {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_RGBA8888,
+ 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
+ {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_RGB24,
+ 0xFF0000, 0xFF00, 0xFF, 0} ,
+ {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_BGRA8888,
+ 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
+#else /* PJ_IS_BIG_ENDIAN */
+ {PJMEDIA_FORMAT_RGBA, (Uint32)SDL_PIXELFORMAT_ABGR8888,
+ 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
+ {PJMEDIA_FORMAT_RGB24, (Uint32)SDL_PIXELFORMAT_BGR24,
+ 0xFF, 0xFF00, 0xFF0000, 0} ,
+ {PJMEDIA_FORMAT_BGRA, (Uint32)SDL_PIXELFORMAT_ARGB8888,
+ 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
+#endif /* PJ_IS_BIG_ENDIAN */
+
+ {PJMEDIA_FORMAT_DIB , (Uint32)SDL_PIXELFORMAT_RGB24,
+ 0xFF0000, 0xFF00, 0xFF, 0} ,
+
+ {PJMEDIA_FORMAT_YUY2, SDL_PIXELFORMAT_YUY2, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_UYVY, SDL_PIXELFORMAT_UYVY, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_YVYU, SDL_PIXELFORMAT_YVYU, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I420, SDL_PIXELFORMAT_IYUV, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_YV12, SDL_PIXELFORMAT_YV12, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I420JPEG, SDL_PIXELFORMAT_IYUV, 0, 0, 0, 0} ,
+ {PJMEDIA_FORMAT_I422JPEG, SDL_PIXELFORMAT_YV12, 0, 0, 0, 0}
+};
+
+/* sdl_ device info */
+struct sdl_dev_info
+{
+ pjmedia_vid_dev_info info;
+};
+
+/* Linked list of streams */
+struct stream_list
+{
+ PJ_DECL_LIST_MEMBER(struct stream_list);
+ struct sdl_stream *stream;
+};
+
+#define INITIAL_MAX_JOBS 64
+#define JOB_QUEUE_INC_FACTOR 2
+
+typedef pj_status_t (*job_func_ptr)(void *data);
+
+typedef struct job {
+ job_func_ptr func;
+ void *data;
+ unsigned flags;
+ pj_status_t retval;
+} job;
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+@interface JQDelegate: NSObject
+{
+ @public
+ job *pjob;
+}
+
+- (void)run_job;
+@end
+
+@implementation JQDelegate
+- (void)run_job
+{
+ pjob->retval = (*pjob->func)(pjob->data);
+}
+@end
+#endif /* PJ_DARWINOS */
+
+typedef struct job_queue {
+ pj_pool_t *pool;
+ job **jobs;
+ pj_sem_t **job_sem;
+ pj_sem_t **old_sem;
+ pj_mutex_t *mutex;
+ pj_thread_t *thread;
+ pj_sem_t *sem;
+
+ unsigned size;
+ unsigned head, tail;
+ pj_bool_t is_full;
+ pj_bool_t is_quitting;
+} job_queue;
+
+/* sdl_ factory */
+struct sdl_factory
+{
+ pjmedia_vid_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+
+ unsigned dev_count;
+ struct sdl_dev_info *dev_info;
+ job_queue *jq;
+
+ pj_thread_t *sdl_thread; /**< SDL thread. */
+ pj_sem_t *sem;
+ pj_mutex_t *mutex;
+ struct stream_list streams;
+ pj_bool_t is_quitting;
+ pj_thread_desc thread_desc;
+ pj_thread_t *ev_thread;
+};
+
+/* Video stream. */
+struct sdl_stream
+{
+ pjmedia_vid_dev_stream base; /**< Base stream */
+ pjmedia_vid_dev_param param; /**< Settings */
+ pj_pool_t *pool; /**< Memory pool. */
+
+ pjmedia_vid_dev_cb vid_cb; /**< Stream callback. */
+ void *user_data; /**< Application data. */
+
+ struct sdl_factory *sf;
+ const pjmedia_frame *frame;
+ pj_bool_t is_running;
+ pj_timestamp last_ts;
+ struct stream_list list_entry;
+
+ SDL_Window *window; /**< Display window. */
+ SDL_Renderer *renderer; /**< Display renderer. */
+ SDL_Texture *scr_tex; /**< Screen texture. */
+ int pitch; /**< Pitch value. */
+ SDL_Rect rect; /**< Frame rectangle. */
+ SDL_Rect dstrect; /**< Display rectangle. */
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ SDL_GLContext *gl_context;
+ GLuint texture;
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+ pjmedia_video_apply_fmt_param vafp;
+};
+
+/* Prototypes */
+static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info);
+static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param);
+static pj_status_t sdl_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm);
+
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_param *param);
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ void *value);
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
+ pjmedia_vid_dev_cap cap,
+ const void *value);
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame);
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+static pj_status_t resize_disp(struct sdl_stream *strm,
+ pjmedia_rect_size *new_disp_size);
+static pj_status_t sdl_destroy_all(void *data);
+
+/* Job queue prototypes */
+static pj_status_t job_queue_create(pj_pool_t *pool, job_queue **pjq);
+static pj_status_t job_queue_post_job(job_queue *jq, job_func_ptr func,
+ void *data, unsigned flags,
+ pj_status_t *retval);
+static pj_status_t job_queue_destroy(job_queue *jq);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+ &sdl_factory_init,
+ &sdl_factory_destroy,
+ &sdl_factory_get_dev_count,
+ &sdl_factory_get_dev_info,
+ &sdl_factory_default_param,
+ &sdl_factory_create_stream,
+ &sdl_factory_refresh
+};
+
+static pjmedia_vid_dev_stream_op stream_op =
+{
+ &sdl_stream_get_param,
+ &sdl_stream_get_cap,
+ &sdl_stream_set_cap,
+ &sdl_stream_start,
+ NULL,
+ &sdl_stream_put_frame,
+ &sdl_stream_stop,
+ &sdl_stream_destroy
+};
+
+/*
+ * Util
+ */
+static void sdl_log_err(const char *op)
+{
+ PJ_LOG(1,(THIS_FILE, "%s error: %s", op, SDL_GetError()));
+}
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init sdl_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
+{
+ struct sdl_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+static pj_status_t sdl_init(void * data)
+{
+ PJ_UNUSED_ARG(data);
+
+ if (SDL_Init(SDL_INIT_VIDEO)) {
+ sdl_log_err("SDL_Init()");
+ return PJMEDIA_EVID_INIT;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static struct sdl_stream* find_stream(struct sdl_factory *sf,
+ Uint32 windowID,
+ pjmedia_event *pevent)
+{
+ struct stream_list *it, *itBegin;
+ struct sdl_stream *strm = NULL;
+
+ itBegin = &sf->streams;
+ for (it = itBegin->next; it != itBegin; it = it->next) {
+ if (SDL_GetWindowID(it->stream->window) == windowID)
+ {
+ strm = it->stream;
+ break;
+ }
+ }
+
+ if (strm)
+ pjmedia_event_init(pevent, PJMEDIA_EVENT_NONE, &strm->last_ts,
+ strm);
+
+ return strm;
+}
+
+static pj_status_t handle_event(void *data)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)data;
+ SDL_Event sevent;
+
+ if (!pj_thread_is_registered())
+ pj_thread_register("sdl_ev", sf->thread_desc, &sf->ev_thread);
+
+ while (SDL_PollEvent(&sevent)) {
+ struct sdl_stream *strm = NULL;
+ pjmedia_event pevent;
+
+ pj_mutex_lock(sf->mutex);
+ pevent.type = PJMEDIA_EVENT_NONE;
+ switch(sevent.type) {
+ case SDL_MOUSEBUTTONDOWN:
+ strm = find_stream(sf, sevent.button.windowID, &pevent);
+ pevent.type = PJMEDIA_EVENT_MOUSE_BTN_DOWN;
+ break;
+ case SDL_WINDOWEVENT:
+ strm = find_stream(sf, sevent.window.windowID, &pevent);
+ switch (sevent.window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ pevent.type = PJMEDIA_EVENT_WND_RESIZED;
+ pevent.data.wnd_resized.new_size.w =
+ sevent.window.data1;
+ pevent.data.wnd_resized.new_size.h =
+ sevent.window.data2;
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ pevent.type = PJMEDIA_EVENT_WND_CLOSING;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (strm && pevent.type != PJMEDIA_EVENT_NONE) {
+ pj_status_t status;
+
+ pjmedia_event_publish(NULL, strm, &pevent, 0);
+
+ switch (pevent.type) {
+ case PJMEDIA_EVENT_WND_RESIZED:
+ status = resize_disp(strm, &pevent.data.wnd_resized.new_size);
+ if (status != PJ_SUCCESS)
+ PJ_LOG(3, (THIS_FILE, "Failed resizing the display."));
+ break;
+ case PJMEDIA_EVENT_WND_CLOSING:
+ if (pevent.data.wnd_closing.cancel) {
+ /* Cancel the closing operation */
+ break;
+ }
+
+ /* Proceed to cleanup SDL. App must still call
+ * pjmedia_dev_stream_destroy() when getting WND_CLOSED
+ * event
+ */
+ sdl_stream_stop(&strm->base);
+ sdl_destroy_all(strm);
+ pjmedia_event_init(&pevent, PJMEDIA_EVENT_WND_CLOSED,
+ &strm->last_ts, strm);
+ pjmedia_event_publish(NULL, strm, &pevent, 0);
+
+ /*
+ * Note: don't access the stream after this point, it
+ * might have been destroyed
+ */
+ break;
+ default:
+ /* Just to prevent gcc warning about unused enums */
+ break;
+ }
+ }
+
+ pj_mutex_unlock(sf->mutex);
+ }
+
+ return PJ_SUCCESS;
+}
+
+static int sdl_ev_thread(void *data)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)data;
+
+ while(1) {
+ pj_status_t status;
+
+ pj_mutex_lock(sf->mutex);
+ if (pj_list_empty(&sf->streams)) {
+ pj_mutex_unlock(sf->mutex);
+ /* Wait until there is any stream. */
+ pj_sem_wait(sf->sem);
+ } else
+ pj_mutex_unlock(sf->mutex);
+
+ if (sf->is_quitting)
+ break;
+
+ job_queue_post_job(sf->jq, handle_event, sf, 0, &status);
+
+ pj_thread_sleep(50);
+ }
+
+ return 0;
+}
+
+static pj_status_t sdl_quit(void *data)
+{
+ PJ_UNUSED_ARG(data);
+ SDL_Quit();
+ return PJ_SUCCESS;
+}
+
+/* API: init factory */
+static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ struct sdl_dev_info *ddi;
+ unsigned i, j;
+ pj_status_t status;
+ SDL_version version;
+
+ pj_list_init(&sf->streams);
+
+ status = job_queue_create(sf->pool, &sf->jq);
+ if (status != PJ_SUCCESS)
+ return PJMEDIA_EVID_INIT;
+
+ job_queue_post_job(sf->jq, sdl_init, NULL, 0, &status);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_mutex_create_recursive(sf->pool, "sdl_factory",
+ &sf->mutex);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ status = pj_sem_create(sf->pool, NULL, 0, 1, &sf->sem);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create event handler thread. */
+ status = pj_thread_create(sf->pool, "sdl_thread", sdl_ev_thread,
+ sf, 0, 0, &sf->sdl_thread);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ sf->dev_count = 1;
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ sf->dev_count++;
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+ sf->dev_info = (struct sdl_dev_info*)
+ pj_pool_calloc(sf->pool, sf->dev_count,
+ sizeof(struct sdl_dev_info));
+
+ ddi = &sf->dev_info[0];
+ pj_bzero(ddi, sizeof(*ddi));
+ strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
+ ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+ ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ ddi = &sf->dev_info[OPENGL_DEV_IDX];
+ pj_bzero(ddi, sizeof(*ddi));
+ strncpy(ddi->info.name, "SDL openGL renderer", sizeof(ddi->info.name));
+ ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+ ddi->info.fmt_cnt = 1;
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+ for (i = 0; i < sf->dev_count; i++) {
+ ddi = &sf->dev_info[i];
+ strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
+ ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+ ddi->info.dir = PJMEDIA_DIR_RENDER;
+ ddi->info.has_callback = PJ_FALSE;
+ ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
+ PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
+ ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+ ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
+
+ for (j = 0; j < ddi->info.fmt_cnt; j++) {
+ pjmedia_format *fmt = &ddi->info.fmt[j];
+ pjmedia_format_init_video(fmt, sdl_fmts[j].fmt_id,
+ DEFAULT_WIDTH, DEFAULT_HEIGHT,
+ DEFAULT_FPS, 1);
+ }
+ }
+
+ SDL_VERSION(&version);
+ PJ_LOG(4, (THIS_FILE, "SDL %d.%d initialized",
+ version.major, version.minor));
+
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ pj_pool_t *pool = sf->pool;
+ pj_status_t status;
+
+ pj_assert(pj_list_empty(&sf->streams));
+
+ sf->is_quitting = PJ_TRUE;
+ if (sf->sdl_thread) {
+ pj_sem_post(sf->sem);
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ /* To prevent pj_thread_join() of getting stuck if we are in
+ * the main thread and we haven't finished processing the job
+ * posted by sdl_thread.
+ */
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
+#endif
+ pj_thread_join(sf->sdl_thread);
+ pj_thread_destroy(sf->sdl_thread);
+ }
+
+ if (sf->mutex) {
+ pj_mutex_destroy(sf->mutex);
+ sf->mutex = NULL;
+ }
+
+ if (sf->sem) {
+ pj_sem_destroy(sf->sem);
+ sf->sem = NULL;
+ }
+
+ job_queue_post_job(sf->jq, sdl_quit, NULL, 0, &status);
+ job_queue_destroy(sf->jq);
+
+ sf->pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ return sf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_info *info)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+
+ PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
+ pjmedia_vid_dev_factory *f,
+ unsigned index,
+ pjmedia_vid_dev_param *param)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ struct sdl_dev_info *di = &sf->dev_info[index];
+
+ PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
+
+ PJ_UNUSED_ARG(pool);
+
+ pj_bzero(param, sizeof(*param));
+ param->dir = PJMEDIA_DIR_RENDER;
+ param->rend_id = index;
+ param->cap_id = PJMEDIA_VID_INVALID_DEV;
+
+ /* Set the device capabilities here */
+ param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+ param->fmt.type = PJMEDIA_TYPE_VIDEO;
+ param->clock_rate = DEFAULT_CLOCK_RATE;
+ pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+ return PJ_SUCCESS;
+}
+
+static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
+{
+ unsigned i;
+
+ for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
+ if (sdl_fmts[i].fmt_id == id)
+ return &sdl_fmts[i];
+ }
+
+ return NULL;
+}
+
+static pj_status_t sdl_destroy(void *data)
+{
+ struct sdl_stream *strm = (struct sdl_stream *)data;
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->texture) {
+ glDeleteTextures(1, &strm->texture);
+ strm->texture = 0;
+ }
+ if (strm->gl_context) {
+ SDL_GL_DeleteContext(strm->gl_context);
+ strm->gl_context = NULL;
+ }
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+ if (strm->scr_tex) {
+ SDL_DestroyTexture(strm->scr_tex);
+ strm->scr_tex = NULL;
+ }
+ if (strm->renderer) {
+ SDL_DestroyRenderer(strm->renderer);
+ strm->renderer = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t sdl_destroy_all(void *data)
+{
+ struct sdl_stream *strm = (struct sdl_stream *)data;
+
+ sdl_destroy(data);
+#if !defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE == 0
+ if (strm->window &&
+ !(strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW))
+ {
+ SDL_DestroyWindow(strm->window);
+ }
+ strm->window = NULL;
+#endif /* TARGET_OS_IPHONE */
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t sdl_create_rend(struct sdl_stream * strm,
+ pjmedia_format *fmt)
+{
+ sdl_fmt_info *sdl_info;
+ const pjmedia_video_format_info *vfi;
+ pjmedia_video_format_detail *vfd;
+
+ sdl_info = get_sdl_format_info(fmt->id);
+ vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+ fmt->id);
+ if (!vfi || !sdl_info)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ strm->vafp.size = fmt->det.vid.size;
+ strm->vafp.buffer = NULL;
+ if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS)
+ return PJMEDIA_EVID_BADFORMAT;
+
+ vfd = pjmedia_format_get_video_format_detail(fmt, PJ_TRUE);
+ strm->rect.x = strm->rect.y = 0;
+ strm->rect.w = (Uint16)vfd->size.w;
+ strm->rect.h = (Uint16)vfd->size.h;
+ if (strm->param.disp_size.w == 0)
+ strm->param.disp_size.w = strm->rect.w;
+ if (strm->param.disp_size.h == 0)
+ strm->param.disp_size.h = strm->rect.h;
+ strm->dstrect.x = strm->dstrect.y = 0;
+ strm->dstrect.w = (Uint16)strm->param.disp_size.w;
+ strm->dstrect.h = (Uint16)strm->param.disp_size.h;
+
+ sdl_destroy(strm);
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
+ }
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+ if (!strm->window) {
+ Uint32 flags = 0;
+
+ if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS) {
+ if (!(strm->param.window_flags & PJMEDIA_VID_DEV_WND_BORDER))
+ flags |= SDL_WINDOW_BORDERLESS;
+ if (strm->param.window_flags & PJMEDIA_VID_DEV_WND_RESIZABLE)
+ flags |= SDL_WINDOW_RESIZABLE;
+ } else {
+ flags |= SDL_WINDOW_BORDERLESS;
+ }
+
+ if (!((strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) &&
+ strm->param.window_hide))
+ {
+ flags |= SDL_WINDOW_SHOWN;
+ } else {
+ flags &= ~SDL_WINDOW_SHOWN;
+ flags |= SDL_WINDOW_HIDDEN;
+ }
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->param.rend_id == OPENGL_DEV_IDX)
+ flags |= SDL_WINDOW_OPENGL;
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+ if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+ /* Use the window supplied by the application. */
+ strm->window = SDL_CreateWindowFrom(
+ strm->param.window.info.window);
+ if (!strm->window) {
+ sdl_log_err("SDL_CreateWindowFrom()");
+ return PJMEDIA_EVID_SYSERR;
+ }
+ } else {
+ int x, y;
+
+ x = y = SDL_WINDOWPOS_CENTERED;
+ if (strm->param.flags & PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
+ x = strm->param.window_pos.x;
+ y = strm->param.window_pos.y;
+ }
+
+ /* Create the window where we will draw. */
+ strm->window = SDL_CreateWindow("pjmedia-SDL video",
+ x, y,
+ strm->param.disp_size.w,
+ strm->param.disp_size.h,
+ flags);
+ if (!strm->window) {
+ sdl_log_err("SDL_CreateWindow()");
+ return PJMEDIA_EVID_SYSERR;
+ }
+ }
+ }
+
+ /**
+ * We must call SDL_CreateRenderer in order for draw calls to
+ * affect this window.
+ */
+ strm->renderer = SDL_CreateRenderer(strm->window, -1, 0);
+ if (!strm->renderer) {
+ sdl_log_err("SDL_CreateRenderer()");
+ return PJMEDIA_EVID_SYSERR;
+ }
+
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ strm->gl_context = SDL_GL_CreateContext(strm->window);
+ if (!strm->gl_context) {
+ sdl_log_err("SDL_GL_CreateContext()");
+ return PJMEDIA_EVID_SYSERR;
+ }
+ SDL_GL_MakeCurrent(strm->window, strm->gl_context);
+
+ /* Init some OpenGL settings */
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+
+ /* Init the viewport */
+ glViewport(0, 0, strm->param.disp_size.w, strm->param.disp_size.h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ glOrtho(0.0, (GLdouble)strm->param.disp_size.w,
+ (GLdouble)strm->param.disp_size.h, 0.0, 0.0, 1.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ /* Create a texture */
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ glGenTextures(1, &strm->texture);
+
+ if (!strm->texture)
+ return PJMEDIA_EVID_SYSERR;
+ } else
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+ {
+ strm->scr_tex = SDL_CreateTexture(strm->renderer, sdl_info->sdl_format,
+ SDL_TEXTUREACCESS_STREAMING,
+ strm->rect.w, strm->rect.h);
+ if (strm->scr_tex == NULL) {
+ sdl_log_err("SDL_CreateTexture()");
+ return PJMEDIA_EVID_SYSERR;
+ }
+
+ strm->pitch = strm->rect.w * SDL_BYTESPERPIXEL(sdl_info->sdl_format);
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t sdl_create(void *data)
+{
+ struct sdl_stream *strm = (struct sdl_stream *)data;
+ return sdl_create_rend(strm, &strm->param.fmt);
+}
+
+static pj_status_t resize_disp(struct sdl_stream *strm,
+ pjmedia_rect_size *new_disp_size)
+{
+ pj_memcpy(&strm->param.disp_size, new_disp_size,
+ sizeof(strm->param.disp_size));
+
+ if (strm->scr_tex) {
+ strm->dstrect.x = strm->dstrect.y = 0;
+ strm->dstrect.w = (Uint16)strm->param.disp_size.w;
+ strm->dstrect.h = (Uint16)strm->param.disp_size.h;
+ SDL_RenderSetViewport(strm->renderer, &strm->dstrect);
+ }
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ else if (strm->param.rend_id == OPENGL_DEV_IDX) {
+ sdl_create_rend(strm, &strm->param.fmt);
+ }
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t change_format(struct sdl_stream *strm,
+ pjmedia_format *new_fmt)
+{
+ pj_status_t status;
+
+ /* Recreate SDL renderer */
+ status = sdl_create_rend(strm, (new_fmt? new_fmt :
+ &strm->param.fmt));
+ if (status == PJ_SUCCESS && new_fmt)
+ pjmedia_format_copy(&strm->param.fmt, new_fmt);
+
+ return status;
+}
+
+static pj_status_t put_frame(void *data)
+{
+ struct sdl_stream *stream = (struct sdl_stream *)data;
+ const pjmedia_frame *frame = stream->frame;
+
+ if (stream->scr_tex) {
+ SDL_UpdateTexture(stream->scr_tex, NULL, frame->buf, stream->pitch);
+ SDL_RenderClear(stream->renderer);
+ SDL_RenderCopy(stream->renderer, stream->scr_tex,
+ &stream->rect, &stream->dstrect);
+ SDL_RenderPresent(stream->renderer);
+ }
+#if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+ else if (stream->param.rend_id == OPENGL_DEV_IDX && stream->texture) {
+ glBindTexture(GL_TEXTURE_2D, stream->texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ stream->rect.w, stream->rect.h, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, frame->buf);
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0, 0); glVertex2i(0, 0);
+ glTexCoord2f(1, 0); glVertex2i(stream->param.disp_size.w, 0);
+ glTexCoord2f(0, 1); glVertex2i(0, stream->param.disp_size.h);
+ glTexCoord2f(1, 1);
+ glVertex2i(stream->param.disp_size.w, stream->param.disp_size.h);
+ glEnd();
+ SDL_GL_SwapWindow(stream->window);
+ }
+#endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+
+ return PJ_SUCCESS;
+}
+
+/* API: Put frame from stream */
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+ const pjmedia_frame *frame)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ pj_status_t status;
+
+ stream->last_ts.u64 = frame->timestamp.u64;
+
+ if (!stream->is_running)
+ return PJ_EINVALIDOP;
+
+ if (frame->size==0 || frame->buf==NULL ||
+ frame->size < stream->vafp.framebytes)
+ return PJ_SUCCESS;
+
+ stream->frame = frame;
+ job_queue_post_job(stream->sf->jq, put_frame, strm, 0, &status);
+
+ return status;
+}
+
+/* API: create stream */
+static pj_status_t sdl_factory_create_stream(
+ pjmedia_vid_dev_factory *f,
+ pjmedia_vid_dev_param *param,
+ const pjmedia_vid_dev_cb *cb,
+ void *user_data,
+ pjmedia_vid_dev_stream **p_vid_strm)
+{
+ struct sdl_factory *sf = (struct sdl_factory*)f;
+ pj_pool_t *pool;
+ struct sdl_stream *strm;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL);
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ strm->sf = sf;
+ pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+ pj_list_init(&strm->list_entry);
+ strm->list_entry.stream = strm;
+ strm->user_data = user_data;
+
+ /* Create render stream here */
+ job_queue_post_job(sf->jq, sdl_create, strm, 0, &status);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ pj_mutex_lock(strm->sf->mutex);
+ if (pj_list_empty(&strm->sf->streams))
+ pj_sem_post(strm->sf->sem);
+ pj_list_insert_after(&strm->sf->streams, &strm->list_entry);
+ pj_mutex_unlock(strm->sf->mutex);
+
+ /* Done */
+ strm->base.op = &stream_op;
+ *p_vid_strm = &strm->base;
+
+ return PJ_SUCCESS;
+
+on_error:
+ sdl_stream_destroy(&strm->base);
+ return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_param *pi)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+ &pi->window) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION,
+ &pi->window_pos) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE,
+ &pi->disp_size) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE,
+ &pi->window_hide) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE;
+ }
+ if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS,
+ &pi->window_flags) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
+ }
+
+ return PJ_SUCCESS;
+}
+
+struct strm_cap {
+ struct sdl_stream *strm;
+ pjmedia_vid_dev_cap cap;
+ union {
+ void *pval;
+ const void *cpval;
+ } pval;
+};
+
+static pj_status_t get_cap(void *data)
+{
+ struct strm_cap *scap = (struct strm_cap *)data;
+ struct sdl_stream *strm = scap->strm;
+ pjmedia_vid_dev_cap cap = scap->cap;
+ void *pval = scap->pval.pval;
+
+ if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+ {
+ SDL_SysWMinfo info;
+ SDL_VERSION(&info.version);
+
+ if (SDL_GetWindowWMInfo(strm->window, &info)) {
+ pjmedia_vid_dev_hwnd *wnd = (pjmedia_vid_dev_hwnd *)pval;
+ if (0) { }
+#if defined(SDL_VIDEO_DRIVER_WINDOWS)
+ else if (info.subsystem == SDL_SYSWM_WINDOWS) {
+ wnd->type = PJMEDIA_VID_DEV_HWND_TYPE_WINDOWS;
+ wnd->info.win.hwnd = (void *)info.info.win.window;
+ }
+#endif
+#if defined(SDL_VIDEO_DRIVER_X11)
+ else if (info.subsystem == SDL_SYSWM_X11) {
+ wnd->info.x11.window = (void *)info.info.x11.window;
+ wnd->info.x11.display = (void *)info.info.x11.display;
+ }
+#endif
+#if defined(SDL_VIDEO_DRIVER_COCOA)
+ else if (info.subsystem == SDL_SYSWM_COCOA) {
+ wnd->info.cocoa.window = (void *)info.info.cocoa.window;
+ }
+#endif
+#if defined(SDL_VIDEO_DRIVER_UIKIT)
+ else if (info.subsystem == SDL_SYSWM_UIKIT) {
+ wnd->info.ios.window = (void *)info.info.uikit.window;
+ }
+#endif
+ else {
+ return PJMEDIA_EVID_INVCAP;
+ }
+ return PJ_SUCCESS;
+ } else
+ return PJMEDIA_EVID_INVCAP;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
+ SDL_GetWindowPosition(strm->window, &((pjmedia_coord *)pval)->x,
+ &((pjmedia_coord *)pval)->y);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
+ SDL_GetWindowSize(strm->window, (int *)&((pjmedia_rect_size *)pval)->w,
+ (int *)&((pjmedia_rect_size *)pval)->h);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
+ Uint32 flag = SDL_GetWindowFlags(strm->window);
+ *((pj_bool_t *)pval) = (flag & SDL_WINDOW_HIDDEN)? PJ_TRUE: PJ_FALSE;
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS) {
+ Uint32 flag = SDL_GetWindowFlags(strm->window);
+ unsigned *wnd_flags = (unsigned *)pval;
+ if (!(flag & SDL_WINDOW_BORDERLESS))
+ *wnd_flags |= PJMEDIA_VID_DEV_WND_BORDER;
+ if (flag & SDL_WINDOW_RESIZABLE)
+ *wnd_flags |= PJMEDIA_VID_DEV_WND_RESIZABLE;
+ return PJ_SUCCESS;
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: get capability */
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ void *pval)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+ struct strm_cap scap;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ scap.strm = strm;
+ scap.cap = cap;
+ scap.pval.pval = pval;
+
+ job_queue_post_job(strm->sf->jq, get_cap, &scap, 0, &status);
+
+ return status;
+}
+
+static pj_status_t set_cap(void *data)
+{
+ struct strm_cap *scap = (struct strm_cap *)data;
+ struct sdl_stream *strm = scap->strm;
+ pjmedia_vid_dev_cap cap = scap->cap;
+ const void *pval = scap->pval.cpval;
+
+ if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION) {
+ /**
+ * Setting window's position when the window is hidden also sets
+ * the window's flag to shown (while the window is, actually,
+ * still hidden). This causes problems later when setting/querying
+ * the window's visibility.
+ * See ticket #1429 (http://trac.pjsip.org/repos/ticket/1429)
+ */
+ Uint32 flag = SDL_GetWindowFlags(strm->window);
+ if (flag & SDL_WINDOW_HIDDEN)
+ SDL_ShowWindow(strm->window);
+ SDL_SetWindowPosition(strm->window, ((pjmedia_coord *)pval)->x,
+ ((pjmedia_coord *)pval)->y);
+ if (flag & SDL_WINDOW_HIDDEN)
+ SDL_HideWindow(strm->window);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE) {
+ if (*(pj_bool_t *)pval)
+ SDL_HideWindow(strm->window);
+ else
+ SDL_ShowWindow(strm->window);
+ return PJ_SUCCESS;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_FORMAT) {
+ pj_status_t status;
+
+ status = change_format(strm, (pjmedia_format *)pval);
+ if (status != PJ_SUCCESS) {
+ pj_status_t status_;
+
+ /**
+ * Failed to change the output format. Try to revert
+ * to its original format.
+ */
+ status_ = change_format(strm, &strm->param.fmt);
+ if (status_ != PJ_SUCCESS) {
+ /**
+ * This means that we failed to revert to our
+ * original state!
+ */
+ status = PJMEDIA_EVID_ERR;
+ }
+ }
+
+ return status;
+ } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE) {
+ pjmedia_rect_size *new_size = (pjmedia_rect_size *)pval;
+
+ SDL_SetWindowSize(strm->window, new_size->w, new_size->h);
+ return resize_disp(strm, new_size);
+ }
+
+ return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: set capability */
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
+ pjmedia_vid_dev_cap cap,
+ const void *pval)
+{
+ struct sdl_stream *strm = (struct sdl_stream*)s;
+ struct strm_cap scap;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ scap.strm = strm;
+ scap.cap = cap;
+ scap.pval.cpval = pval;
+
+ job_queue_post_job(strm->sf->jq, set_cap, &scap, 0, &status);
+
+ return status;
+}
+
+/* API: Start stream. */
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+ PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
+
+ stream->is_running = PJ_TRUE;
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Stop stream. */
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+ PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
+
+ stream->is_running = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+ struct sdl_stream *stream = (struct sdl_stream*)strm;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+ sdl_stream_stop(strm);
+
+ job_queue_post_job(stream->sf->jq, sdl_destroy_all, strm, 0, &status);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_mutex_lock(stream->sf->mutex);
+ if (!pj_list_empty(&stream->list_entry))
+ pj_list_erase(&stream->list_entry);
+ pj_mutex_unlock(stream->sf->mutex);
+
+ pj_pool_release(stream->pool);
+
+ return PJ_SUCCESS;
+}
+
+/****************************************************************************
+ * Job queue implementation
+ */
+#if PJ_DARWINOS==0
+static int job_thread(void * data)
+{
+ job_queue *jq = (job_queue *)data;
+
+ while (1) {
+ job *jb;
+
+ /* Wait until there is a job. */
+ pj_sem_wait(jq->sem);
+
+ /* Make sure there is no pending jobs before we quit. */
+ if (jq->is_quitting && jq->head == jq->tail && !jq->is_full)
+ break;
+
+ jb = jq->jobs[jq->head];
+ jb->retval = (*jb->func)(jb->data);
+ /* If job queue is full and we already finish all the pending
+ * jobs, increase the size.
+ */
+ if (jq->is_full && ((jq->head + 1) % jq->size == jq->tail)) {
+ unsigned i, head;
+ pj_status_t status;
+
+ if (jq->old_sem) {
+ for (i = 0; i < jq->size / JOB_QUEUE_INC_FACTOR; i++) {
+ pj_sem_destroy(jq->old_sem[i]);
+ }
+ }
+ jq->old_sem = jq->job_sem;
+
+ /* Double the job queue size. */
+ jq->size *= JOB_QUEUE_INC_FACTOR;
+ pj_sem_destroy(jq->sem);
+ status = pj_sem_create(jq->pool, "thread_sem", 0, jq->size + 1,
+ &jq->sem);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Failed growing SDL job queue size."));
+ return 0;
+ }
+ jq->jobs = (job **)pj_pool_calloc(jq->pool, jq->size,
+ sizeof(job *));
+ jq->job_sem = (pj_sem_t **) pj_pool_calloc(jq->pool, jq->size,
+ sizeof(pj_sem_t *));
+ for (i = 0; i < jq->size; i++) {
+ status = pj_sem_create(jq->pool, "job_sem", 0, 1,
+ &jq->job_sem[i]);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(3, (THIS_FILE, "Failed growing SDL job "
+ "queue size."));
+ return 0;
+ }
+ }
+ jq->is_full = PJ_FALSE;
+ head = jq->head;
+ jq->head = jq->tail = 0;
+ pj_sem_post(jq->old_sem[head]);
+ } else {
+ pj_sem_post(jq->job_sem[jq->head]);
+ jq->head = (jq->head + 1) % jq->size;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static pj_status_t job_queue_create(pj_pool_t *pool, job_queue **pjq)
+{
+ unsigned i;
+ pj_status_t status;
+
+ job_queue *jq = PJ_POOL_ZALLOC_T(pool, job_queue);
+ jq->pool = pool;
+ jq->size = INITIAL_MAX_JOBS;
+ status = pj_sem_create(pool, "thread_sem", 0, jq->size + 1, &jq->sem);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ jq->jobs = (job **)pj_pool_calloc(pool, jq->size, sizeof(job *));
+ jq->job_sem = (pj_sem_t **) pj_pool_calloc(pool, jq->size,
+ sizeof(pj_sem_t *));
+ for (i = 0; i < jq->size; i++) {
+ status = pj_sem_create(pool, "job_sem", 0, 1, &jq->job_sem[i]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+
+ status = pj_mutex_create_recursive(pool, "job_mutex", &jq->mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ PJ_UNUSED_ARG(status);
+#else
+ status = pj_thread_create(pool, "job_th", job_thread, jq, 0, 0,
+ &jq->thread);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+#endif /* PJ_DARWINOS */
+
+ *pjq = jq;
+ return PJ_SUCCESS;
+
+on_error:
+ job_queue_destroy(jq);
+ return status;
+}
+
+static pj_status_t job_queue_post_job(job_queue *jq, job_func_ptr func,
+ void *data, unsigned flags,
+ pj_status_t *retval)
+{
+ job jb;
+ int tail;
+
+ if (jq->is_quitting)
+ return PJ_EBUSY;
+
+ jb.func = func;
+ jb.data = data;
+ jb.flags = flags;
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+ PJ_UNUSED_ARG(tail);
+ NSAutoreleasePool *apool = [[NSAutoreleasePool alloc]init];
+ JQDelegate *jqd = [[JQDelegate alloc]init];
+ jqd->pjob = &jb;
+ [jqd performSelectorOnMainThread:@selector(run_job)
+ withObject:nil waitUntilDone:YES];
+ [jqd release];
+ [apool release];
+#else /* PJ_DARWINOS */
+ pj_mutex_lock(jq->mutex);
+ jq->jobs[jq->tail] = &jb;
+ tail = jq->tail;
+ jq->tail = (jq->tail + 1) % jq->size;
+ if (jq->tail == jq->head) {
+ jq->is_full = PJ_TRUE;
+ PJ_LOG(4, (THIS_FILE, "SDL job queue is full, increasing "
+ "the queue size."));
+ pj_sem_post(jq->sem);
+ /* Wait until our posted job is completed. */
+ pj_sem_wait(jq->job_sem[tail]);
+ pj_mutex_unlock(jq->mutex);
+ } else {
+ pj_mutex_unlock(jq->mutex);
+ pj_sem_post(jq->sem);
+ /* Wait until our posted job is completed. */
+ pj_sem_wait(jq->job_sem[tail]);
+ }
+#endif /* PJ_DARWINOS */
+
+ *retval = jb.retval;
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t job_queue_destroy(job_queue *jq)
+{
+ unsigned i;
+
+ jq->is_quitting = PJ_TRUE;
+
+ if (jq->thread) {
+ pj_sem_post(jq->sem);
+ pj_thread_join(jq->thread);
+ pj_thread_destroy(jq->thread);
+ }
+
+ if (jq->sem) {
+ pj_sem_destroy(jq->sem);
+ jq->sem = NULL;
+ }
+ for (i = 0; i < jq->size; i++) {
+ if (jq->job_sem[i]) {
+ pj_sem_destroy(jq->job_sem[i]);
+ jq->job_sem[i] = NULL;
+ }
+ }
+ if (jq->old_sem) {
+ for (i = 0; i < jq->size / JOB_QUEUE_INC_FACTOR; i++) {
+ if (jq->old_sem[i]) {
+ pj_sem_destroy(jq->old_sem[i]);
+ jq->old_sem[i] = NULL;
+ }
+ }
+ }
+ if (jq->mutex) {
+ pj_mutex_destroy(jq->mutex);
+ jq->mutex = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+#ifdef _MSC_VER
+# if defined(PJMEDIA_SDL_LIB)
+# pragma comment( lib, PJMEDIA_SDL_LIB)
+# elif SDL_VERSION_ATLEAST(2,0,0)
+# pragma comment( lib, "sdl2.lib")
+# elif SDL_VERSION_ATLEAST(1,3,0)
+# pragma comment( lib, "sdl.lib")
+# endif
+# if PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL
+# pragma comment(lib, "OpenGL32.lib")
+# endif /* PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL */
+#endif /* _MSC_VER */
+
+
+#endif /* PJMEDIA_VIDEO_DEV_HAS_SDL */
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef041af3567671b497e099d59ca33dbb4c8c2024.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef041af3567671b497e099d59ca33dbb4c8c2024.svn-base
new file mode 100644
index 0000000..d850301
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef041af3567671b497e099d59ca33dbb4c8c2024.svn-base
@@ -0,0 +1,982 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJNATH_ICE_SESSION_H__
+#define __PJNATH_ICE_SESSION_H__
+
+/**
+ * @file ice_session.h
+ * @brief ICE session management
+ */
+#include <pjnath/types.h>
+#include <pjnath/stun_session.h>
+#include <pjnath/errno.h>
+#include <pj/sock.h>
+#include <pj/timer.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @addtogroup PJNATH_ICE_SESSION
+ * @{
+ *
+ * This module describes #pj_ice_sess, a transport independent ICE session,
+ * part of PJNATH - the Open Source NAT helper library.
+ *
+ * \section pj_ice_sess_sec ICE Session
+ *
+ * An ICE session, represented by #pj_ice_sess structure, is the lowest
+ * abstraction of ICE in PJNATH, and it is used to perform and manage
+ * connectivity checks of transport address candidates <b>within a
+ * single media stream</b> (note: this differs from what is described
+ * in ICE draft, where an ICE session manages the whole media sessions
+ * rather than just a single stream).
+ *
+ * The ICE session described here is independent from any transports,
+ * meaning that the actual network I/O for this session would have to
+ * be performed by the application, or higher layer abstraction.
+ * Using this framework, application would give any incoming packets to
+ * the ICE session, and it would provide the ICE session with a callback
+ * to send outgoing message.
+ *
+ * For higher abstraction of ICE where transport is included, please
+ * see \ref PJNATH_ICE_STREAM_TRANSPORT.
+ *
+ * \subsection pj_ice_sess_using_sec Using The ICE Session
+ *
+ * The steps below describe how to use ICE session. Alternatively application
+ * can use the higher level ICE API, \ref PJNATH_ICE_STREAM_TRANSPORT,
+ * which has provided the integration of ICE with socket transport.
+ *
+ * The steps to use ICE session is similar for both offerer and
+ * answerer:
+ * - create ICE session with #pj_ice_sess_create(). Among other things,
+ * application needs to specify:
+ * - STUN configuration (pj_stun_config), containing STUN settings
+ * such as timeout values and the instances of timer heap and
+ * ioqueue.
+ * - Session name, useful for identifying this session in the log.
+ * - Initial ICE role (#pj_ice_sess_role). The role can be changed
+ * at later time with #pj_ice_sess_change_role(), and ICE session
+ * can also change its role automatically when it detects role
+ * conflict.
+ * - Number of components in the media session.
+ * - Callback to receive ICE events (#pj_ice_sess_cb)
+ * - Optional local ICE username and password. If these arguments
+ * are NULL, they will be generated randomly.
+ * - Add local candidates for each component, with #pj_ice_sess_add_cand().
+ * A candidate is represented with #pj_ice_sess_cand structure.
+ * Each component must be provided with at least one candidate, and
+ * all components must have the same number of candidates. Failing
+ * to comply with this will cause failure during pairing process.
+ * - Create offer to describe local ICE candidates. ICE session does not
+ * provide a function to create such offer, but application should be
+ * able to create one since it knows about all components and candidates.
+ * If application uses \ref PJNATH_ICE_STREAM_TRANSPORT, it can
+ * enumerate local candidates by calling #pj_ice_strans_enum_cands().
+ * Application may use #pj_ice_sess_find_default_cand() to let ICE
+ * session chooses the default transport address to be used in SDP
+ * c= and m= lines.
+ * - Send the offer to remote endpoint using signaling such as SIP.
+ * - Once application has received the answer, it should parse this
+ * answer, build array of remote candidates, and create check lists by
+ * calling #pj_ice_sess_create_check_list(). This process is known as
+ * pairing the candidates, and will result in the creation of check lists.
+ * - Once checklist has been created, application then can call
+ * #pj_ice_sess_start_check() to instruct ICE session to start
+ * performing connectivity checks. The ICE session performs the
+ * connectivity checks by processing each check in the checklists.
+ * - Application will be notified about the result of ICE connectivity
+ * checks via the callback that was given in #pj_ice_sess_create()
+ * above.
+ *
+ * To send data, application calls #pj_ice_sess_send_data(). If ICE
+ * negotiation has not completed, ICE session would simply drop the data,
+ * and return error to caller. If ICE negotiation has completed
+ * successfully, ICE session will in turn call the \a on_tx_pkt
+ * callback of #pj_ice_sess_cb instance that was previously registered
+ * in #pj_ice_sess_create() above.
+ *
+ * When application receives any packets on the underlying sockets, it
+ * must call #pj_ice_sess_on_rx_pkt(). The ICE session will inspect the
+ * packet to decide whether to process it locally (if the packet is a
+ * STUN message and is part of ICE session) or otherwise pass it back to
+ * application via \a on_rx_data callback.
+ */
+
+/**
+ * Forward declaration for checklist.
+ */
+typedef struct pj_ice_sess_checklist pj_ice_sess_checklist;
+
+/**
+ * This enumeration describes the type of an ICE candidate.
+ */
+typedef enum pj_ice_cand_type
+{
+ /**
+ * ICE host candidate. A host candidate represents the actual local
+ * transport address in the host.
+ */
+ PJ_ICE_CAND_TYPE_HOST,
+
+ /**
+ * ICE server reflexive candidate, which represents the public mapped
+ * address of the local address, and is obtained by sending STUN
+ * Binding request from the host candidate to a STUN server.
+ */
+ PJ_ICE_CAND_TYPE_SRFLX,
+
+ /**
+ * ICE peer reflexive candidate, which is the address as seen by peer
+ * agent during connectivity check.
+ */
+ PJ_ICE_CAND_TYPE_PRFLX,
+
+ /**
+ * ICE relayed candidate, which represents the address allocated in
+ * TURN server.
+ */
+ PJ_ICE_CAND_TYPE_RELAYED,
+
+ /**
+ * Number of defined ICE candidate types.
+ */
+ PJ_ICE_CAND_TYPE_MAX
+
+} pj_ice_cand_type;
+
+
+/** Forward declaration for pj_ice_sess */
+typedef struct pj_ice_sess pj_ice_sess;
+
+/** Forward declaration for pj_ice_sess_check */
+typedef struct pj_ice_sess_check pj_ice_sess_check;
+
+
+/**
+ * This structure describes ICE component.
+ * A media stream may require multiple components, each of which has
+ * to work for the media stream as a whole to work. For media streams
+ * based on RTP, there are two components per media stream - one for RTP,
+ * and one for RTCP.
+ */
+typedef struct pj_ice_sess_comp
+{
+ /**
+ * Pointer to ICE check with highest priority which connectivity check
+ * has been successful. The value will be NULL if a no successful check
+ * has not been found for this component.
+ */
+ pj_ice_sess_check *valid_check;
+
+ /**
+ * Pointer to ICE check with highest priority which connectivity check
+ * has been successful and it has been nominated. The value may be NULL
+ * if there is no such check yet.
+ */
+ pj_ice_sess_check *nominated_check;
+
+ /**
+ * The STUN session to be used to send and receive STUN messages for this
+ * component.
+ */
+ pj_stun_session *stun_sess;
+
+} pj_ice_sess_comp;
+
+
+/**
+ * Data structure to be attached to internal message processing.
+ */
+typedef struct pj_ice_msg_data
+{
+ /** Transport ID for this message */
+ unsigned transport_id;
+
+ /** Flag to indicate whether data.req contains data */
+ pj_bool_t has_req_data;
+
+ /** The data */
+ union data {
+ /** Request data */
+ struct request_data {
+ pj_ice_sess *ice; /**< ICE session */
+ pj_ice_sess_checklist *clist; /**< Checklist */
+ unsigned ckid; /**< Check ID */
+ } req;
+ } data;
+
+} pj_ice_msg_data;
+
+
+/**
+ * This structure describes an ICE candidate.
+ * ICE candidate is a transport address that is to be tested by ICE
+ * procedures in order to determine its suitability for usage for
+ * receipt of media. Candidates also have properties - their type
+ * (server reflexive, relayed or host), priority, foundation, and
+ * base.
+ */
+typedef struct pj_ice_sess_cand
+{
+ /**
+ * The candidate type, as described in #pj_ice_cand_type enumeration.
+ */
+ pj_ice_cand_type type;
+
+ /**
+ * Status of this candidate. The value will be PJ_SUCCESS if candidate
+ * address has been resolved successfully, PJ_EPENDING when the address
+ * resolution process is in progress, or other value when the address
+ * resolution has completed with failure.
+ */
+ pj_status_t status;
+
+ /**
+ * The component ID of this candidate. Note that component IDs starts
+ * with one for RTP and two for RTCP. In other words, it's not zero
+ * based.
+ */
+ pj_uint8_t comp_id;
+
+ /**
+ * Transport ID to be used to send packets for this candidate.
+ */
+ pj_uint8_t transport_id;
+
+ /**
+ * Local preference value, which typically is 65535.
+ */
+ pj_uint16_t local_pref;
+
+ /**
+ * The foundation string, which is an identifier which value will be
+ * equivalent for two candidates that are of the same type, share the
+ * same base, and come from the same STUN server. The foundation is
+ * used to optimize ICE performance in the Frozen algorithm.
+ */
+ pj_str_t foundation;
+
+ /**
+ * The candidate's priority, a 32-bit unsigned value which value will be
+ * calculated by the ICE session when a candidate is registered to the
+ * ICE session.
+ */
+ pj_uint32_t prio;
+
+ /**
+ * IP address of this candidate. For host candidates, this represents
+ * the local address of the socket. For reflexive candidates, the value
+ * will be the public address allocated in NAT router for the host
+ * candidate and as reported in MAPPED-ADDRESS or XOR-MAPPED-ADDRESS
+ * attribute of STUN Binding request. For relayed candidate, the value
+ * will be the address allocated in the TURN server by STUN Allocate
+ * request.
+ */
+ pj_sockaddr addr;
+
+ /**
+ * Base address of this candidate. "Base" refers to the address an agent
+ * sends from for a particular candidate. For host candidates, the base
+ * is the same as the host candidate itself. For reflexive candidates,
+ * the base is the local IP address of the socket. For relayed candidates,
+ * the base address is the transport address allocated in the TURN server
+ * for this candidate.
+ */
+ pj_sockaddr base_addr;
+
+ /**
+ * Related address, which is used for informational only and is not used
+ * in any way by the ICE session.
+ */
+ pj_sockaddr rel_addr;
+
+} pj_ice_sess_cand;
+
+
+/**
+ * This enumeration describes the state of ICE check.
+ */
+typedef enum pj_ice_sess_check_state
+{
+ /**
+ * A check for this pair hasn't been performed, and it can't
+ * yet be performed until some other check succeeds, allowing this
+ * pair to unfreeze and move into the Waiting state.
+ */
+ PJ_ICE_SESS_CHECK_STATE_FROZEN,
+
+ /**
+ * A check has not been performed for this pair, and can be
+ * performed as soon as it is the highest priority Waiting pair on
+ * the check list.
+ */
+ PJ_ICE_SESS_CHECK_STATE_WAITING,
+
+ /**
+ * A check has not been performed for this pair, and can be
+ * performed as soon as it is the highest priority Waiting pair on
+ * the check list.
+ */
+ PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS,
+
+ /**
+ * A check has not been performed for this pair, and can be
+ * performed as soon as it is the highest priority Waiting pair on
+ * the check list.
+ */
+ PJ_ICE_SESS_CHECK_STATE_SUCCEEDED,
+
+ /**
+ * A check for this pair was already done and failed, either
+ * never producing any response or producing an unrecoverable failure
+ * response.
+ */
+ PJ_ICE_SESS_CHECK_STATE_FAILED
+
+} pj_ice_sess_check_state;
+
+
+/**
+ * This structure describes an ICE connectivity check. An ICE check
+ * contains a candidate pair, and will involve sending STUN Binding
+ * Request transaction for the purposes of verifying connectivity.
+ * A check is sent from the local candidate to the remote candidate
+ * of a candidate pair.
+ */
+struct pj_ice_sess_check
+{
+ /**
+ * Pointer to local candidate entry of this check.
+ */
+ pj_ice_sess_cand *lcand;
+
+ /**
+ * Pointer to remote candidate entry of this check.
+ */
+ pj_ice_sess_cand *rcand;
+
+ /**
+ * Check priority.
+ */
+ pj_timestamp prio;
+
+ /**
+ * Connectivity check state.
+ */
+ pj_ice_sess_check_state state;
+
+ /**
+ * STUN transmit data containing STUN Binding request that was sent
+ * as part of this check. The value will only be set when this check
+ * has a pending transaction, and is used to cancel the transaction
+ * when other check has succeeded.
+ */
+ pj_stun_tx_data *tdata;
+
+ /**
+ * Flag to indicate whether this check is nominated. A nominated check
+ * contains USE-CANDIDATE attribute in its STUN Binding request.
+ */
+ pj_bool_t nominated;
+
+ /**
+ * When the check failed, this will contain the failure status of the
+ * STUN transaction.
+ */
+ pj_status_t err_code;
+};
+
+
+/**
+ * This enumeration describes ICE checklist state.
+ */
+typedef enum pj_ice_sess_checklist_state
+{
+ /**
+ * The checklist is not yet running.
+ */
+ PJ_ICE_SESS_CHECKLIST_ST_IDLE,
+
+ /**
+ * In this state, ICE checks are still in progress for this
+ * media stream.
+ */
+ PJ_ICE_SESS_CHECKLIST_ST_RUNNING,
+
+ /**
+ * In this state, ICE checks have completed for this media stream,
+ * either successfully or with failure.
+ */
+ PJ_ICE_SESS_CHECKLIST_ST_COMPLETED
+
+} pj_ice_sess_checklist_state;
+
+
+/**
+ * This structure represents ICE check list, that is an ordered set of
+ * candidate pairs that an agent will use to generate checks.
+ */
+struct pj_ice_sess_checklist
+{
+ /**
+ * The checklist state.
+ */
+ pj_ice_sess_checklist_state state;
+
+ /**
+ * Number of candidate pairs (checks).
+ */
+ unsigned count;
+
+ /**
+ * Array of candidate pairs (checks).
+ */
+ pj_ice_sess_check checks[PJ_ICE_MAX_CHECKS];
+
+ /**
+ * A timer used to perform periodic check for this checklist.
+ */
+ pj_timer_entry timer;
+
+};
+
+
+/**
+ * This structure contains callbacks that will be called by the ICE
+ * session.
+ */
+typedef struct pj_ice_sess_cb
+{
+ /**
+ * An optional callback that will be called by the ICE session when
+ * ICE negotiation has completed, successfully or with failure.
+ *
+ * @param ice The ICE session.
+ * @param status Will contain PJ_SUCCESS if ICE negotiation is
+ * successful, or some error code.
+ */
+ void (*on_ice_complete)(pj_ice_sess *ice, pj_status_t status);
+
+ /**
+ * A mandatory callback which will be called by the ICE session when
+ * it needs to send outgoing STUN packet.
+ *
+ * @param ice The ICE session.
+ * @param comp_id ICE component ID.
+ * @param transport_id Transport ID.
+ * @param pkt The STUN packet.
+ * @param size The size of the packet.
+ * @param dst_addr Packet destination address.
+ * @param dst_addr_len Length of destination address.
+ */
+ pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id,
+ unsigned transport_id,
+ const void *pkt, pj_size_t size,
+ const pj_sockaddr_t *dst_addr,
+ unsigned dst_addr_len);
+
+ /**
+ * A mandatory callback which will be called by the ICE session when
+ * it receives packet which is not part of ICE negotiation.
+ *
+ * @param ice The ICE session.
+ * @param comp_id ICE component ID.
+ * @param transport_id Transport ID.
+ * @param pkt The whole packet.
+ * @param size Size of the packet.
+ * @param src_addr Source address where this packet was received
+ * from.
+ * @param src_addr_len The length of source address.
+ */
+ void (*on_rx_data)(pj_ice_sess *ice, unsigned comp_id,
+ unsigned transport_id,
+ void *pkt, pj_size_t size,
+ const pj_sockaddr_t *src_addr,
+ unsigned src_addr_len);
+} pj_ice_sess_cb;
+
+
+/**
+ * This enumeration describes the role of the ICE agent.
+ */
+typedef enum pj_ice_sess_role
+{
+ /**
+ * The role is unknown.
+ */
+ PJ_ICE_SESS_ROLE_UNKNOWN,
+
+ /**
+ * The ICE agent is in controlled role.
+ */
+ PJ_ICE_SESS_ROLE_CONTROLLED,
+
+ /**
+ * The ICE agent is in controlling role.
+ */
+ PJ_ICE_SESS_ROLE_CONTROLLING
+
+} pj_ice_sess_role;
+
+
+/**
+ * This structure represents an incoming check (an incoming Binding
+ * request message), and is mainly used to keep early checks in the
+ * list in the ICE session. An early check is a request received
+ * from remote when we haven't received SDP answer yet, therefore we
+ * can't perform triggered check. For such cases, keep the incoming
+ * request in a list, and we'll do triggered checks (simultaneously)
+ * as soon as we receive answer.
+ */
+typedef struct pj_ice_rx_check
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ice_rx_check); /**< Standard list */
+
+ unsigned comp_id; /**< Component ID. */
+ unsigned transport_id; /**< Transport ID. */
+
+ pj_sockaddr src_addr; /**< Source address of request */
+ unsigned src_addr_len; /**< Length of src address. */
+
+ pj_bool_t use_candidate; /**< USE-CANDIDATE is present? */
+ pj_uint32_t priority; /**< PRIORITY value in the req. */
+ pj_stun_uint64_attr *role_attr; /**< ICE-CONTROLLING/CONTROLLED */
+
+} pj_ice_rx_check;
+
+
+/**
+ * This structure describes various ICE session options. Application
+ * configure the ICE session with these options by calling
+ * #pj_ice_sess_set_options().
+ */
+typedef struct pj_ice_sess_options
+{
+ /**
+ * Specify whether to use aggressive nomination.
+ */
+ pj_bool_t aggressive;
+
+ /**
+ * For controlling agent if it uses regular nomination, specify the delay
+ * to perform nominated check (connectivity check with USE-CANDIDATE
+ * attribute) after all components have a valid pair.
+ *
+ * Default value is PJ_ICE_NOMINATED_CHECK_DELAY.
+ */
+ unsigned nominated_check_delay;
+
+ /**
+ * For a controlled agent, specify how long it wants to wait (in
+ * milliseconds) for the controlling agent to complete sending
+ * connectivity check with nominated flag set to true for all components
+ * after the controlled agent has found that all connectivity checks in
+ * its checklist have been completed and there is at least one successful
+ * (but not nominated) check for every component.
+ *
+ * Default value for this option is
+ * ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable
+ * this timer.
+ */
+ int controlled_agent_want_nom_timeout;
+
+} pj_ice_sess_options;
+
+
+/**
+ * This structure describes the ICE session. For this version of PJNATH,
+ * an ICE session corresponds to a single media stream (unlike the ICE
+ * session described in the ICE standard where an ICE session covers the
+ * whole media and may consist of multiple media streams). The decision
+ * to support only a single media session was chosen for simplicity,
+ * while still allowing application to utilize multiple media streams by
+ * creating multiple ICE sessions, one for each media stream.
+ */
+struct pj_ice_sess
+{
+ char obj_name[PJ_MAX_OBJ_NAME]; /**< Object name. */
+
+ pj_pool_t *pool; /**< Pool instance. */
+ void *user_data; /**< App. data. */
+ pj_grp_lock_t *grp_lock; /**< Group lock */
+ pj_ice_sess_role role; /**< ICE role. */
+ pj_ice_sess_options opt; /**< Options */
+ pj_timestamp tie_breaker; /**< Tie breaker value */
+ pj_uint8_t *prefs; /**< Type preference. */
+ pj_bool_t is_nominating; /**< Nominating stage */
+ pj_bool_t is_complete; /**< Complete? */
+ pj_bool_t is_destroying; /**< Destroy is called */
+ pj_status_t ice_status; /**< Error status. */
+ pj_timer_entry timer; /**< ICE timer. */
+ pj_ice_sess_cb cb; /**< Callback. */
+
+ pj_stun_config stun_cfg; /**< STUN settings. */
+
+ /* STUN credentials */
+ pj_str_t tx_ufrag; /**< Remote ufrag. */
+ pj_str_t tx_uname; /**< Uname for TX. */
+ pj_str_t tx_pass; /**< Remote password. */
+ pj_str_t rx_ufrag; /**< Local ufrag. */
+ pj_str_t rx_uname; /**< Uname for RX */
+ pj_str_t rx_pass; /**< Local password. */
+
+ /* Components */
+ unsigned comp_cnt; /**< # of components. */
+ pj_ice_sess_comp comp[PJ_ICE_MAX_COMP]; /**< Component array */
+ unsigned comp_ka; /**< Next comp for KA */
+
+ /* Local candidates */
+ unsigned lcand_cnt; /**< # of local cand. */
+ pj_ice_sess_cand lcand[PJ_ICE_MAX_CAND]; /**< Array of cand. */
+
+ /* Remote candidates */
+ unsigned rcand_cnt; /**< # of remote cand. */
+ pj_ice_sess_cand rcand[PJ_ICE_MAX_CAND]; /**< Array of cand. */
+
+ /** Array of transport datas */
+ pj_ice_msg_data tp_data[4];
+
+ /* List of eearly checks */
+ pj_ice_rx_check early_check; /**< Early checks. */
+
+ /* Checklist */
+ pj_ice_sess_checklist clist; /**< Active checklist */
+
+ /* Valid list */
+ pj_ice_sess_checklist valid_list; /**< Valid list. */
+
+ /** Temporary buffer for misc stuffs to avoid using stack too much */
+ union {
+ char txt[128];
+ char errmsg[PJ_ERR_MSG_SIZE];
+ } tmp;
+};
+
+
+/**
+ * This is a utility function to retrieve the string name for the
+ * particular candidate type.
+ *
+ * @param type Candidate type.
+ *
+ * @return The string representation of the candidate type.
+ */
+PJ_DECL(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type);
+
+
+/**
+ * This is a utility function to retrieve the string name for the
+ * particular role type.
+ *
+ * @param role Role type.
+ *
+ * @return The string representation of the role.
+ */
+PJ_DECL(const char*) pj_ice_sess_role_name(pj_ice_sess_role role);
+
+
+/**
+ * This is a utility function to calculate the foundation identification
+ * for a candidate.
+ *
+ * @param pool Pool to allocate the foundation string.
+ * @param foundation Pointer to receive the foundation string.
+ * @param type Candidate type.
+ * @param base_addr Base address of the candidate.
+ */
+PJ_DECL(void) pj_ice_calc_foundation(pj_pool_t *pool,
+ pj_str_t *foundation,
+ pj_ice_cand_type type,
+ const pj_sockaddr *base_addr);
+
+/**
+ * Initialize ICE session options with library default values.
+ *
+ * @param opt ICE session options.
+ */
+PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt);
+
+/**
+ * Create ICE session with the specified role and number of components.
+ * Application would typically need to create an ICE session before
+ * sending an offer or upon receiving one. After the session is created,
+ * application can register candidates to the ICE session by calling
+ * #pj_ice_sess_add_cand() function.
+ *
+ * @param stun_cfg The STUN configuration settings, containing among
+ * other things the timer heap instance to be used
+ * by the ICE session.
+ * @param name Optional name to identify this ICE instance in
+ * the log file.
+ * @param role ICE role.
+ * @param comp_cnt Number of components.
+ * @param cb ICE callback.
+ * @param local_ufrag Optional string to be used as local username to
+ * authenticate incoming STUN binding request. If
+ * the value is NULL, a random string will be
+ * generated.
+ * @param local_passwd Optional string to be used as local password.
+ * @param grp_lock Optional group lock to be used by this session.
+ * If NULL, the session will create one itself.
+ * @param p_ice Pointer to receive the ICE session instance.
+ *
+ * @return PJ_SUCCESS if ICE session is created successfully.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
+ const char *name,
+ pj_ice_sess_role role,
+ unsigned comp_cnt,
+ const pj_ice_sess_cb *cb,
+ const pj_str_t *local_ufrag,
+ const pj_str_t *local_passwd,
+ pj_grp_lock_t *grp_lock,
+ pj_ice_sess **p_ice);
+
+/**
+ * Get the value of various options of the ICE session.
+ *
+ * @param ice The ICE session.
+ * @param opt The options to be initialized with the values
+ * from the ICE session.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
+ pj_ice_sess_options *opt);
+
+/**
+ * Specify various options for this ICE session. Application MUST only
+ * call this function after the ICE session has been created but before
+ * any connectivity check is started.
+ *
+ * Application should call #pj_ice_sess_get_options() to initialize the
+ * options with their default values.
+ *
+ * @param ice The ICE session.
+ * @param opt Options to be applied to the ICE session.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
+ const pj_ice_sess_options *opt);
+
+/**
+ * Destroy ICE session. This will cancel any connectivity checks currently
+ * running, if any, and any other events scheduled by this session, as well
+ * as all memory resources.
+ *
+ * @param ice ICE session instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice);
+
+
+/**
+ * Change session role. This happens for example when ICE session was
+ * created with controlled role when receiving an offer, but it turns out
+ * that the offer contains "a=ice-lite" attribute when the SDP gets
+ * inspected.
+ *
+ * @param ice The ICE session.
+ * @param new_role The new role to be set.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice,
+ pj_ice_sess_role new_role);
+
+
+/**
+ * Assign a custom preference values for ICE candidate types. By assigning
+ * custom preference value, application can control the order of candidates
+ * to be checked first. The default preference settings is to use 126 for
+ * host candidates, 100 for server reflexive candidates, 110 for peer
+ * reflexive candidates, an 0 for relayed candidates.
+ *
+ * Note that this function must be called before any candidates are added
+ * to the ICE session.
+ *
+ * @param ice The ICE session.
+ * @param prefs Array of candidate preference value. The values are
+ * put in the array indexed by the candidate type as
+ * specified in pj_ice_cand_type.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice,
+ const pj_uint8_t prefs[4]);
+
+
+
+/**
+ * Add a candidate to this ICE session. Application must add candidates for
+ * each components ID before it can start pairing the candidates and
+ * performing connectivity checks.
+ *
+ * @param ice ICE session instance.
+ * @param comp_id Component ID of this candidate.
+ * @param transport_id Transport ID to be used to send packets for this
+ * candidate.
+ * @param type Candidate type.
+ * @param local_pref Local preference for this candidate, which
+ * normally should be set to 65535.
+ * @param foundation Foundation identification.
+ * @param addr The candidate address.
+ * @param base_addr The candidate's base address.
+ * @param rel_addr Optional related address.
+ * @param addr_len Length of addresses.
+ * @param p_cand_id Optional pointer to receive the candidate ID.
+ *
+ * @return PJ_SUCCESS if candidate is successfully added.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice,
+ unsigned comp_id,
+ unsigned transport_id,
+ pj_ice_cand_type type,
+ pj_uint16_t local_pref,
+ const pj_str_t *foundation,
+ const pj_sockaddr_t *addr,
+ const pj_sockaddr_t *base_addr,
+ const pj_sockaddr_t *rel_addr,
+ int addr_len,
+ unsigned *p_cand_id);
+
+/**
+ * Find default candidate for the specified component ID, using this
+ * rule:
+ * - if the component has a successful candidate pair, then the
+ * local candidate of this pair will be returned.
+ * - otherwise a relay, reflexive, or host candidate will be selected
+ * on that specified order.
+ *
+ * @param ice The ICE session instance.
+ * @param comp_id The component ID.
+ * @param p_cand_id Pointer to receive the candidate ID.
+ *
+ * @return PJ_SUCCESS if a candidate has been selected.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice,
+ unsigned comp_id,
+ int *p_cand_id);
+
+/**
+ * Pair the local and remote candidates to create check list. Application
+ * typically would call this function after receiving SDP containing ICE
+ * candidates from the remote host (either upon receiving the initial
+ * offer, for UAS, or upon receiving the answer, for UAC).
+ *
+ * Note that ICE connectivity check will not start until application calls
+ * #pj_ice_sess_start_check().
+ *
+ * @param ice ICE session instance.
+ * @param rem_ufrag Remote ufrag, as seen in the SDP received from
+ * the remote agent.
+ * @param rem_passwd Remote password, as seen in the SDP received from
+ * the remote agent.
+ * @param rem_cand_cnt Number of remote candidates.
+ * @param rem_cand Remote candidate array. Remote candidates are
+ * gathered from the SDP received from the remote
+ * agent.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pj_ice_sess_create_check_list(pj_ice_sess *ice,
+ const pj_str_t *rem_ufrag,
+ const pj_str_t *rem_passwd,
+ unsigned rem_cand_cnt,
+ const pj_ice_sess_cand rem_cand[]);
+
+/**
+ * Start ICE periodic check. This function will return immediately, and
+ * application will be notified about the connectivity check status in
+ * #pj_ice_sess_cb callback.
+ *
+ * @param ice The ICE session instance.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice);
+
+
+/**
+ * Send data using this ICE session. If ICE checks have not produced a
+ * valid check for the specified component ID, this function will return
+ * with failure. Otherwise ICE session will send the packet to remote
+ * destination using the nominated local candidate for the specified
+ * component.
+ *
+ * This function will in turn call \a on_tx_pkt function in
+ * #pj_ice_sess_cb callback to actually send the packet to the wire.
+ *
+ * @param ice The ICE session.
+ * @param comp_id Component ID.
+ * @param data The data or packet to be sent.
+ * @param data_len Size of data or packet, in bytes.
+ *
+ * @return PJ_SUCCESS if data is sent successfully.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
+ unsigned comp_id,
+ const void *data,
+ pj_size_t data_len);
+
+/**
+ * Report the arrival of packet to the ICE session. Since ICE session
+ * itself doesn't have any transports, it relies on application or
+ * higher layer component to give incoming packets to the ICE session.
+ * If the packet is not a STUN packet, this packet will be given back
+ * to application via \a on_rx_data() callback in #pj_ice_sess_cb.
+ *
+ * @param ice The ICE session.
+ * @param comp_id Component ID.
+ * @param transport_id Number to identify where this packet was received
+ * from. This parameter will be returned back to
+ * application in \a on_tx_pkt() callback.
+ * @param pkt Incoming packet.
+ * @param pkt_size Size of incoming packet.
+ * @param src_addr Source address of the packet.
+ * @param src_addr_len Length of the address.
+ *
+ * @return PJ_SUCCESS or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
+ unsigned comp_id,
+ unsigned transport_id,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_sockaddr_t *src_addr,
+ int src_addr_len);
+
+
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif /* __PJNATH_ICE_SESSION_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef0cadecb743f8a4c1a92af0df0e7182de41ca44.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef0cadecb743f8a4c1a92af0df0e7182de41ca44.svn-base
new file mode 100644
index 0000000..ec32c6e
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef0cadecb743f8a4c1a92af0df0e7182de41ca44.svn-base
@@ -0,0 +1,508 @@
+/* Copyright (C) 2005 Jean-Marc Valin, CSIRO, Christopher Montgomery
+ File: vorbis_psy.c
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of the Xiph.org Foundation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef VORBIS_PSYCHO
+
+#include "arch.h"
+#include "smallft.h"
+#include "lpc.h"
+#include "vorbis_psy.h"
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+/* psychoacoustic setup ********************************************/
+
+static VorbisPsyInfo example_tuning = {
+
+ .5,.5,
+ 3,3,25,
+
+ /*63 125 250 500 1k 2k 4k 8k 16k*/
+ // vorbis mode 4 style
+ //{-32,-32,-32,-32,-28,-24,-22,-20,-20, -20, -20, -8, -6, -6, -6, -6, -6},
+ { -4, -6, -6, -6, -6, -6, -6, -6, -8, -8,-10,-10, -8, -6, -4, -4, -2},
+
+ {
+ 0, 1, 2, 3, 4, 5, 5, 5, /* 7dB */
+ 6, 6, 6, 5, 4, 4, 4, 4, /* 15dB */
+ 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */
+ 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */
+ 11,12,13,14,15,16,17, 18, /* 39dB */
+ }
+
+};
+
+
+
+/* there was no great place to put this.... */
+#include <stdio.h>
+static void _analysis_output(char *base,int i,float *v,int n,int bark,int dB){
+ int j;
+ FILE *of;
+ char buffer[80];
+
+ sprintf(buffer,"%s_%d.m",base,i);
+ of=fopen(buffer,"w");
+
+ if(!of)perror("failed to open data dump file");
+
+ for(j=0;j<n;j++){
+ if(bark){
+ float b=toBARK((4000.f*j/n)+.25);
+ fprintf(of,"%f ",b);
+ }else
+ fprintf(of,"%f ",(double)j);
+
+ if(dB){
+ float val;
+ if(v[j]==0.)
+ val=-140.;
+ else
+ val=todB(v[j]);
+ fprintf(of,"%f\n",val);
+ }else{
+ fprintf(of,"%f\n",v[j]);
+ }
+ }
+ fclose(of);
+}
+
+static void bark_noise_hybridmp(int n,const long *b,
+ const float *f,
+ float *noise,
+ const float offset,
+ const int fixed){
+
+ float *N=alloca(n*sizeof(*N));
+ float *X=alloca(n*sizeof(*N));
+ float *XX=alloca(n*sizeof(*N));
+ float *Y=alloca(n*sizeof(*N));
+ float *XY=alloca(n*sizeof(*N));
+
+ float tN, tX, tXX, tY, tXY;
+ int i;
+
+ int lo, hi;
+ float R, A, B, D;
+ float w, x, y;
+
+ tN = tX = tXX = tY = tXY = 0.f;
+
+ y = f[0] + offset;
+ if (y < 1.f) y = 1.f;
+
+ w = y * y * .5;
+
+ tN += w;
+ tX += w;
+ tY += w * y;
+
+ N[0] = tN;
+ X[0] = tX;
+ XX[0] = tXX;
+ Y[0] = tY;
+ XY[0] = tXY;
+
+ for (i = 1, x = 1.f; i < n; i++, x += 1.f) {
+
+ y = f[i] + offset;
+ if (y < 1.f) y = 1.f;
+
+ w = y * y;
+
+ tN += w;
+ tX += w * x;
+ tXX += w * x * x;
+ tY += w * y;
+ tXY += w * x * y;
+
+ N[i] = tN;
+ X[i] = tX;
+ XX[i] = tXX;
+ Y[i] = tY;
+ XY[i] = tXY;
+ }
+
+ for (i = 0, x = 0.f;; i++, x += 1.f) {
+
+ lo = b[i] >> 16;
+ if( lo>=0 ) break;
+ hi = b[i] & 0xffff;
+
+ tN = N[hi] + N[-lo];
+ tX = X[hi] - X[-lo];
+ tXX = XX[hi] + XX[-lo];
+ tY = Y[hi] + Y[-lo];
+ tXY = XY[hi] - XY[-lo];
+
+ A = tY * tXX - tX * tXY;
+ B = tN * tXY - tX * tY;
+ D = tN * tXX - tX * tX;
+ R = (A + x * B) / D;
+ if (R < 0.f)
+ R = 0.f;
+
+ noise[i] = R - offset;
+ }
+
+ for ( ;; i++, x += 1.f) {
+
+ lo = b[i] >> 16;
+ hi = b[i] & 0xffff;
+ if(hi>=n)break;
+
+ tN = N[hi] - N[lo];
+ tX = X[hi] - X[lo];
+ tXX = XX[hi] - XX[lo];
+ tY = Y[hi] - Y[lo];
+ tXY = XY[hi] - XY[lo];
+
+ A = tY * tXX - tX * tXY;
+ B = tN * tXY - tX * tY;
+ D = tN * tXX - tX * tX;
+ R = (A + x * B) / D;
+ if (R < 0.f) R = 0.f;
+
+ noise[i] = R - offset;
+ }
+ for ( ; i < n; i++, x += 1.f) {
+
+ R = (A + x * B) / D;
+ if (R < 0.f) R = 0.f;
+
+ noise[i] = R - offset;
+ }
+
+ if (fixed <= 0) return;
+
+ for (i = 0, x = 0.f;; i++, x += 1.f) {
+ hi = i + fixed / 2;
+ lo = hi - fixed;
+ if(lo>=0)break;
+
+ tN = N[hi] + N[-lo];
+ tX = X[hi] - X[-lo];
+ tXX = XX[hi] + XX[-lo];
+ tY = Y[hi] + Y[-lo];
+ tXY = XY[hi] - XY[-lo];
+
+
+ A = tY * tXX - tX * tXY;
+ B = tN * tXY - tX * tY;
+ D = tN * tXX - tX * tX;
+ R = (A + x * B) / D;
+
+ if (R - offset < noise[i]) noise[i] = R - offset;
+ }
+ for ( ;; i++, x += 1.f) {
+
+ hi = i + fixed / 2;
+ lo = hi - fixed;
+ if(hi>=n)break;
+
+ tN = N[hi] - N[lo];
+ tX = X[hi] - X[lo];
+ tXX = XX[hi] - XX[lo];
+ tY = Y[hi] - Y[lo];
+ tXY = XY[hi] - XY[lo];
+
+ A = tY * tXX - tX * tXY;
+ B = tN * tXY - tX * tY;
+ D = tN * tXX - tX * tX;
+ R = (A + x * B) / D;
+
+ if (R - offset < noise[i]) noise[i] = R - offset;
+ }
+ for ( ; i < n; i++, x += 1.f) {
+ R = (A + x * B) / D;
+ if (R - offset < noise[i]) noise[i] = R - offset;
+ }
+}
+
+static void _vp_noisemask(VorbisPsy *p,
+ float *logfreq,
+ float *logmask){
+
+ int i,n=p->n/2;
+ float *work=alloca(n*sizeof(*work));
+
+ bark_noise_hybridmp(n,p->bark,logfreq,logmask,
+ 140.,-1);
+
+ for(i=0;i<n;i++)work[i]=logfreq[i]-logmask[i];
+
+ bark_noise_hybridmp(n,p->bark,work,logmask,0.,
+ p->vi->noisewindowfixed);
+
+ for(i=0;i<n;i++)work[i]=logfreq[i]-work[i];
+
+ {
+ static int seq=0;
+
+ float work2[n];
+ for(i=0;i<n;i++){
+ work2[i]=logmask[i]+work[i];
+ }
+
+ //_analysis_output("logfreq",seq,logfreq,n,0,0);
+ //_analysis_output("median",seq,work,n,0,0);
+ //_analysis_output("envelope",seq,work2,n,0,0);
+ seq++;
+ }
+
+ for(i=0;i<n;i++){
+ int dB=logmask[i]+.5;
+ if(dB>=NOISE_COMPAND_LEVELS)dB=NOISE_COMPAND_LEVELS-1;
+ if(dB<0)dB=0;
+ logmask[i]= work[i]+p->vi->noisecompand[dB]+p->noiseoffset[i];
+ }
+
+}
+
+VorbisPsy *vorbis_psy_init(int rate, int n)
+{
+ long i,j,lo=-99,hi=1;
+ VorbisPsy *p = speex_alloc(sizeof(VorbisPsy));
+ memset(p,0,sizeof(*p));
+
+ p->n = n;
+ spx_drft_init(&p->lookup, n);
+ p->bark = speex_alloc(n*sizeof(*p->bark));
+ p->rate=rate;
+ p->vi = &example_tuning;
+
+ /* BH4 window */
+ p->window = speex_alloc(sizeof(*p->window)*n);
+ float a0 = .35875f;
+ float a1 = .48829f;
+ float a2 = .14128f;
+ float a3 = .01168f;
+ for(i=0;i<n;i++)
+ p->window[i] = //a0 - a1*cos(2.*M_PI/n*(i+.5)) + a2*cos(4.*M_PI/n*(i+.5)) - a3*cos(6.*M_PI/n*(i+.5));
+ sin((i+.5)/n * M_PI)*sin((i+.5)/n * M_PI);
+ /* bark scale lookups */
+ for(i=0;i<n;i++){
+ float bark=toBARK(rate/(2*n)*i);
+
+ for(;lo+p->vi->noisewindowlomin<i &&
+ toBARK(rate/(2*n)*lo)<(bark-p->vi->noisewindowlo);lo++);
+
+ for(;hi<=n && (hi<i+p->vi->noisewindowhimin ||
+ toBARK(rate/(2*n)*hi)<(bark+p->vi->noisewindowhi));hi++);
+
+ p->bark[i]=((lo-1)<<16)+(hi-1);
+
+ }
+
+ /* set up rolling noise median */
+ p->noiseoffset=speex_alloc(n*sizeof(*p->noiseoffset));
+
+ for(i=0;i<n;i++){
+ float halfoc=toOC((i+.5)*rate/(2.*n))*2.;
+ int inthalfoc;
+ float del;
+
+ if(halfoc<0)halfoc=0;
+ if(halfoc>=P_BANDS-1)halfoc=P_BANDS-1;
+ inthalfoc=(int)halfoc;
+ del=halfoc-inthalfoc;
+
+ p->noiseoffset[i]=
+ p->vi->noiseoff[inthalfoc]*(1.-del) +
+ p->vi->noiseoff[inthalfoc+1]*del;
+
+ }
+#if 0
+ _analysis_output_always("noiseoff0",ls,p->noiseoffset,n,1,0,0);
+#endif
+
+ return p;
+}
+
+void vorbis_psy_destroy(VorbisPsy *p)
+{
+ if(p){
+ spx_drft_clear(&p->lookup);
+ if(p->bark)
+ speex_free(p->bark);
+ if(p->noiseoffset)
+ speex_free(p->noiseoffset);
+ if(p->window)
+ speex_free(p->window);
+ memset(p,0,sizeof(*p));
+ speex_free(p);
+ }
+}
+
+void compute_curve(VorbisPsy *psy, float *audio, float *curve)
+{
+ int i;
+ float work[psy->n];
+
+ float scale=4.f/psy->n;
+ float scale_dB;
+
+ scale_dB=todB(scale);
+
+ /* window the PCM data; use a BH4 window, not vorbis */
+ for(i=0;i<psy->n;i++)
+ work[i]=audio[i] * psy->window[i];
+
+ {
+ static int seq=0;
+
+ //_analysis_output("win",seq,work,psy->n,0,0);
+
+ seq++;
+ }
+
+ /* FFT yields more accurate tonal estimation (not phase sensitive) */
+ spx_drft_forward(&psy->lookup,work);
+
+ /* magnitudes */
+ work[0]=scale_dB+todB(work[0]);
+ for(i=1;i<psy->n-1;i+=2){
+ float temp = work[i]*work[i] + work[i+1]*work[i+1];
+ work[(i+1)>>1] = scale_dB+.5f * todB(temp);
+ }
+
+ /* derive a noise curve */
+ _vp_noisemask(psy,work,curve);
+#define SIDEL 12
+ for (i=0;i<SIDEL;i++)
+ {
+ curve[i]=curve[SIDEL];
+ }
+#define SIDEH 12
+ for (i=0;i<SIDEH;i++)
+ {
+ curve[(psy->n>>1)-i-1]=curve[(psy->n>>1)-SIDEH];
+ }
+ for(i=0;i<((psy->n)>>1);i++)
+ curve[i] = fromdB(1.2*curve[i]+.2*i);
+ //curve[i] = fromdB(0.8*curve[i]+.35*i);
+ //curve[i] = fromdB(0.9*curve[i])*pow(1.0*i+45,1.3);
+}
+
+/* Transform a masking curve (power spectrum) into a pole-zero filter */
+void curve_to_lpc(VorbisPsy *psy, float *curve, float *awk1, float *awk2, int ord)
+{
+ int i;
+ float ac[psy->n];
+ float tmp;
+ int len = psy->n >> 1;
+ for (i=0;i<2*len;i++)
+ ac[i] = 0;
+ for (i=1;i<len;i++)
+ ac[2*i-1] = curve[i];
+ ac[0] = curve[0];
+ ac[2*len-1] = curve[len-1];
+
+ spx_drft_backward(&psy->lookup, ac);
+ _spx_lpc(awk1, ac, ord);
+ tmp = 1.;
+ for (i=0;i<ord;i++)
+ {
+ tmp *= .99;
+ awk1[i] *= tmp;
+ }
+#if 0
+ for (i=0;i<ord;i++)
+ awk2[i] = 0;
+#else
+ /* Use the second (awk2) filter to correct the first one */
+ for (i=0;i<2*len;i++)
+ ac[i] = 0;
+ for (i=0;i<ord;i++)
+ ac[i+1] = awk1[i];
+ ac[0] = 1;
+ spx_drft_forward(&psy->lookup, ac);
+ /* Compute (power) response of awk1 (all zero) */
+ ac[0] *= ac[0];
+ for (i=1;i<len;i++)
+ ac[i] = ac[2*i-1]*ac[2*i-1] + ac[2*i]*ac[2*i];
+ ac[len] = ac[2*len-1]*ac[2*len-1];
+ /* Compute correction required */
+ for (i=0;i<len;i++)
+ curve[i] = 1. / (1e-6f+curve[i]*ac[i]);
+
+ for (i=0;i<2*len;i++)
+ ac[i] = 0;
+ for (i=1;i<len;i++)
+ ac[2*i-1] = curve[i];
+ ac[0] = curve[0];
+ ac[2*len-1] = curve[len-1];
+
+ spx_drft_backward(&psy->lookup, ac);
+ _spx_lpc(awk2, ac, ord);
+ tmp = 1;
+ for (i=0;i<ord;i++)
+ {
+ tmp *= .99;
+ awk2[i] *= tmp;
+ }
+#endif
+}
+
+#if 0
+#include <stdio.h>
+#include <math.h>
+
+#define ORDER 10
+#define CURVE_SIZE 24
+
+int main()
+{
+ int i;
+ float curve[CURVE_SIZE];
+ float awk1[ORDER], awk2[ORDER];
+ for (i=0;i<CURVE_SIZE;i++)
+ scanf("%f ", &curve[i]);
+ for (i=0;i<CURVE_SIZE;i++)
+ curve[i] = pow(10.f, .1*curve[i]);
+ curve_to_lpc(curve, CURVE_SIZE, awk1, awk2, ORDER);
+ for (i=0;i<ORDER;i++)
+ printf("%f ", awk1[i]);
+ printf ("\n");
+ for (i=0;i<ORDER;i++)
+ printf("%f ", awk2[i]);
+ printf ("\n");
+ return 0;
+}
+#endif
+
+#endif
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef16fc2203f83ab2d7a78ce72a65081c437e56d8.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef16fc2203f83ab2d7a78ce72a65081c437e56d8.svn-base
new file mode 100644
index 0000000..0170f6f
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef16fc2203f83ab2d7a78ce72a65081c437e56d8.svn-base
@@ -0,0 +1,24 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "sip_endpoint.c"
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef174868bb97ac011acaab0582193cd873654658.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef174868bb97ac011acaab0582193cd873654658.svn-base
new file mode 100644
index 0000000..97fadb5
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef174868bb97ac011acaab0582193cd873654658.svn-base
Binary files differ
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef2727526ae3a7c64bb9e58826d3fe65445e6485.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef2727526ae3a7c64bb9e58826d3fe65445e6485.svn-base
new file mode 100644
index 0000000..94d03af
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef2727526ae3a7c64bb9e58826d3fe65445e6485.svn-base
@@ -0,0 +1,13 @@
+export HOST_MV := mv
+export HOST_RM := rm -f @@
+export HOST_RMR := rm -rf @@
+export HOST_RMDIR := rm -rf @@
+export HOST_MKDIR := mkdir @@
+export HOST_EXE := .exe
+export HOST_PSEP := /
+
+export HOST_SOURCES :=
+export HOST_CFLAGS :=
+export HOST_CXXFLAGS :=
+export HOST_LDFLAGS := $(CC_LIB)stdc++$(LIBEXT2)
+
diff --git a/jni/pjproject-android/.svn/pristine/ef/ef57a0900f91bcc0130fc6d2ce35de5f21bc51e5.svn-base b/jni/pjproject-android/.svn/pristine/ef/ef57a0900f91bcc0130fc6d2ce35de5f21bc51e5.svn-base
new file mode 100644
index 0000000..74f3d93
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/ef57a0900f91bcc0130fc6d2ce35de5f21bc51e5.svn-base
@@ -0,0 +1,728 @@
+/****************************************************************************
+**
+** ITU-T G.722.1 (2005-05) - Fixed point implementation for main body and Annex C
+** > Software Release 2.1 (2008-06)
+** (Simple repackaging; no change from 2005-05 Release 2.0 code)
+**
+** © 2004 Polycom, Inc.
+**
+** All rights reserved.
+**
+****************************************************************************/
+
+/****************************************************************************
+ Filename: dct4_a.h
+
+ Purpose: Contains tables used by dct4_a.c
+
+ Design Notes:
+
+****************************************************************************/
+
+/***************************************************************************
+ Include files
+***************************************************************************/
+#include <stdio.h>
+#include <math.h>
+
+typedef struct
+{
+ Word16 cosine;
+ Word16 minus_sine;
+} cos_msin_t;
+
+cos_msin_t a_cos_msin_2[10] = {
+ { 29805 , -1171 } ,
+ { 29621 , -3506 } ,
+ { 29255 , -5819 } ,
+ { 28708 , -8097 } ,
+ { 27984 , -10324 } ,
+ { 27088 , -12488 } ,
+ { 26025 , -14575 } ,
+ { 24801 , -16572 } ,
+ { 23425 , -18466 } ,
+ { 21903 , -20247 }
+ };
+cos_msin_t a_cos_msin_4[20] = {
+ { 29822 , -586 } ,
+ { 29776 , -1756 } ,
+ { 29684 , -2924 } ,
+ { 29547 , -4087 } ,
+ { 29364 , -5244 } ,
+ { 29135 , -6392 } ,
+ { 28862 , -7531 } ,
+ { 28544 , -8659 } ,
+ { 28182 , -9773 } ,
+ { 27776 , -10871 } ,
+ { 27328 , -11954 } ,
+ { 26838 , -13017 } ,
+ { 26306 , -14061 } ,
+ { 25734 , -15083 } ,
+ { 25122 , -16081 } ,
+ { 24471 , -17055 } ,
+ { 23783 , -18003 } ,
+ { 23057 , -18923 } ,
+ { 22297 , -19813 } ,
+ { 21502 , -20673 }
+ };
+cos_msin_t a_cos_msin_8[40] = {
+ { 29827 , -293 } ,
+ { 29815 , -878 } ,
+ { 29792 , -1464 } ,
+ { 29758 , -2048 } ,
+ { 29712 , -2632 } ,
+ { 29654 , -3215 } ,
+ { 29586 , -3797 } ,
+ { 29505 , -4377 } ,
+ { 29414 , -4955 } ,
+ { 29311 , -5532 } ,
+ { 29196 , -6106 } ,
+ { 29071 , -6678 } ,
+ { 28934 , -7248 } ,
+ { 28786 , -7814 } ,
+ { 28627 , -8378 } ,
+ { 28457 , -8938 } ,
+ { 28276 , -9495 } ,
+ { 28084 , -10049 } ,
+ { 27882 , -10598 } ,
+ { 27668 , -11144 } ,
+ { 27444 , -11685 } ,
+ { 27209 , -12221 } ,
+ { 26964 , -12753 } ,
+ { 26709 , -13280 } ,
+ { 26443 , -13802 } ,
+ { 26167 , -14318 } ,
+ { 25881 , -14829 } ,
+ { 25584 , -15335 } ,
+ { 25278 , -15834 } ,
+ { 24963 , -16327 } ,
+ { 24637 , -16814 } ,
+ { 24302 , -17295 } ,
+ { 23958 , -17769 } ,
+ { 23605 , -18236 } ,
+ { 23242 , -18695 } ,
+ { 22871 , -19148 } ,
+ { 22490 , -19594 } ,
+ { 22101 , -20031 } ,
+ { 21704 , -20461 } ,
+ { 21298 , -20884 }
+ };
+cos_msin_t a_cos_msin_16[80] = {
+ { 29828 , -146 } ,
+ { 29825 , -439 } ,
+ { 29819 , -732 } ,
+ { 29811 , -1025 } ,
+ { 29799 , -1317 } ,
+ { 29785 , -1610 } ,
+ { 29767 , -1902 } ,
+ { 29747 , -2194 } ,
+ { 29724 , -2486 } ,
+ { 29698 , -2778 } ,
+ { 29670 , -3069 } ,
+ { 29638 , -3360 } ,
+ { 29604 , -3651 } ,
+ { 29567 , -3942 } ,
+ { 29526 , -4232 } ,
+ { 29483 , -4521 } ,
+ { 29438 , -4811 } ,
+ { 29389 , -5099 } ,
+ { 29338 , -5388 } ,
+ { 29283 , -5676 } ,
+ { 29226 , -5963 } ,
+ { 29166 , -6249 } ,
+ { 29103 , -6535 } ,
+ { 29038 , -6821 } ,
+ { 28969 , -7106 } ,
+ { 28898 , -7390 } ,
+ { 28824 , -7673 } ,
+ { 28748 , -7956 } ,
+ { 28668 , -8237 } ,
+ { 28586 , -8518 } ,
+ { 28501 , -8799 } ,
+ { 28413 , -9078 } ,
+ { 28323 , -9357 } ,
+ { 28229 , -9634 } ,
+ { 28133 , -9911 } ,
+ { 28035 , -10187 } ,
+ { 27933 , -10461 } ,
+ { 27829 , -10735 } ,
+ { 27723 , -11008 } ,
+ { 27613 , -11279 } ,
+ { 27501 , -11550 } ,
+ { 27387 , -11819 } ,
+ { 27269 , -12088 } ,
+ { 27149 , -12355 } ,
+ { 27027 , -12621 } ,
+ { 26901 , -12885 } ,
+ { 26774 , -13149 } ,
+ { 26643 , -13411 } ,
+ { 26510 , -13672 } ,
+ { 26375 , -13932 } ,
+ { 26237 , -14190 } ,
+ { 26096 , -14447 } ,
+ { 25953 , -14702 } ,
+ { 25807 , -14956 } ,
+ { 25659 , -15209 } ,
+ { 25509 , -15460 } ,
+ { 25356 , -15710 } ,
+ { 25200 , -15958 } ,
+ { 25043 , -16205 } ,
+ { 24882 , -16450 } ,
+ { 24720 , -16693 } ,
+ { 24554 , -16935 } ,
+ { 24387 , -17175 } ,
+ { 24217 , -17414 } ,
+ { 24045 , -17651 } ,
+ { 23871 , -17886 } ,
+ { 23694 , -18119 } ,
+ { 23515 , -18351 } ,
+ { 23334 , -18581 } ,
+ { 23150 , -18809 } ,
+ { 22964 , -19036 } ,
+ { 22776 , -19260 } ,
+ { 22586 , -19483 } ,
+ { 22394 , -19704 } ,
+ { 22199 , -19923 } ,
+ { 22003 , -20140 } ,
+ { 21804 , -20355 } ,
+ { 21603 , -20568 } ,
+ { 21400 , -20779 } ,
+ { 21195 , -20988 }
+ };
+cos_msin_t a_cos_msin_32[160]= {
+ { 29828 , -73 } ,
+ { 29827 , -220 } ,
+ { 29826 , -366 } ,
+ { 29824 , -512 } ,
+ { 29821 , -659 } ,
+ { 29817 , -805 } ,
+ { 29813 , -952 } ,
+ { 29808 , -1098 } ,
+ { 29802 , -1244 } ,
+ { 29796 , -1390 } ,
+ { 29789 , -1537 } ,
+ { 29781 , -1683 } ,
+ { 29772 , -1829 } ,
+ { 29763 , -1975 } ,
+ { 29753 , -2121 } ,
+ { 29742 , -2267 } ,
+ { 29730 , -2413 } ,
+ { 29718 , -2559 } ,
+ { 29705 , -2705 } ,
+ { 29692 , -2851 } ,
+ { 29677 , -2997 } ,
+ { 29662 , -3142 } ,
+ { 29646 , -3288 } ,
+ { 29630 , -3433 } ,
+ { 29613 , -3579 } ,
+ { 29595 , -3724 } ,
+ { 29576 , -3869 } ,
+ { 29557 , -4014 } ,
+ { 29537 , -4159 } ,
+ { 29516 , -4304 } ,
+ { 29494 , -4449 } ,
+ { 29472 , -4594 } ,
+ { 29449 , -4738 } ,
+ { 29426 , -4883 } ,
+ { 29401 , -5027 } ,
+ { 29376 , -5172 } ,
+ { 29351 , -5316 } ,
+ { 29324 , -5460 } ,
+ { 29297 , -5604 } ,
+ { 29269 , -5747 } ,
+ { 29241 , -5891 } ,
+ { 29211 , -6034 } ,
+ { 29181 , -6178 } ,
+ { 29151 , -6321 } ,
+ { 29119 , -6464 } ,
+ { 29087 , -6607 } ,
+ { 29054 , -6749 } ,
+ { 29021 , -6892 } ,
+ { 28987 , -7034 } ,
+ { 28952 , -7177 } ,
+ { 28916 , -7319 } ,
+ { 28880 , -7460 } ,
+ { 28843 , -7602 } ,
+ { 28805 , -7744 } ,
+ { 28767 , -7885 } ,
+ { 28728 , -8026 } ,
+ { 28688 , -8167 } ,
+ { 28648 , -8308 } ,
+ { 28607 , -8448 } ,
+ { 28565 , -8589 } ,
+ { 28522 , -8729 } ,
+ { 28479 , -8869 } ,
+ { 28435 , -9008 } ,
+ { 28391 , -9148 } ,
+ { 28346 , -9287 } ,
+ { 28300 , -9426 } ,
+ { 28253 , -9565 } ,
+ { 28206 , -9703 } ,
+ { 28158 , -9842 } ,
+ { 28109 , -9980 } ,
+ { 28060 , -10118 } ,
+ { 28010 , -10255 } ,
+ { 27959 , -10393 } ,
+ { 27908 , -10530 } ,
+ { 27856 , -10667 } ,
+ { 27803 , -10803 } ,
+ { 27750 , -10940 } ,
+ { 27696 , -11076 } ,
+ { 27641 , -11212 } ,
+ { 27586 , -11347 } ,
+ { 27529 , -11482 } ,
+ { 27473 , -11617 } ,
+ { 27415 , -11752 } ,
+ { 27357 , -11886 } ,
+ { 27299 , -12021 } ,
+ { 27239 , -12154 } ,
+ { 27179 , -12288 } ,
+ { 27119 , -12421 } ,
+ { 27057 , -12554 } ,
+ { 26996 , -12687 } ,
+ { 26933 , -12819 } ,
+ { 26870 , -12951 } ,
+ { 26806 , -13083 } ,
+ { 26741 , -13215 } ,
+ { 26676 , -13346 } ,
+ { 26610 , -13476 } ,
+ { 26544 , -13607 } ,
+ { 26477 , -13737 } ,
+ { 26409 , -13867 } ,
+ { 26340 , -13996 } ,
+ { 26271 , -14125 } ,
+ { 26202 , -14254 } ,
+ { 26132 , -14383 } ,
+ { 26061 , -14511 } ,
+ { 25989 , -14638 } ,
+ { 25917 , -14766 } ,
+ { 25844 , -14893 } ,
+ { 25771 , -15020 } ,
+ { 25697 , -15146 } ,
+ { 25622 , -15272 } ,
+ { 25547 , -15397 } ,
+ { 25471 , -15523 } ,
+ { 25394 , -15648 } ,
+ { 25317 , -15772 } ,
+ { 25239 , -15896 } ,
+ { 25161 , -16020 } ,
+ { 25082 , -16143 } ,
+ { 25003 , -16266 } ,
+ { 24923 , -16389 } ,
+ { 24842 , -16511 } ,
+ { 24760 , -16632 } ,
+ { 24678 , -16754 } ,
+ { 24596 , -16875 } ,
+ { 24513 , -16995 } ,
+ { 24429 , -17115 } ,
+ { 24345 , -17235 } ,
+ { 24260 , -17354 } ,
+ { 24174 , -17473 } ,
+ { 24088 , -17592 } ,
+ { 24002 , -17710 } ,
+ { 23914 , -17827 } ,
+ { 23827 , -17945 } ,
+ { 23738 , -18061 } ,
+ { 23649 , -18178 } ,
+ { 23560 , -18293 } ,
+ { 23470 , -18409 } ,
+ { 23379 , -18524 } ,
+ { 23288 , -18638 } ,
+ { 23196 , -18752 } ,
+ { 23104 , -18866 } ,
+ { 23011 , -18979 } ,
+ { 22917 , -19092 } ,
+ { 22824 , -19204 } ,
+ { 22729 , -19316 } ,
+ { 22634 , -19427 } ,
+ { 22538 , -19538 } ,
+ { 22442 , -19649 } ,
+ { 22345 , -19759 } ,
+ { 22248 , -19868 } ,
+ { 22150 , -19977 } ,
+ { 22052 , -20086 } ,
+ { 21953 , -20194 } ,
+ { 21854 , -20301 } ,
+ { 21754 , -20408 } ,
+ { 21653 , -20515 } ,
+ { 21552 , -20621 } ,
+ { 21451 , -20726 } ,
+ { 21349 , -20831 } ,
+ { 21246 , -20936 } ,
+ { 21143 , -21040 }
+ };
+cos_msin_t a_cos_msin_64[320] = {
+{29827, -34},
+{29827, -106},
+{29827, -177},
+{29827, -249},
+{29826, -320},
+{29825, -392},
+{29824, -463},
+{29823, -535},
+{29821, -606},
+{29819, -678},
+{29818, -750},
+{29816, -821},
+{29814, -893},
+{29812, -964},
+{29809, -1035},
+{29807, -1106},
+{29804, -1177},
+{29801, -1249},
+{29797, -1320},
+{29795, -1392},
+{29791, -1463},
+{29787, -1535},
+{29784, -1606},
+{29780, -1678},
+{29776, -1749},
+{29771, -1820},
+{29767, -1892},
+{29763, -1963},
+{29758, -2035},
+{29753, -2106},
+{29748, -2177},
+{29742, -2249},
+{29737, -2320},
+{29731, -2391},
+{29726, -2462},
+{29719, -2534},
+{29713, -2605},
+{29707, -2676},
+{29701, -2747},
+{29694, -2819},
+{29686, -2890},
+{29680, -2961},
+{29673, -3032},
+{29665, -3103},
+{29658, -3174},
+{29650, -3245},
+{29643, -3316},
+{29635, -3387},
+{29626, -3459},
+{29618, -3529},
+{29610, -3600},
+{29601, -3671},
+{29592, -3742},
+{29583, -3813},
+{29574, -3884},
+{29564, -3955},
+{29554, -4026},
+{29544, -4097},
+{29535, -4167},
+{29525, -4238},
+{29514, -4309},
+{29504, -4380},
+{29493, -4450},
+{29483, -4521},
+{29472, -4591},
+{29461, -4662},
+{29450, -4733},
+{29439, -4803},
+{29427, -4874},
+{29415, -4944},
+{29403, -5015},
+{29391, -5085},
+{29379, -5155},
+{29366, -5226},
+{29353, -5296},
+{29341, -5367},
+{29328, -5438},
+{29314, -5508},
+{29301, -5578},
+{29289, -5648},
+{29274, -5718},
+{29260, -5788},
+{29247, -5858},
+{29232, -5928},
+{29218, -5998},
+{29204, -6068},
+{29188, -6139},
+{29175, -6209},
+{29159, -6279},
+{29145, -6348},
+{29128, -6418},
+{29114, -6488},
+{29097, -6557},
+{29082, -6627},
+{29066, -6697},
+{29050, -6767},
+{29034, -6837},
+{29017, -6906},
+{29001, -6975},
+{28984, -7045},
+{28966, -7114},
+{28950, -7184},
+{28933, -7254},
+{28915, -7323},
+{28897, -7392},
+{28880, -7461},
+{28862, -7530},
+{28843, -7600},
+{28825, -7669},
+{28807, -7738},
+{28788, -7806},
+{28769, -7875},
+{28751, -7944},
+{28732, -8014},
+{28712, -8082},
+{28692, -8151},
+{28672, -8219},
+{28653, -8289},
+{28633, -8357},
+{28613, -8425},
+{28593, -8494},
+{28572, -8563},
+{28551, -8632},
+{28531, -8700},
+{28510, -8768},
+{28488, -8837},
+{28468, -8905},
+{28447, -8973},
+{28425, -9041},
+{28403, -9109},
+{28381, -9177},
+{28359, -9245},
+{28336, -9313},
+{28315, -9381},
+{28292, -9448},
+{28269, -9517},
+{28246, -9584},
+{28223, -9652},
+{28200, -9720},
+{28176, -9787},
+{28153, -9854},
+{28129, -9922},
+{28105, -9990},
+{28082, -10056},
+{28057, -10124},
+{28032, -10191},
+{28009, -10258},
+{27984, -10326},
+{27959, -10392},
+{27934, -10460},
+{27909, -10526},
+{27883, -10593},
+{27858, -10661},
+{27832, -10727},
+{27807, -10794},
+{27780, -10860},
+{27754, -10927},
+{27728, -10993},
+{27701, -11059},
+{27676, -11126},
+{27648, -11192},
+{27622, -11259},
+{27595, -11324},
+{27567, -11391},
+{27540, -11456},
+{27512, -11523},
+{27484, -11588},
+{27456, -11655},
+{27429, -11720},
+{27401, -11786},
+{27372, -11852},
+{27344, -11917},
+{27315, -11982},
+{27286, -12049},
+{27257, -12114},
+{27229, -12179},
+{27199, -12244},
+{27169, -12309},
+{27140, -12375},
+{27110, -12439},
+{27080, -12505},
+{27050, -12570},
+{27019, -12634},
+{26990, -12699},
+{26958, -12764},
+{26928, -12828},
+{26897, -12892},
+{26866, -12956},
+{26835, -13021},
+{26804, -13086},
+{26773, -13149},
+{26741, -13214},
+{26709, -13278},
+{26677, -13342},
+{26645, -13406},
+{26613, -13470},
+{26581, -13534},
+{26549, -13597},
+{26515, -13661},
+{26483, -13725},
+{26450, -13788},
+{26417, -13851},
+{26384, -13915},
+{26350, -13978},
+{26316, -14041},
+{26283, -14103},
+{26248, -14166},
+{26215, -14229},
+{26180, -14292},
+{26146, -14355},
+{26112, -14417},
+{26077, -14480},
+{26042, -14543},
+{26008, -14605},
+{25972, -14667},
+{25937, -14730},
+{25901, -14792},
+{25866, -14854},
+{25830, -14916},
+{25794, -14977},
+{25759, -15039},
+{25723, -15101},
+{25687, -15162},
+{25650, -15224},
+{25613, -15286},
+{25577, -15347},
+{25540, -15408},
+{25503, -15470},
+{25465, -15531},
+{25428, -15592},
+{25391, -15653},
+{25353, -15714},
+{25315, -15774},
+{25277, -15834},
+{25240, -15895},
+{25201, -15956},
+{25162, -16016},
+{25124, -16076},
+{25086, -16136},
+{25047, -16196},
+{25008, -16256},
+{24969, -16316},
+{24930, -16375},
+{24891, -16436},
+{24851, -16496},
+{24811, -16555},
+{24772, -16615},
+{24732, -16674},
+{24692, -16732},
+{24652, -16791},
+{24612, -16852},
+{24572, -16911},
+{24531, -16969},
+{24490, -17027},
+{24449, -17086},
+{24408, -17145},
+{24367, -17203},
+{24325, -17261},
+{24284, -17320},
+{24242, -17379},
+{24200, -17436},
+{24158, -17494},
+{24116, -17552},
+{24075, -17610},
+{24032, -17668},
+{23990, -17725},
+{23947, -17782},
+{23904, -17840},
+{23862, -17897},
+{23819, -17954},
+{23775, -18011},
+{23732, -18068},
+{23689, -18125},
+{23645, -18181},
+{23602, -18238},
+{23558, -18294},
+{23514, -18351},
+{23470, -18407},
+{23426, -18464},
+{23381, -18520},
+{23337, -18576},
+{23293, -18632},
+{23248, -18688},
+{23202, -18743},
+{23158, -18799},
+{23112, -18854},
+{23068, -18910},
+{23022, -18964},
+{22977, -19020},
+{22931, -19074},
+{22885, -19129},
+{22839, -19185},
+{22793, -19239},
+{22747, -19294},
+{22700, -19348},
+{22655, -19403},
+{22607, -19457},
+{22561, -19511},
+{22514, -19565},
+{22467, -19619},
+{22421, -19673},
+{22373, -19726},
+{22326, -19780},
+{22279, -19834},
+{22230, -19887},
+{22183, -19940},
+{22135, -19993},
+{22087, -20047},
+{22039, -20099},
+{21991, -20152},
+{21942, -20205},
+{21894, -20257},
+{21845, -20309},
+{21797, -20362},
+{21748, -20413},
+{21699, -20466},
+{21650, -20518},
+{21601, -20570},
+{21551, -20621},
+{21502, -20674}
+};
+
+cos_msin_t *a_cos_msin_table[] = {a_cos_msin_2, a_cos_msin_4,
+ a_cos_msin_8, a_cos_msin_16,
+ a_cos_msin_32,a_cos_msin_64
+ };
+
+Word16 dct_core_a[10][10] = {
+
+{ 10453, 10196, 9688, 8941, 7973, 6810, 5479, 4013, 2448, 823 },
+{ 10196, 7973, 4013, -823, -5479, -8941, -10453, -9688, -6810, -2448 },
+{ 9688 , 4013, -4013, -9688, -9688, -4013, 4013, 9688, 9688, 4013 },
+{ 8941 , -823, -9688, -7973, 2448, 10196, 6810, -4013, -10453, -5479 },
+{ 7973 , -5479, -9688, 2448, 10453, 823, -10196, -4013, 8941, 6810 },
+{ 6810 , -8941, -4013, 10196, 823, -10453, 2448, 9688, -5479, -7973 },
+{ 5479 , -10453, 4013, 6810, -10196, 2448, 7973, -9688, 823, 8941 },
+{ 4013 , -9688, 9688, -4013, -4013, 9688, -9688, 4013, 4013, -9688 },
+{ 2448 , -6810, 9688, -10453, 8941, -5479, 823, 4013, -7973, 10196 },
+{ 823 , -2448, 4013, -5479, 6810, -7973, 8941, -9688, 10196, -10453 }};
+
+Word16 anal_bias[320] = {
+ 1, 1, 3, 1, 4, 1, 3, -2, 4, 3,
+ 4, 1, 3, 0, 2, -3, 0, 0, 2, 2,
+ 4, 1, 1, -5, 4, 1, 2, -1, 0, -1,
+ 1, -2, 0, 2, 2, 2, 4, 1, 3, 0,
+ 5, 3, 2, 0, 3, 0, 1, -4, 1, 1,
+ 2, 0, 4, 0, 1, -4, 6, 1, 3, -1,
+ 1, 0, 0, -4, 1, 1, 3, 1, 3, 2,
+ 4, -2, 4, 3, 5, 1, 3, 0, 1, -3,
+ 1, 1, 2, 0, 4, 1, 2, -4, 4, 2,
+ 2, -1, 1, -1, 1, -4, 0, 0, 3, 0,
+ 5, 2, 3, -1, 6, 2, 5, 0, 4, 0,
+ 1, -3, 1, 0, 3, 0, 4, 0, 1, -3,
+ 4, 1, 3, -1, 1, -2, 1, -4, 0, 1,
+ 2, 1, 3, 2, 2, -2, 4, 3, 3, 0,
+ 3, 0, 0, -2, 1, 0, 2, 0, 5, -1,
+ 1, -3, 4, 2, 2, 0, 2, -3, 1, -4,
+ -1, 1, 2, 2, 4, 1, 3, -1, 5, 2,
+ 2, 0, 3, -1, 2, -3, 0, 1, 2, 2,
+ 4, 0, 1, -5, 5, 1, 3, 0, 2, -1,
+ 0, -2, 1, 2, 2, 2, 4, 1, 0, 0,
+ 4, 2, 4, 1, 4, -1, 1, -4, 0, 1,
+ 3, 1, 5, 1, 1, -2, 4, 0, 2, 0,
+ 2, -1, 0, -2, 0, 1, 1, 1, 4, 2,
+ 3, -2, 5, 4, 4, 0, 3, 0, 3, -4,
+ 1, 2, 2, 0, 4, 1, 0, -3, 4, 2,
+ 3, -1, 1, -1, 1, -4, 0, 2, 3, 1,
+ 4, 1, 3, 0, 3, 3, 4, 1, 2, 0,
+ 1, -3, 2, 2, 2, 1, 5, 0, 1, -4,
+ 4, 1, 3, -2, 3, -1, 0, -2, 0, 2,
+ 2, 0, 5, 1, 4, -1, 4, 3, 4, 1,
+ 3, 0, 1, -4, 2, 0, 3, 1, 5, 0,
+ 1, -5, 5, 2, 2, 0, 0, 0, 0, -4};
+
diff --git a/jni/pjproject-android/.svn/pristine/ef/efb7ad9e5ddcddd48fb26cd8dc0ff2bc56d20aeb.svn-base b/jni/pjproject-android/.svn/pristine/ef/efb7ad9e5ddcddd48fb26cd8dc0ff2bc56d20aeb.svn-base
new file mode 100644
index 0000000..ebed3a3
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/efb7ad9e5ddcddd48fb26cd8dc0ff2bc56d20aeb.svn-base
@@ -0,0 +1,1603 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sdp_neg.h>
+#include <pjmedia/sdp.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/ctype.h>
+#include <pj/array.h>
+
+/**
+ * This structure describes SDP media negotiator.
+ */
+struct pjmedia_sdp_neg
+{
+ pjmedia_sdp_neg_state state; /**< Negotiator state. */
+ pj_bool_t prefer_remote_codec_order;
+ pj_bool_t answer_with_multiple_codecs;
+ pj_bool_t has_remote_answer;
+ pj_bool_t answer_was_remote;
+
+ pjmedia_sdp_session *initial_sdp, /**< Initial local SDP */
+ *active_local_sdp, /**< Currently active local SDP. */
+ *active_remote_sdp, /**< Currently active remote's. */
+ *neg_local_sdp, /**< Temporary local SDP. */
+ *neg_remote_sdp; /**< Temporary remote SDP. */
+};
+
+static const char *state_str[] =
+{
+ "STATE_NULL",
+ "STATE_LOCAL_OFFER",
+ "STATE_REMOTE_OFFER",
+ "STATE_WAIT_NEGO",
+ "STATE_DONE",
+};
+
+/* Definition of customized SDP format negotiation callback */
+struct fmt_match_cb_t
+{
+ pj_str_t fmt_name;
+ pjmedia_sdp_neg_fmt_match_cb cb;
+};
+
+/* Number of registered customized SDP format negotiation callbacks */
+static unsigned fmt_match_cb_cnt;
+
+/* The registered customized SDP format negotiation callbacks */
+static struct fmt_match_cb_t
+ fmt_match_cb[PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB];
+
+/* Redefining a very long identifier name, just for convenience */
+#define ALLOW_MODIFY_ANSWER PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER
+
+static pj_status_t custom_fmt_match( pj_pool_t *pool,
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option);
+
+
+/*
+ * Get string representation of negotiator state.
+ */
+PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state)
+{
+ if (state >=0 && state < (pjmedia_sdp_neg_state)PJ_ARRAY_SIZE(state_str))
+ return state_str[state];
+
+ return "<?UNKNOWN?>";
+}
+
+
+/*
+ * Create with local offer.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool,
+ const pjmedia_sdp_session *local,
+ pjmedia_sdp_neg **p_neg)
+{
+ pjmedia_sdp_neg *neg;
+ pj_status_t status;
+
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && local && p_neg, PJ_EINVAL);
+
+ *p_neg = NULL;
+
+ /* Validate local offer. */
+ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(local))==PJ_SUCCESS, status);
+
+ /* Create and initialize negotiator. */
+ neg = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_neg);
+ PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM);
+
+ neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
+ neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER;
+ neg->answer_with_multiple_codecs = PJMEDIA_SDP_NEG_ANSWER_MULTIPLE_CODECS;
+ neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
+
+ *p_neg = neg;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create with remote offer and initial local offer/answer.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool,
+ const pjmedia_sdp_session *initial,
+ const pjmedia_sdp_session *remote,
+ pjmedia_sdp_neg **p_neg)
+{
+ pjmedia_sdp_neg *neg;
+ pj_status_t status;
+
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && remote && p_neg, PJ_EINVAL);
+
+ *p_neg = NULL;
+
+ /* Validate remote offer and initial answer */
+ status = pjmedia_sdp_validate2(remote, PJ_FALSE);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create and initialize negotiator. */
+ neg = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_neg);
+ PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM);
+
+ neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER;
+ neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
+
+ if (initial) {
+ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(initial))==PJ_SUCCESS,
+ status);
+
+ neg->initial_sdp = pjmedia_sdp_session_clone(pool, initial);
+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, initial);
+
+ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
+
+ } else {
+
+ neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER;
+
+ }
+
+ *p_neg = neg;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Set codec order preference.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_prefer_remote_codec_order(
+ pjmedia_sdp_neg *neg,
+ pj_bool_t prefer_remote)
+{
+ PJ_ASSERT_RETURN(neg, PJ_EINVAL);
+ neg->prefer_remote_codec_order = prefer_remote;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Set multiple codec answering.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_answer_multiple_codecs(
+ pjmedia_sdp_neg *neg,
+ pj_bool_t answer_multiple)
+{
+ PJ_ASSERT_RETURN(neg, PJ_EINVAL);
+ neg->answer_with_multiple_codecs = answer_multiple;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get SDP negotiator state.
+ */
+PJ_DEF(pjmedia_sdp_neg_state) pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg )
+{
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(neg != NULL, PJMEDIA_SDP_NEG_STATE_NULL);
+ return neg->state;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session **local)
+{
+ PJ_ASSERT_RETURN(neg && local, PJ_EINVAL);
+ PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
+
+ *local = neg->active_local_sdp;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session **remote)
+{
+ PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL);
+ PJ_ASSERT_RETURN(neg->active_remote_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
+
+ *remote = neg->active_remote_sdp;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_bool_t) pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg)
+{
+ PJ_ASSERT_RETURN(neg, PJ_FALSE);
+
+ return neg->answer_was_remote;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session **remote)
+{
+ PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL);
+ PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJMEDIA_SDPNEG_ENONEG);
+
+ *remote = neg->neg_remote_sdp;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session **local)
+{
+ PJ_ASSERT_RETURN(neg && local, PJ_EINVAL);
+ PJ_ASSERT_RETURN(neg->neg_local_sdp, PJMEDIA_SDPNEG_ENONEG);
+
+ *local = neg->neg_local_sdp;
+ return PJ_SUCCESS;
+}
+
+static pjmedia_sdp_media *sdp_media_clone_deactivate(
+ pj_pool_t *pool,
+ const pjmedia_sdp_media *rem_med,
+ const pjmedia_sdp_media *local_med,
+ const pjmedia_sdp_session *local_sess)
+{
+ pjmedia_sdp_media *res;
+
+ res = pjmedia_sdp_media_clone_deactivate(pool, rem_med);
+ if (!res)
+ return NULL;
+
+ if (!res->conn && (!local_sess || !local_sess->conn)) {
+ if (local_med && local_med->conn)
+ res->conn = pjmedia_sdp_conn_clone(pool, local_med->conn);
+ else {
+ res->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+ res->conn->net_type = pj_str("IN");
+ res->conn->addr_type = pj_str("IP4");
+ res->conn->addr = pj_str("127.0.0.1");
+ }
+ }
+
+ return res;
+}
+
+/*
+ * Modify local SDP and wait for remote answer.
+ */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session *local)
+{
+ return pjmedia_sdp_neg_modify_local_offer2(pool, neg, 0, local);
+}
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer2(
+ pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ unsigned flags,
+ const pjmedia_sdp_session *local)
+{
+ pjmedia_sdp_session *new_offer;
+ pjmedia_sdp_session *old_offer;
+ char media_used[PJMEDIA_MAX_SDP_MEDIA];
+ unsigned oi; /* old offer media index */
+ pj_status_t status;
+
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
+
+ /* Can only do this in STATE_DONE. */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ /* Validate the new offer */
+ status = pjmedia_sdp_validate(local);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Change state to STATE_LOCAL_OFFER */
+ neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
+
+ /* Init vars */
+ pj_bzero(media_used, sizeof(media_used));
+ old_offer = neg->active_local_sdp;
+ new_offer = pjmedia_sdp_session_clone(pool, local);
+
+ /* RFC 3264 Section 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.
+ */
+ pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user);
+ new_offer->origin.id = old_offer->origin.id;
+ new_offer->origin.version = old_offer->origin.version + 1;
+ pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type);
+ pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type);
+ pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr);
+
+ if ((flags & PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE) == 0) {
+ /* Generating the new offer, in the case media lines doesn't match the
+ * active SDP (e.g. current/active SDP's have m=audio and m=video lines,
+ * and the new offer only has m=audio line), the negotiator will fix
+ * the new offer by reordering and adding the missing media line with
+ * port number set to zero.
+ */
+ for (oi = 0; oi < old_offer->media_count; ++oi) {
+ pjmedia_sdp_media *om;
+ pjmedia_sdp_media *nm;
+ unsigned ni; /* new offer media index */
+ pj_bool_t found = PJ_FALSE;
+
+ om = old_offer->media[oi];
+ for (ni = oi; ni < new_offer->media_count; ++ni) {
+ nm = new_offer->media[ni];
+ if (pj_strcmp(&nm->desc.media, &om->desc.media) == 0) {
+ if (ni != oi) {
+ /* The same media found but the position unmatched to
+ * the old offer, so let's put this media in the right
+ * place, and keep the order of the rest.
+ */
+ pj_array_insert(
+ new_offer->media, /* array */
+ sizeof(new_offer->media[0]), /* elmt size*/
+ ni, /* count */
+ oi, /* pos */
+ &nm); /* new elmt */
+ }
+ found = PJ_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ pjmedia_sdp_media *m;
+
+ m = sdp_media_clone_deactivate(pool, om, om, local);
+
+ pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
+ new_offer->media_count++, oi, &m);
+ }
+ }
+ } else {
+ /* If media type change is allowed, the negotiator only needs to fix
+ * the new offer by adding the missing media line(s) with port number
+ * set to zero.
+ */
+ for (oi = new_offer->media_count; oi < old_offer->media_count; ++oi) {
+ pjmedia_sdp_media *m;
+
+ m = sdp_media_clone_deactivate(pool, old_offer->media[oi],
+ old_offer->media[oi], local);
+
+ pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
+ new_offer->media_count++, oi, &m);
+
+ }
+ }
+
+ /* New_offer fixed */
+ neg->initial_sdp = new_offer;
+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session **offer)
+{
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(neg && offer, PJ_EINVAL);
+
+ *offer = NULL;
+
+ /* Can only do this in STATE_DONE or STATE_LOCAL_OFFER. */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE ||
+ neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ if (neg->state == PJMEDIA_SDP_NEG_STATE_DONE) {
+ /* If in STATE_DONE, set the active SDP as the offer. */
+ PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
+
+ neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool,
+ neg->active_local_sdp);
+ *offer = neg->active_local_sdp;
+
+ } else {
+ /* We assume that we're in STATE_LOCAL_OFFER.
+ * In this case set the neg_local_sdp as the offer.
+ */
+ *offer = neg->neg_local_sdp;
+ }
+
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session *remote)
+{
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL);
+
+ /* Can only do this in STATE_LOCAL_OFFER.
+ * If we haven't provided local offer, then rx_remote_offer() should
+ * be called instead of this function.
+ */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ /* We're ready to negotiate. */
+ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
+ neg->has_remote_answer = PJ_TRUE;
+ neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session *remote)
+{
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL);
+
+ /* Can only do this in STATE_DONE.
+ * If we already provide local offer, then rx_remote_answer() should
+ * be called instead of this function.
+ */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ /* State now is STATE_REMOTE_OFFER. */
+ neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER;
+ neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ const pjmedia_sdp_session *local)
+{
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
+
+ /* Can only do this in STATE_REMOTE_OFFER.
+ * If we already provide local offer, then rx_remote_answer() should
+ * be called instead of this function.
+ */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ /* State now is STATE_WAIT_NEGO. */
+ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
+ if (local) {
+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
+ if (neg->initial_sdp) {
+ /* I don't think there is anything in RFC 3264 that mandates
+ * answerer to place the same origin (and increment version)
+ * in the answer, but probably it won't hurt either.
+ * Note that the version will be incremented in
+ * pjmedia_sdp_neg_negotiate()
+ */
+ neg->neg_local_sdp->origin.id = neg->initial_sdp->origin.id;
+ } else {
+ neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
+ }
+ } else {
+ PJ_ASSERT_RETURN(neg->initial_sdp, PJMEDIA_SDPNEG_ENOINITIAL);
+ neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp);
+ }
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg)
+{
+ pj_assert(neg && neg->state==PJMEDIA_SDP_NEG_STATE_WAIT_NEGO);
+ return !neg->has_remote_answer;
+}
+
+
+/* Swap string. */
+static void str_swap(pj_str_t *str1, pj_str_t *str2)
+{
+ pj_str_t tmp = *str1;
+ *str1 = *str2;
+ *str2 = tmp;
+}
+
+static void remove_all_media_directions(pjmedia_sdp_media *m)
+{
+ pjmedia_sdp_media_remove_all_attr(m, "inactive");
+ pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
+ pjmedia_sdp_media_remove_all_attr(m, "sendonly");
+ pjmedia_sdp_media_remove_all_attr(m, "recvonly");
+}
+
+/* Update media direction based on peer's media direction */
+static void update_media_direction(pj_pool_t *pool,
+ const pjmedia_sdp_media *remote,
+ pjmedia_sdp_media *local)
+{
+ pjmedia_dir old_dir = PJMEDIA_DIR_ENCODING_DECODING,
+ new_dir;
+
+ /* Get the media direction of local SDP */
+ if (pjmedia_sdp_media_find_attr2(local, "sendonly", NULL))
+ old_dir = PJMEDIA_DIR_ENCODING;
+ else if (pjmedia_sdp_media_find_attr2(local, "recvonly", NULL))
+ old_dir = PJMEDIA_DIR_DECODING;
+ else if (pjmedia_sdp_media_find_attr2(local, "inactive", NULL))
+ old_dir = PJMEDIA_DIR_NONE;
+
+ new_dir = old_dir;
+
+ /* Adjust local media direction based on remote media direction */
+ if (pjmedia_sdp_media_find_attr2(remote, "inactive", NULL) != NULL) {
+ /* If remote has "a=inactive", then local is inactive too */
+
+ new_dir = PJMEDIA_DIR_NONE;
+
+ } else if(pjmedia_sdp_media_find_attr2(remote, "sendonly", NULL) != NULL) {
+ /* If remote has "a=sendonly", then set local to "recvonly" if
+ * it is currently "sendrecv". Otherwise if local is NOT "recvonly",
+ * then set local direction to "inactive".
+ */
+ switch (old_dir) {
+ case PJMEDIA_DIR_ENCODING_DECODING:
+ new_dir = PJMEDIA_DIR_DECODING;
+ break;
+ case PJMEDIA_DIR_DECODING:
+ /* No change */
+ break;
+ default:
+ new_dir = PJMEDIA_DIR_NONE;
+ break;
+ }
+
+ } else if(pjmedia_sdp_media_find_attr2(remote, "recvonly", NULL) != NULL) {
+ /* If remote has "a=recvonly", then set local to "sendonly" if
+ * it is currently "sendrecv". Otherwise if local is NOT "sendonly",
+ * then set local direction to "inactive"
+ */
+
+ switch (old_dir) {
+ case PJMEDIA_DIR_ENCODING_DECODING:
+ new_dir = PJMEDIA_DIR_ENCODING;
+ break;
+ case PJMEDIA_DIR_ENCODING:
+ /* No change */
+ break;
+ default:
+ new_dir = PJMEDIA_DIR_NONE;
+ break;
+ }
+
+ } else {
+ /* Remote indicates "sendrecv" capability. No change to local
+ * direction
+ */
+ }
+
+ if (new_dir != old_dir) {
+ pjmedia_sdp_attr *a = NULL;
+
+ remove_all_media_directions(local);
+
+ switch (new_dir) {
+ case PJMEDIA_DIR_NONE:
+ a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
+ break;
+ case PJMEDIA_DIR_ENCODING:
+ a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
+ break;
+ case PJMEDIA_DIR_DECODING:
+ a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
+ break;
+ default:
+ /* sendrecv */
+ break;
+ }
+
+ if (a) {
+ pjmedia_sdp_media_add_attr(local, a);
+ }
+ }
+}
+
+
+/* Update single local media description to after receiving answer
+ * from remote.
+ */
+static pj_status_t process_m_answer( pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ pjmedia_sdp_media *answer,
+ pj_bool_t allow_asym)
+{
+ unsigned i;
+
+ /* Check that the media type match our offer. */
+
+ if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) {
+ /* The media type in the answer is different than the offer! */
+ return PJMEDIA_SDPNEG_EINVANSMEDIA;
+ }
+
+
+ /* Check that transport in the answer match our offer. */
+
+ /* At this point, transport type must be compatible,
+ * the transport instance will do more validation later.
+ */
+ if (pjmedia_sdp_transport_cmp(&answer->desc.transport,
+ &offer->desc.transport)
+ != PJ_SUCCESS)
+ {
+ return PJMEDIA_SDPNEG_EINVANSTP;
+ }
+
+
+ /* Check if remote has rejected our offer */
+ if (answer->desc.port == 0) {
+
+ /* Remote has rejected our offer.
+ * Deactivate our media too.
+ */
+ pjmedia_sdp_media_deactivate(pool, offer);
+
+ /* Don't need to proceed */
+ return PJ_SUCCESS;
+ }
+
+ /* Ticket #1148: check if remote answer does not set port to zero when
+ * offered with port zero. Let's just tolerate it.
+ */
+ if (offer->desc.port == 0) {
+ /* Don't need to proceed */
+ return PJ_SUCCESS;
+ }
+
+ /* Process direction attributes */
+ update_media_direction(pool, answer, offer);
+
+ /* If asymetric media is allowed, then just check that remote answer has
+ * codecs that are within the offer.
+ *
+ * Otherwise if asymetric media is not allowed, then we will choose only
+ * one codec in our initial offer to match the answer.
+ */
+ if (allow_asym) {
+ for (i=0; i<answer->desc.fmt_count; ++i) {
+ unsigned j;
+ pj_str_t *rem_fmt = &answer->desc.fmt[i];
+
+ for (j=0; j<offer->desc.fmt_count; ++j) {
+ if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0)
+ break;
+ }
+
+ if (j != offer->desc.fmt_count) {
+ /* Found at least one common codec. */
+ break;
+ }
+ }
+
+ if (i == answer->desc.fmt_count) {
+ /* No common codec in the answer! */
+ return PJMEDIA_SDPNEG_EANSNOMEDIA;
+ }
+
+ PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED);
+
+ } else {
+ /* Offer format priority based on answer format index/priority */
+ unsigned offer_fmt_prior[PJMEDIA_MAX_SDP_FMT];
+
+ /* Remove all format in the offer that has no matching answer */
+ for (i=0; i<offer->desc.fmt_count;) {
+ unsigned pt;
+ pj_uint32_t j;
+ pj_str_t *fmt = &offer->desc.fmt[i];
+
+
+ /* Find matching answer */
+ pt = pj_strtoul(fmt);
+
+ if (pt < 96) {
+ for (j=0; j<answer->desc.fmt_count; ++j) {
+ if (pj_strcmp(fmt, &answer->desc.fmt[j])==0)
+ break;
+ }
+ } else {
+ /* This is dynamic payload type.
+ * For dynamic payload type, we must look the rtpmap and
+ * compare the encoding name.
+ */
+ const pjmedia_sdp_attr *a;
+ pjmedia_sdp_rtpmap or_;
+
+ /* Get the rtpmap for the payload type in the offer. */
+ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
+ if (!a) {
+ pj_assert(!"Bug! Offer should have been validated");
+ return PJ_EBUG;
+ }
+ pjmedia_sdp_attr_get_rtpmap(a, &or_);
+
+ /* Find paylaod in answer SDP with matching
+ * encoding name and clock rate.
+ */
+ for (j=0; j<answer->desc.fmt_count; ++j) {
+ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap",
+ &answer->desc.fmt[j]);
+ if (a) {
+ pjmedia_sdp_rtpmap ar;
+ pjmedia_sdp_attr_get_rtpmap(a, &ar);
+
+ /* See if encoding name, clock rate, and channel
+ * count match
+ */
+ if (!pj_stricmp(&or_.enc_name, &ar.enc_name) &&
+ or_.clock_rate == ar.clock_rate &&
+ (pj_stricmp(&or_.param, &ar.param)==0 ||
+ (ar.param.slen==1 && *ar.param.ptr=='1')))
+ {
+ /* Call custom format matching callbacks */
+ if (custom_fmt_match(pool, &or_.enc_name,
+ offer, i, answer, j, 0) ==
+ PJ_SUCCESS)
+ {
+ /* Match! */
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (j == answer->desc.fmt_count) {
+ /* This format has no matching answer.
+ * Remove it from our offer.
+ */
+ pjmedia_sdp_attr *a;
+
+ /* Remove rtpmap associated with this format */
+ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
+ if (a)
+ pjmedia_sdp_media_remove_attr(offer, a);
+
+ /* Remove fmtp associated with this format */
+ a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt);
+ if (a)
+ pjmedia_sdp_media_remove_attr(offer, a);
+
+ /* Remove this format from offer's array */
+ pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]),
+ offer->desc.fmt_count, i);
+ --offer->desc.fmt_count;
+
+ } else {
+ offer_fmt_prior[i] = j;
+ ++i;
+ }
+ }
+
+ if (0 == offer->desc.fmt_count) {
+ /* No common codec in the answer! */
+ return PJMEDIA_SDPNEG_EANSNOMEDIA;
+ }
+
+ /* Post process:
+ * - Resort offer formats so the order match to the answer.
+ * - Remove answer formats that unmatches to the offer.
+ */
+
+ /* Resort offer formats */
+ for (i=0; i<offer->desc.fmt_count; ++i) {
+ unsigned j;
+ for (j=i+1; j<offer->desc.fmt_count; ++j) {
+ if (offer_fmt_prior[i] > offer_fmt_prior[j]) {
+ unsigned tmp = offer_fmt_prior[i];
+ offer_fmt_prior[i] = offer_fmt_prior[j];
+ offer_fmt_prior[j] = tmp;
+ str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]);
+ }
+ }
+ }
+
+ /* Remove unmatched answer formats */
+ {
+ unsigned del_cnt = 0;
+ for (i=0; i<answer->desc.fmt_count;) {
+ /* The offer is ordered now, also the offer_fmt_prior */
+ if (i >= offer->desc.fmt_count ||
+ offer_fmt_prior[i]-del_cnt != i)
+ {
+ pj_str_t *fmt = &answer->desc.fmt[i];
+ pjmedia_sdp_attr *a;
+
+ /* Remove rtpmap associated with this format */
+ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", fmt);
+ if (a)
+ pjmedia_sdp_media_remove_attr(answer, a);
+
+ /* Remove fmtp associated with this format */
+ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", fmt);
+ if (a)
+ pjmedia_sdp_media_remove_attr(answer, a);
+
+ /* Remove this format from answer's array */
+ pj_array_erase(answer->desc.fmt,
+ sizeof(answer->desc.fmt[0]),
+ answer->desc.fmt_count, i);
+ --answer->desc.fmt_count;
+
+ ++del_cnt;
+ } else {
+ ++i;
+ }
+ }
+ }
+ }
+
+ /* Looks okay */
+ return PJ_SUCCESS;
+}
+
+
+/* Update local media session (offer) to create active local session
+ * after receiving remote answer.
+ */
+static pj_status_t process_answer(pj_pool_t *pool,
+ pjmedia_sdp_session *offer,
+ pjmedia_sdp_session *answer,
+ pj_bool_t allow_asym,
+ pjmedia_sdp_session **p_active)
+{
+ unsigned omi = 0; /* Offer media index */
+ unsigned ami = 0; /* Answer media index */
+ pj_bool_t has_active = PJ_FALSE;
+ pj_status_t status;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(pool && offer && answer && p_active, PJ_EINVAL);
+
+ /* Check that media count match between offer and answer */
+ // Ticket #527, different media count is allowed for more interoperability,
+ // however, the media order must be same between offer and answer.
+ // if (offer->media_count != answer->media_count)
+ // return PJMEDIA_SDPNEG_EMISMEDIA;
+
+ /* Now update each media line in the offer with the answer. */
+ for (; omi<offer->media_count; ++omi) {
+ if (ami == answer->media_count) {
+ /* The answer has less media than the offer */
+ pjmedia_sdp_media *am;
+
+ /* Generate matching-but-disabled-media for the answer */
+ am = sdp_media_clone_deactivate(pool, offer->media[omi],
+ offer->media[omi], offer);
+ answer->media[answer->media_count++] = am;
+ ++ami;
+
+ /* Deactivate our media offer too */
+ pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
+
+ /* No answer media to be negotiated */
+ continue;
+ }
+
+ status = process_m_answer(pool, offer->media[omi], answer->media[ami],
+ allow_asym);
+
+ /* If media type is mismatched, just disable the media. */
+ if (status == PJMEDIA_SDPNEG_EINVANSMEDIA) {
+ pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
+ continue;
+ }
+ /* No common format in the answer media. */
+ else if (status == PJMEDIA_SDPNEG_EANSNOMEDIA) {
+ pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
+ pjmedia_sdp_media_deactivate(pool, answer->media[ami]);
+ }
+ /* Return the error code, for other errors. */
+ else if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ if (offer->media[omi]->desc.port != 0)
+ has_active = PJ_TRUE;
+
+ ++ami;
+ }
+
+ *p_active = offer;
+
+ return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA;
+}
+
+
+/* Internal function to rewrite the format string in SDP attribute rtpmap
+ * and fmtp.
+ */
+PJ_INLINE(void) rewrite_pt(pj_pool_t *pool, pj_str_t *attr_val,
+ const pj_str_t *old_pt, const pj_str_t *new_pt)
+{
+ int len_diff = (int)(new_pt->slen - old_pt->slen);
+
+ /* Note that attribute value should be null-terminated. */
+ if (len_diff > 0) {
+ pj_str_t new_val;
+ new_val.ptr = (char*)pj_pool_alloc(pool, attr_val->slen+len_diff+1);
+ new_val.slen = attr_val->slen + len_diff;
+ pj_memcpy(new_val.ptr + len_diff, attr_val->ptr, attr_val->slen + 1);
+ *attr_val = new_val;
+ } else if (len_diff < 0) {
+ attr_val->slen += len_diff;
+ pj_memmove(attr_val->ptr, attr_val->ptr - len_diff,
+ attr_val->slen + 1);
+ }
+ pj_memcpy(attr_val->ptr, new_pt->ptr, new_pt->slen);
+}
+
+
+/* Internal function to apply symmetric PT for the local answer. */
+static void apply_answer_symmetric_pt(pj_pool_t *pool,
+ pjmedia_sdp_media *answer,
+ unsigned pt_cnt,
+ const pj_str_t pt_offer[],
+ const pj_str_t pt_answer[])
+{
+ pjmedia_sdp_attr *a_tmp[PJMEDIA_MAX_SDP_ATTR];
+ unsigned i, a_tmp_cnt = 0;
+
+ /* Rewrite the payload types in the answer if different to
+ * the ones in the offer.
+ */
+ for (i = 0; i < pt_cnt; ++i) {
+ pjmedia_sdp_attr *a;
+
+ /* Skip if the PTs are the same already, e.g: static PT. */
+ if (pj_strcmp(&pt_answer[i], &pt_offer[i]) == 0)
+ continue;
+
+ /* Rewrite payload type in the answer to match to the offer */
+ pj_strdup(pool, &answer->desc.fmt[i], &pt_offer[i]);
+
+ /* Also update payload type in rtpmap */
+ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &pt_answer[i]);
+ if (a) {
+ rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]);
+ /* Temporarily remove the attribute in case the new payload
+ * type is being used by another format in the media.
+ */
+ pjmedia_sdp_media_remove_attr(answer, a);
+ a_tmp[a_tmp_cnt++] = a;
+ }
+
+ /* Also update payload type in fmtp */
+ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &pt_answer[i]);
+ if (a) {
+ rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]);
+ /* Temporarily remove the attribute in case the new payload
+ * type is being used by another format in the media.
+ */
+ pjmedia_sdp_media_remove_attr(answer, a);
+ a_tmp[a_tmp_cnt++] = a;
+ }
+ }
+
+ /* Return back 'rtpmap' and 'fmtp' attributes */
+ for (i = 0; i < a_tmp_cnt; ++i)
+ pjmedia_sdp_media_add_attr(answer, a_tmp[i]);
+}
+
+
+/* Try to match offer with answer. */
+static pj_status_t match_offer(pj_pool_t *pool,
+ pj_bool_t prefer_remote_codec_order,
+ pj_bool_t answer_with_multiple_codecs,
+ const pjmedia_sdp_media *offer,
+ const pjmedia_sdp_media *preanswer,
+ const pjmedia_sdp_session *preanswer_sdp,
+ pjmedia_sdp_media **p_answer)
+{
+ unsigned i;
+ pj_bool_t master_has_codec = 0,
+ master_has_telephone_event = 0,
+ master_has_other = 0,
+ found_matching_codec = 0,
+ found_matching_telephone_event = 0,
+ found_matching_other = 0;
+ unsigned pt_answer_count = 0;
+ pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
+ pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT];
+ pjmedia_sdp_media *answer;
+ const pjmedia_sdp_media *master, *slave;
+
+ /* If offer has zero port, just clone the offer */
+ if (offer->desc.port == 0) {
+ answer = sdp_media_clone_deactivate(pool, offer, preanswer,
+ preanswer_sdp);
+ *p_answer = answer;
+ return PJ_SUCCESS;
+ }
+
+ /* If the preanswer define zero port, this media is being rejected,
+ * just clone the preanswer.
+ */
+ if (preanswer->desc.port == 0) {
+ answer = pjmedia_sdp_media_clone(pool, preanswer);
+ *p_answer = answer;
+ return PJ_SUCCESS;
+ }
+
+ /* Set master/slave negotiator based on prefer_remote_codec_order. */
+ if (prefer_remote_codec_order) {
+ master = offer;
+ slave = preanswer;
+ } else {
+ master = preanswer;
+ slave = offer;
+ }
+
+ /* With the addition of telephone-event and dodgy MS RTC SDP,
+ * the answer generation algorithm looks really shitty...
+ */
+ for (i=0; i<master->desc.fmt_count; ++i) {
+ unsigned j;
+
+ if (pj_isdigit(*master->desc.fmt[i].ptr)) {
+ /* This is normal/standard payload type, where it's identified
+ * by payload number.
+ */
+ unsigned pt;
+
+ pt = pj_strtoul(&master->desc.fmt[i]);
+
+ if (pt < 96) {
+ /* For static payload type, it's enough to compare just
+ * the payload number.
+ */
+
+ master_has_codec = 1;
+
+ /* We just need to select one codec if not allowing multiple.
+ * Continue if we have selected matching codec for previous
+ * payload.
+ */
+ if (!answer_with_multiple_codecs && found_matching_codec)
+ continue;
+
+ /* Find matching codec in local descriptor. */
+ for (j=0; j<slave->desc.fmt_count; ++j) {
+ unsigned p;
+ p = pj_strtoul(&slave->desc.fmt[j]);
+ if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) {
+ found_matching_codec = 1;
+ pt_offer[pt_answer_count] = slave->desc.fmt[j];
+ pt_answer[pt_answer_count++] = slave->desc.fmt[j];
+ break;
+ }
+ }
+
+ } else {
+ /* This is dynamic payload type.
+ * For dynamic payload type, we must look the rtpmap and
+ * compare the encoding name.
+ */
+ const pjmedia_sdp_attr *a;
+ pjmedia_sdp_rtpmap or_;
+ pj_bool_t is_codec;
+
+ /* Get the rtpmap for the payload type in the master. */
+ a = pjmedia_sdp_media_find_attr2(master, "rtpmap",
+ &master->desc.fmt[i]);
+ if (!a) {
+ pj_assert(!"Bug! Offer should have been validated");
+ return PJMEDIA_SDP_EMISSINGRTPMAP;
+ }
+ pjmedia_sdp_attr_get_rtpmap(a, &or_);
+
+ if (!pj_stricmp2(&or_.enc_name, "telephone-event")) {
+ master_has_telephone_event = 1;
+ if (found_matching_telephone_event)
+ continue;
+ is_codec = 0;
+ } else {
+ master_has_codec = 1;
+ if (!answer_with_multiple_codecs && found_matching_codec)
+ continue;
+ is_codec = 1;
+ }
+
+ /* Find paylaod in our initial SDP with matching
+ * encoding name and clock rate.
+ */
+ for (j=0; j<slave->desc.fmt_count; ++j) {
+ a = pjmedia_sdp_media_find_attr2(slave, "rtpmap",
+ &slave->desc.fmt[j]);
+ if (a) {
+ pjmedia_sdp_rtpmap lr;
+ pjmedia_sdp_attr_get_rtpmap(a, &lr);
+
+ /* See if encoding name, clock rate, and
+ * channel count match
+ */
+ if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
+ or_.clock_rate == lr.clock_rate &&
+ (pj_stricmp(&or_.param, &lr.param)==0 ||
+ (lr.param.slen==0 && or_.param.slen==1 &&
+ *or_.param.ptr=='1') ||
+ (or_.param.slen==0 && lr.param.slen==1 &&
+ *lr.param.ptr=='1')))
+ {
+ /* Match! */
+ if (is_codec) {
+ pjmedia_sdp_media *o, *a;
+ unsigned o_fmt_idx, a_fmt_idx;
+
+ o = (pjmedia_sdp_media*)offer;
+ a = (pjmedia_sdp_media*)preanswer;
+ o_fmt_idx = prefer_remote_codec_order? i:j;
+ a_fmt_idx = prefer_remote_codec_order? j:i;
+
+ /* Call custom format matching callbacks */
+ if (custom_fmt_match(pool, &or_.enc_name,
+ o, o_fmt_idx,
+ a, a_fmt_idx,
+ ALLOW_MODIFY_ANSWER) !=
+ PJ_SUCCESS)
+ {
+ continue;
+ }
+ found_matching_codec = 1;
+ } else {
+ found_matching_telephone_event = 1;
+ }
+
+ pt_offer[pt_answer_count] =
+ prefer_remote_codec_order?
+ offer->desc.fmt[i]:
+ offer->desc.fmt[j];
+ pt_answer[pt_answer_count++] =
+ prefer_remote_codec_order?
+ preanswer->desc.fmt[j]:
+ preanswer->desc.fmt[i];
+ break;
+ }
+ }
+ }
+ }
+
+ } else {
+ /* This is a non-standard, brain damaged SDP where the payload
+ * type is non-numeric. It exists e.g. in Microsoft RTC based
+ * UA, to indicate instant messaging capability.
+ * Example:
+ * - m=x-ms-message 5060 sip null
+ */
+ master_has_other = 1;
+ if (found_matching_other)
+ continue;
+
+ for (j=0; j<slave->desc.fmt_count; ++j) {
+ if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) {
+ /* Match */
+ found_matching_other = 1;
+ pt_offer[pt_answer_count] = prefer_remote_codec_order?
+ offer->desc.fmt[i]:
+ offer->desc.fmt[j];
+ pt_answer[pt_answer_count++] = prefer_remote_codec_order?
+ preanswer->desc.fmt[j]:
+ preanswer->desc.fmt[i];
+ break;
+ }
+ }
+ }
+ }
+
+ /* See if all types of master can be matched. */
+ if (master_has_codec && !found_matching_codec) {
+ return PJMEDIA_SDPNEG_NOANSCODEC;
+ }
+
+ /* If this comment is removed, negotiation will fail if remote has offered
+ telephone-event and local is not configured with telephone-event
+
+ if (offer_has_telephone_event && !found_matching_telephone_event) {
+ return PJMEDIA_SDPNEG_NOANSTELEVENT;
+ }
+ */
+
+ if (master_has_other && !found_matching_other) {
+ return PJMEDIA_SDPNEG_NOANSUNKNOWN;
+ }
+
+ /* Seems like everything is in order.
+ * Build the answer by cloning from preanswer, but rearrange the payload
+ * to suit the offer.
+ */
+ answer = pjmedia_sdp_media_clone(pool, preanswer);
+ for (i=0; i<pt_answer_count; ++i) {
+ unsigned j;
+ for (j=i; j<answer->desc.fmt_count; ++j) {
+ if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
+ break;
+ }
+ pj_assert(j != answer->desc.fmt_count);
+ str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
+ }
+
+ /* Remove unwanted local formats. */
+ for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
+ pjmedia_sdp_attr *a;
+
+ /* Remove rtpmap for this format */
+ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap",
+ &answer->desc.fmt[i]);
+ if (a) {
+ pjmedia_sdp_media_remove_attr(answer, a);
+ }
+
+ /* Remove fmtp for this format */
+ a = pjmedia_sdp_media_find_attr2(answer, "fmtp",
+ &answer->desc.fmt[i]);
+ if (a) {
+ pjmedia_sdp_media_remove_attr(answer, a);
+ }
+ }
+ answer->desc.fmt_count = pt_answer_count;
+
+#if PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT
+ apply_answer_symmetric_pt(pool, answer, pt_answer_count,
+ pt_offer, pt_answer);
+#endif
+
+ /* Update media direction. */
+ update_media_direction(pool, offer, answer);
+
+ *p_answer = answer;
+ return PJ_SUCCESS;
+}
+
+/* Create complete answer for remote's offer. */
+static pj_status_t create_answer( pj_pool_t *pool,
+ pj_bool_t prefer_remote_codec_order,
+ pj_bool_t answer_with_multiple_codecs,
+ const pjmedia_sdp_session *initial,
+ const pjmedia_sdp_session *offer,
+ pjmedia_sdp_session **p_answer)
+{
+ pj_status_t status = PJMEDIA_SDPNEG_ENOMEDIA;
+ pj_bool_t has_active = PJ_FALSE;
+ pjmedia_sdp_session *answer;
+ char media_used[PJMEDIA_MAX_SDP_MEDIA];
+ unsigned i;
+
+ /* Validate remote offer.
+ * This should have been validated before.
+ */
+ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status);
+
+ /* Create initial answer by duplicating initial SDP,
+ * but clear all media lines. The media lines will be filled up later.
+ */
+ answer = pjmedia_sdp_session_clone(pool, initial);
+ PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM);
+
+ answer->media_count = 0;
+
+ pj_bzero(media_used, sizeof(media_used));
+
+ /* For each media line, create our answer based on our initial
+ * capability.
+ */
+ for (i=0; i<offer->media_count; ++i) {
+ const pjmedia_sdp_media *om; /* offer */
+ const pjmedia_sdp_media *im; /* initial media */
+ pjmedia_sdp_media *am = NULL; /* answer/result */
+ unsigned j;
+
+ om = offer->media[i];
+
+ /* Find media description in our initial capability that matches
+ * the media type and transport type of offer's media, has
+ * matching codec, and has not been used to answer other offer.
+ */
+ for (im=NULL, j=0; j<initial->media_count; ++j) {
+ im = initial->media[j];
+ if (pj_strcmp(&om->desc.media, &im->desc.media)==0 &&
+ pj_strcmp(&om->desc.transport, &im->desc.transport)==0 &&
+ media_used[j] == 0)
+ {
+ pj_status_t status2;
+
+ /* See if it has matching codec. */
+ status2 = match_offer(pool, prefer_remote_codec_order,
+ answer_with_multiple_codecs,
+ om, im, initial, &am);
+ if (status2 == PJ_SUCCESS) {
+ /* Mark media as used. */
+ media_used[j] = 1;
+ break;
+ } else {
+ status = status2;
+ }
+ }
+ }
+
+ if (j==initial->media_count) {
+ /* No matching media.
+ * Reject the offer by setting the port to zero in the answer.
+ */
+ /* For simplicity in the construction of the answer, we'll
+ * just clone the media from the offer. Anyway receiver will
+ * ignore anything in the media once it sees that the port
+ * number is zero.
+ */
+ am = sdp_media_clone_deactivate(pool, om, om, answer);
+ } else {
+ /* The answer is in am */
+ pj_assert(am != NULL);
+ }
+
+ /* Add the media answer */
+ answer->media[answer->media_count++] = am;
+
+ /* Check if this media is active.*/
+ if (am->desc.port != 0)
+ has_active = PJ_TRUE;
+ }
+
+ *p_answer = answer;
+
+ return has_active ? PJ_SUCCESS : status;
+}
+
+/* Cancel offer */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg)
+{
+ PJ_ASSERT_RETURN(neg, PJ_EINVAL);
+
+ /* Must be in LOCAL_OFFER state. */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
+ neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ /* Clear temporary SDP */
+ neg->neg_local_sdp = neg->neg_remote_sdp = NULL;
+ neg->has_remote_answer = PJ_FALSE;
+
+ if (neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
+ /* Increment next version number. This happens if for example
+ * the reinvite offer is rejected by 488. If we don't increment
+ * the version here, the next offer will have the same version.
+ */
+ neg->active_local_sdp->origin.version++;
+ }
+
+ /* Reset state to done */
+ neg->state = PJMEDIA_SDP_NEG_STATE_DONE;
+
+ return PJ_SUCCESS;
+}
+
+
+/* The best bit: SDP negotiation function! */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
+ pjmedia_sdp_neg *neg,
+ pj_bool_t allow_asym)
+{
+ pj_status_t status;
+
+ /* Check arguments are valid. */
+ PJ_ASSERT_RETURN(pool && neg, PJ_EINVAL);
+
+ /* Must be in STATE_WAIT_NEGO state. */
+ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO,
+ PJMEDIA_SDPNEG_EINSTATE);
+
+ /* Must have remote offer. */
+ PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJ_EBUG);
+
+ if (neg->has_remote_answer) {
+ pjmedia_sdp_session *active;
+ status = process_answer(pool, neg->neg_local_sdp, neg->neg_remote_sdp,
+ allow_asym, &active);
+ if (status == PJ_SUCCESS) {
+ /* Only update active SDPs when negotiation is successfull */
+ neg->active_local_sdp = active;
+ neg->active_remote_sdp = neg->neg_remote_sdp;
+ }
+ } else {
+ pjmedia_sdp_session *answer = NULL;
+
+ status = create_answer(pool, neg->prefer_remote_codec_order,
+ neg->answer_with_multiple_codecs,
+ neg->neg_local_sdp, neg->neg_remote_sdp,
+ &answer);
+ if (status == PJ_SUCCESS) {
+ pj_uint32_t active_ver;
+
+ if (neg->active_local_sdp)
+ active_ver = neg->active_local_sdp->origin.version;
+ else
+ active_ver = neg->initial_sdp->origin.version;
+
+ /* Only update active SDPs when negotiation is successfull */
+ neg->active_local_sdp = answer;
+ neg->active_remote_sdp = neg->neg_remote_sdp;
+
+ /* Increment SDP version */
+ neg->active_local_sdp->origin.version = ++active_ver;
+ }
+ }
+
+ /* State is DONE regardless */
+ neg->state = PJMEDIA_SDP_NEG_STATE_DONE;
+
+ /* Save state */
+ neg->answer_was_remote = neg->has_remote_answer;
+
+ /* Clear temporary SDP */
+ neg->neg_local_sdp = neg->neg_remote_sdp = NULL;
+ neg->has_remote_answer = PJ_FALSE;
+
+ return status;
+}
+
+
+static pj_status_t custom_fmt_match(pj_pool_t *pool,
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option)
+{
+ unsigned i;
+
+ for (i = 0; i < fmt_match_cb_cnt; ++i) {
+ if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) {
+ pj_assert(fmt_match_cb[i].cb);
+ return (*fmt_match_cb[i].cb)(pool, offer, o_fmt_idx,
+ answer, a_fmt_idx,
+ option);
+ }
+ }
+
+ /* Not customized format matching found, should be matched */
+ return PJ_SUCCESS;
+}
+
+/* Register customized SDP format negotiation callback function. */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb(
+ const pj_str_t *fmt_name,
+ pjmedia_sdp_neg_fmt_match_cb cb)
+{
+ struct fmt_match_cb_t *f = NULL;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(fmt_name, PJ_EINVAL);
+
+ /* Check if the callback for the format name has been registered */
+ for (i = 0; i < fmt_match_cb_cnt; ++i) {
+ if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0)
+ break;
+ }
+
+ /* Unregistration */
+
+ if (cb == NULL) {
+ if (i == fmt_match_cb_cnt)
+ return PJ_ENOTFOUND;
+
+ pj_array_erase(fmt_match_cb, sizeof(fmt_match_cb[0]),
+ fmt_match_cb_cnt, i);
+ fmt_match_cb_cnt--;
+
+ return PJ_SUCCESS;
+ }
+
+ /* Registration */
+
+ if (i < fmt_match_cb_cnt) {
+ /* The same format name has been registered before */
+ if (cb != fmt_match_cb[i].cb)
+ return PJ_EEXISTS;
+ else
+ return PJ_SUCCESS;
+ }
+
+ if (fmt_match_cb_cnt >= PJ_ARRAY_SIZE(fmt_match_cb))
+ return PJ_ETOOMANY;
+
+ f = &fmt_match_cb[fmt_match_cb_cnt++];
+ f->fmt_name = *fmt_name;
+ f->cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Match format in the SDP media offer and answer. */
+PJ_DEF(pj_status_t) pjmedia_sdp_neg_fmt_match(pj_pool_t *pool,
+ pjmedia_sdp_media *offer,
+ unsigned o_fmt_idx,
+ pjmedia_sdp_media *answer,
+ unsigned a_fmt_idx,
+ unsigned option)
+{
+ const pjmedia_sdp_attr *attr;
+ pjmedia_sdp_rtpmap o_rtpmap, a_rtpmap;
+ unsigned o_pt;
+ unsigned a_pt;
+
+ o_pt = pj_strtoul(&offer->desc.fmt[o_fmt_idx]);
+ a_pt = pj_strtoul(&answer->desc.fmt[a_fmt_idx]);
+
+ if (o_pt < 96 || a_pt < 96) {
+ if (o_pt == a_pt)
+ return PJ_SUCCESS;
+ else
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+
+ /* Get the format rtpmap from the offer. */
+ attr = pjmedia_sdp_media_find_attr2(offer, "rtpmap",
+ &offer->desc.fmt[o_fmt_idx]);
+ if (!attr) {
+ pj_assert(!"Bug! Offer haven't been validated");
+ return PJ_EBUG;
+ }
+ pjmedia_sdp_attr_get_rtpmap(attr, &o_rtpmap);
+
+ /* Get the format rtpmap from the answer. */
+ attr = pjmedia_sdp_media_find_attr2(answer, "rtpmap",
+ &answer->desc.fmt[a_fmt_idx]);
+ if (!attr) {
+ pj_assert(!"Bug! Answer haven't been validated");
+ return PJ_EBUG;
+ }
+ pjmedia_sdp_attr_get_rtpmap(attr, &a_rtpmap);
+
+ if (pj_stricmp(&o_rtpmap.enc_name, &a_rtpmap.enc_name) != 0 ||
+ o_rtpmap.clock_rate != a_rtpmap.clock_rate)
+ {
+ return PJMEDIA_SDP_EFORMATNOTEQUAL;
+ }
+
+ return custom_fmt_match(pool, &o_rtpmap.enc_name,
+ offer, o_fmt_idx, answer, a_fmt_idx, option);
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/ef/efdfc668e2626957bc7e0579fc98fbb68fc47d04.svn-base b/jni/pjproject-android/.svn/pristine/ef/efdfc668e2626957bc7e0579fc98fbb68fc47d04.svn-base
new file mode 100644
index 0000000..43dd162
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/efdfc668e2626957bc7e0579fc98fbb68fc47d04.svn-base
@@ -0,0 +1,194 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/errno.h>
+
+#define THIS_FILE "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op) \
+ ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op) \
+ ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT)
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT)
+#else
+# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0
+# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+# define VALIDATE_FD_SET 1
+#else
+# define VALIDATE_FD_SET 0
+#endif
+
+struct pj_ioqueue_key_t
+{
+ PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+ pj_sock_t fd;
+ pj_ioqueue_operation_e op;
+ void *user_data;
+ pj_ioqueue_callback cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
+ pj_size_t max_fd,
+ int max_threads,
+ pj_ioqueue_t **ptr_ioqueue)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque,
+ pj_lock_t *lock,
+ pj_bool_t auto_delete )
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+ pj_ioqueue_t *ioque,
+ pj_sock_t sock,
+ void *user_data,
+ const pj_ioqueue_callback *cb,
+ pj_ioqueue_key_t **ptr_key)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key)
+{
+ return PJ_ENOTSUP;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+ return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ void *buffer,
+ pj_size_t buflen,
+ unsigned flags,
+ pj_sockaddr_t *addr,
+ int *addrlen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags)
+{
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+ pj_ioqueue_key_t *key,
+ const void *data,
+ pj_size_t datalen,
+ unsigned flags,
+ const pj_sockaddr_t *addr,
+ int addrlen)
+{
+ return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ pj_sock_t *new_sock,
+ pj_sockaddr_t *local,
+ pj_sockaddr_t *remote,
+ int *addrlen)
+{
+ return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+ pj_ioqueue_key_t *key,
+ const pj_sockaddr_t *addr,
+ int addrlen )
+{
+ return -1;
+}
+#endif /* PJ_HAS_TCP */
+
diff --git a/jni/pjproject-android/.svn/pristine/ef/efe25a0ba7e95c61b726770ed52f88f6fe0414f1.svn-base b/jni/pjproject-android/.svn/pristine/ef/efe25a0ba7e95c61b726770ed52f88f6fe0414f1.svn-base
new file mode 100644
index 0000000..13a6992
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/efe25a0ba7e95c61b726770ed52f88f6fe0414f1.svn-base
@@ -0,0 +1,36 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJ_COMPAT_M_POWERPC_H__
+#define __PJ_COMPAT_M_POWERPC_H__
+
+/**
+ * @file m_ppc.h
+ * @brief Describes PowerPC family processor specifics.
+ */
+
+#define PJ_M_NAME "powerpc"
+
+#define PJ_HAS_PENTIUM 0
+#define PJ_IS_LITTLE_ENDIAN 0
+#define PJ_IS_BIG_ENDIAN 1
+
+
+#endif /* __PJ_COMPAT_M_POWERPC_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/ef/eff780736ae7b1a538b416bf7c13798b596d09f3.svn-base b/jni/pjproject-android/.svn/pristine/ef/eff780736ae7b1a538b416bf7c13798b596d09f3.svn-base
new file mode 100644
index 0000000..e477a77
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/ef/eff780736ae7b1a538b416bf7c13798b596d09f3.svn-base
@@ -0,0 +1,519 @@
+/*
+ * rtpw.c
+ *
+ * rtp word sender/receiver
+ *
+ * David A. McGrew
+ * Cisco Systems, Inc.
+ *
+ * This app is a simple RTP application intended only for testing
+ * libsrtp. It reads one word at a time from /usr/dict/words (or
+ * whatever file is specified as DICT_FILE), and sends one word out
+ * each USEC_RATE microseconds. Secure RTP protections can be
+ * applied. See the usage() function for more details.
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2001-2006, Cisco Systems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of the Cisco Systems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include "datatypes.h"
+#include "getopt_s.h" /* for local getopt() */
+
+#include <stdio.h> /* for printf, fprintf */
+#include <stdlib.h> /* for atoi() */
+#include <errno.h>
+#include <unistd.h> /* for close() */
+
+#include <string.h> /* for strncpy() */
+#include <time.h> /* for usleep() */
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#elif defined HAVE_WINSOCK2_H
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# define RTPW_USE_WINSOCK2 1
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#include "srtp.h"
+#include "rtp.h"
+
+#ifdef RTPW_USE_WINSOCK2
+# define DICT_FILE "words.txt"
+#else
+# define DICT_FILE "/usr/share/dict/words"
+#endif
+#define USEC_RATE (5e5)
+#define MAX_WORD_LEN 128
+#define ADDR_IS_MULTICAST(a) IN_MULTICAST(htonl(a))
+#define MAX_KEY_LEN 64
+#define MASTER_KEY_LEN 30
+
+
+#ifndef HAVE_USLEEP
+# ifdef HAVE_WINDOWS_H
+# define usleep(us) Sleep((us)/1000)
+# else
+# define usleep(us) sleep((us)/1000000)
+# endif
+#endif
+
+
+/*
+ * the function usage() prints an error message describing how this
+ * program should be called, then calls exit()
+ */
+
+void
+usage(char *prog_name);
+
+/*
+ * leave_group(...) de-registers from a multicast group
+ */
+
+void
+leave_group(int sock, struct ip_mreq mreq, char *name);
+
+
+/*
+ * program_type distinguishes the [s]rtp sender and receiver cases
+ */
+
+typedef enum { sender, receiver, unknown } program_type;
+
+int
+main (int argc, char *argv[]) {
+ char *dictfile = DICT_FILE;
+ FILE *dict;
+ char word[MAX_WORD_LEN];
+ int sock, ret;
+ struct in_addr rcvr_addr;
+ struct sockaddr_in name;
+ struct ip_mreq mreq;
+#if BEW
+ struct sockaddr_in local;
+#endif
+ program_type prog_type = unknown;
+ sec_serv_t sec_servs = sec_serv_none;
+ unsigned char ttl = 5;
+ int c;
+ char *input_key = NULL;
+ char *address = NULL;
+ char key[MAX_KEY_LEN];
+ unsigned short port = 0;
+ rtp_sender_t snd;
+ srtp_policy_t policy;
+ err_status_t status;
+ int len;
+ int do_list_mods = 0;
+ uint32_t ssrc = 0xdeadbeef; /* ssrc value hardcoded for now */
+#ifdef RTPW_USE_WINSOCK2
+ WORD wVersionRequested = MAKEWORD(2, 0);
+ WSADATA wsaData;
+
+ ret = WSAStartup(wVersionRequested, &wsaData);
+ if (ret != 0) {
+ fprintf(stderr, "error: WSAStartup() failed: %d\n", ret);
+ exit(1);
+ }
+#endif
+
+ /* initialize srtp library */
+ status = srtp_init();
+ if (status) {
+ printf("error: srtp initialization failed with error code %d\n", status);
+ exit(1);
+ }
+
+ /* check args */
+ while (1) {
+ c = getopt_s(argc, argv, "k:rsaeld:");
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 'k':
+ input_key = optarg_s;
+ break;
+ case 'e':
+ sec_servs |= sec_serv_conf;
+ break;
+ case 'a':
+ sec_servs |= sec_serv_auth;
+ break;
+ case 'r':
+ prog_type = receiver;
+ break;
+ case 's':
+ prog_type = sender;
+ break;
+ case 'd':
+ status = crypto_kernel_set_debug_module(optarg_s, 1);
+ if (status) {
+ printf("error: set debug module (%s) failed\n", optarg_s);
+ exit(1);
+ }
+ break;
+ case 'l':
+ do_list_mods = 1;
+ break;
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if (prog_type == unknown) {
+ if (do_list_mods) {
+ status = crypto_kernel_list_debug_modules();
+ if (status) {
+ printf("error: list of debug modules failed\n");
+ exit(1);
+ }
+ return 0;
+ } else {
+ printf("error: neither sender [-s] nor receiver [-r] specified\n");
+ usage(argv[0]);
+ }
+ }
+
+ if ((sec_servs && !input_key) || (!sec_servs && input_key)) {
+ /*
+ * a key must be provided if and only if security services have
+ * been requested
+ */
+ usage(argv[0]);
+ }
+
+ if (argc != optind_s + 2) {
+ /* wrong number of arguments */
+ usage(argv[0]);
+ }
+
+ /* get address from arg */
+ address = argv[optind_s++];
+
+ /* get port from arg */
+ port = atoi(argv[optind_s++]);
+
+ /* set address */
+#ifdef HAVE_INET_ATON
+ if (0 == inet_aton(address, &rcvr_addr)) {
+ fprintf(stderr, "%s: cannot parse IP v4 address %s\n", argv[0], address);
+ exit(1);
+ }
+ if (rcvr_addr.s_addr == INADDR_NONE) {
+ fprintf(stderr, "%s: address error", argv[0]);
+ exit(1);
+ }
+#else
+ rcvr_addr.s_addr = inet_addr(address);
+ if (0xffffffff == rcvr_addr.s_addr) {
+ fprintf(stderr, "%s: cannot parse IP v4 address %s\n", argv[0], address);
+ exit(1);
+ }
+#endif
+
+ /* open socket */
+ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ int err;
+#ifdef RTPW_USE_WINSOCK2
+ err = WSAGetLastError();
+#else
+ err = errno;
+#endif
+ fprintf(stderr, "%s: couldn't open socket: %d\n", argv[0], err);
+ exit(1);
+ }
+
+ name.sin_addr = rcvr_addr;
+ name.sin_family = PF_INET;
+ name.sin_port = htons(port);
+
+ if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
+ if (prog_type == sender) {
+ ret = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl));
+ if (ret < 0) {
+ fprintf(stderr, "%s: Failed to set TTL for multicast group", argv[0]);
+ perror("");
+ exit(1);
+ }
+ }
+
+ mreq.imr_multiaddr.s_addr = rcvr_addr.s_addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&mreq,
+ sizeof(mreq));
+ if (ret < 0) {
+ fprintf(stderr, "%s: Failed to join multicast group", argv[0]);
+ perror("");
+ exit(1);
+ }
+ }
+
+ /* report security services selected on the command line */
+ printf("security services: ");
+ if (sec_servs & sec_serv_conf)
+ printf("confidentiality ");
+ if (sec_servs & sec_serv_auth)
+ printf("message authentication");
+ if (sec_servs == sec_serv_none)
+ printf("none");
+ printf("\n");
+
+ /* set up the srtp policy and master key */
+ if (sec_servs) {
+ /*
+ * create policy structure, using the default mechanisms but
+ * with only the security services requested on the command line,
+ * using the right SSRC value
+ */
+ switch (sec_servs) {
+ case sec_serv_conf_and_auth:
+ crypto_policy_set_rtp_default(&policy.rtp);
+ crypto_policy_set_rtcp_default(&policy.rtcp);
+ break;
+ case sec_serv_conf:
+ crypto_policy_set_aes_cm_128_null_auth(&policy.rtp);
+ crypto_policy_set_rtcp_default(&policy.rtcp);
+ break;
+ case sec_serv_auth:
+ crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp);
+ crypto_policy_set_rtcp_default(&policy.rtcp);
+ break;
+ default:
+ printf("error: unknown security service requested\n");
+ return -1;
+ }
+ policy.ssrc.type = ssrc_specific;
+ policy.ssrc.value = ssrc;
+ policy.key = (uint8_t *) key;
+ policy.next = NULL;
+ policy.rtp.sec_serv = sec_servs;
+ policy.rtcp.sec_serv = sec_serv_none; /* we don't do RTCP anyway */
+
+ /*
+ * read key from hexadecimal on command line into an octet string
+ */
+ len = hex_string_to_octet_string(key, input_key, MASTER_KEY_LEN*2);
+
+ /* check that hex string is the right length */
+ if (len < MASTER_KEY_LEN*2) {
+ fprintf(stderr,
+ "error: too few digits in key/salt "
+ "(should be %d hexadecimal digits, found %d)\n",
+ MASTER_KEY_LEN*2, len);
+ exit(1);
+ }
+ if (strlen(input_key) > MASTER_KEY_LEN*2) {
+ fprintf(stderr,
+ "error: too many digits in key/salt "
+ "(should be %d hexadecimal digits, found %u)\n",
+ MASTER_KEY_LEN*2, (unsigned)strlen(input_key));
+ exit(1);
+ }
+
+ printf("set master key/salt to %s/", octet_string_hex_string(key, 16));
+ printf("%s\n", octet_string_hex_string(key+16, 14));
+
+ } else {
+ /*
+ * we're not providing security services, so set the policy to the
+ * null policy
+ *
+ * Note that this policy does not conform to the SRTP
+ * specification, since RTCP authentication is required. However,
+ * the effect of this policy is to turn off SRTP, so that this
+ * application is now a vanilla-flavored RTP application.
+ */
+ policy.key = (uint8_t *)key;
+ policy.ssrc.type = ssrc_specific;
+ policy.ssrc.value = ssrc;
+ policy.rtp.cipher_type = NULL_CIPHER;
+ policy.rtp.cipher_key_len = 0;
+ policy.rtp.auth_type = NULL_AUTH;
+ policy.rtp.auth_key_len = 0;
+ policy.rtp.auth_tag_len = 0;
+ policy.rtp.sec_serv = sec_serv_none;
+ policy.rtcp.cipher_type = NULL_CIPHER;
+ policy.rtcp.cipher_key_len = 0;
+ policy.rtcp.auth_type = NULL_AUTH;
+ policy.rtcp.auth_key_len = 0;
+ policy.rtcp.auth_tag_len = 0;
+ policy.rtcp.sec_serv = sec_serv_none;
+ policy.next = NULL;
+ }
+
+ if (prog_type == sender) {
+
+#if BEW
+ /* bind to local socket (to match crypto policy, if need be) */
+ memset(&local, 0, sizeof(struct sockaddr_in));
+ local.sin_addr.s_addr = htonl(INADDR_ANY);
+ local.sin_port = htons(port);
+ ret = bind(sock, (struct sockaddr *) &local, sizeof(struct sockaddr_in));
+ if (ret < 0) {
+ fprintf(stderr, "%s: bind failed\n", argv[0]);
+ perror("");
+ exit(1);
+ }
+#endif /* BEW */
+
+ /* initialize sender's rtp and srtp contexts */
+ snd = rtp_sender_alloc();
+ if (snd == NULL) {
+ fprintf(stderr, "error: malloc() failed\n");
+ exit(1);
+ }
+ rtp_sender_init(snd, sock, name, ssrc);
+ status = rtp_sender_init_srtp(snd, &policy);
+ if (status) {
+ fprintf(stderr,
+ "error: srtp_create() failed with code %d\n",
+ status);
+ exit(1);
+ }
+
+ /* open dictionary */
+ dict = fopen (dictfile, "r");
+ if (dict == NULL) {
+ fprintf(stderr, "%s: couldn't open file %s\n", argv[0], dictfile);
+ if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
+ leave_group(sock, mreq, argv[0]);
+ }
+ exit(1);
+ }
+
+ /* read words from dictionary, then send them off */
+ while (fgets(word, MAX_WORD_LEN, dict) != NULL) {
+ len = strlen(word) + 1; /* plus one for null */
+
+ if (len > MAX_WORD_LEN)
+ printf("error: word %s too large to send\n", word);
+ else {
+ rtp_sendto(snd, word, len);
+ printf("sending word: %s", word);
+ }
+ usleep(USEC_RATE);
+ }
+
+ } else { /* prog_type == receiver */
+ rtp_receiver_t rcvr;
+
+ if (bind(sock, (struct sockaddr *)&name, sizeof(name)) < 0) {
+ close(sock);
+ fprintf(stderr, "%s: socket bind error\n", argv[0]);
+ perror(NULL);
+ if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
+ leave_group(sock, mreq, argv[0]);
+ }
+ exit(1);
+ }
+
+ rcvr = rtp_receiver_alloc();
+ if (rcvr == NULL) {
+ fprintf(stderr, "error: malloc() failed\n");
+ exit(1);
+ }
+ rtp_receiver_init(rcvr, sock, name, ssrc);
+ status = rtp_receiver_init_srtp(rcvr, &policy);
+ if (status) {
+ fprintf(stderr,
+ "error: srtp_create() failed with code %d\n",
+ status);
+ exit(1);
+ }
+
+ /* get next word and loop */
+ while (1) {
+ len = MAX_WORD_LEN;
+ if (rtp_recvfrom(rcvr, word, &len) > -1)
+ printf("\tword: %s", word);
+ }
+
+ }
+
+ if (ADDR_IS_MULTICAST(rcvr_addr.s_addr)) {
+ leave_group(sock, mreq, argv[0]);
+ }
+
+#ifdef RTPW_USE_WINSOCK2
+ WSACleanup();
+#endif
+
+ return 0;
+}
+
+
+void
+usage(char *string) {
+
+ printf("usage: %s [-d <debug>]* [-k <key> [-a][-e]] "
+ "[-s | -r] dest_ip dest_port\n"
+ "or %s -l\n"
+ "where -a use message authentication\n"
+ " -e use encryption\n"
+ " -k <key> sets the srtp master key\n"
+ " -s act as rtp sender\n"
+ " -r act as rtp receiver\n"
+ " -l list debug modules\n"
+ " -d <debug> turn on debugging for module <debug>\n",
+ string, string);
+ exit(1);
+
+}
+
+
+void
+leave_group(int sock, struct ip_mreq mreq, char *name) {
+ int ret;
+
+ ret = setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (void*)&mreq,
+ sizeof(mreq));
+ if (ret < 0) {
+ fprintf(stderr, "%s: Failed to leave multicast group", name);
+ perror("");
+ }
+}
+